diff options
author | Marvin Borner | 2021-07-04 21:31:28 +0200 |
---|---|---|
committer | Marvin Borner | 2021-07-04 21:34:15 +0200 |
commit | 9b8698769535846d029c44247956eed9a21f1185 (patch) | |
tree | 294a17af4102805ab9863274339e8e030897804e /src |
Initial commit
Diffstat (limited to 'src')
-rw-r--r-- | src/entry/bootsector.asm | 172 | ||||
-rw-r--r-- | src/entry/definitions.asm | 26 | ||||
-rw-r--r-- | src/loader/cpu.c | 26 | ||||
-rw-r--r-- | src/loader/inc/cpu.h | 13 | ||||
-rw-r--r-- | src/loader/inc/def.h | 28 | ||||
-rw-r--r-- | src/loader/inc/lib.h | 14 | ||||
-rw-r--r-- | src/loader/inc/log.h | 11 | ||||
-rw-r--r-- | src/loader/lib.c | 85 | ||||
-rw-r--r-- | src/loader/link.ld | 35 | ||||
-rw-r--r-- | src/loader/log.c | 108 | ||||
-rw-r--r-- | src/loader/main.c | 19 |
11 files changed, 537 insertions, 0 deletions
diff --git a/src/entry/bootsector.asm b/src/entry/bootsector.asm new file mode 100644 index 0000000..1beb850 --- /dev/null +++ b/src/entry/bootsector.asm @@ -0,0 +1,172 @@ +; MIT License, Copyright (c) 2021 Marvin Borner + +; This file includes definitions to reduce magic numbers +%include "src/entry/definitions.asm" + +bits 16 ; Real mode is 16 Bit +org LOCATION ; Bootsector location + +global _start +_start: + jmp .skip_bpb ; Some BIOSes override the BPB area + dd "START" + nop + times 87 db 0 ; Fill BPB area with 0 +.skip_bpb: + + ; Clear registers (some BIOSes are weird) + xor bx, bx + mov ds, bx + mov es, bx + mov ss, bx + ; Other registers may contain relevant data + + mov sp, LOCATION ; Set stack (top) pointer - grows downwards + + mov ax, SCREEN_CLEAR ; Clear screen command + int SCREEN_INT ; CLEAR! + + ; Print friendly welcome message + mov si, hello_msg + call print + + call disk_support ; Is disk supported? Implicitly verifies that this is a 386+ architecture + mov esp, LOCATION ; Clear upper 16 Bit of esp, now that 16 Bit support is guaranteed + cli ; Disable interrupts + + jmp load_stage ; Load and execute main stage + +; Print function uses si as string pointer +print: + ; a and b regs get partially destroyed - push for restoration + push bx + push ax + + mov ah, SCREEN_OUT ; Set VGA command + xor bh, bh ; Clear b register (according to BIOS spec) +.putch: + lodsb ; Load next string byte (using ds:si) + test al, al ; Test loaded byte + jz .end ; End if al is zero (using previous test); NULL-termination + int SCREEN_INT ; WRITE! + jmp .putch ; Continue +.end: + ; Restore ax/bx + pop ax + pop bx + ret + +; Check if disk is supported using ID checks and LBA test +disk_support: + ; BIOS puts the disk id into dl + cmp dl, 0x80 + jb disk_error ; Error if below 0x80 - probably floppy disk + cmp dl, 0x8f + ja disk_error ; Error if above 0x8f - invalid + + ; Check if int 0x13 and LBA are supported + mov ah, DISK_EXT_CHECK ; Set needed interrupt values + mov bx, DISK_EXT_CHECK_REQ + int DISK_INT ; CHECK! + jc disk_error ; Carry means something went wrong + cmp bx, DISK_EXT_CHECK_RESP + jne disk_error ; Response is incorrect => error! + ret + +; Read sectors from disk using dap information +disk_read: + mov si, dap + mov ah, DISK_READ + int DISK_INT + jc error_loop + ret + +; Loads the main stage (main.c) +load_stage: + mov bx, loader + mov [dap.dest], bx + call disk_read + + lgdt [gdt] ; Load GDT + + ; Set protected mode (32 Bit mode) + mov eax, cr0 + or ax, 1 ; Set PE (Protection Enable) Bit + mov cr0, eax + + jmp 0x08:protected_mode + +bits 32 +protected_mode: + ; Set segment registers + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + call loader + jmp error_loop +bits 16 + +; Error handling +error_loop: + cli ; Disable interrupts + hlt ; Wait for interrupt; should wait forever + jmp error_loop ; Loop if returns anyway + +disk_error: + mov si, disk_msg + call print + jmp error_loop + +; Messages (NULL-terminated strings with newline/return) +hello_msg db "Hello world! Booting...", NEWLINE, RETURN, NULL +disk_msg db "Disk is invalid or unsupported", NEWLINE, RETURN, NULL + +; Disk configuration data for DISK_READ interrupt (filled according to spec) +dap: ; Disk Address Packet + db 0x10 ; Disk address packet size + db 0 ; Always 0 +.count: + dw 64 ; Number of sectors (512B each) to read ; 0x8000 +.dest: + dw 0 ; Destination offset + dw 0 ; Destination segment +.lba: + dd 1 ; LBA number ; Inter stage is directly after first LBA (0) + dd 0 ; More storage bytes + +; Global Descriptor Table (GDT) +gdt: + dw .size - 1 + 8 ; GDT size + dd .start - 8 ; GDT start address +.start: + ; Code + dw 0xffff ; Limit + dw 0x0000 ; Base (low 16 bits) + db 0x00 ; Base (mid 8 bits) + db 10011010b ; Access + db 11001111b ; Granularity + db 0x00 ; Base (high 8 bits) + + ; Data + dw 0xffff ; Limit + dw 0x0000 ; Base (low 16 bits) + db 0x00 ; Base (mid 8 bits) + db 10010010b ; Access + db 11001111b ; Granularity + db 0x00 ; Base (high 8 bits) +.end: +.size: equ .end - .start + +times SECTOR_SIZE - 2 - ($ - $$) db 0 ; Fill until 512 (SECTOR_SIZE) - 2 bytes; -2 because of 2B signature +dw SECTOR_END_SIG ; Bootsector end signature + +loader: +incbin "build/loader.bin" + +; Limit to 0x8000 due to ext2 superblock start at 1024 on partition 1 +times 0x8000 - ($ - $$) db 0 +superblock: diff --git a/src/entry/definitions.asm b/src/entry/definitions.asm new file mode 100644 index 0000000..dcaa141 --- /dev/null +++ b/src/entry/definitions.asm @@ -0,0 +1,26 @@ +; MIT License, Copyright (c) 2021 Marvin Borner +; This file includes definitions to reduce magic numbers + +; Boot constants +%define LOCATION 0x7c00 ; Bootloader location +%define SECTOR_END_SIG 0xaa55 ; Bootsector end signature +%define SECTOR_SIZE 512 ; Bootsector size + +; Interrupts +%define SCREEN_INT 0x10 ; Screen/video BIOS interrupt +%define DISK_INT 0x13 ; Disk BIOS interrupt + +; Characters +%define NEWLINE 0x0A ; Newline character (\n) +%define RETURN 0x0D ; Return character (\r) +%define NULL 0x00 ; NULL character (\0) + +; Screen/video commands (for SCREEN_INT; using VGA interface) +%define SCREEN_CLEAR 0x03 ; Clear screen command +%define SCREEN_OUT 0x0e ; Screen output command + +; Disk commands +%define DISK_EXT_CHECK 0x41 ; Disk extension check command +%define DISK_EXT_CHECK_REQ 0x55aa ; First extension check signature (request) +%define DISK_EXT_CHECK_RESP 0xaa55 ; Second extension check signature (response) +%define DISK_READ 0x42 ; Disk extended read command diff --git a/src/loader/cpu.c b/src/loader/cpu.c new file mode 100644 index 0000000..a12d2fa --- /dev/null +++ b/src/loader/cpu.c @@ -0,0 +1,26 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#include <cpu.h> + +/** + * CPU IO + */ + +u8 inb(u16 port) +{ + u8 value; + __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +u16 inw(u16 port) +{ + u16 value; + __asm__ volatile("inw %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +void outb(u16 port, u8 data) +{ + __asm__ volatile("outb %0, %1" ::"a"(data), "Nd"(port)); +} diff --git a/src/loader/inc/cpu.h b/src/loader/inc/cpu.h new file mode 100644 index 0000000..88db34b --- /dev/null +++ b/src/loader/inc/cpu.h @@ -0,0 +1,13 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#ifndef CPU_H +#define CPU_H + +#include <def.h> + +u8 inb(u16 port); +u16 inw(u16 port); + +void outb(u16 port, u8 data); + +#endif diff --git a/src/loader/inc/def.h b/src/loader/inc/def.h new file mode 100644 index 0000000..8c2f9aa --- /dev/null +++ b/src/loader/inc/def.h @@ -0,0 +1,28 @@ +// MIT License, Copyright (c) 2021 Marvin Borner +// Useful macros/types + +#ifndef DEF_H +#define DEF_H + +typedef signed char s8; +typedef unsigned char u8; + +typedef signed short s16; +typedef unsigned short u16; + +typedef signed int s32; +typedef unsigned int u32; + +typedef __builtin_va_list va_list; +#define va_start __builtin_va_start +#define va_end __builtin_va_end +#define va_arg __builtin_va_arg + +#define NULL ((void *)0) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define ABS(a) (((a) < 0) ? -(a) : (a)) + +#define PACKED __attribute__((packed)) + +#endif diff --git a/src/loader/inc/lib.h b/src/loader/inc/lib.h new file mode 100644 index 0000000..997db67 --- /dev/null +++ b/src/loader/inc/lib.h @@ -0,0 +1,14 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#ifndef LIB_H +#define LIB_H + +#include <def.h> + +u32 strlen(const char *str); +u32 strnlen(const char *s, u32 max); +u32 strlcpy(char *dst, const char *src, u32 size); + +int itoa(s32 value, char *buffer, u32 base); + +#endif diff --git a/src/loader/inc/log.h b/src/loader/inc/log.h new file mode 100644 index 0000000..2d65a76 --- /dev/null +++ b/src/loader/inc/log.h @@ -0,0 +1,11 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#ifndef LOG_H +#define LOG_H + +void serial_install(void); +void serial_print(const char *data); + +void log(const char *format, ...); + +#endif diff --git a/src/loader/lib.c b/src/loader/lib.c new file mode 100644 index 0000000..c54ef14 --- /dev/null +++ b/src/loader/lib.c @@ -0,0 +1,85 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#include <lib.h> + +/** + * Common string functions + */ + +u32 strlen(const char *str) +{ + const char *s = str; + while (*s) + s++; + return s - str; +} + +u32 strnlen(const char *str, u32 max) +{ + const char *s = str; + while (max && *s) { + s++; + max--; + } + return s - str; +} + +u32 strlcpy(char *dst, const char *src, u32 size) +{ + const char *orig = src; + u32 left = size; + + if (left) + while (--left) + if (!(*dst++ = *src++)) + break; + + if (!left) { + if (size) + *dst = 0; + while (*src++) + ; + } + + return src - orig - 1; +} + +/** + * Conversion + */ + +int itoa(s32 value, char *buffer, u32 base) +{ + char tmp[16]; + char *tp = tmp; + int i; + unsigned v; + + int sign = (base == 10 && value < 0); + if (sign) + v = -value; + else + v = (unsigned)value; + + while (v || tp == tmp) { + i = v % base; + v /= base; + if (i < 10) + *tp++ = i + '0'; + else + *tp++ = i + 'a' - 10; + } + + int len = tp - tmp; + + if (sign) { + *buffer++ = '-'; + len++; + } + + while (tp > tmp) + *buffer++ = *--tp; + *buffer = '\0'; + + return len; +} diff --git a/src/loader/link.ld b/src/loader/link.ld new file mode 100644 index 0000000..aa97f49 --- /dev/null +++ b/src/loader/link.ld @@ -0,0 +1,35 @@ +/* MIT License, Copyright (c) 2021 Marvin Borner */ + +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(start) +phys = 0x7e00; + +SECTIONS +{ + . = phys; + + .text : + { + *(.text.start) + *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + .bss : + { + *(COMMON) + *(.bss) + } + + end = .; +} diff --git a/src/loader/log.c b/src/loader/log.c new file mode 100644 index 0000000..a2c05f0 --- /dev/null +++ b/src/loader/log.c @@ -0,0 +1,108 @@ +#include <cpu.h> +#include <lib.h> +#include <log.h> + +/** + * Formatting + */ + +static u32 vsnprintf(char *str, u32 size, const char *format, va_list ap) +{ + u32 length = 0; + + int temp_int; + char temp_ch; + char *temp_str; + + char buffer[64] = { 0 }; + + // TODO: Fix potential memory overflows because of str[length++]=xxx + char ch; + while ((ch = *format++)) { + if (ch == '%') { + switch (*format++) { + case '%': + str[length++] = '%'; + break; + case 'c': + temp_ch = va_arg(ap, int); + str[length++] = temp_ch; + break; + case 's': + temp_str = va_arg(ap, char *); + length += strlcpy(&str[length], temp_str, size - length); + break; + case 'b': + temp_int = va_arg(ap, int); + itoa(temp_int, buffer, 2); + length += strlcpy(&str[length], buffer, size - length); + break; + case 'd': + temp_int = va_arg(ap, int); + itoa(temp_int, buffer, 10); + length += strlcpy(&str[length], buffer, size - length); + break; + case 'x': + temp_int = va_arg(ap, int); + itoa(temp_int, buffer, 16); + length += strlcpy(&str[length], buffer, size - length); + break; + default: + serial_print("Unknown printf format\n"); + } + } else { + str[length++] = ch; + } + } + + return length; +} + +/** + * Serial + */ + +void serial_install(void) +{ + outb(0x3f8 + 1, 0x00); + outb(0x3f8 + 3, 0x80); + outb(0x3f8 + 0, 0x03); + outb(0x3f8 + 1, 0x00); + outb(0x3f8 + 3, 0x03); + outb(0x3f8 + 2, 0xc7); + outb(0x3f8 + 4, 0x0B); +} + +static int is_transmit_empty(void) +{ + return inb(0x3f8 + 5) & 0x20; +} + +static void serial_put(char ch) +{ + while (is_transmit_empty() == 0) + ; + outb(0x3f8, (u8)ch); +} + +void serial_print(const char *data) +{ + for (const char *p = data; *p; p++) + serial_put(*p); +} + +/** + * Universal print function + */ + +void log(const char *format, ...) +{ + char buf[1024] = { 0 }; + + va_list ap; + va_start(ap, format); + vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + + serial_print(buf); +} diff --git a/src/loader/main.c b/src/loader/main.c new file mode 100644 index 0000000..54b5122 --- /dev/null +++ b/src/loader/main.c @@ -0,0 +1,19 @@ +// MIT License, Copyright (c) 2021 Marvin Borner + +#include <def.h> +#include <log.h> + +/** + * Entry + */ + +int start(void); +int start(void) +{ + serial_install(); + log("Hello %d\n", 42); + + while (1) + ; + return 0; +} |