feat: a better boot command line parser
[lunaix-os.git] / lunaix-os / arch / i386 / boot / kpt_setup.c
1 #define __BOOT_CODE__
2
3 #include <lunaix/mm/page.h>
4
5 #include <sys/boot/bstage.h>
6 #include <sys/mm/mempart.h>
7
8 #define PT_ADDR(ptd, pt_index) ((ptd_t*)ptd + (pt_index + 1) * 1024)
9 #define SET_PDE(ptd, pde_index, pde) *((ptd_t*)ptd + pde_index) = pde;
10 #define SET_PTE(ptd, pt_index, pte_index, pte)                                 \
11     *(PT_ADDR(ptd, pt_index) + pte_index) = pte;
12 #define sym_val(sym) (ptr_t)(&sym)
13
14 #define KERNEL_PAGE_COUNT                                                      \
15     ((sym_val(__kexec_end) - sym_val(__kexec_start) + 0x1000 - 1) >> 12);
16 #define HHK_PAGE_COUNT                                                         \
17     ((sym_val(__kexec_boot_end) - 0x100000 + 0x1000 - 1) >> 12)
18
19 #define V2P(vaddr) ((ptr_t)(vaddr)-KERNEL_EXEC)
20
21 // use table #1
22 #define PG_TABLE_IDENTITY 0
23
24 // use table #2-8
25 // hence the max size of kernel is 8MiB
26 #define PG_TABLE_KERNEL 1
27
28 // use table #9
29 #define PG_TABLE_STACK 8
30
31 // Provided by linker (see linker.ld)
32 extern u8_t __kexec_start;
33 extern u8_t __kexec_end;
34 extern u8_t __kexec_text_start;
35 extern u8_t __kexec_text_end;
36
37 extern u8_t __kexec_boot_end;
38
39 void boot_text
40 _init_page(x86_page_table* ptd)
41 {
42     ptd->entry[0] = NEW_L1_ENTRY(PG_PREM_RW, (ptd_t*)ptd + PG_MAX_ENTRIES);
43
44     // 对低1MiB空间进行对等映射(Identity
45     // mapping),也包括了我们的VGA,方便内核操作。
46     x86_page_table* id_pt =
47       (x86_page_table*)GET_PG_ADDR(ptd->entry[PG_TABLE_IDENTITY]);
48
49     for (u32_t i = 0; i < 256; i++) {
50         id_pt->entry[i] = NEW_L2_ENTRY(PG_PREM_RW, (i << PG_SIZE_BITS));
51     }
52
53     // 对等映射我们的hhk_init,这样一来,当分页与地址转换开启后,我们依然能够照常执行最终的
54     // jmp 指令来跳转至
55     //  内核的入口点
56     for (u32_t i = 0; i < HHK_PAGE_COUNT; i++) {
57         id_pt->entry[256 + i] =
58           NEW_L2_ENTRY(PG_PREM_RW, 0x100000 + (i << PG_SIZE_BITS));
59     }
60
61     // --- 将内核重映射至高半区 ---
62
63     // 这里是一些计算,主要是计算应当映射进的 页目录 与 页表 的条目索引(Entry
64     // Index)
65     u32_t kernel_pde_index = L1_INDEX(sym_val(__kexec_start));
66     u32_t kernel_pte_index = L2_INDEX(sym_val(__kexec_start));
67     u32_t kernel_pg_counts = KERNEL_PAGE_COUNT;
68
69     // 将内核所需要的页表注册进页目录
70     //  当然,就现在而言,我们的内核只占用不到50个页(每个页表包含1024个页)
71     //  这里分配了3个页表(12MiB),未雨绸缪。
72     for (u32_t i = 0; i < PG_TABLE_STACK - PG_TABLE_KERNEL; i++) {
73         ptd->entry[kernel_pde_index + i] =
74           NEW_L1_ENTRY(PG_PREM_RW, PT_ADDR(ptd, PG_TABLE_KERNEL + i));
75     }
76
77     // 首先,检查内核的大小是否可以fit进我们这几个表(12MiB)
78     if (kernel_pg_counts >
79         (PG_TABLE_STACK - PG_TABLE_KERNEL) * PG_MAX_ENTRIES) {
80         // ERROR: require more pages
81         //  here should do something else other than head into blocking
82         asm("ud2");
83     }
84
85     // 计算内核.text段的物理地址
86     ptr_t kernel_pm = V2P(&__kexec_start);
87     ptr_t ktext_start = V2P(&__kexec_text_start);
88     ptr_t ktext_end = V2P(&__kexec_text_end);
89
90     // 重映射内核至高半区地址(>=0xC0000000)
91     for (u32_t i = 0; i < kernel_pg_counts; i++) {
92         ptr_t paddr = kernel_pm + (i << PG_SIZE_BITS);
93         u32_t flags = PG_PREM_RW;
94
95         if (paddr >= ktext_start && paddr <= ktext_end) {
96             flags = PG_PREM_R;
97         }
98
99         SET_PTE(ptd,
100                 PG_TABLE_KERNEL,
101                 kernel_pte_index + i,
102                 NEW_L2_ENTRY(flags, paddr))
103     }
104
105     // 最后一个entry用于循环映射
106     ptd->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, ptd);
107 }
108
109 void boot_text
110 kpg_init(x86_page_table* ptd, u32_t kpg_size)
111 {
112
113     // 初始化 kpg 全为0
114     //      P.s. 真没想到GRUB会在这里留下一堆垃圾! 老子的页表全乱套了!
115     u8_t* kpg = (u8_t*)ptd;
116     for (u32_t i = 0; i < kpg_size; i++) {
117         *(kpg + i) = 0;
118     }
119
120     _init_page(ptd);
121 }