Changes

17,404 bytes added ,  03:01, 5 December 2019
Line 1: Line 1:  
The Kernel Loader ("KernelLdr"/"Kernelldr") was added in [[8.0.0]]. It is responsible for applying relocations to the Kernel, and mapping the Kernel's .text/.rodata/.data/.bss at a random slide.
 
The Kernel Loader ("KernelLdr"/"Kernelldr") was added in [[8.0.0]]. It is responsible for applying relocations to the Kernel, and mapping the Kernel's .text/.rodata/.data/.bss at a random slide.
   −
= Kernel Loader =
+
= Functions =
    
KernelLdr is called immediately by the Kernel's crt0 (after it deprivileges from EL2 to EL1, if required), with the following signature:
 
KernelLdr is called immediately by the Kernel's crt0 (after it deprivileges from EL2 to EL1, if required), with the following signature:
   −
     void KernelLoader_Main(uintptr_t kernel_base_address, KernelMap *kernel_map, uintptr_t ini1_base_address);
+
     void KernelLdr_Main(uintptr_t kernel_base_address, KernelMap *kernel_map, uintptr_t ini1_base_address);
    
== KernelLdr_Main ==
 
== KernelLdr_Main ==
Line 21: Line 21:  
     KernelLdr_ApplyRelocations(&KernelLdr_Main, __dynamic_start);
 
     KernelLdr_ApplyRelocations(&KernelLdr_Main, __dynamic_start);
 
     KernelLdr_libc_init_array();
 
     KernelLdr_libc_init_array();
 +
</pre>
 +
 +
[9.0.0+]
 +
Then it clears TPIDR_EL1 to 0, and sets VBAR_EL1.
 +
<pre>
 +
    // 9.0.0+
 +
    TPIDR_EL1 = 0
 +
    VBAR_EL1 = KernelLdr_ExceptionTable
 
</pre>
 
</pre>
   Line 29: Line 37:  
     uintptr_t kernel_relocation_offset = KernelLdr_LoadKernel(kernel_base, kernel_map, ini_base);
 
     uintptr_t kernel_relocation_offset = KernelLdr_LoadKernel(kernel_base, kernel_map, ini_base);
 
      
 
      
     // dtor called for static page allocator.
+
     // finalize called for static page allocator.
     g_InitialPageAllocator.~KInitialPageAllocator();
+
     g_InitialPageAllocator.Finalize();
 
      
 
      
 
     // Jumps back to the kernel code that called KernelLdr_Main.
 
     // Jumps back to the kernel code that called KernelLdr_Main.
Line 37: Line 45:     
== KernelLdr_ApplyRelocations ==
 
== KernelLdr_ApplyRelocations ==
TODO: Fill this out
+
This does standard ELF relocation using .dynamic.
 +
 
 +
First, it iterates over all entries in .dynamic, extracting .rel.dyn, .rela.dyn, relent, relatent, relcount, relacount from the relevant entries.
 +
 
 +
Then it does the following two loops to apply R_AARCH64_RELATIVE relocations:
 +
 
 +
<pre>
 +
    for (size_t i = 0; i < rel_count; i++) {
 +
        const Elf64_Rel *rel = dyn_rel_start + rel_ent * i;
 +
        while (uint32_t(rel->r_info) != R_AARCH64_RELATIVE) { /* Invalid entry, infloops */ }
 +
        *((Elf64_Addr *)(base_address + rel->r_offset)) += base_address;
 +
    }
 +
</pre>
 +
 
 +
<pre>
 +
    for (size_t i = 0; i < rela_count; i++) {
 +
        const Elf64_Rela *rela = dyn_rela_start + rela_ent * i;
 +
        while (uint32_t(rela->r_info) != R_AARCH64_RELATIVE) { /* Invalid entry, infloops */ }
 +
        *((Elf64_Addr *)(base_address + rela->r_offset)) = base_address + rela->r_addend;
 +
    }
 +
</pre>
   −
