Comments
This commit is contained in:
parent
cbf6c48b22
commit
e08b93e3bb
46
Makefile
46
Makefile
@ -2,7 +2,7 @@ BUILD=./build
|
|||||||
|
|
||||||
# Default rule
|
# Default rule
|
||||||
.PHONY: default
|
.PHONY: default
|
||||||
default: all
|
default: $(BUILD)/disk.img
|
||||||
|
|
||||||
# Remove all build files
|
# Remove all build files
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
@ -10,16 +10,12 @@ clean:
|
|||||||
rm -drf $(BUILD)
|
rm -drf $(BUILD)
|
||||||
cd tetros; cargo clean
|
cd tetros; cargo clean
|
||||||
|
|
||||||
# Make everything
|
|
||||||
# (but don't run qemu)
|
|
||||||
.PHONY: all
|
|
||||||
all: img
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# MARK: boot
|
# MARK: disk
|
||||||
#
|
#
|
||||||
|
|
||||||
# Compile tetros as library
|
# Compile tetros as a library
|
||||||
|
# (so that we can link it with a custom linker script)
|
||||||
LIB_SRC = ./tetros/Cargo.toml ./tetros/Cargo.lock $(shell find ./tetros/src -type f)
|
LIB_SRC = ./tetros/Cargo.toml ./tetros/Cargo.lock $(shell find ./tetros/src -type f)
|
||||||
$(BUILD)/tetros.lib: $(LIB_SRC)
|
$(BUILD)/tetros.lib: $(LIB_SRC)
|
||||||
@mkdir -p $(BUILD)
|
@mkdir -p $(BUILD)
|
||||||
@ -35,7 +31,7 @@ $(BUILD)/tetros.lib: $(LIB_SRC)
|
|||||||
-- \
|
-- \
|
||||||
--emit link="$(CURDIR)/$@"
|
--emit link="$(CURDIR)/$@"
|
||||||
|
|
||||||
# Link tetros
|
# Link tetros using custom linker script
|
||||||
BIOS_LD = ./tetros/linkers/x86-unknown-none.ld
|
BIOS_LD = ./tetros/linkers/x86-unknown-none.ld
|
||||||
$(BUILD)/tetros.elf: $(BUILD)/tetros.lib $(BIOS_LD)
|
$(BUILD)/tetros.elf: $(BUILD)/tetros.lib $(BIOS_LD)
|
||||||
ld \
|
ld \
|
||||||
@ -49,13 +45,13 @@ $(BUILD)/tetros.elf: $(BUILD)/tetros.lib $(BIOS_LD)
|
|||||||
objcopy --only-keep-debug "$@" "$@.sym"
|
objcopy --only-keep-debug "$@" "$@.sym"
|
||||||
objcopy --strip-debug "$@"
|
objcopy --strip-debug "$@"
|
||||||
|
|
||||||
# Wrap tetros in three-stage BIOS loader
|
# Wrap tetros in BIOS loader
|
||||||
# Parameters:
|
# Parameters:
|
||||||
# - BIOS_SRC: source directory of bios assembly
|
# - BIOS_SRC: source directory of bios assembly
|
||||||
# - STAGE2_SECTOR: the index of the first sector of the stage 2 binary on the disk
|
# - STAGE2_SECTOR: the index of the first sector of the stage 2 binary on the disk
|
||||||
BIOS_SRC = ./bios
|
BIOS_SRC = ./bios
|
||||||
STAGE2_SECTOR = 1
|
STAGE2_SECTOR = 1
|
||||||
$(BUILD)/bios.bin: $(wildcard $(BIOS_SRC)/*.asm) $(BUILD)/tetros.elf
|
$(BUILD)/disk.img: $(wildcard $(BIOS_SRC)/*.asm) $(BUILD)/tetros.elf
|
||||||
@mkdir -p "$(BUILD)"
|
@mkdir -p "$(BUILD)"
|
||||||
nasm \
|
nasm \
|
||||||
-f bin \
|
-f bin \
|
||||||
@ -66,30 +62,12 @@ $(BUILD)/bios.bin: $(wildcard $(BIOS_SRC)/*.asm) $(BUILD)/tetros.elf
|
|||||||
-i "$(BIOS_SRC)" \
|
-i "$(BIOS_SRC)" \
|
||||||
"$(BIOS_SRC)/main.asm"
|
"$(BIOS_SRC)/main.asm"
|
||||||
|
|
||||||
# Extract full mbr (first 512 bytes)
|
|
||||||
$(BUILD)/mbr.bin: $(BUILD)/bios.bin
|
|
||||||
@mkdir -p "$(BUILD)"
|
|
||||||
@echo ""
|
|
||||||
dd if="$<" bs=512 count=1 of="$@"
|
|
||||||
|
|
||||||
# Extract stage 2 (rest of file)
|
|
||||||
$(BUILD)/stage2.bin: $(BUILD)/bios.bin
|
|
||||||
@mkdir -p "$(BUILD)"
|
|
||||||
@echo ""
|
|
||||||
dd if="$<" bs=512 skip=1 of="$@"
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# MARK: bundle
|
# MARK: qemu
|
||||||
|
#
|
||||||
|
# Do not use `-enable-kvm` or `-cpu host`,
|
||||||
|
# this confuses gdb.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Make full disk image
|
|
||||||
.PHONY: img
|
|
||||||
img: $(BUILD)/disk.img
|
|
||||||
$(BUILD)/disk.img: $(BUILD)/mbr.bin $(BUILD)/stage2.bin
|
|
||||||
@mkdir -p $(BUILD)
|
|
||||||
@echo ""
|
|
||||||
dd if="$(BUILD)/mbr.bin" of=$@ conv=notrunc bs=512
|
|
||||||
dd if="$(BUILD)/stage2.bin" of=$@ conv=notrunc seek=$(STAGE2_SECTOR) bs=512
|
|
||||||
|
|
||||||
.PHONY: qemu
|
.PHONY: qemu
|
||||||
qemu: $(BUILD)/disk.img
|
qemu: $(BUILD)/disk.img
|
||||||
@ -129,5 +107,3 @@ qemu-gdb: $(BUILD)/disk.img
|
|||||||
-gdb tcp::26000 \
|
-gdb tcp::26000 \
|
||||||
-S
|
-S
|
||||||
|
|
||||||
# Do not use `-enable-kvm` or `-cpu host`,
|
|
||||||
# this confuses gdb.
|
|
@ -1,8 +1,5 @@
|
|||||||
# TetrOS: bare-metal tetris
|
# TetrOS: bare-metal tetris
|
||||||
|
|
||||||
## TODO:
|
|
||||||
- Fix stage 1 loader
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Compiles to a standalone disk image
|
- Compiles to a standalone disk image
|
||||||
- Written from scratch using only Nasm and Rust
|
- Written from scratch using only Nasm and Rust
|
||||||
|
@ -1,30 +1,25 @@
|
|||||||
sectalign off
|
sectalign off
|
||||||
|
|
||||||
; This program expects two external macros:
|
; The following code expects two external macros:
|
||||||
; STAGE3, a path to the stage3 binary
|
; STAGE3, a path to the stage3 binary
|
||||||
; STAGE2_SECTOR, the location of stage 2
|
; STAGE2_SECTOR, the location of stage 2
|
||||||
; on the disk, in 512-byte sectors.
|
; on the disk, in 512-byte sectors.
|
||||||
; On a gpt disk, this is probably 34.
|
;
|
||||||
|
; Both of these are set in the makefile.
|
||||||
; Stage 1 is MBR code, and should fit in LBA 0
|
|
||||||
; (512 bytes). Layout is as follows:
|
|
||||||
; (Format is `offset, length: purpose`)
|
|
||||||
; 0, 424: x86 boot code
|
|
||||||
; 440, 4: Unique disk signature
|
|
||||||
; 444, 2: unknown
|
|
||||||
; 446, 16*4: Array of four legacy MBR records
|
|
||||||
; 510, 2: signature 0x55 0xAA
|
|
||||||
; 512 to end of logical block: reserved
|
|
||||||
;
|
|
||||||
; See https://uefi.org/specs/UEFI/2.10/05_GUID_Partition_Table_Format.html
|
|
||||||
|
|
||||||
|
; BIOS loads stage 1 at 0x7C00
|
||||||
ORG 0x7C00
|
ORG 0x7C00
|
||||||
SECTION .text
|
SECTION .text
|
||||||
|
|
||||||
; stage 1 is sector 0, loaded into memory at 0x7C00
|
; Stage 1 is MBR code, and should fit in LBA 0
|
||||||
|
; (i.e, in the first 512 bytes).
|
||||||
%include "stage1.asm"
|
%include "stage1.asm"
|
||||||
|
|
||||||
; Stage 1 is at most 440 bytes
|
; Stage 1 is at most 440 bytes
|
||||||
|
; This limit is set by the GPT spec.
|
||||||
|
; See https://uefi.org/specs/UEFI/2.10/05_GUID_Partition_Table_Format.html
|
||||||
|
;
|
||||||
|
; This `times` will throw an error if the subtraction is negative.
|
||||||
times 440-($-$$) db 0
|
times 440-($-$$) db 0
|
||||||
db 0xee
|
db 0xee
|
||||||
|
|
||||||
@ -32,30 +27,38 @@ db 0xee
|
|||||||
times 510-($-$$) db 0
|
times 510-($-$$) db 0
|
||||||
|
|
||||||
; MBR signature.
|
; MBR signature.
|
||||||
; This isn't loaded into memory, it's
|
; This tells the BIOS that this disk is bootable.
|
||||||
; only here for debugging.
|
|
||||||
db 0x55
|
db 0x55
|
||||||
db 0xaa
|
db 0xaa
|
||||||
|
|
||||||
|
|
||||||
|
; Include stage 2. This is loaded into memory by stage 1.
|
||||||
|
; (stage 1 loads both stage 2 and stage 3)
|
||||||
|
;
|
||||||
|
; Stage 2 sets up protected mode, sets up the GDT,
|
||||||
|
; and initializes a minimal environment for stage 3.
|
||||||
|
;
|
||||||
|
; On a "real" boot disk, this data will not immediately follow stage 1.
|
||||||
|
; It would be stored in a special disk partition.
|
||||||
|
;
|
||||||
|
; We don't need this kind of complexity here, though, so we store
|
||||||
|
; stage 2 right after stage 1. (This is why STAGE2_SECTOR is 1.)
|
||||||
|
;
|
||||||
|
; This is nice, because the layout of the code on our boot disk
|
||||||
|
; matches the layout of the code in memory. THIS IS NOT USUALLY THE CASE.
|
||||||
stage2:
|
stage2:
|
||||||
%include "stage2.asm"
|
%include "stage2.asm"
|
||||||
align 512, db 0
|
align 512, db 0
|
||||||
stage2.end:
|
stage2.end:
|
||||||
|
|
||||||
; The maximum size of stage2 is 4 KiB,
|
; Pad to 0x3000.
|
||||||
; This fill will throw an error if the subtraction is negative.
|
; This makes sure that state3 is loaded at the address
|
||||||
times (4*1024)-($-stage2) db 0
|
; the linker expects. Must match the value in `tetros/linkers/x86-unknown-none.ld`.
|
||||||
|
times (0x8000 - 0x7c00)-($-$$) db 0
|
||||||
; Pad to 0x9000.
|
|
||||||
; This needs to match the value configured in the stage3 linker script
|
|
||||||
times (0x9000 - 0x7c00)-($-$$) db 0
|
|
||||||
|
|
||||||
|
; Include stage 3, the binary compiled from Rust sources.
|
||||||
stage3:
|
stage3:
|
||||||
%defstr STAGE3_STR %[STAGE3]
|
%defstr STAGE3_STR %[STAGE3]
|
||||||
incbin STAGE3_STR
|
incbin STAGE3_STR
|
||||||
align 512, db 0
|
align 512, db 0
|
||||||
.end:
|
.end:
|
||||||
|
|
||||||
; TODO: why? Of the disk, or of memory?
|
|
||||||
; the maximum size of the boot loader portion is 384 KiB
|
|
||||||
times (384*1024)-($-$$) db 0
|
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
SECTION .text
|
SECTION .text
|
||||||
USE16
|
USE16
|
||||||
|
|
||||||
; provide function for printing in x86 real mode
|
; Print a string and a newline
|
||||||
|
;
|
||||||
; print a string and a newline
|
; Clobbers ax
|
||||||
; CLOBBER
|
|
||||||
; ax
|
|
||||||
print_line:
|
print_line:
|
||||||
mov al, 13
|
mov al, 13
|
||||||
call print_char
|
call print_char
|
||||||
mov al, 10
|
mov al, 10
|
||||||
jmp print_char
|
jmp print_char
|
||||||
|
|
||||||
; print a string
|
; Print a string
|
||||||
; IN
|
;
|
||||||
|
; Input:
|
||||||
; si: points at zero-terminated String
|
; si: points at zero-terminated String
|
||||||
; CLOBBER
|
;
|
||||||
; si, ax
|
; Clobbers si, ax
|
||||||
print:
|
print:
|
||||||
pushf
|
pushf
|
||||||
cld
|
cld
|
||||||
@ -30,8 +29,9 @@ print:
|
|||||||
popf
|
popf
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; print a character
|
; Print a character
|
||||||
; IN
|
;
|
||||||
|
; Input:
|
||||||
; al: character to print
|
; al: character to print
|
||||||
print_char:
|
print_char:
|
||||||
pusha
|
pusha
|
||||||
@ -42,10 +42,11 @@ print_char:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
; print a number in hex
|
; print a number in hex
|
||||||
; IN
|
;
|
||||||
|
; Input:
|
||||||
; bx: the number
|
; bx: the number
|
||||||
; CLOBBER
|
;
|
||||||
; al, cx
|
; Clobbers al, cx
|
||||||
print_hex:
|
print_hex:
|
||||||
mov cx, 4
|
mov cx, 4
|
||||||
.lp:
|
.lp:
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
SECTION .text
|
|
||||||
USE16
|
|
||||||
|
|
||||||
protected_mode:
|
|
||||||
|
|
||||||
.func: dd 0
|
|
||||||
|
|
||||||
.entry:
|
|
||||||
; disable interrupts
|
|
||||||
cli
|
|
||||||
|
|
||||||
; load protected mode GDT
|
|
||||||
lgdt [gdtr]
|
|
||||||
|
|
||||||
; set protected mode bit of cr0
|
|
||||||
mov eax, cr0
|
|
||||||
or eax, 1
|
|
||||||
mov cr0, eax
|
|
||||||
|
|
||||||
; far jump to load CS with 32 bit segment
|
|
||||||
; (we are in 32-bit mode, but instruction pipeline
|
|
||||||
; has 16-bit instructions.
|
|
||||||
jmp gdt.pm32_code:.inner
|
|
||||||
|
|
||||||
; gdt.pm32_code is a multiple of 8, so it always ends with three zero bits.
|
|
||||||
; The GDT spec abuses this fact, and uses these last three bits to store other
|
|
||||||
; data (table type and privilege). In this case, 000 is what we need anyway.
|
|
||||||
;
|
|
||||||
; Also note that CS isn't an address in protected mode---it's a GDT descriptor.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
USE32
|
|
||||||
|
|
||||||
.inner:
|
|
||||||
; load all the other segments with 32 bit data segments
|
|
||||||
mov eax, gdt.pm32_data
|
|
||||||
mov ds, eax
|
|
||||||
mov es, eax
|
|
||||||
mov fs, eax
|
|
||||||
mov gs, eax
|
|
||||||
mov ss, eax
|
|
||||||
|
|
||||||
; jump to specified function
|
|
||||||
mov eax, [.func]
|
|
||||||
jmp eax
|
|
121
bios/stage1.asm
121
bios/stage1.asm
@ -1,37 +1,39 @@
|
|||||||
USE16
|
USE16
|
||||||
|
|
||||||
stage1: ; dl comes with disk
|
stage1:
|
||||||
; initialize segment registers
|
; Initialize segment registers
|
||||||
xor ax, ax
|
xor ax, ax ; Set ax to 0
|
||||||
mov ds, ax
|
mov ds, ax
|
||||||
mov es, ax
|
mov es, ax
|
||||||
mov ss, ax
|
mov ss, ax
|
||||||
|
|
||||||
; initialize stack
|
; Initialize stack pointer
|
||||||
|
; (stack grows up)
|
||||||
mov sp, 0x7C00
|
mov sp, 0x7C00
|
||||||
|
|
||||||
; initialize CS
|
; Initialize CS
|
||||||
; far jump sets both CS and IP to a known-good state,
|
;
|
||||||
; we don't know where the BIOS put us at startup.
|
; `retf` sets both CS and IP to a known-good state.
|
||||||
; (could be 0x00:0x7C00, could be 0x7C00:0x00.
|
; This is necessary because we don't know where the BIOS put us at startup.
|
||||||
; Not everybody follows spec.)
|
; (could be 0x00:0x7C00, could be 0x7C00:0x00. Not everybody follows spec.)
|
||||||
push ax
|
push ax ; `ax` is still 0
|
||||||
push word .set_cs
|
push word .set_cs
|
||||||
retf
|
retf
|
||||||
|
|
||||||
.set_cs:
|
.set_cs:
|
||||||
; save disk number
|
; Save disk number.
|
||||||
|
; BIOS sets `dl` to the number of
|
||||||
|
; the disk we're booting from.
|
||||||
mov [disk], dl
|
mov [disk], dl
|
||||||
|
|
||||||
|
; Print "Stage 1"
|
||||||
mov si, stage_msg
|
mov si, stage_msg
|
||||||
call print
|
call print
|
||||||
mov al, '1'
|
mov al, '1'
|
||||||
call print_char
|
call print_char
|
||||||
call print_line
|
call print_line
|
||||||
|
|
||||||
|
; read CHS gemotry, save into [chs]
|
||||||
|
|
||||||
; read CHS gemotry
|
|
||||||
; CL (bits 0-5) = maximum sector number
|
; CL (bits 0-5) = maximum sector number
|
||||||
; CL (bits 6-7) = high bits of max cylinder number
|
; CL (bits 6-7) = high bits of max cylinder number
|
||||||
; CH = low bits of maximum cylinder number
|
; CH = low bits of maximum cylinder number
|
||||||
@ -51,11 +53,10 @@ stage1: ; dl comes with disk
|
|||||||
and cl, 0x3f
|
and cl, 0x3f
|
||||||
mov [chs.s], cl
|
mov [chs.s], cl
|
||||||
|
|
||||||
; disk address of stage 2
|
; First sector of stage 2
|
||||||
; (start sector)
|
|
||||||
mov eax, STAGE2_SECTOR
|
mov eax, STAGE2_SECTOR
|
||||||
|
|
||||||
; where to load stage 2
|
; Where to load stage 2
|
||||||
mov bx, stage2
|
mov bx, stage2
|
||||||
|
|
||||||
; length of stage2 + stage3
|
; length of stage2 + stage3
|
||||||
@ -63,36 +64,40 @@ stage1: ; dl comes with disk
|
|||||||
mov cx, (stage3.end - stage2) / 512
|
mov cx, (stage3.end - stage2) / 512
|
||||||
mov dx, 0
|
mov dx, 0
|
||||||
|
|
||||||
|
; Consume eax, bx, cx, dx
|
||||||
|
; and load code from disk.
|
||||||
call load
|
call load
|
||||||
|
|
||||||
jmp stage2.entry
|
jmp stage2.entry
|
||||||
|
|
||||||
; load some sectors from disk to a buffer in memory
|
; Load sectors from disk to memory.
|
||||||
; buffer has to be below 1MiB
|
; Cannot load more than 1MiB.
|
||||||
; IN
|
;
|
||||||
|
; Input:
|
||||||
; ax: start sector
|
; ax: start sector
|
||||||
; bx: offset of buffer
|
; bx: offset of buffer
|
||||||
; cx: number of sectors (512 Bytes each)
|
; cx: number of sectors (512 Bytes each)
|
||||||
; dx: segment of buffer
|
; dx: segment of buffer
|
||||||
; CLOBBER
|
;
|
||||||
; ax, bx, cx, dx, si
|
; Clobbers ax, bx, cx, dx, si
|
||||||
; TODO rewrite to (eventually) move larger parts at once
|
|
||||||
; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end)
|
|
||||||
load:
|
load:
|
||||||
; replaced 127 with 1.
|
; Every "replace 1" comment means that the `1`
|
||||||
; see https://stackoverflow.com/questions/58564895/problem-with-bios-int-13h-read-sectors-from-drive
|
; on that line could be bigger.
|
||||||
; TODO: fix later
|
;
|
||||||
|
; See https://stackoverflow.com/questions/58564895/problem-with-bios-int-13h-read-sectors-from-drive
|
||||||
|
; We have to load one sector at a time to avoid the 1K boundary error.
|
||||||
|
; Would be nice to read more sectors at a time, though, that's faster.
|
||||||
|
|
||||||
cmp cx, 1 ;127
|
cmp cx, 1 ; replace 1
|
||||||
jbe .good_size
|
jbe .good_size
|
||||||
|
|
||||||
pusha
|
pusha
|
||||||
mov cx, 1; 127
|
mov cx, 1 ; replace 1
|
||||||
call load
|
call load
|
||||||
popa
|
popa
|
||||||
add eax, 1; 127
|
add eax, 1 ; replace 1
|
||||||
add dx, 1 * 512 / 16 ; 127
|
add dx, 1 * 512 / 16 ; replace 1
|
||||||
sub cx, 1;127
|
sub cx, 1 ; replace 1
|
||||||
|
|
||||||
jmp load
|
jmp load
|
||||||
.good_size:
|
.good_size:
|
||||||
@ -101,44 +106,37 @@ load:
|
|||||||
mov [DAPACK.count], cx
|
mov [DAPACK.count], cx
|
||||||
mov [DAPACK.seg], dx
|
mov [DAPACK.seg], dx
|
||||||
|
|
||||||
; This should be a subroutine,
|
; Print the data we're reading
|
||||||
; but we don't call/ret to save a few bytes.
|
; Prints AAAAAAAA#BBBB CCCC:DDDD, where:
|
||||||
; (we only use this once)
|
; - A..A is the lba we're reading (printed in two parts)
|
||||||
;
|
; - BBBB is the number of sectors we're reading
|
||||||
;call print_dapack
|
; - CCCC is the index we're writing to
|
||||||
;print_dapack:
|
; - DDDD is the buffer we're writing to
|
||||||
mov bx, [DAPACK.addr + 2]
|
mov bx, [DAPACK.addr + 2] ; last two bytes
|
||||||
call print_hex
|
call print_hex
|
||||||
|
mov bx, [DAPACK.addr] ; first two bytes
|
||||||
mov bx, [DAPACK.addr]
|
|
||||||
call print_hex
|
call print_hex
|
||||||
|
|
||||||
mov al, '#'
|
mov al, '#'
|
||||||
call print_char
|
call print_char
|
||||||
|
|
||||||
mov bx, [DAPACK.count]
|
mov bx, [DAPACK.count]
|
||||||
call print_hex
|
call print_hex
|
||||||
|
|
||||||
mov al, ' '
|
mov al, ' '
|
||||||
call print_char
|
call print_char
|
||||||
|
|
||||||
mov bx, [DAPACK.seg]
|
mov bx, [DAPACK.seg]
|
||||||
call print_hex
|
call print_hex
|
||||||
|
|
||||||
mov al, ':'
|
mov al, ':'
|
||||||
call print_char
|
call print_char
|
||||||
|
|
||||||
mov bx, [DAPACK.buf]
|
mov bx, [DAPACK.buf]
|
||||||
call print_hex
|
call print_hex
|
||||||
|
|
||||||
call print_line
|
call print_line
|
||||||
;ret
|
|
||||||
; End of print_dapack
|
|
||||||
|
|
||||||
|
|
||||||
|
; Read from disk.
|
||||||
|
; int13h, ah=0x42 does not work on some disks.
|
||||||
|
; use int13h, ah=0x02 in thes case.
|
||||||
cmp byte [chs.s], 0
|
cmp byte [chs.s], 0
|
||||||
jne .chs
|
jne .chs
|
||||||
;INT 0x13 extended read does not work on CDROM!
|
|
||||||
mov dl, [disk]
|
mov dl, [disk]
|
||||||
mov si, DAPACK
|
mov si, DAPACK
|
||||||
mov ah, 0x42
|
mov ah, 0x42
|
||||||
@ -188,6 +186,10 @@ load:
|
|||||||
jc error ; carry flag set on error
|
jc error ; carry flag set on error
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
;
|
||||||
|
; MARK: errors
|
||||||
|
;
|
||||||
|
|
||||||
error_chs:
|
error_chs:
|
||||||
mov ah, 0
|
mov ah, 0
|
||||||
|
|
||||||
@ -200,13 +202,18 @@ error:
|
|||||||
|
|
||||||
mov si, stage1_error_msg
|
mov si, stage1_error_msg
|
||||||
call print
|
call print
|
||||||
call print_line
|
call print_line
|
||||||
|
|
||||||
|
; halt after printing error details
|
||||||
.halt:
|
.halt:
|
||||||
cli
|
cli
|
||||||
hlt
|
hlt
|
||||||
jmp .halt
|
jmp .halt
|
||||||
|
|
||||||
|
;
|
||||||
|
; MARK: data
|
||||||
|
;
|
||||||
|
|
||||||
%include "print.asm"
|
%include "print.asm"
|
||||||
|
|
||||||
stage_msg: db "Stage ",0
|
stage_msg: db "Stage ",0
|
||||||
@ -215,9 +222,9 @@ stage1_error_msg: db " ERROR",0
|
|||||||
disk: db 0
|
disk: db 0
|
||||||
|
|
||||||
chs:
|
chs:
|
||||||
.c: dd 0
|
.c: dd 0
|
||||||
.h: dd 0
|
.h: dd 0
|
||||||
.s: dd 0
|
.s: dd 0
|
||||||
|
|
||||||
DAPACK:
|
DAPACK:
|
||||||
db 0x10
|
db 0x10
|
||||||
@ -225,6 +232,4 @@ DAPACK:
|
|||||||
.count: dw 0 ; int 13 resets this to # of blocks actually read/written
|
.count: dw 0 ; int 13 resets this to # of blocks actually read/written
|
||||||
.buf: dw 0 ; memory buffer destination address (0:7c00)
|
.buf: dw 0 ; memory buffer destination address (0:7c00)
|
||||||
.seg: dw 0 ; in memory page zero
|
.seg: dw 0 ; in memory page zero
|
||||||
.addr: dq 0 ; put the lba to read in this spot
|
.addr: dq 0 ; put the lba to read in this spot
|
||||||
|
|
||||||
db 0xff
|
|
@ -1,6 +1,9 @@
|
|||||||
SECTION .text
|
SECTION .text
|
||||||
USE16
|
USE16
|
||||||
|
|
||||||
|
%include "gdt.asm"
|
||||||
|
%include "thunk.asm"
|
||||||
|
|
||||||
stage2.entry:
|
stage2.entry:
|
||||||
mov si, stage_msg
|
mov si, stage_msg
|
||||||
call print
|
call print
|
||||||
@ -13,26 +16,57 @@ stage2.entry:
|
|||||||
or al, 2
|
or al, 2
|
||||||
out 0x92, al
|
out 0x92, al
|
||||||
|
|
||||||
mov dword [protected_mode.func], stage3.entry
|
protected_mode:
|
||||||
jmp protected_mode.entry
|
; disable interrupts
|
||||||
|
cli
|
||||||
|
|
||||||
%include "gdt.asm"
|
; load protected mode GDT
|
||||||
%include "protected_mode.asm"
|
lgdt [gdtr]
|
||||||
%include "thunk.asm"
|
|
||||||
|
|
||||||
|
; set protected mode bit of cr0
|
||||||
|
mov eax, cr0
|
||||||
|
or eax, 1
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
; far jump to load CS with 32 bit segment
|
||||||
|
; We need to do this because we are entering 32-bit mode,
|
||||||
|
; but the instruction pipeline still has 16-bit instructions.
|
||||||
|
;
|
||||||
|
; gdt.pm32_code is a multiple of 8, so it always ends with three zero bits.
|
||||||
|
; The GDT spec abuses this fact, and uses these last three bits to store other
|
||||||
|
; data (table type and privilege). In this case, 000 is what we need anyway.
|
||||||
|
;
|
||||||
|
; Also note that CS isn't an address in protected mode---it's a GDT descriptor.
|
||||||
|
jmp gdt.pm32_code:protected_mode_inner
|
||||||
|
|
||||||
|
; We can now use 32-bit instructions!
|
||||||
USE32
|
USE32
|
||||||
|
|
||||||
stage3.entry:
|
protected_mode_inner:
|
||||||
; stage3 stack at 448 KiB (512KiB minus 64KiB disk buffer)
|
; load all the other segments with 32 bit data segments
|
||||||
|
mov eax, gdt.pm32_data
|
||||||
|
mov ds, eax
|
||||||
|
mov es, eax
|
||||||
|
mov fs, eax
|
||||||
|
mov gs, eax
|
||||||
|
mov ss, eax
|
||||||
|
|
||||||
|
; Place stage 3 stack at 448 KiB
|
||||||
|
; (512KiB minus 64KiB disk buffer)
|
||||||
mov esp, 0x70000
|
mov esp, 0x70000
|
||||||
|
|
||||||
; push arguments to `start()`
|
; push arguments to `start()`
|
||||||
mov eax, thunk.int10
|
mov eax, thunk.int10
|
||||||
push eax
|
push eax
|
||||||
|
|
||||||
|
; Call `start()`.
|
||||||
|
; 0x18 skips ELF headers.
|
||||||
mov eax, [stage3 + 0x18]
|
mov eax, [stage3 + 0x18]
|
||||||
call eax
|
call eax
|
||||||
|
|
||||||
.halt:
|
.halt:
|
||||||
|
; Halt if `start()` ever returns (it shouldn't, but just in case)
|
||||||
|
; Without this, we'll try to execute whatever comes next in memory.
|
||||||
cli
|
cli
|
||||||
hlt
|
hlt
|
||||||
jmp .halt
|
jmp .halt
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
/* This is the name of the Rust function we start in */
|
||||||
ENTRY(start)
|
ENTRY(start)
|
||||||
OUTPUT_FORMAT(elf32-i386)
|
OUTPUT_FORMAT(elf32-i386)
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
/* The start address must match bootloader.asm */
|
/* The start address must match main.asm */
|
||||||
. = 0x9000;
|
. = 0x8000;
|
||||||
|
|
||||||
. += SIZEOF_HEADERS;
|
. += SIZEOF_HEADERS;
|
||||||
. = ALIGN(4096);
|
. = ALIGN(4096);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user