[asm/C] 2x bootloader, jak uruchomić program, który...
Pytałem się koleżkę o zgodę na umieszczenie jego kodu tutaj, odpowiedzi nie dostałem, mimo że jest 24h na dobę... online ;] Trzeba się dzielić, może się komuś przydać.
Jeszcze do niedawna miał konto na Sourceforge (był tam ten kod), to raczej nie powinien mieć pretensji o zamieszczenie tutaj ;p
plik: bootloader.asm
;*************************************************************************
;the ultimate boot-strap loader
;to load a file from a DOS FAT12 floppy as the OS
;*************************************************************************
BITS 16
CPU 8086
ORG 0
jmp short START
nop
OEM_ID db "JON OS "
BytesPerSector dw 0x0200
SectorsPerCluster db 0x01
ReservedSectors dw 0x0001
TotalFATs db 0x02
MaxRootEntries dw 0x00E0
TotalSectorsSmall dw 0x0B40
MediaDescriptor db 0xF0
SectorsPerFAT dw 0x0009
SectorsPerTrack dw 0x0012
NumHeads dw 0x0002
HiddenSectors dd 0x00000000
TotalSectorsLarge dd 0x00000000
DriveNumber db 0x00
Flags db 0x00
Signature db 0x29
VolumeID dd 0xFFFFFFFF
VolumeLabel db "JON OS "
SystemID db "FAT12 "
START:
; code located at 0000:7C00, adjust segment registers
cli
mov ax, 7C0h
mov ds, ax
mov es, ax
; create stack
xor ax, ax
mov ss, ax
mov sp, 0FFFFh
sti
LOAD_ROOT:
; compute size of root directory and store in cx
xor cx, cx
xor dx, dx
mov ax, 20h ; 32 byte directory entry
mul WORD [MaxRootEntries] ; total size of directory
div WORD [BytesPerSector] ; sectors used by directory
xchg ax, cx
; compute location of root directory and store in ax
mov al, BYTE [TotalFATs] ; number of FATs
mul WORD [SectorsPerFAT] ; sectors used by FATs
add ax, WORD [ReservedSectors] ; adjust for bootsector
mov WORD [datasector], ax ; base of root directory
add WORD [datasector], cx
; read root directory into memory (7C00:0200)
mov bx, 200h ; copy root dir above bootcode
call ReadSectors
; browse root directory for binary image
mov cx, WORD [MaxRootEntries] ; load loop counter
mov di, 200h ; locate first root entry
.LOOP:
push cx
mov cx, 0Bh ; eleven character name
mov si, ImageName ; image name to find
push di
rep cmpsb ; test for entry match
pop di
je LOAD_FAT
pop cx
add di, 20h ; queue next directory entry
loop .LOOP
jmp FAILURE
LOAD_FAT:
; save starting cluster of boot image
mov dx, WORD [di + 0x001A]
mov WORD [cluster], dx ; files first cluster
; compute size of FAT and store in cx
xor ax, ax
mov al, BYTE [TotalFATs] ; number of FATs
mul WORD [SectorsPerFAT] ; sectors used by FATs
mov cx, ax
; compute location of FAT and store in ax
mov ax, WORD [ReservedSectors] ; adjust for bootsector
; read FAT into memory (7C00:0200)
mov bx, 200h ; copy FAT above bootcode
call ReadSectors
; read image file into memory (0000:0600)
xor ax, ax ; destination of image CS
mov es, ax
mov bx, 0x0600 ; destination for image IP
push bx
LOAD_IMAGE:
mov ax, WORD [cluster] ; cluster to read
pop bx ; buffer to read into
call ClusterLBA ; convert cluster to LBA
xor cx, cx
mov cl, BYTE [SectorsPerCluster] ; sectors to read
call ReadSectors
push bx
; compute next cluster
mov ax, WORD [cluster] ; identify current cluster
mov cx, ax ; copy current cluster
mov dx, ax ; copy current cluster
shr dx, 1
;divide by two
add cx, dx ; sum for (3/2)
mov bx, 200h ; location of FAT in memory
add bx, cx ; index into FAT
mov dx, WORD [bx] ; read two bytes from FAT
test ax, 1
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
and dx, 0FFFh ; take low twelve bits
jmp .DONE
.ODD_CLUSTER:
TIMES 4 shr dx, 1 ; take high twelve bits
; times for 8086 compablility
; as it can't SHR by imm8
.DONE:
mov WORD [cluster], dx ; store new cluster
cmp dx, 0x0FF0 ; test for end of file
jb LOAD_IMAGE
DONE:
;mov si,msgExecute
;call DisplayMessage
jmp 0000h:0600h
FAILURE:
mov si, msgFailure
call DisplayMessage
xor ah,ah
int 16h ; await keypress
int 19h ; warm boot computer
;*************************************************************************
; PROCEDURE DisplayMessage
; display ASCIIZ string at ds:si via BIOS
;*************************************************************************
DisplayMessage:
push ax
push bx
disp_msg:
lodsb ; load next character
test al, al ; test for NUL character
jz .DONE
mov ah, 0Eh ; BIOS teletype
xor bx,bx ; attributes
int 10h ; invoke BIOS
jmp disp_msg
.DONE:
pop bx
pop ax
ret
;*************************************************************************
; PROCEDURE ReadSectors
; reads cx sectors from disk starting at ax into
;memory location es:bx
;*************************************************************************
ReadSectors:
rs_MAIN:
mov di, 10 ; five retries for error
rs_SECTORLOOP:
push ax
push bx
push cx
call LBACHS
mov ah, 2 ; BIOS read sector
mov al, 1 ; read one sector
mov ch, BYTE [absoluteTrack] ; track
mov cl, BYTE [absoluteSector] ; sector
mov dh, BYTE [absoluteHead] ; head
mov dl, BYTE [DriveNumber] ; drive
int 13h ; invoke BIOS
jnc rs_SUCCESS ; test for read error
xor ax, ax ; BIOS reset disk
int 13h ; invoke BIOS
dec di ; decrement error counter
pop cx
pop bx
pop ax
jnz rs_SECTORLOOP ; attempt to read again
int 18h
rs_SUCCESS:
pop cx
pop bx
pop ax
mov si,msgProgress
call DisplayMessage
add bx, WORD [BytesPerSector] ; queue next buffer
inc ax ; queue next sector
loop rs_MAIN ; read next sector
ret
;*************************************************************************
; PROCEDURE ClusterLBA
; convert FAT cluster into LBA addressing scheme
; LBA = (cluster - 2) * sectors per cluster
;*************************************************************************
ClusterLBA:
sub ax, 2 ; zero base cluster number
xor cx, cx
mov cl, BYTE [SectorsPerCluster] ; convert byte to word
mul cx
add ax, WORD [datasector] ; base data sector
ret
;*************************************************************************
; PROCEDURE LBACHS
; convert ax LBA addressing scheme to CHS addressing scheme
; absolute sector = (logical sector / sectors per track) + 1
; absolute head = (logical sector / sectors per track) MOD number of heads
; absolute track = logical sector / (sectors per track * number of heads)
;*************************************************************************
LBACHS:
xor dx, dx ; prepare dx:ax for operation
div WORD [SectorsPerTrack] ; calculate
inc dl ; adjust for sector 0
mov BYTE [absoluteSector], dl
xor dx, dx ; prepare dx:ax for operation
div WORD [NumHeads] ; calculate
mov BYTE [absoluteHead], dl
mov BYTE [absoluteTrack], al
ret
absoluteSector db 0x00
absoluteHead db 0x00
absoluteTrack db 0x00
datasector dw 0x0000
cluster dw 0x0000
ImageName db "KERNEL BIN"
msgCRLF db 0Dh, 0Ah, 0
msgFailure db 0Dh, 0Ah, "Disk boot faliture. Press any key to reboot", 0
msgProgress db 177,0
msgExecute db 0Dh,0Ah, "Booting JonOS!",0
TIMES 510-($-$$) DB 0
DW 0xAA55
;*************************************************************************
Ten bootloader ma uruchomić kernel.bin, nie mam do niego kodu źródłowego a tylko skompilowany plik .bin.
Drugi bootloader + skompilowany program w czystym C za pomocą GCC.Tutaj mam pytanie X, które zadam na koniec. Całość odbywa się pod Ubuntu, u mnie 12.10. Odpaliłem na win7 pod Virtual-Box. Jeśli ktoś tak jak ja nie instalował wcześniej na swoim Ubulawu programu qemu, niech go zainstaluje, jest w repo (Qemulauncher, Qemulator).
Instrukcja krok po kroku, jak skompilować i uruchomić mini kernel. Całość bezczelnie zerżnięta z http://www.youtube.com/watch?v=Qit1civWf7Y
Tworzymy katalog, ja mam na pulpicie. W nim po kolei tworzymy wszystkie poniższe pliki.
plik: loader.asm (loader, nie bootloader)
global loader
extern dmain
MODULEALIGN equ 1<<0
MEMINFO equ 1<<1
FLAGS equ MODULEALIGN | MEMINFO
MAGIC equ 0x2BADB002
CHECKSUM equ -(MAGIC + FLAGS)
section .text
align 4
MultiBootHeader:
dd MAGIC
dd FLAGS
dd CHECKSUM
STACKSIZE equ 0x4000
loader:
mov esp, stack+STACKSIZE
push eax
push ebx
call dmain
cli
hang:
hlt
jmp hang
section .bss
align 4
stack:
resb STACKSIZE
plik kernel.c
void print( char *, int );
void dmain( void * mbd, unsigned int magic )
{
if( magic != 0x2BADB002 )
{
print( "Invaild execution", 0x07 );
}
else
{
print( "Booting...", 0x07 );
}
}
void print( char * message, int color )
{
char * mem =( char * )( 0xb8000 );
while( * message != 0 )
{
* mem = * message;
mem++;
message++;
* mem =( char * ) color;
mem++;
}
}
Oba pliki trzeba będzie zlinkować. Ale to zaraz, najpierw plik linkera.
plik: linker.ld
http://speedy.sh/f8hvt/linker.ld
ENTRY (loader)
SECTIONS {
. =0x00100000;
.text : {
*(.text)
}
.rodata ALIGN (0x1000) : {
*(.data)
}
.bss : {
sbss = .;
*(COMMON)
*(.bss)
ebss = .;
}
}
>> Uwaga na formatowanie! Z tego co zauważyłem, linker jest czuły na spacje, należy używać tabulatora. W razie błędów po ctrl+c -> ctrl-v należy obczaić filma tak od 3/4..
Tworzymy teraz plik-skrypt. Będzie on wykonywał:
* kompilację kernel.c do postaci pliku obiektowego,
* kompilację loader.asm do pliku obiektowego,
* linkowanie plików obiektowych z użyciem pliku linkera linker.ld (wynik: kernel.bin)
* odpalenie zlinkowanego kernel.bin poprzez qemu
plik: compile.so
#!/bin/bash
gcc -o kernel.o -c kernel.c -Wall -Wextra -nostartfiles -nodefaultlibs
nasm -f elf -o loader.o loader.asm
ld -m elf_i386 -T linker.ld -o kernel.bin loader.o kernel.o
qemu-system-i386 -kernel kernel.bin
Moje Ubulawu nie miało qemu, a że nie wiedziałem które zainstalować z repo, zainstalowałem: Qemu Launcher oraz Qemulator ;p
Kiedy już zainstalowane, nadajemy w terminalu prawa wykonywalności skryptowi: chmod +x /sciezka/compile.so
Odpalamy skrypt.
#!/bin/bash
gcc -o kernel.o -c kernel.c -Wall -Wextra -nostartfiles -nodefaultlibs
nasm -f elf -o loader.o loader.asm
ld -m elf_i386 -T linker.ld -o kernel.bin loader.o kernel.o
Jeśli wszystko poszło dobrze, powinien uruchomić się nasz system operacyjny, powinien pojawić się komunikat: Booting...
Pytania.Jak z poziomu kernel.c odpalić powiedzmy dosowy program napisany w BorlandC++3.1 16-bit? W skrypcie do kompilacji gcc wywoływany jest dla pliku kernel.c z parametrami uniemożliwiającymi używania biblioteki standardowej. Przyznam szczerze, że przychodzi mi do głowy w takim wypadku Borland-C++, ale tutaj znów nie idzie go zmusić do generowania pliku .o, jeśli już to tylko .exe.
Jak przejśc w tryb procesora 32-bit (protected)? Nie żebym chciał linuxdowsa pisać, pytam z ciekawości. Wszelkie sugestie mile widziane :)
Co ciekawe, ostatnio wszystko co do średnika szło dobrze, zrypało mi się qemu coś i to jak na złość, dzisiaj ;] Pojawia się komunikat w terminalu po wyłączeniu qemu:
Could not access KVM kernel module: No such file or directory
failed to initialize KVM: No such file or directory
Back to tcg accelerator.
Pomocy :)