master
Garrett Mills 3 years ago
commit b7156335cd
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246

2
.gitignore vendored

@ -0,0 +1,2 @@
build/
.idea/

@ -0,0 +1,54 @@
BUILD_DIR ?= ./build
SRC_DIRS ?= ./src
BOOTLOADER_DIR ?= ./asm/boot
SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s)
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP -g -std=c++11 -Wall
# Default makefile target
all: os-image
run: os-image
qemu-system-x86_64 -hda build/os-image.bin
# Builds a bootable x86 image that loads the kernel
os-image: $(BUILD_DIR)/kernel.bin $(BUILD_DIR)/bootloader.bin
cat $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/kernel.bin > build/os-image.bin
# Builds the binary kernel image with the entry prefix
$(BUILD_DIR)/kernel.bin: $(BUILD_DIR)/kernel_entry.o $(OBJS)
ld -m elf_i386 -o $@ -Ttext 0x1000 --oformat binary $(BUILD_DIR)/kernel_entry.o $(OBJS)
# Builds the x86 -> 32-bit bootloader
$(BUILD_DIR)/bootloader.bin: $(BOOTLOADER_DIR)/main16.asm
cd $(BOOTLOADER_DIR); nasm -f bin -o ../../build/bootloader.bin main16.asm
# Builds the kernel entry object file
$(BUILD_DIR)/kernel_entry.o: asm/boot.asm
$(MKDIR_P) $(dir $@)
nasm asm/boot.asm -f elf -o $@
# Build C++ sources
$(BUILD_DIR)/%.cpp.o: %.cpp
$(MKDIR_P) $(dir $@)
g++ $(CPPFLAGS) $(CXXFLAGS) -m32 -ffreestanding -c $< -o $@ -fno-pie
# Build C sources
$(BUILD_DIR)/%.c.o: %.c
$(MKDIR_P) $(dir $@)
gcc $(CFLAGS) $(CXXFLAGS) -m32 -ffreestanding -c $< -o $@ -fno-pie
# Clean all build objects
.PHONY: clean
clean:
$(RM) -rf $(BUILD_DIR)
-include $(DEPS)
MKDIR_P ?= mkdir -p

@ -0,0 +1,8 @@
; NOT part of the bootloader
; this is the kernel entry code that calls main() in kernel.cpp
[bits 32] ; we're in 32-bit protected mode at this point
[extern main] ; going to call main in kernel.c
call main
jmp $

@ -0,0 +1,10 @@
[bits 16]
; Catch end-of-execution in an infinite loop
g_infinite_loop:
jmp g_infinite_loop
; Fill in the remaining space with 00s and set the
; magic bit to make the bios boot the sector.
times 510-($-$$) db 0
dw 0xaa55

@ -0,0 +1,27 @@
[bits 16]
i_disk16_error_message: db "Disk read error!", 0
disk16_load:
push dx ; keep track of the # of sectors requested to be read
mov ah, 0x02 ; bios read sector
mov al, dh ; read dh # sectors
mov ch, 0x00 ; select cylinder 0
mov dh, 0x00 ; select head 0
mov cl, 0x02 ; start reading from 2nd sector
; (i.e. the sector after the boot sector)
int 0x13 ; bios interrupt to trigger read
jc disk16_on_error ; jump if carry (fault) flag was set
pop dx ; restore original # of registers in case it was modified
cmp dh, al ; compare # sectors read (al) to # sectors expected (dh)
jne disk16_on_error ; error if # sectors differs
ret
disk16_on_error:
mov bx, i_disk16_error_message
call print16
jmp g_infinite_loop

