Compare commits
10 Commits
6e3f176457
...
b0b39c56ba
Author | SHA1 | Date |
---|---|---|
Bananymous | b0b39c56ba | |
Bananymous | 055b1a2a1a | |
Bananymous | d99ef11e48 | |
Bananymous | 732eb9da41 | |
Bananymous | 8faad47843 | |
Bananymous | aa4f3046ff | |
Bananymous | b4775fbe75 | |
Bananymous | 8a5753b0fe | |
Bananymous | 1a75262b04 | |
Bananymous | 39801e51da |
|
@ -155,15 +155,12 @@ namespace LibELF
|
||||||
Elf64Xword p_align;
|
Elf64Xword p_align;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if ARCH(i386)
|
#if ARCH(i386)
|
||||||
using ElfNativeAddr = Elf32Addr;
|
using ElfNativeAddr = Elf32Addr;
|
||||||
using ElfNativeOff = Elf32Off;
|
using ElfNativeOff = Elf32Off;
|
||||||
using ElfNativeHalf = Elf32Half;
|
using ElfNativeHalf = Elf32Half;
|
||||||
using ElfNativeWord = Elf32Word;
|
using ElfNativeWord = Elf32Word;
|
||||||
using ElfNativeSword = Elf32Sword;
|
using ElfNativeSword = Elf32Sword;
|
||||||
using ElfNativeXword = Elf32Xword;
|
|
||||||
using ElfNativeSxword = Elf32Sxword;
|
|
||||||
using ElfNativeFileHeader = Elf32FileHeader;
|
using ElfNativeFileHeader = Elf32FileHeader;
|
||||||
using ElfNativeSectionHeader = Elf32SectionHeader;
|
using ElfNativeSectionHeader = Elf32SectionHeader;
|
||||||
using ElfNativeSymbol = Elf32Symbol;
|
using ElfNativeSymbol = Elf32Symbol;
|
||||||
|
|
|
@ -1,667 +0,0 @@
|
||||||
# FIXME: don't assume 512 byte sectors
|
|
||||||
.set SECTOR_SIZE_SHIFT, 9
|
|
||||||
.set SECTOR_SIZE, 1 << SECTOR_SIZE_SHIFT
|
|
||||||
|
|
||||||
.set SCREEN_WIDTH, 80
|
|
||||||
.set SCREEN_HEIGHT, 25
|
|
||||||
|
|
||||||
.code16
|
|
||||||
|
|
||||||
#########################################
|
|
||||||
#
|
|
||||||
# STAGE 1 BOOTLOADER
|
|
||||||
#
|
|
||||||
# its sole purpose is to load stage2 from
|
|
||||||
# bios boot partition
|
|
||||||
#
|
|
||||||
#########################################
|
|
||||||
|
|
||||||
.section .stage1
|
|
||||||
|
|
||||||
stage1_start:
|
|
||||||
jmp stage1_main
|
|
||||||
|
|
||||||
# prints character to screen
|
|
||||||
# al: ascii character to print
|
|
||||||
putc:
|
|
||||||
pusha
|
|
||||||
movb $0x0E, %ah
|
|
||||||
movb $0x00, %bh
|
|
||||||
int $0x10
|
|
||||||
popa
|
|
||||||
ret
|
|
||||||
|
|
||||||
# prints null terminated string to screen
|
|
||||||
# ds:si: string address
|
|
||||||
puts:
|
|
||||||
pushw %si
|
|
||||||
pushw %bx
|
|
||||||
pushw %ax
|
|
||||||
|
|
||||||
movb $0x0E, %ah
|
|
||||||
movb $0x00, %bh
|
|
||||||
|
|
||||||
.puts_loop:
|
|
||||||
lodsb
|
|
||||||
|
|
||||||
test %al, %al
|
|
||||||
jz .puts_done
|
|
||||||
|
|
||||||
int $0x10
|
|
||||||
jmp .puts_loop
|
|
||||||
|
|
||||||
.puts_done:
|
|
||||||
popw %ax
|
|
||||||
popw %bx
|
|
||||||
popw %si
|
|
||||||
ret
|
|
||||||
|
|
||||||
# compares memory between addresses
|
|
||||||
# si: ptr1
|
|
||||||
# di: ptr2
|
|
||||||
# cx: bytes count
|
|
||||||
# return:
|
|
||||||
# al: 1 if equal, 0 otherwise
|
|
||||||
memcmp:
|
|
||||||
pushw %si
|
|
||||||
pushw %di
|
|
||||||
|
|
||||||
cld
|
|
||||||
repe cmpsb
|
|
||||||
setzb %al
|
|
||||||
|
|
||||||
popw %di
|
|
||||||
popw %si
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
# read sectors from disk
|
|
||||||
# bx:eax: lba start
|
|
||||||
# cx: lba count (has to less than 0x80)
|
|
||||||
# dl: drive number
|
|
||||||
# ds:di: physical address
|
|
||||||
# returns only on success
|
|
||||||
read_from_disk:
|
|
||||||
push %ax
|
|
||||||
push %si
|
|
||||||
|
|
||||||
# prepare disk read packet
|
|
||||||
mov $disk_address_packet, %si
|
|
||||||
movb $0x10, 0x00(%si) # packet size
|
|
||||||
movb $0x00, 0x01(%si) # always 0
|
|
||||||
movw %cx, 0x02(%si) # lba count
|
|
||||||
movw %di, 0x04(%si) # offset
|
|
||||||
movw %ds, 0x06(%si) # segment
|
|
||||||
movl %eax, 0x08(%si) # 32 bit lower lba
|
|
||||||
movw %bx, 0x0C(%si) # 16 bit upper lba
|
|
||||||
movw $0, 0x0E(%si) # zero
|
|
||||||
|
|
||||||
# issue read command
|
|
||||||
clc
|
|
||||||
mov $0x42, %ah
|
|
||||||
int $0x13
|
|
||||||
jc .read_failed
|
|
||||||
|
|
||||||
pop %si
|
|
||||||
pop %ax
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
# Validates that GPT header is valid and usable
|
|
||||||
# doesn't return on invalid header
|
|
||||||
validate_gpt_header:
|
|
||||||
# confirm header (starts with 'EFI PART')
|
|
||||||
cmpl $0x20494645, (gpt_header + 0)
|
|
||||||
jne .not_gpt_partition
|
|
||||||
cmpl $0x54524150, (gpt_header + 4)
|
|
||||||
jne .not_gpt_partition
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
# Find partition entry with type guid
|
|
||||||
# si: address of type guid
|
|
||||||
# dl: drive number
|
|
||||||
# returns:
|
|
||||||
# al: non-zero if found
|
|
||||||
# di: address of partition entry
|
|
||||||
find_gpt_partition_entry:
|
|
||||||
# eax := entry_count
|
|
||||||
movl (gpt_header + 80), %eax
|
|
||||||
test %eax, %eax
|
|
||||||
jz .no_gpt_partition_found
|
|
||||||
|
|
||||||
# edx:eax := eax * entry_size
|
|
||||||
pushw %dx
|
|
||||||
mull (gpt_header + 84)
|
|
||||||
test %edx, %edx
|
|
||||||
jnz .too_gpt_big_entries
|
|
||||||
popw %dx
|
|
||||||
|
|
||||||
# FIXME: read one entry array section at a time
|
|
||||||
|
|
||||||
# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $SECTOR_SIZE_SHIFT, %ecx
|
|
||||||
|
|
||||||
# start lba
|
|
||||||
movl (gpt_header + 72), %eax
|
|
||||||
movw (gpt_header + 76), %bx
|
|
||||||
|
|
||||||
movw $gpt_entry_data, %di
|
|
||||||
|
|
||||||
call read_from_disk
|
|
||||||
|
|
||||||
# NOTE: 'only' 0xFFFF partitions supported,
|
|
||||||
# although read will fail with more than 0x80
|
|
||||||
movw (gpt_header + 80), %cx
|
|
||||||
|
|
||||||
.loop_gpt_entries:
|
|
||||||
pushw %cx
|
|
||||||
movw $16, %cx
|
|
||||||
call memcmp
|
|
||||||
popw %cx
|
|
||||||
|
|
||||||
testb %al, %al
|
|
||||||
jnz .gpt_partition_found
|
|
||||||
|
|
||||||
# add entry size to entry pointer
|
|
||||||
addw (gpt_header + 84), %di
|
|
||||||
|
|
||||||
loop .loop_gpt_entries
|
|
||||||
|
|
||||||
.no_gpt_partition_found:
|
|
||||||
movb $0, %al
|
|
||||||
ret
|
|
||||||
|
|
||||||
.gpt_partition_found:
|
|
||||||
movb $1, %al
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
stage1_main:
|
|
||||||
# setup segments
|
|
||||||
movw $0, %ax
|
|
||||||
movw %ax, %ds
|
|
||||||
movw %ax, %es
|
|
||||||
|
|
||||||
# setup stack
|
|
||||||
movw %ax, %ss
|
|
||||||
movw $0x7C00, %sp
|
|
||||||
|
|
||||||
# save boot disk number
|
|
||||||
movb %dl, (boot_disk_number)
|
|
||||||
|
|
||||||
# confirm that int 13h extensions are available
|
|
||||||
clc
|
|
||||||
movb $0x41, %ah
|
|
||||||
movw $0x55AA, %bx
|
|
||||||
movb (boot_disk_number), %dl
|
|
||||||
int $0x13
|
|
||||||
jc .no_int13h_ext
|
|
||||||
|
|
||||||
# read gpt header
|
|
||||||
movl $1, %eax
|
|
||||||
movw $0, %bx
|
|
||||||
movw $1, %cx
|
|
||||||
movb (boot_disk_number), %dl
|
|
||||||
movw $gpt_header, %di
|
|
||||||
call read_from_disk
|
|
||||||
|
|
||||||
call validate_gpt_header
|
|
||||||
|
|
||||||
movw $bios_boot_guid, %si
|
|
||||||
movb boot_disk_number, %dl
|
|
||||||
call find_gpt_partition_entry
|
|
||||||
testb %al, %al
|
|
||||||
jz .no_bios_boot_partition
|
|
||||||
|
|
||||||
# BIOS boot partiton found!
|
|
||||||
|
|
||||||
# first lba
|
|
||||||
movl 32(%di), %eax
|
|
||||||
movw $0, %bx
|
|
||||||
|
|
||||||
# count := last lba - first lba + 1
|
|
||||||
movl 40(%di), %ecx
|
|
||||||
subl %eax, %ecx
|
|
||||||
addl $1, %ecx
|
|
||||||
|
|
||||||
# calculate stage2 sector count
|
|
||||||
movw $((stage2_end - stage2_start + SECTOR_SIZE - 1) / SECTOR_SIZE), %cx
|
|
||||||
|
|
||||||
movb (boot_disk_number), %dl
|
|
||||||
movw $stage2_start, %di
|
|
||||||
|
|
||||||
call read_from_disk
|
|
||||||
jmp stage2_main
|
|
||||||
|
|
||||||
print_and_halt:
|
|
||||||
call puts
|
|
||||||
halt:
|
|
||||||
hlt
|
|
||||||
jmp halt
|
|
||||||
|
|
||||||
.no_int13h_ext:
|
|
||||||
mov $no_int13_ext_msg, %si
|
|
||||||
jmp print_and_halt
|
|
||||||
|
|
||||||
.read_failed:
|
|
||||||
mov $read_failed_msg, %si
|
|
||||||
jmp print_and_halt
|
|
||||||
|
|
||||||
.not_gpt_partition:
|
|
||||||
mov $not_gpt_partition_msg, %si
|
|
||||||
jmp print_and_halt
|
|
||||||
|
|
||||||
.no_bios_boot_partition:
|
|
||||||
mov $no_bios_boot_partition_msg, %si
|
|
||||||
jmp print_and_halt
|
|
||||||
|
|
||||||
.too_gpt_big_entries:
|
|
||||||
mov $too_gpt_big_entries_msg, %si
|
|
||||||
jmp print_and_halt
|
|
||||||
|
|
||||||
|
|
||||||
# 21686148-6449-6E6F-744E-656564454649
|
|
||||||
bios_boot_guid:
|
|
||||||
.long 0x21686148 # little endian
|
|
||||||
.word 0x6449 # little endian
|
|
||||||
.word 0x6E6F # little endian
|
|
||||||
.word 0x4E74 # big endian
|
|
||||||
.quad 0x494645646565 # big endian
|
|
||||||
|
|
||||||
no_int13_ext_msg:
|
|
||||||
.asciz "no INT 13h ext"
|
|
||||||
|
|
||||||
read_failed_msg:
|
|
||||||
.asciz "read error"
|
|
||||||
|
|
||||||
not_gpt_partition_msg:
|
|
||||||
.asciz "not GPT"
|
|
||||||
|
|
||||||
no_bios_boot_partition_msg:
|
|
||||||
.asciz "no bios boot partition"
|
|
||||||
|
|
||||||
too_gpt_big_entries_msg:
|
|
||||||
.asciz "too big GPT array"
|
|
||||||
|
|
||||||
boot_disk_number:
|
|
||||||
.skip 1
|
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
|
||||||
#
|
|
||||||
# STAGE 2 BOOTLOADER
|
|
||||||
#
|
|
||||||
#########################################
|
|
||||||
|
|
||||||
.section .stage2
|
|
||||||
|
|
||||||
stage2_start:
|
|
||||||
|
|
||||||
# read a character from keyboard
|
|
||||||
# return:
|
|
||||||
# al: ascii
|
|
||||||
# ah: bios scan code
|
|
||||||
getc:
|
|
||||||
movb $0x00, %ah
|
|
||||||
int $0x16
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
# prints newline to screen
|
|
||||||
print_newline:
|
|
||||||
pushw %ax
|
|
||||||
movb $'\r', %al
|
|
||||||
call putc
|
|
||||||
movb $'\n', %al
|
|
||||||
call putc
|
|
||||||
pop %ax
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
# prints backspace to screen, can go back a line
|
|
||||||
print_backspace:
|
|
||||||
pusha
|
|
||||||
|
|
||||||
# get cursor position
|
|
||||||
movb $0x03, %ah
|
|
||||||
movb $0x00, %bh
|
|
||||||
int $0x10
|
|
||||||
|
|
||||||
# don't do anyting if on first row
|
|
||||||
testb %dh, %dh
|
|
||||||
jz .print_backspace_done
|
|
||||||
|
|
||||||
# go one line up if on first column
|
|
||||||
test %dl, %dl
|
|
||||||
jz .print_backspace_go_line_up
|
|
||||||
|
|
||||||
# otherwise decrease column
|
|
||||||
decb %dl
|
|
||||||
jmp .print_backspace_do_print
|
|
||||||
|
|
||||||
.print_backspace_go_line_up:
|
|
||||||
# decrease row and set column to the last one
|
|
||||||
decb %dh
|
|
||||||
movb $(SCREEN_WIDTH - 1), %dl
|
|
||||||
|
|
||||||
.print_backspace_do_print:
|
|
||||||
# set cursor position
|
|
||||||
movb $0x02, %ah
|
|
||||||
int $0x10
|
|
||||||
|
|
||||||
# print 'empty' character (space)
|
|
||||||
mov $' ', %al
|
|
||||||
call putc
|
|
||||||
|
|
||||||
# set cursor position
|
|
||||||
movb $0x02, %ah
|
|
||||||
int $0x10
|
|
||||||
|
|
||||||
.print_backspace_done:
|
|
||||||
popa
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
# print number to screen
|
|
||||||
# ax: number to print
|
|
||||||
# bx: number base
|
|
||||||
# cx: min width (zero pads if shorter)
|
|
||||||
printnum:
|
|
||||||
pusha
|
|
||||||
pushw %cx
|
|
||||||
|
|
||||||
movw $printnum_buffer, %si
|
|
||||||
xorw %cx, %cx
|
|
||||||
|
|
||||||
.printnum_fill_loop:
|
|
||||||
# fill buffer with all remainders ax % bx
|
|
||||||
xorw %dx, %dx
|
|
||||||
divw %bx
|
|
||||||
movb %dl, (%si)
|
|
||||||
incw %si
|
|
||||||
incw %cx
|
|
||||||
testw %ax, %ax
|
|
||||||
jnz .printnum_fill_loop
|
|
||||||
|
|
||||||
# check if zero pad is required
|
|
||||||
popw %dx
|
|
||||||
cmpw %cx, %dx
|
|
||||||
jbe .printnum_print_loop
|
|
||||||
|
|
||||||
xchgw %dx, %cx
|
|
||||||
subw %dx, %cx
|
|
||||||
movb $'0', %al
|
|
||||||
.printnum_pad_zeroes:
|
|
||||||
call putc
|
|
||||||
loop .printnum_pad_zeroes
|
|
||||||
|
|
||||||
movw %dx, %cx
|
|
||||||
|
|
||||||
.printnum_print_loop:
|
|
||||||
decw %si
|
|
||||||
movb (%si), %al
|
|
||||||
cmpb $10, %al
|
|
||||||
jae 1f
|
|
||||||
addb $'0', %al
|
|
||||||
jmp 2f
|
|
||||||
1: addb $('a' - 10), %al
|
|
||||||
2: call putc
|
|
||||||
loop .printnum_print_loop
|
|
||||||
|
|
||||||
popa
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
# test if character is printable ascii
|
|
||||||
# al: character to test
|
|
||||||
# sets ZF if is printable
|
|
||||||
isprint:
|
|
||||||
cmpb $0x20, %al
|
|
||||||
jb .isprint_done
|
|
||||||
cmpb $0x7E, %al
|
|
||||||
ja .isprint_done
|
|
||||||
cmpb %al, %al
|
|
||||||
.isprint_done:
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
# fills memory map data structure
|
|
||||||
# doesn't return on error
|
|
||||||
get_memory_map:
|
|
||||||
pusha
|
|
||||||
|
|
||||||
movl $0, (memory_map_entry_count)
|
|
||||||
|
|
||||||
movl $0x0000E820, %eax
|
|
||||||
movl $0x534D4150, %edx
|
|
||||||
xorl %ebx, %ebx
|
|
||||||
movl $20, %ecx
|
|
||||||
movw $memory_map_entries, %di
|
|
||||||
|
|
||||||
clc
|
|
||||||
int $0x15
|
|
||||||
# If first call returs with CF set, the call failed
|
|
||||||
jc .get_memory_map_error
|
|
||||||
|
|
||||||
.get_memory_map_rest:
|
|
||||||
cmpl $0x534D4150, %eax
|
|
||||||
jne .get_memory_map_error
|
|
||||||
|
|
||||||
# FIXME: don't assume BIOS to always return 20 bytes
|
|
||||||
cmpl $20, %ecx
|
|
||||||
jne .get_memory_map_error
|
|
||||||
|
|
||||||
# increment entry count
|
|
||||||
incl (memory_map_entry_count)
|
|
||||||
|
|
||||||
# increment entry pointer
|
|
||||||
addw %cx, %di
|
|
||||||
|
|
||||||
# BIOS can indicate end of list by 0 in ebx
|
|
||||||
testl %ebx, %ebx
|
|
||||||
jz .get_memory_map_done
|
|
||||||
|
|
||||||
movl $0x0000E820, %eax
|
|
||||||
movl $0x534D4150, %edx
|
|
||||||
|
|
||||||
clc
|
|
||||||
int $0x15
|
|
||||||
# BIOS can indicate end of list by setting CF
|
|
||||||
jnc .get_memory_map_rest
|
|
||||||
|
|
||||||
.get_memory_map_done:
|
|
||||||
popa
|
|
||||||
ret
|
|
||||||
|
|
||||||
.get_memory_map_error:
|
|
||||||
movw $memory_map_error_msg, %si
|
|
||||||
jmp print_and_halt
|
|
||||||
|
|
||||||
|
|
||||||
# fills command line buffer
|
|
||||||
get_command_line:
|
|
||||||
pusha
|
|
||||||
|
|
||||||
movw $command_line_enter_msg, %si
|
|
||||||
call puts
|
|
||||||
|
|
||||||
movw $command_line_buffer, %di
|
|
||||||
|
|
||||||
.get_command_line_loop:
|
|
||||||
call getc
|
|
||||||
|
|
||||||
cmpb $'\b', %al
|
|
||||||
je .get_command_line_backspace
|
|
||||||
|
|
||||||
# Not sure if some BIOSes return '\n' as enter, but check it just in case
|
|
||||||
cmpb $'\r', %al
|
|
||||||
je .get_command_line_done
|
|
||||||
cmpb $'\n', %al
|
|
||||||
je .get_command_line_done
|
|
||||||
|
|
||||||
call isprint
|
|
||||||
jnz .get_command_line_loop
|
|
||||||
|
|
||||||
# put byte to buffer
|
|
||||||
movb %al, (%di)
|
|
||||||
incw %di
|
|
||||||
|
|
||||||
# print byte
|
|
||||||
call putc
|
|
||||||
|
|
||||||
jmp .get_command_line_loop
|
|
||||||
|
|
||||||
.get_command_line_backspace:
|
|
||||||
# don't do anything if at the beginning
|
|
||||||
cmpw $command_line_buffer, %di
|
|
||||||
je .get_command_line_loop
|
|
||||||
|
|
||||||
# decrement buffer pointer
|
|
||||||
decw %di
|
|
||||||
|
|
||||||
# erase byte in display
|
|
||||||
call print_backspace
|
|
||||||
|
|
||||||
jmp .get_command_line_loop
|
|
||||||
|
|
||||||
.get_command_line_done:
|
|
||||||
# null terminate command line
|
|
||||||
movb $0, (%di)
|
|
||||||
|
|
||||||
call print_newline
|
|
||||||
|
|
||||||
popa
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
stage2_main:
|
|
||||||
# clear screen and enter 80x25 text mode
|
|
||||||
movb $0x03, %al
|
|
||||||
movb $0x00, %ah
|
|
||||||
int $0x10
|
|
||||||
|
|
||||||
# print hello message
|
|
||||||
movw $hello_msg, %si
|
|
||||||
call puts
|
|
||||||
call print_newline
|
|
||||||
|
|
||||||
call get_memory_map
|
|
||||||
call get_command_line
|
|
||||||
|
|
||||||
call print_newline
|
|
||||||
|
|
||||||
movw $start_kernel_load_msg, %si
|
|
||||||
call puts
|
|
||||||
call print_newline
|
|
||||||
|
|
||||||
movw $command_line_msg, %si
|
|
||||||
call puts
|
|
||||||
movw $command_line_buffer, %si
|
|
||||||
call puts
|
|
||||||
call print_newline
|
|
||||||
|
|
||||||
movw $memory_map_msg, %si
|
|
||||||
call puts
|
|
||||||
call print_newline
|
|
||||||
|
|
||||||
movl (memory_map_entry_count), %edx
|
|
||||||
movw $memory_map_entries, %si
|
|
||||||
|
|
||||||
movw $16, %bx
|
|
||||||
movw $4, %cx
|
|
||||||
|
|
||||||
.loop_memory_map:
|
|
||||||
movb $' ', %al
|
|
||||||
call putc
|
|
||||||
call putc
|
|
||||||
call putc
|
|
||||||
call putc
|
|
||||||
|
|
||||||
movw 0x06(%si), %ax
|
|
||||||
call printnum
|
|
||||||
movw 0x04(%si), %ax
|
|
||||||
call printnum
|
|
||||||
movw 0x02(%si), %ax
|
|
||||||
call printnum
|
|
||||||
movw 0x00(%si), %ax
|
|
||||||
call printnum
|
|
||||||
|
|
||||||
movb $',', %al
|
|
||||||
call putc
|
|
||||||
movb $' ', %al
|
|
||||||
call putc
|
|
||||||
|
|
||||||
movw 0x0E(%si), %ax
|
|
||||||
call printnum
|
|
||||||
movw 0x0C(%si), %ax
|
|
||||||
call printnum
|
|
||||||
movw 0x0A(%si), %ax
|
|
||||||
call printnum
|
|
||||||
movw 0x08(%si), %ax
|
|
||||||
call printnum
|
|
||||||
|
|
||||||
movb $',', %al
|
|
||||||
call putc
|
|
||||||
movb $' ', %al
|
|
||||||
call putc
|
|
||||||
|
|
||||||
movw 0x12(%si), %ax
|
|
||||||
call printnum
|
|
||||||
movw 0x10(%si), %ax
|
|
||||||
call printnum
|
|
||||||
|
|
||||||
call print_newline
|
|
||||||
|
|
||||||
addw $20, %si
|
|
||||||
|
|
||||||
decl %edx
|
|
||||||
jnz .loop_memory_map
|
|
||||||
|
|
||||||
|
|
||||||
jmp halt
|
|
||||||
|
|
||||||
|
|
||||||
hello_msg:
|
|
||||||
.asciz "This is banan-os bootloader"
|
|
||||||
|
|
||||||
command_line_msg:
|
|
||||||
command_line_enter_msg:
|
|
||||||
.asciz "cmdline: "
|
|
||||||
|
|
||||||
memory_map_msg:
|
|
||||||
.asciz "memmap:"
|
|
||||||
|
|
||||||
memory_map_error_msg:
|
|
||||||
.asciz "Failed to get memory map"
|
|
||||||
|
|
||||||
start_kernel_load_msg:
|
|
||||||
.asciz "Starting to load kernel"
|
|
||||||
|
|
||||||
stage2_end:
|
|
||||||
|
|
||||||
|
|
||||||
.section .bss
|
|
||||||
|
|
||||||
.align SECTOR_SIZE
|
|
||||||
gpt_header:
|
|
||||||
.skip SECTOR_SIZE
|
|
||||||
gpt_entry_data:
|
|
||||||
.skip SECTOR_SIZE
|
|
||||||
|
|
||||||
disk_address_packet:
|
|
||||||
.skip 16
|
|
||||||
|
|
||||||
printnum_buffer:
|
|
||||||
.skip 10
|
|
||||||
|
|
||||||
memory_map_entry_count:
|
|
||||||
.skip 4
|
|
||||||
# 100 entries should be enough...
|
|
||||||
memory_map_entries:
|
|
||||||
.skip 20 * 100
|
|
||||||
|
|
||||||
# 100 character command line
|
|
||||||
command_line_buffer:
|
|
||||||
.skip 100
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
.code16
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
#
|
||||||
|
# STAGE 1 BOOTLOADER
|
||||||
|
#
|
||||||
|
# its sole purpose is to load stage2 from
|
||||||
|
# bios boot partition
|
||||||
|
#
|
||||||
|
#########################################
|
||||||
|
|
||||||
|
.section .stage1
|
||||||
|
|
||||||
|
.global stage1_main
|
||||||
|
stage1_main:
|
||||||
|
# setup segments
|
||||||
|
movw $0, %ax
|
||||||
|
movw %ax, %ds
|
||||||
|
movw %ax, %es
|
||||||
|
|
||||||
|
# setup stack
|
||||||
|
movw %ax, %ss
|
||||||
|
movl $0x7C00, %esp
|
||||||
|
|
||||||
|
# save boot disk number
|
||||||
|
call read_stage2_into_memory
|
||||||
|
|
||||||
|
jmp stage2_main
|
||||||
|
|
||||||
|
.global print_and_halt
|
||||||
|
print_and_halt:
|
||||||
|
call puts
|
||||||
|
halt:
|
||||||
|
hlt
|
||||||
|
jmp halt
|
||||||
|
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
#
|
||||||
|
# STAGE 2 BOOTLOADER
|
||||||
|
#
|
||||||
|
#########################################
|
||||||
|
|
||||||
|
.section .stage2
|
||||||
|
|
||||||
|
stage2_main:
|
||||||
|
# clear screen and enter 80x25 text mode
|
||||||
|
movb $0x03, %al
|
||||||
|
movb $0x00, %ah
|
||||||
|
int $0x10
|
||||||
|
|
||||||
|
# print hello message
|
||||||
|
movw $hello_msg, %si
|
||||||
|
call puts; call print_newline
|
||||||
|
|
||||||
|
call get_memory_map
|
||||||
|
call read_user_command_line
|
||||||
|
|
||||||
|
call print_newline
|
||||||
|
|
||||||
|
movw $start_kernel_load_msg, %si
|
||||||
|
call puts; call print_newline
|
||||||
|
|
||||||
|
call print_memory_map
|
||||||
|
|
||||||
|
call find_root_disk
|
||||||
|
call find_root_partition
|
||||||
|
|
||||||
|
call print_root_partition_info
|
||||||
|
|
||||||
|
jmp halt
|
||||||
|
|
||||||
|
hello_msg:
|
||||||
|
.asciz "This is banan-os bootloader"
|
||||||
|
|
||||||
|
start_kernel_load_msg:
|
||||||
|
.asciz "Starting to load kernel"
|
|
@ -1,45 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
CURRENT_DIR=$(dirname $(realpath $0))
|
|
||||||
|
|
||||||
INSTALLER_DIR=$CURRENT_DIR/installer
|
|
||||||
INSTALLER_BUILD_DIR=$INSTALLER_DIR/build
|
|
||||||
|
|
||||||
BUILD_DIR=$CURRENT_DIR/build
|
|
||||||
DISK_IMAGE_PATH=$CURRENT_DIR/test.img
|
|
||||||
|
|
||||||
if ! [ -d $INSTALLER_BUILD_DIR ]; then
|
|
||||||
mkdir -p $INSTALLER_BUILD_DIR
|
|
||||||
cd $INSTALLER_BUILD_DIR
|
|
||||||
cmake ..
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd $INSTALLER_BUILD_DIR
|
|
||||||
make
|
|
||||||
|
|
||||||
cd $CURRENT_DIR
|
|
||||||
|
|
||||||
echo creating clean disk image
|
|
||||||
truncate --size 0 $DISK_IMAGE_PATH
|
|
||||||
truncate --size 50M $DISK_IMAGE_PATH
|
|
||||||
echo -ne 'g\nn\n\n\n+1M\nt 1\n4\nw\n' | fdisk $DISK_IMAGE_PATH > /dev/null
|
|
||||||
|
|
||||||
mkdir -p $BUILD_DIR
|
|
||||||
|
|
||||||
echo compiling bootloader
|
|
||||||
x86_64-banan_os-as arch/x86_64/boot.S -o $BUILD_DIR/bootloader.o
|
|
||||||
|
|
||||||
echo linking bootloader
|
|
||||||
x86_64-banan_os-ld -nostdlib -T arch/x86_64/linker.ld $BUILD_DIR/bootloader.o -o $BUILD_DIR/bootloader
|
|
||||||
|
|
||||||
echo installing bootloader
|
|
||||||
$INSTALLER_BUILD_DIR/x86_64-banan_os-bootloader-installer $BUILD_DIR/bootloader $DISK_IMAGE_PATH
|
|
||||||
|
|
||||||
if [ "$1" == "debug" ] ; then
|
|
||||||
QEMU_FLAGS="-s -S"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo running qemu
|
|
||||||
env BANAN_DISK_IMAGE_PATH=${DISK_IMAGE_PATH} BANAN_ARCH=x86_64 ../script/qemu.sh $QEMU_FLAGS
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
.code16
|
||||||
|
|
||||||
|
.section .stage2
|
||||||
|
|
||||||
|
# fills command line buffer
|
||||||
|
# NO REGISTERS SAVED
|
||||||
|
.global read_user_command_line
|
||||||
|
read_user_command_line:
|
||||||
|
movw $command_line_enter_msg, %si
|
||||||
|
call puts
|
||||||
|
|
||||||
|
movw $command_line_buffer, %di
|
||||||
|
|
||||||
|
.read_user_command_line_loop:
|
||||||
|
call getc
|
||||||
|
|
||||||
|
cmpb $'\b', %al
|
||||||
|
je .read_user_command_line_backspace
|
||||||
|
|
||||||
|
# Not sure if some BIOSes return '\n' as enter, but check it just in case
|
||||||
|
cmpb $'\r', %al
|
||||||
|
je .read_user_command_line_done
|
||||||
|
cmpb $'\n', %al
|
||||||
|
je .read_user_command_line_done
|
||||||
|
|
||||||
|
call isprint
|
||||||
|
testb %al, %al
|
||||||
|
jnz .read_user_command_line_loop
|
||||||
|
|
||||||
|
# put byte to buffer
|
||||||
|
movb %al, (%di)
|
||||||
|
incw %di
|
||||||
|
|
||||||
|
# print byte
|
||||||
|
call putc
|
||||||
|
|
||||||
|
jmp .read_user_command_line_loop
|
||||||
|
|
||||||
|
.read_user_command_line_backspace:
|
||||||
|
# don't do anything if at the beginning
|
||||||
|
cmpw $command_line_buffer, %di
|
||||||
|
je .read_user_command_line_loop
|
||||||
|
|
||||||
|
# decrement buffer pointer
|
||||||
|
decw %di
|
||||||
|
|
||||||
|
# erase byte in display
|
||||||
|
call print_backspace
|
||||||
|
|
||||||
|
jmp .read_user_command_line_loop
|
||||||
|
|
||||||
|
.read_user_command_line_done:
|
||||||
|
# null terminate command line
|
||||||
|
movb $0, (%di)
|
||||||
|
|
||||||
|
call print_newline
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
command_line_enter_msg:
|
||||||
|
.asciz "cmdline: "
|
||||||
|
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
|
||||||
|
# 100 character command line
|
||||||
|
command_line_buffer:
|
||||||
|
.skip 100
|
|
@ -0,0 +1,491 @@
|
||||||
|
# FIXME: don't assume 512 byte sectors
|
||||||
|
.set SECTOR_SIZE_SHIFT, 9
|
||||||
|
.set SECTOR_SIZE, 1 << SECTOR_SIZE_SHIFT
|
||||||
|
|
||||||
|
.code16
|
||||||
|
|
||||||
|
.section .stage1
|
||||||
|
|
||||||
|
.global stage2_start
|
||||||
|
.global stage2_end
|
||||||
|
|
||||||
|
# check that drive has int13 ext
|
||||||
|
# dl: drive number
|
||||||
|
# returns only if drive does have the extension
|
||||||
|
drive_has_int13_ext:
|
||||||
|
pusha
|
||||||
|
|
||||||
|
movb $0x41, %ah
|
||||||
|
movw $0x55AA, %bx
|
||||||
|
int $0x13
|
||||||
|
jc .drive_has_int13_ext_no_int13_ext
|
||||||
|
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
|
.drive_has_int13_ext_no_int13_ext:
|
||||||
|
mov $no_int13_ext_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
|
||||||
|
# read sectors from disk
|
||||||
|
# bx:eax: lba start
|
||||||
|
# cx: lba count (has to less than 0x80)
|
||||||
|
# dl: drive number
|
||||||
|
# ds:di: physical address
|
||||||
|
# returns only on success
|
||||||
|
.global read_from_disk
|
||||||
|
read_from_disk:
|
||||||
|
pusha
|
||||||
|
|
||||||
|
call drive_has_int13_ext
|
||||||
|
|
||||||
|
# prepare disk read packet
|
||||||
|
mov $disk_address_packet, %si
|
||||||
|
movb $0x10, 0x00(%si) # packet size
|
||||||
|
movb $0x00, 0x01(%si) # always 0
|
||||||
|
movw %cx, 0x02(%si) # lba count
|
||||||
|
movw %di, 0x04(%si) # offset
|
||||||
|
movw %ds, 0x06(%si) # segment
|
||||||
|
movl %eax, 0x08(%si) # 32 bit lower lba
|
||||||
|
movw %bx, 0x0C(%si) # 16 bit upper lba
|
||||||
|
movw $0, 0x0E(%si) # zero
|
||||||
|
|
||||||
|
# issue read command
|
||||||
|
mov $0x42, %ah
|
||||||
|
int $0x13
|
||||||
|
jc .read_from_disk_failed
|
||||||
|
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
|
.read_from_disk_failed:
|
||||||
|
mov $read_from_disk_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
|
||||||
|
# Reads GPT header into gpt_header buffer
|
||||||
|
# dl: drive number
|
||||||
|
# return:
|
||||||
|
# ax: 1 if has GPT header, 0 otherwise
|
||||||
|
.global read_gpt_header
|
||||||
|
read_gpt_header:
|
||||||
|
pushw %bx
|
||||||
|
pushw %cx
|
||||||
|
pushw %di
|
||||||
|
|
||||||
|
xorw %bx, %bx
|
||||||
|
movl $1, %eax
|
||||||
|
movw $1, %cx
|
||||||
|
movw $gpt_header, %di
|
||||||
|
call read_from_disk
|
||||||
|
|
||||||
|
xorw %bx, %bx
|
||||||
|
movw $1, %ax
|
||||||
|
|
||||||
|
# check if header starts with 'EFI PART'
|
||||||
|
cmpl $0x20494645, (gpt_header + 0)
|
||||||
|
cmovnew %bx, %ax
|
||||||
|
cmpl $0x54524150, (gpt_header + 4)
|
||||||
|
cmovnew %bx, %ax
|
||||||
|
|
||||||
|
popw %di
|
||||||
|
popw %cx
|
||||||
|
popw %bx
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
# Find bios boot partition from boot drive
|
||||||
|
# returns:
|
||||||
|
# bx:eax: first lba
|
||||||
|
# cx: sector count
|
||||||
|
find_stage2_partition:
|
||||||
|
# read boot disk GPT header
|
||||||
|
movb (boot_disk_number), %dl
|
||||||
|
call read_gpt_header
|
||||||
|
|
||||||
|
testb %al, %al
|
||||||
|
jz .find_stage2_partition_not_gpt
|
||||||
|
|
||||||
|
# eax := entry_count
|
||||||
|
movl (gpt_header + 80), %eax
|
||||||
|
test %eax, %eax
|
||||||
|
jz .find_stage2_partition_not_found
|
||||||
|
|
||||||
|
# edx:eax := eax * entry_size
|
||||||
|
mull (gpt_header + 84)
|
||||||
|
test %edx, %edx
|
||||||
|
jnz .find_stage2_partition_too_big_entries
|
||||||
|
|
||||||
|
# FIXME: read one entry array section at a time
|
||||||
|
|
||||||
|
# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
|
||||||
|
movl %eax, %ecx
|
||||||
|
shrl $SECTOR_SIZE_SHIFT, %ecx
|
||||||
|
|
||||||
|
# start lba
|
||||||
|
movl (gpt_header + 72), %eax
|
||||||
|
movw (gpt_header + 76), %bx
|
||||||
|
|
||||||
|
movw $gpt_entry_data, %di
|
||||||
|
movw $bios_boot_guid, %si
|
||||||
|
movb (boot_disk_number), %dl
|
||||||
|
|
||||||
|
call read_from_disk
|
||||||
|
|
||||||
|
# NOTE: 'only' 0xFFFF partitions supported,
|
||||||
|
# although read will fail with more than 0x80
|
||||||
|
movw (gpt_header + 80), %cx
|
||||||
|
|
||||||
|
.find_stage2_partition_loop_gpt_entries:
|
||||||
|
pushw %cx
|
||||||
|
movw $16, %cx
|
||||||
|
call memcmp
|
||||||
|
popw %cx
|
||||||
|
|
||||||
|
testb %al, %al
|
||||||
|
jnz .find_stage2_partition_found
|
||||||
|
|
||||||
|
# add entry size to entry pointer
|
||||||
|
addw (gpt_header + 84), %di
|
||||||
|
|
||||||
|
loop .find_stage2_partition_loop_gpt_entries
|
||||||
|
|
||||||
|
# fall through to not found case
|
||||||
|
.find_stage2_partition_not_found:
|
||||||
|
movw $no_bios_boot_partition_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
.find_stage2_partition_not_gpt:
|
||||||
|
movw $not_gpt_partition_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
.find_stage2_partition_too_big_entries:
|
||||||
|
movw $too_gpt_big_entries_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
.find_stage2_partition_found:
|
||||||
|
# first lba
|
||||||
|
movl 32(%di), %eax
|
||||||
|
movw 36(%di), %bx
|
||||||
|
|
||||||
|
# count := last lba - first lba + 1
|
||||||
|
movl 40(%di), %ecx
|
||||||
|
subl %eax, %ecx
|
||||||
|
incl %ecx
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
# reads stage2 into memory
|
||||||
|
# dl: boot drive number
|
||||||
|
# returns only on success
|
||||||
|
.global read_stage2_into_memory
|
||||||
|
read_stage2_into_memory:
|
||||||
|
movb %dl, (boot_disk_number)
|
||||||
|
|
||||||
|
# push stage2 sector count
|
||||||
|
movl $stage2_end, %eax
|
||||||
|
subl $stage2_start, %eax
|
||||||
|
addl $(SECTOR_SIZE - 1), %eax
|
||||||
|
movl $SECTOR_SIZE, %ecx
|
||||||
|
xorl %edx, %edx
|
||||||
|
divl %ecx
|
||||||
|
pushl %eax
|
||||||
|
|
||||||
|
call find_stage2_partition
|
||||||
|
|
||||||
|
movb (boot_disk_number), %dl
|
||||||
|
popl %ecx # FIXME: validate that partition has enough sectors
|
||||||
|
movw $stage2_start, %di
|
||||||
|
call read_from_disk
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
# 21686148-6449-6E6F-744E-656564454649
|
||||||
|
.align 4
|
||||||
|
bios_boot_guid:
|
||||||
|
.long 0x21686148 # little endian
|
||||||
|
.word 0x6449 # little endian
|
||||||
|
.word 0x6E6F # little endian
|
||||||
|
.word 0x4E74 # big endian
|
||||||
|
.quad 0x494645646565 # big endian
|
||||||
|
|
||||||
|
boot_disk_number:
|
||||||
|
.skip 1
|
||||||
|
|
||||||
|
read_from_disk_msg:
|
||||||
|
.asciz "read error"
|
||||||
|
|
||||||
|
no_int13_ext_msg:
|
||||||
|
.asciz "no INT13 ext"
|
||||||
|
|
||||||
|
no_bios_boot_partition_msg:
|
||||||
|
.asciz "no bios boot"
|
||||||
|
|
||||||
|
too_gpt_big_entries_msg:
|
||||||
|
.asciz "too big GPT array"
|
||||||
|
|
||||||
|
not_gpt_partition_msg:
|
||||||
|
.asciz "not GPT"
|
||||||
|
|
||||||
|
|
||||||
|
.section .stage2
|
||||||
|
|
||||||
|
# check if drive exists
|
||||||
|
# dl: drive number
|
||||||
|
# return:
|
||||||
|
# al: 1 if disk is usable, 0 otherwise
|
||||||
|
drive_exists:
|
||||||
|
pusha
|
||||||
|
|
||||||
|
movb $0x48, %ah
|
||||||
|
movw $disk_drive_parameters, %si
|
||||||
|
movw $0x1A, (disk_drive_parameters) # set buffer size
|
||||||
|
|
||||||
|
int $0x13
|
||||||
|
jc .drive_exists_nope
|
||||||
|
|
||||||
|
popa
|
||||||
|
movb $1, %al
|
||||||
|
ret
|
||||||
|
|
||||||
|
.drive_exists_nope:
|
||||||
|
popa
|
||||||
|
movb $0, %al
|
||||||
|
ret
|
||||||
|
|
||||||
|
# find root disk and populate root_disk_drive_number field
|
||||||
|
# NO REGISTERS SAVED
|
||||||
|
.global find_root_disk
|
||||||
|
find_root_disk:
|
||||||
|
movb $0x80, %dl
|
||||||
|
|
||||||
|
.find_root_disk_loop:
|
||||||
|
call drive_exists
|
||||||
|
testb %al, %al
|
||||||
|
jz .find_root_disk_not_found
|
||||||
|
|
||||||
|
# read GPT header
|
||||||
|
xorw %bx, %bx
|
||||||
|
movl $1, %eax
|
||||||
|
movw $1, %cx
|
||||||
|
movw $gpt_header, %di
|
||||||
|
call read_from_disk
|
||||||
|
|
||||||
|
# confirm header (starts with 'EFI PART')
|
||||||
|
cmpl $0x20494645, (gpt_header + 0)
|
||||||
|
jne .find_root_disk_next_disk
|
||||||
|
cmpl $0x54524150, (gpt_header + 4)
|
||||||
|
jne .find_root_disk_next_disk
|
||||||
|
|
||||||
|
# compare disk GUID
|
||||||
|
movw $root_disk_guid, %si
|
||||||
|
movw $(gpt_header + 56), %di
|
||||||
|
movw $16, %cx
|
||||||
|
call memcmp
|
||||||
|
testb %al, %al
|
||||||
|
jz .find_root_disk_next_disk
|
||||||
|
|
||||||
|
movw $root_disk_found_msg, %si
|
||||||
|
call puts; call print_newline
|
||||||
|
|
||||||
|
movb %dl, (root_disk_drive_number)
|
||||||
|
ret
|
||||||
|
|
||||||
|
.find_root_disk_next_disk:
|
||||||
|
incb %dl
|
||||||
|
jmp .find_root_disk_loop
|
||||||
|
|
||||||
|
.find_root_disk_not_found:
|
||||||
|
movw $root_disk_not_found_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
|
||||||
|
# finds root partition from root disk
|
||||||
|
# fills root_partition_entry data structure
|
||||||
|
# NOTE: assumes GPT header is in `gpt_header`
|
||||||
|
# NO REGISTERS SAVED
|
||||||
|
.global find_root_partition
|
||||||
|
find_root_partition:
|
||||||
|
pushl %ebp
|
||||||
|
movl %esp, %ebp
|
||||||
|
subl $16, %esp
|
||||||
|
|
||||||
|
# esp + 0: 8 byte entry array lba
|
||||||
|
movl (gpt_header + 72), %eax
|
||||||
|
movl %eax, 0(%esp)
|
||||||
|
movl (gpt_header + 76), %eax
|
||||||
|
movl %eax, 4(%esp)
|
||||||
|
# FIXME: check that bits 48-63 are zero
|
||||||
|
|
||||||
|
# esp + 8: 4 byte entries per sector
|
||||||
|
xorl %edx, %edx
|
||||||
|
movl $SECTOR_SIZE, %eax
|
||||||
|
divl (gpt_header + 84)
|
||||||
|
movl %eax, 8(%esp)
|
||||||
|
|
||||||
|
# esp + 12: 4 byte entries remaining
|
||||||
|
movl (gpt_header + 80), %eax
|
||||||
|
testl %eax, %eax
|
||||||
|
jz .find_root_partition_not_found
|
||||||
|
movl %eax, 12(%esp)
|
||||||
|
|
||||||
|
.find_root_partition_read_entry_section:
|
||||||
|
movl 0(%esp), %eax
|
||||||
|
movl 4(%esp), %ebx
|
||||||
|
movw $1, %cx
|
||||||
|
movb (root_disk_drive_number), %dl
|
||||||
|
movw $sector_buffer, %di
|
||||||
|
call read_from_disk
|
||||||
|
|
||||||
|
# ecx: min(entries per section, entries remaining)
|
||||||
|
movl 8(%esp), %ecx
|
||||||
|
cmpl 12(%esp), %ecx
|
||||||
|
jae .find_root_partition_got_entry_count
|
||||||
|
movl 12(%esp), %ecx
|
||||||
|
|
||||||
|
.find_root_partition_got_entry_count:
|
||||||
|
# update entries remaining
|
||||||
|
subl %ecx, 12(%esp)
|
||||||
|
|
||||||
|
# si: entry pointer
|
||||||
|
movw $sector_buffer, %si
|
||||||
|
|
||||||
|
.find_root_partition_loop_entries:
|
||||||
|
# temporarily save cx in dx
|
||||||
|
movw %cx, %dx
|
||||||
|
|
||||||
|
# check that entry is used
|
||||||
|
movw $16, %cx
|
||||||
|
movw $zero_guid, %di
|
||||||
|
call memcmp
|
||||||
|
test %al, %al
|
||||||
|
jnz .find_root_partition_next_entry
|
||||||
|
|
||||||
|
# compare entry guid to root guid
|
||||||
|
movw $16, %cx
|
||||||
|
addw $16, %si
|
||||||
|
movw $root_partition_guid, %di
|
||||||
|
call memcmp
|
||||||
|
subw $16, %si
|
||||||
|
|
||||||
|
testb %al, %al
|
||||||
|
jnz .find_root_partition_found
|
||||||
|
|
||||||
|
.find_root_partition_next_entry:
|
||||||
|
|
||||||
|
# restore cx
|
||||||
|
movw %dx, %cx
|
||||||
|
|
||||||
|
# entry pointer += entry size
|
||||||
|
addw (gpt_header + 84), %si
|
||||||
|
loop .find_root_partition_loop_entries
|
||||||
|
|
||||||
|
# entry not found in this sector
|
||||||
|
|
||||||
|
# increment 8 byte entry array lba
|
||||||
|
incl 0(%esp)
|
||||||
|
jno .find_root_partition_no_overflow
|
||||||
|
incl 4(%esp)
|
||||||
|
|
||||||
|
.find_root_partition_no_overflow:
|
||||||
|
# loop to read next section if entries remaining
|
||||||
|
cmpl $0, 12(%esp)
|
||||||
|
jnz .find_root_partition_read_entry_section
|
||||||
|
|
||||||
|
.find_root_partition_not_found:
|
||||||
|
movw $root_partition_not_found_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
.find_root_partition_found:
|
||||||
|
# copy entry to buffer
|
||||||
|
movw $root_partition_entry, %di
|
||||||
|
movw $128, %cx
|
||||||
|
rep movsb
|
||||||
|
|
||||||
|
movw $root_partition_found_msg, %si
|
||||||
|
call puts; call print_newline
|
||||||
|
|
||||||
|
leavel
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
# print information about root partition
|
||||||
|
.global print_root_partition_info
|
||||||
|
print_root_partition_info:
|
||||||
|
pushw %ax
|
||||||
|
pushw %bx
|
||||||
|
pushw %cx
|
||||||
|
pushw %si
|
||||||
|
|
||||||
|
movw $root_partition_info_start_msg, %si
|
||||||
|
call puts;
|
||||||
|
|
||||||
|
movw $16, %bx
|
||||||
|
movw $2, %cx
|
||||||
|
movw (root_partition_entry + 38), %ax; call print_number
|
||||||
|
movw (root_partition_entry + 36), %ax; call print_number
|
||||||
|
movw (root_partition_entry + 34), %ax; call print_number
|
||||||
|
movw (root_partition_entry + 32), %ax; call print_number
|
||||||
|
|
||||||
|
movb $'-', %al; call putc
|
||||||
|
movb $'>', %al; call putc
|
||||||
|
|
||||||
|
movw (root_partition_entry + 46), %ax; call print_number
|
||||||
|
movw (root_partition_entry + 44), %ax; call print_number
|
||||||
|
movw (root_partition_entry + 42), %ax; call print_number
|
||||||
|
movw (root_partition_entry + 40), %ax; call print_number
|
||||||
|
|
||||||
|
call print_newline
|
||||||
|
|
||||||
|
popw %si
|
||||||
|
popw %cx
|
||||||
|
popw %bx
|
||||||
|
popw %ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
# These will be patched during bootloader installation
|
||||||
|
root_disk_guid:
|
||||||
|
.ascii "root disk guid "
|
||||||
|
root_partition_guid:
|
||||||
|
.ascii "root part guid "
|
||||||
|
zero_guid:
|
||||||
|
.skip 16, 0
|
||||||
|
|
||||||
|
root_disk_found_msg:
|
||||||
|
.asciz "Root disk found!"
|
||||||
|
root_disk_not_found_msg:
|
||||||
|
.asciz "Root disk not found"
|
||||||
|
|
||||||
|
root_partition_found_msg:
|
||||||
|
.asciz "Root partition found!"
|
||||||
|
root_partition_not_found_msg:
|
||||||
|
.asciz "Root partition not found"
|
||||||
|
|
||||||
|
root_partition_info_start_msg:
|
||||||
|
.asciz "Root partition: "
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
|
||||||
|
.align SECTOR_SIZE
|
||||||
|
gpt_header:
|
||||||
|
.skip SECTOR_SIZE
|
||||||
|
gpt_entry_data:
|
||||||
|
.skip SECTOR_SIZE
|
||||||
|
sector_buffer:
|
||||||
|
.skip SECTOR_SIZE
|
||||||
|
|
||||||
|
disk_address_packet:
|
||||||
|
.skip 16
|
||||||
|
|
||||||
|
disk_drive_parameters:
|
||||||
|
.skip 0x1A
|
||||||
|
.skip 2 # padding
|
||||||
|
|
||||||
|
root_disk_drive_number:
|
||||||
|
.skip 1
|
||||||
|
.skip 3 # padding
|
||||||
|
|
||||||
|
root_partition_entry:
|
||||||
|
.skip 128
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ -z $BANAN_DISK_IMAGE_PATH ]]; then
|
||||||
|
echo "You must set the BANAN_DISK_IMAGE_PATH environment variable" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ROOT_PARTITION_INDEX=2
|
||||||
|
ROOT_PARTITION_INFO=$(fdisk -x $BANAN_DISK_IMAGE_PATH | grep "^$BANAN_DISK_IMAGE_PATH" | head -$ROOT_PARTITION_INDEX | tail -1)
|
||||||
|
ROOT_PARTITION_GUID=$(echo $ROOT_PARTITION_INFO | cut -d' ' -f6)
|
||||||
|
|
||||||
|
CURRENT_DIR=$(dirname $(realpath $0))
|
||||||
|
|
||||||
|
INSTALLER_DIR=$CURRENT_DIR/installer
|
||||||
|
INSTALLER_BUILD_DIR=$INSTALLER_DIR/build
|
||||||
|
|
||||||
|
BUILD_DIR=$CURRENT_DIR/build
|
||||||
|
|
||||||
|
if ! [ -d $INSTALLER_BUILD_DIR ]; then
|
||||||
|
mkdir -p $INSTALLER_BUILD_DIR
|
||||||
|
cd $INSTALLER_BUILD_DIR
|
||||||
|
cmake ..
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $INSTALLER_BUILD_DIR
|
||||||
|
make
|
||||||
|
|
||||||
|
mkdir -p $BUILD_DIR
|
||||||
|
|
||||||
|
echo compiling bootloader
|
||||||
|
x86_64-banan_os-as $CURRENT_DIR/boot.S -o $BUILD_DIR/boot.o
|
||||||
|
x86_64-banan_os-as $CURRENT_DIR/command_line.S -o $BUILD_DIR/command_line.o
|
||||||
|
x86_64-banan_os-as $CURRENT_DIR/disk.S -o $BUILD_DIR/disk.o
|
||||||
|
x86_64-banan_os-as $CURRENT_DIR/memory_map.S -o $BUILD_DIR/memory_map.o
|
||||||
|
x86_64-banan_os-as $CURRENT_DIR/utils.S -o $BUILD_DIR/utils.o
|
||||||
|
|
||||||
|
echo linking bootloader
|
||||||
|
x86_64-banan_os-ld -nostdlib -T $CURRENT_DIR/linker.ld $BUILD_DIR/boot.o $BUILD_DIR/command_line.o $BUILD_DIR/disk.o $BUILD_DIR/memory_map.o $BUILD_DIR/utils.o -o $BUILD_DIR/bootloader
|
||||||
|
|
||||||
|
echo installing bootloader to
|
||||||
|
$INSTALLER_BUILD_DIR/x86_64-banan_os-bootloader-installer $BUILD_DIR/bootloader $BANAN_DISK_IMAGE_PATH $ROOT_PARTITION_GUID
|
|
@ -11,4 +11,7 @@ set(SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(x86_64-banan_os-bootloader-installer ${SOURCES})
|
add_executable(x86_64-banan_os-bootloader-installer ${SOURCES})
|
||||||
target_compile_options(x86_64-banan_os-bootloader-installer PUBLIC -O2 -std=c++20)
|
target_compile_options(x86_64-banan_os-bootloader-installer PRIVATE -O2 -std=c++20)
|
||||||
|
target_compile_definitions(x86_64-banan_os-bootloader-installer PRIVATE __arch=x86_64)
|
||||||
|
target_include_directories(x86_64-banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../LibELF/include)
|
||||||
|
target_include_directories(x86_64-banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../kernel/include)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "ELF.h"
|
#include "ELF.h"
|
||||||
|
|
||||||
|
#include <LibELF/Values.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -8,6 +10,8 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using namespace LibELF;
|
||||||
|
|
||||||
ELFFile::ELFFile(std::string_view path)
|
ELFFile::ELFFile(std::string_view path)
|
||||||
: m_path(path)
|
: m_path(path)
|
||||||
{
|
{
|
||||||
|
@ -49,14 +53,14 @@ ELFFile::~ELFFile()
|
||||||
m_fd = -1;
|
m_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Elf64_Ehdr& ELFFile::elf_header() const
|
const ElfNativeFileHeader& ELFFile::elf_header() const
|
||||||
{
|
{
|
||||||
return *reinterpret_cast<Elf64_Ehdr*>(m_mmap);
|
return *reinterpret_cast<LibELF::ElfNativeFileHeader*>(m_mmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ELFFile::validate_elf_header() const
|
bool ELFFile::validate_elf_header() const
|
||||||
{
|
{
|
||||||
if (m_stat.st_size < sizeof(Elf64_Ehdr))
|
if (m_stat.st_size < sizeof(ElfNativeFileHeader))
|
||||||
{
|
{
|
||||||
std::cerr << m_path << " is too small to be a ELF executable" << std::endl;
|
std::cerr << m_path << " is too small to be a ELF executable" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
@ -75,9 +79,13 @@ bool ELFFile::validate_elf_header() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ARCH(x86_64)
|
||||||
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
|
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
|
||||||
|
#elif ARCH(i386)
|
||||||
|
if (elf_header.e_ident[EI_CLASS] != ELFCLASS32)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
std::cerr << m_path << " is not 64 bit ELF" << std::endl;
|
std::cerr << m_path << " architecture doesn't match" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,24 +107,18 @@ bool ELFFile::validate_elf_header() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elf_header.e_machine != EM_X86_64)
|
|
||||||
{
|
|
||||||
std::cerr << m_path << " is not an x86_64 ELF file" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Elf64_Shdr& ELFFile::section_header(std::size_t index) const
|
const ElfNativeSectionHeader& ELFFile::section_header(std::size_t index) const
|
||||||
{
|
{
|
||||||
const auto& elf_header = this->elf_header();
|
const auto& elf_header = this->elf_header();
|
||||||
assert(index < elf_header.e_shnum);
|
assert(index < elf_header.e_shnum);
|
||||||
const uint8_t* section_array_start = m_mmap + elf_header.e_shoff;
|
const uint8_t* section_array_start = m_mmap + elf_header.e_shoff;
|
||||||
return *reinterpret_cast<const Elf64_Shdr*>(section_array_start + index * elf_header.e_shentsize);
|
return *reinterpret_cast<const ElfNativeSectionHeader*>(section_array_start + index * elf_header.e_shentsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view ELFFile::section_name(const Elf64_Shdr& section_header) const
|
std::string_view ELFFile::section_name(const ElfNativeSectionHeader& section_header) const
|
||||||
{
|
{
|
||||||
const auto& elf_header = this->elf_header();
|
const auto& elf_header = this->elf_header();
|
||||||
assert(elf_header.e_shstrndx != SHN_UNDEF);
|
assert(elf_header.e_shstrndx != SHN_UNDEF);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibELF/Types.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
@ -7,15 +9,13 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <elf.h>
|
|
||||||
|
|
||||||
class ELFFile
|
class ELFFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ELFFile(std::string_view path);
|
ELFFile(std::string_view path);
|
||||||
~ELFFile();
|
~ELFFile();
|
||||||
|
|
||||||
const Elf64_Ehdr& elf_header() const;
|
const LibELF::ElfNativeFileHeader& elf_header() const;
|
||||||
std::optional<std::span<const uint8_t>> find_section(std::string_view name) const;
|
std::optional<std::span<const uint8_t>> find_section(std::string_view name) const;
|
||||||
|
|
||||||
bool success() const { return m_success; }
|
bool success() const { return m_success; }
|
||||||
|
@ -23,8 +23,8 @@ public:
|
||||||
std::string_view path() const { return m_path; }
|
std::string_view path() const { return m_path; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Elf64_Shdr& section_header(std::size_t index) const;
|
const LibELF::ElfNativeSectionHeader& section_header(std::size_t index) const;
|
||||||
std::string_view section_name(const Elf64_Shdr&) const;
|
std::string_view section_name(const LibELF::ElfNativeSectionHeader&) const;
|
||||||
bool validate_elf_header() const;
|
bool validate_elf_header() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -63,18 +63,18 @@ const GPTHeader& GPTFile::gpt_header() const
|
||||||
return *reinterpret_cast<GPTHeader*>(m_mmap + SECTOR_SIZE);
|
return *reinterpret_cast<GPTHeader*>(m_mmap + SECTOR_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPTFile::install_bootcode(std::span<const uint8_t> boot_code)
|
bool GPTFile::install_stage1(std::span<const uint8_t> stage1)
|
||||||
{
|
{
|
||||||
auto& mbr = this->mbr();
|
auto& mbr = this->mbr();
|
||||||
|
|
||||||
if (boot_code.size() > sizeof(mbr.boot_code))
|
if (stage1.size() > sizeof(mbr.boot_code))
|
||||||
{
|
{
|
||||||
std::cerr << m_path << ": can't fit " << boot_code.size() << " bytes of boot code in mbr (max is " << sizeof(mbr.boot_code) << ")" << std::endl;
|
std::cerr << m_path << ": can't fit " << stage1.size() << " bytes of boot code in mbr (max is " << sizeof(mbr.boot_code) << ")" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy boot code
|
// copy boot code
|
||||||
memcpy(mbr.boot_code, boot_code.data(), boot_code.size());
|
memcpy(mbr.boot_code, stage1.data(), stage1.size());
|
||||||
|
|
||||||
// setup mbr
|
// setup mbr
|
||||||
mbr.unique_mbr_disk_signature = 0xdeadbeef;
|
mbr.unique_mbr_disk_signature = 0xdeadbeef;
|
||||||
|
@ -99,29 +99,104 @@ bool GPTFile::install_bootcode(std::span<const uint8_t> boot_code)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPTFile::write_partition(std::span<const uint8_t> data, GUID type_guid)
|
bool GPTFile::install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid)
|
||||||
{
|
{
|
||||||
auto partition = find_partition(type_guid);
|
if (stage2.size() < 16)
|
||||||
|
{
|
||||||
|
std::cerr << m_path << ": contains invalid .stage2 section, too small for patches" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find GUID patch offsets
|
||||||
|
std::size_t disk_guid_offset(-1);
|
||||||
|
std::size_t part_guid_offset(-1);
|
||||||
|
for (std::size_t i = 0; i < stage2.size() - 16; i++)
|
||||||
|
{
|
||||||
|
if (memcmp(stage2.data() + i, "root disk guid ", 16) == 0)
|
||||||
|
{
|
||||||
|
if (disk_guid_offset != std::size_t(-1))
|
||||||
|
{
|
||||||
|
std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable disk guids" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
disk_guid_offset = i;
|
||||||
|
}
|
||||||
|
if (memcmp(stage2.data() + i, "root part guid ", 16) == 0)
|
||||||
|
{
|
||||||
|
if (part_guid_offset != std::size_t(-1))
|
||||||
|
{
|
||||||
|
std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable partition guids" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
part_guid_offset = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (disk_guid_offset == std::size_t(-1))
|
||||||
|
{
|
||||||
|
std::cerr << m_path << ": contains invalid .stage2 section, no patchable disk guid" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (part_guid_offset == std::size_t(-1))
|
||||||
|
{
|
||||||
|
std::cerr << m_path << ": contains invalid .stage2 section, no patchable partition guid" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto partition = find_partition_with_type(bios_boot_guid);
|
||||||
if (!partition.has_value())
|
if (!partition.has_value())
|
||||||
{
|
{
|
||||||
std::cerr << m_path << ": could not find partition with type " << type_guid << std::endl;
|
std::cerr << m_path << ": could not find partition with type " << bios_boot_guid << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE;
|
const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE;
|
||||||
|
|
||||||
if (data.size() > partition_size)
|
if (stage2.size() > partition_size)
|
||||||
{
|
{
|
||||||
std::cerr << m_path << ": can't fit " << data.size() << " bytes of data to partition of size " << partition_size << std::endl;
|
std::cerr << m_path << ": can't fit " << stage2.size() << " bytes of data to partition of size " << partition_size << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(m_mmap + partition->starting_lba * SECTOR_SIZE, data.data(), data.size());
|
uint8_t* partition_start = m_mmap + partition->starting_lba * SECTOR_SIZE;
|
||||||
|
memcpy(partition_start, stage2.data(), stage2.size());
|
||||||
|
|
||||||
|
// patch GUIDs
|
||||||
|
*reinterpret_cast<GUID*>(partition_start + disk_guid_offset) = gpt_header().disk_guid;
|
||||||
|
*reinterpret_cast<GUID*>(partition_start + part_guid_offset) = root_partition_guid;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<GPTPartitionEntry> GPTFile::find_partition(const GUID& type_guid) const
|
bool GPTFile::install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, const GUID& root_partition_guid)
|
||||||
|
{
|
||||||
|
if (!find_partition_with_guid(root_partition_guid).has_value())
|
||||||
|
{
|
||||||
|
std::cerr << m_path << ": no partition with GUID " << root_partition_guid << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!install_stage1(stage1))
|
||||||
|
return false;
|
||||||
|
if (!install_stage2(stage2, root_partition_guid))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_guid(const GUID& guid) const
|
||||||
|
{
|
||||||
|
const auto& gpt_header = this->gpt_header();
|
||||||
|
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
|
||||||
|
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
|
||||||
|
{
|
||||||
|
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
|
||||||
|
if (partition_entry.partition_guid != guid)
|
||||||
|
continue;
|
||||||
|
return partition_entry;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_type(const GUID& type_guid) const
|
||||||
{
|
{
|
||||||
const auto& gpt_header = this->gpt_header();
|
const auto& gpt_header = this->gpt_header();
|
||||||
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
|
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
|
||||||
|
|
|
@ -65,8 +65,7 @@ public:
|
||||||
GPTFile(std::string_view path);
|
GPTFile(std::string_view path);
|
||||||
~GPTFile();
|
~GPTFile();
|
||||||
|
|
||||||
bool install_bootcode(std::span<const uint8_t>);
|
bool install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, const GUID& root_partition_guid);
|
||||||
bool write_partition(std::span<const uint8_t>, GUID type_guid);
|
|
||||||
|
|
||||||
const GPTHeader& gpt_header() const;
|
const GPTHeader& gpt_header() const;
|
||||||
|
|
||||||
|
@ -77,7 +76,11 @@ public:
|
||||||
private:
|
private:
|
||||||
MBR& mbr();
|
MBR& mbr();
|
||||||
bool validate_gpt_header() const;
|
bool validate_gpt_header() const;
|
||||||
std::optional<GPTPartitionEntry> find_partition(const GUID& type_guid) const;
|
std::optional<GPTPartitionEntry> find_partition_with_guid(const GUID& guid) const;
|
||||||
|
std::optional<GPTPartitionEntry> find_partition_with_type(const GUID& type_guid) const;
|
||||||
|
|
||||||
|
bool install_stage1(std::span<const uint8_t> stage1);
|
||||||
|
bool install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string m_path;
|
const std::string m_path;
|
||||||
|
|
|
@ -3,6 +3,54 @@
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
std::optional<uint64_t> parse_hex(std::string_view hex_string)
|
||||||
|
{
|
||||||
|
uint64_t result = 0;
|
||||||
|
for (char c : hex_string)
|
||||||
|
{
|
||||||
|
if (!isxdigit(c))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
uint8_t nibble = 0;
|
||||||
|
if ('0' <= c && c <= '9')
|
||||||
|
nibble = c - '0';
|
||||||
|
else if ('a' <= c && c <= 'f')
|
||||||
|
nibble = c - 'a' + 10;
|
||||||
|
else
|
||||||
|
nibble = c - 'A' + 10;
|
||||||
|
result = (result << 4) | nibble;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<GUID> GUID::from_string(std::string_view guid_string)
|
||||||
|
{
|
||||||
|
if (guid_string.size() != 36)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (guid_string[8] != '-' || guid_string[13] != '-' || guid_string[18] != '-' || guid_string[23] != '-')
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto comp1 = parse_hex(guid_string.substr(0, 8));
|
||||||
|
auto comp2 = parse_hex(guid_string.substr(9, 4));
|
||||||
|
auto comp3 = parse_hex(guid_string.substr(14, 4));
|
||||||
|
auto comp4 = parse_hex(guid_string.substr(19, 4));
|
||||||
|
auto comp5 = parse_hex(guid_string.substr(24, 12));
|
||||||
|
|
||||||
|
if (!comp1.has_value() || !comp2.has_value() || !comp3.has_value() || !comp4.has_value() || !comp5.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
GUID result;
|
||||||
|
result.component1 = *comp1;
|
||||||
|
result.component2 = *comp2;
|
||||||
|
result.component3 = *comp3;
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
result.component45[i + 0] = *comp4 >> ((2-1) * 8 - i * 8);
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
result.component45[i + 2] = *comp5 >> ((6-1) * 8 - i * 8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool GUID::operator==(const GUID& other) const
|
bool GUID::operator==(const GUID& other) const
|
||||||
{
|
{
|
||||||
return std::memcmp(this, &other, sizeof(GUID)) == 0;
|
return std::memcmp(this, &other, sizeof(GUID)) == 0;
|
||||||
|
@ -19,7 +67,7 @@ std::ostream& operator<<(std::ostream& out, const GUID& guid)
|
||||||
out << std::setw(2);
|
out << std::setw(2);
|
||||||
for (int i = 0; i < 2; i++) out << +guid.component45[i];
|
for (int i = 0; i < 2; i++) out << +guid.component45[i];
|
||||||
out << '-';
|
out << '-';
|
||||||
for (int i = 2; i < 6; i++) out << +guid.component45[i];
|
for (int i = 2; i < 8; i++) out << +guid.component45[i];
|
||||||
|
|
||||||
out.flags(flags);
|
out.flags(flags);
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
struct GUID
|
struct GUID
|
||||||
{
|
{
|
||||||
|
static std::optional<GUID> from_string(std::string_view);
|
||||||
|
|
||||||
uint32_t component1;
|
uint32_t component1;
|
||||||
uint16_t component2;
|
uint16_t component2;
|
||||||
uint16_t component3;
|
uint16_t component3;
|
||||||
|
|
|
@ -7,9 +7,16 @@ int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
using namespace std::string_view_literals;
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
if (argc != 3)
|
if (argc != 4)
|
||||||
{
|
{
|
||||||
std::fprintf(stderr, "usage: %s BOOTLOADER DISK_IMAGE}\n", argv[0]);
|
std::fprintf(stderr, "usage: %s BOOTLOADER DISK_IMAGE ROOT_PARTITION_GUID\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto root_partition_guid = GUID::from_string(argv[3]);
|
||||||
|
if (!root_partition_guid.has_value())
|
||||||
|
{
|
||||||
|
std::cerr << "invalid guid '" << argv[3] << '\'' << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,13 +36,9 @@ int main(int argc, char** argv)
|
||||||
if (!disk_image.success())
|
if (!disk_image.success())
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!disk_image.install_bootcode(*stage1))
|
if (!disk_image.install_bootloader(*stage1, *stage2, *root_partition_guid))
|
||||||
return 1;
|
return 1;
|
||||||
std::cout << "wrote stage1 bootloader" << std::endl;
|
std::cout << "bootloader installed" << std::endl;
|
||||||
|
|
||||||
if (!disk_image.write_partition(*stage2, bios_boot_guid))
|
|
||||||
return 1;
|
|
||||||
std::cout << "wrote stage2 bootloader" << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -1,10 +1,14 @@
|
||||||
|
ENTRY(stage1_main)
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
. = 0x7C00;
|
. = 0x7C00;
|
||||||
.stage1 : { *(.stage1) }
|
.stage1 : { *(.stage1) }
|
||||||
|
|
||||||
. = ALIGN(512);
|
. = ALIGN(512);
|
||||||
|
stage2_start = .;
|
||||||
.stage2 : { *(.stage2) }
|
.stage2 : { *(.stage2) }
|
||||||
|
stage2_end = .;
|
||||||
|
|
||||||
. = ALIGN(512);
|
. = ALIGN(512);
|
||||||
.bss : { *(.bss) }
|
.bss : { *(.bss) }
|
|
@ -0,0 +1,129 @@
|
||||||
|
.code16
|
||||||
|
|
||||||
|
.section .stage2
|
||||||
|
|
||||||
|
# fills memory map data structure
|
||||||
|
# doesn't return on error
|
||||||
|
# NO REGISTERS SAVED
|
||||||
|
.global get_memory_map
|
||||||
|
get_memory_map:
|
||||||
|
movl $0, (memory_map_entry_count)
|
||||||
|
|
||||||
|
movl $0x0000E820, %eax
|
||||||
|
movl $0x534D4150, %edx
|
||||||
|
xorl %ebx, %ebx
|
||||||
|
movl $20, %ecx
|
||||||
|
movw $memory_map_entries, %di
|
||||||
|
|
||||||
|
clc
|
||||||
|
int $0x15
|
||||||
|
# If first call returs with CF set, the call failed
|
||||||
|
jc .get_memory_map_error
|
||||||
|
|
||||||
|
.get_memory_map_rest:
|
||||||
|
cmpl $0x534D4150, %eax
|
||||||
|
jne .get_memory_map_error
|
||||||
|
|
||||||
|
# FIXME: don't assume BIOS to always return 20 bytes
|
||||||
|
cmpl $20, %ecx
|
||||||
|
jne .get_memory_map_error
|
||||||
|
|
||||||
|
# increment entry count
|
||||||
|
incl (memory_map_entry_count)
|
||||||
|
|
||||||
|
# increment entry pointer
|
||||||
|
addw %cx, %di
|
||||||
|
|
||||||
|
# BIOS can indicate end of list by 0 in ebx
|
||||||
|
testl %ebx, %ebx
|
||||||
|
jz .get_memory_map_done
|
||||||
|
|
||||||
|
movl $0x0000E820, %eax
|
||||||
|
movl $0x534D4150, %edx
|
||||||
|
|
||||||
|
clc
|
||||||
|
int $0x15
|
||||||
|
# BIOS can indicate end of list by setting CF
|
||||||
|
jnc .get_memory_map_rest
|
||||||
|
|
||||||
|
.get_memory_map_done:
|
||||||
|
ret
|
||||||
|
|
||||||
|
.get_memory_map_error:
|
||||||
|
movw $memory_map_error_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
|
||||||
|
# print memory map from memory_map_entries
|
||||||
|
# NO REGISTERS SAVED
|
||||||
|
.global print_memory_map
|
||||||
|
print_memory_map:
|
||||||
|
movw $memory_map_msg, %si
|
||||||
|
call puts
|
||||||
|
call print_newline
|
||||||
|
|
||||||
|
movl (memory_map_entry_count), %edx
|
||||||
|
movw $memory_map_entries, %si
|
||||||
|
|
||||||
|
movw $16, %bx
|
||||||
|
movw $4, %cx
|
||||||
|
|
||||||
|
.loop_memory_map:
|
||||||
|
movb $' ', %al
|
||||||
|
call putc; call putc; call putc; call putc
|
||||||
|
|
||||||
|
movw 0x06(%si), %ax
|
||||||
|
call print_number
|
||||||
|
movw 0x04(%si), %ax
|
||||||
|
call print_number
|
||||||
|
movw 0x02(%si), %ax
|
||||||
|
call print_number
|
||||||
|
movw 0x00(%si), %ax
|
||||||
|
call print_number
|
||||||
|
|
||||||
|
movb $',', %al
|
||||||
|
call putc
|
||||||
|
movb $' ', %al
|
||||||
|
call putc
|
||||||
|
|
||||||
|
movw 0x0E(%si), %ax
|
||||||
|
call print_number
|
||||||
|
movw 0x0C(%si), %ax
|
||||||
|
call print_number
|
||||||
|
movw 0x0A(%si), %ax
|
||||||
|
call print_number
|
||||||
|
movw 0x08(%si), %ax
|
||||||
|
call print_number
|
||||||
|
|
||||||
|
movb $',', %al
|
||||||
|
call putc
|
||||||
|
movb $' ', %al
|
||||||
|
call putc
|
||||||
|
|
||||||
|
movw 0x12(%si), %ax
|
||||||
|
call print_number
|
||||||
|
movw 0x10(%si), %ax
|
||||||
|
call print_number
|
||||||
|
|
||||||
|
call print_newline
|
||||||
|
|
||||||
|
addw $20, %si
|
||||||
|
|
||||||
|
decl %edx
|
||||||
|
jnz .loop_memory_map
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
memory_map_msg:
|
||||||
|
.asciz "memmap:"
|
||||||
|
memory_map_error_msg:
|
||||||
|
.asciz "Failed to get memory map"
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
|
||||||
|
memory_map_entry_count:
|
||||||
|
.skip 4
|
||||||
|
# 100 entries should be enough...
|
||||||
|
memory_map_entries:
|
||||||
|
.skip 20 * 100
|
|
@ -0,0 +1,218 @@
|
||||||
|
.set SCREEN_WIDTH, 80
|
||||||
|
.set SCREEN_HEIGHT, 25
|
||||||
|
|
||||||
|
.code16
|
||||||
|
|
||||||
|
.section .stage1
|
||||||
|
|
||||||
|
# prints character to screen
|
||||||
|
# al: ascii character to print
|
||||||
|
.global putc
|
||||||
|
putc:
|
||||||
|
pushw %ax
|
||||||
|
pushw %bx
|
||||||
|
movb $0x0E, %ah
|
||||||
|
xorb %bh, %bh
|
||||||
|
int $0x10
|
||||||
|
popw %bx
|
||||||
|
popw %ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
# prints null terminated string to screen
|
||||||
|
# ds:si: string address
|
||||||
|
.global puts
|
||||||
|
puts:
|
||||||
|
pushw %si
|
||||||
|
pushw %bx
|
||||||
|
pushw %ax
|
||||||
|
|
||||||
|
movb $0x0E, %ah
|
||||||
|
xorb %bh, %bh
|
||||||
|
|
||||||
|
.puts_loop:
|
||||||
|
lodsb
|
||||||
|
|
||||||
|
test %al, %al
|
||||||
|
jz .puts_done
|
||||||
|
|
||||||
|
int $0x10
|
||||||
|
jmp .puts_loop
|
||||||
|
|
||||||
|
.puts_done:
|
||||||
|
popw %ax
|
||||||
|
popw %bx
|
||||||
|
popw %si
|
||||||
|
ret
|
||||||
|
|
||||||
|
# compares memory between addresses
|
||||||
|
# si: ptr1
|
||||||
|
# di: ptr2
|
||||||
|
# cx: bytes count
|
||||||
|
# return:
|
||||||
|
# al: 1 if equal, 0 otherwise
|
||||||
|
.global memcmp
|
||||||
|
memcmp:
|
||||||
|
# NOTE: using pusha + popa to save space
|
||||||
|
pusha
|
||||||
|
cld
|
||||||
|
repe cmpsb
|
||||||
|
popa
|
||||||
|
setzb %al
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
.section .stage2
|
||||||
|
|
||||||
|
# read a character from keyboard
|
||||||
|
# return:
|
||||||
|
# al: ascii
|
||||||
|
# ah: bios scan code
|
||||||
|
.global getc
|
||||||
|
getc:
|
||||||
|
movb $0x00, %ah
|
||||||
|
int $0x16
|
||||||
|
ret
|
||||||
|
|
||||||
|
# prints newline to screen
|
||||||
|
.global print_newline
|
||||||
|
print_newline:
|
||||||
|
pushw %ax
|
||||||
|
movb $'\r', %al
|
||||||
|
call putc
|
||||||
|
movb $'\n', %al
|
||||||
|
call putc
|
||||||
|
pop %ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
# prints backspace to screen, can go back a line
|
||||||
|
.global print_backspace
|
||||||
|
print_backspace:
|
||||||
|
pushw %ax
|
||||||
|
pushw %bx
|
||||||
|
pushw %cx
|
||||||
|
pushw %dx
|
||||||
|
|
||||||
|
# get cursor position
|
||||||
|
movb $0x03, %ah
|
||||||
|
movb $0x00, %bh
|
||||||
|
int $0x10
|
||||||
|
|
||||||
|
# don't do anyting if on first row
|
||||||
|
testb %dh, %dh
|
||||||
|
jz .print_backspace_done
|
||||||
|
|
||||||
|
# go one line up if on first column
|
||||||
|
test %dl, %dl
|
||||||
|
jz .print_backspace_go_line_up
|
||||||
|
|
||||||
|
# otherwise decrease column
|
||||||
|
decb %dl
|
||||||
|
jmp .print_backspace_do_print
|
||||||
|
|
||||||
|
.print_backspace_go_line_up:
|
||||||
|
# decrease row and set column to the last one
|
||||||
|
decb %dh
|
||||||
|
movb $(SCREEN_WIDTH - 1), %dl
|
||||||
|
|
||||||
|
.print_backspace_do_print:
|
||||||
|
# set cursor position
|
||||||
|
movb $0x02, %ah
|
||||||
|
int $0x10
|
||||||
|
|
||||||
|
# print 'empty' character (space)
|
||||||
|
mov $' ', %al
|
||||||
|
call putc
|
||||||
|
|
||||||
|
# set cursor position
|
||||||
|
movb $0x02, %ah
|
||||||
|
int $0x10
|
||||||
|
|
||||||
|
.print_backspace_done:
|
||||||
|
popw %dx
|
||||||
|
popw %cx
|
||||||
|
popw %bx
|
||||||
|
popw %ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
# print number to screen
|
||||||
|
# ax: number to print
|
||||||
|
# bx: number base
|
||||||
|
# cx: min width (zero pads if shorter)
|
||||||
|
.global print_number
|
||||||
|
print_number:
|
||||||
|
pusha
|
||||||
|
pushl %ebp
|
||||||
|
movl %esp, %ebp
|
||||||
|
|
||||||
|
# save min width
|
||||||
|
subl $4, %esp
|
||||||
|
movw %cx, (%esp)
|
||||||
|
|
||||||
|
movw $print_number_buffer, %si
|
||||||
|
xorw %cx, %cx
|
||||||
|
|
||||||
|
.print_number_fill_loop:
|
||||||
|
# fill buffer with all remainders ax % bx
|
||||||
|
xorw %dx, %dx
|
||||||
|
divw %bx
|
||||||
|
movb %dl, (%si)
|
||||||
|
incw %si
|
||||||
|
incw %cx
|
||||||
|
testw %ax, %ax
|
||||||
|
jnz .print_number_fill_loop
|
||||||
|
|
||||||
|
# check if zero pad is required
|
||||||
|
cmpw (%esp), %cx
|
||||||
|
jae .print_number_print_loop
|
||||||
|
|
||||||
|
# dx: saved number count
|
||||||
|
# cx: zero pad count
|
||||||
|
movw %cx, %dx
|
||||||
|
movw (%esp), %cx
|
||||||
|
subw %dx, %cx
|
||||||
|
movb $'0', %al
|
||||||
|
|
||||||
|
.print_number_pad_zeroes:
|
||||||
|
call putc
|
||||||
|
loop .print_number_pad_zeroes
|
||||||
|
|
||||||
|
# restore number count
|
||||||
|
movw %dx, %cx
|
||||||
|
|
||||||
|
.print_number_print_loop:
|
||||||
|
decw %si
|
||||||
|
movb (%si), %al
|
||||||
|
cmpb $10, %al
|
||||||
|
jae .print_number_hex
|
||||||
|
addb $'0', %al
|
||||||
|
jmp .print_number_do_print
|
||||||
|
.print_number_hex:
|
||||||
|
addb $('a' - 10), %al
|
||||||
|
.print_number_do_print:
|
||||||
|
call putc
|
||||||
|
loop .print_number_print_loop
|
||||||
|
|
||||||
|
leavel
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
|
# test if character is printable ascii
|
||||||
|
# al: character to test
|
||||||
|
# return:
|
||||||
|
# al: 1 if is printable, 0 otherwise
|
||||||
|
.global isprint
|
||||||
|
isprint:
|
||||||
|
subb $0x20, %al
|
||||||
|
cmpb $(0x7E - 0x20), %al
|
||||||
|
ja .isprint_not_printable
|
||||||
|
movb $1, %al
|
||||||
|
ret
|
||||||
|
.isprint_not_printable:
|
||||||
|
movb $0, %al
|
||||||
|
ret
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
|
||||||
|
# enough for base 2 printing
|
||||||
|
print_number_buffer:
|
||||||
|
.skip 16
|
|
@ -92,8 +92,17 @@ case $1 in
|
||||||
check-fs)
|
check-fs)
|
||||||
$BANAN_SCRIPT_DIR/check-fs.sh
|
$BANAN_SCRIPT_DIR/check-fs.sh
|
||||||
;;
|
;;
|
||||||
|
clean)
|
||||||
|
build_target clean
|
||||||
|
rm -f $FAKEROOT_FILE
|
||||||
|
rm -rf $BANAN_SYSROOT
|
||||||
|
;;
|
||||||
|
bootloader)
|
||||||
|
create_image
|
||||||
|
$BANAN_ROOT_DIR/bootloader/install.sh
|
||||||
|
$BANAN_SCRIPT_DIR/qemu.sh -serial stdio $QEMU_ACCEL
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
build_target $1
|
build_target $1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue