diff --git a/build/build-temp-vm-uefi-1.sh b/build/build-temp-vm-uefi-1.sh index 17df1095..03c8de10 100755 --- a/build/build-temp-vm-uefi-1.sh +++ b/build/build-temp-vm-uefi-1.sh @@ -35,9 +35,13 @@ umount_tempdisk() { [ ! -d $TMPMOUNT ] && mkdir -p $TMPMOUNT +set -e + echo "Building ZealBooter..." ( cd ../zealbooter && make clean all ) +set +e + echo "Making temp vdisk, running auto-install..." qemu-img create -f raw $TMPDISK 192M qemu-system-x86_64 -machine q35,accel=kvm -drive format=raw,file=$TMPDISK -m 1G -rtc base=localtime -cdrom AUTO-VM-1.ISO -device isa-debug-exit diff --git a/src/Kernel/KStart32.ZC b/src/Kernel/KStart32.ZC index 53451831..fb18d040 100755 --- a/src/Kernel/KStart32.ZC +++ b/src/Kernel/KStart32.ZC @@ -77,6 +77,10 @@ SYS_SEMAS:: DU8 SEMA_SEMAS_NUM * DEFAULT_CACHE_LINE_WIDTH DUP(0); //************************************ ALIGN 16, OC_NOP + // Signature to find CORE0_32BIT_INIT + DU64 0xaa23c08ed10bd4d7; + DU64 0xf6ceba7d4b74179a; + CORE0_32BIT_INIT:: //Entry point for $LK,"BootRAM",A="MN:BootRAM"$. PUSH U32 RFLAGG_START POPFD diff --git a/zealbooter/GNUmakefile b/zealbooter/GNUmakefile index 4e7ba1fb..72671afa 100644 --- a/zealbooter/GNUmakefile +++ b/zealbooter/GNUmakefile @@ -21,7 +21,7 @@ $(eval $(call DEFAULT_VAR,CC,cc)) $(eval $(call DEFAULT_VAR,LD,ld)) # User controllable CFLAGS. -CFLAGS ?= -O2 -g -Wall -Wextra -Wpedantic -pipe +CFLAGS ?= -O2 -g -Wall -Wextra -pipe # User controllable preprocessor flags. We set none by default. CPPFLAGS ?= @@ -35,7 +35,7 @@ LDFLAGS ?= # Internal C flags that should not be changed by the user. override CFLAGS += \ -I. \ - -std=c11 \ + -std=gnu11 \ -ffreestanding \ -fno-stack-protector \ -fno-stack-check \ diff --git a/zealbooter/lib.c b/zealbooter/lib.c new file mode 100644 index 00000000..e9e2e6e1 --- /dev/null +++ b/zealbooter/lib.c @@ -0,0 +1,105 @@ +#include +#include +#include + +void *memcpy(void *dest, const void *src, size_t n) { + uint8_t *pdest = (uint8_t *)dest; + const uint8_t *psrc = (const uint8_t *)src; + + for (size_t i = 0; i < n; i++) { + pdest[i] = psrc[i]; + } + + return dest; +} + +void *memset(void *s, int c, size_t n) { + uint8_t *p = (uint8_t *)s; + + for (size_t i = 0; i < n; i++) { + p[i] = (uint8_t)c; + } + + return s; +} + +void *memmove(void *dest, const void *src, size_t n) { + uint8_t *pdest = (uint8_t *)dest; + const uint8_t *psrc = (const uint8_t *)src; + + if (src > dest) { + for (size_t i = 0; i < n; i++) { + pdest[i] = psrc[i]; + } + } else if (src < dest) { + for (size_t i = n; i > 0; i--) { + pdest[i-1] = psrc[i-1]; + } + } + + return dest; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + const uint8_t *p1 = (const uint8_t *)s1; + const uint8_t *p2 = (const uint8_t *)s2; + + for (size_t i = 0; i < n; i++) { + if (p1[i] != p2[i]) + return p1[i] < p2[i] ? -1 : 1; + } + + return 0; +} + +char *strcpy(char *dest, const char *src) { + size_t i; + + for (i = 0; src[i]; i++) + dest[i] = src[i]; + + dest[i] = 0; + + return dest; +} + +char *strncpy(char *dest, const char *src, size_t n) { + size_t i; + + for (i = 0; i < n && src[i]; i++) + dest[i] = src[i]; + for ( ; i < n; i++) + dest[i] = 0; + + return dest; +} + +int strcmp(const char *s1, const char *s2) { + for (size_t i = 0; ; i++) { + char c1 = s1[i], c2 = s2[i]; + if (c1 != c2) + return c1 - c2; + if (!c1) + return 0; + } +} + +int strncmp(const char *s1, const char *s2, size_t n) { + for (size_t i = 0; i < n; i++) { + char c1 = s1[i], c2 = s2[i]; + if (c1 != c2) + return c1 - c2; + if (!c1) + return 0; + } + + return 0; +} + +size_t strlen(const char *str) { + size_t len; + + for (len = 0; str[len]; len++); + + return len; +} diff --git a/zealbooter/lib.h b/zealbooter/lib.h new file mode 100644 index 00000000..4dd6a2fc --- /dev/null +++ b/zealbooter/lib.h @@ -0,0 +1,37 @@ +#ifndef __LIB_H__ +#define __LIB_H__ + +#include + +#define DIV_ROUNDUP(A, B) ({ \ + typeof(A) _a_ = A; \ + typeof(B) _b_ = B; \ + (_a_ + (_b_ - 1)) / _b_; \ +}) + +#define ALIGN_UP(A, B) ({ \ + typeof(A) _a__ = A; \ + typeof(B) _b__ = B; \ + DIV_ROUNDUP(_a__, _b__) * _b__; \ +}) + +#define ALIGN_DOWN(A, B) ({ \ + typeof(A) _a_ = A; \ + typeof(B) _b_ = B; \ + (_a_ / _b_) * _b_; \ +}) + +typedef char symbol[]; + +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +void *memmove(void *dest, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +char *strcpy(char *dest, const char *src); +char *strncpy(char *dest, const char *src, size_t n); +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); +size_t strlen(const char *str); + +#endif diff --git a/zealbooter/lower.S b/zealbooter/lower.S new file mode 100644 index 00000000..8abdb9dc --- /dev/null +++ b/zealbooter/lower.S @@ -0,0 +1,36 @@ +.section .text + +.global lower +lower: + pushq $0x18 + addq $(1f - lower), %rax + pushq %rax + lretq + + .code32 + 1: + + mov $0x20, %eax + mov %eax, %ds + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + mov %eax, %ss + + lgdt (%ecx) + + mov %cr0, %eax + btr $31, %eax + mov %eax, %cr0 + + mov $0xc0000080, %ecx + xor %eax, %eax + xor %edx, %edx + wrmsr + + mov %ebx, %eax + mov $2, %ebx + + mov $0x7c00, %esp + jmp *%eax + diff --git a/zealbooter/zealbooter.c b/zealbooter/zealbooter.c index da2b4d96..2556a3b7 100644 --- a/zealbooter/zealbooter.c +++ b/zealbooter/zealbooter.c @@ -1,26 +1,71 @@ #include #include #include - -// The Limine requests can be placed anywhere, but it is important that -// the compiler does not optimise them away, so, usually, they should -// be made volatile or equivalent. +#include static volatile struct limine_module_request module_request = { .id = LIMINE_MODULE_REQUEST, .revision = 0 }; -static volatile struct limine_terminal_request terminal_request = { - .id = LIMINE_TERMINAL_REQUEST, +static volatile struct limine_kernel_address_request kernel_address_request = { + .id = LIMINE_KERNEL_ADDRESS_REQUEST, .revision = 0 }; -static void done(void) { - for (;;) { - __asm__("hlt"); - } -} +static volatile struct limine_memmap_request memmap_request = { + .id = LIMINE_MEMMAP_REQUEST, + .revision = 0 +}; + +struct CZXE { + uint16_t jmp; + uint8_t module_align_bits; + uint8_t reserved; + uint32_t signature; + int64_t org; + int64_t patch_table_offset; + int64_t file_size; +}; + +struct CDate { + uint32_t time; + int32_t date; +}; + +#define MEM_E820_ENTRIES_NUM 48 + +#define MEM_E820T_USABLE 1 +#define MEM_E820T_RESERVED 2 +#define MEM_E820T_ACPI 3 +#define MEM_E820T_ACPI_NVS 4 +#define MEM_E820T_BAD_MEM 5 +#define MEM_E820T_PERM_MEM 7 + +struct CMemE820 { + uint8_t *base; + int64_t len; + uint8_t type, pad[3]; +}; + +struct CGDTEntry { + uint64_t lo, hi; +}; + +#define MP_PROCESSORS_NUM 128 + +struct CGDT { + struct CGDTEntry null; + struct CGDTEntry boot_ds; + struct CGDTEntry boot_cs; + struct CGDTEntry cs32; + struct CGDTEntry cs64; + struct CGDTEntry cs64_ring3; + struct CGDTEntry ds; + struct CGDTEntry ds_ring3; + struct CGDTEntry tr[MP_PROCESSORS_NUM]; + struct CGDTEntry tr_ring3[MP_PROCESSORS_NUM]; +}; struct CZXE { uint16_t jmp; @@ -50,56 +95,131 @@ struct CSysLimitBase { }; struct CKernel { - struct CZXE zxe; + struct CZXE h; uint32_t jmp; uint32_t boot_src; uint32_t boot_blk; uint32_t boot_patch_table_base; uint32_t sys_run_level; struct CDate compile_time; -//U0 start; + // U0 start uint32_t boot_base; uint16_t mem_E801[2]; - struct CMemE820 mem_E820[48]; + struct CMemE820 mem_E820[MEM_E820_ENTRIES_NUM]; uint64_t mem_physical_space; - struct CSysLimitBase sys_gdt_ptr; + struct { + uint16_t limit; + uint8_t *base; + } __attribute__((packed)) sys_gdt_ptr; uint16_t sys_pci_buses; -// ;$ = ($ + 15) & -16; -// struct CGDT sys_gdt; -// uint32_t sys_font_ptr; -// struct CVBEInfo sys_vbe_info; -// struct CVBEModeShort sys_vbe_modes[32]; -// struct CVBEMode sys_vbe_mode; -// uint16_t sys_vbe_mode_num; + struct CGDT sys_gdt; + uint32_t sys_font_ptr; +} __attribute__((packed)); +#define BOOT_SRC_RAM 2 +#define RLF_16BIT 0b01 +#define RLF_VESA 0b10 + +void lower(void); + +struct E801 { + size_t lowermem; + size_t uppermem; }; -// The following will be our kernel's entry point. -void _start(void) { - // Ensure we got a terminal - if (terminal_request.response == NULL - || terminal_request.response->terminal_count < 1) { - done(); +struct E801 get_E801(void) { + struct E801 E801 = {0}; + + for (size_t i = 0; i < memmap_request.response->entry_count; i++) { + struct limine_memmap_entry *entry = memmap_request.response->entries[i]; + + if (entry->type == LIMINE_MEMMAP_USABLE) { + if (entry->base == 0x100000) { + if (entry->length > 0xf00000) { + E801.lowermem = 0x3c00; + } else { + E801.lowermem = entry->length / 1024; + } + } + if (entry->base <= 0x1000000 && entry->base + entry->length > 0x1000000) { + E801.uppermem = ((entry->length - (0x1000000 - entry->base)) / 1024) / 64; + } + } } - // We should now be able to call the Limine terminal to print out - // a simple "Hello World" to screen. - struct limine_terminal *terminal = terminal_request.response->terminals[0]; - terminal_request.response->write(terminal, "Hello World", 11); - - struct limine_file *kernel_module = module_request.response->modules[0]; - struct CKernel *kernel = kernel_module->address; - - char str[128]; - str[0] = ' '; - str[1] = kernel->zxe.signature; - str[2] = kernel->zxe.signature >> 8; - str[3] = kernel->zxe.signature >> 16; - str[4] = kernel->zxe.signature >> 24; - str[5] = 0; - - terminal_request.response->write(terminal, str, 5); - - // We're done, just hang... - done(); + return E801; +} + +void _start(void) { + struct limine_file *kernel = module_request.response->modules[0]; + struct CKernel *CKernel = (void *)0x7c00; + memcpy(CKernel, kernel->address, kernel->size); + + void *CORE0_32BIT_INIT; + for (uint64_t *p = (uint64_t *)CKernel; ; p++) { + if (*p != 0xaa23c08ed10bd4d7) { + continue; + } + p++; + if (*p != 0xf6ceba7d4b74179a) { + continue; + } + p++; + CORE0_32BIT_INIT = p; + break; + } + + CKernel->boot_src = BOOT_SRC_RAM; + CKernel->boot_blk = 0; + CKernel->boot_patch_table_base = (uintptr_t)CKernel + CKernel->h.patch_table_offset; + CKernel->sys_run_level = RLF_VESA | RLF_16BIT; + + CKernel->boot_base = (uintptr_t)&CKernel->jmp; + + CKernel->sys_gdt.boot_ds.lo = 0x000093000000ffff; + CKernel->sys_gdt.boot_cs.lo = 0x00009a000000ffff; + CKernel->sys_gdt.cs32.lo = 0x00cf9a000000ffff; + CKernel->sys_gdt.cs64.lo = 0x00af9b000000ffff; + CKernel->sys_gdt.cs64_ring3.lo = 0x00affb000000ffff; + CKernel->sys_gdt.ds.lo = 0x00af93000000ffff; + CKernel->sys_gdt.ds_ring3.lo = 0x00aff3000000ffff; + + CKernel->sys_gdt_ptr.limit = sizeof(CKernel->sys_gdt) - 1; + CKernel->sys_gdt_ptr.base = (void *)&CKernel->sys_gdt; + + struct E801 E801 = get_E801(); + CKernel->mem_E801[0] = E801.lowermem; + CKernel->mem_E801[1] = E801.uppermem; + + for (size_t i = 0; i < memmap_request.response->entry_count; i++) { + struct limine_memmap_entry *entry = memmap_request.response->entries[i]; + + int our_type; + switch (entry->type) { + case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE: + case LIMINE_MEMMAP_KERNEL_AND_MODULES: + case LIMINE_MEMMAP_USABLE: + our_type = MEM_E820T_USABLE; break; + case LIMINE_MEMMAP_ACPI_RECLAIMABLE: + our_type = MEM_E820T_ACPI; break; + case LIMINE_MEMMAP_ACPI_NVS: + our_type = MEM_E820T_ACPI_NVS; break; + case LIMINE_MEMMAP_BAD_MEMORY: + our_type = MEM_E820T_BAD_MEM; break; + case LIMINE_MEMMAP_RESERVED: + default: + our_type = MEM_E820T_RESERVED; break; + } + + CKernel->mem_E820[i].base = (void *)entry->base; + CKernel->mem_E820[i].len = entry->length; + CKernel->mem_E820[i].type = our_type; + } + + void *target_addr = (void *)lower - kernel_address_request.response->virtual_base; + target_addr += kernel_address_request.response->physical_base; + + asm volatile ("jmp *%0" :: "a"(target_addr), "b"(CORE0_32BIT_INIT), "c"(&CKernel->sys_gdt_ptr) : "memory"); + + __builtin_unreachable(); }