Memory layout

From Nintendo Switch Brew
Jump to navigation Jump to search

Userspace

The userspace virtual address space has 38 bits. It seems that when the IPC protocol was designed, it was only 36 bits leading to a weird encoding format.

There are several regions maintained by the kernel, each one starting at the upper bits bit37-21 randomized:

  • Main binary region.
  • Heap region.
  • Stack mapping region, available from SVC#svcGetInfo.

For the stack mapping region, the userland randomizes a page-offset where to start inside the region. This adds some additional entropy.

Binaries mapped by RO seems to be mapped randomly everywhere in the entire address space. The random base address for each NRO has bits 37-12 randomized, unlike the main binary region.

For all binaries(main area / NROs), the R-- section is always located immediately after R-X. The RW- section is always located immediately after the R-- section. Hence, there's no extra randomization / guard-pages for these sections.

TLS

This is the 0x200-byte thread-local-storage, the base address is loaded via ARM threadid register tpidrro_el0.

Offset Size Description
0x0 0x100 IPC command buffer
0x100 0xF8 ?
0x1F8 0x8 Address of threadctx+0x58.

Thread context

This is the structure of the 0x228-byte threadctx used by official userland software.

Offset Size Description
0x0 0xA8 ?
0xA8 0x8 Address of the stack-bottom-mirror which the thread was created with.
0xB0 0x8 Size of the stack.
0xB8 0x178 ?

Kernel

 Granule size for TTBR0*_EL1 is 4KB.
 TTBR0_EL1 vmem starts at vaddr 0x0.
 vmem end-addr for TTBR1_EL1 is 0xffffffffffffffff. vmem start-addr for TTBR1_EL1 is 0xFFFFFFF000000000.
 T0SZ = 31. Hence, bit-size of the TTBR0*_EL1 vmem region is 33. (0x0000000200000000)
 T1SZ = 28. Hence, bit-size of the TTBR1*_EL1 vmem region is 36. (0x0000001000000000)
 
 Note: ARM config for TTBR0 is presumably configured for userland later.
 
 See arm-doc for "Table D4-25 Translation table entry addresses when using the 4KB translation granule".
 
 See arm-doc for "Overview of VMSAv8-64 address translation using the 4KB translation granule".
 
 See arm-doc for "Table D4-11 TCR.TnSZ values and IA ranges, 4K granule with no concatenation of tables".
 Both TTBR*_EL1 use "Initial lookup level" 1. Therefore, the TTBR*_EL1 tables are level1.
 
 Due to T*SZ, Stage1/Stage2 translation for the initial table(level1) are the same, except Stage2 uses hard-coded T0SZ.
 Basically, the table is accessed as: ((u64*)tablebase)[<IA[y:30]>], where y = (37-T*SZ)+26. That is, starting at bit "y" ending(inclusive) at bit30. For TTBR0*_EL1, y = 32, while for TTBR1_EL1 y = 35.
 Hence, for TTBR0, index=((vaddr>>30) & 0x7), and for TTBR1, index=((vaddr>>30) & 0x3f).

2.0.0

"Vector Base Address Register (EL1)" = 0xfffffff7ffc50800.

The table for TTBR0 only contains the following:

  • Vmem 0x80000000 is mapped to physmem 0x80000000, using a size loaded from a register. This is only done when: "endaddr = 0x7fffffff + size; if(endaddr >= 0x80000001){...}"
    • The size is loaded from: "(u32 *0x70019050 & 0x3fff) << 20;"
    • The value written to the MMU-table descriptor is: "physaddr | val | 0x709;". val is 1<<52 when "tmp>>34" is non-zero and when "if((physaddr & 0x3c0000000) == 0)", otherwise val=0. tmp=size at the start and increased by 0xffffffffc0000000 each loop iteration. physaddr is increased by 0x40000000 each loop iteration.

