haiku/src/apps/bootmanager/bootman.S

741 lines
15 KiB
ArmAsm

;
; Copyright 2007, Dengg David, david-d@gmx.at. All rights reserved.
; Copyright 2008, Michael Pfeiffer, laplace@users.sourceforge.net. All rights reserved.
; Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
; Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
; Distributed under the terms of the MIT License.
%assign USE_TEST_MENU 0
%assign BOOT_BLOCK_START_ADDRESS 0x7c00
%assign MBR_SIGNATURE 0xAA55
; BIOS calls
%assign BIOS_VIDEO_SERVICES 0x10
%assign BIOS_DISK_SERVICES 0x13
%assign BIOS_KEYBOARD_SERVICES 0x16
%assign BIOS_REBOOT 0x19 ; dl - boot drive number
%assign BIOS_TIME_SERVICES 0x1A
; video services
%assign SET_VIDEO_MODE 0x00 ; al - mode
%assign SET_CURSOR_SHAPE 0x01 ; ch - starting scan line (5 bits)
; cl - ending scan line (5 bits)
%assign SET_CURSOR 0x02 ; dl - column
; dh - row
; bh - page
%assign GET_CURSOR 0x03 ; bh - page
; -> dl - column
; dh - row
; Cursor shape:
; ch - starting scan line
; cl - ending scan line
%assign SCROLL_UP 0x06 ; al - lines (0: clear screen)
; bh - attribute
; ch - upper line
; cl - left column
; dh - lower line
; dl - right column
%assign WRITE_CHAR 0x09 ; al - char
; bh - page
; bl - attribute
; cx - count
;%assign WRITE_CHAR 0x0e ; al - char
; bh - page
; bl - foreground color (graphics mode only)
; disk services
%assign READ_DISK_SECTORS 0x02 ; dl - drive
; es:bx - buffer
; dh - head (0 - 15)
; ch - track 7:0 (0 - 1023)
; cl - track 9:8,
; sector (1 - 17)
; al - sector count
; -> al - sectors read
%assign READ_DRIVE_PARAMETERS 0x08 ; dl - drive
; -> cl - max cylinder 9:8
; - sectors per track
; ch - max cylinder 7:0
; dh - max head
; dl - number of drives (?)
%assign CHECK_DISK_EXTENSIONS_PRESENT 0x41 ; bx - 0x55aa
; dl - drive
; -> success: carry clear
; ah - extension version
; bx - 0xaa55
; cx - support bit mask
; -> error: carry set
%assign EXTENDED_READ 0x42 ; dl - drive
; ds:si - address packet
; -> success: carry clear
; -> error: carry set
%assign FIXED_DISK_SUPPORT 0x1 ; flag indicating fixed disk
; extension command subset
; keyboard services
%assign READ_CHAR 0x00 ; -> al - ASCII char
; ah - scan code
%assign PROBE_CHAR 0x01 ; -> zf = 0
; al - ASCII char
; ah - scan code
%assign GET_MODIFIER_KEYS 0x02 ;-> al - modifier key bitmask
; timer services
%assign READ_CLOCK 0x00 ; -> cx - high word
; dx - low word
; one tick = 1/18.2s
%assign TICKS_PER_SECOND 19
; video modes
%assign GRAPHIC_MODE_80x25 0x12 ; 640 x 480 graphic mode
%assign TEXT_COLUMNS 80 ; Number of columns
%assign TEXT_ROWS 25 ; Number of rows
; Colors
%assign BLACK 0
%assign BLUE 1
%assign GREEN 2
%assign CYAN 3
%assign RED 4
%assign MAGENTA 5
%assign BROWN 6
%assign LIGHT_GRAY 7
%assign DARK_GRAY 8
%assign LIGHT_BLUE 9
%assign LIGHT_GREEN 10
%assign LIGHT_CYAN 11
%assign LIGHT_RED 12
%assign LIGHT_MAGENTA 13
%assign YELLOW 14
%assign WHITE 15
%assign BRIGHT_COLOR_MASK 8
; Characters
%assign TRIANGLE_TO_RIGHT 16
%assign TRIANGLE_TO_LEFT 17
; Key codes
%assign KEY_DOWN 0x50
%assign KEY_UP 0x48
%assign KEY_PAGE_DOWN 0x51
%assign KEY_PAGE_UP 0x49
%assign KEY_HOME 0x47
%assign KEY_END 0x4f
%assign KEY_RETURN 0x1C
; Modifier key bitmasks
%assign MODIFIER_RIGHT_SHIFT_KEY 0x01
%assign MODIFIER_LEFT_SHIFT_KEY 0x02
%assign MODIFIER_CONTROL_KEY 0x04
%assign MODIFIER_ALT_KEY 0x08
%assign MODIFIER_SCROLL_LOCK_KEY 0x10
%assign MODIFIER_NUM_LOCK_KEY 0x20
%assign MODIFIER_CAPS_LOCK_KEY 0x40
%assign MODIFIER_INSERT_KEY 0x80
; String constants with their length
%define TITLE 'Haiku Boot Manager'
%strlen TITLE_LENGTH TITLE
%define SELECT_OS_MESSAGE 'Select an OS from the menu'
%strlen SELECT_OS_MESSAGE_LENGTH SELECT_OS_MESSAGE
; 16 bit code
SECTION .text
BITS 16
; nicer way to get the size of a structure
%define sizeof(s) s %+ _size
; using a structure in a another structure definition
%macro nstruc 1-2 1
resb sizeof(%1) * %2
%endmacro
; Variables on stack
struc Locals
selection resw 1
firstLine resb 2 ; low byte used only
timeoutTicks resd 1
cursorX resb 1
cursorY resb 1
cursorShape resw 1
biosDrive resb 1
endstruc
cursorPosition equ cursorX
%macro DEBUG_PAUSE 0
push ax
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES
pop ax
%endmacro
%macro CLEAR_SCREEN 0
mov ah, SCROLL_UP
xor al, al
mov bh, WHITE
xor cx, cx
mov dx, (TEXT_ROWS-1) * 0x100 + (TEXT_COLUMNS-1)
int BIOS_VIDEO_SERVICES
%endmacro
; Prints a null terminated string
; bl ... color
; si ... offset to string
%macro PRINT_STRING 0
push ax
push bx
push cx
push dx
xor bh, bh ; write on page 0
jmp .loop_condition
.loop:
mov dx, [bp + cursorPosition]
mov ah, SET_CURSOR
int BIOS_VIDEO_SERVICES
inc byte [bp + cursorX]
mov cx, 1
mov ah, WRITE_CHAR
int BIOS_VIDEO_SERVICES
.loop_condition:
lodsb
cmp al, 0
jnz .loop
pop dx
pop cx
pop bx
pop ax
ret
%endmacro
; 64 bit value
struc quadword
.lower resd 1
.upper resd 1
endstruc
; address packet as required by the EXTENDED_READ BIOS call
struc AddressPacket
.packet_size resb 1
.reserved1 resb 1
.block_count resb 1
.reserved2 resb 1
.buffer resd 1
.offset nstruc quadword
endstruc
struc BootLoaderAddress
.device resb 1 ; hard drive number
.offset nstruc quadword ; LBA of start start sector
endstruc
; use code available in stage 1
%define printstr printStringStage1
stage1:
mov ax, 0x07c0 ; BOOT_BLOCK_START_ADDRESS / 16
mov ds, ax ; Setup segment registers
mov es, ax
mov ss, ax
mov sp, 0xFFFF - sizeof(Locals) ; Make stack empty
mov bp, sp
mov [bp + biosDrive], dl ; Store boot drive
cld ; String operations increment index
; registers
CLEAR_SCREEN
call hideCursor
mov bh, 0 ; Text output on page 0
; Print title centered at row 2
mov dx, 1 * 0x100 + (40 - TITLE_LENGTH / 2)
mov [bp + cursorPosition], dx
mov si, kTitle
mov bl, WHITE
call printstr
; Print message centered at second last row
mov dx, (TEXT_ROWS-2) * 0x100 + (40 - SELECT_OS_MESSAGE_LENGTH / 2)
mov [bp + cursorPosition], dx
mov bl, LIGHT_GRAY
mov si, kSelectOSMessage
call printstr
; Chain load rest of boot loader
mov ah, EXTENDED_READ ; Load 3 more sectors
mov dl, [bp + biosDrive]
mov si, nextStageDAP
int BIOS_DISK_SERVICES
jc .error ; I/O error
jmp stage2 ; Continue in loaded stage 2
.error:
call showCursor
mov si, kError
mov bl, RED
call printstr
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES
mov dl, [bp + biosDrive]
int BIOS_REBOOT
printStringStage1:
PRINT_STRING
hideCursor:
mov ah, GET_CURSOR
int BIOS_VIDEO_SERVICES
mov [bp + cursorShape], cx
mov ah, SET_CURSOR_SHAPE
mov cx, 0x2000
int BIOS_VIDEO_SERVICES
ret
showCursor:
mov cx, [bp + cursorShape]
mov ah, SET_CURSOR_SHAPE
int BIOS_VIDEO_SERVICES
ret
nextStageDAP:
istruc AddressPacket
at AddressPacket.packet_size, db 0x10
at AddressPacket.block_count, db 0x03
at AddressPacket.buffer, dw 0x0200, 0x07c0
at AddressPacket.offset, dw 1
iend
kTitle:
db TITLE, 0x00
kSelectOSMessage:
db SELECT_OS_MESSAGE, 0x00
kError:
db 'Error loading sectors!', 0x00
kStage1UnusedSpace equ 440 - ($-$$)
; Fill the missing space to reach byte 440
times kStage1UnusedSpace db 'B'
kDiskSignature:
dw 0, 0
kReserved:
dw 0
kPartitionTable:
times 64 db 0
kMBRSignature:
; Magic marker "AA55" (to identify a valid boot record)
dw MBR_SIGNATURE
; ======================================================================
; ======================= SECOND SECTOR ================================
; ======================================================================
; Use code available in stage 2
%define printstr printStringStage2
%assign TIMEOUT_OFF 0xffff
stage2:
mov ax, [defaultItem] ; Select default item
mov [bp + selection], ax
mov ax, TICKS_PER_SECOND ; Calculate timeout ticks
mul word [timeout]
mov bx, dx
push ax
mov ah, READ_CLOCK
int BIOS_TIME_SERVICES
pop ax ; Add current ticks
add ax, dx
adc bx, cx
mov [bp + timeoutTicks], ax
mov [bp + timeoutTicks + 2], bx
mov al, [listItemCount] ; Calculate start row for menu
shr al, 1
mov bl, TEXT_ROWS / 2
sub bl, al ; y = TEXT_ROWS / 2 - number of items / 2
mov [bp + firstLine], bl
mov ah, GET_MODIFIER_KEYS ; Disable timeout if ALT key is pressed
int BIOS_KEYBOARD_SERVICES
and al, MODIFIER_ALT_KEY
jz showMenu
mov word [timeout], TIMEOUT_OFF
showMenu:
call printMenu
cmp word [timeout], TIMEOUT_OFF
je inputLoop
timeoutLoop:
mov ah, PROBE_CHAR
int BIOS_KEYBOARD_SERVICES
jnz inputLoop ; cancel timeout if key is pressed
call isTimeoutReached
jnc timeoutLoop
jmp bootSelectedPartition
isTimeoutReached:
mov ah, READ_CLOCK
int BIOS_TIME_SERVICES
cmp cx, [bp + timeoutTicks + 2]
jb .returnFalse
ja .returnTrue
cmp dx, [bp + timeoutTicks]
ja .returnTrue
.returnFalse:
clc
ret
.returnTrue:
stc
ret
; ================== Wait for a key and do something with it ==================
mainLoop:
call printMenu
inputLoop:
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES ; AL = ASCII Code, AH = Scancode
cmp ah, KEY_DOWN
je selectNextPartition
cmp ah, KEY_PAGE_DOWN
je selectLastPartition
cmp ah, KEY_END
je selectLastPartition
cmp ah, KEY_UP
je selectPreviousPartition
cmp ah, KEY_PAGE_UP
je selectFirstPartition
cmp ah, KEY_HOME
je selectFirstPartition
cmp ah, KEY_RETURN
jne inputLoop
jmp bootSelectedPartition
selectNextPartition:
mov ax, [bp + selection]
inc ax
cmp ax, [listItemCount]
jne .done ; At end of list?
xor ax, ax ; Then jump to first entry
.done:
mov [bp + selection], ax
jmp mainLoop
selectLastPartition:
mov ax, [listItemCount]
dec ax
mov [bp + selection], ax
jmp mainLoop
selectPreviousPartition:
mov ax, [bp + selection]
or ax, ax
jnz .done ; At top of list?
mov ax, [listItemCount] ; Then jump to last entry
.done:
dec ax
mov [bp + selection], ax
jmp mainLoop
selectFirstPartition:
xor ax, ax
mov [bp + selection], ax
jmp mainLoop
; ======================= Print the OS list ============================
printMenu:
mov al, [bp + firstLine]
mov [bp + cursorY], al
mov si, list ; Start at top of list
xor cx, cx ; The index of the current item
.loop:
lodsb ; String length incl. 0-terminator
add al, 3 ; center menu item
shr al, 1 ; x = TEXT_COLUMNS / 2 - length / 2
mov dl, TEXT_COLUMNS / 2
sub dl, al
mov [bp + cursorX], dl
mov al, TRIANGLE_TO_RIGHT
call updateMarker
inc byte [bp + cursorX]
mov di, cx
and di, 3
mov bl, [kColorTable + di] ; Text color
cmp cx, [bp + selection]
jne .print ; Selected item reached?
xor bl, BRIGHT_COLOR_MASK ; Highlight it
.print:
call printstr
add si, sizeof(BootLoaderAddress)
add byte [bp + cursorX], 1
mov al, TRIANGLE_TO_LEFT
call updateMarker
inc byte [bp + cursorY]
inc cx
cmp cx, [listItemCount]
jne .loop
ret
updateMarker:
cmp cx, [bp + selection]
je .print
mov al, ' ' ; Clear marker
.print:
mov bl, WHITE
jmp printChar ; return from subroutine
; ========================== Chainload ==========================
bootSelectedPartition:
call showCursor
call getSelectedBootLoaderAddress
lodsb ; Set boot drive
mov dl, al
mov di, bootSectorDAP+AddressPacket.offset ; Copy start sector
mov cx, 4 ; It is stored in a quad word
.copy_start_sector:
lodsw
stosw
loop .copy_start_sector
mov ah, EXTENDED_READ ; Now read start sector from HD
mov si, bootSectorDAP
int BIOS_DISK_SERVICES
mov si, kReadError
jc printAndHalt ; Failed to read sector
mov ax, [kMBRSignature]
cmp ax, MBR_SIGNATURE
mov si, kNoBootablePartitionError
jne printAndHalt ; Missing signature
CLEAR_SCREEN
; Print "Loading <name>" at top of screen
mov word [bp + cursorPosition], 0
mov si, kLoadingMessage
mov bl, LIGHT_GRAY
call printstr
inc byte [bp + cursorX]
call getSelectedMenuItem
inc si ; Skip string length byte
call printstr
mov dx, 0x100
xor bh, bh
mov ah, SET_CURSOR
int BIOS_VIDEO_SERVICES
call getSelectedBootLoaderAddress
mov dl, [si] ; drive number in dl
jmp $$ ; Start loaded boot loader
printAndHalt:
mov dx, (TEXT_ROWS-4) * 0x100 + (TEXT_COLUMNS / 3)
mov [bp + cursorPosition], dx
mov bx, 0x0F ; Page number and foreground color
call printstr
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES
mov dl, [bp + biosDrive]
int BIOS_REBOOT
; Output:
; si address of selected menu item
; Trashes:
; ax, cx
getSelectedMenuItem:
mov si, list ; Search address of start sector
; of the selected item.
mov cx, [bp + selection]
inc cx ; Number of required iterations
xor ah, ah ; The high-byte of the string length
; see loop body
jmp .entry
.loop:
lodsb ; Length of menu item name
add si, ax ; Skip name to BootLoaderAddess
add si, sizeof(BootLoaderAddress)
.entry:
loop .loop
ret
getSelectedBootLoaderAddress:
call getSelectedMenuItem
lodsb
xor ah, ah
add si, ax ; Skip name
mov dl, [si]
test dl, 0 ; if drive is 0, use boot drive
jz .takeOverBootDrive
ret
.takeOverBootDrive:
mov dl, [bp + biosDrive]
mov [si], dl
ret
printStringStage2:
PRINT_STRING
; al ... ASCII character
; bl ... color
printChar:
push ax
push bx
push cx
push dx
xor bh, bh ; Write on page 0
mov dx, [bp + cursorPosition]
mov ah, SET_CURSOR
int BIOS_VIDEO_SERVICES
inc byte [bp + cursorX]
mov cx, 1
mov ah, WRITE_CHAR
int BIOS_VIDEO_SERVICES
pop dx
pop cx
pop bx
pop ax
ret
; ================================ DATA ===========================
bootSectorDAP:
istruc AddressPacket
at AddressPacket.packet_size, db 0x10
at AddressPacket.block_count, db 0x01
at AddressPacket.buffer, dw 0x0000, 0x07c0
iend
kColorTable:
db BLUE, RED, GREEN, CYAN
kReadError:
db 'Error loading sectors', 0x00
kNoBootablePartitionError:
db 'Not a bootable partition', 0x00
kLoadingMessage:
db 'Loading', 0x00
listItemCount:
defaultItem equ listItemCount + 2
timeout equ defaultItem + 2
list equ timeout + 2
; dw number of entries
; dw the default entry
; dw the timeout (-1 for none)
; entry:
; db size of partition name 0-terminated string
; db 0-terminated string with partition name
; db hard drive number
; quadword start sector
%if USE_TEST_MENU
dw 0x06
dw 2
dw 5
db 0x06
db 'HAIKU', 0
db 0x80
dw 1, 0, 0, 0
db 0x08
db 'FreeBSD', 0
db 0x80
dw 0x003F, 0, 0, 0
db 0x04
db 'DOS', 0
db 0x80
dw 0x003E, 0, 0, 0
db 0x06
db 'LINUX', 0
db 0x80
dw 0x003F, 0, 0, 0
db 0x08
db 'BeOS R5', 0
db 0x80
dw 0x003F, 0, 0, 0
db 0x07
db 'OpenBSD', 0
db 0x80
dw 0xAAAA, 0, 0, 0
dw kStage1UnusedSpace
%endif