== KernelLdr_lib_init_array() ==
+
== KernelLdr_libc_init_array() ==
 
This is just standard libc init array code. .init_array is empty in all available binaries.
 
This is just standard libc init array code. .init_array is empty in all available binaries.
    
== KernelLdr_LoadKernel ==
 
== KernelLdr_LoadKernel ==
TODO: Fill this out
+
 
 +
First, it backs up the original kernel base, and then relocates the kernel physically to the upper half of DRAM if enough memory is available.
 +
 
 +
<pre>
 +
    // Backup kernel_base argument for use later
 +
    original_kernel_base = kernel_base;
 +
   
 +
    // Move kernel elsewhere in DRAM if needed (unused in practice?)
 +
    // This is maybe to support reserving unused memory for a second OS/hypervisor?
 +
    KernelLdr_RelocateKernelPhysically(&kernel_base, &kernel_map);
 +
</pre>
 +
 
 +
Then it checks all of the kernel map's offsets (and the kernel base) for page alignment.
 +
<pre>
 +
    // Read offsets from the kernel map, save on stack.
 +
    text_offset          = kernel_map->text_offset;
 +
    text_end_offset      = kernel_map->text_end_offset;
 +
    ro_offset            = kernel_map->ro_offset;
 +
    ro_end_offset        = kernel_map->ro_end_offset;
 +
    rw_offset            = kernel_map->rw_offset;
 +
    rw_end_offset        = kernel_map->rw_end_offset;
 +
    bss_offset            = kernel_map->bss_offset;
 +
    ini1_end_offset      = kernel_map->ini1_end_offset;
 +
    dynamic_offset        = kernel_map->dynamic_offset;
 +
    init_array_offset    = kernel_map->init_array_offset;
 +
    init_array_end_offset = kernel_map->init_array_end_offset;
 +
 
 +
    // Check all offsets are appropriately aligned.
 +
    while (kernel_base & 0xFFF) { }
 +
    while (text_offset & 0xFFF) { }
 +
    while (text_end_offset & 0xFFF) { }
 +
    while (ro_offset & 0xFFF) { }
 +
    while (ro_end_offset & 0xFFF) { }
 +
    while (rw_offset & 0xFFF) { }
 +
    while (rw_end_offset & 0xFFF) { }
 +
</pre>
 +
 
 +
Next, it relocates the INI1 to its appropriate load address.
 +
 
 +
<pre>
 +
    // If configured to do so, an extra 0x68000 bytes will be reserved for kernel usage.
 +
    reserved_kernel_data_size = KernelLdr_ShouldReserveAdditionalKernelData() ? 0x1790000 : 0x1728000;
 +
 
 +
    // Calculate address at which to place INI1.
 +
    ini1_end_address  = kernel_base + ini1_end_offset + reserved_kernel_data_size;
 +
    ini1_load_address = ini1_end_address - 0xC00000;
 +
 
 +
    // Relocate INI1 if destination address isn't the input argument address
 +
    if (ini1_load_address != ini1_address) {
 +
        // Validate INI1 binary has correct magic and valid size.
 +
        INI1Header *ini = (INI1Header *)ini1_address;
 +
        if (ini->magic == MAGIC_INI1 && ini->size <= 0xC00000) {
 +
            memmove(ini1_load_address, ini1_address, ini->size); // NOTE: No ToCToU, ini1->size is cached on stack.
 +
        } else {
 +
            // Invalid INI, place invalid header at load address. This will cause Kernel Panic later.
 +
            memset(ini1_load_address, 0, sizeof(INI1Header));
 +
        }
 +
    }
 +
</pre>
 +
 
 +
Next, it initializes the MMU with a basic identity mapping for Kernel + KernelLdr.
 +