@ -0,0 +1,38 @@
[bits 16]
; Sets up the GDT for the switch to 32-bit protected mode
gdt_setup_flat:
gdt_flat_null: ; mandatory null descriptor
dd 0x0 ; dd means define double-word (i.e. 4 bytes)
dd 0x0
gdt_flat_code_section:
; base: 0x0, limit: 0xfffff
; 1st flags: (present) 1 (privilege) 00 (descriptor type) 1 -> 1001b
; Type flags: (code) 1 (conforming) 0 (readable) 1 (accessed) 0 -> 1010b
; 2nd flags: (granularity) 1 (32-bit default) 1 (64-bit segment) 0 (AVL) 0 -> 1100b
dw 0xffff ; limit (bits 0 - 15)
dw 0x0 ; base (bits 0 - 15)
db 0x0 ; base (bits 16 - 23)
db 10011010b ; 1st flags, type flags
db 11001111b ; 2nd flags, limit (bits 16 - 19)
db 0x0 ; base (bits 24 - 31)
gdt_flat_data_section:
; Same as code segment except for the type flags:
; type flags: (code) 0 (expand down) 0 (writable) 1 (accessed) 0 -> 0010b
dw 0xffff ; limit (bits 0 - 15)
dw 0x0 ; base (bits 0 - 15)
db 0x0 ; base (bits 16 - 23)
db 10010010b ; 1st flags, type flags
db 11001111b ; 2nd flags, limit (bits 16 - 19)
db 0x0 ; base (bits 24 - 31)
gdt_flat_end: ; so the assembler can calculate the size of the gdt section
gdt_flat_descriptor:
dw gdt_flat_end - gdt_setup_flat - 1 ; size of our GDT, always less 1 of the true size
dd gdt_setup_flat ; start address of the gdt
i_gdt_code_segment equ gdt_flat_code_section - gdt_setup_flat
i_gdt_data_segment equ gdt_flat_data_section - gdt_setup_flat

@ -0,0 +1,27 @@
; this offsets all memory addresses in the asm code
; to be relative to the start of the assembled bytecode
; in memory, where the BIOS loads it.
[org 0x7c00]
[bits 16]
; store the boot drive - bios puts in dl
mov [i_global_boot_drive], dl
jmp main16 ; start the main label
i_global_boot_drive: db 0
; Helper label that pops all stack registers and returns
; Used as a one-line return in service routines
; e.g. jmp global_return
g_return:
popa
ret
; Include common code
%include "print16.asm"
%include "disk16.asm"
%include "gdt16.asm"
%include "switch16.asm"
%include "kern16.asm"
%include "print32.asm"

@ -0,0 +1,19 @@
[bits 16]
; Loads the 32-bit kernel
i_kernel16_string: db 'Loading 32-bit kernel...', 0
i_kernel16_offset equ 0x1000
kernel16_load:
pusha
mov bx, i_kernel16_string
call print16
mov bx, i_kernel16_offset ; specify the disk load location
mov dh, 15 ; load first 15 sectors
mov dl, [i_global_boot_drive] ; from the boot disk
call disk16_load
jmp g_return

@ -0,0 +1,20 @@
[bits 16]
%include "header.asm"
i_16bit_string: db 'Started 16-bit real mode.', 0
main16:
mov bp, 0x9000 ; move the stack out of the way of anything important
mov sp, bp
mov bx, i_16bit_string
call print16
call kernel16_load ; load the kernel
call switch16_to_32 ; never returns. Calls main32.
jmp g_infinite_loop
%include "main32.asm"
%include "bootable.asm"

@ -0,0 +1,11 @@
[bits 32]
i_32bit_string: db 'Switched to 32-bit protected mode.', 0
main32:
mov ebx, i_32bit_string
call print32 ; print the 32-bit protected mode message
call i_kernel16_offset ; jump to the address of the loaded kernel
jmp g_infinite_loop ; hang

@ -0,0 +1,76 @@
[bits 16]
; Prints \r\n using BIOS teletype output
print16_newline:
push ax
mov ah, 0x0e ; bios teletype output
mov al, 0x0a ; newline char
int 0x10
mov al, 0x0d ; carriage return
int 0x10
pop ax
ret
; Prints a null-terminated string beginning in the addr in bx
print16_string:
pusha
print16_string_char_loop:
mov al, [bx] ; move current value of bx into lower-half of ax
cmp al, 0 ; check if char is null
je g_return ; if so, end of string, so return
; otherwise, print the character
mov ah, 0x0e ; bios teletype output
int 0x10
; Now, advance mem addr of bx by 1 to point to next char
add bx, 1
jmp print16_string_char_loop
; Calls print16_string then print16_newline
print16:
call print16_string
call print16_newline
ret
; Template for printing hex strings
i_print16_hex_template: db '0x0000', 0
; Prints the value of dx register as a hex-string
print16_hex_string:
pusha
mov cx, 0 ; init looping variable
print16_hex_string_loop:
cmp cx, 4 ; check if looping is == 4
je print16_hex_string_end ; if so, break the loop
mov ax, dx ; copy the hex value to a mutable register
and ax, 0x000f ; mask off all but the last bit
add al, 0x30 ; convert the number to ASCII 0-9
cmp al, 0x39 ; check if number is 0-9 or a-f
jle print16_hex_string_update_template ; if 0-9, no further adding needed
add al, 39 ; otherwise, a-f so offset to proper ASCII range
print16_hex_string_update_template:
mov bx, i_print16_hex_template + 5 ; end of ascii template
sub bx, cx ; make bx point to currently-updated char
mov [bx], al ; copy ASCII char we computed above into current position of bx
shr dx, 4 ; shift dx over 1 byte so we can compute next char
add cx, 1 ; increment looping var
jmp print16_hex_string_loop
print16_hex_string_end:
mov bx, i_print16_hex_template ; copy the filled-out template addr to bx
call print16_string ; print it out
jmp g_return
; Calls print16_hex_string then print16_newline
print16_hex:
call print16_hex_string
call print16_newline
ret

