Xyris  0.5
stivale2.c
Go to the documentation of this file.
1 /**
2  * @file stivale.cpp
3  * @author Keeton Feavel ([email protected])
4  * @brief Stivale2 bootloader header and stage 1 handler.
5  * @version 0.1
6  * @date 2021-09-11
7  *
8  * @copyright Copyright the Xyris Contributors (c) 2021
9  *
10  */
14 #include <Arch/i686/regs.hpp>
15 #include <Support/sections.hpp>
16 #include <stdint.h>
17 
18 // Use the 32-bit version of stivale2
19 #define _STIVALE2_SPLIT_64
20 #include <stivale/stivale2.h>
21 
22 #define KERNEL_STACK_SZ 4 * ARCH_PAGE_SIZE // stage2 & stage3 (kernel) stack size
23 #define STIVALE2_MAGIC 0x73747632 // "stv2"
24 
25 //-----------------------------------------------
26 // Stage1 variables
27 //-----------------------------------------------
28 
29 // reserve 1024 DWORDs for our page table pointers
30 __attribute__((section(".early_bss"),aligned(4096)))
31 struct Directory pageDirectory;
32 
33 // lowmem identity mappings
34 __attribute__((section(".early_bss"),aligned(4096)))
35 struct Table lowMemoryPageTable;
36 
37 // kernel page table mappings
38 __attribute__((section(".early_bss"),aligned(4096)))
39 struct Table kernelPageTable[2];
40 
41 // page table to hold bootloader structures
42 __attribute__((section(".early_bss"),aligned(4096)))
43 struct Table bootPageTable;
44 
45 // stack for C/C++ runtime
46 __attribute__((section(".early_bss"),aligned(4096)))
47 struct Table stackPageTable;
48 
49 // bootloader info pointer (for preserving when stack changes)
50 __attribute__((section(".early_bss")))
51 struct stivale2_struct *stivale2Info;
52 
53 // stack for stage1 C/C++ runtime
54 __attribute__((section(".early_bss")))
55 uint8_t stage1Stack[KERNEL_STACK_SZ];
56 
57 // stack for stage2 C/C++ runtime
58 __attribute__((section(".bss"),aligned(4)))
59 uint8_t stage2Stack[KERNEL_STACK_SZ];
60 
61 //-----------------------------------------------
62 // Stage1 functions
63 //-----------------------------------------------
64 
65 static void stage1MapBootloader(void);
66 static void stage1MapHighMemory(void);
67 static void stage1MapLowMemory(void);
68 static void stage1Entry(struct stivale2_struct *info);
69 static uint32_t stage1GetBootInfoAddr(struct stivale2_tag* tag);
70 
71 /**
72  * @brief Scan the stivale2 tags to find the reclaimable bootloader
73  * memory map tag that starts at the stivale2 tag base address. Return
74  * the length found. We'll memory map the entire area.
75  *
76  */
77 __attribute__((section(".early_text")))
78 static uint32_t stage1GetBootInfoAddr(struct stivale2_tag* tag)
79 {
80  while (tag) {
81  switch (tag->identifier) {
82  case STIVALE2_STRUCT_TAG_MEMMAP_ID:
83  {
84  struct stivale2_struct_tag_memmap* memmap = (struct stivale2_struct_tag_memmap*)tag;
85  for (size_t i = 0; i < memmap->entries; i++) {
86  switch (memmap->memmap[i].type)
87  {
88  case STIVALE2_MMAP_BOOTLOADER_RECLAIMABLE:
89  if (((uint32_t)memmap->memmap[i].base) == (uint32_t)tag) {
90  return (uint32_t)memmap->memmap[i].length;
91  }
92  break;
93 
94  default:
95  break;
96  }
97  }
98  }
99  default:
100  break;
101  }
102  tag = (struct stivale2_tag*)(uint32_t)tag->next;
103  }
104  // If we get here, there's a problem so we will panic and never return
105  EarlyPanic("Error: Cannot detect bootloader info length!");
106 }
107 
108 /**
109  * @brief Finalize stage1 by loading the finalized page directory
110  * and enabling paging.
111  *
112  */
113 __attribute__((section(".early_text")))
114 static void stage1EnablePaging(void)
115 {
116  setPageDirectory((uintptr_t)&pageDirectory);
117 
118  struct CR0 cr0 = readCR0();
119  cr0.pagingEnable = 1;
120  writeCR0(cr0);
121 }
122 
123 /**
124  * @brief Prepare a higher-half stack for kernel usage.
125  *
126  */
127 __attribute__((section(".early_text"),noreturn))
128 static void stage1JumpToStage2(void)
129 {
130  // zero the kernel BSS (higher half stack)
131  for (size_t i = 0; i < (size_t)&_BSS_SIZE; i++) {
132  ((uint8_t*)&_BSS_START)[i] = 0;
133  }
134 
135  // adjust the stack pointer to use the higher half, kernel stack
136  asm volatile (
137  "movl %0, %%esp\n" // set the stack pointer
138  "xor %%ebp, %%ebp\n" // clear the base pointer
139  "pushl %1\n" // push argument 2 (magic)
140  "pushl %2\n" // push argument 1 (info ptr)
141  "pushl $0\n" // push a null return address
142  "jmp stage2Entry\n" // jump to stage 2
143  : // no output
144  : "i" ((stage2Stack + sizeof(stage2Stack)))
145  , "i" (STIVALE2_MAGIC)
146  , "rm" (stivale2Info)
147  );
148 
149  // it's impossible to return back to this function
150  __builtin_unreachable();
151 }
152 
153 /**
154  * @brief Map bootloader information into the higher half of memory
155  * so that the kernel can access it and parse it.
156  *
157  */
158 __attribute__((section(".early_text")))
159 static void stage1MapBootloader(void)
160 {
161  // Grab the address of the stivale2 boot info tags
162  uint32_t stivale2InfoAddr = (uint32_t)stivale2Info->tags;
163 
164  // Bootloader info page directory
165  uint32_t bootDirectoryEntryIdx = stivale2InfoAddr >> ARCH_PAGE_DIR_ENTRY_SHIFT;
166  struct DirectoryEntry* bootDirectoryEntry = &pageDirectory.entries[bootDirectoryEntryIdx];
167 
168  // Determine if page table needs to be initalized
169  struct Table *bootTable;
170  if (bootDirectoryEntry->present) {
171  bootTable = (struct Table*)(bootDirectoryEntry->tableAddr << ARCH_PAGE_TABLE_ENTRY_SHIFT);
172  } else {
173  bootDirectoryEntry->present = 1;
174  bootDirectoryEntry->readWrite = 1;
175  bootDirectoryEntry->tableAddr = (uint32_t)&bootPageTable >> ARCH_PAGE_TABLE_ENTRY_SHIFT;
176  bootTable = &bootPageTable;
177  }
178 
179  // Get the length of the bootloader information
180  struct stivale2_tag *tags = (struct stivale2_tag *)(uintptr_t)stivale2Info->tags;
181  uint32_t stivale2InfoLength = stage1GetBootInfoAddr(tags);
182  uint32_t stivale2InfoEnd = stivale2InfoAddr + stivale2InfoLength;
183 
184  // Map in the entire bootloader information linked list
185  for (uintptr_t addr = stivale2InfoAddr; addr < stivale2InfoEnd; addr += ARCH_PAGE_SIZE) {
186  size_t bootMemoryIdx = addr >> ARCH_PAGE_TABLE_ENTRY_SHIFT;
187  struct TableEntry* bootTableEntry = &bootTable->pages[bootMemoryIdx & ARCH_PAGE_TABLE_ENTRY_MASK];
188  bootTableEntry->present = 1;
189  bootTableEntry->readWrite = 1;
190  bootTableEntry->frame = bootMemoryIdx;
191  }
192 }
193 
194 /**
195  * @brief Map kernel memory into the higher half of memory
196  * See linker.ld for details on where the kernel should be mapped
197  *
198  */
199 __attribute__((section(".early_text")))
200 static void stage1MapHighMemory(void)
201 {
202  uint32_t kernelDirectoryEntryIdx = (uint32_t)&_KERNEL_START >> ARCH_PAGE_DIR_ENTRY_SHIFT;
203 
204  // First kernel page table entry
205  struct DirectoryEntry* kernelDirectoryEntry = &pageDirectory.entries[kernelDirectoryEntryIdx];
206  kernelDirectoryEntry->present = 1;
207  kernelDirectoryEntry->readWrite = 1;
208  kernelDirectoryEntry->tableAddr = (uint32_t)&kernelPageTable[0] >> ARCH_PAGE_TABLE_ENTRY_SHIFT;
209 
210  // Second kernel page table entry
211  struct DirectoryEntry* pagesDirectoryEntry = kernelDirectoryEntry + 1;
212  pagesDirectoryEntry->present = 1;
213  pagesDirectoryEntry->readWrite = 1;
214  pagesDirectoryEntry->tableAddr = (uint32_t)&kernelPageTable[1] >> ARCH_PAGE_TABLE_ENTRY_SHIFT;
215 
216  for (uintptr_t addr = KERNEL_START; addr < KERNEL_END; addr += ARCH_PAGE_SIZE) {
217  size_t kernelMemoryIdx = addr >> ARCH_PAGE_TABLE_ENTRY_SHIFT;
218  // OR the mask with 0x400 so that pagesDirectoryEntry is used after kernelDirectoryEntry.
219  // This only works because these two pages tables are next to each other in memory.
220  struct TableEntry* kernelMemoryTableEntry = &kernelPageTable[0].pages[kernelMemoryIdx & (0x400 | ARCH_PAGE_TABLE_ENTRY_MASK)];
221  kernelMemoryTableEntry->present = 1;
222  kernelMemoryTableEntry->readWrite = 1;
223  kernelMemoryTableEntry->frame = kernelMemoryIdx - (KERNEL_BASE >> ARCH_PAGE_TABLE_ENTRY_SHIFT);
224  }
225 }
226 
227 /**
228  * @brief identity map from 0x00000000 -> LOWMEM_END
229  *
230  */
231 __attribute__((section(".early_text")))
232 static void stage1MapLowMemory(void)
233 {
234  // No need to get an index here since we're starting at 0x00000000,
235  // which corresponds to the first entry in the page directory.
236  // WARNING: code assumes that the kernel won't be greater than 7MB
237  struct DirectoryEntry* lowMem = &pageDirectory.entries[0];
238  lowMem->present = 1;
239  lowMem->readWrite = 1;
240  lowMem->tableAddr = (uint32_t)&lowMemoryPageTable >> ARCH_PAGE_TABLE_ENTRY_SHIFT;
241 
242  // Map in the entirety of low-memory and stage1
243  for (uintptr_t addr = ARCH_PAGE_SIZE; addr < EARLY_KERNEL_END; addr += ARCH_PAGE_SIZE) {
244  size_t pageIdx = addr >> ARCH_PAGE_TABLE_ENTRY_SHIFT;
245  struct TableEntry* lowMemoryTableEntry = &lowMemoryPageTable.pages[pageIdx & ARCH_PAGE_TABLE_ENTRY_MASK];
246  lowMemoryTableEntry->present = 1;
247  lowMemoryTableEntry->readWrite = 1;
248  lowMemoryTableEntry->frame = pageIdx;
249  }
250 }
251 
252 /**
253  * @brief Stivale2 protocol kernel stage 1 entry. Stage 1 is responsible
254  * for providing an entry point for the bootloader in C, performing any
255  * necessary bootstrappign and then calling into the C++ stage 2.
256  *
257  */
258 __attribute__((section(".early_text")))
259 static void stage1Entry(struct stivale2_struct *info)
260 {
261  // By this point the stivale2 bootloader should have provided us a healthy
262  // stack to use (at the location we specified in stivale2_header_i686 struct)
263  if (!info) {
264  // xyris requires information (such as memory maps, framebuffer location, etc)
265  // from the bootloader, so if that's missing we're already in trouble.
266  EarlyPanic("Error: Missing bootloader information!");
267  }
268 
269  // preserve bootloader information pointer so that when we move the
270  // stack pointer we don't lose the one thing we need to pass into
271  // stage2 / the higher half stack
272  stivale2Info = info;
273 
274  stage1MapLowMemory();
275  stage1MapHighMemory();
276  stage1MapBootloader();
277  stage1EnablePaging();
278  stage1JumpToStage2();
279 }
280 
281 /*
282  * ___ _ _ _ ___ ___ _ _
283  * / __| |_(_)_ ____ _| |___|_ ) / __| |_ _ _ _ _ __| |_ _ _ _ _ ___ ___
284  * \__ \ _| \ V / _` | / -_)/ / \__ \ _| '_| || / _| _| || | '_/ -_|_-<
285  * |___/\__|_|\_/\__,_|_\___/___| |___/\__|_| \_,_\__|\__|\_,_|_| \___/__/
286  *
287  * These structures define what features the Stivale2 compliant bootloader
288  * (such as Limine) should provide for the kernel. Because Stivale2 is made
289  * for 64 bit systems first and foremost, we have to create a "patched" version
290  * of the primary Stivale2 header to properly work for 32 bit, i686, systems
291  */
292 
293 __attribute__((section(".early_data"), used))
294 const struct stivale2_header_tag_framebuffer framebuffer_hdr_tag = {
295  .tag = {
296  .identifier = STIVALE2_HEADER_TAG_FRAMEBUFFER_ID,
297  .next = 0,
298  },
299  .framebuffer_width = 0,
300  .framebuffer_height = 0,
301  .framebuffer_bpp = 0,
302 };
303 
304 __attribute__((section(".stivale2hdr"), used))
305 const struct stivale2_header stivale_hdr = {
306  .entry_point = (uint32_t)stage1Entry,
307  .stack = (uint32_t)stage1Stack + sizeof(stage1Stack),
308  .flags = 0,
309  .tags = (uint32_t)&framebuffer_hdr_tag,
310 };
STIVALE2_MAGIC
#define STIVALE2_MAGIC
Definition: stivale2.c:23
_KERNEL_START
size_t _KERNEL_START
TableEntry::readWrite
uint32_t readWrite
Definition: Memory.i686.hpp:66
DirectoryEntry::readWrite
uint32_t readWrite
Definition: Memory.i686.hpp:96
ARCH_PAGE_TABLE_ENTRY_SHIFT
#define ARCH_PAGE_TABLE_ENTRY_SHIFT
Definition: Memory.i686.hpp:24
ARCH_PAGE_DIR_ENTRY_SHIFT
#define ARCH_PAGE_DIR_ENTRY_SHIFT
Definition: Memory.i686.hpp:23
Directory
Page directory contains pointers to all of the virtual memory addresses for the page tables along wit...
Definition: Memory.i686.hpp:113
CR0::pagingEnable
uint32_t pagingEnable
Definition: regs.hpp:45
DirectoryEntry
Page directory entry structure as defined in accordance to the Intel Developer Manual Vol....
Definition: Memory.i686.hpp:93
EarlyPanic.hpp
Early CGA-mode panic (no framebuffer)
KERNEL_BASE
#define KERNEL_BASE
Definition: sections.hpp:27
TableEntry::frame
uint32_t frame
Definition: Memory.i686.hpp:75
TableEntry
Page table entry defined in accordance to the Intel Developer Manual Vol. 3a p. 4-12.
Definition: Memory.i686.hpp:63
regs.hpp
i686 control register definitions. C & C++ compatible header.
Loader.hpp
_BSS_START
size_t _BSS_START
EARLY_KERNEL_END
#define EARLY_KERNEL_END
Definition: sections.hpp:24
Table
Page table structure as defined in accordance to the Intel Developer Manual Vol. 3a p....
Definition: Memory.i686.hpp:83
sections.hpp
Kernel ELF section definitions.
KERNEL_START
#define KERNEL_START
Definition: sections.hpp:30
_BSS_SIZE
size_t _BSS_SIZE
KERNEL_END
#define KERNEL_END
Definition: sections.hpp:32
KERNEL_STACK_SZ
#define KERNEL_STACK_SZ
Definition: stivale2.c:22
CR0
Definition: regs.hpp:30
ARCH_PAGE_TABLE_ENTRY_MASK
#define ARCH_PAGE_TABLE_ENTRY_MASK
Definition: Memory.i686.hpp:25
Memory.i686.hpp
i686 memory structures and definitions. C & C++ compatible header.
DirectoryEntry::tableAddr
uint32_t tableAddr
Definition: Memory.i686.hpp:104
__attribute__
__attribute__((section(".early_bss"), aligned(4096)))
Scan the stivale2 tags to find the reclaimable bootloader memory map tag that starts at the stivale2 ...
Definition: stivale2.c:30
ARCH_PAGE_SIZE
#define ARCH_PAGE_SIZE
Definition: Memory.i686.hpp:21
TableEntry::present
uint32_t present
Definition: Memory.i686.hpp:65
DirectoryEntry::present
uint32_t present
Definition: Memory.i686.hpp:95