<pre>
 +
    // Set page table region
 +
    page_table_region = ini1_end_address;
 +
    page_table_region_size = 0x200000;
 +
    g_InitialPageAllocator.Initialize(page_table_region);
 +
 
 +
    // Initialize new page table, eventually ends up in TTBR1_EL1.
 +
    KInitialPageTable ttbr1_page_table(&g_InitialPageAllocator);
 +
 
 +
    // Setup MMU with initial identity mapping.
 +
    KernelLdr_MapInitialIdentityMapping(&ttbr1_page_table, kernel_base, rw_end_offset, page_table_region, page_table_region_size, &g_InitialPageAllocator);
 +
</pre>
 +
 
 +
Next, it generates a random KASLR slide for the Kernel.
 +
<pre>
 +
    // Repeatedly try to generate a random slide
 +
    while (true) {
 +
        // Get random value from secure monitor in range
 +
        // This is "probably" KSystemControl::GenerateRandomRange, as in normal kernel
 +
        // However, it's unclear whether KSystemControl is actually included, or whether this is just copy/pasted?
 +
        random_kaslr_slide = KernelLdr_GenerateRandomRange(0xFFFFFF8000000000, 0xFFFFFFFFFFDFFFFF);
 +
        aligned_random_kaslr_slide = random_kaslr_slide & 0xFFFFFFFFFFE00000;
 +
       
 +
        // Calculate end address for kernel with this slide, rounding up.
 +
        random_kernel_end = aligned_random_kaslr_slide + (kernel_base & 0x1FFFFF) + rw_end_offset + 0x1FFFFF) & 0x1FFE00000;
 +
     
 +
        // Validate no overflow, and that the kernel will fit with the slide.
 +
        if (aligned_random_kaslr_slide >= random_kaslr_end || ((random_kaslr_end - 1) > 0xFFFFFFFFFFDFFFFF)) {
 +
            continue;
 +
        }
 +
 
 +
        // Validate we can map this range without conflicts.
 +
        // NOTE: This is inlined, but code looks same as in older kernel binaries.
 +
        if (!ttbr1_page_table.IsFree(aligned_random_kaslr_slide, random_kernel_end - aligned_random_kaslr_slide)) {
 +
            continue;
 +
        }
 +
 
 +
        // Valid kaslr slide, so we're done.
 +
        break;
 +
    }
 +
    final_virtual_kernel_base = aligned_random_kaslr_slide | (kernel_base & 0x1FFFFF);
 +
</pre>
 +
 
 +
Then, it maps the kernel at the final virtual address.
 +
<pre>
 +
    // Maps .text as R-X
 +
    attribute = 0x40000000000788;
 +
    ttbr1_page_table.Map(final_virtual_kernel_base + text_offset, text_end_offset - text_offset, kernel_base + text_offset, &attribute, &g_InitialPageAllocator);
 +
   
 +
    // Maps .rodata as R--
 +
    attribute = 0x60000000000788;
 +
 
 +
    // 9.0.0+
 +
    {
 +
        // On 9.0.0+, .rodata is initially RW- to facilitate .rel.ro.
 +
        attribute = 0x60000000000708;
 +
    }
 +
 
 +
    ttbr1_page_table.Map(final_virtual_kernel_base + ro_offset, ro_end_offset - ro_offset, kernel_base + ro_offset, &attribute, &g_InitialPageAllocator);
 +
 
 +
    // Maps .rwdata and .bss as RW-
 +
    attribute = 0x60000000000708;
 +
    ttbr1_page_table.Map(final_virtual_kernel_base + rw_offset, rw_end_offset - rw_offset, kernel_base + rw_offset, &attribute, &g_InitialPageAllocator);
 +
 
 +
    // Clears BSS.
 +
    memset(final_kernel_virtual_base + bss_offset, 0, rw_end_offset - bss_offset);
 +
</pre>
 +
 
 +
Then, it applies the kernel's .dynamic relocations and calls the kernel's libc .init_array functions.
 +
