]> git.dujemihanovic.xyz Git - nameless-os.git/commitdiff
Merge branch 'drive' master origin/master
authorDuje Mihanović <duje.mihanovic@skole.hr>
Fri, 17 Jun 2022 19:26:27 +0000 (21:26 +0200)
committerDuje Mihanović <duje.mihanovic@skole.hr>
Fri, 17 Jun 2022 19:26:53 +0000 (21:26 +0200)
This way that branch can be deleted without any history being lost.

30 files changed:
.gitignore
Makefile
boot.s [deleted file]
boot/x86/.gitignore [new file with mode: 0644]
boot/x86/Makefile [new file with mode: 0644]
boot/x86/disk.dump [new file with mode: 0644]
boot/x86/fat32/fat32-structs.s [new file with mode: 0644]
boot/x86/fat32/fat32.s [new file with mode: 0644]
boot/x86/mbr.s [new file with mode: 0644]
boot/x86/stage3/a20.s [new file with mode: 0644]
boot/x86/stage3/gdt.s [new file with mode: 0644]
boot/x86/stage3/loader.s [new file with mode: 0644]
boot/x86/stage3/print.s [new file with mode: 0644]
boot/x86/stage3/unreal.s [new file with mode: 0644]
boot/x86/vbr-fat32.s [new file with mode: 0644]
disk.s [deleted file]
how-to-compile [new file with mode: 0644]
include/arch/x86/io.h [new file with mode: 0644]
include/arch/x86/irq/i8259a.h [new file with mode: 0644]
include/arch/x86/irq/idt.h [new file with mode: 0644]
include/arch/x86/tty.h [new file with mode: 0644]
kernel/arch/x86/irq/idt.c [new file with mode: 0644]
kernel/arch/x86/irq/sample_handler.c [new file with mode: 0644]
kernel/arch/x86/tty/tty.c [new file with mode: 0644]
kernel/drivers/irq/i8259a.c [new file with mode: 0644]
kernel/entry.s [new file with mode: 0644]
kernel/kernel.c [new file with mode: 0644]
kernel/linker.ld [new file with mode: 0644]
nameless2disk [new file with mode: 0755]
print.s [deleted file]

index 12ab2c11bd258a0abd8440e078d4790162de5bd6..9fc5bb791de04c0037818d3ffd4aa4f55db8b732 100644 (file)
@@ -1,2 +1,7 @@
 toolchain/
-boot
+boot.img
+boot/x86/boot
+**/*.o
+**/*.bin
+**/*.elf
+**/*.dbg
index a85a94acd141033159828870efa184d744d3b681..aee8cc187a36d118823f8b1e66abedbbd4a99d87 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,18 +1,55 @@
-AS = yasm # choose yasm or nasm here
-ASFLAGS = -f bin # compile a raw binary
+export AS = yasm
+export CC = i686-elf-gcc
+QEMU = qemu-system-i386 -monitor stdio
 
-boot: boot.s print.s disk.s
-       $(AS) $(ASFLAGS) -o $@ boot.s
+export GIT_REV = $(shell git describe --long HEAD)
 
-clean:
-       -rm boot
+CFLAGS = -std=gnu89 -g -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_REV)\"
+
+KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/kernel.o
+BOOTLOADER_OBJ = boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN
+
+default: kernel/kernel.elf bootloader
+
+all: default boot/x86/disk.img
+
+bootloader: $(BOOTLOADER_OBJ)
+
+run: all
+       $(QEMU) boot/x86/disk.img
 
