mirror of
https://github.com/encounter/osdev.git
synced 2026-03-30 11:33:54 -07:00
Load simple ELF binary & execute; improved paging; start VGA RGB output
This commit is contained in:
+4
-5
@@ -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)
|
||||
add_executable(test test)
|
||||
+4
-1
@@ -1,3 +1,6 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
int _start(int argc, char **argv) {
|
||||
return 1234;
|
||||
return 5; // ((uintptr_t) strlen) + 256;
|
||||
}
|
||||
@@ -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})
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -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);
|
||||
+41
-19
@@ -1,17 +1,45 @@
|
||||
#include "vga.h"
|
||||
#include "ports.h"
|
||||
#include "../arch/x86/mmu.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -4,18 +4,16 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#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;
|
||||
+16
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+43
-18
@@ -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;
|
||||
|
||||
+18
-18
@@ -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]
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
#include "fatfs/ff.h"
|
||||
|
||||
#ifdef ENABLE_DWARF
|
||||
#include "debug/dwarf.h"
|
||||
#include "dwarf.h"
|
||||
#endif
|
||||
|
||||
#include <common.h>
|
||||
@@ -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) {
|
||||
|
||||
+59
-21
@@ -1,9 +1,14 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
+40
-2
@@ -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);
|
||||
|
||||
|
||||
+13
-38
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user