bf3bb33dd552ff80c73272c1c6bf256ff887854b
[lunaix-os.git] / lunaix-os / arch / x86 / boot / x86_64 / kremap64.c
1 #define __BOOT_CODE__
2
3 #include <lunaix/mm/pagetable.h>
4 #include <lunaix/compiler.h>
5
6 #include <sys/boot/bstage.h>
7 #include <sys/mm/mm_defs.h>
8
9 #define RSVD_PAGES 32
10
11 bridge_farsym(__kexec_start);
12 bridge_farsym(__kexec_end);
13 bridge_farsym(__kexec_text_start);
14 bridge_farsym(__kexec_text_end);
15
16 // define the initial page table layout
17 struct kernel_map;
18
19 static struct kernel_map kpt __section(".kpg");
20 export_symbol(debug, boot, kpt);
21
22 struct kernel_map {
23     pte_t l0t[_PAGE_LEVEL_SIZE];        // root table
24     pte_t l1t_rsvd[_PAGE_LEVEL_SIZE];   // 0~4G reservation
25
26     struct {
27         pte_t _lft[_PAGE_LEVEL_SIZE];
28     } krsvd[RSVD_PAGES];
29 } align(8);
30
31 struct allocator
32 {
33     struct kernel_map* kpt_pa;
34     int pt_usage;
35 };
36
37 static inline ptr_t
38 alloc_rsvd_page(struct allocator* _alloc)
39 {
40     if (_alloc->pt_usage >= KEXEC_RSVD) {
41         asm ("ud2");
42     }
43
44     return __ptr(&_alloc->kpt_pa->krsvd[_alloc->pt_usage++]);
45 }
46
47 static pte_t* boot_text
48 prealloc_pt(struct allocator* _allc, ptr_t va, 
49             pte_attr_t prot, size_t to_gran) 
50 {
51     int lvl_i;
52     pte_t *ptep, pte;
53     size_t gran = L0T_SIZE;
54
55     ptep = (pte_t*)&_allc->kpt_pa->l0t[0];
56
57     for (int i = 0; i < _PTW_LEVEL && gran > to_gran; i++)
58     {
59         lvl_i = va_level_index(va, gran);
60         ptep = &ptep[lvl_i];
61         pte  = pte_at(ptep);
62
63         gran = gran >> _PAGE_LEVEL_SHIFT;
64
65         if (pte_isnull(pte)) {
66             pte = mkpte(alloc_rsvd_page(_allc), KERNEL_DATA);
67             if (to_gran == gran) {
68                 pte = pte_setprot(pte, prot);
69             }
70
71             set_pte(ptep, pte);
72         }
73         ptep = (pte_t*) pte_paddr(pte);
74     }
75
76     return ptep;
77 }
78
79 static void boot_text
80 do_remap()
81 {
82     struct kernel_map* kpt_pa = (struct kernel_map*)to_kphysical(&kpt);
83     
84     pte_t* boot_l0tep   = (pte_t*)  kpt_pa;
85     pte_t *klptep, pte;
86
87     // identity map the first 4G for legacy compatibility
88     pte_t* l1_rsvd = (pte_t*) kpt_pa->l1t_rsvd;
89     pte_t id_map   = pte_mkhuge(mkpte_prot(KERNEL_DATA));
90     
91     set_pte(boot_l0tep, mkpte((ptr_t)l1_rsvd, KERNEL_DATA));
92     
93     for (int i = 0; i < 4; i++, l1_rsvd++)
94     {
95         id_map = pte_setpaddr(id_map, (ptr_t)i << 30);
96         set_pte(l1_rsvd, id_map);
97     }
98
99     // Remap the kernel to -2GiB
100
101     int table_usage = 0;
102     unsigned int lvl_i = 0;
103     struct allocator alloc = {
104         .kpt_pa = kpt_pa,
105         .pt_usage = 0
106     };
107
108     prealloc_pt(&alloc, VMAP, KERNEL_DATA, L1T_SIZE);
109
110     prealloc_pt(&alloc, PG_MOUNT_1, KERNEL_DATA, LFT_SIZE);
111
112
113     ptr_t kstart = page_aligned(__far(__kexec_text_start));
114
115 #if LnT_ENABLED(3)
116     size_t gran  = L3T_SIZE;
117 #else
118     size_t gran  = L2T_SIZE;
119 #endif
120
121     prealloc_pt(&alloc, PMAP, KERNEL_DATA, gran);
122     klptep = prealloc_pt(&alloc, kstart, KERNEL_DATA, gran);
123     klptep += va_level_index(kstart, gran);
124
125     pte = mkpte(0, KERNEL_DATA);
126     for (int i = alloc.pt_usage; i < KEXEC_RSVD; i++)
127     {
128         pte = pte_setpaddr(pte, (ptr_t)&kpt_pa->krsvd[i]);
129         set_pte(klptep++, pte);
130     }
131
132     // this is the first LFT we hooked on.
133     // all these LFT are contig in physical address
134     klptep = (pte_t*) &kpt_pa->krsvd[alloc.pt_usage];
135     
136     // Ensure the size of kernel is within the reservation
137     int remain = KEXEC_RSVD - table_usage;
138     pfn_t kimg_pagecount = 
139         pfn(__far(__kexec_end) - __far(__kexec_start));
140     if (kimg_pagecount > remain * _PAGE_LEVEL_SIZE) {
141         // ERROR: require more pages
142         //  here should do something else other than head into blocking
143         asm("ud2");
144     }
145
146     // kernel .text
147     pfn_t ktext_end = pfn(to_kphysical(__far(__kexec_text_end)));
148     pfn_t i = pfn(to_kphysical(kstart));
149
150     klptep += i;
151     pte = pte_setprot(pte, KERNEL_EXEC);
152     for (; i < ktext_end; i++) {
153         pte = pte_setpaddr(pte, page_addr(i));
154         set_pte(klptep, pte);
155
156         klptep++;
157     }
158     
159     pfn_t kimg_end = pfn(to_kphysical(__far(__kexec_end)));
160
161     // all remaining kernel sections
162     pte = pte_setprot(pte, KERNEL_DATA);
163     for (; i < kimg_end; i++) {
164         pte = pte_setpaddr(pte, page_addr(i));
165         set_pte(klptep, pte);
166
167         klptep++;
168     }
169
170     // Build up self-reference
171     lvl_i = va_level_index(VMS_SELF, L0T_SIZE);
172     pte   = mkpte_root(__ptr(kpt_pa), KERNEL_DATA);
173     set_pte(boot_l0tep + lvl_i, pte);
174 }
175
176
177 ptr_t boot_text
178 remap_kernel()
179 {
180     ptr_t kmap_pa = to_kphysical(&kpt);
181     
182     asm volatile("movq %1, %%rdi\n"
183                  "rep stosb\n" ::"c"(sizeof(kpt)),
184                  "r"(kmap_pa),
185                  "a"(0)
186                  : "rdi", "memory");
187
188     do_remap();
189
190     return kmap_pa;
191 }