Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Some macros taken from https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/common/aarch64/asm_macros.S */
|
||||
/*
|
||||
* Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* Declare the exception vector table, enforcing it is aligned on a
|
||||
* 2KB boundary, as required by the ARMv8 architecture.
|
||||
* Use zero bytes as the fill value to be stored in the padding bytes
|
||||
* so that it inserts illegal AArch64 instructions. This increases
|
||||
* security, robustness and potentially facilitates debugging.
|
||||
*/
|
||||
.macro vector_base label, section_name=.vectors
|
||||
.section \section_name, "ax"
|
||||
.align 11, 0
|
||||
\label:
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Create an entry in the exception vector table, enforcing it is
|
||||
* aligned on a 128-byte boundary, as required by the ARMv8 architecture.
|
||||
* Use zero bytes as the fill value to be stored in the padding bytes
|
||||
* so that it inserts illegal AArch64 instructions. This increases
|
||||
* security, robustness and potentially facilitates debugging.
|
||||
*/
|
||||
.macro vector_entry label, section_name=.vectors
|
||||
.cfi_sections .debug_frame
|
||||
.section \section_name, "ax"
|
||||
.align 7, 0
|
||||
.type \label, %function
|
||||
.func \label
|
||||
.cfi_startproc
|
||||
\label:
|
||||
.endm
|
||||
|
||||
/*
|
||||
* This macro verifies that the given vector doesnt exceed the
|
||||
* architectural limit of 32 instructions. This is meant to be placed
|
||||
* immediately after the last instruction in the vector. It takes the
|
||||
* vector entry as the parameter
|
||||
*/
|
||||
.macro check_vector_size since
|
||||
.endfunc
|
||||
.cfi_endproc
|
||||
.if (. - \since) > (32 * 4)
|
||||
.error "Vector exceeds 32 instructions"
|
||||
.endif
|
||||
.endm
|
||||
|
||||
/* Actual Vectors for KernelLdr. */
|
||||
.global kernelldr_vectors
|
||||
vector_base kernelldr_vectors
|
||||
|
||||
/* Current EL, SP0 */
|
||||
.global unknown_exception
|
||||
unknown_exception:
|
||||
vector_entry synch_sp0
|
||||
/* Just infinite loop. */
|
||||
b unknown_exception
|
||||
check_vector_size synch_sp0
|
||||
|
||||
vector_entry irq_sp0
|
||||
b unknown_exception
|
||||
check_vector_size irq_sp0
|
||||
|
||||
vector_entry fiq_sp0
|
||||
b unknown_exception
|
||||
check_vector_size fiq_sp0
|
||||
|
||||
vector_entry serror_sp0
|
||||
b unknown_exception
|
||||
check_vector_size serror_sp0
|
||||
|
||||
/* Current EL, SPx */
|
||||
vector_entry synch_spx
|
||||
b restore_tpidr_el1
|
||||
check_vector_size synch_spx
|
||||
|
||||
vector_entry irq_spx
|
||||
b unknown_exception
|
||||
check_vector_size irq_spx
|
||||
|
||||
vector_entry fiq_spx
|
||||
b unknown_exception
|
||||
check_vector_size fiq_spx
|
||||
|
||||
vector_entry serror_spx
|
||||
b unknown_exception
|
||||
check_vector_size serror_spx
|
||||
|
||||
/* Lower EL, A64 */
|
||||
vector_entry synch_a64
|
||||
b unknown_exception
|
||||
check_vector_size synch_a64
|
||||
|
||||
vector_entry irq_a64
|
||||
b unknown_exception
|
||||
check_vector_size irq_a64
|
||||
|
||||
vector_entry fiq_a64
|
||||
b unknown_exception
|
||||
check_vector_size fiq_a64
|
||||
|
||||
vector_entry serror_a64
|
||||
b unknown_exception
|
||||
check_vector_size serror_a64
|
||||
|
||||
/* Lower EL, A32 */
|
||||
vector_entry synch_a32
|
||||
b unknown_exception
|
||||
check_vector_size synch_a32
|
||||
|
||||
vector_entry irq_a32
|
||||
b unknown_exception
|
||||
check_vector_size irq_a32
|
||||
|
||||
vector_entry fiq_a32
|
||||
b unknown_exception
|
||||
check_vector_size fiq_a32
|
||||
|
||||
vector_entry serror_a32
|
||||
b unknown_exception
|
||||
.endfunc
|
||||
.cfi_endproc
|
||||
/* To save space, insert in an unused vector segment. */
|
||||
.global restore_tpidr_el1
|
||||
.type restore_tpidr_el1, %function
|
||||
restore_tpidr_el1:
|
||||
mrs x0, tpidr_el1
|
||||
/* Make sure that TPIDR_EL1 can be dereferenced. */
|
||||
invalid_tpidr:
|
||||
cbz x0, invalid_tpidr
|
||||
/* Restore saved registers. */
|
||||
ldp x19, x20, [x0], #0x10
|
||||
ldp x21, x22, [x0], #0x10
|
||||
ldp x23, x24, [x0], #0x10
|
||||
ldp x25, x26, [x0], #0x10
|
||||
ldp x27, x28, [x0], #0x10
|
||||
ldp x29, x30, [x0], #0x10
|
||||
ldp x1, xzr, [x0], #0x10
|
||||
mov sp, x1
|
||||
mov x0, #0x1
|
||||
ret
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.section .text._ZN3ams4kern4init6loader23SaveRegistersToTpidrEl1EPv, "ax", %progbits
|
||||
.global _ZN3ams4kern4init6loader23SaveRegistersToTpidrEl1EPv
|
||||
_ZN3ams4kern4init6loader23SaveRegistersToTpidrEl1EPv:
|
||||
/* Set TPIDR_EL1 to the input register. */
|
||||
msr tpidr_el1, x0
|
||||
|
||||
/* Save registers to the region specified. */
|
||||
mov x1, sp
|
||||
stp x19, x20, [x0], #0x10
|
||||
stp x21, x22, [x0], #0x10
|
||||
stp x23, x24, [x0], #0x10
|
||||
stp x25, x26, [x0], #0x10
|
||||
stp x27, x28, [x0], #0x10
|
||||
stp x29, x30, [x0], #0x10
|
||||
stp x1, xzr, [x0], #0x10
|
||||
mov x0, #0x0
|
||||
ret
|
||||
|
||||
.section .text._ZN3ams4kern4init6loader22VerifyAndClearTpidrEl1EPv, "ax", %progbits
|
||||
.global _ZN3ams4kern4init6loader22VerifyAndClearTpidrEl1EPv
|
||||
_ZN3ams4kern4init6loader22VerifyAndClearTpidrEl1EPv:
|
||||
/* Get system register area from thread-specific processor id */
|
||||
mrs x1, tpidr_el1
|
||||
|
||||
/* We require here that the region registers are saved is same as input. */
|
||||
cmp x0, x1
|
||||
invalid_tpidr:
|
||||
b.ne invalid_tpidr
|
||||
|
||||
/* Clear TPIDR_EL1. */
|
||||
msr tpidr_el1, xzr
|
||||
ret
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */
|
||||
#define cpuactlr_el1 s3_1_c15_c2_0
|
||||
#define cpuectlr_el1 s3_1_c15_c2_1
|
||||
|
||||
#define LOAD_IMMEDIATE_32(reg, val) \
|
||||
mov reg, #(((val) >> 0x00) & 0xFFFF); \
|
||||
movk reg, #(((val) >> 0x10) & 0xFFFF), lsl#16
|
||||
|
||||
.section .crt0.text.start, "ax", %progbits
|
||||
.global _start
|
||||
_start:
|
||||
b _main
|
||||
__metadata_begin:
|
||||
.ascii "MLD0" /* Magic */
|
||||
__metadata_target_firmware:
|
||||
.word 0xCCCCCCCC /* Target Firmware. */
|
||||
__metadata_reserved:
|
||||
.word 0xCCCCCCCC /* Reserved. */
|
||||
_main:
|
||||
/* KernelLdr_Main(uintptr_t kernel_base_address, KernelMap *kernel_map, uintptr_t ini1_base_address); */
|
||||
adr x18, _start
|
||||
adr x16, __external_references
|
||||
ldr x17, [x16, #0x8] /* bss end */
|
||||
ldr x16, [x16, #0x0] /* bss start */
|
||||
add x16, x16, x18
|
||||
add x17, x17, x18
|
||||
clear_bss:
|
||||
cmp x16, x17
|
||||
b.cs clear_bss_done
|
||||
str xzr, [x16],#0x8
|
||||
b clear_bss
|
||||
clear_bss_done:
|
||||
adr x17, __external_references
|
||||
ldr x17, [x17, #0x10] /* stack top */
|
||||
add sp, x17, x18
|
||||
|
||||
/* Stack is now set up, so save important state. */
|
||||
sub sp, sp, #0x30
|
||||
stp x0, x1, [sp, #0x00]
|
||||
stp x2, x30, [sp, #0x10]
|
||||
stp xzr, xzr, [sp, #0x20]
|
||||
|
||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
|
||||
/* Get the target firmware from exosphere. */
|
||||
LOAD_IMMEDIATE_32(w0, 0xC3000004)
|
||||
mov w1, #65000
|
||||
smc #1
|
||||
cmp x0, #0
|
||||
0:
|
||||
b.ne 0b
|
||||
|
||||
/* Store the target firmware. */
|
||||
adr x0, __metadata_target_firmware
|
||||
str w1, [x0]
|
||||
#endif
|
||||
|
||||
/* Apply relocations and call init array for KernelLdr. */
|
||||
adr x0, _start
|
||||
adr x1, __external_references
|
||||
ldr x1, [x1, #0x18] /* .dynamic. */
|
||||
add x1, x0, x1
|
||||
|
||||
/* branch to ams::kern::init::Elf::ApplyRelocations(uintptr_t, const ams::kern::init::Elf::Elf64::Dyn *); */
|
||||
bl _ZN3ams4kern4init3Elf16ApplyRelocationsEmPKNS2_5Elf643DynE
|
||||
|
||||
/* branch to ams::kern::init::Elf::CallInitArrayFuncs(uintptr_t, uintptr_t) */
|
||||
adr x2, _start
|
||||
adr x1, __external_references
|
||||
ldr x0, [x1, #0x20] /* init_array_start */
|
||||
ldr x1, [x1, #0x28] /* init_array_end */
|
||||
add x0, x0, x2
|
||||
add x1, x1, x2
|
||||
bl _ZN3ams4kern4init3Elf18CallInitArrayFuncsEmm
|
||||
|
||||
/* Setup system registers, for detection of errors during init later. */
|
||||
msr tpidr_el1, xzr
|
||||
msr cntv_cval_el0, xzr
|
||||
adr x0, __external_references
|
||||
adr x1, _start
|
||||
ldr x0, [x0, #0x30]
|
||||
add x0, x1, x0
|
||||
msr vbar_el1, x0
|
||||
isb
|
||||
|
||||
/* Call ams::kern::init::loader::Main(uintptr_t, ams::kern::init::KernelLayout *, uintptr_t) */
|
||||
ldp x0, x1, [sp, #0x00]
|
||||
ldr x2, [sp, #0x10]
|
||||
|
||||
bl _ZN3ams4kern4init6loader4MainEmPNS1_12KernelLayoutEm
|
||||
str x0, [sp, #0x00]
|
||||
|
||||
/* Get ams::kern::init::loader::AllocateKernelInitStack(). */
|
||||
bl _ZN3ams4kern4init6loader23AllocateKernelInitStackEv
|
||||
str x0, [sp, #0x20]
|
||||
|
||||
|
||||
/* Call ams::kern::init::loader::GetFinalState() */
|
||||
bl _ZN3ams4kern4init6loader13GetFinalStateEv
|
||||
|
||||
/* X0 is now the saved state. */
|
||||
/* We will return this to the kernel. */
|
||||
|
||||
/* Adjust return address to point to the relocated kernel. */
|
||||
ldr x1, [sp, #0x18] /* Return address to Kernel */
|
||||
ldr x2, [sp, #0x00] /* Relocated kernel base address diff. */
|
||||
add x1, x2, x1
|
||||
|
||||
/* Translate the relocated address back to a physical address. */
|
||||
and x4, x1, #0xFFF
|
||||
sub x3, x1, x4
|
||||
at s1e1r, x3
|
||||
isb
|
||||
mrs x3, par_el1
|
||||
1:
|
||||
tbnz w3, #0, 1b
|
||||
and x3, x3, #0xFFFFFFFFF000
|
||||
add x3, x3, x4
|
||||
|
||||
/* Return the difference between relocated and physical in x1. */
|
||||
sub x1, x1, x3
|
||||
|
||||
/* Setup stack, and return to the kernel. */
|
||||
ldr x2, [sp, #0x20]
|
||||
mov sp, x2
|
||||
br x3
|
||||
|
||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
|
||||
.global _ZN3ams4kern17GetTargetFirmwareEv
|
||||
.type _ZN3ams4kern17GetTargetFirmwareEv, %function
|
||||
_ZN3ams4kern17GetTargetFirmwareEv:
|
||||
adr x0, __metadata_target_firmware
|
||||
ldr w0, [x0]
|
||||
ret
|
||||
#endif
|
||||
|
||||
.balign 8
|
||||
__external_references:
|
||||
.quad __bss_start__ - _start
|
||||
.quad __bss_end__ - _start
|
||||
.quad __stack_end - _start
|
||||
.quad _DYNAMIC - _start
|
||||
.quad __init_array_start - _start
|
||||
.quad __init_array_end - _start
|
||||
.quad __vectors_start__ - _start
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
#include "../../../kern_init_loader_board_setup.hpp"
|
||||
|
||||
namespace ams::kern::init::loader {
|
||||
|
||||
void PerformBoardSpecificSetup() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
#include "kern_init_loader_board_setup.hpp"
|
||||
|
||||
/* Necessary for calculating kernelldr size/base for initial identity mapping */
|
||||
extern "C" {
|
||||
|
||||
extern const u8 __bin_start__[];
|
||||
extern const u8 __bin_end__[];
|
||||
|
||||
}
|
||||
|
||||
namespace ams::kern::init::loader {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uintptr_t KernelBaseAlignment = 0x200000;
|
||||
constexpr uintptr_t KernelBaseRangeStart = 0xFFFFFF8000000000;
|
||||
constexpr uintptr_t KernelBaseRangeEnd = 0xFFFFFFFFFFE00000;
|
||||
constexpr uintptr_t KernelBaseRangeLast = KernelBaseRangeEnd - 1;
|
||||
static_assert(util::IsAligned(KernelBaseRangeStart, KernelBaseAlignment));
|
||||
static_assert(util::IsAligned(KernelBaseRangeEnd, KernelBaseAlignment));
|
||||
static_assert(KernelBaseRangeStart <= KernelBaseRangeLast);
|
||||
|
||||
static_assert(InitialProcessBinarySizeMax <= KernelResourceSize);
|
||||
|
||||
constexpr size_t InitialPageTableRegionSizeMax = 2_MB;
|
||||
static_assert(InitialPageTableRegionSizeMax < KernelPageTableHeapSize + KernelInitialPageHeapSize);
|
||||
|
||||
/* Global Allocator. */
|
||||
constinit KInitialPageAllocator g_initial_page_allocator;
|
||||
|
||||
constinit KInitialPageAllocator::State g_final_page_allocator_state;
|
||||
constinit InitialProcessBinaryLayoutWithSize g_initial_process_binary_meta;
|
||||
|
||||
constinit void *g_final_state[2];
|
||||
|
||||
void RelocateKernelPhysically(uintptr_t &base_address, KernelLayout *&layout, const uintptr_t &ini_base_address) {
|
||||
/* Adjust layout to be correct. */
|
||||
{
|
||||
const ptrdiff_t layout_offset = reinterpret_cast<uintptr_t>(layout) - base_address;
|
||||
layout->rx_offset += layout_offset;
|
||||
layout->rx_end_offset += layout_offset;
|
||||
layout->ro_offset += layout_offset;
|
||||
layout->ro_end_offset += layout_offset;
|
||||
layout->rw_offset += layout_offset;
|
||||
layout->rw_end_offset += layout_offset;
|
||||
layout->bss_offset += layout_offset;
|
||||
layout->bss_end_offset += layout_offset;
|
||||
layout->resource_offset += layout_offset;
|
||||
layout->dynamic_offset += layout_offset;
|
||||
layout->init_array_offset += layout_offset;
|
||||
layout->init_array_end_offset += layout_offset;
|
||||
layout->sysreg_offset += layout_offset;
|
||||
}
|
||||
|
||||
/* Relocate the kernel if necessary. */
|
||||
KPhysicalAddress correct_base = KSystemControl::Init::GetKernelPhysicalBaseAddress(base_address);
|
||||
if (correct_base != base_address) {
|
||||
const uintptr_t diff = GetInteger(correct_base) - base_address;
|
||||
const size_t size = layout->rw_end_offset;
|
||||
|
||||
/* Check that the new kernel doesn't overlap with us. */
|
||||
MESOSPHERE_INIT_ABORT_UNLESS((GetInteger(correct_base) >= reinterpret_cast<uintptr_t>(__bin_end__)) || (GetInteger(correct_base) + size <= reinterpret_cast<uintptr_t>(__bin_start__)));
|
||||
|
||||
/* Check that the new kernel doesn't overlap with the initial process binary. */
|
||||
MESOSPHERE_INIT_ABORT_UNLESS((ini_base_address + InitialProcessBinarySizeMax <= GetInteger(correct_base)) || (GetInteger(correct_base) + size <= ini_base_address));
|
||||
|
||||
/* Conversion from KPhysicalAddress to void * is safe here, because MMU is not set up yet. */
|
||||
std::memmove(reinterpret_cast<void *>(GetInteger(correct_base)), reinterpret_cast<void *>(base_address), size);
|
||||
base_address += diff;
|
||||
layout = reinterpret_cast<KernelLayout *>(reinterpret_cast<uintptr_t>(layout) + diff);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupInitialIdentityMapping(KInitialPageTable &init_pt, uintptr_t base_address, uintptr_t kernel_size, uintptr_t page_table_region, size_t page_table_region_size, KInitialPageAllocator &allocator, KernelSystemRegisters *sysregs) {
|
||||
/* Map in an RWX identity mapping for the kernel. */
|
||||
constexpr PageTableEntry KernelRWXIdentityAttribute(PageTableEntry::Permission_KernelRWX, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
||||
init_pt.Map(base_address, kernel_size, base_address, KernelRWXIdentityAttribute, allocator, 0);
|
||||
|
||||
/* Map in an RWX identity mapping for ourselves. */
|
||||
constexpr PageTableEntry KernelLdrRWXIdentityAttribute(PageTableEntry::Permission_KernelRWX, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
||||
const uintptr_t kernel_ldr_base = util::AlignDown(reinterpret_cast<uintptr_t>(__bin_start__), PageSize);
|
||||
const uintptr_t kernel_ldr_size = util::AlignUp(reinterpret_cast<uintptr_t>(__bin_end__), PageSize) - kernel_ldr_base;
|
||||
init_pt.Map(kernel_ldr_base, kernel_ldr_size, kernel_ldr_base, KernelLdrRWXIdentityAttribute, allocator, 0);
|
||||
|
||||
/* Map in the page table region as RW- for ourselves. */
|
||||
constexpr PageTableEntry PageTableRegionRWAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
||||
init_pt.Map(page_table_region, page_table_region_size, page_table_region, PageTableRegionRWAttribute, allocator, 0);
|
||||
|
||||
/* Place the L1 table addresses in the relevant system registers. */
|
||||
cpu::SetTtbr0El1(init_pt.GetTtbr0L1TableAddress());
|
||||
cpu::SetTtbr1El1(init_pt.GetTtbr1L1TableAddress());
|
||||
|
||||
/* Setup MAIR_EL1, TCR_EL1. */
|
||||
/* TODO: Define these bits properly elsewhere, document exactly what each bit set is doing .*/
|
||||
constexpr u64 MairValue = 0x0000000044FF0400ul;
|
||||
constexpr u64 TcrValue = 0x00000011B5193519ul;
|
||||
cpu::MemoryAccessIndirectionRegisterAccessor(MairValue).Store();
|
||||
cpu::TranslationControlRegisterAccessor(TcrValue).Store();
|
||||
|
||||
/* Ensure that our configuration takes before proceeding. */
|
||||
cpu::EnsureInstructionConsistency();
|
||||
|
||||
/* Perform board-specific setup. */
|
||||
PerformBoardSpecificSetup();
|
||||
|
||||
/* Setup SCTLR_EL1. */
|
||||
/* TODO: Define these bits properly elsewhere, document exactly what each bit set is doing .*/
|
||||
constexpr u64 SctlrValue = 0x0000000034D5D92Dul;
|
||||
cpu::SetSctlrEl1(SctlrValue);
|
||||
cpu::InstructionMemoryBarrier();
|
||||
|
||||
/* Setup the system registers for other cores. */
|
||||
/* NOTE: sctlr_el1 on other cores has the WXN bit set (0x80000); this will be set before KernelMain() on this core. */
|
||||
sysregs->ttbr0_el1 = init_pt.GetTtbr0L1TableAddress();
|
||||
sysregs->ttbr1_el1 = init_pt.GetTtbr1L1TableAddress();
|
||||
sysregs->tcr_el1 = TcrValue;
|
||||
sysregs->mair_el1 = MairValue;
|
||||
sysregs->sctlr_el1 = SctlrValue | 0x80000;
|
||||
}
|
||||
|
||||
KVirtualAddress GetRandomKernelBaseAddress(KInitialPageTable &page_table, KPhysicalAddress phys_base_address, size_t kernel_size) {
|
||||
/* Define useful values for random generation. */
|
||||
|
||||
const uintptr_t kernel_offset = GetInteger(phys_base_address) % KernelBaseAlignment;
|
||||
|
||||
/* Repeatedly generate a random virtual address until we get one that's unmapped in the destination page table. */
|
||||
while (true) {
|
||||
const uintptr_t random_kaslr_slide = KSystemControl::Init::GenerateRandomRange(KernelBaseRangeStart / KernelBaseAlignment, KernelBaseRangeLast / KernelBaseAlignment);
|
||||
const KVirtualAddress kernel_region_start = random_kaslr_slide * KernelBaseAlignment;
|
||||
const KVirtualAddress kernel_region_end = kernel_region_start + util::AlignUp(kernel_offset + kernel_size, KernelBaseAlignment);
|
||||
const size_t kernel_region_size = GetInteger(kernel_region_end) - GetInteger(kernel_region_start);
|
||||
|
||||
/* Make sure the region has not overflowed */
|
||||
if (kernel_region_start >= kernel_region_end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make sure that the region stays within our intended bounds. */
|
||||
if (kernel_region_end > KernelBaseRangeEnd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Validate we can map the range we've selected. */
|
||||
if (!page_table.IsFree(kernel_region_start, kernel_region_size)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Our range is valid! */
|
||||
return kernel_region_start + kernel_offset;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uintptr_t Main(uintptr_t base_address, KernelLayout *layout, uintptr_t ini_base_address) {
|
||||
/* Relocate the kernel to the correct physical base address. */
|
||||
/* Base address and layout are passed by reference and modified. */
|
||||
RelocateKernelPhysically(base_address, layout, ini_base_address);
|
||||
|
||||
/* Validate kernel layout. */
|
||||
const uintptr_t rx_offset = layout->rx_offset;
|
||||
const uintptr_t rx_end_offset = layout->rx_end_offset;
|
||||
const uintptr_t ro_offset = layout->ro_offset;
|
||||
const uintptr_t ro_end_offset = layout->ro_end_offset;
|
||||
const uintptr_t rw_offset = layout->rw_offset;
|
||||
/* UNUSED: const uintptr_t rw_end_offset = layout->rw_end_offset; */
|
||||
const uintptr_t bss_end_offset = layout->bss_end_offset;
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(rx_offset, PageSize));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(rx_end_offset, PageSize));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(ro_offset, PageSize));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(ro_end_offset, PageSize));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(rw_offset, PageSize));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(bss_end_offset, PageSize));
|
||||
const uintptr_t bss_offset = layout->bss_offset;
|
||||
const uintptr_t resource_offset = layout->resource_offset;
|
||||
const uintptr_t dynamic_offset = layout->dynamic_offset;
|
||||
const uintptr_t init_array_offset = layout->init_array_offset;
|
||||
const uintptr_t init_array_end_offset = layout->init_array_end_offset;
|
||||
const uintptr_t sysreg_offset = layout->sysreg_offset;
|
||||
|
||||
/* Determine the size of the resource region. */
|
||||
const size_t resource_region_size = KMemoryLayout::GetResourceRegionSizeForInit(KSystemControl::Init::ShouldIncreaseThreadResourceLimit());
|
||||
const uintptr_t resource_end_address = base_address + resource_offset + resource_region_size;
|
||||
|
||||
/* Setup the INI1 header in memory for the kernel. */
|
||||
{
|
||||
/* Get the kernel layout. */
|
||||
KSystemControl::Init::GetInitialProcessBinaryLayout(std::addressof(g_initial_process_binary_meta.layout), base_address);
|
||||
|
||||
/* If there's no desired base address, use the ini in place. */
|
||||
if (g_initial_process_binary_meta.layout.address == 0) {
|
||||
g_initial_process_binary_meta.layout.address = ini_base_address;
|
||||
}
|
||||
|
||||
|
||||
/* Validate and potentially relocate the INI. */
|
||||
const InitialProcessBinaryHeader *ini_header = reinterpret_cast<const InitialProcessBinaryHeader *>(ini_base_address);
|
||||
size_t ini_size = 0;
|
||||
if (ini_header->magic == InitialProcessBinaryMagic && (ini_size = ini_header->size) <= InitialProcessBinarySizeMax) {
|
||||
/* INI is valid, relocate it if necessary. */
|
||||
if (ini_base_address != g_initial_process_binary_meta.layout.address) {
|
||||
std::memmove(reinterpret_cast<void *>(g_initial_process_binary_meta.layout.address), ini_header, ini_size);
|
||||
}
|
||||
} else {
|
||||
/* INI is invalid. Make the destination header invalid. */
|
||||
std::memset(reinterpret_cast<void *>(g_initial_process_binary_meta.layout.address), 0, sizeof(InitialProcessBinaryHeader));
|
||||
}
|
||||
|
||||
/* Set the INI size in layout. */
|
||||
g_initial_process_binary_meta.size = util::AlignUp(ini_size, PageSize);
|
||||
}
|
||||
|
||||
/* We want to start allocating page tables at the end of the resource region. */
|
||||
g_initial_page_allocator.Initialize(resource_end_address);
|
||||
|
||||
/* Make a new page table for TTBR1_EL1. */
|
||||
KInitialPageTable init_pt(KernelBaseRangeStart, KernelBaseRangeLast, g_initial_page_allocator);
|
||||
|
||||
/* Setup initial identity mapping. TTBR1 table passed by reference. */
|
||||
SetupInitialIdentityMapping(init_pt, base_address, bss_end_offset, resource_end_address, InitialPageTableRegionSizeMax, g_initial_page_allocator, reinterpret_cast<KernelSystemRegisters *>(base_address + sysreg_offset));
|
||||
|
||||
/* NOTE: On 19.0.0+, Nintendo calls an unknown function here on init_pt and g_initial_page_allocator. */
|
||||
/* This is stubbed in prod KernelLdr. */
|
||||
|
||||
/* Generate a random slide for the kernel's base address. */
|
||||
const KVirtualAddress virtual_base_address = GetRandomKernelBaseAddress(init_pt, base_address, bss_end_offset);
|
||||
|
||||
/* Map kernel .text as R-X. */
|
||||
constexpr PageTableEntry KernelTextAttribute(PageTableEntry::Permission_KernelRX, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
||||
init_pt.Map(virtual_base_address + rx_offset, rx_end_offset - rx_offset, base_address + rx_offset, KernelTextAttribute, g_initial_page_allocator, 0);
|
||||
|
||||
/* Map kernel .rodata and .rwdata as RW-. */
|
||||
/* Note that we will later reprotect .rodata as R-- */
|
||||
constexpr PageTableEntry KernelRoDataAttribute(PageTableEntry::Permission_KernelR, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
||||
constexpr PageTableEntry KernelRwDataAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
||||
init_pt.Map(virtual_base_address + ro_offset, ro_end_offset - ro_offset, base_address + ro_offset, KernelRwDataAttribute, g_initial_page_allocator, 0);
|
||||
init_pt.Map(virtual_base_address + rw_offset, bss_end_offset - rw_offset, base_address + rw_offset, KernelRwDataAttribute, g_initial_page_allocator, 0);
|
||||
|
||||
/* Physically randomize the kernel region. */
|
||||
/* NOTE: Nintendo does this only on 10.0.0+ */
|
||||
init_pt.PhysicallyRandomize(virtual_base_address + rx_offset, bss_end_offset - rx_offset, true);
|
||||
|
||||
/* Apply relocations to the kernel. */
|
||||
const Elf::Dyn *kernel_dynamic = reinterpret_cast<const Elf::Dyn *>(GetInteger(virtual_base_address) + dynamic_offset);
|
||||
Elf::ApplyRelocations(GetInteger(virtual_base_address), kernel_dynamic);
|
||||
|
||||
/* Clear kernel .bss. */
|
||||
/* NOTE: The kernel does this before applying relocations, but we do it after. */
|
||||
/* This allows us to place our relocations in space overlapping with .bss...and thereby reclaim the memory that would otherwise be wasted. */
|
||||
std::memset(GetVoidPointer(virtual_base_address + bss_offset), 0, bss_end_offset - bss_offset);
|
||||
|
||||
/* Call the kernel's init array functions. */
|
||||
/* NOTE: The kernel does this after reprotecting .rodata, but we do it before. */
|
||||
/* This allows our global constructors to edit .rodata, which is valuable for editing the SVC tables to support older firmwares' ABIs. */
|
||||
Elf::CallInitArrayFuncs(GetInteger(virtual_base_address) + init_array_offset, GetInteger(virtual_base_address) + init_array_end_offset);
|
||||
|
||||
/* Reprotect .rodata as R-- */
|
||||
init_pt.Reprotect(virtual_base_address + ro_offset, ro_end_offset - ro_offset, KernelRwDataAttribute, KernelRoDataAttribute);
|
||||
|
||||
/* Return the difference between the random virtual base and the physical base. */
|
||||
return GetInteger(virtual_base_address) - base_address;
|
||||
}
|
||||
|
||||
KPhysicalAddress AllocateKernelInitStack() {
|
||||
return g_initial_page_allocator.Allocate(PageSize) + PageSize;
|
||||
}
|
||||
|
||||
void **GetFinalState() {
|
||||
/* Get final page allocator state. */
|
||||
g_initial_page_allocator.GetFinalState(std::addressof(g_final_page_allocator_state));
|
||||
|
||||
/* Setup final kernel loader state. */
|
||||
g_final_state[0] = std::addressof(g_final_page_allocator_state);
|
||||
g_final_state[1] = std::addressof(g_initial_process_binary_meta);
|
||||
|
||||
return g_final_state;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::init::loader {
|
||||
|
||||
struct SavedRegisterState {
|
||||
u64 x[(30 - 19) + 1];
|
||||
u64 sp;
|
||||
u64 xzr;
|
||||
};
|
||||
static_assert(sizeof(SavedRegisterState) == 0x70);
|
||||
|
||||
int SaveRegistersToTpidrEl1(void *tpidr_el1);
|
||||
void VerifyAndClearTpidrEl1(void *tpidr_el1);
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
#include "kern_init_loader_asm.hpp"
|
||||
#include "kern_init_loader_board_setup.hpp"
|
||||
|
||||
namespace ams::kern::init::loader {
|
||||
|
||||
void PerformDefaultAarch64SpecificSetup() {
|
||||
SavedRegisterState saved_registers;
|
||||
SaveRegistersToTpidrEl1(std::addressof(saved_registers));
|
||||
ON_SCOPE_EXIT { VerifyAndClearTpidrEl1(std::addressof(saved_registers)); };
|
||||
|
||||
/* Main ID specific setup. */
|
||||
cpu::MainIdRegisterAccessor midr_el1;
|
||||
if (midr_el1.GetImplementer() == cpu::MainIdRegisterAccessor::Implementer::ArmLimited) {
|
||||
/* ARM limited specific setup. */
|
||||
const auto cpu_primary_part = midr_el1.GetPrimaryPartNumber();
|
||||
const auto cpu_variant = midr_el1.GetVariant();
|
||||
const auto cpu_revision = midr_el1.GetRevision();
|
||||
if (cpu_primary_part == cpu::MainIdRegisterAccessor::PrimaryPartNumber::CortexA57) {
|
||||
/* Cortex-A57 specific setup. */
|
||||
|
||||
/* Non-cacheable load forwarding enabled. */
|
||||
u64 cpuactlr_value = 0x1000000;
|
||||
|
||||
/* Enable the processor to receive instruction cache and TLB maintenance */
|
||||
/* operations broadcast from other processors in the cluster; */
|
||||
/* set the L2 load/store data prefetch distance to 8 requests; */
|
||||
/* set the L2 instruction fetch prefetch distance to 3 requests. */
|
||||
u64 cpuectlr_value = 0x1B00000040;
|
||||
|
||||
/* Disable load-pass DMB on certain hardware variants. */
|
||||
if (cpu_variant == 0 || (cpu_variant == 1 && cpu_revision <= 1)) {
|
||||
cpuactlr_value |= 0x800000000000000;
|
||||
}
|
||||
|
||||
/* Set actlr and ectlr. */
|
||||
if (cpu::GetCpuActlrEl1() != cpuactlr_value) {
|
||||
cpu::SetCpuActlrEl1(cpuactlr_value);
|
||||
}
|
||||
if (cpu::GetCpuEctlrEl1() != cpuectlr_value) {
|
||||
cpu::SetCpuEctlrEl1(cpuectlr_value);
|
||||
}
|
||||
} else if (cpu_primary_part == cpu::MainIdRegisterAccessor::PrimaryPartNumber::CortexA53) {
|
||||
/* Cortex-A53 specific setup. */
|
||||
|
||||
/* Set L1 data prefetch control to allow 5 outstanding prefetches; */
|
||||
/* enable device split throttle; */
|
||||
/* set the number of independent data prefetch streams to 2; */
|
||||
/* disable transient and no-read-allocate hints for loads; */
|
||||
/* set write streaming no-allocate threshold so the 128th consecutive streaming */
|
||||
/* cache line does not allocate in the L1 or L2 cache. */
|
||||
u64 cpuactlr_value = 0x90CA000;
|
||||
|
||||
/* Enable hardware management of data coherency with other cores in the cluster. */
|
||||
u64 cpuectlr_value = 0x40;
|
||||
|
||||
/* If supported, enable data cache clean as data cache clean/invalidate. */
|
||||
if (cpu_variant != 0 || (cpu_variant == 0 && cpu_revision > 2)) {
|
||||
cpuactlr_value |= 0x100000000000;
|
||||
}
|
||||
|
||||
/* Set actlr and ectlr. */
|
||||
if (cpu::GetCpuActlrEl1() != cpuactlr_value) {
|
||||
cpu::SetCpuActlrEl1(cpuactlr_value);
|
||||
}
|
||||
if (cpu::GetCpuEctlrEl1() != cpuectlr_value) {
|
||||
cpu::SetCpuEctlrEl1(cpuectlr_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This is a default implementation, which should be overridden in a source file in board/ */
|
||||
WEAK_SYMBOL void PerformBoardSpecificSetup() {
|
||||
return PerformDefaultAarch64SpecificSetup();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::init::loader {
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
void PerformDefaultAarch64SpecificSetup();
|
||||
#endif
|
||||
|
||||
void PerformBoardSpecificSetup();
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
/* This overrides the panic implementation from the kernel, to prevent linking debug print into kldr. */
|
||||
|
||||
NORETURN void PanicImpl(const char *file, int line, const char *format, ...) {
|
||||
MESOSPHERE_UNUSED(file, line, format);
|
||||
MESOSPHERE_INIT_ABORT();
|
||||
}
|
||||
|
||||
NORETURN void PanicImpl() {
|
||||
MESOSPHERE_INIT_ABORT();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* Definitions for libc genericity. */
|
||||
#define MESOSPHERE_LIBC_MEMCPY_GENERIC 1
|
||||
#define MESOSPHERE_LIBC_MEMCMP_GENERIC 1
|
||||
#define MESOSPHERE_LIBC_MEMMOVE_GENERIC 1
|
||||
#define MESOSPHERE_LIBC_MEMSET_GENERIC 1
|
||||
#define MESOSPHERE_LIBC_STRNCPY_GENERIC 1
|
||||
#define MESOSPHERE_LIBC_STRNCMP_GENERIC 1
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
|
||||
#include "kern_libc_config.arch.arm64.h"
|
||||
|
||||
#else
|
||||
|
||||
#error "Unknown architecture for libc"
|
||||
|
||||
#endif
|
||||
@@ -1,673 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include "kern_libc_config.h"
|
||||
|
||||
/* Note: copied from newlib */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
FUNCTION
|
||||
<<memmove>>---move possibly overlapping memory
|
||||
INDEX
|
||||
memmove
|
||||
SYNOPSIS
|
||||
#include <string.h>
|
||||
void *memmove(void *<[dst]>, const void *<[src]>, size_t <[length]>);
|
||||
DESCRIPTION
|
||||
This function moves <[length]> characters from the block of
|
||||
memory starting at <<*<[src]>>> to the memory starting at
|
||||
<<*<[dst]>>>. <<memmove>> reproduces the characters correctly
|
||||
at <<*<[dst]>>> even if the two areas overlap.
|
||||
RETURNS
|
||||
The function returns <[dst]> as passed.
|
||||
PORTABILITY
|
||||
<<memmove>> is ANSI C.
|
||||
<<memmove>> requires no supporting OS subroutines.
|
||||
QUICKREF
|
||||
memmove ansi pure
|
||||
*/
|
||||
|
||||
/* Nonzero if either X or Y is not aligned on a "long" boundary. */
|
||||
#define UNALIGNED(X, Y) \
|
||||
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
|
||||
|
||||
/* How many bytes are copied each iteration of the 4X unrolled loop. */
|
||||
#define BIGBLOCKSIZE (sizeof (long) << 2)
|
||||
|
||||
/* How many bytes are copied each iteration of the word copy loop. */
|
||||
#define LITTLEBLOCKSIZE (sizeof (long))
|
||||
|
||||
/* Threshhold for punting to the byte copier. */
|
||||
#undef TOO_SMALL
|
||||
#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE)
|
||||
|
||||
#if MESOSPHERE_LIBC_MEMMOVE_GENERIC
|
||||
|
||||
/*SUPPRESS 20*/
|
||||
void *
|
||||
//__inhibit_loop_to_libcall
|
||||
__attribute__((weak))
|
||||
memmove (void *dst_void,
|
||||
const void *src_void,
|
||||
size_t length)
|
||||
{
|
||||
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
|
||||
char *dst = dst_void;
|
||||
const char *src = src_void;
|
||||
|
||||
if (src < dst && dst < src + length)
|
||||
{
|
||||
/* Have to copy backwards */
|
||||
src += length;
|
||||
dst += length;
|
||||
while (length--)
|
||||
{
|
||||
*--dst = *--src;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (length--)
|
||||
{
|
||||
*dst++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
return dst_void;
|
||||
#else
|
||||
char *dst = dst_void;
|
||||
const char *src = src_void;
|
||||
long *aligned_dst;
|
||||
const long *aligned_src;
|
||||
|
||||
if (src < dst && dst < src + length)
|
||||
{
|
||||
/* Destructive overlap...have to copy backwards */
|
||||
src += length;
|
||||
dst += length;
|
||||
while (length--)
|
||||
{
|
||||
*--dst = *--src;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use optimizing algorithm for a non-destructive copy to closely
|
||||
match memcpy. If the size is small or either SRC or DST is unaligned,
|
||||
then punt into the byte copy loop. This should be rare. */
|
||||
if (!TOO_SMALL(length) && !UNALIGNED (src, dst))
|
||||
{
|
||||
aligned_dst = (long*)dst;
|
||||
aligned_src = (long*)src;
|
||||
|
||||
/* Copy 4X long words at a time if possible. */
|
||||
while (length >= BIGBLOCKSIZE)
|
||||
{
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
length -= BIGBLOCKSIZE;
|
||||
}
|
||||
|
||||
/* Copy one long word at a time if possible. */
|
||||
while (length >= LITTLEBLOCKSIZE)
|
||||
{
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
length -= LITTLEBLOCKSIZE;
|
||||
}
|
||||
|
||||
/* Pick up any residual with a byte copier. */
|
||||
dst = (char*)aligned_dst;
|
||||
src = (char*)aligned_src;
|
||||
}
|
||||
|
||||
while (length--)
|
||||
{
|
||||
*dst++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
return dst_void;
|
||||
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||
}
|
||||
|
||||
#endif /* MESOSPHERE_LIBC_MEMMOVE_GENERIC */
|
||||
|
||||
/*
|
||||
FUNCTION
|
||||
<<memcpy>>---copy memory regions
|
||||
SYNOPSIS
|
||||
#include <string.h>
|
||||
void* memcpy(void *restrict <[out]>, const void *restrict <[in]>,
|
||||
size_t <[n]>);
|
||||
DESCRIPTION
|
||||
This function copies <[n]> bytes from the memory region
|
||||
pointed to by <[in]> to the memory region pointed to by
|
||||
<[out]>.
|
||||
If the regions overlap, the behavior is undefined.
|
||||
RETURNS
|
||||
<<memcpy>> returns a pointer to the first byte of the <[out]>
|
||||
region.
|
||||
PORTABILITY
|
||||
<<memcpy>> is ANSI C.
|
||||
<<memcpy>> requires no supporting OS subroutines.
|
||||
QUICKREF
|
||||
memcpy ansi pure
|
||||
*/
|
||||
|
||||
#if MESOSPHERE_LIBC_MEMCPY_GENERIC
|
||||
|
||||
void *
|
||||
__attribute__((weak))
|
||||
memcpy (void * dst0,
|
||||
const void * __restrict src0,
|
||||
size_t len0)
|
||||
{
|
||||
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
|
||||
char *dst = (char *) dst0;
|
||||
char *src = (char *) src0;
|
||||
|
||||
void *save = dst0;
|
||||
|
||||
while (len0--)
|
||||
{
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
return save;
|
||||
#else
|
||||
char *dst = dst0;
|
||||
const char *src = src0;
|
||||
long *aligned_dst;
|
||||
const long *aligned_src;
|
||||
|
||||
/* If the size is small, or either SRC or DST is unaligned,
|
||||
then punt into the byte copy loop. This should be rare. */
|
||||
if (!TOO_SMALL(len0) && !UNALIGNED (src, dst))
|
||||
{
|
||||
aligned_dst = (long*)dst;
|
||||
aligned_src = (long*)src;
|
||||
|
||||
/* Copy 4X long words at a time if possible. */
|
||||
while (len0 >= BIGBLOCKSIZE)
|
||||
{
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
len0 -= BIGBLOCKSIZE;
|
||||
}
|
||||
|
||||
/* Copy one long word at a time if possible. */
|
||||
while (len0 >= LITTLEBLOCKSIZE)
|
||||
{
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
len0 -= LITTLEBLOCKSIZE;
|
||||
}
|
||||
|
||||
/* Pick up any residual with a byte copier. */
|
||||
dst = (char*)aligned_dst;
|
||||
src = (char*)aligned_src;
|
||||
}
|
||||
|
||||
while (len0--)
|
||||
*dst++ = *src++;
|
||||
|
||||
return dst0;
|
||||
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||
}
|
||||
|
||||
#endif /* MESOSPHERE_LIBC_MEMCPY_GENERIC */
|
||||
|
||||
/*
|
||||
FUNCTION
|
||||
<<memset>>---set an area of memory
|
||||
INDEX
|
||||
memset
|
||||
SYNOPSIS
|
||||
#include <string.h>
|
||||
void *memset(void *<[dst]>, int <[c]>, size_t <[length]>);
|
||||
DESCRIPTION
|
||||
This function converts the argument <[c]> into an unsigned
|
||||
char and fills the first <[length]> characters of the array
|
||||
pointed to by <[dst]> to the value.
|
||||
RETURNS
|
||||
<<memset>> returns the value of <[dst]>.
|
||||
PORTABILITY
|
||||
<<memset>> is ANSI C.
|
||||
<<memset>> requires no supporting OS subroutines.
|
||||
QUICKREF
|
||||
memset ansi pure
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#undef LBLOCKSIZE
|
||||
#undef UNALIGNED
|
||||
#undef TOO_SMALL
|
||||
|
||||
#define LBLOCKSIZE (sizeof(long))
|
||||
#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1))
|
||||
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
||||
|
||||
#if MESOSPHERE_LIBC_MEMSET_GENERIC
|
||||
|
||||
void *
|
||||
__attribute__((weak))
|
||||
memset (void *m,
|
||||
int c,
|
||||
size_t n)
|
||||
{
|
||||
char *s = (char *) m;
|
||||
|
||||
#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__)
|
||||
unsigned int i;
|
||||
unsigned long buffer;
|
||||
unsigned long *aligned_addr;
|
||||
unsigned int d = c & 0xff; /* To avoid sign extension, copy C to an
|
||||
unsigned variable. */
|
||||
|
||||
while (UNALIGNED (s))
|
||||
{
|
||||
if (n--)
|
||||
*s++ = (char) c;
|
||||
else
|
||||
return m;
|
||||
}
|
||||
|
||||
if (!TOO_SMALL (n))
|
||||
{
|
||||
/* If we get this far, we know that n is large and s is word-aligned. */
|
||||
aligned_addr = (unsigned long *) s;
|
||||
|
||||
/* Store D into each char sized location in BUFFER so that
|
||||
we can set large blocks quickly. */
|
||||
buffer = (d << 8) | d;
|
||||
buffer |= (buffer << 16);
|
||||
for (i = 32; i < LBLOCKSIZE * 8; i <<= 1)
|
||||
buffer = (buffer << i) | buffer;
|
||||
|
||||
/* Unroll the loop. */
|
||||
while (n >= LBLOCKSIZE*4)
|
||||
{
|
||||
*aligned_addr++ = buffer;
|
||||
*aligned_addr++ = buffer;
|
||||
*aligned_addr++ = buffer;
|
||||
*aligned_addr++ = buffer;
|
||||
n -= 4*LBLOCKSIZE;
|
||||
}
|
||||
|
||||
while (n >= LBLOCKSIZE)
|
||||
{
|
||||
*aligned_addr++ = buffer;
|
||||
n -= LBLOCKSIZE;
|
||||
}
|
||||
/* Pick up the remainder with a bytewise loop. */
|
||||
s = (char*)aligned_addr;
|
||||
}
|
||||
|
||||
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||
|
||||
while (n--)
|
||||
*s++ = (char) c;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
#endif /* MESOSPHERE_LIBC_MEMSET_GENERIC */
|
||||
|
||||
/*
|
||||
FUNCTION
|
||||
<<memcmp>>---compare two memory areas
|
||||
INDEX
|
||||
memcmp
|
||||
SYNOPSIS
|
||||
#include <string.h>
|
||||
int memcmp(const void *<[s1]>, const void *<[s2]>, size_t <[n]>);
|
||||
DESCRIPTION
|
||||
This function compares not more than <[n]> characters of the
|
||||
object pointed to by <[s1]> with the object pointed to by <[s2]>.
|
||||
RETURNS
|
||||
The function returns an integer greater than, equal to or
|
||||
less than zero according to whether the object pointed to by
|
||||
<[s1]> is greater than, equal to or less than the object
|
||||
pointed to by <[s2]>.
|
||||
PORTABILITY
|
||||
<<memcmp>> is ANSI C.
|
||||
<<memcmp>> requires no supporting OS subroutines.
|
||||
QUICKREF
|
||||
memcmp ansi pure
|
||||
*/
|
||||
|
||||
#undef LBLOCKSIZE
|
||||
#undef UNALIGNED
|
||||
#undef TOO_SMALL
|
||||
|
||||
/* Nonzero if either X or Y is not aligned on a "long" boundary. */
|
||||
#define UNALIGNED(X, Y) \
|
||||
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
|
||||
|
||||
/* How many bytes are copied each iteration of the word copy loop. */
|
||||
#define LBLOCKSIZE (sizeof (long))
|
||||
|
||||
/* Threshhold for punting to the byte copier. */
|
||||
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
||||
|
||||
#if MESOSPHERE_LIBC_MEMCMP_GENERIC
|
||||
|
||||
int
|
||||
__attribute__((weak))
|
||||
memcmp (const void *m1,
|
||||
const void *m2,
|
||||
size_t n)
|
||||
{
|
||||
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
|
||||
unsigned char *s1 = (unsigned char *) m1;
|
||||
unsigned char *s2 = (unsigned char *) m2;
|
||||
|
||||
while (n--)
|
||||
{
|
||||
if (*s1 != *s2)
|
||||
{
|
||||
return *s1 - *s2;
|
||||
}
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
unsigned char *s1 = (unsigned char *) m1;
|
||||
unsigned char *s2 = (unsigned char *) m2;
|
||||
unsigned long *a1;
|
||||
unsigned long *a2;
|
||||
|
||||
/* If the size is too small, or either pointer is unaligned,
|
||||
then we punt to the byte compare loop. Hopefully this will
|
||||
not turn up in inner loops. */
|
||||
if (!TOO_SMALL(n) && !UNALIGNED(s1,s2))
|
||||
{
|
||||
/* Otherwise, load and compare the blocks of memory one
|
||||
word at a time. */
|
||||
a1 = (unsigned long*) s1;
|
||||
a2 = (unsigned long*) s2;
|
||||
while (n >= LBLOCKSIZE)
|
||||
{
|
||||
if (*a1 != *a2)
|
||||
break;
|
||||
a1++;
|
||||
a2++;
|
||||
n -= LBLOCKSIZE;
|
||||
}
|
||||
|
||||
/* check m mod LBLOCKSIZE remaining characters */
|
||||
|
||||
s1 = (unsigned char*)a1;
|
||||
s2 = (unsigned char*)a2;
|
||||
}
|
||||
|
||||
while (n--)
|
||||
{
|
||||
if (*s1 != *s2)
|
||||
return *s1 - *s2;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||
}
|
||||
|
||||
#endif /* MESOSPHERE_LIBC_MEMCMP_GENERIC */
|
||||
|
||||
/*
|
||||
FUNCTION
|
||||
<<strncpy>>---counted copy string
|
||||
INDEX
|
||||
strncpy
|
||||
SYNOPSIS
|
||||
#include <string.h>
|
||||
char *strncpy(char *restrict <[dst]>, const char *restrict <[src]>,
|
||||
size_t <[length]>);
|
||||
DESCRIPTION
|
||||
<<strncpy>> copies not more than <[length]> characters from the
|
||||
the string pointed to by <[src]> (including the terminating
|
||||
null character) to the array pointed to by <[dst]>. If the
|
||||
string pointed to by <[src]> is shorter than <[length]>
|
||||
characters, null characters are appended to the destination
|
||||
array until a total of <[length]> characters have been
|
||||
written.
|
||||
RETURNS
|
||||
This function returns the initial value of <[dst]>.
|
||||
PORTABILITY
|
||||
<<strncpy>> is ANSI C.
|
||||
<<strncpy>> requires no supporting OS subroutines.
|
||||
QUICKREF
|
||||
strncpy ansi pure
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*SUPPRESS 560*/
|
||||
/*SUPPRESS 530*/
|
||||
|
||||
/* Nonzero if either X or Y is not aligned on a "long" boundary. */
|
||||
#define UNALIGNED(X, Y) \
|
||||
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
|
||||
|
||||
#if LONG_MAX == 2147483647L
|
||||
#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)
|
||||
#else
|
||||
#if LONG_MAX == 9223372036854775807L
|
||||
/* Nonzero if X (a long int) contains a NULL byte. */
|
||||
#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080)
|
||||
#else
|
||||
#error long int is not a 32bit or 64bit type.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DETECTNULL
|
||||
#error long int is not a 32bit or 64bit byte
|
||||
#endif
|
||||
|
||||
#undef TOO_SMALL
|
||||
#define TOO_SMALL(LEN) ((LEN) < sizeof (long))
|
||||
|
||||
#if MESOSPHERE_LIBC_STRNCMP_GENERIC
|
||||
|
||||
char *
|
||||
strncpy (char *__restrict dst0,
|
||||
const char *__restrict src0,
|
||||
size_t count)
|
||||
{
|
||||
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
|
||||
char *dscan;
|
||||
const char *sscan;
|
||||
|
||||
dscan = dst0;
|
||||
sscan = src0;
|
||||
while (count > 0)
|
||||
{
|
||||
--count;
|
||||
if ((*dscan++ = *sscan++) == '\0')
|
||||
break;
|
||||
}
|
||||
while (count-- > 0)
|
||||
*dscan++ = '\0';
|
||||
|
||||
return dst0;
|
||||
#else
|
||||
char *dst = dst0;
|
||||
const char *src = src0;
|
||||
long *aligned_dst;
|
||||
const long *aligned_src;
|
||||
|
||||
/* If SRC and DEST is aligned and count large enough, then copy words. */
|
||||
if (!UNALIGNED (src, dst) && !TOO_SMALL (count))
|
||||
{
|
||||
aligned_dst = (long*)dst;
|
||||
aligned_src = (long*)src;
|
||||
|
||||
/* SRC and DEST are both "long int" aligned, try to do "long int"
|
||||
sized copies. */
|
||||
while (count >= sizeof (long int) && !DETECTNULL(*aligned_src))
|
||||
{
|
||||
count -= sizeof (long int);
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
}
|
||||
|
||||
dst = (char*)aligned_dst;
|
||||
src = (char*)aligned_src;
|
||||
}
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
--count;
|
||||
if ((*dst++ = *src++) == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
while (count-- > 0)
|
||||
*dst++ = '\0';
|
||||
|
||||
return dst0;
|
||||
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||
}
|
||||
|
||||
#endif /* MESOSPHERE_LIBC_STRNCPY_GENERIC */
|
||||
|
||||
/*
|
||||
FUNCTION
|
||||
<<strncmp>>---character string compare
|
||||
|
||||
INDEX
|
||||
strncmp
|
||||
SYNOPSIS
|
||||
#include <string.h>
|
||||
int strncmp(const char *<[a]>, const char * <[b]>, size_t <[length]>);
|
||||
DESCRIPTION
|
||||
<<strncmp>> compares up to <[length]> characters
|
||||
from the string at <[a]> to the string at <[b]>.
|
||||
RETURNS
|
||||
If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>,
|
||||
<<strncmp>> returns a number greater than zero. If the two
|
||||
strings are equivalent, <<strncmp>> returns zero. If <<*<[a]>>>
|
||||
sorts lexicographically before <<*<[b]>>>, <<strncmp>> returns a
|
||||
number less than zero.
|
||||
PORTABILITY
|
||||
<<strncmp>> is ANSI C.
|
||||
<<strncmp>> requires no supporting OS subroutines.
|
||||
QUICKREF
|
||||
strncmp ansi pure
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* Nonzero if either X or Y is not aligned on a "long" boundary. */
|
||||
#define UNALIGNED(X, Y) \
|
||||
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
|
||||
|
||||
/* DETECTNULL returns nonzero if (long)X contains a NULL byte. */
|
||||
#if LONG_MAX == 2147483647L
|
||||
#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)
|
||||
#else
|
||||
#if LONG_MAX == 9223372036854775807L
|
||||
#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080)
|
||||
#else
|
||||
#error long int is not a 32bit or 64bit type.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DETECTNULL
|
||||
#error long int is not a 32bit or 64bit byte
|
||||
#endif
|
||||
|
||||
#if MESOSPHERE_LIBC_STRNCMP_GENERIC
|
||||
|
||||
int
|
||||
strncmp (const char *s1,
|
||||
const char *s2,
|
||||
size_t n)
|
||||
{
|
||||
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
while (n-- != 0 && *s1 == *s2)
|
||||
{
|
||||
if (n == 0 || *s1 == '\0')
|
||||
break;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
|
||||
return (*(unsigned char *) s1) - (*(unsigned char *) s2);
|
||||
#else
|
||||
unsigned long *a1;
|
||||
unsigned long *a2;
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
/* If s1 or s2 are unaligned, then compare bytes. */
|
||||
if (!UNALIGNED (s1, s2))
|
||||
{
|
||||
/* If s1 and s2 are word-aligned, compare them a word at a time. */
|
||||
a1 = (unsigned long*)s1;
|
||||
a2 = (unsigned long*)s2;
|
||||
while (n >= sizeof (long) && *a1 == *a2)
|
||||
{
|
||||
n -= sizeof (long);
|
||||
|
||||
/* If we've run out of bytes or hit a null, return zero
|
||||
since we already know *a1 == *a2. */
|
||||
if (n == 0 || DETECTNULL (*a1))
|
||||
return 0;
|
||||
|
||||
a1++;
|
||||
a2++;
|
||||
}
|
||||
|
||||
/* A difference was detected in last few bytes of s1, so search bytewise */
|
||||
s1 = (char*)a1;
|
||||
s2 = (char*)a2;
|
||||
}
|
||||
|
||||
while (n-- > 0 && *s1 == *s2)
|
||||
{
|
||||
/* If we've run out of bytes or hit a null, return zero
|
||||
since we already know *s1 == *s2. */
|
||||
if (n == 0 || *s1 == '\0')
|
||||
return 0;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return (*(unsigned char *) s1) - (*(unsigned char *) s2);
|
||||
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||
}
|
||||
|
||||
#endif /* MESOSPHERE_LIBC_STRNCMP_GENERIC */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
Reference in New Issue
Block a user