<pre>
 +
    // Applies all R_AARCH64_RELATIVE relocations.
 +
    KernelLdr_ApplyRelocations(final_kernel_virtual_base, final_kernel_virtual_base + dynamic_offset);
 +
 
 +
    // 9.0.0+: Reprotects .rodata as R--.
 +
    ttbr1_page_table.ReprotectToReadOnly(final_virtual_kernel_base + ro_offset, ro_end_offset - ro_offset);
 +
   
 +
    // This is standard libc init_array code, but called for the kernel's binary instead of kernelldr's.
 +
    for (uintptr_t cur_func = final_virtual_kernel_base + init_array_offset; cur_func < final_virtual_kernel_base + init_array_end_offset; cur_func += 8) {
 +
        ((void (*)(void))(*(uint64_t *)cur_func)();
 +
    }
 +
</pre>
 +
 
 +
Finally, it returns the difference between the kernel's original physical base address and the relocated kaslr'd virtual base address.
 +
<pre>
 +
    return final_virtual_kernel_base - original_kernel_base;
 +
</pre>
 +
 
 +
== KernelLdr_MapInitialIdentityMapping ==
 +
 
 +
Signature is like
 +
 
 +
    void KernelLdr_MapInitialIdentityMapping(KInitialPageTable *ttbr1_page_table, uintptr_t kernel_base, uintptr_t kernel_size,
 +
                                            uintptr_t page_tables_base, uintptr_t page_tables_size, InitialPageAllocator *allocator);
 +
 
 +
 
 +
First, this creates a new page table (eventually ends up in TTBR0_EL1), and adds identity mappings for Kernel, KernelLdr, and the Page Table region to it.
 +
 
 +
<pre>
 +
    // Create new KInitialPageTable
 +
    KInitialPageTable ttbr0_page_table(allocator);
 +
 
 +
    // Maps kernel with RWX identity mapping.
 +
    attribute = 0x40000000000708;
 +
    ttbr0_page_table.Map(kernel_base, kernel_size, kernel_base, &attribute, allocator);
 +
 
 +
    // Maps kernel loader with RWX identity mapping.
 +
    attribute = 0x40000000000708;
 +
    ttbr0_page_table.Map(__start, __end - __start, __start, &attribute, allocator);
 +
 
 +
    // Maps page table region with RW- identity mapping.
 +
    attribute = 0x60000000000708;
 +
    ttbr0_page_table.Map(page_tables_base, page_tables_size, page_tables_base, &attribute, allocator);
 +
</pre>
 +
 
 +
Next, this sets some system registers.
 +
<pre>
 +
    // Set TTBR0/TTBR1 with initial page tables.
 +
    TTBR0_EL1 = ttbr0_page_table.GetL1Table();
 +
    TTBR1_EL1 = ttbr1_page_table->GetL1Table();
 +
   
 +
    // Configure MAIR, TCR. TODO: Document here what bits these are.
 +
    MAIR_EL1 = 0x44FF0400;
 +
    TCR_EL1  = 0x11B5193519;
 +
 
 +
    // Check what CPU we're running on to configure CPUECTLR, CPUACTLR appropriately.
 +
    manufacture_id = MIDR_EL1;
 +
    implementer = manufacturer_id >> 24) & 0xFF;
 +
   
 +
    // 9.0.0+: Save X19-X30 + SP, save context struct in TPIDR_EL1.
 +
    KernelLdr_SaveRegistersToTpidrEl1();
 +
 
 +
    if (implementer == 0x41) {
 +
        // Implementer ID is 0x41 (ARM Limited).
 +
        architecture = (manufacture_id >> 4)  & 0x0FFF;
 +
        hw_variant  = (manufacture_id >> 20) & 0xF;
 +
        hw_revision  = (manufacture_id >> 0)  & 0xF;
 +
        if (architecture == 0xD07) {
 +
            // Architecture is 0xD07 (Cortex-A57).
 +
            cpuactlr_value = 0x1000000;    // Non-cacheable load forwarding enabled
 +
            cpuectlr_value = 0x1B00000040; // 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.
 +
            if (hw_variant == 0 || (hw_variant == 1 && hw_revision <= 1)) {
 +
                // If supported, disable load-pass DMB.
 +
                cpuactlr_value |= 0x800000000000000;
 +
            }
 +
            CPUACTLR_EL1 = cpuactlr_value;
 +
            if (CPUECTLR_EL1 != cpuectlr_value) {
 +
                CPUECTLR_EL1 = cpuectlr_value;
 +
            }
 +
        } else if (architecture == 0xD03) { // 9.0.0+
 +
            // Architecture is 0xD03 (Cortex-A53).
 +
            cpuactlr_value = 0x90CA000; // 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.
 +
            cpuectlr_value = 0x40;      // Enable hardware management of data coherency with other cores in the cluster.
 +
            if (hw_variant != 0 || (hw_variant == 0 && hw_revision > 2)) {
 +
                // If supported, enable data cache clean as data cache clean/invalidate.
 +
                cpuactlr_value |= 0x100000000000;
 +
            }
 +
            CPUACTLR_EL1 = cpuactlr_value;
 +
            if (CPUECTLR_EL1 != cpuectlr_value) {
 +
                CPUECTLR_EL1 = cpuectlr_value;
 +
            }
 +
        }
 +
    }
 +
 
 +
 
 +
    // 9.0.0+: Verify that TPIDR_EL1 is still set.
 +
    KernelLdr_VerifyTpidrEl1();
 +
