Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | /* * 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 <ankry@green.mif.pg.gda.pl> * * 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. * * 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> 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: # 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. 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. # # 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. 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. 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 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.. # 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) call kill_motor # reset FDC 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 pushw $SYSSEG popw %es # %es = SYSSEG call read_it call kill_motor call print_nl # 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 # After that (everything loaded), we jump to the setup-routine # loaded directly after the bootblock: ljmp $SETUPSEG, $0 # 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 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: movw $0x3f2, %dx xorb %al, %al outb %al, %dx ret sectors: .word 0 disksizes: .byte 36, 18, 15, 9 msg1: .byte 13, 10 .ascii "Loading" # XXX: This is a fairly snug fit. .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 |