diff --git a/STIVALE.md b/STIVALE.md index adfa8ff9..be40b28a 100644 --- a/STIVALE.md +++ b/STIVALE.md @@ -14,7 +14,7 @@ stivale will recognise whether the ELF file is 32-bit or 64-bit and load the ker into the appropriate CPU mode. stivale natively supports (only for 64-bit kernels) and encourages higher half kernels. -The kernel can load itself at `0xffffffff80100000` (as defined in the linker script) +The kernel can load itself at `0xffffffff80100000` or higher (as defined in the linker script) and the bootloader will take care of everything, no AT linker script directives needed. If the kernel loads itself in the lower half (`0x100000` or higher), the bootloader @@ -36,15 +36,17 @@ The kernel MUST NOT request to load itself at an address lower than `0x100000` field in the stivale header is set to a non-0 value, in which case, it is set to the value of `entry_point`. -At entry, the bootloader will have setup paging such that there is a 4GiB identity -mapped block of memory at `0x0000000000000000`, a 2GiB mapped area of memory -that maps from `0x0000000000000000` physical to `0x0000000080000000` physical -to `0xffffffff80000000` virtual. This area is for the higher half kernels. -Further more, a 4GiB area of memory from `0x0000000000000000` physical to -`0x0000000100000000` physical to `0xffff800000000000` virtual is mapped. +At entry, the bootloader will have setup paging mappings as such: + +``` + Base Physical Address - Top Physical Address -> Virtual address + 0x0000000000000000 - 0x0000000100000000 -> 0x0000000000000000 + 0x0000000000000000 - 0x0000000100000000 -> 0xffff800000000000 + 0x0000000000000000 - 0x0000000080000000 -> 0xffffffff80000000 +``` If the kernel is dynamic and not statically linked, the bootloader will relocate it. -Furthermore if bit 2 of the flags field in the stivale header is set, the bootloader +Furthermore if bit 0 of the flags field in the stivale header is set, the bootloader will perform kernel address space layout randomisation (KASLR). The kernel should NOT modify the bootloader page tables, and it should only use them @@ -62,8 +64,8 @@ IF flag, VM flag, and direction flag are cleared on entry. Other flags undefined PG is enabled (`cr0`), PE is enabled (`cr0`), PAE is enabled (`cr4`), LME is enabled (`EFER`). -If stivale header flag bit 1 is set, then, if available, 5-level paging is enabled -(LA57 bit in `cr4`). +If the stivale header tag for 5-level paging is present, then, if available, +5-level paging is enabled (LA57 bit in `cr4`). The A20 gate is enabled. @@ -112,57 +114,138 @@ the header that the bootloader will parse. Said header looks like this: ```c struct stivale_header { - uint64_t stack; // This is the stack address which will be in RSP - // when the kernel is loaded. + uint64_t entry_point; // If not 0, this address will be jumped to as the + // entry point of the kernel. + // If set to 0, the ELF entry point will be used + // instead. - uint16_t flags; // Flags - // bit 0 0 = text mode, 1 = graphics framebuffer mode - // bit 1 0 = 4-level paging, 1 = use 5-level paging (if - available) - Ignored if booting a 32-bit kernel. - // bit 2 0 = Disable KASLR, 1 = enable KASLR (up to 1GB slide) - Ignored if booting a 32-bit or non-relocatable kernel - // All other bits undefined. + uint64_t stack; // This is the stack address which will be in RSP + // when the kernel is loaded. + // It can be set to a non-valid stack address such as 0 + // as long as the OS is 64-bit and sets up a stack on its + // own. - uint16_t framebuffer_width; // These 3 values are parsed if a graphics mode - uint16_t framebuffer_height; // is requested. If all values are set to 0 - uint16_t framebuffer_bpp; // then the bootloader will pick the best possible - // video mode automatically (recommended). - uint64_t entry_point; // If not 0, this field will be jumped to at entry - // instead of the ELF entry point. + uint64_t flags; // Bit 0: if 1, enable KASLR + // All other bits undefined + + uint64_t tags; // Pointer to the first of the linked list of tags. + // see "stivale header tags" section. + // NULL = no tags. } __attribute__((packed)); ``` +### stivale header tags + +The stivale header uses a mechanism to avoid having protocol versioning, but +rather, feature-specific support detection. + +The kernel executable provides the bootloader with a linked list of structures, +the first of which is pointed to by the `tags` entry of the stivale header. + +Each tag shall contain these 2 fields: +```c +struct stivale_hdr_tag { + uint64_t identifier; + uint64_t next; +} __attribute__((packed)); + +``` + +The `identifier` field identifies what feature the tag is requesting from the +bootloader. + +The `next` field points to another tag in the linked list. A NULL value determines +the end of the linked list. + +Tag structures can have more than just these 2 members, but these 2 members MUST +appear at the beginning of any given tag. + +Tags can have no extra members and just serve as "flags" to enable some behaviour +that does not require extra parameters. + +#### Framebuffer header tag + +This tag asks the stivale-compliant bootloader to initialise a graphical framebuffer +video mode. +Omitting this tag will make the bootloader default to a CGA-compatible text mode, +if supported. + +```c +struct stivale_hdr_tag_framebuffer { + uint64_t identifier; // Identifier: 0x3ecc1bc43d0f7971 + uint64_t next; + uint16_t framebuffer_width; // If all values are set to 0 + uint16_t framebuffer_height; // then the bootloader will pick the best possible + uint16_t framebuffer_bpp; // video mode automatically. +} __attribute__((packed)); +``` + +#### 5-level paging header tag + +The presence of this tag enables support for 5-level paging, if available. + +Identifier: `0x932f477032007e8f` + +This tag does not have extra members. + ## stivale structure The stivale structure returned by the bootloader looks like this: ```c struct stivale_struct { - uint64_t cmdline; // Pointer to a null-terminated cmdline - uint64_t memory_map_addr; // Pointer to the memory map (entries described below) - uint64_t memory_map_entries; // Count of memory map entries - uint64_t framebuffer_addr; // Address of the framebuffer and related info - uint16_t framebuffer_pitch; - uint16_t framebuffer_width; - uint16_t framebuffer_height; - uint16_t framebuffer_bpp; - uint64_t rsdp; // Pointer to the ACPI RSDP structure - uint64_t module_count; // Count of modules that stivale loaded according to config - uint64_t modules; // Pointer to the first entry in the linked list of modules (described below) - uint64_t epoch; // UNIX epoch at boot, read from system RTC - uint64_t flags; // Flags - // bit 0: 1 if booted with BIOS, 0 if booted with UEFI - // All other bits undefined. + char bootloader_brand[64]; // Bootloader null-terminated brand string + char bootloader_version[64]; // Bootloader null-terminated version string + + uint64_t tags; // Pointer to the first of the linked list of tags. + // see "stivale structure tags" section. + // NULL = no tags. } __attribute__((packed)); ``` -## Memory map entry +### stivale structure tags + +These tags work *very* similarly to the header tags, with the main difference being +that these tags are returned to the kernel by the bootloader, instead. + +See "stivale header tags". + +The kernel is responsible for parsing the tags and the identifiers, and interpreting +the tags that it supports, while handling in a graceful manner the tags it does not +recognise. + +#### Command line structure tag + +This tag reports to the kernel the command line string that was passed to it by +the bootloader. ```c -struct mmap_entry { +struct stivale_struct_tag_cmdline { + uint64_t identifier; // Identifier: 0xe5e76a1b4597a781 + uint64_t next; + uint64_t cmdline; // Pointer to a null-terminated cmdline +} __attribute__((packed)); +``` + +#### Memory map structure tag + +This tag reports to the kernel the memory map built by the bootloader. + +```c +struct stivale_struct_tag_memmap { + uint64_t identifier; // Identifier: 0x2187f79e8612de07 + uint64_t next; + uint64_t entries; // Count of memory map entries + struct stivale_mmap_entry memmap[]; // Array of memory map entries +} __attribute__((packed)); +``` + +###### Memory map entry + +```c +struct stivale_mmap_entry { uint64_t base; // Base of the memory section uint64_t length; // Length of the section - uint32_t type; // Type (described below) + enum stivale_mmap_type type; // Type (described below) uint32_t unused; } __attribute__((packed)); ``` @@ -170,18 +253,21 @@ struct mmap_entry { `type` is an enumeration that can have the following values: ``` -1 - Usable RAM -2 - Reserved -3 - ACPI reclaimable -4 - ACPI NVS -5 - Bad memory -10 - Kernel/Modules +enum stivale_mmap_type : uint32_t { + USABLE = 1, + RESERVED = 2, + ACPI_RECLAIMABLE = 3, + ACPI_NVS = 4, + BAD_MEMORY = 5, + BOOTLOADER_RECLAIMABLE = 0x1000, + KERNEL_AND_MODULES = 0x1001 +}; ``` All other values are undefined. The kernel and modules loaded **are not** marked as usable memory. They are marked -as Kernel/Modules (type 10). +as Kernel/Modules (type 0x1001). Usable RAM chunks are guaranteed to be 4096 byte aligned for both base and length. @@ -192,17 +278,75 @@ Usable RAM chunks are guaranteed not to overlap with any other entry. To the contrary, all non-usable RAM chunks are not guaranteed any alignment, nor is it guaranteed that they do not overlap each other (except usable RAM). -## Modules +#### Framebuffer structure tag + +This tag reports to the kernel the currently set up framebuffer details, if any. + +```c +struct stivale_struct_tag_framebuffer { + uint64_t identifier; // Identifier: 0x506461d2950408fa + uint64_t next; + uint64_t framebuffer_addr; // Address of the framebuffer and related info + uint16_t framebuffer_width; + uint16_t framebuffer_height; + uint16_t framebuffer_pitch; + uint16_t framebuffer_bpp; +} __attribute__((packed)); +``` + +#### Modules structure tag + +This tag lists modules that the bootloader loaded alongside the kernel, if any. + +```c +struct stivale_struct_tag_modules { + uint64_t identifier; // Identifier: 0x4b6fe466aade04ce + uint64_t next; + uint64_t module_count; // Count of loaded modules + struct stivale_module modules[]; // Array of module descriptors +} __attribute__((packed)); +``` -The `modules` variable points to the first entry of the linked list of module -structures. -A module structure looks like this: ```c struct stivale_module { uint64_t begin; // Address where the module is loaded uint64_t end; // End address of the module - char string[128]; // String passed to the module (by config file) - uint64_t next; // Pointer to the next module (if any), check module_count - // in the stivale_struct + char string[128]; // 0-terminated string passed to the module +} __attribute__((packed)); +``` + +#### RSDP structure tag + +This tag reports to the kernel the location of the ACPI RSDP structure in memory. + +```c +struct stivale_struct_tag_rsdp { + uint64_t identifier; // Identifier: 0x9e1786930a375e78 + uint64_t next; + uint64_t rsdp; // Pointer to the ACPI RSDP structure +} __attribute__((packed)); +``` + +#### Epoch structure tag + +This tag reports to the kernel the current UNIX epoch, as per RTC. + +```c +struct stivale_struct_tag_epoch { + uint64_t identifier; // Identifier: 0x566a7bed888e1407 + uint64_t next; + uint64_t epoch; // UNIX epoch at boot, read from system RTC +} __attribute__((packed)); +``` + +#### Firmware structure tag + +This tag reports to the kernel info about the firmware. + +```c +struct stivale_struct_tag_firmware { + uint64_t identifier; // Identifier: 0x359d837855e3858c + uint64_t next; + uint64_t flags; // Bit 0: 0 = UEFI, 1 = BIOS } __attribute__((packed)); ``` diff --git a/qloader2.bin b/qloader2.bin index 9e32a7dc..2bc87efe 100644 Binary files a/qloader2.bin and b/qloader2.bin differ diff --git a/src/bootsect/bootsect.asm b/src/bootsect/bootsect.asm index 9cde36a2..cd6f862e 100644 --- a/src/bootsect/bootsect.asm +++ b/src/bootsect/bootsect.asm @@ -103,7 +103,10 @@ stage2: mov gs, ax mov ss, ax - jmp 0x8000 + and edx, 0xff + push edx + + call 0x8000 bits 16 %include 'a20_enabler.inc' diff --git a/src/lib/elf.c b/src/lib/elf.c index cf21c35d..35a9378d 100644 --- a/src/lib/elf.c +++ b/src/lib/elf.c @@ -271,8 +271,6 @@ int elf32_load_section(struct file_handle *fd, void *buffer, const char *name, s return 2; } -#define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000) - int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uint64_t slide) { struct elf64_hdr hdr; fread(fd, &hdr, 0, sizeof(struct elf64_hdr)); diff --git a/src/lib/elf.h b/src/lib/elf.h index 8f146def..4b4b1b6a 100644 --- a/src/lib/elf.h +++ b/src/lib/elf.h @@ -4,6 +4,8 @@ #include #include +#define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000) + int elf_bits(struct file_handle *fd); int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uint64_t slide); diff --git a/src/lib/memmap.c b/src/lib/memmap.c index 0d3b69ec..d4da5f96 100644 --- a/src/lib/memmap.c +++ b/src/lib/memmap.c @@ -10,22 +10,32 @@ #define MEMMAP_BASE ((size_t)0x100000) #define MEMMAP_MAX_ENTRIES 256 +#define MEMMAP_USABLE 1 +#define MEMMAP_RESERVED 2 +#define MEMMAP_ACPI_RECLAIMABLE 3 +#define MEMMAP_ACPI_NVS 4 +#define MEMMAP_BAD_MEMORY 5 +#define MEMMAP_BOOTLOADER_RECLAIMABLE 0x1000 +#define MEMMAP_KERNEL_AND_MODULES 0x1001 + static struct e820_entry_t memmap[MEMMAP_MAX_ENTRIES]; static size_t memmap_entries = 0; static const char *memmap_type(uint32_t type) { switch (type) { - case 1: + case MEMMAP_USABLE: return "Usable RAM"; - case 2: + case MEMMAP_RESERVED: return "Reserved"; - case 3: + case MEMMAP_ACPI_RECLAIMABLE: return "ACPI reclaimable"; - case 4: + case MEMMAP_ACPI_NVS: return "ACPI NVS"; - case 5: + case MEMMAP_BAD_MEMORY: return "Bad memory"; - case 10: + case MEMMAP_BOOTLOADER_RECLAIMABLE: + return "Bootloader reclaimable"; + case MEMMAP_KERNEL_AND_MODULES: return "Kernel/Modules"; default: return "???"; @@ -196,7 +206,7 @@ void memmap_alloc_range(uint64_t base, uint64_t length) { } target = &memmap[memmap_entries++]; - target->type = 10; + target->type = MEMMAP_KERNEL_AND_MODULES; target->base = base; target->length = length; diff --git a/src/main.c b/src/main.c index 1f0a78ab..b83e3103 100644 --- a/src/main.c +++ b/src/main.c @@ -1,19 +1,17 @@ asm ( ".section .entry\n\t" - "xor dh, dh\n\t" - "push edx\n\t" // Zero out .bss "xor al, al\n\t" - "lea edi, bss_begin\n\t" - "lea ecx, bss_end\n\t" - "lea edx, bss_begin\n\t" - "sub ecx, edx\n\t" + "mov edi, OFFSET bss_begin\n\t" + "mov ecx, OFFSET bss_end\n\t" + "sub ecx, OFFSET bss_begin\n\t" "rep stosb\n\t" - "call main\n\t" + "jmp main\n\t" ); +#include #include #include #include @@ -26,6 +24,7 @@ asm ( #include #include #include +#include #include #include #include @@ -35,7 +34,7 @@ void main(int boot_drive) { // Initial prompt. init_vga_textmode(); - print("qloader2\n\n"); + print("qloader2 " QLOADER2_VERSION "\n\n"); print("Boot drive: %x\n", boot_drive); @@ -73,6 +72,8 @@ void main(int boot_drive) { if (!strcmp(proto, "stivale")) { stivale_load(cmdline, boot_drive); + } else if (!strcmp(proto, "stivale2")) { + stivale2_load(cmdline, boot_drive); } else if (!strcmp(proto, "linux")) { linux_load(cmdline, boot_drive); } else if (!strcmp(proto, "templeos")) { diff --git a/src/menu.c b/src/menu.c index 8930fff1..5ee2a762 100644 --- a/src/menu.c +++ b/src/menu.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -31,11 +32,7 @@ char *menu(void) { refresh: text_clear(); - print("\n"); - print(" \e[44m \e[40m\n"); - print(" \e[44m qloader\e[33m2\e[37m \e[40m\n"); - print(" \e[44m \e[40m\n"); - print("\n"); + print("\n\n \e[44m qloader\e[33m2\e[37m " QLOADER2_VERSION " \e[40m\n\n\n"); print("Select an entry:\n\n"); diff --git a/src/protos/stivale2.c b/src/protos/stivale2.c new file mode 100644 index 00000000..b9360dfd --- /dev/null +++ b/src/protos/stivale2.c @@ -0,0 +1,575 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct stivale2_tag { + uint64_t identifier; + uint64_t next; +} __attribute__((packed)); + +struct stivale2_header { + uint64_t entry_point; + uint64_t stack; + uint64_t flags; + uint64_t tags; +} __attribute__((packed)); + +#define STIVALE2_HDR_TAG_FRAMEBUFFER_ID 0x3ecc1bc43d0f7971 + +struct stivale2_hdr_tag_framebuffer { + struct stivale2_tag tag; + uint16_t framebuffer_width; + uint16_t framebuffer_height; + uint16_t framebuffer_bpp; +} __attribute__((packed)); + +#define STIVALE2_HDR_TAG_5LV_PAGING_ID 0x932f477032007e8f + +struct stivale2_struct { + char bootloader_brand[64]; + char bootloader_version[64]; + uint64_t tags; +} __attribute__((packed)); + +#define STIVALE2_STRUCT_TAG_CMDLINE_ID 0xe5e76a1b4597a781 + +struct stivale2_struct_tag_cmdline { + struct stivale2_tag tag; + uint64_t cmdline; +} __attribute__((packed)); + +#define STIVALE2_STRUCT_TAG_MEMMAP_ID 0x2187f79e8612de07 + +struct stivale2_mmap_entry { + uint64_t base; + uint64_t length; + uint32_t type; + uint32_t unused; +} __attribute__((packed)); + +struct stivale2_struct_tag_memmap { + struct stivale2_tag tag; + uint64_t entries; + uint64_t memmap; +} __attribute__((packed)); + +#define STIVALE2_STRUCT_TAG_FRAMEBUFFER_ID 0x506461d2950408fa + +struct stivale2_struct_tag_framebuffer { + struct stivale2_tag tag; + uint64_t framebuffer_addr; + uint16_t framebuffer_width; + uint16_t framebuffer_height; + uint16_t framebuffer_pitch; + uint16_t framebuffer_bpp; +} __attribute__((packed)); + +#define STIVALE2_STRUCT_TAG_MODULES_ID 0x4b6fe466aade04ce + +struct stivale2_module { + uint64_t begin; + uint64_t end; + char string[128]; +} __attribute__((packed)); + +struct stivale2_struct_tag_modules { + struct stivale2_tag tag; + uint64_t module_count; + struct stivale2_module modules[]; +} __attribute__((packed)); + +#define STIVALE2_STRUCT_TAG_RSDP_ID 0x9e1786930a375e78 + +struct stivale2_struct_tag_rsdp { + struct stivale2_tag tag; + uint64_t rsdp; +} __attribute__((packed)); + +#define STIVALE2_STRUCT_TAG_EPOCH_ID 0x566a7bed888e1407 + +struct stivale2_struct_tag_epoch { + struct stivale2_tag tag; + uint64_t epoch; +} __attribute__((packed)); + +#define STIVALE2_STRUCT_TAG_FIRMWARE_ID 0x359d837855e3858c + +struct stivale2_struct_tag_firmware { + struct stivale2_tag tag; + uint64_t flags; +} __attribute__((packed)); + +#define KASLR_SLIDE_BITMASK 0x03FFFF000u + +struct stivale2_struct stivale2_struct = {0}; + +inline static size_t get_phys_addr(uint64_t addr) { + if (addr & ((uint64_t)1 << 63)) + return addr - FIXED_HIGHER_HALF_OFFSET_64; + return addr; +} + +static void *get_tag(struct stivale2_header *s, uint64_t id) { + struct stivale2_tag *tag = (void*)get_phys_addr(s->tags); + for (;;) { + if (tag == NULL) + return NULL; + if (tag->identifier == id) + return tag; + tag = (void*)get_phys_addr(tag->next); + } +} + +static void append_tag(struct stivale2_struct *s, struct stivale2_tag *tag) { + tag->next = s->tags; + s->tags = (uint64_t)(size_t)tag; +} + +void stivale2_load(char *cmdline, int boot_drive) { + int kernel_drive; { + char buf[32]; + if (!config_get_value(buf, 0, 32, "KERNEL_DRIVE")) { + kernel_drive = boot_drive; + } else { + kernel_drive = (int)strtoui(buf); + } + } + + int kernel_part; { + char buf[32]; + if (!config_get_value(buf, 0, 32, "KERNEL_PARTITION")) { + panic("KERNEL_PARTITION not specified"); + } else { + kernel_part = (int)strtoui(buf); + } + } + + char *kernel_path = balloc(128); + if (!config_get_value(kernel_path, 0, 128, "KERNEL_PATH")) { + panic("KERNEL_PATH not specified"); + } + + struct file_handle *fd = balloc(sizeof(struct file_handle)); + if (fopen(fd, kernel_drive, kernel_part, kernel_path)) { + panic("Could not open kernel file"); + } + + struct stivale2_header stivale2_hdr; + + int bits = elf_bits(fd); + + int ret; + + uint64_t slide = 0; + + bool level5pg = false; + switch (bits) { + case 64: { + // Check if 64 bit CPU + uint32_t eax, ebx, ecx, edx; + cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx); + if (!(edx & (1 << 29))) { + panic("stivale2: This CPU does not support 64-bit mode."); + } + // Check if 5-level paging is available + cpuid(0x00000007, 0, &eax, &ebx, &ecx, &edx); + if (ecx & (1 << 16)) { + print("stivale2: CPU has 5-level paging support\n"); + level5pg = true; + } + + ret = elf64_load_section(fd, &stivale2_hdr, ".stivale2hdr", sizeof(struct stivale2_header), slide); + + if (!ret && (stivale2_hdr.flags & 1)) { + // KASLR is enabled, set the slide + slide = rand64() & KASLR_SLIDE_BITMASK; + + // Re-read the .stivale2hdr with slid relocations + ret = elf64_load_section(fd, &stivale2_hdr, ".stivale2hdr", sizeof(struct stivale2_header), slide); + } + + break; + } + case 32: + ret = elf32_load_section(fd, &stivale2_hdr, ".stivale2hdr", sizeof(struct stivale2_header)); + break; + default: + panic("stivale2: Not 32 nor 64 bit x86 ELF file."); + } + + print("stivale2: %u-bit ELF file detected\n", bits); + + switch (ret) { + case 1: + panic("stivale2: File is not a valid ELF."); + case 2: + panic("stivale2: Section .stivale2hdr not found."); + case 3: + panic("stivale2: Section .stivale2hdr exceeds the size of the struct."); + case 4: + panic("stivale2: Section .stivale2hdr is smaller than size of the struct."); + } + + print("stivale2: Requested stack at %X\n", stivale2_hdr.stack); + + uint64_t entry_point = 0; + uint64_t top_used_addr = 0; + + switch (bits) { + case 64: + elf64_load(fd, &entry_point, &top_used_addr, slide); + break; + case 32: + elf32_load(fd, (uint32_t *)&entry_point, (uint32_t *)&top_used_addr); + break; + } + + if (stivale2_hdr.entry_point != 0) + entry_point = stivale2_hdr.entry_point; + + print("stivale2: Kernel slide: %X\n", slide); + + print("stivale2: Top used address in ELF: %X\n", top_used_addr); + + strcpy(stivale2_struct.bootloader_brand, "qloader2"); + strcpy(stivale2_struct.bootloader_version, QLOADER2_VERSION); + + ////////////////////////////////////////////// + // Create firmware struct tag + ////////////////////////////////////////////// + { + struct stivale2_struct_tag_firmware *tag = balloc(sizeof(struct stivale2_struct_tag_firmware)); + tag->tag.identifier = STIVALE2_STRUCT_TAG_FIRMWARE_ID; + + tag->flags = 1 << 0; // bit 0 = BIOS boot + + append_tag(&stivale2_struct, (struct stivale2_tag *)tag); + } + + ////////////////////////////////////////////// + // Create modules struct tag + ////////////////////////////////////////////// + { + struct stivale2_struct_tag_modules *tag = balloc(sizeof(struct stivale2_struct_tag_modules)); + tag->tag.identifier = STIVALE2_STRUCT_TAG_MODULES_ID; + + tag->module_count = 0; + + for (int i = 0; ; i++) { + char module_file[64]; + if (!config_get_value(module_file, i, 64, "MODULE_PATH")) + break; + + tag->module_count++; + + struct stivale2_module *m = balloc(sizeof(struct stivale2_module)); + + if (!config_get_value(m->string, i, 128, "MODULE_STRING")) { + m->string[0] = '\0'; + } + + int part; { + char buf[32]; + if (!config_get_value(buf, i, 32, "MODULE_PARTITION")) { + part = kernel_part; + } else { + part = (int)strtoui(buf); + } + } + + struct file_handle f; + if (fopen(&f, fd->disk, part, module_file)) { + panic("Requested module with path \"%s\" not found!\n", module_file); + } + + void *module_addr = (void *)(((uint32_t)top_used_addr & 0xfff) ? + ((uint32_t)top_used_addr & ~((uint32_t)0xfff)) + 0x1000 : + (uint32_t)top_used_addr); + + memmap_alloc_range((size_t)module_addr, f.size); + fread(&f, module_addr, 0, f.size); + + m->begin = (uint64_t)(size_t)module_addr; + m->end = m->begin + f.size; + + top_used_addr = (uint64_t)(size_t)m->end; + + print("stivale2: Requested module %u:\n", i); + print(" Path: %s\n", module_file); + print(" String: %s\n", m->string); + print(" Begin: %X\n", m->begin); + print(" End: %X\n", m->end); + } + + append_tag(&stivale2_struct, (struct stivale2_tag *)tag); + } + + ////////////////////////////////////////////// + // Create RSDP struct tag + ////////////////////////////////////////////// + { + struct stivale2_struct_tag_rsdp *tag = balloc(sizeof(struct stivale2_struct_tag_rsdp)); + tag->tag.identifier = STIVALE2_STRUCT_TAG_RSDP_ID; + + tag->rsdp = (uint64_t)(size_t)get_rsdp(); + + append_tag(&stivale2_struct, (struct stivale2_tag *)tag); + } + + ////////////////////////////////////////////// + // Create cmdline struct tag + ////////////////////////////////////////////// + { + struct stivale2_struct_tag_cmdline *tag = balloc(sizeof(struct stivale2_struct_tag_cmdline)); + tag->tag.identifier = STIVALE2_STRUCT_TAG_CMDLINE_ID; + + tag->cmdline = (uint64_t)(size_t)cmdline; + + append_tag(&stivale2_struct, (struct stivale2_tag *)tag); + } + + ////////////////////////////////////////////// + // Create epoch struct tag + ////////////////////////////////////////////// + { + struct stivale2_struct_tag_epoch *tag = balloc(sizeof(struct stivale2_struct_tag_epoch)); + tag->tag.identifier = STIVALE2_STRUCT_TAG_EPOCH_ID; + + tag->epoch = time(); + print("stivale2: Current epoch: %U\n", tag->epoch); + + append_tag(&stivale2_struct, (struct stivale2_tag *)tag); + } + + ////////////////////////////////////////////// + // Create framebuffer struct tag + ////////////////////////////////////////////// + { + struct stivale2_hdr_tag_framebuffer *hdrtag = get_tag(&stivale2_hdr, STIVALE2_HDR_TAG_FRAMEBUFFER_ID); + + if (hdrtag == NULL) { + deinit_vga_textmode(); + } else { + struct stivale2_struct_tag_framebuffer *tag = balloc(sizeof(struct stivale2_struct_tag_framebuffer)); + tag->tag.identifier = STIVALE2_STRUCT_TAG_FRAMEBUFFER_ID; + + tag->framebuffer_width = hdrtag->framebuffer_width; + tag->framebuffer_height = hdrtag->framebuffer_height; + tag->framebuffer_bpp = hdrtag->framebuffer_bpp; + + init_vbe(&tag->framebuffer_addr, + &tag->framebuffer_pitch, + &tag->framebuffer_width, + &tag->framebuffer_height, + &tag->framebuffer_bpp); + + append_tag(&stivale2_struct, (struct stivale2_tag *)tag); + } + } + + ////////////////////////////////////////////// + // Create memmap struct tag + ////////////////////////////////////////////// + { + struct stivale2_struct_tag_memmap *tag = balloc(sizeof(struct stivale2_struct_tag_memmap)); + tag->tag.identifier = STIVALE2_STRUCT_TAG_MEMMAP_ID; + + size_t memmap_entries; + struct e820_entry_t *memmap = get_memmap(&memmap_entries); + + tag->entries = (uint64_t)memmap_entries; + + void *tag_memmap = balloc(sizeof(struct e820_entry_t) * memmap_entries); + memcpy(tag_memmap, memmap, sizeof(struct e820_entry_t) * memmap_entries); + + append_tag(&stivale2_struct, (struct stivale2_tag *)tag); + } + + // Check if 5-level paging tag is requesting support + bool level5pg_requested = get_tag(&stivale2_hdr, STIVALE2_HDR_TAG_5LV_PAGING_ID) ? true : false; + + if (bits == 64) { + // If we're going 64, we might as well call this BIOS interrupt + // to tell the BIOS that we are entering Long Mode, since it is in + // the specification. + struct rm_regs r = {0}; + r.eax = 0xec00; + r.ebx = 0x02; // Long mode only + rm_int(0x15, &r, &r); + } + + rm_flush_irqs(); + + if (bits == 64) { + void *pagemap_ptr; + if (level5pg && level5pg_requested) { + // Enable CR4.LA57 + asm volatile ( + "mov eax, cr4\n\t" + "bts eax, 12\n\t" + "mov cr4, eax\n\t" + : + : + : "eax", "memory" + ); + + struct pagemap { + uint64_t pml5[512]; + uint64_t pml4_lo[512]; + uint64_t pml4_hi[512]; + uint64_t pml3_lo[512]; + uint64_t pml3_hi[512]; + uint64_t pml2_0gb[512]; + uint64_t pml2_1gb[512]; + uint64_t pml2_2gb[512]; + uint64_t pml2_3gb[512]; + }; + struct pagemap *pagemap = balloc_aligned(sizeof(struct pagemap), 0x1000); + pagemap_ptr = (void *)pagemap; + + // zero out the pagemap + for (uint64_t *p = (uint64_t *)pagemap; p < &pagemap->pml3_hi[512]; p++) + *p = 0; + + pagemap->pml5[511] = (uint64_t)(size_t)pagemap->pml4_hi | 0x03; + pagemap->pml5[0] = (uint64_t)(size_t)pagemap->pml4_lo | 0x03; + pagemap->pml4_hi[511] = (uint64_t)(size_t)pagemap->pml3_hi | 0x03; + pagemap->pml4_hi[256] = (uint64_t)(size_t)pagemap->pml3_lo | 0x03; + pagemap->pml4_lo[0] = (uint64_t)(size_t)pagemap->pml3_lo | 0x03; + pagemap->pml3_hi[510] = (uint64_t)(size_t)pagemap->pml2_0gb | 0x03; + pagemap->pml3_hi[511] = (uint64_t)(size_t)pagemap->pml2_1gb | 0x03; + pagemap->pml3_lo[0] = (uint64_t)(size_t)pagemap->pml2_0gb | 0x03; + pagemap->pml3_lo[1] = (uint64_t)(size_t)pagemap->pml2_1gb | 0x03; + pagemap->pml3_lo[2] = (uint64_t)(size_t)pagemap->pml2_2gb | 0x03; + pagemap->pml3_lo[3] = (uint64_t)(size_t)pagemap->pml2_3gb | 0x03; + + // populate the page directories + for (size_t i = 0; i < 512 * 4; i++) + (&pagemap->pml2_0gb[0])[i] = (i * 0x200000) | 0x03 | (1 << 7); + } else { + struct pagemap { + uint64_t pml4[512]; + uint64_t pml3_lo[512]; + uint64_t pml3_hi[512]; + uint64_t pml2_0gb[512]; + uint64_t pml2_1gb[512]; + uint64_t pml2_2gb[512]; + uint64_t pml2_3gb[512]; + }; + struct pagemap *pagemap = balloc_aligned(sizeof(struct pagemap), 0x1000); + pagemap_ptr = (void *)pagemap; + + // zero out the pagemap + for (uint64_t *p = (uint64_t *)pagemap; p < &pagemap->pml3_hi[512]; p++) + *p = 0; + + pagemap->pml4[511] = (uint64_t)(size_t)pagemap->pml3_hi | 0x03; + pagemap->pml4[256] = (uint64_t)(size_t)pagemap->pml3_lo | 0x03; + pagemap->pml4[0] = (uint64_t)(size_t)pagemap->pml3_lo | 0x03; + pagemap->pml3_hi[510] = (uint64_t)(size_t)pagemap->pml2_0gb | 0x03; + pagemap->pml3_hi[511] = (uint64_t)(size_t)pagemap->pml2_1gb | 0x03; + pagemap->pml3_lo[0] = (uint64_t)(size_t)pagemap->pml2_0gb | 0x03; + pagemap->pml3_lo[1] = (uint64_t)(size_t)pagemap->pml2_1gb | 0x03; + pagemap->pml3_lo[2] = (uint64_t)(size_t)pagemap->pml2_2gb | 0x03; + pagemap->pml3_lo[3] = (uint64_t)(size_t)pagemap->pml2_3gb | 0x03; + + // populate the page directories + for (size_t i = 0; i < 512 * 4; i++) + (&pagemap->pml2_0gb[0])[i] = (i * 0x200000) | 0x03 | (1 << 7); + } + + asm volatile ( + "cli\n\t" + "cld\n\t" + "mov cr3, eax\n\t" + "mov eax, cr4\n\t" + "or eax, 1 << 5\n\t" + "mov cr4, eax\n\t" + "mov ecx, 0xc0000080\n\t" + "rdmsr\n\t" + "or eax, 1 << 8\n\t" + "wrmsr\n\t" + "mov eax, cr0\n\t" + "or eax, 1 << 31\n\t" + "mov cr0, eax\n\t" + "jmp 0x28:1f\n\t" + "1: .code64\n\t" + "mov ax, 0x30\n\t" + "mov ds, ax\n\t" + "mov es, ax\n\t" + "mov fs, ax\n\t" + "mov gs, ax\n\t" + "mov ss, ax\n\t" + + "push 0x30\n\t" + "push [rsi]\n\t" + "pushfq\n\t" + "push 0x28\n\t" + "push [rbx]\n\t" + + "xor rax, rax\n\t" + "xor rbx, rbx\n\t" + "xor rcx, rcx\n\t" + "xor rdx, rdx\n\t" + "xor rsi, rsi\n\t" + "xor rbp, rbp\n\t" + "xor r8, r8\n\t" + "xor r9, r9\n\t" + "xor r10, r10\n\t" + "xor r11, r11\n\t" + "xor r12, r12\n\t" + "xor r13, r13\n\t" + "xor r14, r14\n\t" + "xor r15, r15\n\t" + + "iretq\n\t" + ".code32\n\t" + : + : "a" (pagemap_ptr), "b" (&entry_point), + "D" (&stivale2_struct), "S" (&stivale2_hdr.stack) + : "memory" + ); + } else if (bits == 32) { + asm volatile ( + "cli\n\t" + "cld\n\t" + + "sub esp, 4\n\t" + "mov [esp], edi\n\t" + + "push 0x20\n\t" + "push [esi]\n\t" + "pushfd\n\t" + "push 0x18\n\t" + "push [ebx]\n\t" + + "xor eax, eax\n\t" + "xor ebx, ebx\n\t" + "xor ecx, ecx\n\t" + "xor edx, edx\n\t" + "xor esi, esi\n\t" + "xor edi, edi\n\t" + "xor ebp, ebp\n\t" + + "iret\n\t" + : + : "b" (&entry_point), "D" (&stivale2_struct), "S" (&stivale2_hdr.stack) + : "memory" + ); + } +} diff --git a/src/protos/stivale2.h b/src/protos/stivale2.h new file mode 100644 index 00000000..307eff1f --- /dev/null +++ b/src/protos/stivale2.h @@ -0,0 +1,6 @@ +#ifndef __PROTOS__STIVALE2_H__ +#define __PROTOS__STIVALE2_H__ + +void stivale2_load(char *cmdline, int boot_drive); + +#endif diff --git a/src/qloader2.h b/src/qloader2.h new file mode 100644 index 00000000..c4a02ae7 --- /dev/null +++ b/src/qloader2.h @@ -0,0 +1,6 @@ +#ifndef __QLOADER2_H__ +#define __QLOADER2_H__ + +#define QLOADER2_VERSION "0.4" + +#endif diff --git a/test/qloader2.cfg b/test/qloader2.cfg index 4f193f67..00e0a846 100644 --- a/test/qloader2.cfg +++ b/test/qloader2.cfg @@ -2,7 +2,7 @@ TIMEOUT=3 :Test kernel -PROTOCOL=stivale +PROTOCOL=stivale2 KERNEL_PARTITION=0 KERNEL_PATH=boot/test.elf diff --git a/test/test.asm b/test/test.asm index aaed2b0f..0d2aff6a 100644 --- a/test/test.asm +++ b/test/test.asm @@ -1,15 +1,13 @@ ; This is a compliant "kernel" meant for testing purposes. ; Header -section .stivalehdr +section .stivale2hdr stivale_header: - dq stack.top ; rsp - dw 0 ; video mode - dw 0 ; fb_width - dw 0 ; fb_height - dw 0 ; fb_bpp - dq 0 + dq 0 ; entry point + dq stack.top ; rsp + dq 0 ; flags + dq 0 ; tags section .bss