From: Duje Mihanović Date: Fri, 17 Jun 2022 19:26:27 +0000 (+0200) Subject: Merge branch 'drive' X-Git-Tag: 0.1.1~13 X-Git-Url: http://git.dujemihanovic.xyz/posts?a=commitdiff_plain;h=628db992cdf71f22597a2dcc86bcf456ff7c8db8;hp=d826097f3b032247dd0396f8cf64b2fa775cea68;p=nameless-os.git Merge branch 'drive' This way that branch can be deleted without any history being lost. --- diff --git a/.gitignore b/.gitignore index 12ab2c1..9fc5bb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ toolchain/ -boot +boot.img +boot/x86/boot +**/*.o +**/*.bin +**/*.elf +**/*.dbg diff --git a/Makefile b/Makefile index a85a94a..aee8cc1 100644 --- 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 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 index 0000000..ef679a9 --- /dev/null +++ b/boot/x86/.gitignore @@ -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 index 0000000..1de8199 --- /dev/null +++ b/boot/x86/Makefile @@ -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 index 0000000..ebf63ac --- /dev/null +++ b/boot/x86/disk.dump @@ -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 index 0000000..e72735d --- /dev/null +++ b/boot/x86/fat32/fat32-structs.s @@ -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 index 0000000..41d5688 --- /dev/null +++ b/boot/x86/fat32/fat32.s @@ -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 index 0000000..b5d01c9 --- /dev/null +++ b/boot/x86/mbr.s @@ -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 index 0000000..8748dde --- /dev/null +++ b/boot/x86/stage3/a20.s @@ -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 index 0000000..d81bb64 --- /dev/null +++ b/boot/x86/stage3/gdt.s @@ -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 index 0000000..ec08c61 --- /dev/null +++ b/boot/x86/stage3/loader.s @@ -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 index 0000000..73b34d5 --- /dev/null +++ b/boot/x86/stage3/print.s @@ -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 index 0000000..cdd361b --- /dev/null +++ b/boot/x86/stage3/unreal.s @@ -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 index 0000000..439157f --- /dev/null +++ b/boot/x86/vbr-fat32.s @@ -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 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 index 0000000..c84edb4 --- /dev/null +++ b/how-to-compile @@ -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 index 0000000..09f6fa7 --- /dev/null +++ b/include/arch/x86/io.h @@ -0,0 +1,25 @@ +#ifndef X86_IO_H +#define X86_IO_H + +#include + +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 index 0000000..31448e4 --- /dev/null +++ b/include/arch/x86/irq/i8259a.h @@ -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 index 0000000..daf586f --- /dev/null +++ b/include/arch/x86/irq/idt.h @@ -0,0 +1,28 @@ +#ifndef X86_IDT_H +#define X86_IDT_H + +#include + +#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 index 0000000..2224737 --- /dev/null +++ b/include/arch/x86/tty.h @@ -0,0 +1,28 @@ +#ifndef X86_TTY_H +#define X86_TTY_H + +#include + +#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 index 0000000..54e8612 --- /dev/null +++ b/kernel/arch/x86/irq/idt.c @@ -0,0 +1,27 @@ +#include +#include + +/* 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 index 0000000..f4f4a4b --- /dev/null +++ b/kernel/arch/x86/irq/sample_handler.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +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 index 0000000..07d734e --- /dev/null +++ b/kernel/arch/x86/tty/tty.c @@ -0,0 +1,139 @@ +#include +#include +#include + +#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 index 0000000..ed1e6ac --- /dev/null +++ b/kernel/drivers/irq/i8259a.c @@ -0,0 +1,80 @@ +#include +#include +#include + +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 index 0000000..10c95e8 --- /dev/null +++ b/kernel/entry.s @@ -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 index 0000000..4c6efd8 --- /dev/null +++ b/kernel/kernel.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +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 index 0000000..645a824 --- /dev/null +++ b/kernel/linker.ld @@ -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 index 0000000..28b3427 --- /dev/null +++ b/nameless2disk @@ -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 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