</pre>
 +
 
 +
Next, the cache is flushed, to ensure that page tables will be successfully read once the MMU is enabled.
 +
<pre>
 +
    KernelLdr_EnsureCacheFlushed();
 +
</pre>
 +
 
 +
Finally, SCTLR is written to, enabling the MMU.
 +
<pre>
 +
    SCTLR_EL1 = 0x34D5D925;
 +
    __dsb_sy();
 +
    __isb();
 +
</pre>
 +
 
 +
== KernelLdr_RelocateKernelPhysically ==
 +
 
 +
This retrieves memory layout information from the secure monitor, and adjusts the kernel's physical location if necessary.
 +
 
 +
<pre>
 +
    adjusted_kernel_base = KernelLdr_GetAdjustedKernelPhysicalBase(*p_kernel_base);
 +
 
 +
    if (adjusted_kernel_base != *p_kernel_base) {
 +
        // Copy data to adjusted destination
 +
        memmove(adjusted_kernel_base, *p_kernel_base, (*p_kernel_map)->data_end_offset);
 +
 
 +
        // Adjust pointers.
 +
        kernel_base_diff = adjusted_kernel_base - *p_kernel_base;
 +
        *p_kernel_base = (uintptr_t)*p_kernel_base + kernel_base_diff;
 +
        *p_kernel_map  = (uintptr_t)*p_kernel_map  + kernel_base_diff;
 +
    }
 +
</pre>
 +
 
 +
 
 +
== KernelLdr_GetAdjustedKernelPhysicalBase ==
 +
 
 +
This sees how much more memory is available than expected, and relocates the kernel accordingly.
 +
 
 +
Note: Panic (infloop) happens on any smc call error, this isn't depicted in pseudocode for brevity reasons.
 +
 
 +
<pre>
 +
    // Gets DRAM size information from Memory Controller
 +
    dram_size_from_mc = (smc_read_write_register(MC_EMEM_CFG, 0, 0) & 0x3FFF) << 20;
 +
   
 +
    // Gets DRAM size information from Secure Monitor KernelConfiguration
 +
    memory_type = (smc_get_config(ConfigItem_KernelConfiguration) >> 16) & 3;
 +
    switch (memory_type) {
 +
        case MemoryType_4GB: // 0
 +
        default:
 +
            dram_size_from_kernel_cfg = 0x100000000;
 +
            break;
 +
        case MemoryType_6GB: // 1
 +
            dram_size_from_kernel_cfg = 0x180000000;
 +
            break;
 +
        case MemoryType_8GB: // 2
 +
            dram_size_from_kernel_cfg = 0x200000000;
 +
            break;
 +
    }
 +
   
 +
    // On normal systems, these should be equal (and kernel will not be relocated).
 +
    if (dram_size_from_mc < 2 * dram_size_from_kernel_cfg) {
 +
        return kernel_base + (dram_size_from_mc - dram_size_from_kernel_cfg) / 2;
 +
    } else {
 +
        return kernel_base;
 +
    }
 +
