RISC-V SMP boot works
[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 static void
11 build_multiboot_info(multiboot_info_t* mbi)
12 {
13         long memsize_kb = mfpcr(PCR_MEMSIZE)*(PGSIZE/1024);
14         long basemem_kb = EXTPHYSMEM/1024;
15
16         memset(mbi, 0, sizeof(mbi));
17
18         mbi->flags = 0x00000001;
19         mbi->mem_lower = basemem_kb;
20         mbi->mem_upper = memsize_kb - basemem_kb;
21 }
22
23 #define KERNSIZE ((uintptr_t)(-KERNBASE))
24
25 pte_t l1pt_boot[NPTENTRIES]
26       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
27 pte_t l1pt[NPTENTRIES]
28       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
29 #ifdef __riscv64
30 pte_t l2pt[NPTENTRIES]
31       __attribute__((section(".data"))) __attribute__((aligned(PGSIZE)));
32 #endif
33
34 #ifdef __riscv64
35 void pagetable_init(pte_t* l1pt_phys, pte_t* l1pt_boot_phys, pte_t* l2pt_phys)
36 #else
37 void pagetable_init(pte_t* l1pt_phys, pte_t* l1pt_boot_phys)
38 #endif
39 {
40         // The boot L1 PT retains the identity mapping [0,KERNSIZE-1],
41         // whereas the post-boot L1 PT does not.
42         for(uintptr_t va = 0; va < KERNSIZE+L1PGSIZE-1; va += L1PGSIZE)
43                 l1pt_boot_phys[L1X(va)] = PTE(LA2PPN(va), PTE_KERN_RW | PTE_E);
44
45         #ifdef __riscv64
46         // for rv64, we need to create an L1 and an L2 PT.
47         
48         // kernel can be mapped by a single L1 page and several L2 pages
49         static_assert(KERNSIZE <= L1PGSIZE);
50         static_assert(KERNBASE % L2PGSIZE == 0);
51
52         // highest L1 page contains KERNBASE mapping
53         l1pt_phys[L1X(KERNBASE)] = l1pt_boot_phys[L1X(KERNBASE)] = PTD(l2pt_phys);
54
55         // KERNBASE mapping with 1GB pages
56         for(uintptr_t va = KERNBASE; va < LOAD_ADDR; va += L2PGSIZE)
57                 l2pt_phys[L2X(va)] = PTE(LA2PPN(va-KERNBASE), PTE_KERN_RW | PTE_E);
58
59         // The kernel code and static data actually are usually not accessed
60         // via the KERNBASE mapping, but rather by an aliased mapping in the
61         // upper 2GB (0xFFFFFFFF80000000 and up).
62         // This simplifies the linking model by making all static addresses
63         // representable in 32 bits.
64         // In RISC-V, this allows static addresses to be loaded with a two
65         // instruction sequence, rather than 8 instructions worst-case.
66         static_assert(LOAD_ADDR % L2PGSIZE == 0);
67         for(uintptr_t va = LOAD_ADDR; va != 0; va += L2PGSIZE)
68                 l2pt_phys[L2X(va)] = PTE(LA2PPN(va-LOAD_ADDR), PTE_KERN_RW|PTE_E);
69         #else
70         // for rv32, just create the L1 page table.
71         static_assert(KERNBASE % L1PGSIZE == 0);
72
73         // KERNBASE mapping
74         for(uintptr_t pa = 0; pa < KERNSIZE; pa += L1PGSIZE)
75         {
76                 pte_t pte = PTE(LA2PPN(pa), PTE_KERN_RW|PTE_E);
77                 l1pt_phys[L1X(KERNBASE+pa)] = pte;
78                 l1pt_boot_phys[L1X(KERNBASE+pa)] = pte;
79         }
80         #endif
81 }
82
83 void
84 mmu_init(pte_t* l1pt_boot_phys)
85 {
86         // load in the boot page table
87         lcr3((uintptr_t)l1pt_boot_phys);
88         mtpcr(PCR_SR, mfpcr(PCR_SR) | SR_VM);
89 }
90
91 void
92 cmain()
93 {
94         multiboot_info_t mbi;
95         build_multiboot_info(&mbi);
96
97         extern void kernel_init(multiboot_info_t *mboot_info);
98         // kernel_init expects a pre-relocation mbi address
99         kernel_init((multiboot_info_t*)((uint8_t*)&mbi - LOAD_ADDR));
100 }