Identity Paging

Basic Settings

More Details

System Control Register (SCTLR)

include/mmu.h

1
2
3
4
// SCTLR_EL1, System Control Register (EL1)

#define SCTLR_VALUE_MMU_DISABLED 0
#define SCTLR_VALUE_MMU_ENABLE   1

src/start.S

1
2
3
4
5
    // enable MMU
    ldr     x1, =SCTLR_VALUE_MMU_ENABLE
    mrs     x2, sctlr_el1
    orr     x2, x2, x1
    msr     sctlr_el1, x2

Translation Control Register (TCR)

include/mmu.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// TCR_EL1, Translation Control Register

#define TTBR0_EL1_REGION_BIT    48
#define TTBR1_EL1_REGION_BIT    48
#define TTBR0_EL1_GRANULE       0b00 // 4KB
#define TTBR1_EL1_GRANULE       0b10 // 4KB

#define TCR_EL1_T0SZ            ((64 - TTBR0_EL1_REGION_BIT) << 0)
#define TCR_EL1_T1SZ            ((64 - TTBR1_EL1_REGION_BIT) << 16)
#define TCR_EL1_TG0             (TTBR0_EL1_GRANULE << 14)
#define TCR_EL1_TG1             (TTBR1_EL1_GRANULE << 30)

#define TCR_EL1_VALUE           (TCR_EL1_T0SZ | TCR_EL1_T1SZ | TCR_EL1_TG0 | TCR_EL1_TG1)

src/start.S

1
2
3
    // setup tcr
    ldr     x0, =TCR_EL1_VALUE
    msr     tcr_el1, x0

Memory attribute indirection register (MAIR)

這次 Lab 會使用到兩種 Attribute:

  • Device memory nGnRnE
    • Peripheral access
    • The most restricted memory access
  • Normal memory without cache
    • Normal RAM access
    • Memory gathering, reordering, and speculative execution are possible but without cache

include/mmu.h

1
2
3
4
5
6
7
// MAIR_EL1, Memory Attribute Indirection Register

#define MAIR_DEVICE_nGnRnE      0b00000000
#define MAIR_NORMAL_NOCACHE     0b01000100 // Normal memory, Outer Non-cacheable | Normal memory, Inner Non-cacheable
#define MAIR_IDX_DEVICE_nGnRnE  0
#define MAIR_IDX_NORMAL_NOCACHE 1
#define MAIR_VALUE              ((MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)))

src/start.S

1
2
3
    // setup mair
    ldr     x0, =MAIR_VALUE
    msr     mair_el1, x0

Identity Paging

Two-level translation => 僅需要 PGD 和 PUD。每個 PUD 指到 1GB 的 block:

  • The first entry of PGD which points to PUD
  • The first two entries of PUD.
    • The first one maps 0x00000000 - 0x3fffffff (RAM and GPU peripherals)
    • The second one maps 0x40000000 - 0x7fffffff (ARM local peripherals).

include/mmu.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Page Descriptor

#define PD_TABLE                0b11
#define PD_BLOCK                0b01
#define PD_ACCESS               (1 << 10)
#define PGD_ATTR                PD_TABLE // Lower attributes is ignored
#define PUD_ATTR                (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK)

#define PGD_ADDR                0x0
#define PUD_ADDR                0x1000

#define PGD_VALUE               (PUD_ADDR | PGD_ATTR)
#define PUD1_VALUE              (0x00000000 | PUD_ATTR)
#define PUD2_VALUE              (0x40000000 | PUD_ATTR)

#endif

src/start.S

設定完 PGD 與 PUD 後即可開啟 MMU

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    // identity paging
    ldr     x0, =PGD_ADDR
    ldr     x1, =PUD_ADDR

    ldr     x2, =PGD_VALUE
    str     x2, [x0]

    ldr     x2, =PUD1_VALUE // 1st 1GB mapped by the 1st entry of PUD
    str     x2, [x1]

    ldr     x2, =PUD2_VALUE // 2nd 1GB mapped by the 2nd entry of PUD
    str     x2, [x1, #8]

    msr     ttbr0_el1, x0 // load PGD to the buttom translation based register.

    // enable MMU
    ldr     x1, =SCTLR_VALUE_MMU_ENABLE
    mrs     x2, sctlr_el1
    orr     x2, x2, x1
    msr     sctlr_el1, x2

Map Kernel to the upper address space

kernel 現在會被放到 Upper Address Space (0xFFFF000000000000 後),所以 Linker 需要做對應的調整。

src/linker.ld

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SECTIONS
{
  . = 0xFFFF000000000000;
  . += 0x80000;
  __kernel_start = .;
  .text :
  {
    KEEP(*(.text.boot))
    *(.text)
  }
  ...

KERNEL_VIRT_BASE

一些曾經有定義記憶體位置的 MACRO 需要加上 KERNEL_VIRT_BASE 的 offset

src/mmu.h

1
#define KERNEL_VIRT_BASE        0xFFFF000000000000

src/mmio.h

1
#define MMIO_BASE       (KERNEL_VIRT_BASE | 0x3F000000)

Indirect Branch

src/start.S

stack 在初始化時也需要調整到正確的記憶體位址上,並在最後準備進到 C Code 時,需要使用 indirect branch (QA1)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
...
    // disable MMU
    ldr     x1, =SCTLR_VALUE_MMU_DISABLED
    msr     sctlr_el1, x1
    
    ... // skip for simplicity

    adr     x1, el1_start
    msr     elr_el2, x1

    eret

el1_start:
    ... // skip for simplicity

    // set stack pointer
    ldr     x1, =0x60000
    mov     sp, x1

    // setup tcr
    ldr     x0, =TCR_EL1_VALUE
    msr     tcr_el1, x0

    // setup mair
    ldr     x0, =MAIR_VALUE
    msr     mair_el1, x0

    // identity paging
    ldr     x0, =PGD_ADDR
    ldr     x1, =PUD_ADDR

    ldr     x2, =PGD_VALUE
    str     x2, [x0]

    ldr     x2, =PUD1_VALUE // 1st 1GB mapped by the 1st entry of PUD
    str     x2, [x1]

    ldr     x2, =PUD2_VALUE // 2nd 1GB mapped by the 2nd entry of PUD
    str     x2, [x1, #8]

    msr     ttbr0_el1, x0 // load PGD to the buttom translation based register.
    msr     ttbr1_el1, x0 // load PGD to the upper translation based register.

    // enable MMU
    ldr     x1, =SCTLR_VALUE_MMU_ENABLE
    mrs     x2, sctlr_el1
    orr     x2, x2, x1
    msr     sctlr_el1, x2

    ... // skip for simplicity

    // mov sp to virtual address
    ldr     x1, =KERNEL_VIRT_BASE
    add     sp, sp, x1

    // indirect branch
    ldr     x0, =boot_init
    br      x0

1:
    b       1b

    ... // skip for simplicity

Appendix

nGnRnE

The three device attributes are G, R, and E, with the following definitions:

Gathering (G/nG)

  • Determines whether multiple accesses can be merged into a single bus transaction
  • nG: Number/size of accesses on the bus = number/size of accesses in code

Reordering (R/nR)

  • Determines whether accesses to the same device can be reordered
  • nR: Accesses to the same IMPLEMENTATION DEFINED block size will appear on the bus in program order

Early Write Acknowledgement (E/nE)

  • Indicates to the memory system whether a buffer can send acknowledgements
  • nE: The response should come from the end slave, not buffering in the interconnect

Ref: https://community.arm.com/developer/tools-software/oss-platforms/f/dev-platforms-forum/5433/how-to-understand-device-ngre

comments powered by Disqus