</pre>
 +
 
 +
== KernelLdr_ShouldReserveAdditionalKernelData ==
 +
 
 +
This just gets a flag from the KernelConfiguration.
 +
 
 +
Note: Panic (infloop) happens on any smc call error, this isn't depicted in pseudocode for brevity reasons.
 +
 
 +
<pre>
 +
    return (smc_get_config(ConfigItem_KernelConfiguration) >> 3) & 1;
 +
</pre>
 +
 
 +
== KernelLdr_GenerateRandomRange ==
 +
 
 +
This uses entropy from the secure monitor to generate a random value in a range (inclusive).
 +
 
 +
<pre>
 +
    range_size  = (range_end + 1 - range_start);
 +
    random_value = smc_generate_random_bytes(8);
 +
    random_value -= random_value / range_size * range_size;
 +
    return range_start + random_value;
 +
</pre>
 +
 
 +
== KernelLdr_EnsureCacheFlushed ==
 +
 
 +
Note: this is inlined, however it uses instructions that no compiler has intrinsics for (and looks like hand-written asm), so it's presumably its own thing.
 +
 
 +
<pre>
 +
    // Invalidate Local Cache
 +
    KernelLdr_InvalidateCacheLocal();
 +
    __dsb_sy();
 +
 
 +
    // Invalidate Share
 +
    KernelLdr_InvalidateCacheShared();
 +
    __dsb_sy();
 +
 
 +
    // Invalidate Local Cache again
 +
    KernelLdr_InvalidateCacheLocal();
 +
    __dsb_sy();
 +
   
 +
    // asm { tlbi vmalle1is; }
 +
    __dsb_sy();
 +
    __isb();
 +
</pre>
 +
 
 +
== KernelLdr_InvalidateCacheLocal ==
 +
 
 +
Standard ARM cache clean code, uses LoUIS + LoC from CLIDR_EL1.
 +
 
 +
== KernelLdr_InvalidateCacheShared ==
 +
 
 +
Standard ARM cache clean code, uses LoUIS from CLIDR_EL1.
 +
 
 +
== KernelLdr_ExceptionTable ==
 +
 
 +
Standard aarch64 exception table, only function that doesn't infinite loop is synchronous exception from same EL (synch_spx_exception)
 +
 
 +
synch_spx_exception does the following:
 +
* Moves TPIDR_EL1 into X0
 +
* Infinite loops if it is 0/NULL.
 +
* Restores X19-X30 + SP from the memory pointed to by TPIDR_EL1.
 +
* Returns to the saved LR stored in the context save struct.
 +
 
 +
== KernelLdr_SaveRegistersToTpidrEl1 ==
 +
 
 +
This saves X19-X30 + SP to an input pointer, and moves the pointer into TPIDR_EL1.
 +
 
 +
== KernelLdr_VerifyTpidrEl1 ==
 +
 
 +
This just verifies that TPIDR_EL1 is equal to an input argument, and clears it.
 +
 
 +
<pre>
 +
    // 9.0.0+
 +
    if (TPIDR_EL1 != input_arg) {
 +
        while (1) { /* Infinite loop panic */ }
 +
    }
 +
    TPIDR_EL1 = 0
 +
</pre>
    
== KInitialPageAllocator::KInitialPageAllocator ==
 
== KInitialPageAllocator::KInitialPageAllocator ==
Line 52: Line 448:  
</pre>
 
</pre>
   −
