diff options
-rw-r--r-- | bootstrap/stage1/arch/x86_64/asm/__chkstk.s | 25 | ||||
-rw-r--r-- | bootstrap/stage1/arch/x86_64/asm/entry.s | 25 | ||||
-rw-r--r-- | bootstrap/stage1/firmware/common/bprintf.c | 95 | ||||
-rw-r--r-- | bootstrap/stage1/firmware/common/string.c | 153 | ||||
-rw-r--r-- | bootstrap/stage1/firmware/efi/alloc.c | 50 | ||||
-rw-r--r-- | bootstrap/stage1/firmware/efi/efi.c | 210 | ||||
-rw-r--r-- | bootstrap/stage1/firmware/efi/tty.c | 55 | ||||
-rw-r--r-- | bootstrap/stage1/include/alloc.h | 28 | ||||
-rw-r--r-- | bootstrap/stage1/include/bprintf.h | 28 | ||||
-rw-r--r-- | bootstrap/stage1/include/tty.h | 38 |
10 files changed, 707 insertions, 0 deletions
diff --git a/bootstrap/stage1/arch/x86_64/asm/__chkstk.s b/bootstrap/stage1/arch/x86_64/asm/__chkstk.s new file mode 100644 index 0000000..7d5f0c4 --- /dev/null +++ b/bootstrap/stage1/arch/x86_64/asm/__chkstk.s @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +.section .text + +# FIXME: this causes GP faults under certain conditions +.global __chkstk +__chkstk: + ret diff --git a/bootstrap/stage1/arch/x86_64/asm/entry.s b/bootstrap/stage1/arch/x86_64/asm/entry.s new file mode 100644 index 0000000..05797ca --- /dev/null +++ b/bootstrap/stage1/arch/x86_64/asm/entry.s @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +.section .text + +.global _start +_start: + call efi_main + ret diff --git a/bootstrap/stage1/firmware/common/bprintf.c b/bootstrap/stage1/firmware/common/bprintf.c new file mode 100644 index 0000000..9d42cb9 --- /dev/null +++ b/bootstrap/stage1/firmware/common/bprintf.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <bprintf.h> +#include <tty.h> +#include <stdint.h> +#include <string.h> + +char* _convert(uint64_t num, int base) { + static char rep[] = "0123456789ABCDEF"; + static char buffer[50]; + char *ptr; + + ptr = &buffer[49]; + *ptr = '\0'; + + do { + *--ptr = rep[num%base]; + num /= base; + } while (num != 0); + + return ptr; +} + +int vbprintf(const char *fmt, va_list args) { + char *s; + uint64_t i; + + char buffer[4096]; + memset(buffer, 0, 4096); + for (size_t n = 0; n < strlen(fmt); n++) { + if (fmt[n] != '%') { + buffer[strlen(buffer)] = fmt[n]; + continue; + } else { + n++; + } + + switch (fmt[n]) { + case 'c': + i = va_arg(args, int); + buffer[strlen(buffer)] = i; + break; + case 's': + s = va_arg(args, char*); + strcat(buffer, s); + break; + case 'd': + i = va_arg(args, uint64_t); + if (i < 0) { + i = -i; + strcat(buffer, "-"); + } + strcat(buffer, _convert(i, 10)); + break; + case 'o': + i = va_arg(args, uint64_t); + strcat(buffer, _convert(i, 10)); + break; + case 'x': + i = va_arg(args, uint64_t); + strcat(buffer, _convert(i, 16)); + break; + } + } + tty_write(buffer, strlen(buffer)); + return 0; +} + +int bprintf(const char *fmt, ...) { + va_list args; + int done; + + va_start(args, fmt); + done = vbprintf(fmt, args); + va_end(args); + + return done; +} diff --git a/bootstrap/stage1/firmware/common/string.c b/bootstrap/stage1/firmware/common/string.c new file mode 100644 index 0000000..2c367a4 --- /dev/null +++ b/bootstrap/stage1/firmware/common/string.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stddef.h> + +int memcmp(const void *str1, const void *str2, size_t n) { + unsigned char const *p1 = str1; + unsigned char const *p2 = str2; + int ret = 0; + + if (str1 == str2) + return 0; + + while (n > 0) { + if (*p1 != *p2) { + ret = (*p1 > *p2)?1:-1; + break; + } + n--; + p1++; + p2++; + } + return ret; +} + +void* memcpy(void* __restrict dest, const void* __restrict src, size_t n) { + if (dest == src) + return dest; + + unsigned char *pdest = dest; + unsigned char const *psrc = src; + for (size_t i = 0; i < n; i++) + pdest[i] = psrc[i]; + return dest; +} + +char* strncpy(char* __restrict dest, const char* __restrict src, size_t n) { + for (size_t i = 0; i < n; i++) { + if (src[i] == '\0') + break; + dest[i] = src[i]; + } + return dest; +} + +char* strcpy(char* __restrict dest, const char* __restrict src) { + return (char*)memcpy(dest, src, strlen(src)); +} + +char* strcat(char* __restrict dest, const char* __restrict src) { + return (char*)memcpy(&dest[strlen(dest)], src, strlen(src)); +} + +char* strncat(char* __restrict dest, const char* __restrict src, size_t n) { + return (char*)memcpy(&dest[strlen(dest)], src, n); +} + +void* memmove(void* __restrict dest, const void* __restrict src, size_t n) { + if (dest == src) + return dest; + + unsigned char const *psrc = src; + unsigned char buffer[n]; + + for (size_t i = 0; i < n; i++) + buffer[i] = psrc[i]; + return memcpy(dest, (void*)buffer, n); +} + +void* memset(void *str, int c, size_t n) { + unsigned char *p = str; + for (size_t i = 0; i < n; i++) + p[i] = (unsigned char)c; + return str; +} + +int strncmp(const char *str1, const char *str2, size_t n) { + return memcmp(str1, str2, n); +} + +int strcmp(const char *str1, const char *str2) { + size_t str1_sz = strlen(str1); + size_t str2_sz = strlen(str2); + if (str1_sz > str2_sz) + return memcmp(str1, str2, str2_sz); + return memcmp(str1, str2, str1_sz); +} + +size_t strlen(const char *str) { + size_t i = 0; + while (str[i] != '\0') + i++; + return i; +} + +int _is_delim(char c, const char *delim) { + while (*delim != '\0') { + if (c == *delim) + return 1; + delim++; + } + return 0; +} + +char* strtok(char* __restrict str, const char* __restrict delim) { + static char *old_str; + + if (str == NULL) + str = old_str; + + while (1) { + if (_is_delim(*str, delim)) { + str++; + continue; + } + + if (*str == '\0') + return NULL; + break; + } + + char *ret = str; + while (1) { + if (*str == '\0') { + old_str = str; + return ret; + } + + if (_is_delim(*str, delim)) { + *str = '\0'; + old_str = str + 1; + return ret; + } + str++; + } +} diff --git a/bootstrap/stage1/firmware/efi/alloc.c b/bootstrap/stage1/firmware/efi/alloc.c new file mode 100644 index 0000000..d57e481 --- /dev/null +++ b/bootstrap/stage1/firmware/efi/alloc.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <bootabi/efi.h> + +#ifndef NULL +#define NULL (void*)(0) +#endif + +extern uint64_t errno; +extern efi_loaded_image_protocol_t *lip; + +void* balloc(size_t len) { + if (lip == NULL) + return NULL; + + void *buffer; + uint64_t status = lip->system_table->bootsrv->allocate_pool(LoaderData, len, &buffer); + if (status != 0) { + errno = status; + return NULL; + } + return buffer; +} + +void bfree(void *ptr) { + if (lip == NULL) + return; + + if (ptr == NULL) + return; + + lip->system_table->bootsrv->free_pool(ptr); +} diff --git a/bootstrap/stage1/firmware/efi/efi.c b/bootstrap/stage1/firmware/efi/efi.c new file mode 100644 index 0000000..140a9a6 --- /dev/null +++ b/bootstrap/stage1/firmware/efi/efi.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <bootabi/efi.h> +#include <bootabi/bootparam.h> +#include <bprintf.h> +#include <alloc.h> +#include <elf.h> +#include <string.h> +#include <stddef.h> + +uint64_t errno = EFI_SUCCESS; +efi_loaded_image_protocol_t *lip = NULL; +void (*stage2_entry)(bootparam_t *params); + +void print_error(uint64_t errno) { + bprintf("EFI error: %s\r\n", efi_status_str[(uint16_t)errno]); +} + +efi_bootparam_t* load_mmap(efi_bootparam_t *params) { + if (lip == NULL) { + errno = EFIERR(EFI_NOT_READY); + return NULL; + } + efi_boot_t *bootsrv = lip->system_table->bootsrv; + + uint64_t mmap_size = 0; + uint64_t mmap_key = 0; + uint64_t dsize = 0; + uint64_t status = bootsrv->get_memory_map(&mmap_size, + NULL, + &mmap_key, + &dsize, + NULL); + if (status != EFIERR(EFI_BUFFER_TOO_SMALL)) { + errno = status; + return NULL; + } + + efi_memory_descriptor_t *mmap = balloc(mmap_size/dsize); + status = bootsrv->get_memory_map(&mmap_size, + mmap, + &mmap_key, + &dsize, + NULL); + if (status != 0) { + errno = status; + return NULL; + } + + params->mmap = mmap; + params->mmap_size = mmap_size; + params->mmap_key = mmap_key; + params->mmap_dsize = dsize; + return params; +} + +ssize_t read_stage2(char16_t *filename, uint8_t **filedata) { + if (lip == NULL) + return -1; + efi_boot_t *bootsrv = lip->system_table->bootsrv; + + efi_guid_t sifs_id = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_ID; + efi_simple_file_system_protocol_t *sifs = NULL; + uint64_t status = bootsrv->locate_protocol(&sifs_id, NULL, (void**)&sifs); + if (status != 0) { + errno = status; + return -1; + } + + efi_file_protocol_t *rootdir = NULL; + status = sifs->open_volume(sifs, &rootdir); + if (status != 0) { + errno = status; + return -1; + } + + efi_file_protocol_t *stage2_file = NULL; + status = rootdir->open(rootdir, &stage2_file, filename, EFI_FILE_MODE_READ, 0); + if (status != 0) { + errno = status; + return -1; + } + + stage2_file->set_position(stage2_file, UINT64_MAX); + uint64_t bufsz = 0; + stage2_file->get_position(stage2_file, &bufsz); + stage2_file->set_position(stage2_file, 0); + + *filedata = balloc(bufsz); + memset(*filedata, 0, bufsz); + + status = stage2_file->read(stage2_file, &bufsz, *filedata); + if (status != 0) { + errno = status; + return -1; + } + + stage2_file->close(stage2_file); + return (size_t)bufsz; +} + +void* alloc_segment(Elf64_Phdr *phdr, uint8_t *filedata) { + if (lip == NULL) { + errno = EFI_NOT_READY; + return NULL; + } + efi_boot_t *bootsrv = lip->system_table->bootsrv; + + uintptr_t ret = (uintptr_t)phdr->p_paddr; + uint64_t status = bootsrv->allocate_pages(AllocateAddress, + LoaderData, + (phdr->p_memsz/4096)+1, + &ret); + if (status != 0) { + errno = status; + return NULL; + } + void *segment = (void*)filedata+phdr->p_offset; + memcpy((void*)ret, segment, phdr->p_memsz); + return (void*)ret; +} + +void* load_stage2(uint8_t *filedata) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr*)filedata; + if (memcmp(ehdr->e_ident, ELF_MAGIC, 4) != 0) { + errno = EFIERR(EFI_LOAD_ERROR); + return NULL; + } + + Elf64_Phdr *phdrs = (Elf64_Phdr*)(filedata + ehdr->e_phoff); + for (uint16_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *segment = &phdrs[i]; + if (segment->p_type != PT_LOAD) + continue; + if (alloc_segment(segment, filedata) == NULL) + return NULL; + } + + return (void*)ehdr->e_entry; +} + +uint64_t efi_main(void *handle, efi_system_t *st) { + efi_guid_t lip_id = EFI_LOADED_IMAGE_PROTOCOL_ID; + st->bootsrv->allocate_pool(LoaderData, sizeof(efi_loaded_image_protocol_t), (void**)&lip); + uint64_t status = st->bootsrv->handle_protocol(handle, &lip_id, (void**)&lip); + if (status != 0) + return 0; + + efi_guid_t gop_id = EFI_GRAPHICS_OUTPUT_PROTOCOL_ID; + efi_gop_t *gop = NULL; + status = st->bootsrv->locate_protocol(&gop_id, NULL, (void**)&gop); + if (status != 0) { + print_error(status); + return 0; + } + + uint8_t *stage2_image = NULL; + ssize_t stage2_size = read_stage2(L"bootstrap-stage2", &stage2_image); + if (stage2_size == -1) { + print_error(errno); + return 0; + } + stage2_entry = load_stage2(stage2_image); + if (stage2_entry == NULL) { + print_error(errno); + return 0; + } + + bootparam_t *params = balloc(sizeof(bootparam_t)); + params->magic = BOOTPARAM_MAGIC; + params->firmware_id = FIRMWARE_UEFI; + efi_bootparam_t *uefi_params = balloc(sizeof(efi_bootparam_t)); + uefi_params->st = st; + uefi_params->gop_info = gop->mode->info; + params->stack_ptr = balloc(4096); + memset(params->stack_ptr, 0, 4096); + params->firmware_params = (void*)uefi_params; + + if (load_mmap(uefi_params) == NULL) { + print_error(errno); + return 0; + } + + status = st->bootsrv->exit_boot_services(handle, uefi_params->mmap_key); + if (status != 0) { + print_error(status); + return 0; + } + st->bootsrv = NULL; + + (*stage2_entry)(params); + return 0; +} diff --git a/bootstrap/stage1/firmware/efi/tty.c b/bootstrap/stage1/firmware/efi/tty.c new file mode 100644 index 0000000..9131d95 --- /dev/null +++ b/bootstrap/stage1/firmware/efi/tty.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <bootabi/efi.h> + +#ifndef NULL +#define NULL (void*)(0) +#endif + +extern uint64_t errno; +extern efi_loaded_image_protocol_t *lip; + +void tty_putchar(char c) { + if (lip == NULL) + return; + + efi_system_t *st = lip->system_table; + char16_t new_line[3] = L"\r\n"; + if (c == '\n') { + st->console_out->output_str(st->console_out, new_line); + return; + } + char16_t wc[2]; + wc[0] = (char16_t)c; + wc[1] = L'\0'; + st->console_out->output_str(st->console_out, wc); +} + +char tty_getchar(void) { + if (lip == NULL) + return '\0'; + + efi_system_t *st = lip->system_table; + efi_input_key_t key; + st->console_in->read_key_stroke(st->console_in, &key); + if (key.unicode < 127) + return (char)key.unicode; + return '?'; +} diff --git a/bootstrap/stage1/include/alloc.h b/bootstrap/stage1/include/alloc.h new file mode 100644 index 0000000..088b389 --- /dev/null +++ b/bootstrap/stage1/include/alloc.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef BOOTSTRAP_ALLOC_H +#define BOOTSTRAP_ALLOC_H + +#include <stddef.h> + +void* balloc(size_t len); +void bfree(void *ptr); + +#endif diff --git a/bootstrap/stage1/include/bprintf.h b/bootstrap/stage1/include/bprintf.h new file mode 100644 index 0000000..d67d518 --- /dev/null +++ b/bootstrap/stage1/include/bprintf.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef BPRINTF_H +#define BPRINTF_H + +#include <stdarg.h> + +int vbprintf(const char *fmt, va_list args); +int bprintf(const char *fmt, ...); + +#endif diff --git a/bootstrap/stage1/include/tty.h b/bootstrap/stage1/include/tty.h new file mode 100644 index 0000000..af0d8f9 --- /dev/null +++ b/bootstrap/stage1/include/tty.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2025 Danny Holman <dholman@gymli.org> + * + * This file is part of BoxOS, a free and open-source Unix-like operating + * system. + * + * BoxOS is free software; you can redistribute it and/or modify under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * BoxOS is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * BoxOS; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef BOOTSTRAP_TTY_H +#define BOOTSTRAP_TTY_H + +#include <stdint.h> + +void tty_putchar(char c); +char tty_getchar(void); + +static inline void tty_write(const char *data, size_t size) { + for (size_t i = 0; i < size; i++) + tty_putchar(data[i]); +} + +static inline void tty_read(char *data, size_t size) { + for (size_t i = 0; i < size; i++) + data[i] = tty_getchar(); +} + +#endif |