.code16 # rewrite with AT&T syntax by falconat 081012 # # setup.s (C) 1991 Linus Torvalds # # setup.s is responsible for getting the system data from the BIOS, # and putting them into the appropriate places in system memory. # both setup.s and system has been loaded by the bootblock. # # This code asks the bios for memory/disk/other parameters, and # puts them in a "safe" place: 0x90000-0x901FF, ie where the # boot-block used to be. It is then up to the protected mode # system to read them from there before the area is overwritten # for buffer-blocks. # # NOTE! These had better be the same as in bootsect.s! .equ INITSEG, 0x9000 # we move boot here - out of the way .equ SYSSEG, 0x1000 # system loaded at 0x10000 (65536). .equ SETUPSEG, 0x9020 # this is the current segment .global _start, begtext, begdata, begbss, endtext, enddata, endbss .text begtext: .data begdata: .bss begbss: .text ljmp $SETUPSEG, $_start #boot/bootsetup.s中,會透過ljmp $SETUPSEG, $0跳到這一行 _start: # ok, the read went well so we get current cursor position and save it for # posterity. mov $INITSEG, %ax # this is done in bootsect already, but...,這裡原先是放用來執行bootsect的code, #但現在已經用不到了,改為放從bios讀到的資訊(cursor pos,memory size,...) mov %ax, %ds mov $0x03, %ah # read cursor pos xor %bh, %bh int $0x10 # save it in known place, con_init fetches mov %dx, %ds:0 # it from 0x90000. # Get memory size (extended mem, kB) mov $0x88, %ah int $0x15 mov %ax, %ds:2 # Get video-card data: mov $0x0f, %ah int $0x10 mov %bx, %ds:4 # bh = display page mov %ax, %ds:6 # al = video mode, ah = window width # check for EGA/VGA and some config parameters mov $0x12, %ah mov $0x10, %bl int $0x10 mov %ax, %ds:8 mov %bx, %ds:10 mov %cx, %ds:12 # Get hd0 data mov $0x0000, %ax mov %ax, %ds lds %ds:4*0x41, %si mov $INITSEG, %ax mov %ax, %es mov $0x0080, %di mov $0x10, %cx rep movsb # Get hd1 data mov $0x0000, %ax mov %ax, %ds lds %ds:4*0x46, %si mov $INITSEG, %ax mov %ax, %es mov $0x0090, %di mov $0x10, %cx rep movsb # Check that there IS a hd1 :-) mov $0x01500, %ax mov $0x81, %dl int $0x13 jc no_disk1 cmp $3, %ah je is_disk1 no_disk1: mov $INITSEG, %ax mov %ax, %es mov $0x0090, %di mov $0x10, %cx mov $0x00, %ax rep stosb is_disk1: # now we want to move to protected mode ... cli # no interrupts allowed ! # first we move the system to it's rightful place,system大小=tools/kernel=121508bytes mov $0x0000, %ax cld # 'direction'=0, movs moves forward,SI DI遞增 do_move: #第一次為將資料從source 0x1000 移到destination 0x0000,移65536bytes, #之後為source與dest分別加0x1000再做相同的動作,直到src=0x9000時,則跳出迴圈 mov %ax, %es # destination segment add $0x1000, %ax cmp $0x9000, %ax #因為這邊system大小為121508bytes,故最少只須搬兩次(65536*2),即最少只須cmp到$0x3000 jz end_move mov %ax, %ds # source segment sub %di, %di sub %si, %si mov $0x8000, %cx rep movsw jmp do_move # then we load the segment descriptors end_move: mov $SETUPSEG, %ax # right, forgot this at first. didn't work :-) mov %ax, %ds lidt idt_48 # load idt with 0,0 lgdt gdt_48 # load gdt with whatever appropriate # that was painless, now we enable A20 #call empty_8042 # 8042 is the keyboard controller #mov $0xD1, %al # command write #out %al, $0x64 #call empty_8042 #mov $0xDF, %al # A20 on #out %al, $0x60 #call empty_8042 inb $0x92, %al # open A20 line(Fast Gate A20). orb $0b00000010, %al outb %al, $0x92 # well, that went ok, I hope. Now we have to reprogram the interrupts :-( # we put them right after the intel-reserved hardware interrupts, at # int 0x20-0x2F. There they won't mess up anything. Sadly IBM really # messed this up with the original PC, and they haven't been able to # rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, # which is used for the internal hardware interrupts as well. We just # have to reprogram the 8259's, and it isn't fun. mov $0x11, %al # initialization sequence(ICW1) # ICW4 needed(1),CASCADE mode,Level-triggered out %al, $0x20 # send it to 8259A-1 .word 0x00eb,0x00eb # jmp $+2, jmp $+2 out %al, $0xA0 # and to 8259A-2 .word 0x00eb,0x00eb mov $0x20, %al # start of hardware int's (0x20)(ICW2) out %al, $0x21 # from 0x20-0x27 .word 0x00eb,0x00eb mov $0x28, %al # start of hardware int's 2 (0x28) out %al, $0xA1 # from 0x28-0x2F .word 0x00eb,0x00eb # IR 7654 3210 mov $0x04, %al # 8259-1 is master(0000 0100) --\ out %al, $0x21 # | .word 0x00eb,0x00eb # INT / mov $0x02, %al # 8259-2 is slave( 010 --> 2) out %al, $0xA1 .word 0x00eb,0x00eb mov $0x01, %al # 8086 mode for both out %al, $0x21 .word 0x00eb,0x00eb out %al, $0xA1 .word 0x00eb,0x00eb mov $0xFF, %al # mask off all interrupts for now out %al, $0x21 .word 0x00eb,0x00eb out %al, $0xA1 # well, that certainly wasn't fun :-(. Hopefully it works, and we don't # need no steenking BIOS anyway (except for the initial loading :-). # The BIOS-routine wants lots of unnecessary data, and it's less # "interesting" anyway. This is how REAL programmers do it. # # Well, now's the time to actually move into protected mode. To make # things as simple as possible, we do no register set-up or anything, # we let the gnu-compiled 32-bit programs do that. We just jump to # absolute address 0x00000, in 32-bit protected mode. #mov $0x0001, %ax # protected mode (PE) bit #lmsw %ax # This is it! mov %cr0, %eax # get machine status(cr0|MSW) bts $0, %eax # turn on the PE-bit mov %eax, %cr0 # protection enabled # segment-descriptor (INDEX:TI:RPL) .equ sel_cs0, 0x0008 # select for code segment 0 ( 001:0 :00) ljmp $sel_cs0, $0 # jmp offset 0 of code segment 0 in gdt # This routine checks that the keyboard command queue is empty # No timeout is used - if this hangs there is something wrong with # the machine, and we probably couldn't proceed anyway. empty_8042: .word 0x00eb,0x00eb in $0x64, %al # 8042 status port test $2, %al # is input buffer full? jnz empty_8042 # yes - loop ret gdt: .word 0,0,0,0 # dummy .word 0x07FF # 8Mb - limit=2047 (2048*4096=8Mb) .word 0x0000 # base address=0 .word 0x9A00 # code read/exec .word 0x00C0 # granularity=4096, 386 .word 0x07FF # 8Mb - limit=2047 (2048*4096=8Mb) .word 0x0000 # base address=0 .word 0x9200 # data read/write .word 0x00C0 # granularity=4096, 386 idt_48: .word 0 # idt limit=0 .word 0,0 # idt base=0L gdt_48: .word 0x800 # gdt limit=2048, 256 GDT entries .word 512+gdt, 0x9 # gdt base = 0X9xxxx, # 512+gdt is the real gdt after setup is moved to 0x9020 * 0x10 .text endtext: .data enddata: .bss endbss:
2018年3月6日 星期二
linux 0.11 trace boot/setup.s
linux 0.11 trace boot/bootsect.s
.code16 # rewrite with AT&T syntax by falconat 081012 # # SYS_SIZE is the number of clicks (16 bytes) to be loaded. # 0x3000 is 0x30000 bytes = 196kB, more than enough for current # versions of linux # .equ SYSSIZE, 0x3000 # # bootsect.s (C) 1991 Linus Torvalds # # bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves # iself out of the way to address 0x90000, and jumps there. # # It then loads 'setup' directly after itself (0x90200), and the system # at 0x10000, using BIOS interrupts. # # NOTE! currently system is at most 8*65536 bytes long. This should be no # problem, even in the future. I want to keep it simple. This 512 kB # kernel size should be enough, especially as this doesn't contain the # buffer cache as in minix # # The loader has been made as simple as possible, and continuos # read errors will result in a unbreakable loop. Reboot by hand. It # loads pretty fast by getting whole sectors at a time whenever possible. .global _start, begtext, begdata, begbss, endtext, enddata, endbss .text begtext: .data begdata: .bss begbss: .text .equ SETUPLEN, 4 # nr of setup-sectors .equ BOOTSEG, 0x07c0 # original address of boot-sector .equ INITSEG, 0x9000 # we move boot here - out of the way .equ SETUPSEG, 0x9020 # setup starts here .equ SYSSEG, 0x1000 # system loaded at 0x10000 (65536). .equ ENDSEG, SYSSEG + SYSSIZE # where to stop loading # ROOT_DEV: 0x000 - same type of floppy as boot. # 0x301 - first partition on first drive etc .equ ROOT_DEV, 0x301 ljmp $BOOTSEG, $_start #跳至cs:eip,這樣一行可以不用加,因為現在的cs即為BOOTSEG(0x07c0) _start: #將BOOTSEG上的資料依序往後複製至INITSEG,共複製256回合,每回合複製兩個byte(即一個word),即從BOOTSEG複製512bytes到INITSEG上 mov $BOOTSEG, %ax mov %ax, %ds mov $INITSEG, %ax mov %ax, %es #es設定為INITSEG,之後ljmp會用到 mov $256, %cx sub %si, %si sub %di, %di rep movsw ljmp $INITSEG, $go #跳至INITSEG seg(跳過去後,會執行複製後的bootsect code) go: mov %cs, %ax #ax=cs=INITSEG mov %ax, %ds #ds=ax=INITSEG mov %ax, %es #es=ax=INITSEG # put stack at 0x9ff00. mov %ax, %ss #ss=ax=INITSEG=0x9000 mov $0xFF00, %sp # arbitrary value >>512 # load the setup-sectors directly after the bootblock. # Note that 'es' is already set up. load_setup: mov $0x0000, %dx # drive 0, head 0 mov $0x0002, %cx # sector 2, track 0 #從sector2開始讀(sector2,3放得是setup部分的code) mov $0x0200, %bx # address = 512, in INITSEG .equ AX, 0x0200+SETUPLEN mov $AX, %ax # service 2, nr of sectors #ax=0x0204(AH=02h: Read Sectors From Drive AL=Sectors To Read Count) int $0x13 # read it jnc ok_load_setup # ok - continue #會把讀到的兩個sector放在es(INITSEG):bx(0x0200)上,即為SETUPSEG mov $0x0000, %dx mov $0x0000, %ax # reset the diskette int $0x13 jmp load_setup ok_load_setup: # Get disk drive parameters, specifically nr of sectors/track mov $0x00, %dl mov $0x0800, %ax # AH=8 is get drive parameters int $0x13 mov $0x00, %ch #seg cs mov %cx, %cs:sectors+0 # %cs means sectors is in %cs mov $INITSEG, %ax mov %ax, %es # Print some inane message mov $0x03, %ah # read cursor pos xor %bh, %bh int $0x10 mov $24, %cx mov $0x0007, %bx # page 0, attribute 7 (normal) #lea msg1, %bp mov $msg1, %bp mov $0x1301, %ax # write string, move cursor int $0x10 # ok, we've written the message, now # we want to load the system (at 0x10000) mov $SYSSEG, %ax mov %ax, %es # segment of 0x010000 call read_it call kill_motor # 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, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending # on the number of sectors that the BIOS reports currently. #seg cs mov %cs:root_dev+0, %ax cmp $0, %ax jne root_defined #seg cs mov %cs:sectors+0, %bx mov $0x0208, %ax # /dev/ps0 - 1.2Mb cmp $15, %bx je root_defined mov $0x021c, %ax # /dev/PS0 - 1.44Mb cmp $18, %bx je root_defined undef_root: jmp undef_root root_defined: #seg cs mov %ax, %cs:root_dev+0 # after that (everyting loaded), we jump to # the setup-routine loaded directly after # the bootblock: ljmp $SETUPSEG, $0 #跳至SETUPSEG,執行setup code ,需看boot/setup.s # 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 1+ SETUPLEN # sectors read of current track head: .word 0 # current head track: .word 0 # current track read_it: mov %es, %ax test $0x0fff, %ax die: jne die # es must be at 64kB boundary xor %bx, %bx # bx is starting address within segment rp_read: mov %es, %ax cmp $ENDSEG, %ax # have we loaded all yet? jb ok1_read ret ok1_read: #seg cs mov %cs:sectors+0, %ax sub sread, %ax mov %ax, %cx shl $9, %cx add %bx, %cx jnc ok2_read je ok2_read xor %ax, %ax sub %bx, %ax shr $9, %ax ok2_read: call read_track mov %ax, %cx add sread, %ax #seg cs cmp %cs:sectors+0, %ax jne ok3_read mov $1, %ax sub head, %ax jne ok4_read incw track ok4_read: mov %ax, head xor %ax, %ax ok3_read: mov %ax, sread shl $9, %cx add %cx, %bx jnc rp_read mov %es, %ax add $0x1000, %ax mov %ax, %es xor %bx, %bx jmp rp_read read_track: push %ax push %bx push %cx push %dx mov track, %dx mov sread, %cx inc %cx mov %dl, %ch mov head, %dx mov %dl, %dh mov $0, %dl and $0x0100, %dx mov $2, %ah int $0x13 jc bad_rt pop %dx pop %cx pop %bx pop %ax ret bad_rt: mov $0, %ax mov $0, %dx int $0x13 pop %dx pop %cx pop %bx pop %ax jmp read_track #/* # * 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 $0x3f2, %dx mov $0, %al outsb pop %dx ret sectors: .word 0 msg1: .byte 13,10 .ascii "Loading system ..." .byte 13,10,13,10 .org 508 root_dev: .word ROOT_DEV boot_flag: .word 0xAA55 .text endtext: .data enddata: .bss endbss:
訂閱:
文章 (Atom)