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