fixes towards risc-v user programs running
[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 #ifdef __riscv64
11 # define MAX_KERNBASE_SIZE L1PGSIZE
12 #else
13 # define MAX_KERNBASE_SIZE ((uintptr_t)(-KERNBASE))
14 #endif
15
16 static void
17 build_multiboot_info(multiboot_info_t* mbi)
18 {
19         long memsize = mfpcr(PCR_MEMSIZE)*PGSIZE;
20         // the current memory mapping requires the kernel be mapped by a single
21         // L2 page table.
22         memsize = MIN(memsize, L1PGSIZE);
23         memsize = ROUNDDOWN(memsize, L2PGSIZE);
24
25         long memsize_kb = memsize/1024;
26         long basemem_kb = EXTPHYSMEM/1024;
27
28         memset(mbi, 0, sizeof(mbi));
29
30         mbi->flags = 0x00000001;
31         mbi->mem_lower = basemem_kb;
32         mbi->mem_upper = memsize_kb - basemem_kb;
33 }
34
35 pte_t l1pt_boot[NPTENTRIES]
36       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
37 pte_t l1pt[NPTENTRIES]
38       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
39 #ifdef __riscv64
40 pte_t l2pt_kernbase[NPTENTRIES]
41       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
42 pte_t l2pt_load[NPTENTRIES]
43       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
44 #endif
45
46 #ifdef __riscv64
47 void pagetable_init(pte_t* l1pt_phys, pte_t* l1pt_boot_phys,
48                     pte_t* l2pt_kernbase_phys, pte_t* l2pt_load_phys)
49 #else
50 void pagetable_init(pte_t* l1pt_phys, pte_t* l1pt_boot_phys)
51 #endif
52 {
53         pte_t perm = PTE_KERN_RW | PTE_E;
54
55         // The boot L1 PT retains the identity mapping [0,MAX_KERNBASE_SIZE-1],
56         // whereas the post-boot L1 PT does not.  This is not a "safe" mapping,
57         // since there may be fewer than MAX_KERNBASE_SIZE bytes of physical memory,
58         // so we'll only use this mapping briefly.
59         for(uintptr_t va = 0; va < MAX_KERNBASE_SIZE+L1PGSIZE-1; va += L1PGSIZE)
60                 l1pt_boot_phys[L1X(va)] = PTE(LA2PPN(va), perm);
61
62         #ifdef __riscv64
63         // kernel can be mapped by a single L2 PT.
64         static_assert(L1X(KERNBASE) == L1X(KERNBASE+MAX_KERNBASE_SIZE-1));
65         static_assert(MAX_KERNBASE_SIZE % L2PGSIZE == 0);
66         static_assert(KERNBASE % L2PGSIZE == 0);
67
68         // KERNBASE mapping uses one L1 PTD -> L2 PT
69         l1pt_phys[L1X(KERNBASE)]      = PTD(l2pt_kernbase_phys);
70         l1pt_boot_phys[L1X(KERNBASE)] = PTD(l2pt_kernbase_phys);
71
72         // don't actually map all the way up to MAX_KERNBASE_SIZE, just to phys. mem size
73         uintptr_t memsize = ROUNDDOWN(mfpcr(PCR_MEMSIZE)*PGSIZE, L2PGSIZE);
74         for(uintptr_t va = KERNBASE; va < KERNBASE+memsize; va += L2PGSIZE)
75                 l2pt_kernbase_phys[L2X(va)] = PTE(LA2PPN(va-KERNBASE), perm);
76
77         // The kernel code and static data actually are usually not accessed
78         // via the KERNBASE mapping, but rather by an aliased "load" mapping in
79         // the upper 2GB (0xFFFFFFFF80000000 and up).
80         // This simplifies the linking model by making all static addresses
81         // representable in 32 bits.
82         // In RISC-V, this allows static addresses to be loaded with a two
83         // instruction sequence, rather than 8 instructions worst-case.
84         static_assert(L1X(KERN_LOAD_ADDR) > L1X(KERNBASE));
85         static_assert(KERN_LOAD_ADDR % L2PGSIZE == 0);
86         static_assert((uintptr_t)(-KERN_LOAD_ADDR) <= L1PGSIZE);
87
88         l1pt_phys[L1X(KERN_LOAD_ADDR)]      = PTD(l2pt_load_phys);
89         l1pt_boot_phys[L1X(KERN_LOAD_ADDR)] = PTD(l2pt_load_phys);
90
91         for(uintptr_t va = KERN_LOAD_ADDR; va != 0; va += L2PGSIZE)
92                 l2pt_load_phys[L2X(va)] = PTE(LA2PPN(va-KERN_LOAD_ADDR), perm);
93         #else
94         // for rv32, just create the L1 page table.
95         static_assert(KERNBASE % L1PGSIZE == 0);
96
97         // KERNBASE mapping
98         for(uintptr_t pa = 0; pa < MAX_KERNBASE_SIZE; pa += L1PGSIZE)
99         {
100                 pte_t pte = PTE(LA2PPN(pa), perm);
101                 l1pt_phys[L1X(KERNBASE+pa)] = pte;
102                 l1pt_boot_phys[L1X(KERNBASE+pa)] = pte;
103         }
104         #endif
105 }
106
107 void
108 mmu_init(pte_t* l1pt_boot_phys)
109 {
110         // load in the boot page table
111         lcr3((uintptr_t)l1pt_boot_phys);
112         mtpcr(PCR_SR, mfpcr(PCR_SR) | SR_VM);
113 }
114
115 void
116 cmain()
117 {
118         multiboot_info_t mbi;
119         build_multiboot_info(&mbi);
120
121         extern void kernel_init(multiboot_info_t *mboot_info);
122         // kernel_init expects a pre-relocation mbi address
123         kernel_init((multiboot_info_t*)PADDR(&mbi));
124 }