diff options
| -rw-r--r-- | README.md | 32 | ||||
| -rw-r--r-- | TODO | 8 | ||||
| -rwxr-xr-x | build.sh | 231 | ||||
| -rw-r--r-- | include/defs.h | 14 | ||||
| -rw-r--r-- | include/printk.h | 10 | ||||
| -rw-r--r-- | include/string.h | 9 | ||||
| -rw-r--r-- | include/vga.h | 16 | ||||
| -rw-r--r-- | lib/printk.c | 36 | ||||
| -rw-r--r-- | lib/string.c | 13 | ||||
| -rw-r--r-- | lib/vga.c | 23 | ||||
| -rw-r--r-- | linker.ld | 45 | ||||
| -rw-r--r-- | stage1/32bit.c | 22 | ||||
| -rwxr-xr-x | stage1/32bit.o | bin | 0 -> 796 bytes | |||
| -rw-r--r-- | stage1/entry.S | 56 | ||||
| -rw-r--r-- | stage1/stage1.S | 84 | ||||
| -rw-r--r-- | stage1/stage1.h | 8 |
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. @@ -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 Binary files differnew file mode 100755 index 0000000..bd221e3 --- /dev/null +++ b/stage1/32bit.o 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 |