-run:
-       qemu-system-i386 boot
+boot/x86/mbr: boot/x86/mbr.s
+boot/x86/vbr-fat32: boot/x86/vbr-fat32.s boot/x86/fat32/*.s 
+boot/x86/stage3/LOADER.BIN: boot/x86/stage3/*.s boot/x86/fat32/*.s
+$(BOOTLOADER_OBJ):
+       $(MAKE) -C boot/x86
 
-help:
-       @echo "Run 'make' to compile."
-       @echo "Run 'make clean' to delete compiled objects."
-       @echo "Run 'make run' to run the compiled object. Must have qemu-system-i386 (but x86_64 should work too)."
+boot/x86/disk.img: boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN boot/x86/disk.dump kernel/kernel.bin
+       truncate -s1G boot/x86/disk.img
+       sfdisk boot/x86/disk.img < boot/x86/disk.dump
+       mkfs.fat -F 32 --offset 2048 boot/x86/disk.img
+       dd if=boot/x86/mbr of=boot/x86/disk.img bs=440 count=1 conv=notrunc
+       dd if=boot/x86/vbr-fat32 of=boot/x86/disk.img bs=1 skip=90 seek=1048666 conv=notrunc
+       mcopy -i boot/x86/disk.img@@1M boot/x86/stage3/LOADER.BIN ::.
+       mcopy -i boot/x86/disk.img@@1M kernel/kernel.bin ::./KERNEL.BIN
+
+kernel/kernel.bin: ${KERNEL_OBJ} kernel/linker.ld
+       $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ}
+
+kernel/entry.o: kernel/entry.s
+       $(AS) -f elf $< -o $@
+
+kernel/arch/x86/irq/sample_handler.o: kernel/arch/x86/irq/sample_handler.c
+       $(CC) $(CFLAGS) -mgeneral-regs-only -c $< -o $@
+
+kernel/kernel.elf: kernel/kernel.bin
+       $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ} -Wl,--oformat=elf32-i386
+       i686-elf-objcopy --only-keep-debug kernel/kernel.elf kernel/kernel.dbg
+       i686-elf-objcopy --add-gnu-debuglink=kernel/kernel.dbg kernel/kernel.elf
+       i686-elf-strip --strip-unneeded kernel/kernel.elf
+
+clean:
+       -rm kernel/kernel.{bin,dbg,elf} ${KERNEL_OBJ}
+       cd boot/x86 && $(MAKE) clean
 
-.PHONY = clean run help
+.PHONY: default all clean run bootloader
diff --git a/boot.s b/boot.s
deleted file mode 100644 (file)
index 3a8adc7..0000000
--- a/boot.s
+++ /dev/null
@@ -1,33 +0,0 @@
-       bits 16 ; boot sectors run in real mode
-       org 7c00h ; boot sector is loaded at 0x7c00
-
-_start
-       mov [boot_drive], dl ; BIOS sets dl to number of boot drive, we don't want to forget this
-
-       mov bp, 8000h ; Move the stack away
-       mov sp, bp
-
-       push cs
-       pop es
-       mov bx, 9000h ; Load at 0000:9000
-       mov dh, 2 ; Load 5 sectors
-       mov dl, [boot_drive] ; Load from boot drive
-       call read_sectors
-
-       mov dx, [9000h] ; Print value at 9000h, should be 0xDEAD
-       call print_word 
-       mov dx, [9000h + 512] ; Print value at 9200h, should be 0xBEEF
-       call print_word
-
-       jmp $ ; Endless loop
-
-%include "print.s"
-%include "disk.s"
-
-boot_drive resb 1 ; Reserve 1 byte for boot drive number
-
-times 510-($-$$) db 0 ; fill with 0 until 0x1fc is reached
-dw 0AA55h ; BIOS MBR signature
-
-times 256 dw 0DEADh ; Fill in 2nd sector with 0xDEAD
-times 256 dw 0BEEFh ; Fill in 3rd sector with 0xBEEF
diff --git a/boot/x86/.gitignore b/boot/x86/.gitignore
new file mode 100644 (file)
index 0000000..ef679a9
--- /dev/null
@@ -0,0 +1,4 @@
+mbr
+vbr-fat32
+LOADER.BIN
+disk.img
diff --git a/boot/x86/Makefile b/boot/x86/Makefile
new file mode 100644 (file)
index 0000000..1de8199
--- /dev/null
@@ -0,0 +1,15 @@
+default: mbr vbr-fat32 stage3/LOADER.BIN
+
+mbr: mbr.s
+       $(AS) $(ASFLAGS) -w-zeroing -o $@ $<
+
+vbr-fat32: vbr-fat32.s fat32/*.s
+       $(AS) $(ASFLAGS) -o $@ $<
+
+stage3/LOADER.BIN: stage3/loader.s stage3/*.s
+       $(AS) $(ASFLAGS) -DGIT_REVISION=\"$(GIT_REV)\" -o $@ $<
+
+clean:
+       -rm mbr vbr-fat32 stage3/LOADER.BIN disk.img
+
+.PHONY : default clean
diff --git a/boot/x86/disk.dump b/boot/x86/disk.dump
new file mode 100644 (file)
index 0000000..ebf63ac
--- /dev/null
@@ -0,0 +1,7 @@
+label: dos
+label-id: 0x8bb0766f
+device: disk.img
+unit: sectors
+sector-size: 512
+
+disk.img1 : start=        2048, size=     2095104, type=c, bootable
diff --git a/boot/x86/fat32/fat32-structs.s b/boot/x86/fat32/fat32-structs.s
new file mode 100644 (file)
index 0000000..e72735d
--- /dev/null
@@ -0,0 +1,47 @@
+; FAT32 data structures
+; Because they have to be defined before use
+
+struc dir_entry
+       .name: resb 11
+       .attr: resb 1
+       .ntres: resb 1
+       .crttimetenth: resb 1
+       .crttime: resw 1
+       .crtdate: resw 1
+       .lstaccdate: resw 1
+       .firstclushi: resw 1
+       .wrttime: resw 1
+       .wrtdate: resw 1
+       .firstcluslo: resw 1
+       .filesize: resd 1
+endstruc
+
+; BPB definition, we use offsets to bp to save space
+       %define BS_jmpBoot [bp]
+       %define BS_OemName [bp+0x3]
+       %define BPB_BytsPerSec [bp+0xb]
+       %define BPB_SecPerClus [bp+0xd]
+       %define BPB_RsvdSecCnt [bp+0xe]
+       %define BPB_NumFats [bp+0x10]
+       %define BPB_RootEntCnt [bp+0x11]
+       %define BPB_TotSec16 [bp+0x13]
+       %define BPB_Media [bp+0x15]
+       %define BPB_FatSz16 [bp+0x16] ; count of sectors
+       %define BPB_SecPerTrk [bp+0x18]
+       %define BPB_NumHeads [bp+0x1a]
+       %define BPB_HiddSec [bp+0x1c]
+       %define BPB_TotSec32 [bp+0x20]
+       %define BPB_FatSz32 [bp+0x24]
+       %define BPB_ExtFlags [bp+0x28]
+       %define BPB_FSVer [bp+0x2a]
+       %define BPB_RootClus [bp+0x2c]
+       %define BPB_FsInfo [bp+0x30]
+       %define BPB_BkBootSec [bp+0x32] ; number of sector
+       %define BPB_Reserved [bp+0x34]
+       %define BS_DrvNum [bp+0x40]
+       %define BS_Reserved1 [bp+0x41]
+       %define BS_BootSig [bp+0x42]
+       %define BS_VolId [bp+0x43]
+       %define BS_VolLab [bp+0x47]
+       %define BS_FilSysType [bp+0x52]
+
diff --git a/boot/x86/fat32/fat32.s b/boot/x86/fat32/fat32.s
new file mode 100644 (file)
index 0000000..41d5688
--- /dev/null
@@ -0,0 +1,150 @@
+; FAT32 driver
+; BOOT_DRIVE, si and bp must be set up by the code using this driver.
+; (Specifically, si must be a pointer to the partition's partition
+; table entry and bp must be a pointer to the BPB.)
+; Before reading any cluster chains, callers must call get_1st_data_sec
+; and must be careful not to trash ecx after calling it.
+
+; eax - start cluster number
+; es:di - where to load the cluster chain
+; RETURN: CF set = error
+; NOTE: Cluster chain might be the root directory or a file.
+read_cluster_chain:
+       push eax
+       push es
+       push di
+       .loop:
+               call read_cluster
+               jc .done
+               cmp eax, 0xffffff7
+               jl .loop
+.done:
+       pop di
+       pop es
+       pop eax
+       ret
+
+; eax - cluster number
+; es:di - where to load the cluster (incremented automatically)
+; RETURN: eax - next cluster number
+;        CF set = error
+read_cluster:
+       push ebx
+       push ecx
+       push edx
+.loop:
+       ; get the first sector of the cluster to read
+       push eax
+       sub eax, 2
+       xor ebx, ebx
+       mov bl, BPB_SecPerClus
+       mul ebx
+       add eax, ecx
+       mov ebx, eax
+       pop eax
+
+       ; read the cluster
+       push cx
+       movzx cx, BPB_SecPerClus
+       call read_sectors
+       pop cx
+       cmp dl, 0
+       je .error
+
+       ; increment the load offset (and segment if needed)
+       push eax
+       xor eax, eax
+       movzx ax, BPB_SecPerClus
+       mul word BPB_BytsPerSec
+       add di, ax
+       jno .get_next_cluster
+       mov ax, es
+       add ax, 0x1000
+       mov es, ax
+
+.get_next_cluster:
+       pop eax
+       ; get FAT sector number and offset containing the next cluster number
+       xor ebx, ebx
+       mov bl, 4 ; put 4 in EBX, doing this instead of "mov ebx, 4" saves us an entire byte
+       mul ebx
+       movzx ebx, word BPB_BytsPerSec
+       div ebx
+       movzx ebx, word BPB_RsvdSecCnt
+       add eax, ebx
+       add eax, [si]
+       mov ebx, edx
+       ; reminder for myself: EAX is FAT sector number, (E)BX is offset into FAT sector
+       
+       ; load the FAT sector we're looking for
+       push di
+       push ebx ; offset
+       push es ; we want to read at 0:800, not STAGE3_SEGMENT:800
+       push eax ; desired LBA
+       xor ax, ax
+       mov es, ax
+       pop ebx ; pop LBA into EBX
+       mov di, 0x800
+       mov cx, 1
+       call read_sectors
+       cmp dl, 0
+       ; find the cluster we're looking for
+       pop es ; restore STAGE3_SEGMENT
+       pop ebx ; pop FAT offset back into EBX for cmp
+       mov eax, [di+bx]
+       pop di
+       jne .end
+       ; cleanup and return
+.error:
+       stc
+.end:
+       pop edx
+       pop ecx
+       pop ebx
+       ret
+
+; es:di - where to load the sector(s)
+; ebx - start LBA address
+; cx - how many sectors to read
+; RETURN: DL=0 means error
+read_sectors:
+       clc
+       push ax
+       push si
+       mov ah, 0x42
+       push dword 0
+       push dword ebx
+       push es
+       push di
+       push cx
+       push word 0x10
+       mov si, sp
+       mov dl, [BOOT_DRIVE]
+       int 0x13
+       mov dl, 1
+       jnc .done
+       mov dl, 0
+.done:
+       add sp, 16
+       pop si
+       pop ax
+       ret
+
+; ds:si - pointer to partition table entry
+; RETURN: ecx - first data sector
+get_1st_data_sec:
+       push eax
+       push ebx
+       movzx ax, BPB_NumFats 
+       mul dword BPB_FatSz32 ; space occupied by all FATs
+       movzx ebx, word BPB_RsvdSecCnt
+       add eax, ebx ; space occupied by the reserved area
+       add eax, [si] ; space before the partition
+       mov ecx, eax
+       pop ebx
+       pop eax
+       ret
+
+read_error: db "Read error", 0
+
+BOOT_DRIVE: db 0
diff --git a/boot/x86/mbr.s b/boot/x86/mbr.s
new file mode 100644 (file)
index 0000000..b5d01c9
--- /dev/null
@@ -0,0 +1,179 @@
+; x86 bootloader for nameless-os, MBR portion
+
+bits 16
+cpu 686
+org 0x600
+
+_start:
+       cli ; we really don't want to be interrupted during relocation
+       ; set up segment registers
+       xor ax, ax
+       mov ds, ax
+       mov es, ax
+       mov ss, ax
+       mov sp, 0xfe00 ; just under the soon-to-be-loaded VBR, should be more than sufficient space
+
+       ; perform self-relocation
+       cld
+       mov si, 0x7c00
+       mov di, 0x600
+       mov cx, 0x100 ; 256 words = 512 bytes
+       rep movsw
+       ; some BIOSes may set CS to 0x7c0, work around that
+       jmp 0:real_start
+real_start:
+       sti
+       ; check for int 13h ext
+       call check_int13_ext
+       mov byte [BOOT_DRIVE], dl
+       ; look for active partition
+       mov si, part_1
+       mov cx, 4
+       .check_part_loop:
+               mov al, [si]
+               test al, 0x80
+               jnz .part_found
+               add si, 16
+               loop .check_part_loop
+               .error:
+                       mov si, no_os
+                       call print
+                       hlt
+                       jmp short $-1
+       .part_found:
+               ; look for any other active partitions, if they exist the partition table is invalid
+               cmp cx, 0
+               je .load_vbr
+               push si 
+       .look_other_loop:
+               add si, 16
+               mov al, [si]
+               test al, 0x80
+               jnz .invalid_mbr
+               loop .look_other_loop
+       .load_vbr:
+               ; load active partition's VBR
+               pop si
+               mov ax, 0xfe0
+               add si, 8
+               mov edx, [si]
+               xor bx, bx
+               call read_sector
+               ; check is the VBR bootable (ends with 0x55 0xaa), if not halt
+               cmp word [0xfffe], 0xaa55
+               jne .not_bootable
+               mov dl, [BOOT_DRIVE]
+               call 0xfe00
+               .not_bootable:
+                       mov si, no_os
+                       call print
+                       hlt
+                       jmp short $-1
+       .invalid_mbr:
+               mov si, invalid_mbr
+               call print
+               hlt
+               jmp short $-1
+check_int13_ext:
+       pusha
+       mov ah, 0x41
+       mov bx, 0x55aa
+       mov dl, 0x80
+       int 0x13
+       jc .no_ext
+       test cx, 1
+       jz .no_ext
+       popa
+       ret
+       .no_ext:
+               mov si, no_int13_ext
+               call print
+               hlt
+               jmp short $-1
+
+; ax = segment
+; bx = offset
+; edx = start LBA of where to read
+read_sector:
+       pusha
+       ; set up temporary DAP
+       push dword 0
+       push edx
+       push ax
+       push bx
+       push word 1
+       push word 0x10
+       mov ah, 0x42
+       mov dl, byte [BOOT_DRIVE]
+       mov si, sp
+       int 0x13
+       jc .error
+       add sp, 16 ; dump our temporary DAP
+       popa
+       ret
+       .error:
+               mov si, read_fail
+               call print
+               hlt
+               jmp short $-1
+
+; DS:SI = string
+print:
+       pusha
+       mov ah, 0xe
+       xor bh, bh
+.loop:
+       lodsb
+       cmp al, 0
+       je .done
+       int 10h
+       jmp .loop
+.done:
+       mov al, 0xd
+       int 10h
+       mov al, 0xa
+       int 10h
+       popa
+       ret
+
+BOOT_DRIVE: resb 1
+
+read_fail: db "Error reading stage 2", 0
+invalid_mbr: db "Invalid partition table", 0
+no_os: db "No OS found", 0
+no_int13_ext: db "INT 13h extensions not found", 0
+
+times 440-($-$$) db 0
+
+part_table:
+disk_id: dd 0
+reserved: dw 0
+part_1:
+       .attrib db 0
+       .chs_start times 3 db 0
+       .part_type db 0
+       .chs_end times 3 db 0
+       .lba_start dd 0
+       .sect_count dd 0
+part_2:
+       .attrib db 0
+       .chs_start times 3 db 0
+       .part_type db 0
+       .chs_end times 3 db 0
+       .lba_start dd 0
+       .sect_count dd 0
+part_3:
+       .attrib db 0
+       .chs_start times 3 db 0
+       .part_type db 0
+       .chs_end times 3 db 0
+       .lba_start dd 0
+       .sect_count dd 0
+part_4:
+       .attrib db 0
+       .chs_start times 3 db 0
+       .part_type db 0
+       .chs_end times 3 db 0
+       .lba_start dd 0
+       .sect_count dd 0
+signature: dw 0xaa55
diff --git a/boot/x86/stage3/a20.s b/boot/x86/stage3/a20.s
new file mode 100644 (file)
index 0000000..8748dde
--- /dev/null
@@ -0,0 +1,74 @@
+%macro in_wait 0
+       push ax
+%%loop:
+       in al, 0x64
+       test al, 2
+       jnz %%loop
+       pop ax
+%endmacro
+
+%macro out_wait 0
+       push ax
+%%loop:
+       in al, 0x64
+       test al, 1
+       jz %%loop
+       pop ax
+%endmacro
+
+; CF set = A20 enabled
+check_a20:
+       push esi
+       push edi
+       push ax
+       mov esi, 0x500
+       mov edi, 0x100500
+
+       mov al, [esi]
+       mov ah, [edi]
+
+       mov [esi], byte 0
+       mov [edi], byte 0xff
+       cmp [esi], byte 0xff
+       
+       mov [esi], al
+       mov [edi], ah
+
+       pop ax
+       pop edi
+       pop esi
+       ret
+
+enable_a20:
+       push ax
+       mov ax, 0x2401
+       int 0x15
+       jc .i8042_method
+       call check_a20
+       jnc .i8042_method
+       pop ax
+       ret
+       .i8042_method:
+               in_wait
+               mov al, 0xad
+               out 0x64, al
+               in_wait
+               mov al, 0xd0
+               out 0x64, al
+               out_wait
+               in al, 0x60
+               or al, 2
+               push ax
+               in_wait
+               mov al, 0xd1
+               out 0x64, al
+               in_wait
+               pop ax
+               out 0x60, al
+               in_wait
+               mov al, 0xae
+               out 0x60, al
+               call check_a20
+               pop ax
+               ret
+
diff --git a/boot/x86/stage3/gdt.s b/boot/x86/stage3/gdt.s
new file mode 100644 (file)
index 0000000..d81bb64
--- /dev/null
@@ -0,0 +1,21 @@
+; Initial GDT for kernel, which wants a flat code and data descriptor
+gdt:
+       dw .end-.start-1
+       dd .start
+.start: dq 0 ; null descriptor
+.code:
+       dw 0xffff
+       dw 0
+       db 0
+       db 10011010b
+       db 11001111b
+       db 0
+.data:
+       dw 0xffff
+       dw 0
+       db 0
+       db 10010010b
+       db 11001111b
+       db 0
+.end:
+
diff --git a/boot/x86/stage3/loader.s b/boot/x86/stage3/loader.s
new file mode 100644 (file)
index 0000000..ec08c61
--- /dev/null
@@ -0,0 +1,188 @@
+bits 16
+cpu 686
+org 0x1800
+
+%include "../fat32/fat32-structs.s"
+
+%macro print 1
+       push si
+       mov si, %1
+       call print_str
+       pop si
+%endmacro
+
+_start:
+       mov [BOOT_DRIVE], dl
+       call enable_unreal
+       print begin
+       call check_a20
+       jc .a20_enabled
+       call enable_a20
+       jnc .a20_enable_fail
+.a20_enabled:
+       print a20_enabled
+       call get_1st_data_sec
+       mov ax, 0x1000
+       mov es, ax
+       mov eax, BPB_RootClus
+       xor di, di
+       call read_cluster_chain
+       jc critical_error
+
+       push cx
+       push si
+.find_kernel:
+       mov si, kernel_name
+       cmp byte [es:di], 0 ; end of root directory
+       je .kernel_missing
+       cmp byte [es:di], 0xe5 ; unused entry
+       je .increment
+       mov cx, 11
+       push si
+       push di
+       repe cmpsb
+       pop di
+       pop si
+       je .kernel_found
+.increment:
+       add di, 32
+       jno .find_kernel
+       mov bx, es
+       add bx, 0x1000
+       mov es, bx
+       jmp .find_kernel
+.kernel_found:
+       pop si
+       pop cx
+       print kernel_found
+       mov ax, [es:di+dir_entry.firstclushi]
+       shl eax, 16
+       mov ax, [es:di+(dir_entry.firstcluslo)]
+       call print_dword
+       mov edi, 0x100000
+       print kernel_loading
+       call read_clus_chain_unreal ; load kernel
+       print kernel_loaded
+       
+       cli
+       lgdt [gdt]
+       mov eax, cr0
+       or al, 1
+       mov cr0, eax
+       jmp 0x8:in_protected
+.kernel_missing:
+       print missing_kernel
+       jmp .halt
+
+.a20_enable_fail:
+       print a20_fail
+.halt:
+       hlt
+       jmp $-1
+
+; eax - start cluster
+; edi - address to load to
+read_clus_chain_unreal:
+       push eax
+       push edi
+.loop:
+       push di
+       push es
+       push ax
+       xor di, di
+       mov ax, 0x1000
+       mov es, ax
+       pop ax
+       call read_cluster
+       pop es
+       pop di
+       jc .done
+       push esi
+       push eax
+       push ebx
+       push ecx
+       mov esi, 0x10000
+       xor ebx, ebx
+       movzx eax, word BPB_BytsPerSec
+       movzx bx, byte BPB_SecPerClus
+       mul ebx
+       mov ecx, eax
+       call memcpy
+       add edi, ecx
+       pop ecx
+       pop ebx
+       pop eax
+       pop esi
+       cmp eax, 0xffffff7
+       jl .loop
+.done:
+       pop edi
+       pop eax
+       ret
+
+; esi - copy from
+; edi - copy to
+; ecx - bytes to copy
+memcpy:
+       push esi
+       push edi
+       push ecx
+       push dx
+
+.loop:
+       mov dl, [esi]
+       mov [edi], dl
+       dec ecx
+       cmp ecx, 0
+       je .done
+       inc esi
+       inc edi
+       jmp .loop
+.done:
+       pop dx
+       pop ecx
+       pop edi
+       pop esi
+       ret
+
+
+%include "unreal.s"
+%include "a20.s"
+%include "../fat32/fat32.s"
+%include "gdt.s"
+%include "print.s"
+
+in_protected:
+bits 32
+       mov ax, 0x10
+       mov ds, ax
+       mov es, ax
+       mov ss, ax
+       mov fs, ax
+       mov gs, ax
+       call 0x100000
+       hlt
+       jmp $-1
+
+kernel_name: db "KERNEL  BIN"
+begin: db "Nameless Bootloader revision ", GIT_REVISION, 0xd, 0xa, 0
+a20_enabled: db "A20 has been enabled", 0xd, 0xa, "Searching for kernel...", 0xd, 0xa, 0
+a20_fail: db "Failed to enable A20, giving up!", 0xd, 0xa, 0
+crit_err: db "A critical error occurred, dumping registers now: ", 0xd, 0xa, 0
+kernel_found: db "Found kernel at cluster ", 0
+kernel_loading: db 0xd, 0xa, "Loading kernel...", 0xd, 0xa, 0
+kernel_loaded: db "Kernel successfully loaded.", 0xd, 0xa, "Setting up kernel environment and running kernel...", 0xd, 0xa, 0
+missing_kernel: db "Could not find KERNEL.BIN", 0xd, 0xa, 0
+eax_s: db "EAX: ", 0
+ebx_s: db "EBX: ", 0
+ecx_s: db "ECX: ", 0
+edx_s: db "EDX: ", 0
+esi_s: db "ESI: ", 0
+edi_s: db "EDI: ", 0
+cs_s: db "CS: ", 0
+ds_s: db "DS: ", 0
+es_s: db "ES: ", 0
+ss_s: db "SS: ", 0
+space: db " ", 0
+hex_delm: db "0x", 0
+newline: db 0xd, 0xa, 0
diff --git a/boot/x86/stage3/print.s b/boot/x86/stage3/print.s
new file mode 100644 (file)
index 0000000..73b34d5
--- /dev/null
@@ -0,0 +1,126 @@
+; Routines for printing
+
+print_str:
+       push ax
+       push bx
+       push si
+       mov ah, 0xe
+       xor bh, bh
+       mov bl, 0xf
+.loop:
+       lodsb
+       cmp al, 0
+       je .done
+       int 0x10
+       jmp .loop
+.done:
+       pop si
+       pop bx
+       pop ax
+       ret
+
+; AL - nibble to print
+; CF - clear prints low nibble, set prints high nibble
+print_nibble:
+       print hex_delm
+.no_delm:
+       pushf
+       push ax
+       push bx
+       pushf
+       xor bh, bh
+       mov bl, 0xf
+       mov ah, 0xe
+       popf
+       jnc .no_shift
+       shr al, 4
+.no_shift:
+       and al, 0xf
+       cmp al, 0xa
+       jge .letter
+       .numeric:
+               add al, '0'
+               int 0x10
+               jmp .done
+       .letter:
+               add al, 'a'-10
+               int 0x10
+.done:
+       pop bx
+       pop ax
+       popf
+       ret
+
+; AL - byte to print
+print_byte:
+       print hex_delm
+.no_delm:
+       pushf
+       stc
+       call print_nibble.no_delm
+       clc
+       call print_nibble.no_delm
+       popf
+       ret
+       
+print_word:
+       print hex_delm
+.no_delm:
+       ror ax, 8
+       call print_byte.no_delm
+       ror ax, 8
+       call print_byte.no_delm
+       ret
+
+print_dword:
+       print hex_delm
+.no_delm:
+       ror eax, 16
+       call print_word.no_delm
+       ror eax, 16
+       call print_word.no_delm
+       ret
+
+; Do a dump of all general purpose regs
+critical_error:
+       print crit_err
+       print eax_s
+       call print_dword
+       print space
+       print ebx_s
+       mov eax, ebx
+       call print_dword
+       print space
+       print ecx_s
+       mov eax, ecx
+       call print_dword
+       print space
+       print edx_s
+       mov eax, edx
+       call print_dword
+       print newline
+       print esi_s
+       mov eax, esi
+       call print_dword
+       print space
+       print edi_s
+       mov eax, edi
+       call print_dword
+       print newline
+       print cs_s
+       mov ax, cs
+       call print_word
+       print space
+       print ds_s
+       mov ax, ds
+       call print_word
+       print space
+       print es_s
+       mov ax, es
+       call print_word
+       print space
+       print ss_s
+       mov ax, ss
+       call print_word
+       hlt
+       jmp $-1
diff --git a/boot/x86/stage3/unreal.s b/boot/x86/stage3/unreal.s
new file mode 100644 (file)
index 0000000..cdd361b
--- /dev/null
@@ -0,0 +1,40 @@
+; Routine for enabling unreal mode, which allows using 32-bit offsets in real mode
+
+enable_unreal:
+       cli
+       push eax
+       push bx
+       push ds
+
+       lgdt [gdt_info]
+       mov eax, cr0
+       or al, 1
+       mov cr0, eax
+
+       jmp $+2
+
+       mov bx, 0x8
+       mov ds, bx
+
+       and al, 0xfe
+       mov cr0, eax
+
+       pop ds
+       pop bx
+       pop eax
+       sti
+       ret
+
+; GDT with 1 flat data segment descriptor
+gdt_info:
+       dw gdt_end-gdt_start-1
+       dd gdt_start
+gdt_start: dq 0 ; null entry
+gdt_flat:
+       dw 0xffff
+       dw 0
+       db 0
+       db 10010010b
+       db 11001111b
+       db 0
+gdt_end:
diff --git a/boot/x86/vbr-fat32.s b/boot/x86/vbr-fat32.s
new file mode 100644 (file)
index 0000000..439157f
--- /dev/null
@@ -0,0 +1,112 @@
+; x86 bootloader for nameless-os, FAT32 VBR portion
+; This is what's going to be on most USB sticks and HDDs, for now
+
+bits 16
+org 0xfe00
+cpu 686
+
+STAGE3_ADDRESS equ 0x1800
+STAGE3_SEGMENT equ STAGE3_ADDRESS >> 4
+STAGE3_OFFSET equ STAGE3_ADDRESS & 0xf
+
+%include "fat32/fat32-structs.s"
+
+%macro print 1
+       mov si, %1
+       call print_str
+%endmacro
+
+_start:
+       jmp short real_start
+       nop
+
+times 0x57 db 0 ; skip past BPB
+
+real_start:
+       sti
+       ; no need to set up segments and stack again, because MBR did it for us
+       mov bp, 0xfe00
+
+       ; we expect the boot drive to be in DL and our partition table entry in DS:SI
+       mov [BOOT_DRIVE], dl
+
+       ; calculate the 1st sector of the data area
+       call get_1st_data_sec
+
+.load_root:
+       ; load the root directory
+       mov ax, 0x1000
+       mov es, ax
+       xor di, di
+       mov eax, BPB_RootClus
+       call read_cluster_chain
+
+       push cx
+       mov cx, 11
+       push si
+.find_stage_3:
+       mov si, STAGE3_NAME
+       cmp byte [es:di], 0 ; we have no more entries to look at
+       je .stage3_missing
+       cmp byte [es:di], 0xe5 ; the entry was only previously used and as such not worth looking at
+       je .increment
+       repe cmpsb
+       je .stage3_found
+.increment:
+       add di, 32
+       jno .find_stage_3
+       mov bx, es
+       add bx, 0x1000
+       mov es, bx
+.stage3_found:
+       pop si
+       pop cx
+       add di, 9 ; knowing that cmpsb incremented this by 11, we can also increment it by 9
+                      ; right here so we can use 1 less offset below
+       ; stage 3 has been found and ES:DI points to its directory entry
+       mov ax, [es:di] ; load high half of the 1st cluster
+       shl eax, 16
+       mov ax, [es:di+(dir_entry.firstcluslo-dir_entry.firstclushi)] ; load low half of the 1st cluster
+       mov bx, STAGE3_SEGMENT
+       mov es, bx
+       mov di, STAGE3_OFFSET
+       call read_cluster_chain ; read stage 3
+       mov dl, [BOOT_DRIVE]
+       call STAGE3_ADDRESS ; call stage 3
+       jmp .halt ; halt in case we return, which should never happen
+
+.stage3_missing:
+       print stage3_missing
+       jmp .halt
+.error:
+       print read_error
+.halt:
+       hlt
+       jmp short $-1
+
+; ds:si - string
+print_str:
+       pusha
+       mov ah, 0xe
+       xor bh, bh
+.loop:
+       lodsb
+       cmp al, 0
+       je .done
+       int 0x10
+       jmp .loop
+.done:
+       mov al, 0xd
+       int 0x10
+       mov al, 0xa
+       int 0x10
+       popa
+       ret
+
+%include "fat32/fat32.s"
+
+stage3_missing: db "LOADER.BIN is missing", 0
+STAGE3_NAME: db "LOADER  BIN"
+
+times 510-($-$$) db 0
+dw 0xaa55
diff --git a/disk.s b/disk.s
deleted file mode 100644 (file)
index b281c3a..0000000
--- a/disk.s
+++ /dev/null
@@ -1,39 +0,0 @@
-; ES:BX - where to load
-; DH - sector count
-; DL - drive number
-
-read_sectors:
-       xor bl, bl
-       mov di, disk_reading
-       call print
-       push dx
-       mov ah, 02h
-       mov al, dh
-       mov ch, 0
-       mov dh, 0
-       mov cl, 2
-       
-       int 13h
-
-       jc read_error
-
-       pop dx
-       cmp dh, al
-       jne read_error
-       ret
-       
-read_error:
-       xor bl, bl
-       mov di, disk_error
-       call print
-       mov dl, ah
-       xor dh, dh
-       call print_word
-       mov dl, al
-       call print_word
-       jmp $
-
-disk_reading db "Reading from disk...", 0
-disk_error db "Failed to read from disk! Take a look at this error code:", 0
-
-       ret
diff --git a/how-to-compile b/how-to-compile
new file mode 100644 (file)
index 0000000..c84edb4
--- /dev/null
@@ -0,0 +1,11 @@
+1. Compile an i686-elf GCC and binutils following the instructions on this page:
+   https://wiki.osdev.org/GCC_Cross-Compiler
+   Or use some tool to automate it, like I use crossdev on my Gentoo machine.
+
+2. Install yasm, or modify the makefile to use nasm if you prefer it
+
+3. Run make in the source root
+
+4. Run the OS:
+   make run
+   This will create a disk image (if needed) and run it. Assumes that sfdisk, mtools and dosfstools are installed.
diff --git a/include/arch/x86/io.h b/include/arch/x86/io.h
new file mode 100644 (file)
index 0000000..09f6fa7
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef X86_IO_H
+#define X86_IO_H
+
+#include <stdint.h>
+
+static inline void outb(uint16_t port, uint8_t value)
+{
+       asm volatile ("outb %0, %1": : "a" (value), "Nd" (port));
+}
+
+static inline uint8_t inb(uint16_t port)
+{
+       uint8_t ret;
+       asm volatile ("inb %1, %0"
+                                       : "=a" (ret)
+                                       : "d" (port));
+       return ret;
+}
+
+static inline void io_wait(void)
+{
+       inb(0x80);
+}
+
+#endif
diff --git a/include/arch/x86/irq/i8259a.h b/include/arch/x86/irq/i8259a.h
new file mode 100644 (file)
index 0000000..31448e4
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef X86_I8259A_H
+#define X86_I8259A_H
+
+#define PIC1_COMMAND 0x20
+#define PIC1_DATA 0x21
+#define PIC2_COMMAND 0xA0
+#define PIC2_DATA 0xA1
+
+#define PIC_ICW1_INIT 0x10
+#define PIC_ICW1_ICW4 0x1
+
+#define PIC_ICW4_8086 0x1
+
+#define PIC_EOI 0x20
+
+extern void pic_init(int offset1, int offset2);
+extern void pic_mask(uint8_t irq);
+extern void pic_unmask(uint8_t irq);
+extern void pic_mask_all(void);
+extern void pic_unmask_all(void);
+extern void pic_send_eoi(uint8_t irq);
+
+#endif
diff --git a/include/arch/x86/irq/idt.h b/include/arch/x86/irq/idt.h
new file mode 100644 (file)
index 0000000..daf586f
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef X86_IDT_H
+#define X86_IDT_H
+
+#include <stdint.h>
+
+#define IDT_VECTOR_COUNT 256 /* our IDT will have 256 vectors */
+#define IDT_DESCRIPTOR_SIZE 8 /* each IDT descriptor is 8 bytes long */
+
+#define IDT_TRAP_GATE          0xF
+#define IDT_INTERRUPT_GATE     0xE
+
+struct idt_descriptor {
+       uint16_t offset_1; /* bits 0-15 of offset */
+       uint16_t segsel; /* segment selector */
+       unsigned unused : 8, type : 5, dpl : 2, present : 1;
+       uint16_t offset_2; /* bits 16-31 of offset */
+} __attribute__((packed));
+
+struct idtr {
+       uint16_t limit; /* size of IDT minus 1 */
+       uint32_t base; /* starting address of IDT */
+} __attribute__((packed));
+
+extern void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl);
+extern inline void load_idt(struct idtr idtr);
+extern inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt);
+
+#endif
diff --git a/include/arch/x86/tty.h b/include/arch/x86/tty.h
new file mode 100644 (file)
index 0000000..2224737
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef X86_TTY_H
+#define X86_TTY_H
+
+#include <stdint.h>
+
+#define VGA_COLOR_BLACK 0x0
+#define VGA_COLOR_BLUE 0x1
+#define VGA_COLOR_GREEN 0x2
+#define VGA_COLOR_TEAL 0x3
+#define VGA_COLOR_DARK_RED 0x4
+#define VGA_COLOR_MAGENTA 0x5
+#define VGA_COLOR_BROWN 0x6
+#define VGA_COLOR_LIGHT_GRAY 0x7
+#define VGA_COLOR_DARK_GRAY 0x8
+#define VGA_COLOR_PURPLE 0x9
+#define VGA_COLOR_LIME 0xA
+#define VGA_COLOR_CYAN 0xB
+#define VGA_COLOR_BRIGHT_RED 0xC
+#define VGA_COLOR_PINK 0xD
+#define VGA_COLOR_YELLOW 0xE
+#define VGA_COLOR_WHITE 0xF
+
+extern void screen_clear(void);
+extern void kprint(const char *string, uint8_t color);
+extern void kprintb(const uint8_t byte);
+extern void kprintw(const uint16_t word);
+extern void kprintd(const uint32_t dword);
+#endif
diff --git a/kernel/arch/x86/irq/idt.c b/kernel/arch/x86/irq/idt.c
new file mode 100644 (file)
index 0000000..54e8612
--- /dev/null
@@ -0,0 +1,27 @@
+#include <irq/idt.h>
+#include <stdint.h>
+
+/* Note to self: passing a pointer to an asm constraint as "m" will give a pointer to pointer */
+inline void load_idt(struct idtr idtr)
+{
+       asm volatile ("lidt %0": : "m" (idtr));
+}
+
+void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl)
+{
+       struct idt_descriptor *descriptor = &idt[vector];
+
+       descriptor->offset_1 = offset & 0xFFFF;
+       descriptor->segsel = segment;
+       descriptor->unused = 0;
+       descriptor->type = type;
+       descriptor->dpl = dpl;
+       descriptor->present = 1;
+       descriptor->offset_2 = offset >> 16;
+}
+
+inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt)
+{
+       idtr->limit = ((IDT_VECTOR_COUNT * IDT_DESCRIPTOR_SIZE) - 1); /* limit must be size - 1 */
+       idtr->base = (uint32_t) idt;
+}
diff --git a/kernel/arch/x86/irq/sample_handler.c b/kernel/arch/x86/irq/sample_handler.c
new file mode 100644 (file)
index 0000000..f4f4a4b
--- /dev/null
@@ -0,0 +1,33 @@
+#include <tty.h>
+#include <irq/i8259a.h>
+#include <io.h>
+#include <stdint.h>
+
+typedef uint32_t uword_t;
+
+struct interrupt_frame {
+       uword_t ip;
+       uword_t cs;
+       uword_t flags;
+};
+
+struct abort_frame;
+
+__attribute__((interrupt))
+void keyb_handler(struct interrupt_frame *frame)
+{
+       pic_send_eoi(1);
+       kprint("Got a keyboard interrupt!\n", 0);
+       inb(0x60);
+}
+
+
+__attribute__((interrupt))
+void double_fault(struct abort_frame *frame)
+{
+       *(volatile uint32_t *) (0xb8000) = 0xcf28cf3a;
+halt:
+       asm volatile ("cli; hlt");
+       goto halt;
+}
+
diff --git a/kernel/arch/x86/tty/tty.c b/kernel/arch/x86/tty/tty.c
new file mode 100644 (file)
index 0000000..07d734e
--- /dev/null
@@ -0,0 +1,139 @@
+#include <io.h>
+#include <tty.h>
+#include <stdint.h>
+
+#define VGA_WIDTH 80
+#define VGA_HEIGHT 25
+
+volatile char *video_memory = (char *) 0xB8000; /* VGA VRAM starts at 0xB8000 */
+
+static int cursor_x = 0; /* keep track of where cursor is */
+static int cursor_y = 0;
+
+char *hex_chars = "0123456789ABCDEF";
+
+void screen_clear(void)
+{
+       int x, y;
+       for ( y = 0; y < VGA_HEIGHT; y++ ) {
+               for ( x = 0; x < VGA_WIDTH; x++ ) {
+                       video_memory[(y * VGA_WIDTH + x) * 2 + 1] = VGA_COLOR_LIGHT_GRAY;
+                       video_memory[(y * VGA_WIDTH + x) * 2] = ' ';
+               }
+       }
+
+       cursor_x = 0;
+       cursor_y = 0;
+}
+
+void scroll_up(void)
+{
+       int x, y;
+       for ( y = 1; y < VGA_HEIGHT; y++ ) {
+               for ( x = 0; x < VGA_WIDTH; x++ ) {
+                       video_memory[((y - 1) * VGA_WIDTH + x) * 2] = video_memory[(y * VGA_WIDTH + x) * 2];
+               }
+       }
+       for ( x = 0; x < VGA_WIDTH; x++ ) {
+               video_memory[((VGA_HEIGHT - 1) * VGA_WIDTH + x) * 2] = ' ';
+       }
+       cursor_y = VGA_HEIGHT - 1;
+}
+
+void kprint(const char *string, uint8_t color)
+{
+       char next_char;
+       uint8_t vga_misc_output;
+       uint16_t crtc_port;
+       next_char = *string;
+
+       while ( next_char != 0 ) {
+               if ( next_char == '\n') { cursor_x = 0; cursor_y++; }
+               else { video_memory[(cursor_y * VGA_WIDTH + cursor_x) * 2] = next_char; video_memory[((cursor_y * VGA_WIDTH + cursor_x++) * 2)+ 1] = color != 0 ? color : VGA_COLOR_LIGHT_GRAY; }
+               if ( cursor_x >= VGA_WIDTH ) { cursor_x = 0; cursor_y++; }
+               if ( cursor_y >= VGA_HEIGHT ) { scroll_up(); }
+               next_char = *++string;
+       }
+
+       vga_misc_output = inb(0x3CC);
+       if (vga_misc_output & 0x1 == 0) {
+               crtc_port = 0x3B4;
+       } else {
+               crtc_port = 0x3D4;
+       }
+
+       outb(crtc_port, 0xE);
+       outb(crtc_port + 1, (cursor_y * VGA_WIDTH + cursor_x) >> 8);
+       outb(crtc_port, 0xF);
+       outb(crtc_port + 1, (cursor_y * VGA_WIDTH + cursor_x) & 0xFF);
+}
+
+void kprintc(const char character, uint8_t color)
+{
+       uint8_t vga_misc_output;
+       uint16_t crtc_port;
+
+       if ( character == '\n') { cursor_x = 0; cursor_y++; }
+       else { video_memory[(cursor_y * VGA_WIDTH + cursor_x) * 2] = character; video_memory[((cursor_y * VGA_WIDTH + cursor_x++) * 2)+ 1] = color != 0 ? color : VGA_COLOR_LIGHT_GRAY; }
+       if ( cursor_x >= VGA_WIDTH ) { cursor_x = 0; cursor_y++; }
+       if ( cursor_y >= VGA_HEIGHT ) { scroll_up(); }
+
+       vga_misc_output = inb(0x3CC);
+       if (vga_misc_output & 0x1 == 0) {
+               crtc_port = 0x3B4;
+       } else {
+               crtc_port = 0x3D4;
+       }
+
+       outb(crtc_port, 0xE);
+       outb(crtc_port + 1, (cursor_y * VGA_WIDTH + cursor_x) >> 8);
+       outb(crtc_port, 0xF);
+       outb(crtc_port + 1, (cursor_y * VGA_WIDTH + cursor_x) & 0xFF);
+
+}
+
+void kprintb(uint8_t byte)
+{
+       uint8_t temp;
+       temp = byte >> 4;
+       kprint("0x", VGA_COLOR_LIGHT_GRAY);
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = byte & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+}
+
+void kprintw(uint16_t word)
+{
+       uint8_t temp;
+       temp = word >> 12;
+       kprint("0x", VGA_COLOR_LIGHT_GRAY);
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = (word >> 8) & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = (word >> 4) & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = word & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+}
+
+void kprintd(uint32_t dword)
+{
+       uint8_t temp;
+       temp = dword >> 28;
+       kprint("0x", VGA_COLOR_LIGHT_GRAY);
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = (dword >> 24) & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = (dword >> 20) & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = (dword >> 16) & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = (dword >> 12) & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = (dword >> 8) & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = (dword >> 4) & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+       temp = dword & 0xF;
+       kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+}
diff --git a/kernel/drivers/irq/i8259a.c b/kernel/drivers/irq/i8259a.c
new file mode 100644 (file)
index 0000000..ed1e6ac
--- /dev/null
@@ -0,0 +1,80 @@
+#include <io.h>
+#include <tty.h>
+#include <irq/i8259a.h>
+
+void pic_init(int offset1, int offset2)
+{
+       uint8_t a1, a2;
+
+       a1 = inb(PIC1_DATA);
+       a2 = inb(PIC2_DATA);
+
+       outb(PIC1_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
+       io_wait();
+       outb(PIC2_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
+       io_wait();
+       outb(PIC1_DATA, offset1);
+       io_wait();
+       outb(PIC2_DATA, offset2);
+       io_wait();
+       outb(PIC1_DATA, 4);
+       io_wait();
+       outb(PIC2_DATA, 2);
+       io_wait();
+
+       outb(PIC1_DATA, PIC_ICW4_8086);
+       io_wait();
+       outb(PIC2_DATA, PIC_ICW4_8086);
+       io_wait();
+
+       outb(PIC1_DATA, a1);
+       outb(PIC2_DATA, a2);
+}
+
+void pic_mask(uint8_t irq)
+{
+       uint16_t port;
+       uint8_t value;
+
+       if (irq < 8) {
+               port = PIC1_DATA;
+       } else {
+               port = PIC2_DATA;
+       }
+
+       value = inb(port) | (1 << irq);
+       outb(port, value);
+}
+
+void pic_unmask(uint8_t irq)
+{
+       uint16_t port;
+       uint8_t value;
+
+       if (irq < 8) {
+               port = PIC1_DATA;
+       } else {
+               port = PIC2_DATA;
+       }
+
+       value = inb(port) & ~(1 << irq);
+       outb(port, value);
+}
+
+void pic_mask_all(void)
+{
+       outb(PIC1_DATA, 0xFF);
+}
+
+void pic_unmask_all(void)
+{
+       outb(PIC1_DATA, 0);
+}
+
+void pic_send_eoi(uint8_t irq)
+{
+       if (irq >= 8)
+               outb(PIC2_COMMAND, PIC_EOI);
+
+       outb(PIC1_COMMAND, PIC_EOI);
+}
diff --git a/kernel/entry.s b/kernel/entry.s
new file mode 100644 (file)
index 0000000..10c95e8
--- /dev/null
@@ -0,0 +1,11 @@
+bits 32
+extern kmain
+extern __STACK_BOTTOM__
+
+global _start
+_start:
+       mov ebp, __STACK_BOTTOM__
+       mov esp, ebp
+       call kmain
+       hlt
+       jmp $-1
diff --git a/kernel/kernel.c b/kernel/kernel.c
new file mode 100644 (file)
index 0000000..4c6efd8
--- /dev/null
@@ -0,0 +1,31 @@
+#include <tty.h>
+#include <io.h>
+#include <irq/idt.h>
+#include <irq/i8259a.h>
+#include <stdint.h>
+
+extern void double_fault(struct abort_frame *frame);
+extern void keyb_handler(struct interrupt_frame *frame);
+struct idt_descriptor idt[256] __attribute__((aligned(0x10)));
+struct idtr idtr __attribute__((aligned(0x10)));
+
+void kmain(void)
+{
+       screen_clear();
+       kprint("Welcome to Nameless OS!\nRunning revision: ", 0);
+       kprint(GIT_COMMIT, 0);
+       kprint("\n", 0);
+       kprint("Preparing IDT...\n", 0);
+       idt_set_descriptor(idt, 0x8, 0x8, (uint32_t) double_fault, IDT_TRAP_GATE, 0x0);
+       idt_set_descriptor(idt, 0x21, 0x8, (uint32_t) keyb_handler, IDT_INTERRUPT_GATE, 0x0);
+       kprint("IDT prepared, loading...\n", 0);
+       populate_idtr(&idtr, idt);
+       load_idt(idtr);
+       kprint("IDT loaded, enabling interrupts...\n", 0);
+       pic_init(0x20, 0x28);
+       pic_mask_all();
+       pic_unmask(1);
+       asm volatile ("sti");
+       kprint("All done\n", 0);
+       while(1);
+}
diff --git a/kernel/linker.ld b/kernel/linker.ld
new file mode 100644 (file)
index 0000000..645a824
--- /dev/null
@@ -0,0 +1,32 @@
+ENTRY(_start)
+OUTPUT_FORMAT(binary)
+
+SECTIONS
+{
+       . = 0x100000;
+       __KERNEL_BASE__ = .;
+
+       .text : ALIGN(4K) { 
+               kernel/entry.o (.text)
+               *(.text) 
+       }
+       .rodata : ALIGN(4K) { *(.rodata) }
+
+       /* .rodata is put after .text so that the read-only entry in the program
+        * header covers .rodata as well. When .rodata is after .data, that entry
+        * only covers .text, leaving .rodata in the writable program header
+        * entry. The problem with this approach is that the program header
+        * entry where .text and .rodata are is marked as executable, which
+        * would mean that .rodata should be executable too, but that doesn't
+        * matter yet because we (currently) assume that the NX bit is not
+        * supported anyway. */
+
+       .data : ALIGN(4K) { *(.data) }
+       .bss : ALIGN(4K) { 
+               *(.bss)
+               /* Reserving 16KiB for the stack. A __STACK_TOP__ is not really
+                * needed (yet). */
+               . += 16K; 
+               __STACK_BOTTOM__ = .;
+       }
+}
diff --git a/nameless2disk b/nameless2disk
new file mode 100755 (executable)
index 0000000..28b3427
--- /dev/null
@@ -0,0 +1,111 @@
+#!/bin/sh
+# Shell script to install Nameless to a disk.
+
+BINARIES=( boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN kernel/kernel.bin )
+
+check_binaries() {
+       for i in "${BINARIES[@]}"; do
+               if ! [ -e $i ]; then
+                       echo $i does not exist, have you compiled Nameless?
+                       exit 1
+               fi
+       done
+}
+
+install_blkdev() {
+       # Ask the user are they sure they want to install.
+       local prompt
+       echo About to install Nameless to real disk $target.
+       echo This will WIPE ALL DATA on $target, type YES if you want to continue.
+       read -r prompt
+       if [ $prompt != "YES" ]; then
+               echo -e "Aborting."
+               exit 1
+       fi
+
+       echo OK, installing Nameless to $target.
+       echo Creating partition table on $target...
+
+       # Create a partition table on the disk.
+       fdisk $target <<- END > /dev/null
+       o
+       n
+       p
+       1
+
+
+       a
+       t
+       0c
+       w
+       END
+       if ! [ $? -eq 0 ]; then
+               echo An error occurred!
+               exit 1
+       fi
+       # Create a FAT32 filesystem on the disk.
+       echo Formatting $target...
+       mkfs.fat -F 32 "$target"1
+       if ! [ $? -eq 0 ]; then
+               echo An error occurred!
+               exit 1
+       fi
+       # Write MBR and VBR to the disk.
+       # TODO: write VBR to the backup BPB as well?
+       echo Writing bootsectors to $target...
+       dd 'if=boot/x86/mbr' "of=$target" 'bs=440' 'count=1'
+       if ! [ $? -eq 0 ]; then
+               echo An error occurred!
+               exit 1
+       fi
+       dd 'if=boot/x86/vbr-fat32' 'of='"$target"1 'bs=1' 'skip=90' 'seek=90'
+       if ! [ $? -eq 0 ]; then
+               echo An error occurred!
+               exit 1
+       fi
+       # Mount the partition and copy stage3 and kernel to it.
+       echo Copying files to $target...
+       local mountpoint=$(mktemp -d)
+       mount "$target"1 $mountpoint
+       if ! [ $? -eq 0 ]; then
+               echo An error occurred!
+               exit 1
+       fi
+       cp boot/x86/stage3/LOADER.BIN $mountpoint/LOADER.BIN
+       cp kernel/kernel.bin $mountpoint/KERNEL.BIN
+       if ! [ $? -eq 0 ]; then
+               echo An error occurred!
+               exit 1
+       fi
+       # Unmount the partition and flush write cache to make sure the OS
+       # binaries have actually been written to the disk.
+       echo Unmounting $target...
+       umount $mountpoint
+       rm -rf $mountpoint
+       echo Flushing cache...
+       sync
+       echo Nameless has been successfully installed to $target!
+       return
+}
+
+# Check if all the required tools are installed.
+[ -z $(command -v fdisk) ] && echo fdisk not found, is util-linux installed? && exit 127
+[ -z $(command -v mkfs.fat) ] && echo mkfs.fat not found, is dosfstools installed? && exit 127
+
+# Make sure that Nameless has been compiled.
+check_binaries
+
+# cd to the directory we're in.
+cd $(dirname $(realpath $0))
+
+# For convenience, list all connected disks.
+fdisk -l
+
+# Ask the user where to install.
+echo -n "Choose the disk you want to install to: "
+read -r target
+
+# If the target does not exist or is not a blkdev, exit.
+[ -b $target ] || echo $target does not exist or is not a block device! || exit 1
+install_blkdev
+exit 0
diff --git a/print.s b/print.s
deleted file mode 100644 (file)
index 3a46c72..0000000
--- a/print.s
+++ /dev/null
@@ -1,136 +0,0 @@
-%define CURRENT_ADDR cs:di
-
-; Prints a null terminated string of your choice.
-; Arguments:
-;     CS:DI - pointer to the string you want to print
-;     BL - 0 prints newline, otherwise no
-print
-       pusha
-       mov ah, 0Eh ; teletype print
-.write2
-       mov al, [CURRENT_ADDR] ; get current char and put it in al
-       cmp al, 0 ; check if al is null (string terminator)
-       je .newline ; if it is, start a new line
-       int 10h ; otherwise write the char
-       inc di ; increment pointer to string
-       jmp .write2 ; jump back to this section
-.newline
-       cmp bl, 1
-       je .done
-       mov al, 0Dh ; carriage return
-       int 10h
-       mov al, 0Ah ; line feed
-       int 10h
-.done
-       popa
-       ret ; return
-
-; Prints a word (16-bit value) in hex format.
-; Arguments:
-;     DX - word to print
-;     BL - 0 will print a newline, 1 won't
-print_word
-       pusha ; push all regs to stack
-       mov di, HEX_OUT+5 ; set destination index to last char in HEX_OUT
-       mov ax, dx ; copy argument to accumulator
-       and al, 00001111b ; extract the low nibble of the low half of the accumulator
-       call .compare ; fill in the string with correct value
-       dec di ; decrement string pointer
-       mov ax, dx ; copy argument to accumulator
-       and al, 11110000b ; extract high nibble of low half of accumulator
-       shr al, 4 ; shift high nibble to low nibble
-       call .compare ; fill in string with correct value
-       dec di
-       mov al, ah ; copy high portion of accumulator to low
-       and al, 00001111b ; 
-       call .compare
-       dec di
-       mov al, ah
-       and al, 11110000b
-       shr al, 4
-       call .compare
-       mov di, HEX_OUT
-       call print ; write string
-       popa
-       ret
-
-.compare
-       cmp al, 0 ; compare al with 0
-       jne .one ; if not equal, compare with 1
-       mov byte [CURRENT_ADDR], '0' ; set character to ASCII 0
-       ret ; return
-.one
-       cmp al, 1
-       jne .two
-       mov byte [CURRENT_ADDR], '1'
-       ret
-.two
-       cmp al, 2
-       jne .three
-       mov byte [CURRENT_ADDR], '2'
-       ret
-.three
-       cmp al, 3
-       jne .four
-       mov byte [CURRENT_ADDR], '3'
-       ret
-.four
-       cmp al, 4
-       jne .five
-       mov byte [CURRENT_ADDR], '4'
-       ret
-.five
-       cmp al, 5
-       jne .six
-       mov byte [CURRENT_ADDR], '5'
-       ret
-.six
-       cmp al, 6
-       jne .seven
-       mov byte [CURRENT_ADDR], '6'
-       ret
-.seven
-       cmp al, 7
-       jne .eight
-       mov byte [CURRENT_ADDR], '7'
-       ret
-.eight
-       cmp al, 8
-       jne .nine
-       mov byte [CURRENT_ADDR], '8'
-       ret
-.nine
-       cmp al, 9
-       jne .ten
-       mov byte [CURRENT_ADDR], '9'
-       ret
-.ten
-       cmp al, 0Ah
-       jne .eleven
-       mov byte [CURRENT_ADDR], 'A'
-       ret
-.eleven
-       cmp al, 0Bh
-       jne .twelve
-       mov byte [CURRENT_ADDR], 'B'
-       ret
-.twelve
-       cmp al, 0Ch
-       jne .thirteen
-       mov byte [CURRENT_ADDR], 'C'
-       ret
-.thirteen
-       cmp al, 0Dh
-       jne .fourteen
-       mov byte [CURRENT_ADDR], 'D'
-       ret
-.fourteen
-       cmp al, 0Eh
-       jne .fifteen
-       mov byte [CURRENT_ADDR], 'E'
-       ret
-.fifteen
-       mov byte [CURRENT_ADDR], 'F'
-       ret
-
-HEX_OUT db "0x0000", 0