Lab5: Virtual memory - A finer granularity kernel mapping

4KB Granularity

Page size = 4KB
Page table size = 2^9 * 8 Byte = 4KB

Page Table Layout

  • PGD
    • PUD
      • PMD (0x00000000~0x3FFFFFFF), 1GB
        • 504 PTE, normal memory (0x00000000~0x3EFFFFFF), 1G-16MB
        • 8 PTE, device memory (0x3F000000~0x3FFFFFFF), 16MB
      • Block, device memory (0x40000000~0x7FFFFFFF), 1GB

include/mm.h

1
2
#define PAGE_TABLE_SIZE 4096
#define PAGE_SIZE 4096

include/arm/mmu.h

1
2
3
4
5
6
7
8
9
10
11
12
/* Page descriptor */
#define PD_TABLE 0b11
#define PD_BLOCK 0b01
#define PD_PAGE 0b11
#define PD_ACCESS (1 << 10)

#define PGD0_ATTR PD_TABLE
#define PUD0_ATTR PD_TABLE
#define PUD1_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK)
#define PMD0_ATTR PD_TABLE
#define PTE_NORMAL_ATTR (PD_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_PAGE)
#define PTE_DEVICE_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_PAGE)

kernel/start.S

1
bl      create_page_table

kernel/mm.c

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
64
65
66
#include <include/arm/mmu.h>
#include <include/mm.h>
#include <include/types.h>

/* Page table size and content:
* PGD: 1 page, 1 entry (point to 1 PUD)
* PUD: 1 page, 2 entry (point to 1 PMD + 1 block)
* PMD: 1 page, 512 entry (point to 512 PTE)
* PTE: 1 page, 512 entry (entry of last 8 PTE point to device memory, while the
* others point to normal memory)
*
* Page table layout:
* |PGD0|PUD0|PMD0|PTE0| ... |PTE503|PTE504| ... |PTE511|
*/
void create_page_table()
{
extern uint64_t pg_dir;
uintptr_t *pgd, *pud, *pmd, *pte[512], page_addr = 0x0;

pgd = (((uintptr_t) &pg_dir) << 16) >> 16; // first 16 bits must be 0
pud = (uintptr_t) pgd + PAGE_TABLE_SIZE;
pmd = (uintptr_t) pud + PAGE_TABLE_SIZE;
for (int i = 1; i <= 512; ++i) {
pte[i] = (uintptr_t) pmd + PAGE_TABLE_SIZE * i;
}

// set up PGD
pgd[0] = (uintptr_t) pud | PGD0_ATTR;

// set up PUD
pud[0] =
(uintptr_t) pmd | PUD0_ATTR; // 1st 1GB mapped by the 1st entry of PUD
pud[1] = (uintptr_t) 0x40000000 |
PUD1_ATTR; // 2nd 1GB mapped by the 2nd entry of PUD

// set up PMD (512 entry in 1 PMD)
for (int i = 0; i < 512; ++i) {
pmd[i] = (uintptr_t) pte[i] | PMD0_ATTR;
}

// set up PTE of normal memory
int i;
for (i = 0; i < 504; ++i) {
for (int j = 0; j < 512; ++j, page_addr += PAGE_SIZE) {
pte[i][j] = page_addr | PTE_NORMAL_ATTR;
}
}

// set up PTE of device memory (0x3F000000~0x3FFFFFFF, 16 MB = 8 PTE = 4096
// page)
for (; i < 512; ++i) {
for (int j = 0; j < 512; ++j, page_addr += PAGE_SIZE) {
pte[i][j] = page_addr | PTE_DEVICE_ATTR;
}
}

asm volatile(
"msr ttbr0_el1, %0\n" // load PGD to the buttom translation based
// register.
"msr ttbr1_el1, %0\n" // also load PGD to the upper translation based
// register.
"mrs %0, sctlr_el1\n"
"orr %0, %0, 1\n"
"msr sctlr_el1, %0" // enable MMU, cache remains disabled
::"r"(pgd));
}

Page table location

  • PGDx1
  • PUDx1
  • PMDx1
  • PTEx512

共 1+1+1+512=515 page table,總大小 515x4KB。放在 kernel image 的最末端。

kernel/linker.ld

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.istack : {
KEEP(*(.istack))
}
.kstack : {
KEEP(*(.kstack))
}
.ustack : {
KEEP(*(.ustack))
}
. = ALIGN(0x1000);
pg_dir = .;
.data.pgd :
{
. += (515 * (1 << 12)); /* (PGD * 1) + (PUD * 1) + (PMD * 1) + (PTE * 512) */
}
. = ALIGN(0x1000);
_end = .;
}