//----------------------------------------------------------------- // usfcsipl.s // // Here is a 'boot-loader' that you can use for launching any // program-demos posted on this website which are intended to // execute in a 'Pre-boot eXecution Environment' (outside the // context of an operating system). It is designed to reside // in sector zero of an (otherwise unused) Linux partition on // the primary hard disk. Its algorithm assumes that this is // the disk's final Linux partition and that the partition is // large enough to encompass at least 127 additional sectors. // For the convenience of students using this disk-space as a // launching pad for programming experiments, the partition's // access-attributes should allow read/write by any user. By // tweaking of hard-coded parameters, we've successfully used // this code to boot our program-demos from an alternate disk // (e.g., from a secondary drive or from a memory-stick), but // in the example depicted here our partition is '/dev/sda4'. // // to assemble: $ as usfcsipl.s -o usgcsipl.o // and to link: $ ld usfcsipl.o -o usfcsipl.b -T ldscript // and install: $ dd if=usfcsipl.b of=/dev/sda4 // // NOTE: We use our custom linker-script (called 'ldscript') // to create a 'binary-format' executable, as required here. // // programmer: ALLAN CRUSE // written on: 15 FEB 2010 //----------------------------------------------------------------- .equ DRIVE_ID, 0x80 # BIOS ID-number for disk .equ EDD_READ, 0x42 # 'Read_Sectors' function .section .text #------------------------------------------------------------------ start: .code16 # executes in 'real-mode' ljmp $0x07C0, $main # renormalizes CS and IP #------------------------------------------------------------------ progID: .ascii " usfcsipl 02/15/2010 " # to aid file-recognition #------------------------------------------------------------------ # Our initial Device Address Packet for EDD ROM-BIOS Function 0x42 packet: .byte 16, 0, 1, 0 # read one 512-byte sector .word 0x7E00, 0x0000 # where to put disk-sector .quad 0 # where to get disk-sector #------------------------------------------------------------------ relLBA: .long 0 # current disk start-block #------------------------------------------------------------------ msg0: .ascii "Hit any key to reboot system\r\n" # message-text len0: .short . - msg0 # length of message-string msg1: .ascii "Unable to read from disk\r\n" # message-text len1: .short . - msg1 # length of message-string msg2: .ascii "Disk program is invalid\r\n" # message-text len2: .short . - msg2 # length of message-string msg3: .ascii "Linux partition not found\r\n" # message-text len3: .short . - msg3 # length of message-string #------------------------------------------------------------------ #------------------------------------------------------------------ main: # initialize our stack-pointer for servicing interrupts xor %ax, %ax # address bottom arena mov %ax, %ss # with SS register mov $0x7C00, %sp # stack is beneath code sti # now permit interrupts # setup segment-registers to address our program data mov %cs, %ax # address our variables mov %ax, %ds # with DS register # read disk's 'Master Boot Record' into region at 0x7E00 again: movw $0, %ss:0x7FFE # boot-signature's field lea packet, %si # point DS:SI to packet mov $DRIVE_ID, %dl # select the hard-disk mov $EDD_READ, %ah # select BIOS function int $0x13 # invoke BIOS service jc rderr # error? exit w/message # check that the record read has a valid boot-signature cmpw $0xAA55, %ss:0x7FFE # boot-signature there? jne inval # no, exit w/message # search the Partition-Table entries in backward order mov $1022, %bx # point DS:BX to signature mov $4, %cx # number of table entries nxpte: sub $16, %bx # point to the next entry cmpb $0x05, 4(%bx) # Extended partition-type? je isext # yes, need another access cmpb $0x83, 4(%bx) # Linux partition-type? je found # Linux partition found loopne nxpte # else check next entry jmp nopte # else exit w/message isext: # DS:BX points to an 'Extended Partition' table-entry # Algorithm: # if this is a 'primary' extended partition, then # change our logical disk's relative starting LBA mov 8(%ebx), %eax # get partition base add relLBA, %eax # add partition start cmpl $0, relLBA # primary Ext-Partn? jne inner # no, retain relLBA mov %eax, relLBA # else modify relLBA inner: mov %eax, packet+8 # set as packet source jmp again # get next boot-sector #------------------------------------------------------------------ rderr: lea msg1, %bp # message-offset in BP mov len1, %cx # message-length in CX jmp showmsg # display that message #------------------------------------------------------------------ inval: lea msg2, %bp # message-offset in BP mov len2, %cx # message-length in CX jmp showmsg # display that message #------------------------------------------------------------------ nopte: lea msg3, %bp # message-offset in BP mov len3, %cx # message-length in CX jmp showmsg # display that message #------------------------------------------------------------------ found: # DS:BX = table-entry for disk's final Linux partition # Algorithm: # adjust our packet for reading multiple records mov 8(%bx), %eax # partition starting-LBA inc %eax # skip past boot-record add %eax, packet+8 # plus disk's start LBA movb $127, packet+2 # read 127 disk-sectors movw $0x0000, packet+4 # load-address offset movw $0x1000, packet+6 # load-address segment # read the cs698 program-blocks into region at 0x10000 lea packet, %si # point DS:SI to packet mov $DRIVE_ID, %dl # select the hard disk mov $EDD_READ, %ah # select BIOS function int $0x13 # invoke BIOS service jc rderr # error? exit w/message # check for our special 'application signature' les packet+4, %di # point ES:DI to arena cmpw $0xABCD, %es:(%di) # our signature there? jne inval # no, exit w/message # else perform a direct far call to our application mov packet+8, %ebx # pass partition's LBA dec %ebx # as parameter in EBX mov %ebx, %ss:0x04F0 # and as ROM-BIOS data lcall $0x1000, $0x0002 # with call to program # accommodate 'quirk' in some ROM-BIOS service-functions by # insuring segment-registers FS,GS have 4GB segment-limits mov %cs, %ax # address program data mov %ax, %ds # with DS register lgdt regGDT # setup register GDTR cli # turn off interrupts mov %cr0, %eax # get machine status bts $0, %eax # set image of PE-bit mov %eax, %cr0 # enter protected mode mov $0x0008, %dx # descriptor's selector mov %dx, %fs # for 4GB segment-limit mov %dx, %gs # both in FS and in GS btr $0, %eax # clear PE-bit's image mov %eax, %cr0 # leave protected mode sti # interrupts on again # now show the user our 'reboot' message lea msg0, %bp # message-offset in BP mov len0, %cx # message-length in CX showmsg: # use ROM-BIOS services to write a message to the screen push %cx # preserve string-length mov $0x0F, %ah # get page-number in BH int $0x10 # invoke BIOS service mov $0x03, %ah # get cursor locn in DX int $0x10 # invoke BIOS service pop %cx # recover string-length mov %ds, %ax # address our variables mov %ax, %es # using ES register mov $0x0F, %bl # put text colors in BL mov $0x1301, %ax # select 'write_string' int $0x10 # invoke BIOS service # await our user's keypress mov $0x00, %ah # await keyboard input int $0x16 # invoke BIOS service # invoke the ROM-BIOS reboot service int $0x19 # reboot this machine #------------------------------------------------------------------ theGDT: .quad 0, 0x008F92000000FFFF # has 4GB data-descriptor regGDT: .word 15, theGDT + 0x7C00, 0 # image for register GDTR #------------------------------------------------------------------ .org 510 # offset of boot-signature .byte 0x55, 0xAA # value for boot-signature #------------------------------------------------------------------ .end # nothing more to assemble