summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author0x221E <0x221E@0xinfinity.dev>2026-06-11 20:10:31 +0200
committer0x221E <0x221E@0xinfinity.dev>2026-06-11 20:11:56 +0200
commit6d559b7e50dbab428d324b4ddaed9db7a8ced887 (patch)
treec2e807a19d2d0b6054e13a6d3ece16750796198d
downloadtmpbootloader-master.tar.gz
initial commitHEADmaster
-rw-r--r--README.md32
-rw-r--r--TODO8
-rwxr-xr-xbuild.sh231
-rw-r--r--include/defs.h14
-rw-r--r--include/printk.h10
-rw-r--r--include/string.h9
-rw-r--r--include/vga.h16
-rw-r--r--lib/printk.c36
-rw-r--r--lib/string.c13
-rw-r--r--lib/vga.c23
-rw-r--r--linker.ld45
-rw-r--r--stage1/32bit.c22
-rwxr-xr-xstage1/32bit.obin0 -> 796 bytes
-rw-r--r--stage1/entry.S56
-rw-r--r--stage1/stage1.S84
-rw-r--r--stage1/stage1.h8
16 files changed, 607 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..87e6b93
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+## Disclaimer
+-----------------------
+For now, assumes that A20 is already enabled on your device by BIOS.
+
+## Why Bash?
+-----------------------
+I like it more, and feels more customizable than makefile.
+This project is not going to be permanent anyways, just some bootloader.
+
+## Disk Mapping
+-----------------------
+0x0 - 0x200 -> Master Boot Record (MBR)
+0x200 - 0x9ff -> Stage 1
+* - * -> Stage 2
+
+## Memory Mapping
+-----------------------
+0x7c00 - 0x7e00 -> BIOS MBR
+0x7e00 - 0x9200 -> stage 1
+
+## Stages
+-----------------------
+- early stage 1 (MBR)
+The only duty of this stage is to load the stage 1 bootloader.
+
+- stage 1
+1. Detects available video memory, and RAM memory.
+2. Switches to 32-bit execution and protected mode.
+
+- stage 2
+1. Switches to 64-bit execution (long mode).
+2. Implements a FAT driver to load a kernel (or OS) file from disk.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..91af834
--- /dev/null
+++ b/TODO
@@ -0,0 +1,8 @@
+TODO
+- ensure proper checks after mbr extended read
+- request available memory from BIOS in real-mode
+- 64-bit switch
+- FAT filesystem
+- Kernel Loading
+- So basically make a fucking bootloader.
+- Cleanup the linker script a bit.
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..bb9c67a
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,231 @@
+#!/bin/bash
+
+set -e
+
+if [ -z "${CROSSCC_i686}" ]; then
+ CROSSCC_i686=i686-elf-gcc
+fi
+
+if [ -z "${CROSSAS_i686}" ]; then
+ CROSSAS_i686=i686-elf-as
+fi
+
+if [ -z "${CROSSCC_x8664}" ]; then
+ CROSSCC_x8664=x86_64-elf-gcc
+fi
+
+if [ -z "${CROSSAS_x8664}" ]; then
+ CROSSAS_x8664=x86_64-elf-as
+fi
+
+if [ -z "${CROSSLD_i686}" ]; then
+ CROSSLD_i686=i686-elf-ld
+fi
+
+if [ -z "${CROSSLD_x8664}" ]; then
+ CROSSLD_x8664=x86_64-elf-ld
+fi
+
+if [ -z "${STAGE1_DIR}" ]; then
+ STAGE1_DIR="./stage1/"
+fi
+
+if [ -z "${STAGE2_DIR}" ]; then
+ STAGE2_DIR="$(pwd)/stage2"
+fi
+
+if [ -z "${BUILD_DIR}" ]; then
+ BUILD_DIR="$(pwd)/build"
+fi
+mkdir -p "${BUILD_DIR}"
+
+if [ -z "${TARGET}" ]; then
+ TARGET="$(pwd)/bootloader"
+fi
+
+GLOBAL_CFLAGS=(-I./include -ffreestanding -m32 -nostdlib)
+
+LINKER_LD="linker.ld"
+
+# Stage 1
+
+STAGE1_LD="${STAGE1_DIR}/linker.ld"
+
+STAGE1_C_FILES=$(find "${STAGE1_DIR}" -name "*.c")
+STAGE1_AS_FILES=$(find "${STAGE1_DIR}" -name "*.S")
+
+STAGE1_AS_TARGET="${BUILD_DIR}/stage1_as.o"
+STAGE1_CC_TARGET="${BUILD_DIR}/stage1_cc.o"
+
+STAGE1_TARGET="${BUILD_DIR}/stage1.bin"
+
+# Stage 2
+
+STAGE2_LD="${STAGE1_DIR}/linker.ld"
+
+STAGE2_C_FILES=$(find "${STAGE2_DIR}" -name "*.c")
+STAGE2_AS_FILES=$(find "${STAGE2_DIR}" -name "*.S")
+
+STAGE2_AS_TARGET="${BUILD_DIR}/stage2_as.o"
+STAGE2_CC_TARGET="${BUILD_DIR}/stage2_cc.o"
+
+STAGE1_DIR="$(pwd)/stage1"
+STAGE2_DIR="$(pwd)/stage2"
+
+function usage {
+ echo -e "Custom build system."
+ echo -e "Available commands:"
+ echo -e " ${0} all"
+ echo -e " ${0} run"
+ echo -e " ${0} build_drive"
+ echo -e " ${0} clean"
+ echo -e " ${0} stage1"
+}
+
+function build_lib {
+ macro="$1"
+
+ set -e
+
+ local lib_cfiles=$(find "./lib/" -name "*.c")
+
+ mkdir -p "${BUILD_DIR}/lib"
+ for i in ${lib_cfiles[@]}; do
+ "${CROSSCC_i686}" -m32 -c "$i" "${GLOBAL_CFLAGS[@]}" -o "${BUILD_DIR}/${i}.o" -D${macro}
+ echo -e " [CC] ${i}"
+ done
+
+ echo "[LIB] Compiled libraries."
+}
+
+function stage1 {
+ set -e
+
+ if [[ -e "${BUILD_DIR}/${STAGE1_DIR}" ]] && [[ -f "${BUILD_DIR}/${STAGE1_DIR}" ]]; then
+ mkdir -p "${BUILD_DIR}/${STAGE1_DIR}"
+ echo -e "Creating the stage 1 build directory ${BUILD_DIR}/${STAGE1_DIR}"
+ fi
+
+ echo -e "Producing stage 1 binaries..."
+
+ if [[ ${#STAGE1_AS_FILES[@]} -gt 0 ]] && [[ -n "${STAGE1_AS_FILES[@]}" ]]; then
+ echo -e "[STAGE1] Found assembly files: "
+ for i in ${STAGE1_AS_FILES[@]}; do
+ mkdir -p "${BUILD_DIR}/${i}"
+ "${CROSSAS_i686}" "$i" -o "${BUILD_DIR}/${i}.o"
+ echo -e " [AS]: $i"
+ done
+ echo -e " [AS] Assembled stage 1."
+ else
+ echo -e "[STAGE1] No assembly files were found."
+ fi
+
+ if [[ ${#STAGE1_C_FILES[@]} -gt 0 ]] && [[ -n "${STAGE1_C_FILES[@]}" ]]; then
+ echo -e "[STAGE1] Found C files: "
+ for i in ${STAGE1_C_FILES[@]}; do
+ "${CROSSCC_i686}" -c "$i" -o "${BUILD_DIR}/${i}.o" "${GLOBAL_CFLAGS[@]}"
+ echo -e " [CC]: $i"
+ done
+ echo -e " [CC] Compiled stage 1."
+ else
+ echo -e "[STAGE1] No C files were found."
+ fi
+
+ if [[ ${#STAGE1_C_FILES[@]} -eq 0 ]] && [[ ${#STAGE1_AS_FILES[@]} -eq 0 ]]; then
+ echo -e "[STAGE1] No stage1 files were found!"
+ exit 1
+ fi
+
+ echo -e "Stage 1 binaries completed."
+}
+
+function stage2 {
+ if [[ -e "${BUILD_DIR}/${STAGE1_DIR}" ]] && [[ -f "${BUILD_DIR}/${STAGE1_DIR}" ]]; then
+ mkdir -p "${BUILD_DIR}/${STAGE1_DIR}"
+ fi
+
+ ${CROSSAS_i686} "${STAGE2_AS_FILES}" -o "${STAGE2_AS_TARGET}"
+ echo -e " [AS] Assembled stage 2."
+ ${CROSSCC_x8664} "${STAGE2_C_FILES}" -o "${STAGE2_CC_TARGET}"
+ echo -e " [CC] Compiled stage 2."
+}
+
+function link {
+ set -e
+
+ local linker_files=()
+
+ echo -e "[LINK] Linking files..."
+
+ for i in $(find "${BUILD_DIR}" -name "*.o"); do
+ linker_files+=("$i")
+ echo -e " [L] Added: $i"
+ done
+
+ "${CROSSLD_i686}" -T "${LINKER_LD}" "${linker_files[@]}" -o "${STAGE1_TARGET}"
+
+ echo -e "[LINK] Linking complete."
+}
+
+function clean {
+ if [[ -e "${BUILD_DIR}" && -d "${BUILD_DIR}" ]]; then
+ echo -e "[CLEAN] Deleting build/ directory..."
+ rm -rf "${BUILD_DIR}"
+ fi
+
+ if [[ -e "$(pwd)/disk.img" && -f "$(pwd)/disk.img" ]]; then
+ echo -e "[CLEAN] Deleting disk.img..."
+ rm "$(pwd)/disk.img"
+ fi
+ echo -e "[CLEAN] Project cleaned."
+}
+
+function build_image {
+ dd if="${STAGE1_TARGET}" of="$(pwd)/disk.img" bs=512 count=15
+}
+
+function run {
+ qemu-system-x86_64 -drive format=raw,file="$(pwd)/disk.img"
+}
+
+function debug {
+ qemu-system-x86_64 -drive format=raw,file="$(pwd)/disk.img" -S -s
+}
+
+function compile {
+ echo "[COMPILE] Compiling and preparing the entire build."
+ clean
+ mkdir -p ./build/
+ build_lib "STAGE1"
+ stage1
+ link
+ build_image
+ echo "[COMPILE] DONE"
+}
+
+function run {
+ qemu-system-x86_64 -drive format=raw,file=disk.img
+}
+
+case "$1" in
+ "all") compile ;;
+ "run") compile && run ;;
+ "debug") compile && debug ;;
+ "stage1") stage1 ;;
+ "build_drive") build_image ;;
+ "clean") clean ;;
+ "help"|"usage")
+ usage
+ exit 0
+ ;;
+ "")
+ echo -e "You must specify a command."
+ echo -e "Type '${0} usage' or '${0} help' for more information."
+ exit 1
+ ;;
+ *)
+ echo -e "Command not found: '${1}'"
+ usage
+ exit 1
+ ;;
+esac
diff --git a/include/defs.h b/include/defs.h
new file mode 100644
index 0000000..9932af1
--- /dev/null
+++ b/include/defs.h
@@ -0,0 +1,14 @@
+#ifndef DEFS_H
+#define DEFS_H
+
+#ifdef STAGE1
+ #define BLSEC_TEXT __attribute__((section(".stage1.text")))
+ #define BLSEC_DATA __attribute__((section(".stage1.data")))
+#elif STAGE2
+ #define BLSEC_DATA __attribute__((section(".stage2.data")))
+ #define BLSEC_DATA __attribute__((section(".stage2.data")))
+#else
+ #error "You must define a stage to compile the libraries!"
+#endif
+
+#endif // DEFS_H
diff --git a/include/printk.h b/include/printk.h
new file mode 100644
index 0000000..08a2778
--- /dev/null
+++ b/include/printk.h
@@ -0,0 +1,10 @@
+#ifndef PRINTK_H
+#define PRINTK_H
+
+#include <defs.h>
+
+#include <stdint.h>
+
+void write(char *str, uint8_t color) BLSEC_TEXT;
+
+#endif
diff --git a/include/string.h b/include/string.h
new file mode 100644
index 0000000..7c1caa5
--- /dev/null
+++ b/include/string.h
@@ -0,0 +1,9 @@
+#ifndef STRING_H
+#define STRING_H
+
+#include <defs.h>
+#include <stddef.h>
+
+size_t strlen(char* s) BLSEC_TEXT;
+
+#endif
diff --git a/include/vga.h b/include/vga.h
new file mode 100644
index 0000000..66a5441
--- /dev/null
+++ b/include/vga.h
@@ -0,0 +1,16 @@
+#ifndef VGA_H
+#define VGA_H
+
+#include <defs.h>
+#include <stdint.h>
+
+#define VGA_MEMORY_ADDR (volatile uint16_t*)0xB8000;
+
+#define MAX_WIDTH 80
+#define MAX_HEIGHT 25
+
+void vga_setup(int maxw, int maxh) BLSEC_TEXT;
+uint8_t vga_color(uint8_t fg, uint8_t bg) BLSEC_TEXT;
+void vga_put(uint32_t x, uint32_t y, unsigned char uc, uint8_t color) BLSEC_TEXT;
+
+#endif
diff --git a/lib/printk.c b/lib/printk.c
new file mode 100644
index 0000000..e2ae612
--- /dev/null
+++ b/lib/printk.c
@@ -0,0 +1,36 @@
+#include <printk.h>
+
+#include <string.h>
+#include <vga.h>
+
+#include <stddef.h>
+
+void write(char *s, uint8_t color)
+{
+ static size_t x BLSEC_DATA = 0;
+ static size_t y BLSEC_DATA = 0;
+
+ size_t len = strlen(s);
+ for (size_t i = 0; i < len; i++) {
+
+ if (s[i] == '\n') {
+ y++;
+ x = 0;
+ continue;
+ }
+
+ vga_put(x, y, s[i], color);
+
+ if (y >= MAX_HEIGHT)
+ return; //TODO: To be implemented.
+
+
+ if (x >= MAX_WIDTH) {
+ y++;
+ x = 0;
+ } else {
+ x++;
+ }
+
+ }
+}
diff --git a/lib/string.c b/lib/string.c
new file mode 100644
index 0000000..b48ffc0
--- /dev/null
+++ b/lib/string.c
@@ -0,0 +1,13 @@
+#include <string.h>
+
+size_t strlen(char* s)
+{
+ int pos=0;
+
+ while (s[pos]) {
+
+ pos++;
+ }
+
+ return pos;
+}
diff --git a/lib/vga.c b/lib/vga.c
new file mode 100644
index 0000000..9727025
--- /dev/null
+++ b/lib/vga.c
@@ -0,0 +1,23 @@
+#include <vga.h>
+#include <stddef.h>
+
+static uint16_t* vga_buffer BLSEC_DATA = VGA_MEMORY_ADDR;
+
+void vga_setup(int xmax, int ymax)
+{
+ for (size_t i = 0; i < MAX_WIDTH; i++) {
+ for (size_t j = 0; j < MAX_HEIGHT; j++) {
+ vga_put(i, j, ' ', 0);
+ }
+ }
+}
+
+uint8_t vga_color(uint8_t fg, uint8_t bg)
+{
+ return (uint8_t) fg | (uint8_t) bg << 4;
+}
+
+void vga_put(uint32_t x, uint32_t y, unsigned char uc, uint8_t color)
+{
+ vga_buffer[y*80+x] = (uint16_t) uc | (uint16_t) color << 8;
+}
diff --git a/linker.ld b/linker.ld
new file mode 100644
index 0000000..65d8abd
--- /dev/null
+++ b/linker.ld
@@ -0,0 +1,45 @@
+ENTRY(_start)
+OUTPUT_FORMAT("binary")
+
+SECTIONS {
+ /DISCARD/ : {
+ *(.comment)
+ *(.data)
+ }
+
+ . = 0x7c00;
+
+ .mbr 0x7c00 : AT(0x0) {
+ *(.mbr)
+ }
+
+ .mbr_data 0x7d00 : AT(0x100) {
+ *(.mbr_data)
+ }
+
+ . = 0x7c00 + 0x1FE;
+ .mbr_magic : AT(0x1FE) {
+ BYTE(0x55)
+ BYTE(0xAA)
+ }
+
+ .stage1 0x7e00 : AT(0x200) {
+ *(.stage1)
+ *(.stage1_ro)
+ *(.rodata*)
+ *(.stage1.text)
+ *(.stage1.data)
+ }
+
+ pad 0x91ff : AT(0x1600) { /* 11th block */
+ BYTE(0)
+ }
+
+ _stack_start = .;
+
+ .stack 0x99ff : AT(0x1cff) { /* 15th block */
+ BYTE(0)
+ }
+
+ _stack_end = .;
+}
diff --git a/stage1/32bit.c b/stage1/32bit.c
new file mode 100644
index 0000000..1e4a7ef
--- /dev/null
+++ b/stage1/32bit.c
@@ -0,0 +1,22 @@
+#define STAGE1
+#include "stage1.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <vga.h>
+#include <printk.h>
+#include <string.h>
+
+void centry(void)
+{
+ vga_setup(25,80);
+
+ uint8_t color = vga_color(0xF, 0);
+
+ write("Bootloader is set to 32-bit mode.", color);
+
+ while (1) {
+ asm ("cli");
+ asm ("hlt");
+ }
+}
diff --git a/stage1/32bit.o b/stage1/32bit.o
new file mode 100755
index 0000000..bd221e3
--- /dev/null
+++ b/stage1/32bit.o
Binary files differ
diff --git a/stage1/entry.S b/stage1/entry.S
new file mode 100644
index 0000000..81390d9
--- /dev/null
+++ b/stage1/entry.S
@@ -0,0 +1,56 @@
+.intel_syntax noprefix
+
+.section .mbr, "ax"
+
+.extern _stack_start
+.extern _stack_end
+.extern centry
+
+.global _start
+.code16
+
+_start:
+ xor ax, ax
+ mov ds, ax
+ jmp 0x0:_entry
+
+_entry:
+ mov ah, 0x42
+ mov si, offset dap
+
+ int 0x13
+
+ jc error
+
+ mov ah, 0x0E
+ mov al, 'S'
+ mov bh, 0x0
+ mov bl, 0x0
+
+ int 0x10
+
+ jmp 0x0:stage1_sector
+
+ cli
+ hlt
+
+error:
+ mov ah, 0x0e
+ mov al, 'E'
+ mov bh, 0x0
+ mov bl, 0x0
+
+ int 0x10
+
+ cli
+ hlt
+
+.section .mbr_data, "aw"
+
+dap:
+ .byte 0x10
+ .byte 0
+ .word 14
+ .word 0x7e00
+ .word 0x0
+ .quad 1
diff --git a/stage1/stage1.S b/stage1/stage1.S
new file mode 100644
index 0000000..e11e36b
--- /dev/null
+++ b/stage1/stage1.S
@@ -0,0 +1,84 @@
+.intel_syntax noprefix
+.code16
+
+.extern _stack_start
+.extern _stack_end
+
+.global stage1_sector
+
+.section .stage1, "ax"
+
+stage1_sector:
+ xor ax, ax
+
+ mov ah, 0x0e
+ mov al, 'C'
+ mov bh, 0x0
+ mov bl, 0x0
+
+ int 0x10
+
+ cli
+
+ lgdt gdtr
+
+ mov eax, cr0
+ or eax, (1 << 0)
+ mov cr0, eax
+
+ jmp 0x8:_32_start
+
+ cli
+ hlt
+
+.code32
+_32_start:
+ mov eax, 0x10
+
+ mov ds, ax
+ mov ss, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ jmp 0x8:_32_entry
+
+ cli
+ hlt
+
+_32_entry:
+ mov eax, offset _stack_start
+ mov ebp, eax
+ mov eax, offset _stack_end
+ mov esp, eax
+
+ call centry
+
+ cli
+ hlt
+
+.code16
+.section .stage1_ro, "aw"
+
+gdt:
+ .quad 0x0 // Null seg
+
+ .word 0xFFFF // Code seg
+ .word 0x0000
+ .byte 0x0
+ .byte 0b10011011 // A=1 RW=1 DC=0 E=1 S=1 DPL=00 P=1
+ .byte 0b11001111
+ .byte 0x0000
+
+ .word 0xFFFF // Data Seg
+ .word 0x0000
+ .byte 0x0
+ .byte 0b10010011 // A=1 RW=1 DC=0 E=0 S=1 DPL=00 P=1
+ .byte 0b11001111
+ .byte 0x0000
+
+end_gdt:
+
+gdtr:
+ .word end_gdt - gdt - 1
+ .long gdt
diff --git a/stage1/stage1.h b/stage1/stage1.h
new file mode 100644
index 0000000..cf29acb
--- /dev/null
+++ b/stage1/stage1.h
@@ -0,0 +1,8 @@
+#ifndef STAGE1_H
+#define STAGE1_H
+
+#include <defs.h>
+
+void centry(void) BLSEC_TEXT;
+
+#endif