From a7fb48922dee006339747d128d83fafbe9dfe79a Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sun, 23 Apr 2017 20:38:39 +0200 Subject: fresh import because of huge documents checked in by accident --- README | 66 +++++++++++++++++++++++ README.Wyoos | 23 ++++++++ src/Makefile | 29 ++++++++++ src/boot.asm | 121 +++++++++++++++++++++++++++++++++++++++++ src/gdt.asm | 43 +++++++++++++++ src/kernel.c | 10 ++++ src/stage1_functions.asm | 136 +++++++++++++++++++++++++++++++++++++++++++++++ src/stage2_functions.asm | 108 +++++++++++++++++++++++++++++++++++++ src/switch_mode.asm | 38 +++++++++++++ 9 files changed, 574 insertions(+) create mode 100644 README create mode 100644 README.Wyoos create mode 100644 src/Makefile create mode 100644 src/boot.asm create mode 100644 src/gdt.asm create mode 100644 src/kernel.c create mode 100644 src/stage1_functions.asm create mode 100644 src/stage2_functions.asm create mode 100644 src/switch_mode.asm diff --git a/README b/README new file mode 100644 index 0000000..8f082d8 --- /dev/null +++ b/README @@ -0,0 +1,66 @@ +Requirements +------------ + +nasm +gcc, clang or tcc + +Debugging +--------- + +hexdump -C image.bin | less + +ndisasm -b16 -o7c00h -a image.bin | less + +objdump -M intel -d kernel.o | less + +gcc -m32 -ffreestanding -c -o kernel.o kernel.c +ld -o kernel.bin -Ttext 0x8000 kernel.o -m elf_i386 --oformat binary +objdump -M intel -d kernel.bin | less + +# oformat: objdump -i lists tons of formats +# -m: ld -V +# -T: set text segment ot start at BIOS block 3 +# TEXT means code + +bochs -q 'boot:floppy' 'floppya: 1_44=image.bin, status=inserted' + +Some interesting breakpoints: + +boot loader (boot.asm, stage 1) +break 0x7c00 + +boot loeader after switching to protecting mode: +break 0x7e4e + +Booting from 0000:7c00 +(0) Breakpoint 1, 0x0000000000007c00 in ?? () +Next at t=14040244 +(0) [0x000000007c00] 0000:7c00 (unk. ctxt): mov ax, 0x0000 ; b80000 + +links +----- + +https://www.youtube.com/watch?v=YvZhgRO7hL4&list=UUjrLiYrvbpXR37c0HV4PmqA + +http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-manual-325462.html +http://wiki.osdev.org/Global_Descriptor_Table +https://en.wikipedia.org/wiki/INT_10H +http://www.osdever.net/FreeVGA/vga/crtcreg.htm +http://wiki.osdev.org/Text_Mode_Cursor +https://en.wikipedia.org/wiki/Calling_convention +http://wiki.osdev.org/Main_Page + +other project links +------------------- + +http://www.helenos.org +https://github.com/cfenollosa/os-tutorial +http://wyoos.org: in C++ +http://osdev.org +https://github.com/Codepixl/CodeOS +http://www.osdever.net/tutorials/ +http://lowelevel.eu +http://download.tyndur.org/releases/0.2/ +https://code.google.com/archive/p/fpos/source/default/source, FreePascal +https://mirage.io/ +http://www.jamesmolloy.co.uk/tutorial_html/ diff --git a/README.Wyoos b/README.Wyoos new file mode 100644 index 0000000..7bf4909 --- /dev/null +++ b/README.Wyoos @@ -0,0 +1,23 @@ +IDT interupt descriptor table + +GDT global descriptor table +- set up segments +- code segments, RX, never W +- data segments, RW, never X +- 16-bit compatibility on Intel, so quite a nightmare +- asm( LGDT ) needed +=> we do GTD all in assembly, not really readable! Ideal would be to + do as much as possible in C/C++ (even with some zero-overhead + C++17 crazyness maybe) and then call a pure assembly function with + a pointer to the GTD doing the 'LGDT' assembly operation + +types: +- have local typedefs for DWORD32 or uint32_t (as Windows and POSIX did + it) + +keyboard: +- 0x20 PIC +- asm( outb( port, byte)), outb function => better have ports knowing + their own bandwith. + SlowPort8, slowing down assembly code + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..ed04a72 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,29 @@ +CC := gcc + +all: image.bin + +image.bin: boot.bin kernel.bin + cat boot.bin kernel.bin > image.bin + truncate -s 1536 image.bin + +boot.bin: boot.asm gdt.asm stage1_functions.asm stage2_functions.asm switch_mode.asm + nasm boot.asm -f bin -o boot.bin + +kernel.bin: kernel.o + ld -o kernel.bin -Ttext 0x8000 kernel.o -m elf_i386 --oformat binary + +kernel.o: kernel.c + $(CC) -m32 -ffreestanding -c -o kernel.o kernel.c + +clean: + -rm -f boot.bin kernel.bin image.bin *.o + +run-qemu: image.bin + qemu-system-i386 -m 16 -drive "file=image.bin,if=ide,format=raw" + +run-bochs: + bochs -q 'boot:floppy' 'floppya: 1_44=image.bin, status=inserted' + + +functions.o: functions.c + gcc -m32 -ffreestanding -c -o functions.o functions.c diff --git a/src/boot.asm b/src/boot.asm new file mode 100644 index 0000000..f053270 --- /dev/null +++ b/src/boot.asm @@ -0,0 +1,121 @@ +; legacy BIOS PC-boot code, starts in 16-bit real mode + +; BIOS always loads us to this location + [org 0x7c00] + +; 16-bit real-mode + [bits 16] + +; initialize segment registers + mov ax, 0 + mov ds, ax + mov es, ax + mov ss, ax + +; dl contains the boot drive, primarily because that's the last function +; called by the BIOS MBR loader, we remember that for loading additional +; blocks from the boot medium + mov [BOOT_DRIVE], dl + +; make sure we know the location of the stack by setting it on our own + mov bp, 0xFFFF + mov sp, bp + +; print out some information about CPU mode and BIOS boot drive + mov si, MESSAGE_REAL_MODE + call print_string + + mov si, MESSAGE_BOOT_DRIVE + call print_string + mov ax, dx + call print_hex + call print_newline + +; print we are going to load stage 2 of the boot blocks + mov si, MESSAGE_LOADING_STAGE_2 + call print_string + + mov dl, [BOOT_DRIVE] + call read_from_disk + + jmp stage2 + +%include "stage1_functions.asm" + +DISK_ERROR: + db "DISK ERROR ", 0 + +SHORT_READ: + db "DISK SHORT READ", 0 + +MESSAGE_REAL_MODE: + db "Started in 16-bit Real Mode", 13, 10, 0 + +MESSAGE_BOOT_DRIVE: + db "Booting from drive ", 0 + +MESSAGE_LOADING_STAGE_2: + db "Loading stage 2 boot loader", 13, 10, 0 + +BOOT_DRIVE: + db 0 + +; pad rest of sector with zeroes so we get 512 bytes in the end +times 510-($-$$) db 0 + +; magic number of a boot sector +dw 0xaa55 + +stage2: + mov si, MESSAGE_STAGE_2_LOADED + call print_string + +; remember current position on screen + call current_row + mov [CURSOR_Y], dh + + call switch_to_protected_mode + +; we should never get here, but just in case + jmp $ + +MESSAGE_STAGE_2_LOADED: + db "Stage 2 boot sectors loaded", 13, 10, 0 + +%include "switch_mode.asm" + +[bits 32] + +BEGIN_PROTECTED_MODE: + mov si, MESSAGE_PROTECTED_MODE + call pm_print_string + call pm_print_newline + + mov si, MESSAGE_CALL_C_ENTRY + call pm_print_string + call pm_print_newline + +; call our kernel + call c_entry + + mov si, MESSAGE_HALTED + call pm_print_string + call pm_print_newline + + jmp $ + +MESSAGE_PROTECTED_MODE: + db "Switched to 32-bit Protected Mode", 0 + +MESSAGE_CALL_C_ENTRY: + db "Calling C entry function", 0 + +MESSAGE_HALTED: + db "Operating system halted", 0 + +%include "stage2_functions.asm" + +; make sure we have full sectors +times 1024-($-$$) db 0 + +c_entry: diff --git a/src/gdt.asm b/src/gdt.asm new file mode 100644 index 0000000..15fe924 --- /dev/null +++ b/src/gdt.asm @@ -0,0 +1,43 @@ +; GDT global descriptor table + +gdt_start: + +; mandatory null entry +gdt_null: + dd 0x0 + dd 0x0 + +; code segment +; base=0x0, limit=0xfffff +; flags: present (not paged), ring 0, executable, direction bit +; conforming, writable, not accessed +; granularity: 4kb pages, 32-bit mode, no 64-bit segment, AVL would +; be for our own extensions +gdt_code: + dw 0xffff + dw 0x0 + db 0x0 + db 10011010b + db 11001111b + db 0x0 + +; flat model, same as code segment, but flags are +; flags: ring 0, +gdt_data: + dw 0xffff + dw 0x0 + db 0x0 + db 10010010b + db 11001111b + db 0x0 + +gdt_end: + +gdt_descriptor: + dw gdt_end -gdt_start - 1 ; size + dd gdt_start ; start address of the GDT + +; constants representing the segment bases +CODE_SEGMENT equ gdt_code - gdt_start +DATA_SEGMENT equ gdt_data - gdt_start + diff --git a/src/kernel.c b/src/kernel.c new file mode 100644 index 0000000..d94f960 --- /dev/null +++ b/src/kernel.c @@ -0,0 +1,10 @@ +void entry( ) +{ + char *VIDEO_MEMORY = (char *)0xb8000; + char *bar = "\\|/-"; + char backup = *VIDEO_MEMORY; + for( int i = 0; i < 20000000; i++ ) { + *VIDEO_MEMORY = bar[i%4]; + } + *VIDEO_MEMORY = backup; +} diff --git a/src/stage1_functions.asm b/src/stage1_functions.asm new file mode 100644 index 0000000..2ca3eb2 --- /dev/null +++ b/src/stage1_functions.asm @@ -0,0 +1,136 @@ +; IN bx: begin of memory area to dump +; IN ax: number of words to dump +dump_range: + mov dx, bx + call print_hex + mov dx, [bx] + call print_hex + call print_newline + add bx, 2 + dec ax + cmp ax, 0 + jnz dump_range + ret + +; IN bx: hex value to print +print_hex: + push bx + push si + mov si, HEX_TEMPLATE + mov bx, dx + shr bx, 12 + mov bx, [HEXABET+bx] + mov [HEX_TEMPLATE+2], bl + mov bx, dx + and bx, 0x0FFF + shr bx, 8 + mov bx, [HEXABET+bx] + mov [HEX_TEMPLATE+3], bl + mov bx, dx + and bx, 0x00FF + shr bx, 4 + mov bx, [HEXABET+bx] + mov [HEX_TEMPLATE+4], bl + mov bx, dx + and bx, 0x000F + mov bx, [HEXABET+bx] + mov [HEX_TEMPLATE+5], bl + call print_string + pop si + pop bx + ret + +HEX_TEMPLATE: + db '0x???? ', 0 + +HEXABET: + db '0123456789ABCDEF' + +print_newline: + push ax + mov al, 10 + call print_char + mov al, 13 + call print_char + pop ax + ret + +; IN si +print_string: + push ax +_loop_print_string: + lodsb + cmp al, 0 + je print_string_fini + call print_char + jmp _loop_print_string +print_string_fini: + pop ax + ret + +; IN al: character to print +; MOD ah +print_char: + mov ah, 0x0e + int 0x10 + ret + +fat_cursor: + push ax + push cx + mov ah, 0x01 + mov cx, 0x0007 + int 0x10 + pop cx + pop ax + ret + +; OUT: current row +current_row: + push ax + push bx + push cx + mov ah, 0x03 + mov bh, 0 + int 0x10 + pop cx + pop bx + pop ax + ret + +; IN dl: drive to read from +read_from_disk: + + mov ah, 0x02 ; read sectors from drive + + mov al, 2 ; read 1 sector + mov ch, 0 ; select first cylinder + mov dh, 0 ; first head + mov cl, 2 ; second sector after boot sector + + mov bx, 0 ; where to store the data + mov es, bx + mov bx, 0x7e00 ; 512 bytes after first sector + + int 0x13 + + jc read_error + + cmp al, 2 ; 1 sector read? + jne short_read ; if not, short read + + ret + +read_error: + mov si, DISK_ERROR + call print_string + mov dh, 0 + mov dl, ah + call print_hex + jmp $ + +short_read: + mov si, SHORT_READ + call print_string + jmp $ + diff --git a/src/stage2_functions.asm b/src/stage2_functions.asm new file mode 100644 index 0000000..018cf6a --- /dev/null +++ b/src/stage2_functions.asm @@ -0,0 +1,108 @@ +VIDEO_MEMORY equ 0xb8000 + +VIDEO_COLS equ 80 +VIDEO_ROWS equ 25 + +CURSOR_X: + dw 0 + +CURSOR_Y: + dw 0 + +pm_print_newline: + push eax + mov [CURSOR_X], word 0 + mov ax, [CURSOR_Y] + inc ax + mov [CURSOR_Y], ax + pop eax + call update_vga_cursor + ret + +; IN si +pm_print_string: + push eax +_loop_pm_print_string: + lodsb + cmp al, 0 + je pm_print_string_fini + call pm_print_char + jmp _loop_pm_print_string +pm_print_string_fini: + pop eax + ret + +; IN al: character to print +pm_print_char: + push edx + push ecx + push ebx + push eax + mov ax, [CURSOR_Y] + mov cl, VIDEO_COLS + mul cl + mov bx, [CURSOR_X] + add ax, bx + shl ax, 1 + mov edx, VIDEO_MEMORY + add dx, ax + pop eax + mov ah, 0x07 + mov [edx], ax + pop ebx + pop ecx + pop edx + call inc_cursor + ret + +inc_cursor: + push eax + mov ax, [CURSOR_X] + inc ax + mov [CURSOR_X], ax + cmp ax, VIDEO_COLS + jl inc_cursor_fini + mov [CURSOR_X], word 1 + mov ax, [CURSOR_Y] + inc ax + mov [CURSOR_Y], ax + cmp ax, VIDEO_ROWS + jl inc_cursor_fini + ; TODO: scoll one line, for now restart on top + mov [CURSOR_Y], word 0 +inc_cursor_fini: + call update_vga_cursor + pop eax + ret + +; update the VGA cursor on screen +update_vga_cursor: + push ebx + push ecx + push edx + mov al, [CURSOR_Y] + mov cl, VIDEO_COLS + mul cl + mov bx, ax + movzx ax, [CURSOR_X] + add bx, ax + mov cx, bx + ;cursor LOW port to vga INDEX register + mov al, 0fh ;Cursor Location Low Register -- + mov dx, 3d4h ;VGA port 3D4h + out dx, al + mov ax, cx ;restore 'postion' back to AX + mov dx, 3d5h ;VGA port 3D5h + out dx, al ;send to VGA hardware + ;cursor HIGH port to vga INDEX register + mov al, 0eh + mov dx, 3d4h ;VGA port 3D4h + out dx, al + mov ax, cx ;restore 'position' back to AX + shr ax, 8 ;get high byte in 'position' + mov dx, 3d5h ;VGA port 3D5h + out dx, al ;send to VGA hardware + pop edx + pop ecx + pop ebx + ret diff --git a/src/switch_mode.asm b/src/switch_mode.asm new file mode 100644 index 0000000..32cca0b --- /dev/null +++ b/src/switch_mode.asm @@ -0,0 +1,38 @@ +[bits 16] + +%include "gdt.asm" + +switch_to_protected_mode: + ; switch off interrupts for now + cli + + ; load GDT (global descriptor table) + lgdt [gdt_descriptor] + + ; do the actual switch + mov eax, cr0 + or eax, 0x1 + mov cr0, eax + + ; unconditional far jump into code segment + jmp CODE_SEGMENT:init_pm + +[bits 32] + +; initialize registers and stack for protected mode +init_pm: + mov ax, DATA_SEGMENT + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; a new stack + mov ebp, 0x90000 + mov esp, ebp + + call BEGIN_PROTECTED_MODE + +[bits 16] + -- cgit v1.2.3-54-g00ecf