Ben yeni yeni assembly öğreniyorum. Bunu da sağdan soldan topladığım bilgilerle yazdım. Bu bir boot kodu. Aslında boot kodu olmadığı bariz ama bunu floppy nin boot sektörüne kopyaladımızda ekrana "calisiyor..." yazmasını istedim. Ve sonra sonsuz döngüye soktum zaten atlayacak bir yer olmayacak. Ben bunu Virtual machine de denedim ama çalışmadı zaten link yapamadım .obj dosyasını direk kullanıdım ama oda çalışmadı. Tasm da derlediğimde "geçersiz başlama noktası" diye bir hata veriyor. Gerçi linux kullanıcıları tasm dam falan pek anlamaz ama neyse yardıma ihtiyacım var.
Sence bu soru için uygun bir yer mi? Belki de programlama bölümüne yazmalısın.
Derledikten sonra bunu boot sektörüne nasıl yazmayı düşünüyordun ki?
Ben yüzeysel bir şekilde boot edebilir assembly kodunun nasıl yazıldığı ve çalıştığı konusunda açıklama yapayım.
Sistem çalışmaya ilk başladığında, BIOS ROM olarak bilinen küçük bir ROM entegresinden BIOS (Temel Giriş Çıkış Sistemi)'u okur ve çalıştırır. BIOS programı POST (Power On SelfTest) işlemini gerçekleştirerek bilgisayarda mevcut olan donanımları algılamaya çalışır. Bu aşamada işletim sistemi programcılarını birkaç küçük şey dışında ilgilendiren fazla birşey yoktur. Sistem açıldığında çalışan bu ilk program işini bitirdikten sonra fiziksel belleğin 0x7C00 adresine BIOS ayarları yüklenerek hangi aygıtların boot kaydı içerdiği incelenmeye başlanır. Boot kaydı dediğimiz şey boot edebilen çeşitli aygıtların ilk sektörlerine yüklenen programlardır. Boot kaydı 510'uncu ofset adresinde bir 0xAA55 boot sinyalini içerir. BIOS tüm boot edebilir aygıtlarda bu imzanın varlığını araştırır. Eğer incelemiş olduğu aygıt boot sinyali içermiyorsa, bir sonraki boot edebilir aygıta geçerek incelemesini sürdürür. Eğer incelemiş olduğu aygıtların hiçbiri, geçerli bir boot sinyali içermiyorsa, ekrana "Non-system disk" mesajına benzer bir mesaj basar. Geçerli bir boot kaydına rastlarsa, çalıştırmak üzere boot koduna sıçrar.
Assembly bildiğini varsayarak, aşağıya da örnek bir assembly kodu ekliyorum.
code_seg segment use16 assume cs: code_seg
jmp start
DisplayMsg db ‘GNU/Linux Kernel 2.6.21.5 Loading...’,0
display proc near prnt: lodsb or al,al jz over mov ah,0Eh mov bh,0 int 10h jmp prnt over: ret display endp
start: mov ax,07C0h mov ds,ax
cli mov ax,1000h mov ss,ax mov sp,0ffffh sti
mov si,offset DisplayMsg call display
mov ah,0 int 16h
db 0eah dw 0000h dw 0ffffh
org 510 dw 0aa55h
code_seg ends end
Biraz uzun olacak ama harika bir inceleme olanağı sağlayacağını düşünerek Linux 2.0 boot sektörünü ekliyorum.
! ! SYS_SIZE is the number of clicks (16 bytes) to be loaded. ! 0x7F00 is 0x7F000 bytes = 508kB, more than enough for current ! versions of linux which compress the kernel ! #include <linux/config.h> SYSSIZE = DEF_SYSSIZE ! ! bootsect.s Copyright (C) 1991, 1992 Linus Torvalds ! modified by Drew Eckhardt ! modified by Bruce Evans (bde) ! ! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves ! itself out of the way to address 0x90000, and jumps there. ! ! bde - should not jump blindly, there may be systems with only 512K low ! memory. Use int 0x12 to get the top of memory, etc. ! ! It then loads 'setup' directly after itself (0x90200), and the system ! at 0x10000, using BIOS interrupts. ! ! NOTE! currently system is at most (8*65536-4096) bytes long. This should ! be no problem, even in the future. I want to keep it simple. This 508 kB ! kernel size should be enough, especially as this doesn't contain the ! buffer cache as in minix (and especially now that the kernel is ! compressed :-) ! ! The loader has been made as simple as possible, and continuous ! read errors will result in a unbreakable loop. Reboot by hand. It ! loads pretty fast by getting whole tracks at a time whenever possible.
.text
SETUPSECS = 4 ! nr of setup-sectors BOOTSEG = 0x07C0 ! original address of boot-sector INITSEG = DEF_INITSEG ! we move boot here - out of the way SETUPSEG = DEF_SETUPSEG ! setup starts here SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
! ROOT_DEV & SWAP_DEV are now written by "build". ROOT_DEV = 0 SWAP_DEV = 0 #ifndef SVGA_MODE #define SVGA_MODE ASK_VGA #endif #ifndef RAMDISK #define RAMDISK 0 #endif #ifndef CONFIG_ROOT_RDONLY #define CONFIG_ROOT_RDONLY 1 #endif
! ld86 requires an entry symbol. This may as well be the usual one. .globl _main _main: #if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */ int 3 #endif mov ax,#BOOTSEG mov ds,ax mov ax,#INITSEG mov es,ax mov cx,#256 sub si,si sub di,di cld rep movsw jmpi go,INITSEG
! ax and es already contain INITSEG
go: mov di,#0x4000-12 ! 0x4000 is arbitrary value >= length of ! bootsect + length of setup + room for stack ! 12 is disk parm size
! bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We ! wouldn't have to worry about this if we checked the top of memory. Also ! my BIOS can be configured to put the wini drive tables in high memory ! instead of in the vector table. The old stack might have clobbered the ! drive table.
mov ds,ax mov ss,ax ! put stack at INITSEG:0x4000-12. mov sp,di /* * Many BIOS's default disk parameter tables will not * recognize multi-sector reads beyond the maximum sector number * specified in the default diskette parameter tables - this may * mean 7 sectors in some cases. * * Since single sector reads are slow and out of the question, * we must take care of this by creating new parameter tables * (for the first disk) in RAM. We will set the maximum sector * count to 36 - the most we will encounter on an ED 2.88. * * High doesn't hurt. Low does. * * Segments are as follows: ds=es=ss=cs - INITSEG, * fs = 0, gs is unused. */
! cx contains 0 from rep movsw above
mov fs,cx mov bx,#0x78 ! fs:bx is parameter table address push ds seg fs lds si,(bx) ! ds:si is source
mov cl,#6 ! copy 12 bytes cld push di
rep movsw
pop di pop ds
movb 4(di),*36 ! patch sector count
seg fs mov (bx),di seg fs mov 2(bx),es
! load the setup-sectors directly after the bootblock. ! Note that 'es' is already set up. ! Also cx is 0 from rep movsw above.
load_setup: xor ah,ah ! reset FDC xor dl,dl int 0x13
xor dx, dx ! drive 0, head 0 mov cl,#0x02 ! sector 2, track 0 mov bx,#0x0200 ! address = 512, in INITSEG mov ah,#0x02 ! service 2, nr of sectors mov al,setup_sects ! (assume all on head 0, track 0) int 0x13 ! read it jnc ok_load_setup ! ok - continue
! Get disk drive parameters, specifically nr of sectors/track
#if 0
! bde - the Phoenix BIOS manual says function 0x08 only works for fixed ! disks. It doesn't work for one of my BIOS's (1987 Award). It was ! fatal not to check the error code.
xor dl,dl mov ah,#0x08 ! AH=8 is get drive parameters int 0x13 xor ch,ch #else
! It seems that there is no BIOS call to get the number of sectors. Guess ! 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read, ! 15 if sector 15 can be read. Otherwise guess 9.
mov si,#disksizes ! table of sizes to try
probe_loop: lodsb cbw ! extend to word mov sectors, ax cmp si,#disksizes+4 jae got_sectors ! if all else fails, try 9 xchg ax, cx ! cx = track and sector xor dx, dx ! drive 0, head 0 xor bl, bl mov bh,setup_sects inc bh shl bh,#1 ! address after setup (es = cs) mov ax,#0x0201 ! service 2, 1 sector int 0x13 jc probe_loop ! try next value
! After that we check which root-device to use. If the device is ! defined (!= 0), nothing is done and the given device is used. ! Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8), ! depending on the number of sectors we pretend to know we have.
seg cs mov ax,root_dev or ax,ax jne root_defined seg cs mov bx,sectors mov ax,#0x0208 ! /dev/ps0 - 1.2Mb cmp bx,#15 je root_defined mov al,#0x1c ! /dev/PS0 - 1.44Mb cmp bx,#18 je root_defined mov al,#0x20 ! /dev/fd0H2880 - 2.88Mb cmp bx,#36 je root_defined mov al,#0 ! /dev/fd0 - autodetect root_defined: seg cs mov root_dev,ax
! after that (everything loaded), we jump to ! the setup-routine loaded directly after ! the bootblock:
jmpi 0,SETUPSEG
! This routine loads the system at address 0x10000, making sure ! no 64kB boundaries are crossed. We try to load it as fast as ! possible, loading whole tracks whenever we can. ! ! in: es - starting address segment (normally 0x1000) ! sread: .word 0 ! sectors read of current track head: .word 0 ! current head track: .word 0 ! current track
read_it: mov al,setup_sects inc al mov sread,al mov ax,es test ax,#0x0fff die: jne die ! es must be at 64kB boundary xor bx,bx ! bx is starting address within segment rp_read: #ifdef __BIG_KERNEL__ #define CALL_HIGHLOAD_KLUDGE .word 0x1eff,0x220 ! call far * bootsect_kludge ! NOTE: as86 can't assemble this CALL_HIGHLOAD_KLUDGE ! this is within setup.S #else mov ax,es sub ax,#SYSSEG #endif cmp ax,syssize ! have we loaded all yet? jbe ok1_read ret ok1_read: mov ax,sectors sub ax,sread mov cx,ax shl cx,#9 add cx,bx jnc ok2_read je ok2_read xor ax,ax sub ax,bx shr ax,#9 ok2_read: call read_track mov cx,ax add ax,sread cmp ax,sectors jne ok3_read mov ax,#1 sub ax,head jne ok4_read inc track ok4_read: mov head,ax xor ax,ax ok3_read: mov sread,ax shl cx,#9 add bx,cx jnc rp_read mov ax,es add ah,#0x10 mov es,ax xor bx,bx jmp rp_read
read_track: pusha pusha mov ax, #0xe2e ! loading... message 2e = . mov bx, #7 int 0x10 popa
push dx ! save for error dump push cx push bx push ax
int 0x13 jc bad_rt add sp, #8 popa ret
bad_rt: push ax ! save error code call print_all ! ah = error, al = read
xor ah,ah xor dl,dl int 0x13
add sp, #10 popa jmp read_track
/* * print_all is for debugging purposes. * It will print out all of the registers. The assumption is that this is * called from a routine, with a stack frame like * dx * cx * bx * ax * error * ret <- sp * */
print_loop: push cx ! save count left call print_nl ! nl for readability
cmp cl, #5 jae no_reg ! see if register name is needed
mov ax, #0xe05 + 'A - 1 sub al, cl int 0x10
mov al, #'X int 0x10
mov al, #': int 0x10
no_reg: add bp, #2 ! next register call print_hex ! print it pop cx loop print_loop ret
print_nl: mov ax, #0xe0d ! CR int 0x10 mov al, #0xa ! LF int 0x10 ret
/* * print_hex is for debugging purposes, and prints the word * pointed to by ss:bp in hexadecimal. */
print_hex: mov cx, #4 ! 4 hex digits mov dx, (bp) ! load word into dx print_digit: rol dx, #4 ! rotate so that lowest 4 bits are used mov ax, #0xe0f ! ah = request, al = mask for nybble and al, dl add al, #0x90 ! convert al to ascii hex (four instructions) daa adc al, #0x40 daa int 0x10 loop print_digit ret
/* * This procedure turns off the floppy drive motor, so * that we enter the kernel in a known state, and * don't have to worry about it later. */ kill_motor: push dx mov dx,#0x3f2 xor al, al outb pop dx ret