diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt index 11eaa4b..66118aa 100644 --- a/bin/CMakeLists.txt +++ b/bin/CMakeLists.txt @@ -12,12 +12,11 @@ set(CMAKE_C_FLAGS "-m32 -std=c11 -fPIC -ffreestanding -fshort-enums -Wall -Werro set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -fno-builtin -nostdlib -nodefaultlibs") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-stack-protector") # FIXME generates bad code using `mov eax, large gs:14h`? set(CMAKE_C_FLAGS_DEBUG "-gdwarf-4 -DENABLE_DWARF") -if (NOT CMAKE_HOST_APPLE) - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=undefined") -endif() +#if (NOT CMAKE_HOST_APPLE) +# set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=undefined") +#endif() set(CMAKE_C_FLAGS_RELEASE "-Os") set(CMAKE_EXE_LINKER_FLAGS "") include_directories(${CMAKE_SOURCE_DIR}/libc) -add_executable(test test) -target_link_libraries(test c) \ No newline at end of file +add_executable(test test) \ No newline at end of file diff --git a/bin/test.c b/bin/test.c index 5793e5f..2a886c3 100644 --- a/bin/test.c +++ b/bin/test.c @@ -1,3 +1,6 @@ +#include +#include + int _start(int argc, char **argv) { - return 1234; + return 5; // ((uintptr_t) strlen) + 256; } \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index f23f755..269eb39 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -32,7 +32,9 @@ set(SOURCE_FILES multiboot div64 elf + dwarf stdio_impl + arch/x86/mmu drivers/ports drivers/timer drivers/keyboard @@ -56,6 +58,6 @@ set(SOURCE_FILES tests/stdio tests/path) if (${CMAKE_BUILD_TYPE} MATCHES "Debug") - set(SOURCE_FILES ${SOURCE_FILES} debug/ubsan debug/dwarf) + set(SOURCE_FILES ${SOURCE_FILES} debug/ubsan) endif () add_library(kernel STATIC ${SOURCE_FILES}) \ No newline at end of file diff --git a/kernel/arch/x86/mmu.c b/kernel/arch/x86/mmu.c new file mode 100644 index 0000000..77ff4d1 --- /dev/null +++ b/kernel/arch/x86/mmu.c @@ -0,0 +1,22 @@ +#include +#include + +#define KERNEL_PAGE_OFFSET 0xC0000000 // FIXME make dynamic? + +void *load_page_table() { + void *raw_ptr; + __asm__ volatile("mov %%cr3, %0" : "=a"(raw_ptr)); + return (uint32_t *) ((uintptr_t) raw_ptr + KERNEL_PAGE_OFFSET); +} + +void page_table_set(uintptr_t address, uint32_t val) { + uint32_t *page_table = load_page_table(); + uint16_t i = (uint16_t) (address >> 22); + page_table[i] = val; + __asm__ volatile("invlpg %0" : : "m"(i)); +} + +void *kernel_page_offset(void *ptr) { + if ((uintptr_t) ptr > KERNEL_PAGE_OFFSET) return ptr; + return (void *) ((uintptr_t) ptr + KERNEL_PAGE_OFFSET); +} \ No newline at end of file diff --git a/kernel/arch/x86/mmu.h b/kernel/arch/x86/mmu.h new file mode 100644 index 0000000..81a12ab --- /dev/null +++ b/kernel/arch/x86/mmu.h @@ -0,0 +1,27 @@ +// +// Paging flags for Page-Tables and Page-Directories. +// +#define X86_PAGE_PRESENT (1<<0) // set if page or page-table is present +#define X86_PAGE_WRITE (1<<1) // set to allow writing to the page or page-table +#define X86_PAGE_USER (1<<2) // set to make the page or page-table a user page or page-table +// if this flag is cleared the page or page-table can only be accessed in supervisor mode +#define X86_PAGE_PWT (1<<3) // when the PWT flag is set, write-through caching is enabled for the +// page or page-table; when the flag is clear, write-back caching is +// enabled for the page or page-table. +// This type of caching is useful for VGA framebuffers. +#define X86_PAGE_PCD (1<<4) // Page Cache Disable, set this flag to disable caching of the page or page-table +#define X86_PAGE_ACCESSED (1<<5) // the processor sets this flag the first time the page or page table is accessed +#define X86_PAGE_DIRTY (1<<6) // the processor sets this flag the first time a page is accessed for a write operation +// this flag is only used in page-table entries and should not be set for page-directory entries +#define X86_PAGE_PAGESIZE (1<<7) // page size extension flag. (Set for enable) +#define X86_PAGE_GLOBAL (1<<8) // this flag is only used in page-table entries +// when a page is marked global and the page global enable (PGE) flag in register +// CR4 is set, the page-table or page-directory entry for the page is not invalidated +// in the TLB when register CR3 is loaded +#define X86_PAGE_AVAIL1 (1<<9) // Available for usage by the Kernel +#define X86_PAGE_AVAIL2 (1<<10) // Available for usage by the Kernel +#define X86_PAGE_AVAIL3 (1<<11) // Available for usage by the Kernel + +void *kernel_page_offset(void *ptr); + +void page_table_set(uintptr_t address, uint32_t val); \ No newline at end of file diff --git a/kernel/drivers/vga.c b/kernel/drivers/vga.c index beca346..a6688ba 100644 --- a/kernel/drivers/vga.c +++ b/kernel/drivers/vga.c @@ -1,17 +1,45 @@ #include "vga.h" #include "ports.h" +#include "../arch/x86/mmu.h" #include #include -#define VIDEO_ADDRESS ((uint16_t *) 0xC00B8000) +uint16_t *console_fb_addr = NULL; + +void *rgb_fb_addr = NULL; +framebuffer_info_t rgb_fb_info = {}; + +void vga_init(void *fb_addr, uint8_t type, framebuffer_info_t *fb_info) { + if (type == VGA_FB_TYPE_EGA_TEXT) { + console_fb_addr = fb_addr; + } else if (type == VGA_FB_TYPE_RGB) { + rgb_fb_addr = fb_addr; + rgb_fb_info = *fb_info; + } +} + +void vga_fill_color(uint8_t r, uint8_t g, uint8_t b) { + if (rgb_fb_addr == NULL || rgb_fb_info.bpp != 32) return; + + uint32_t color = ((uint32_t) r << rgb_fb_info.red_field_position) + | ((uint32_t) g << rgb_fb_info.green_field_position) + | ((uint32_t) b << rgb_fb_info.blue_field_position); + + for (int i = 0; i < rgb_fb_info.width / 2 && i < rgb_fb_info.height / 2; i++) { + uint32_t *pixel = rgb_fb_addr + rgb_fb_info.pitch * i; + printf("Setting pixel %Xh to %Xh\n", (uintptr_t) pixel, color); + *pixel = color; + } +} int vga_print_char(char c, int col, int row, char attr) { + if (console_fb_addr == NULL) return 0; if (!attr) attr = WHITE_ON_BLACK; /* Error control: print a red 'E' if the coords aren't right */ if (col >= MAX_COLS || row >= MAX_ROWS) { - VIDEO_ADDRESS[MAX_COLS * MAX_ROWS - 1] = vga_entry('E', RED_ON_WHITE); + console_fb_addr[MAX_COLS * MAX_ROWS - 1] = vga_entry('E', RED_ON_WHITE); return vga_get_offset(col, row); } @@ -25,18 +53,14 @@ int vga_print_char(char c, int col, int row, char attr) { row = vga_get_offset_row(offset); offset = vga_get_offset(0, row + 1); } else { - VIDEO_ADDRESS[offset++] = ((uint16_t) attr << 8 | c); + console_fb_addr[offset++] = ((uint16_t) attr << 8 | c); } vga_set_cursor_offset(offset = vga_handle_scrolling(offset)); return offset; } int vga_get_cursor_offset() { - /* - * Use the VGA ports to get the current cursor position - * 1. Ask for high byte of the cursor offset (data 14) - * 2. Ask for low byte (data 15) - */ + if (console_fb_addr == NULL) return 0; port_byte_out(REG_SCREEN_CTRL, 14); int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */ port_byte_out(REG_SCREEN_CTRL, 15); @@ -45,7 +69,7 @@ int vga_get_cursor_offset() { } void vga_set_cursor_offset(int offset) { - /* Similar to get_cursor_offset, but instead of reading we write data */ + if (console_fb_addr == NULL) return; port_byte_out(REG_SCREEN_CTRL, 14); port_byte_out(REG_SCREEN_DATA, (unsigned char) (offset >> 8)); port_byte_out(REG_SCREEN_CTRL, 15); @@ -53,30 +77,28 @@ void vga_set_cursor_offset(int offset) { } void vga_clear_screen() { - memset((void *) VIDEO_ADDRESS, 0, MAX_COLS * MAX_ROWS * sizeof(*VIDEO_ADDRESS)); + if (console_fb_addr == NULL) return; + memset((void *) console_fb_addr, 0, MAX_COLS * MAX_ROWS * sizeof(*console_fb_addr)); vga_set_cursor_offset(vga_get_offset(0, 0)); } /* Advance the text cursor, scrolling the video buffer if necessary. */ int vga_handle_scrolling(int cursor_offset) { - // If the cursor is within the screen, return it unmodified. + if (console_fb_addr == NULL) return 0; if (cursor_offset < MAX_ROWS * MAX_COLS) return cursor_offset; /* Shuffle the rows back one. */ for (int i = 1; i < MAX_ROWS; i++) { - memcpy((void *) (VIDEO_ADDRESS + vga_get_offset(0, i - 1)), - (void *) (VIDEO_ADDRESS + vga_get_offset(0, i)), - MAX_COLS * sizeof(*VIDEO_ADDRESS)); + memcpy((void *) (console_fb_addr + vga_get_offset(0, i - 1)), + (void *) (console_fb_addr + vga_get_offset(0, i)), + MAX_COLS * sizeof(*console_fb_addr)); } /* Blank the last line by setting all bytes to 0 */ - memset((void *) (VIDEO_ADDRESS + vga_get_offset(0, MAX_ROWS - 1)), - 0, MAX_COLS * sizeof(*VIDEO_ADDRESS)); + memset((void *) (console_fb_addr + vga_get_offset(0, MAX_ROWS - 1)), + 0, MAX_COLS * sizeof(*console_fb_addr)); - // Move the offset back one row, such that it is now on the last - // row, rather than off the edge of the screen. cursor_offset -= MAX_COLS; - // Return the updated cursor position. return cursor_offset; } \ No newline at end of file diff --git a/kernel/drivers/vga.h b/kernel/drivers/vga.h index d67cf13..9e2fadd 100644 --- a/kernel/drivers/vga.h +++ b/kernel/drivers/vga.h @@ -9,6 +9,30 @@ #define REG_SCREEN_CTRL 0x3D4 #define REG_SCREEN_DATA 0x3D5 +#define VGA_FB_TYPE_INDEXED 0 +#define VGA_FB_TYPE_RGB 1 +#define VGA_FB_TYPE_EGA_TEXT 2 + +struct framebuffer_info { + uint32_t pitch; + uint32_t width; + uint32_t height; + uint8_t bpp; + uint32_t palette_addr; + uint16_t palette_num_colors; + uint8_t red_field_position; + uint8_t red_mask_size; + uint8_t green_field_position; + uint8_t green_mask_size; + uint8_t blue_field_position; + uint8_t blue_mask_size; +}; +typedef struct framebuffer_info framebuffer_info_t; + +void vga_init(void *fb_addr, uint8_t type, framebuffer_info_t *fb_info); + +void vga_fill_color(uint8_t r, uint8_t g, uint8_t b); + int vga_print_char(char c, int col, int row, char attr); int vga_handle_scrolling(int cursor_offset); diff --git a/kernel/debug/dwarf.c b/kernel/dwarf.c similarity index 92% rename from kernel/debug/dwarf.c rename to kernel/dwarf.c index e6bc577..e364a2c 100644 --- a/kernel/debug/dwarf.c +++ b/kernel/dwarf.c @@ -4,18 +4,16 @@ #include #include #include "dwarf.h" -#include "../elf.h" -#include "../console.h" - -#define KERNEL_BASE 0xC0100000 +#include "elf.h" +#include "console.h" void dwarf_find_file(uintptr_t address) { - FILE *file; +// FILE *file; } void *dwarf_find_debug_info() { void *debug_line_ptr = NULL; - uint32_t read = 0; +// uint32_t read = 0; elf_file_t *elf_file; if ((elf_file = elf_open("kernel.bin")) == NULL) goto fail; diff --git a/kernel/debug/dwarf.h b/kernel/dwarf.h similarity index 100% rename from kernel/debug/dwarf.h rename to kernel/dwarf.h diff --git a/kernel/elf.c b/kernel/elf.c index 90a5c52..5502c7f 100644 --- a/kernel/elf.c +++ b/kernel/elf.c @@ -172,6 +172,17 @@ static const char *elf_section_type(elf_section_header_type_t type) { } } +static const char *elf_obj_type(elf_obj_type_t type) { + switch (type) { + case ELF_ET_NONE: return "None"; + case ELF_ET_REL: return "Relocatable"; + case ELF_ET_EXEC: return "Executable"; + case ELF_ET_DYN: return "Dynamic"; + case ELF_ET_CORE: return "Core"; + default: return "UNKNOWN"; + } +} + void elf_print_sections(elf_file_t *file) { if (!_elf_read_header(file) || !_elf_read_section_header_table(file) @@ -179,13 +190,17 @@ void elf_print_sections(elf_file_t *file) { return; elf_header_t *header = file->header; + printf("ELF type: %s\n", elf_obj_type(header->obj_type)); + printf("ELF entry offset: %Xh\n", header->program_entry_offset); + uint16_t num_entries = header->section_header_num_entries; for (uint16_t i = 0; i < num_entries; ++i) { elf_section_header_t *section_header = (void *) file->sht_start + header->section_header_entry_size * i; if (section_header->type == ELF_SHT_NULL) continue; // Skip null header - printf("Section %03d %s: offset "PRIx32", size "PRIx32", type %s\n", + printf("Section %03d %s: offset "PRIx32", size "PRIx32", VMA "PRIXUPTR", type %s\n", i, file->sht_str_section + section_header->name, section_header->offset, section_header->size, + section_header->address, elf_section_type(section_header->type)); } } diff --git a/kernel/elf.h b/kernel/elf.h index b1974d5..7887298 100644 --- a/kernel/elf.h +++ b/kernel/elf.h @@ -44,11 +44,11 @@ static_assert(sizeof(elf_machine_type_t) == sizeof(uint16_t), "elf_machine_type incorrect size"); enum elf_obj_type { - ELF_ET_NONE = 0, - ELF_ET_REL = 1, - ELF_ET_EXEC = 2, - ELF_ET_DYN = 3, - ELF_ET_CORE = 4, + ELF_ET_NONE = 0, // No file type + ELF_ET_REL = 1, // Relocatable file + ELF_ET_EXEC = 2, // Executable file + ELF_ET_DYN = 3, // Shared object file (& PIE executables) + ELF_ET_CORE = 4, // Core file ELF_ET_LOPROC = 0xFF00, ELF_ET_HIPROC = 0xFFFF }; @@ -83,22 +83,22 @@ static_assert(sizeof(elf_header_t) == 52, "elf_header incorrect size"); enum elf_section_header_type { - ELF_SHT_NULL = 0, - ELF_SHT_PROGBITS = 1, - ELF_SHT_SYMTAB = 2, - ELF_SHT_STRTAB = 3, - ELF_SHT_RELA = 4, - ELF_SHT_HASH = 5, - ELF_SHT_DYNAMIC = 6, - ELF_SHT_NOTE = 7, - ELF_SHT_NOBITS = 8, - ELF_SHT_REL = 9, - ELF_SHT_SHLIB = 10, - ELF_SHT_DYNSYM = 11, + ELF_SHT_NULL = 0, // Unused + ELF_SHT_PROGBITS = 1, // Program bits + ELF_SHT_SYMTAB = 2, // Symbol table + ELF_SHT_STRTAB = 3, // String table + ELF_SHT_RELA = 4, // Relocation entries w/ explicit addends + ELF_SHT_HASH = 5, // Symbol hash table + ELF_SHT_DYNAMIC = 6, // Dynamic linking information + ELF_SHT_NOTE = 7, // Auxiliary information + ELF_SHT_NOBITS = 8, // Program bits with zero size in file + ELF_SHT_REL = 9, // Relocation entries w/o explicit addends + ELF_SHT_SHLIB = 10, // Reserved + ELF_SHT_DYNSYM = 11, // Dynamic symbol table ELF_SHT_LOPROC = 0x70000000, ELF_SHT_HIPROC = 0x7FFFFFFF, ELF_SHT_LOUSER = 0x80000000, - ELF_SHT_HIUSER = 0xFFFFFFFF + ELF_SHT_HIUSER = 0xFFFFFFFF, }; typedef enum elf_section_header_type elf_section_header_type_t; @@ -122,6 +122,31 @@ typedef struct elf_section_header elf_section_header_t; //static_assert(sizeof(elf_section_header_t) == 32, // "elf_section_header incorrect size"); +enum elf_program_header_type { + ELF_PT_NULL = 0, // Unused + ELF_PT_LOAD = 1, // Loadable segment + ELF_PT_DYNAMIC = 2, // Dynamic linking information + ELF_PT_INTERP = 3, // Interpreter + ELF_PT_NOTE = 4, // Auxiliary information + ELF_PT_SHLIB = 5, // Reserved + ELF_PT_PHDR = 6, // Program header table + ELF_PT_LOPROC = 0x70000000, + ELF_PT_HIPROC = 0x7FFFFFFF +}; +typedef enum elf_program_header_type elf_program_header_type_t; + +struct _packed elf_program_header { + elf_program_header_type_t type; + uint32_t offset; // Offset from start of file + uint32_t vaddr; // Virtual address + uint32_t paddr; // Physical address (ignored?) + uint32_t file_size; // Size in file + uint32_t memory_size; // Size in memory + uint32_t flags; // ? + uint32_t align; // Memory alignment +}; +typedef struct elf_program_header elf_program_header_t; + struct elf_file { FILE *fd; elf_header_t *header; diff --git a/kernel/interrupt.asm b/kernel/interrupt.asm index e0e3458..6098f6e 100644 --- a/kernel/interrupt.asm +++ b/kernel/interrupt.asm @@ -93,19 +93,19 @@ isr_common_stub: mov ax, ds ; Lower 16-bits of eax = ds. push eax ; save the data segment descriptor - mov ax, 0x10 ; load the kernel data segment descriptor - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax + ;mov ax, 0x10 ; load the kernel data segment descriptor + ;mov ds, ax + ;mov es, ax + ;mov fs, ax + ;mov gs, ax call isr_handler pop eax ; reload the original data segment descriptor - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax + ;mov ds, ax + ;mov es, ax + ;mov fs, ax + ;mov gs, ax ; Restore the MXCSR register. ldmxcsr [esp] @@ -129,19 +129,19 @@ irq_common_stub: mov ax, ds ; Lower 16-bits of eax = ds. push eax ; save the data segment descriptor - mov ax, 0x10 ; load the kernel data segment descriptor - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax + ;mov ax, 0x10 ; load the kernel data segment descriptor + ;mov ds, ax + ;mov es, ax + ;mov fs, ax + ;mov gs, ax call irq_handler pop ebx ; reload the original data segment descriptor - mov ds, bx - mov es, bx - mov fs, bx - mov gs, bx + ;mov ds, bx + ;mov es, bx + ;mov fs, bx + ;mov gs, bx ; Restore the MXCSR register. ldmxcsr [esp] diff --git a/kernel/main.c b/kernel/main.c index e3efcf6..1d82b72 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -10,7 +10,7 @@ #include "fatfs/ff.h" #ifdef ENABLE_DWARF -#include "debug/dwarf.h" +#include "dwarf.h" #endif #include @@ -18,7 +18,7 @@ // #define KDEBUG -extern bool vc_vector_run_tests(); +extern void *load_page_table(); _noreturn _unused void kernel_main(uint32_t multiboot_magic, void *multiboot_info) { diff --git a/kernel/multiboot.c b/kernel/multiboot.c index b6a773a..aeeb2c7 100644 --- a/kernel/multiboot.c +++ b/kernel/multiboot.c @@ -1,9 +1,14 @@ #include +#include #include "multiboot.h" #include "console.h" +#include "arch/x86/mmu.h" +#include "drivers/vga.h" -#define PAGE_OFFSET 0xC0000000 #define KERNEL_OFFSET 0x200000 // FIXME +#define BASE_VIDEO_ADDRESS ((void *) 0xB8000) + +#define MULTIBOOT_CHECK_FLAG(flags, flag) (((flags) & (flag)) == (flag)) extern void *malloc_memory_start; extern void *malloc_memory_end; @@ -13,12 +18,12 @@ void multiboot_init(uint32_t magic, void *info_ptr) { panic("multiboot_magic: Invalid magic "PRIX32"\n", magic); } - // XXX: uintptr_t hack b/c of compiler optimization which then triggers ubsan ¯\_(ツ)_/¯ - struct multiboot_info *info = (struct multiboot_info *) ((uintptr_t) info_ptr + PAGE_OFFSET); + struct multiboot_info *info = (struct multiboot_info *) kernel_page_offset(info_ptr); printf("multiboot_info = "PRIXPTR", flags = "PRIx32"\n", info, info->flags); - if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_MEMORY)) { - malloc_memory_start = (void *) 0x100000 + PAGE_OFFSET + KERNEL_OFFSET; + // Check for base memory information. + if (MULTIBOOT_CHECK_FLAG(info->flags, MULTIBOOT_INFO_MEMORY)) { + malloc_memory_start = kernel_page_offset((void *) 0x100000 + KERNEL_OFFSET); // 1 MiB + (info->mem_upper * 1 KiB) malloc_memory_end = malloc_memory_start + (info->mem_upper * 0x400) - KERNEL_OFFSET; printf("upper_memory_end = "PRIXPTR"\n", malloc_memory_end); @@ -26,44 +31,48 @@ void multiboot_init(uint32_t magic, void *info_ptr) { panic("multiboot: Memory information required"); } - if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_BOOTDEV)) { + // Check for boot device. + if (MULTIBOOT_CHECK_FLAG(info->flags, MULTIBOOT_INFO_BOOTDEV)) { printf("boot_device = "PRIx32"h\n", info->boot_device); } - if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_CMDLINE)) { - printf("cmdline = %s\n", (char *) (info->cmdline + PAGE_OFFSET)); + // Check for command line. + if (MULTIBOOT_CHECK_FLAG(info->flags, MULTIBOOT_INFO_CMDLINE)) { + char *cmdline = (char *) kernel_page_offset((void *) info->cmdline); + printf("cmdline size %d = %s\n", strlen(cmdline), cmdline); } - if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_MODS)) { + // Check for modules. + if (MULTIBOOT_CHECK_FLAG(info->flags, MULTIBOOT_INFO_MODS)) { struct multiboot_mod_list *mod; int i; printf("mods_count = "PRIu32", mods_addr = "PRIXUPTR"\n", info->mods_count, info->mods_addr); - for (i = 0, mod = (struct multiboot_mod_list *) (info->mods_addr + PAGE_OFFSET); + for (i = 0, mod = (struct multiboot_mod_list *) kernel_page_offset((void *) info->mods_addr); i < info->mods_count; i++, mod++) { printf(" mod_start = "PRIXUPTR", mod_end = "PRIXUPTR", cmdline = %s\n", - mod->mod_start, mod->mod_end, (char *) (mod->cmdline + PAGE_OFFSET)); + mod->mod_start, mod->mod_end, (char *) kernel_page_offset((void *) mod->cmdline)); } } - /* Is the section header table of ELF valid? */ - if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_ELF_SHDR)) { + // Check for ELF section header table information. + if (MULTIBOOT_CHECK_FLAG(info->flags, MULTIBOOT_INFO_ELF_SHDR)) { struct multiboot_elf_section_header_table *elf_sec = &info->u.elf_sec; printf("multiboot_elf_sec: num = "PRIu32", size = "PRIu32", addr = "PRIXUPTR", shndx = "PRIu32"\n", elf_sec->num, elf_sec->size, elf_sec->addr, elf_sec->shndx); } - /* Are mmap_* valid? */ - if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_MEM_MAP)) { + // Check for memory map information + if (MULTIBOOT_CHECK_FLAG(info->flags, MULTIBOOT_INFO_MEM_MAP)) { printf("mmap_addr = "PRIXUPTR", mmap_length = "PRIx32"\n", info->mmap_addr, info->mmap_length); struct multiboot_mmap_entry *mmap; struct multiboot_mmap_entry *largest_available_entry = NULL; - for (mmap = (struct multiboot_mmap_entry *) (info->mmap_addr + PAGE_OFFSET); - (unsigned long) mmap < info->mmap_addr + PAGE_OFFSET + info->mmap_length; + for (mmap = (struct multiboot_mmap_entry *) kernel_page_offset((void *) info->mmap_addr); + (uintptr_t) mmap < (uintptr_t) kernel_page_offset((void *) info->mmap_addr + info->mmap_length); mmap = (struct multiboot_mmap_entry *) - ((unsigned long) mmap + mmap->size + sizeof(mmap->size))) { + ((uintptr_t) mmap + mmap->size + sizeof(mmap->size))) { if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE && (largest_available_entry == NULL || mmap->len > largest_available_entry->len)) { largest_available_entry = mmap; @@ -74,26 +83,55 @@ void multiboot_init(uint32_t magic, void *info_ptr) { } if (largest_available_entry != NULL) { - malloc_memory_start = (void *) (uint32_t) largest_available_entry->addr + PAGE_OFFSET + KERNEL_OFFSET; + malloc_memory_start = kernel_page_offset((void *) (uintptr_t) largest_available_entry->addr + KERNEL_OFFSET); malloc_memory_end = malloc_memory_start + largest_available_entry->len - KERNEL_OFFSET; printf("malloc_memory_start = "PRIXPTR", end = "PRIXPTR"\n", malloc_memory_start, malloc_memory_end); } } - /* Check VGA framebuffer. */ - if (CHECK_FLAG (info->flags, MULTIBOOT_INFO_FRAMEBUFFER_INFO)) { + // Check for VBE info. + if (MULTIBOOT_CHECK_FLAG(info->flags, MULTIBOOT_INFO_VBE_INFO)) { + printf("vbe_control_info = "PRIXUPTR", vbe_mode_info = "PRIXUPTR", vbe_mode = %d\n", + info->vbe_control_info, info->vbe_mode_info, info->vbe_mode); + } + + // Check for VGA framebuffer information. + if (MULTIBOOT_CHECK_FLAG(info->flags, MULTIBOOT_INFO_FRAMEBUFFER_INFO)) { printf("framebuffer_addr = "PRIXUPTR64", type = %d, bpp = %d\n", info->framebuffer_addr, info->framebuffer_type, info->framebuffer_bpp); + uintptr_t fbaddr = (uintptr_t) info->framebuffer_addr; + page_table_set(fbaddr, 0x83); // Add framebuffer to page table + + vga_init((void *) fbaddr, info->framebuffer_type, &(framebuffer_info_t) { + .pitch = info->framebuffer_pitch, + .width = info->framebuffer_width, + .height = info->framebuffer_height, + .bpp = info->framebuffer_bpp, + .palette_addr = info->framebuffer_palette_addr, + .palette_num_colors = info->framebuffer_palette_num_colors, + .red_field_position = info->framebuffer_red_field_position, + .red_mask_size = info->framebuffer_red_mask_size, + .green_field_position = info->framebuffer_green_field_position, + .green_mask_size = info->framebuffer_green_mask_size, + .blue_field_position = info->framebuffer_blue_field_position, + .blue_mask_size = info->framebuffer_blue_mask_size, + }); switch (info->framebuffer_type) { case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT: console_set_vga_enabled(true); break; + case MULTIBOOT_FRAMEBUFFER_TYPE_RGB: + vga_fill_color(255, 255, 255); + break; + default: break; } } else { + // Assume text console & hardcoded FB address for now + vga_init(kernel_page_offset(BASE_VIDEO_ADDRESS), 2, 0); console_set_vga_enabled(true); // FIXME } } \ No newline at end of file diff --git a/kernel/multiboot.h b/kernel/multiboot.h index f1e386e..dfd162f 100644 --- a/kernel/multiboot.h +++ b/kernel/multiboot.h @@ -50,8 +50,6 @@ #define MULTIBOOT_MEMORY_NVS 4 #define MULTIBOOT_MEMORY_BADRAM 5 -#define CHECK_FLAG(flags, flag) (((flags) & (flag)) == (flag)) - struct multiboot_aout_symbol_table { uint32_t tabsize; uint32_t strsize; diff --git a/kernel/shell.c b/kernel/shell.c index f19ad95..6b64508 100644 --- a/kernel/shell.c +++ b/kernel/shell.c @@ -186,7 +186,41 @@ static uint8_t command_cat(const char *path) { return 0; } -static void print_prompt(unsigned char ret) { +static int command_exec(const char *path) { + if (!_fs_mounted) return 255; + + elf_file_t *file; + char path_buf[512]; + void *text_ptr; + + path_append(path_buf, curr_path, path, sizeof(path_buf)); + if ((file = elf_open(path_buf)) == NULL) { + fprintf(stderr, "Error opening %s: %d\n", path_buf, errno); + return 1; + } + + elf_section_header_t *text_header = elf_find_section(file, ".text"); + if ((text_ptr = malloc(text_header->size)) == NULL) { + fprintf(stderr, "Failed to allocate memory for %s\n", path_buf); + elf_close(file); + return ENOMEM; + } + fseek(file->fd, text_header->offset, SEEK_SET); + fread(text_ptr, text_header->size, 1, file->fd); + if (ferror(file->fd)) { + fprintf(stderr, "Failed reading %s: %d\n", path_buf, errno); + elf_close(file); + return 2; + } + + int ret = ((int (*)(void)) text_ptr)(); + + free(text_ptr); + elf_close(file); + return ret; +} + +static void print_prompt(int ret) { if (_fs_mounted) { printf("%d %s # ", ret, curr_path); } else { @@ -198,7 +232,7 @@ static void print_prompt(unsigned char ret) { static void shell_callback(char *input) { printf("\n"); - unsigned char ret = 1; + int ret = 1; bool save = true; if (strcmp(input, "exit") == 0 || strcmp(input, "poweroff") == 0) { @@ -261,6 +295,10 @@ static void shell_callback(char *input) { ret = command_objdump(input + 8); } else if (strncmp(input, "cat ", 4) == 0) { ret = command_cat(input + 4); + } else if (strncmp(input, "./", 2) == 0) { + ret = command_exec(input + 2); + } else { + fprintf(stderr, "Command not found: %s\n", input); } print_prompt(ret); diff --git a/multiboot.asm b/multiboot.asm index 4b1c5e5..884a3b8 100644 --- a/multiboot.asm +++ b/multiboot.asm @@ -1,4 +1,3 @@ -; Declare constants for the multiboot header. FMBALIGN equ 1 << 0 ; align loaded modules on page boundaries FMEMINFO equ 1 << 1 ; provide memory map FVIDMODE equ 1 << 2 ; try to set graphics mode @@ -6,34 +5,20 @@ FLAGS equ FMBALIGN | FMEMINFO | FVIDMODE MAGIC equ 0x1BADB002 CHECKSUM equ -(MAGIC + FLAGS) -; This is the virtual base address of kernel space. It must be used to convert virtual -; addresses into physical addresses until paging is enabled. KERNEL_VIRTUAL_BASE equ 0xC0000000 ; 3GB -KERNEL_PAGE_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22) ; 768 -- Page directory index of kernel's 4MB PTE. +KERNEL_PAGE_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22) ; 768 section .data align 0x1000 boot_page_directory: - ; This page directory entry identity-maps the first 4MB of the 32-bit physical address space. - ; All bits are clear except the following: - ; bit 7: PS The kernel page is 4MB. - ; bit 1: RW The kernel page is read/write. - ; bit 0: P The kernel page is present. + ; 4MiB lower-half kernel page + dd 0x83 + times (KERNEL_PAGE_NUMBER - 1) dd 0 - ; This entry must be here -- otherwise the kernel will crash immediately after paging is - ; enabled because it can't fetch the next instruction! It's ok to unmap this page later. - dd 0x00000083 - times (KERNEL_PAGE_NUMBER - 1) dd 0 ; Pages before kernel space. + ; 4MiB higher-half kernel page + dd 0x83 + times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0 - ; This page directory entry defines a 4MB page containing the kernel. - dd 0x00000083 - times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0 ; Pages after the kernel image. - -; Declare a multiboot header that marks the program as a kernel. These are magic -; values that are documented in the multiboot standard. The bootloader will -; search for this signature in the first 8 KiB of the kernel file, aligned at a -; 32-bit boundary. The signature is in its own section so the header can be -; forced to be within the first 8 KiB of the kernel file. section .multiboot align 4 ; header @@ -49,7 +34,7 @@ align 4 dd 0 ; entry_addr ; graphics tag - dd 1 ; mode_type + dd 0 ; mode_type dd 800 ; width dd 600 ; height dd 32 ; depth @@ -61,15 +46,11 @@ stack_bottom: resb 0x4000 ; 16 KiB stack_top: -; The linker script specifies _start as the entry point to the kernel and the -; bootloader will jump to this position once the kernel has been loaded. It -; doesn't make sense to return from this function as the bootloader is gone. -; Declare _start as a function symbol with the given symbol size. section .text global _start:function (_start.end - _start) _start: mov ecx, (boot_page_directory - KERNEL_VIRTUAL_BASE) - mov cr3, ecx ; Load Page Directory Base Register. + mov cr3, ecx ; Load boot_page_directory into CR3 mov ecx, cr4 or ecx, (1 << 4) ; Set PSE bit in CR4 to enable 4MB pages. @@ -79,12 +60,11 @@ _start: or ecx, (1 << 31) ; Set PG bit in CR0 to enable paging. mov cr0, ecx - lea ecx, [.start_higher_half] ; far jump + lea ecx, [.start_higher_half] ; Far jump jmp ecx .start_higher_half: - ; Unmap the identity-mapped first 4MB of physical address space. - ; It should not be needed anymore. + ; Unmap lower-half kernel page mov dword [boot_page_directory], 0 invlpg [0] @@ -107,13 +87,8 @@ _start: ; Initialize GDT & IDT extern init_descriptor_tables call init_descriptor_tables - - ; Enter the high-level kernel. The ABI requires the stack is 16-byte - ; aligned at the time of the call instruction (which afterwards pushes - ; the return pointer of size 4 bytes). The stack was originally 16-byte - ; aligned above and we've since pushed a multiple of 16 bytes to the - ; stack since (pushed 0 bytes so far) and the alignment is thus - ; preserved and the call is well defined. + + ; Enter kernel extern kernel_main call kernel_main