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