summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorMarvin Borner2021-07-04 21:31:28 +0200
committerMarvin Borner2021-07-04 21:34:15 +0200
commit9b8698769535846d029c44247956eed9a21f1185 (patch)
tree294a17af4102805ab9863274339e8e030897804e /src
Initial commit
Diffstat (limited to 'src')
-rw-r--r--src/entry/bootsector.asm172
-rw-r--r--src/entry/definitions.asm26
-rw-r--r--src/loader/cpu.c26
-rw-r--r--src/loader/inc/cpu.h13
-rw-r--r--src/loader/inc/def.h28
-rw-r--r--src/loader/inc/lib.h14
-rw-r--r--src/loader/inc/log.h11
-rw-r--r--src/loader/lib.c85
-rw-r--r--src/loader/link.ld35
-rw-r--r--src/loader/log.c108
-rw-r--r--src/loader/main.c19
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;
+}