@ -0,0 +1,80 @@
[bits 32]
; 32-bit protected print helpers
; VGA is 80 x 25 characters starting at 0xb8000
; 1st byte is ASCII code of char to display, 2nd byte is display info
; Address of particular row/col as: 0xb8000 + 2 * (row * 80 + col)
; Constants
i_video_memory equ 0xb8000
i_white_on_black equ 0x0f
; Variables
i_print32_row_start equ 0xb8000
i_print32_row_current equ 0xb8000
i_print32_row_end equ 0xb8002 + 160
print32_raw_simple:
; prints null-terminated string pointed to by edx
pusha
mov edx, i_video_memory ; set edx to start of the row
print32_raw_simple_loop:
mov al, [ebx] ; store char at ebx in al
mov ah, i_white_on_black ; store display info in ah
cmp al, 0 ; if al is null, then this is the end of the string
je g_return
mov [edx], ax ; store char and attributes at current char cell
add ebx, 1 ; increment ebx to next char in string
add edx, 2 ; move to next char cell in video memory
jmp print32_raw_simple_loop
print32_newline: ; TODO fix this...
pusha
mov eax, i_print32_row_start
mov ebx, i_print32_row_current
mov ecx, i_print32_row_end
add eax, 0xa0
add ebx, 0xa0
mov ecx, eax
jmp g_return
print32_raw:
; prints null-terminated string pointed to by edx
pusha
mov edx, i_print32_row_current ; set edx to start of the row
jmp print32_raw_loop
print32_raw_next_line:
call print32_newline
print32_raw_loop:
cmp edx, i_print32_row_end
je print32_raw_next_line
mov al, [ebx] ; store char at ebx in al
mov ah, i_white_on_black ; store display info in ah
cmp al, 0 ; if al is null, then this is the end of the string
je g_return
mov [edx], ax ; store char and attributes at current char cell
add ebx, 1 ; increment ebx to next char in string
add edx, 2 ; move to next char cell in video memory
jmp print32_raw_loop
print32:
call print32_raw
call print32_newline
ret
[bits 16]

@ -0,0 +1,30 @@
[bits 16]
switch16_to_32:
cli ; disable interrupts until we finish setup, since the
; 16-bit interrupt vector will be invalid
lgdt [gdt_flat_descriptor] ; load the GDT which defines the flat code and data segments
mov eax, cr0 ; move the control register to eax so we can modify it
or eax, 0x1 ; set the 1st bit to enable 32-bit pm
mov cr0, eax ; write the modified control value back
jmp i_gdt_code_segment:initialize32 ; far-jump to our new 32-bit code, forcing CPU to clear the pipeline of 16-bit instructions
[bits 32]
initialize32:
; Set up registers and stack once in 32-bit protected mode
mov ax, i_gdt_data_segment ; point the segment registers to the new data segment in 32-bit
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; update stack position to be at the top of free space
mov esp, ebp
call main32

@ -0,0 +1,8 @@
// Called by boot.asm
void main() {
// Create pointer to a char and point to the first text cell of VGA
char* video_memory = (char*) 0xb8000;
// Write the char X to the video memory to display it on the top-left of screen
*video_memory = 'X';
}
Loading…
Cancel
Save