kern/ldr: move crt0 into .rodata

This commit is contained in:
Michael Scire 2023-10-11 02:59:41 -07:00 committed by SciresM
parent 4ca3c44e5f
commit 0daef4a6e8
7 changed files with 220 additions and 90 deletions

View file

@ -24,6 +24,9 @@ namespace ams::nxboot {
namespace {
constexpr u32 MesoshereMetadataLayout0Magic = util::FourCC<'M','S','S','0'>::Code;
constexpr u32 MesoshereMetadataLayout1Magic = util::FourCC<'M','S','S','1'>::Code;
struct InitialProcessBinaryHeader {
static constexpr u32 Magic = util::FourCC<'I','N','I','1'>::Code;
@ -1011,7 +1014,20 @@ namespace ams::nxboot {
}
/* Set the embedded ini pointer. */
const u32 magic = *reinterpret_cast<const u32 *>(payload_data + 4);
if (magic == MesoshereMetadataLayout0Magic) {
std::memcpy(payload_data + 8, std::addressof(meso_size), sizeof(meso_size));
} else if (magic == MesoshereMetadataLayout1Magic) {
if (const u32 meta_offset = *reinterpret_cast<const u32 *>(payload_data + 8); meta_offset <= meso_size - sizeof(meso_size)) {
s64 relative_offset = meso_size - meta_offset;
std::memcpy(payload_data + meta_offset, std::addressof(relative_offset), sizeof(relative_offset));
} else {
ShowFatalError("Invalid mesosphere metadata layout!\n");
}
} else {
ShowFatalError("Unknown mesosphere metadata version!\n");
}
/* Get the ini pointer. */
InitialProcessBinaryHeader * const ini = reinterpret_cast<InitialProcessBinaryHeader *>(payload_data + meso_size);

View file

@ -31,8 +31,22 @@ namespace ams::kern::init {
u32 dynamic_offset;
u32 init_array_offset;
u32 init_array_end_offset;
u32 sysreg_offset;
};
static_assert(util::is_pod<KernelLayout>::value);
static_assert(sizeof(KernelLayout) == 0x30);
static_assert(sizeof(KernelLayout) == 0x34);
#if defined(ATMOSPHERE_ARCH_ARM64)
struct KernelSystemRegisters {
u64 ttbr0_el1;
u64 ttbr1_el1;
u64 tcr_el1;
u64 mair_el1;
u64 sctlr_el1;
};
#else
struct KernelSystemRegisters {
};
#endif
}

View file

@ -17,11 +17,16 @@ def main(argc, argv):
kernel_ldr = f.read()
with open(argv[2], 'rb') as f:
kernel = f.read()
kernel_metadata_offset = 4
kernel_metaptr_offset = 4
assert (kernel_metaptr_offset <= len(kernel) - 0x40)
assert (kernel[kernel_metaptr_offset:kernel_metaptr_offset + 4] == b'MSS1')
kernel_metadata_offset = up('<I', kernel[kernel_metaptr_offset+4:kernel_metaptr_offset+8])[0]
assert (kernel_metadata_offset <= len(kernel) - 0x40)
assert (kernel[kernel_metadata_offset:kernel_metadata_offset + 4] == b'MSS0')
bss_start, bss_end, kernel_end = up('<III', kernel[kernel_metadata_offset + 0x30:kernel_metadata_offset + 0x3C])
bss_start, bss_end, kernel_end = up('<III', kernel[kernel_metadata_offset + 0x2C:kernel_metadata_offset + 0x38])
bss_start += kernel_metadata_offset + 0x14
bss_end += kernel_metadata_offset + 0x14
kernel_end += kernel_metadata_offset + 0x14
assert (bss_end >= bss_start)
assert (bss_end == kernel_end)
@ -53,9 +58,9 @@ def main(argc, argv):
mesosphere_end = align_up(kernel_ldr_end, 0x1000)
with open(argv[3], 'wb') as f:
f.write(kernel[:kernel_metadata_offset + 4])
f.write(pk('<QQI', embedded_ini_offset, kernel_ldr_offset, atmosphere_target_firmware(13, 0, 0)))
f.write(kernel[kernel_metadata_offset + 0x18:])
f.write(kernel[:kernel_metadata_offset])
f.write(pk('<QQI', embedded_ini_offset - (kernel_metadata_offset), kernel_ldr_offset - (kernel_metadata_offset + 8), atmosphere_target_firmware(17, 0, 0)))
f.write(kernel[kernel_metadata_offset + 0x14:])
f.seek(embedded_ini_offset)
f.write(embedded_ini_header)
f.write(embedded_kips)

View file

@ -16,9 +16,25 @@ SECTIONS
. = __start__;
__code_start = . ;
.crt0 :
.start :
{
KEEP (*(.crt0 .crt0.*))
KEEP (*(.start .start.*))
. = ALIGN(8);
} :code
/* .sleep. */
.sleep :
{
KEEP( *(.sleep .sleep.*) )
. = ALIGN(8);
} :code
/* .vectors. */
. = ALIGN(2K);
__vectors_start__ = . ;
.vectors :
{
KEEP( *(.vectors) )
. = ALIGN(8);
} :code
@ -51,28 +67,16 @@ SECTIONS
. = ALIGN(8);
} :code
/* .sleep. */
. = ALIGN(4K);
__sleep_start__ = . ;
.sleep :
{
KEEP( *(.sleep .sleep.*) )
. = ALIGN(8);
} :code
/* .vectors. */
. = ALIGN(2K);
__vectors_start__ = . ;
.vectors :
{
KEEP( *(.vectors) )
. = ALIGN(8);
} :code
/* =========== RODATA section =========== */
. = ALIGN(0x1000);
__rodata_start = . ;
.rodata.text.crt0 :
{
KEEP (*(.crt0 .crt0.*))
. = ALIGN(8);
} :code
.rodata :
{
*(.rodata .rodata.* .gnu.linkonce.r.*)

View file

@ -33,7 +33,37 @@
adr reg, label; \
ldr reg, [reg]
#define LOAD_RELATIVE_FROM_LABEL(reg, reg2, label) \
adr reg2, label; \
ldr reg, [reg2]; \
add reg, reg, reg2
#define INDIRECT_RELATIVE_CALL(reg, reg2, label) \
adr reg, label; \
add reg, reg, reg2; \
blr reg
#define SETUP_SYSTEM_REGISTER(_reg, _sr) \
LOAD_FROM_LABEL(_reg, __sysreg_constant_ ## _sr); \
msr _sr, _reg
.section .start, "ax", %progbits
.global _start
_start:
b _ZN3ams4kern4init10StartCore0Emm
__metadata_magic_number:
.ascii "MSS1" /* Magic, if executed as gadget "adds w13, w26, #0x4d4, lsl #12" */
__metadata_offset:
.word __metadata_begin - _start
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
.global _ZN3ams4kern17GetTargetFirmwareEv
.type _ZN3ams4kern17GetTargetFirmwareEv, %function
_ZN3ams4kern17GetTargetFirmwareEv:
adr x0, __metadata_target_firmware
ldr w0, [x0]
ret
#endif
.section .crt0.text.start, "ax", %progbits
@ -45,43 +75,6 @@ _ZN3ams4kern4init31IdentityMappedFunctionAreaBeginEv:
/* ================ Functions after this line remain identity-mapped after initialization finishes. ================ */
.global _start
_start:
b _ZN3ams4kern4init10StartCore0Emm
__metadata_begin:
.ascii "MSS0" /* Magic */
__metadata_ini_offset:
.quad 0 /* INI1 base address. */
__metadata_kernelldr_offset:
.quad 0 /* Kernel Loader base address. */
__metadata_target_firmware:
.word 0xCCCCCCCC /* Target firmware. */
__metadata_kernel_layout:
.word _start - _start /* rx_offset */
.word __rodata_start - _start /* rx_end_offset */
.word __rodata_start - _start /* ro_offset */
.word __data_start - _start /* ro_end_offset */
.word __data_start - _start /* rw_offset */
.word __bss_start__ - _start /* rw_end_offset */
.word __bss_start__ - _start /* bss_offset */
.word __bss_end__ - _start /* bss_end_offset */
.word __end__ - _start /* resource_offset */
.word _DYNAMIC - _start /* dynamic_offset */
.word __init_array_start - _start /* init_array_offset */
.word __init_array_end - _start /* init_array_end_offset */
.if (. - __metadata_begin) != 0x48
.error "Incorrect Mesosphere Metadata"
.endif
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
.global _ZN3ams4kern17GetTargetFirmwareEv
.type _ZN3ams4kern17GetTargetFirmwareEv, %function
_ZN3ams4kern17GetTargetFirmwareEv:
adr x0, __metadata_target_firmware
ldr w0, [x0]
ret
#endif
/* ams::kern::init::StartCore0(uintptr_t, uintptr_t) */
.section .crt0.text._ZN3ams4kern4init10StartCore0Emm, "ax", %progbits
.global _ZN3ams4kern4init10StartCore0Emm
@ -139,28 +132,31 @@ _ZN3ams4kern4init10StartCore0Emm:
/* Get the unknown debug region. */
/* TODO: This is always zero in release kernels -- what is this? Is it the device tree buffer? */
mov x21, #0
nop
/* We want to invoke kernel loader. */
adr x0, _start
adr x1, __metadata_kernel_layout
LOAD_FROM_LABEL(x2, __metadata_ini_offset)
add x2, x0, x2
LOAD_FROM_LABEL(x3, __metadata_kernelldr_offset)
add x3, x0, x3
LOAD_RELATIVE_FROM_LABEL(x2, x4, __metadata_ini_offset)
LOAD_RELATIVE_FROM_LABEL(x3, x4, __metadata_kernelldr_offset)
/* Invoke kernel loader. */
blr x3
/* Save the offset to virtual address from this page's physical address for our use. */
mov x24, x1
/* At this point kernelldr has been invoked, and we are relocated at a random virtual address. */
/* Next thing to do is to set up our memory management and slabheaps -- all the other core initialization. */
/* Call ams::kern::init::InitializeCore(uintptr_t, void **) */
mov x1, x0 /* Kernelldr returns a state object for the kernel to re-use. */
mov x0, x21 /* Use the address we determined earlier. */
bl _ZN3ams4kern4init20InitializeCorePhase1EmPPv
INDIRECT_RELATIVE_CALL(x16, x24, _ZN3ams4kern4init20InitializeCorePhase1EmPPv)
/* Get the init arguments for core 0. */
mov x0, xzr
bl _ZN3ams4kern4init16GetInitArgumentsEi
nop
INDIRECT_RELATIVE_CALL(x16, x24, _ZN3ams4kern4init16GetInitArgumentsEi)
/* Setup the stack pointer. */
ldr x2, [x0, #(INIT_ARGUMENTS_SP)]
@ -168,15 +164,24 @@ _ZN3ams4kern4init10StartCore0Emm:
/* Perform further initialization with the stack pointer set up, as required. */
/* This will include e.g. unmapping the identity mapping. */
bl _ZN3ams4kern4init20InitializeCorePhase2Ev
INDIRECT_RELATIVE_CALL(x16, x24, _ZN3ams4kern4init20InitializeCorePhase2Ev)
/* Get the init arguments for core 0. */
mov x0, xzr
bl _ZN3ams4kern4init16GetInitArgumentsEi
nop
INDIRECT_RELATIVE_CALL(x16, x24, _ZN3ams4kern4init16GetInitArgumentsEi)
/* Invoke the entrypoint. */
/* Retrieve entrypoint and argument. */
ldr x1, [x0, #(INIT_ARGUMENTS_ENTRYPOINT)]
ldr x0, [x0, #(INIT_ARGUMENTS_ARGUMENT)]
/* Set sctlr_el1 and ensure instruction consistency. */
SETUP_SYSTEM_REGISTER(x3, sctlr_el1)
dsb sy
isb
/* Invoke the entrypoint. */
blr x1
0: /* If we return here, something has gone wrong, so wait forever. */
@ -218,15 +223,11 @@ _ZN3ams4kern4init14StartOtherCoreEPKNS1_14KInitArgumentsE:
/* Disable the MMU/Caches. */
bl _ZN3ams4kern4init19DisableMmuAndCachesEv
/* Setup system registers using values from our KInitArguments. */
ldr x1, [x20, #(INIT_ARGUMENTS_TTBR0)]
msr ttbr0_el1, x1
ldr x1, [x20, #(INIT_ARGUMENTS_TTBR1)]
msr ttbr1_el1, x1
ldr x1, [x20, #(INIT_ARGUMENTS_TCR)]
msr tcr_el1, x1
ldr x1, [x20, #(INIT_ARGUMENTS_MAIR)]
msr mair_el1, x1
/* Setup system registers using values from constants table. */
SETUP_SYSTEM_REGISTER(x1, ttbr0_el1)
SETUP_SYSTEM_REGISTER(x1, ttbr1_el1)
SETUP_SYSTEM_REGISTER(x1, tcr_el1)
SETUP_SYSTEM_REGISTER(x1, mair_el1)
/* Perform cpu-specific setup. */
mrs x1, midr_el1
@ -251,13 +252,12 @@ _ZN3ams4kern4init14StartOtherCoreEPKNS1_14KInitArgumentsE:
isb
/* Load remaining needed fields from the init args. */
ldr x3, [x20, #(INIT_ARGUMENTS_SCTLR)]
ldr x2, [x20, #(INIT_ARGUMENTS_SP)]
ldr x1, [x20, #(INIT_ARGUMENTS_ENTRYPOINT)]
ldr x0, [x20, #(INIT_ARGUMENTS_ARGUMENT)]
/* Set sctlr_el1 and ensure instruction consistency. */
msr sctlr_el1, x3
SETUP_SYSTEM_REGISTER(x3, sctlr_el1)
dsb sy
isb
@ -271,6 +271,35 @@ _ZN3ams4kern4init14StartOtherCoreEPKNS1_14KInitArgumentsE:
0: /* If we return here, something has gone wrong, so wait forever. */
b 0b
/* Nintendo places the metadata after StartOthercore. */
.align 8
__metadata_begin:
__metadata_ini_offset:
.quad 0 /* INI1 base address. */
__metadata_kernelldr_offset:
.quad 0 /* Kernel Loader base address. */
__metadata_target_firmware:
.word 0xCCCCCCCC /* Target firmware. */
__metadata_kernel_layout:
.word _start - __metadata_kernel_layout /* rx_offset */
.word __rodata_start - __metadata_kernel_layout /* rx_end_offset */
.word __rodata_start - __metadata_kernel_layout /* ro_offset */
.word __data_start - __metadata_kernel_layout /* ro_end_offset */
.word __data_start - __metadata_kernel_layout /* rw_offset */
.word __bss_start__ - __metadata_kernel_layout /* rw_end_offset */
.word __bss_start__ - __metadata_kernel_layout /* bss_offset */
.word __bss_end__ - __metadata_kernel_layout /* bss_end_offset */
.word __end__ - __metadata_kernel_layout /* resource_offset */
.word _DYNAMIC - __metadata_kernel_layout /* dynamic_offset */
.word __init_array_start - __metadata_kernel_layout /* init_array_offset */
.word __init_array_end - __metadata_kernel_layout /* init_array_end_offset */
.word __sysreg_constant_begin - __metadata_kernel_layout /* sysreg_offset */
.if (. - __metadata_begin) != 0x48
.error "Incorrect Mesosphere Metadata"
.endif
/* TODO: Can we remove this while retaining QEMU support? */
#ifndef ATMOSPHERE_BOARD_NINTENDO_NX
/* ams::kern::init::JumpFromEL2ToEL1() */
@ -564,6 +593,24 @@ _ZN3ams4kern4arch5arm643cpu36FlushEntireDataCacheImplWithoutStackEv:
ret
/* System register values. */
.align 8
__sysreg_constant_begin:
__sysreg_constant_ttbr0_el1:
.quad 0 /* ttbr0_e11. */
__sysreg_constant_ttbr1_el1:
.quad 0 /* ttbr1_e11. */
__sysreg_constant_tcr_el1:
.quad 0 /* tcr_e11. */
__sysreg_constant_mair_el1:
.quad 0 /* mair_e11. */
__sysreg_constant_sctlr_el1:
.quad 0 /* sctlr_e11. */
.if (. - __sysreg_constant_begin) != 0x28
.error "Incorrect System Registers"
.endif
/* ================ Functions before this line remain identity-mapped after initialization finishes. ================ */
/* ams::kern::init::IdentityMappedFunctionAreaEnd() */

View file

@ -116,13 +116,29 @@ _main:
/* X0 is now the saved state. */
/* We will return this to the kernel. */
/* Return to the newly-relocated 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 x1
br x3
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
.global _ZN3ams4kern17GetTargetFirmwareEv

View file

@ -50,6 +50,25 @@ namespace ams::kern::init::loader {
constinit void *g_final_state[2];
void RelocateKernelPhysically(uintptr_t &base_address, KernelLayout *&layout) {
/* 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;
@ -62,7 +81,7 @@ namespace ams::kern::init::loader {
}
}
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) {
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);
@ -96,9 +115,17 @@ namespace ams::kern::init::loader {
/* Setup SCTLR_EL1. */
/* TODO: Define these bits properly elsewhere, document exactly what each bit set is doing .*/
constexpr u64 SctlrValue = 0x0000000034D5D925ul;
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) {
@ -159,6 +186,7 @@ namespace ams::kern::init::loader {
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());
@ -199,7 +227,7 @@ namespace ams::kern::init::loader {
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);
SetupInitialIdentityMapping(init_pt, base_address, bss_end_offset, resource_end_address, InitialPageTableRegionSizeMax, g_initial_page_allocator, reinterpret_cast<KernelSystemRegisters *>(base_address + sysreg_offset));
/* Generate a random slide for the kernel's base address. */
const KVirtualAddress virtual_base_address = GetRandomKernelBaseAddress(init_pt, base_address, bss_end_offset);