TTBR1:

  • vmem 0xFFFFFFF800000000 is mapped to physmem 0x80000000. Similar to above, except tmp=0 due to wrap-around, etc. The chunksize used when increasing addr is 0xfffffff840000000, with another +=0x40000000 separate from the addr cmp for the loop.
    • "endaddr = 0x3fffffff + (<size from above> | 0xfffffff800000000); enaddr = (endaddr & 0xffffffffc0000000)-1; if(endaddr >= 0xfffffff800000001){<map mem>}"


  • Initializes level2 pagetable descriptor for vmem 0xFFFFFFF7C0000000. descriptor = 0x3 | physaddr. physaddr is core-specific.
  • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FFC00000. descriptor = 0x3 | physaddr. physaddr is core-specific.
  • The content of the pagetable for the following level3 mmutables are not initialized in the main mmutable-init func. descriptor = 0x8007c003(0x3 | <physaddr tablebase>). tablebase=0x8007c000.
    • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FEE00000. physaddr = tablebase + (0x1<<12).
    • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FF000000. physaddr = tablebase + (0x2<<12).
    • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FF200000. physaddr = tablebase + (0x3<<12).
    • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FFA00000. physaddr = tablebase + (0x7<<12).
    • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FEC00000. physaddr = tablebase.
    • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FF400000. physaddr = tablebase + (0x4<<12).
    • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FF600000. physaddr = tablebase + (0x5<<12).
    • Initializes level3 pagetable descriptor for vmem 0xFFFFFFF7FF800000. physaddr = tablebase + (0x6<<12).
Vmem Physmem Size Descriptor ORR-value Permissions Description
0xFFFFFFF7FFC00000..0xFFFFFFF7FFC62000 (inclusive) 0x800A0000 0x63000 0x78B Kernel .text
0xFFFFFFF7FFC63000..0xFFFFFFF7FFC65000 (inclusive) 0x80103000 0x3000 0x6000000000078B Kernel .rodata
0xFFFFFFF7FFC66000..0xFFFFFFF7FFC6E000 (inclusive) 0x80106000 0x9000 0x6000000000070B Kernel .data/...

The following uses descriptor ORR-value 0x6000000000070B, the size of each one is 0x1000-bytes.

Vmem Physmem
0xFFFFFFF7FFDCA000 0x80060000
0xFFFFFFF7FFDCB000 0x80061000
0xFFFFFFF7FFDCE000 0x80068000
0xFFFFFFF7FFDD4000 0x80062000
0xFFFFFFF7FFDD5000 0x80063000
0xFFFFFFF7FFDD8000 0x8006A000
0xFFFFFFF7FFDD9000 0x8006B000
0xFFFFFFF7FFDDE000 0x80064000
0xFFFFFFF7FFDDF000 0x80065000
0xFFFFFFF7FFDE2000 0x8006C000
0xFFFFFFF7FFDE3000 0x8006D000
0xFFFFFFF7FFDE8000 0x80066000
0xFFFFFFF7FFDE9000 0x80067000
0xFFFFFFF7FFDCF000 0x80069000
0xFFFFFFF7FFDEC000 0x8006E000
0xFFFFFFF7FFDED000 0x8006F000
0xFFFFFFF7FFDD2000 0x80070000
0xFFFFFFF7FFDDC000 0x80071000
0xFFFFFFF7FFDE6000 0x80072000
0xFFFFFFF7FFDF0000 0x80073000

The following uses descriptor ORR-value 0x60000000000607, the size of each one is 0x1000-bytes.

Vmem Physmem
0xFFFFFFF7FFDC6000 0x70019000
0xFFFFFFF7FFDC4000 0x7001C000
0xFFFFFFF7FFDC2000 0x7001D000
0xFFFFFFF7FFDC0000 0x60006000
0xFFFFFFF7FFDC8000 0x70006000
0xFFFFFFF7FFDFB000 0x50041000
0xFFFFFFF7FFDFD000 0x50042000

The rest are are mapped to core-specific physaddrs: ...