new 64b kernel memory map (not userspace yet)
[akaros.git] / kern / arch / riscv / cboot.c
1 #include <multiboot.h>
2 #include <ros/memlayout.h>
3 #include <arch/arch.h>
4 #include <arch/mmu.h>
5 #include <string.h>
6 #include <assert.h>
7 #include <stdio.h>
8 #include <pmap.h>
9
10 #define KERNSIZE L1PGSIZE
11
12 static void
13 build_multiboot_info(multiboot_info_t* mbi)
14 {
15         long memsize = mfpcr(PCR_MEMSIZE)*PGSIZE;
16         // the current memory mapping requires the kernel be mapped by a single
17         // L2 page table.
18         memsize = MIN(memsize, L1PGSIZE);
19         memsize = ROUNDDOWN(memsize, L2PGSIZE);
20
21         long memsize_kb = memsize/1024;
22         long basemem_kb = EXTPHYSMEM/1024;
23
24         memset(mbi, 0, sizeof(mbi));
25
26         mbi->flags = 0x00000001;
27         mbi->mem_lower = basemem_kb;
28         mbi->mem_upper = memsize_kb - basemem_kb;
29 }
30
31 pte_t l1pt_boot[NPTENTRIES]
32       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
33 pte_t l1pt[NPTENTRIES]
34       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
35 #ifdef __riscv64
36 pte_t l2pt_kernbase[NPTENTRIES]
37       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
38 pte_t l2pt_load[NPTENTRIES]
39       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
40 #endif
41
42 #ifdef __riscv64
43 void pagetable_init(pte_t* l1pt_phys, pte_t* l1pt_boot_phys,
44                     pte_t* l2pt_kernbase_phys, pte_t* l2pt_load_phys)
45 #else
46 void pagetable_init(pte_t* l1pt_phys, pte_t* l1pt_boot_phys)
47 #endif
48 {
49         // The boot L1 PT retains the identity mapping [0,KERNSIZE-1],
50         // whereas the post-boot L1 PT does not.
51         for(uintptr_t va = 0; va < KERNSIZE+L1PGSIZE-1; va += L1PGSIZE)
52                 l1pt_boot_phys[L1X(va)] = PTE(LA2PPN(va), PTE_KERN_RW | PTE_E);
53
54         #ifdef __riscv64
55         // for rv64, we need to create an L1 and an L2 PT.
56         
57         // kernel can be mapped by a single L2 PT.
58         static_assert(L1X(KERNBASE) == L1X(KERNBASE+KERNSIZE-1));
59         static_assert(KERNSIZE % L2PGSIZE == 0);
60         static_assert(KERNBASE % L2PGSIZE == 0);
61
62         // KERNBASE mapping uses one L1 PTD -> L2 PT
63         l1pt_phys[L1X(KERNBASE)]      = PTD(l2pt_kernbase_phys);
64         l1pt_boot_phys[L1X(KERNBASE)] = PTD(l2pt_kernbase_phys);
65
66         for(uintptr_t va = KERNBASE; va < KERNBASE+KERNSIZE; va += L2PGSIZE)
67                 l2pt_kernbase_phys[L2X(va)] = PTE(LA2PPN(va-KERNBASE), PTE_KERN_RW | PTE_E);
68
69         // The kernel code and static data actually are usually not accessed
70         // via the KERNBASE mapping, but rather by an aliased "load" mapping in
71         // the upper 2GB (0xFFFFFFFF80000000 and up).
72         // This simplifies the linking model by making all static addresses
73         // representable in 32 bits.
74         // In RISC-V, this allows static addresses to be loaded with a two
75         // instruction sequence, rather than 8 instructions worst-case.
76         static_assert(L1X(KERN_LOAD_ADDR) > L1X(KERNBASE));
77         static_assert(KERN_LOAD_ADDR % L2PGSIZE == 0);
78         static_assert((uintptr_t)(-KERN_LOAD_ADDR) <= L1PGSIZE);
79
80         l1pt_phys[L1X(KERN_LOAD_ADDR)]      = PTD(l2pt_load_phys);
81         l1pt_boot_phys[L1X(KERN_LOAD_ADDR)] = PTD(l2pt_load_phys);
82
83         for(uintptr_t va = KERN_LOAD_ADDR; va != 0; va += L2PGSIZE)
84                 l2pt_load_phys[L2X(va)] = PTE(LA2PPN(va-KERN_LOAD_ADDR), PTE_KERN_RW|PTE_E);
85         #else
86         // for rv32, just create the L1 page table.
87         static_assert(KERNBASE % L1PGSIZE == 0);
88
89         // KERNBASE mapping
90         for(uintptr_t pa = 0; pa < KERNSIZE; pa += L1PGSIZE)
91         {
92                 pte_t pte = PTE(LA2PPN(pa), PTE_KERN_RW|PTE_E);
93                 l1pt_phys[L1X(KERNBASE+pa)] = pte;
94                 l1pt_boot_phys[L1X(KERNBASE+pa)] = pte;
95         }
96         #endif
97 }
98
99 void
100 mmu_init(pte_t* l1pt_boot_phys)
101 {
102         // load in the boot page table
103         lcr3((uintptr_t)l1pt_boot_phys);
104         mtpcr(PCR_SR, mfpcr(PCR_SR) | SR_VM);
105 }
106
107 void
108 cmain()
109 {
110         multiboot_info_t mbi;
111         build_multiboot_info(&mbi);
112
113         extern void kernel_init(multiboot_info_t *mboot_info);
114         // kernel_init expects a pre-relocation mbi address
115         kernel_init((multiboot_info_t*)PADDR(&mbi));
116 }