/* * bootsect.S Copyright (C) 1991, 1992 Linus Torvalds * * modified by Drew Eckhardt * modified by Bruce Evans (bde) * modified by Chris Noe (May 1999) (as86 -> gas) * * 360k/720k disk support: Andrzej Krzysztofowicz* * BIG FAT NOTE: We're in real mode using 64k segments. Therefore segment * addresses must be multiplied by 16 to obtain their respective linear * addresses. To avoid confusion, linear addresses are written using leading * hex while segment addresses are written as segment:offset. * * bde - should not jump blindly, there may be systems with only 512K low * memory. Use int 0x12 to get the top of memory, etc. * ////////////////////////////////////////////////////////////////////////// // cxl: 一些常用的数据:512=2^9=0x200; 4K=2^12=0x1000; 1M=2^20=0x100000; // cxl: 1G=2^30=0x40000000; 2G=0x80000000; 3G=0xc0000000; // cxl: bootsect将setup装载到0x90200,并将系统装载到0x10000=2^16=64K处 ////////////////////////////////////////////////////////////////////////// * 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. */ #include < asm/boot.h > /////////////////////////////////////////////////////////////////////////////// // cxl: setup的缺省的扇区数为4 // cxl: bootsect被BIOS装入0x07C0处 // cxl: bootsect首先将自己拷贝到INITSEG段即0x90000处 // cxl: 然后将setup拷贝到SETUPSEG段即0x90200处 // cxl: 系统被装载在SYSSEG段即0x10000即64k处 // cxl: 系统的大小 // cxl: 察看boot.h可以找到这些宏定义 //////////////////////////////////////////////////////////////////////////////// SETUPSECTS = 4 /* default 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) */ SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */ /* to be loaded */ ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */ SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */ #ifndef SVGA_MODE #define SVGA_MODE ASK_VGA #endif #ifndef RAMDISK #define RAMDISK 0 #endif #ifndef ROOT_RDONLY #define ROOT_RDONLY 1 #endif .code16 .text .global _start _start: ///////////////////////////////////////////////////////////////////////// // cxl: bootsect从这里开始运行 // cxl: 这段代码将bootsect拷贝到INITSEG段开始处,内存地址0x90000 // cxl: 256*2=512 // cxl: 拷贝结束后,跳转到新的go标号对应的地址处开始运行 //////////////////////////////////////////////////////////////////////// # First things first. Move ourself from 0x7C00 -> 0x90000 and jump there. movw $BOOTSEG, %ax movw %ax, %ds # %ds = BOOTSEG movw $INITSEG, %ax movw %ax, %es # %ax = %es = INITSEG movw $256, %cx subw %si, %si subw %di, %di cld rep movsw ljmp $INITSEG, $go # 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. //////////////////////////////////////////////////////////////////////////// // cxl: 设置堆栈到INITSEG段开始的0x4000-12的地方 // cxl: 0x4000即16K,是任意的一个大于(bootsect+setup+堆栈)空间的一个地址值 // cxl: 栈底的12个字节留着存放磁盘参数 //////////////////////////////////////////////////////////////////////////// go: movw $0x4000-12, %di # 0x4000 is an arbitrary value >= # length of bootsect + length of # setup + room for stack; # 12 is disk parm size. movw %ax, %ds # %ax and %es already contain INITSEG movw %ax, %ss movw %di, %sp # put stack at INITSEG:0x4000-12. # 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. # /////////////////////////////////////////////////////////////// // cxl: 为了支持多扇区读操作,需要确定磁盘参数中每个磁道上的扇区数 // cxl: 从大到小逐个判断 /////////////////////////////////////////////////////////////// # 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: %cs = %ds = %es = %ss = INITSEG, %fs = 0, # and %gs is unused. //////////////////////////////////////////////////////////////////////// // cxl: 从0x78处读出BIOS中的磁盘参数 // cxl: 先将最大值写入磁盘参数 /////////////////////////////////////////////////////////////////////// movw %cx, %fs # %fs = 0 movw $0x78, %bx # %fs:%bx is parameter table address pushw %ds ldsw %fs:(%bx), %si # %ds:%si is source movb $6, %cl # copy 12 bytes pushw %di # %di = 0x4000-12. rep # don't worry about cld movsw # already done above popw %di popw %ds movb $36, 0x4(%di) # patch sector count movw %di, %fs:(%bx) movw %es, %fs:2(%bx) # Get disk drive parameters, specifically number of sectors/track. # 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. # Note that %cx = 0 from rep movsw above. ///////////////////////////////////////////////////////////////////// // cxl: disksizes是一个表,存放了从大到小一次判断的几种可能的值 ///////////////////////////////////////////////////////////////////// movw $disksizes, %si # table of sizes to try probe_loop: lodsb cbtw # extend to word movw %ax, sectors cmpw $disksizes+4, %si jae got_sectors # If all else fails, try 9 xchgw %cx, %ax # %cx = track and sector xorw %dx, %dx # drive 0, head 0 movw $0x0200, %bx # address = 512, in INITSEG (%es = %cs) movw $0x0201, %ax # service 2, 1 sector int $0x13 jc probe_loop # try next value ////////////////////////////////////////////////////////////////////////// // cxl: 两种情况会进入got_sectors:1)找到合适的大小;2)最小为9 // cxl: 不管怎样,确定了某个值 // cxl: 接下来,显示一个信息:Loading... ///////////////////////////////////////////////////////////////////////// got_sectors: movb $0x03, %ah # read cursor pos xorb %bh, %bh int $0x10 movw $9, %cx movb $0x07, %bl # page 0, attribute 7 (normal) # %bh is set above; int10 doesn't # modify it movw $msg1, %bp movw $0x1301, %ax # write string, move cursor int $0x10 # tell the user we're loading.. ///////////////////////////////////////////////////////////////////////// // cxl: 下面将setup拷贝到刚才拷贝的bootsect后面即0x90200开始处 ///////////////////////////////////////////////////////////////////////// # Load the setup-sectors directly after the moved bootblock (at 0x90200). # We should know the drive geometry to do it, as setup may exceed first # cylinder (for 9-sector 360K and 720K floppies). movw $0x0001, %ax # set sread (sector-to-read) to 1 as movw $sread, %si # the boot sector has already been read movw %ax, (%si) xorw %ax, %ax # reset FDC xorb %dl, %dl int $0x13 movw $0x0200, %bx # address = 512, in INITSEG next_step: movb setup_sects, %al movw sectors, %cx subw (%si), %cx # (%si) = sread cmpb %cl, %al jbe no_cyl_crossing movw sectors, %ax subw (%si), %ax # (%si) = sread no_cyl_crossing: call read_track pushw %ax # save it call set_next # set %bx properly; it uses %ax,%cx,%dx popw %ax # restore subb %al, setup_sects # rest - for next step jnz next_step ////////////////////////////////////////////////////////////////////////////// // cxl: 下面拷贝系统代码(即带解压缩头的compressed/vmlinux(bvmlinux), // cxl: 目的为SYSSEG段所在的地址,内存地址0x10000即64KB处 ////////////////////////////////////////////////////////////////////////////// pushw $SYSSEG popw %es # %es = SYSSEG call read_it call kill_motor call print_nl ///////////////////////////////////////////////////////////////////////// // cxl: 下面检查根设备 // cxl: 如果指定的根文件设备,OK // cxl: 如果没有指定就根据前面测试出的扇区数判断根文件设备的类型 ///////////////////////////////////////////////////////////////////////// # 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. # Segments are as follows: %cs = %ds = %ss = INITSEG, # %es = SYSSEG, %fs = 0, %gs is unused. movw root_dev, %ax orw %ax, %ax jne root_defined movw sectors, %bx movw $0x0208, %ax # /dev/ps0 - 1.2Mb cmpw $15, %bx je root_defined movb $0x1c, %al # /dev/PS0 - 1.44Mb cmpw $18, %bx je root_defined movb $0x20, %al # /dev/fd0H2880 - 2.88Mb cmpw $36, %bx je root_defined movb $0, %al # /dev/fd0 - autodetect root_defined: movw %ax, root_dev /////////////////////////////////////////////////////////////////// // cxl: 至此,bootsect完成了它的任务 // cxl: 下面的ljmp语句跳转到setup开始的地方运行 //////////////////////////////////////////////////////////////////// # After that (everything loaded), we jump to the setup-routine # loaded directly after the bootblock: ljmp $SETUPSEG, $0 //////////////////////////////////////////////////////////////////// // cxl: 下面定义的内容是一些变量和函数,在上面的过程中已经使用过了 // cxl: 函数主要是:装载系统的函数和打印一些信息的函数 //////////////////////////////////////////////////////////////////// # These variables are addressed via %si register as it gives shorter code. sread: .word 0 # sectors read of current track head: .word 0 # current head track: .word 0 # current track # This routine loads the system at address SYSSEG, making sure # no 64kB boundaries are crossed. We try to load it as fast as # possible, loading whole tracks whenever we can. read_it: movw %es, %ax # %es = SYSSEG when called testw $0x0fff, %ax die: jne die # %es must be at 64kB boundary xorw %bx, %bx # %bx is starting address within segment rp_read: #ifdef __BIG_KERNEL__ # look in setup.S for bootsect_kludge bootsect_kludge = 0x220 # 0x200 + 0x20 which is the size of the lcall bootsect_kludge # bootsector + bootsect_kludge offset #else movw %es, %ax subw $SYSSEG, %ax movw %bx, %cx shr $4, %cx add %cx, %ax # check offset #endif cmpw syssize, %ax # have we loaded everything yet? jbe ok1_read ret ok1_read: movw sectors, %ax subw (%si), %ax # (%si) = sread movw %ax, %cx shlw $9, %cx addw %bx, %cx jnc ok2_read je ok2_read xorw %ax, %ax subw %bx, %ax shrw $9, %ax ok2_read: call read_track call set_next jmp rp_read read_track: pusha pusha movw $0xe2e, %ax # loading... message 2e = . movw $7, %bx int $0x10 popa # Accessing head, track, sread via %si gives shorter code. movw 4(%si), %dx # 4(%si) = track movw (%si), %cx # (%si) = sread incw %cx movb %dl, %ch movw 2(%si), %dx # 2(%si) = head movb %dl, %dh andw $0x0100, %dx movb $2, %ah pushw %dx # save for error dump pushw %cx pushw %bx pushw %ax int $0x13 jc bad_rt addw $8, %sp popa ret set_next: movw %ax, %cx addw (%si), %ax # (%si) = sread cmp sectors, %ax jne ok3_set movw $0x0001, %ax xorw %ax, 2(%si) # change head jne ok4_set incw 4(%si) # next track ok4_set: xorw %ax, %ax ok3_set: movw %ax, (%si) # set sread shlw $9, %cx addw %cx, %bx jnc set_next_fin movw %es, %ax addb $0x10, %ah movw %ax, %es xorw %bx, %bx set_next_fin: ret bad_rt: pushw %ax # save error code call print_all # %ah = error, %al = read xorb %ah, %ah xorb %dl, %dl int $0x13 addw $10, %sp 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_all: movw $5, %cx # error code + 4 registers movw %sp, %bp print_loop: pushw %cx # save count remaining call print_nl # <-- for readability cmpb $5, %cl jae no_reg # see if register name is needed movw $0xe05 + 'A' - 1, %ax subb %cl, %al int $0x10 movb $'X', %al int $0x10 movb $':', %al int $0x10 no_reg: addw $2, %bp # next register call print_hex # print it popw %cx loop print_loop ret //////////////////////////////////////////////////////////////////// // cxl: 输出回车 //////////////////////////////////////////////////////////////////// print_nl: movw $0xe0d, %ax # CR int $0x10 movb $0xa, %al # LF int $0x10 ret # print_hex is for debugging purposes, and prints the word # pointed to by %ss:%bp in hexadecimal. print_hex: movw $4, %cx # 4 hex digits movw (%bp), %dx # load word into %dx print_digit: rolw $4, %dx # rotate to use low 4 bits movw $0xe0f, %ax # %ah = request andb %dl, %al # %al = mask for nybble addb $0x90, %al # convert %al to ascii hex daa # in only four instructions! adc $0x40, %al 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. # NOTE: Doesn't save %ax or %dx; do it yourself if you need to. kill_motor: #if 1 xorw %ax, %ax # reset FDC xorb %dl, %dl int $0x13 #else movw $0x3f2, %dx xorb %al, %al outb %al, %dx #endif ret sectors: .word 0 disksizes: .byte 36, 18, 15, 9 msg1: .byte 13, 10 .ascii "Loading" # XXX: This is a fairly snug fit. ///////////////////////////////////////////////////////// // cxl: 这个文件生成的目标代码长度为512 // cxl: 在512的最后15个字节定义了一些变量,第一个字节的偏移为0x1F1 ///////////////////////////////////////////////////////// .org 497 setup_sects: .byte SETUPSECTS root_flags: .word ROOT_RDONLY syssize: .word SYSSIZE swap_dev: .word SWAP_DEV ram_size: .word RAMDISK vid_mode: .word SVGA_MODE root_dev: .word ROOT_DEV boot_flag: .word 0xAA55