== KInitialPageAllocator::~KInitialPageAllocator ==
+
== KInitialPageAllocator::Initialize ==
This just clears the allocator's next address.
+
This sets the allocator's next address (function inferred as it is (presumably) inlined and next_address is (presumably) private).
    
<pre>
 
<pre>
     this->next_address = 0;
+
     this->next_address = address;
 
</pre>
 
</pre>
   −
== KInitialPageAllocator::Initialize ==
+
== KInitialPageAllocator::Finalize ==
This sets the allocator's next address (function inferred as it is (presumably) inlined and next_address is (presumably) private).
+
This just clears the allocator's next address.
    
<pre>
 
<pre>
     this->next_address = address;
+
     this->next_address = 0;
 
</pre>
 
</pre>
   Line 91: Line 487:  
</pre>
 
</pre>
   −
== Structures ==
+
== KInitialPageTable::KInitialPageTable ==
 +
 
 +
NOTE: This constructor is inferred.
 +
 
 +
<pre>
 +
KInitialPageTable::KInitialPageTable(KInitialPageAllocator *allocator) {
 +
    this->l1_table_ptr = allocator->Allocate();
 +
    memset(this->l1_table_ptr, 0, 0x1000);
 +
    this->num_l1_table_entries = 0x200;
 +
}
 +
</pre>
 +
 
 +
== KInitialPageTable::Map ==
 +
 
 +
Signature is like
 +
 
 +
    KInitialPageTable::Map(uintptr_t virtual_address, size_t size, uintptr_t physical_address, const uint64_t *attribute, InitialPageAllocator *allocator);
 +
 
 +
This is just standard aarch64 page table mapping code. New L2/L3 pages are allocated via allocator->Allocate() when needed.
 +
 
 +
== KInitialPageTable::IsFree ==
 +
 
 +
This is just standard aarch64 page table code. Walks the page table, verifying that all entries it would map for size + range are free.
 +
 
 +
== KInitialPageTable::ReprotectToReadOnly ==
 +
 
 +
This is just standard aarch64 page table code. Walks the page table, reprotects the read-write pages in the specified region as read-only.
 +
 
 +
This is probably a compiler-optimized version of a function that does an arbitrary reprotection.
   −
=== KernelMap ===
+
== KInitialPageTable::GetL1Table ==
 +
 
 +
This is an inferred getter for a (presumably) private member.
 +
 
 +
<pre>
 +
 
 +
    void *KInitialPageTable::GetL1Table() const {
 +
        return this->l1_table_ptr;
 +
    }
 +
 
 +
</pre>
 +
 
 +
= Structures =
 +
 
 +
== KernelMap ==
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 134: Line 572:  
| 0x20
 
| 0x20
 
| 4
 
| 4
| INI1 load offset
+
| INI1 end offset
 
|-
 
|-
 
| 0x24
 
| 0x24
Line 150: Line 588:  
|}
 
|}
   −
=== KInitialPageAllocator ===
+
== KInitialPageAllocator ==
 
KInitialPageAllocator is just a simple linear allocator.
 
KInitialPageAllocator is just a simple linear allocator.
   Line 169: Line 607:  
|}
 
|}
   −
==== KInitialPageAllocator::vtable ====
+
=== KInitialPageAllocator::vtable ===
    
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
Line 184: Line 622:  
| 8
 
| 8
 
| void (*Free)(KInitialPageAllocator *this, void *address);
 
| void (*Free)(KInitialPageAllocator *this, void *address);
 +
|-
 +
|}
 +
 +
== KInitialPageTable ==
 +
KInitialPageTable is a very, very stripped-down KPageTable.
 +
 +
Compared to pre-KernelLoader KInitialPageTable, it has slightly reduced memory footprint.
 +
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset
 +
! Size
 +
! Description
 +
|-
 +
| 0x0
 +
| 8
 +
| Pointer to L1 Table;
 +
|-
 +
| 0x8
 +
| 8
 +
| Number of L1 Table Entries (Normally 0x200);
 
|-
 
|-
 
|}
 
|}