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