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