| Commit | Line | Data |
|---|---|---|
| 409cbc03 MD |
1 | /* |
| 2 | * Copyright (c) 2001 John Baldwin | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms are freely | |
| 6 | * permitted provided that the above copyright notice and this | |
| 7 | * paragraph and the following disclaimer are duplicated in all | |
| 8 | * such forms. | |
| 9 | * | |
| 10 | * This software is provided "AS IS" and without any express or | |
| 11 | * implied warranties, including, without limitation, the implied | |
| 12 | * warranties of merchantability and fitness for a particular | |
| 13 | * purpose. | |
| 14 | * | |
| 15 | * | |
| 16 | * $FreeBSD: src/sys/boot/i386/cdboot/cdboot.s,v 1.9 2001/11/07 01:20:33 jhb Exp $ | |
| 65f5a4df | 17 | * $DragonFly: src/sys/boot/pc32/cdboot/cdboot.S,v 1.8 2007/05/18 07:41:43 dillon Exp $ |
| 409cbc03 MD |
18 | */ |
| 19 | ||
| 20 | /* | |
| 21 | * This program is a freestanding boot program to load an a.out binary | |
| 22 | * from a CD-ROM booted with no emulation mode as described by the El | |
| 23 | * Torito standard. Due to broken BIOSen that do not load the desired | |
| 24 | * number of sectors, we try to fit this in as small a space as possible. | |
| 25 | * | |
| 26 | * Basically, we first create a set of boot arguments to pass to the loaded | |
| 27 | * binary. Then we attempt to load /boot/loader from the CD we were booted | |
| 28 | * off of. | |
| 29 | */ | |
| 30 | ||
| 31 | #include "../bootasm.h" | |
| 32 | ||
| 33 | /* | |
| 34 | * a.out header fields | |
| 35 | */ | |
| 984263bc MD |
36 | .set AOUT_TEXT,0x04 # text segment size |
| 37 | .set AOUT_DATA,0x08 # data segment size | |
| 409cbc03 | 38 | .set AOUT_BSS,0x0c # zerod BSS size |
| 984263bc MD |
39 | .set AOUT_SYMBOLS,0x10 # symbol table |
| 40 | .set AOUT_ENTRY,0x14 # entry point | |
| 41 | .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header | |
| 409cbc03 MD |
42 | |
| 43 | /* | |
| 44 | * Flags for kargs->bootflags | |
| 45 | */ | |
| 984263bc MD |
46 | .set KARGS_FLAGS_CD,0x1 # flag to indicate booting from |
| 47 | # CD loader | |
| 409cbc03 MD |
48 | /* |
| 49 | * Segment selectors. | |
| 50 | */ | |
| 984263bc MD |
51 | .set SEL_SDATA,0x8 # Supervisor data |
| 52 | .set SEL_RDATA,0x10 # Real mode data | |
| 53 | .set SEL_SCODE,0x18 # PM-32 code | |
| 54 | .set SEL_SCODE16,0x20 # PM-16 code | |
| 409cbc03 MD |
55 | |
| 56 | /* | |
| 57 | * BTX constants | |
| 58 | */ | |
| 984263bc | 59 | .set INT_SYS,0x30 # BTX syscall interrupt |
| 409cbc03 MD |
60 | |
| 61 | /* | |
| 62 | * Constants for reading from the CD. | |
| 63 | */ | |
| 984263bc MD |
64 | .set ERROR_TIMEOUT,0x80 # BIOS timeout on read |
| 65 | .set NUM_RETRIES,3 # Num times to retry | |
| 66 | .set SECTOR_SIZE,0x800 # size of a sector | |
| 67 | .set SECTOR_SHIFT,11 # number of place to shift | |
| 68 | .set BUFFER_LEN,0x100 # number of sectors in buffer | |
| f70f1d6b MD |
69 | # Some BIOSes just can't handle this |
| 70 | # .set MAX_READ,0x10000 # max we can read at a time | |
| 71 | # .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT | |
| 72 | .set MAX_READ,0x800 # max we can read at a time | |
| 73 | .set MAX_READ_SEC,1 | |
| 984263bc MD |
74 | .set MEM_READ_BUFFER,0x9000 # buffer to read from CD |
| 75 | .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor | |
| 76 | .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer | |
| 77 | .set VOLDESC_LBA,0x10 # LBA of vol descriptor | |
| 78 | .set VD_PRIMARY,1 # Primary VD | |
| 79 | .set VD_END,255 # VD Terminator | |
| 80 | .set VD_ROOTDIR,156 # Offset of Root Dir Record | |
| 81 | .set DIR_LEN,0 # Offset of Dir Record length | |
| 82 | .set DIR_EA_LEN,1 # Offset of EA length | |
| 83 | .set DIR_EXTENT,2 # Offset of 64-bit LBA | |
| 84 | .set DIR_SIZE,10 # Offset of 64-bit length | |
| 85 | .set DIR_NAMELEN,32 # Offset of 8-bit name len | |
| 86 | .set DIR_NAME,33 # Offset of dir name | |
| 409cbc03 MD |
87 | |
| 88 | /* | |
| 89 | * Program start. | |
| 90 | * | |
| 91 | * We expect to be loaded by the BIOS at 0x7c00 (standard | |
| 92 | * boot loader entry point) | |
| 93 | */ | |
| 984263bc MD |
94 | .code16 |
| 95 | .globl start | |
| 96 | .org 0x0, 0x0 | |
| 409cbc03 | 97 | |
| 984263bc MD |
98 | start: cld # string ops inc |
| 99 | xor %ax,%ax # zero %ax | |
| 100 | mov %ax,%ss # setup the | |
| 101 | mov $start,%sp # stack | |
| 102 | mov %ax,%ds # setup the | |
| 103 | mov %ax,%es # data segments | |
| 104 | mov %dl,drive # Save BIOS boot device | |
| f70f1d6b | 105 | sti # make sure intrs are enabled |
| 984263bc MD |
106 | mov $msg_welcome,%si # %ds:(%si) -> welcome message |
| 107 | call putstr # display the welcome message | |
| 409cbc03 MD |
108 | |
| 109 | /* | |
| 110 | * Setup the arguments that the loader is expecting from | |
| 111 | * boot[12] | |
| 112 | */ | |
| 984263bc MD |
113 | mov $msg_bootinfo,%si # %ds:(%si) -> boot args message |
| 114 | call putstr # display the message | |
| 115 | mov $MEM_ARG,%bx # %ds:(%bx) -> boot args | |
| 116 | mov %bx,%di # %es:(%di) -> boot args | |
| 117 | xor %eax,%eax # zero %eax | |
| 118 | mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit | |
| 119 | # dwords | |
| 120 | rep # Clear the arguments | |
| 121 | stosl # to zero | |
| 122 | mov drive,%dl # Store BIOS boot device | |
| 123 | mov %dl,0x4(%bx) # in kargs->bootdev | |
| 124 | or $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |= | |
| 125 | # KARGS_FLAGS_CD | |
| 409cbc03 MD |
126 | /* |
| 127 | * Load Volume Descriptor | |
| 128 | */ | |
| 984263bc | 129 | mov $VOLDESC_LBA,%eax # Set LBA of first VD |
| 15facac8 | 130 | load_vd: |
| 984263bc MD |
131 | mov $1,%dh # One sector |
| 132 | mov $MEM_VOLDESC,%ebx # Destination | |
| 133 | call read # Read it in | |
| 134 | cmpb $VD_PRIMARY,(%bx) # Primary VD? | |
| 135 | je have_vd # Yes | |
| 984263bc MD |
136 | inc %eax # try next |
| 137 | cmpb $VD_END,(%bx) # Last VD? | |
| 138 | jne load_vd # No, read next | |
| 139 | mov $msg_novd,%si # No VD | |
| 140 | jmp error # Halt | |
| 141 | have_vd: # Have Primary VD | |
| 409cbc03 MD |
142 | |
| 143 | /* | |
| 144 | * Lookup the loader binary. | |
| 145 | */ | |
| 984263bc MD |
146 | mov $loader_path,%si # File to lookup |
| 147 | call lookup # Try to find it | |
| 409cbc03 MD |
148 | |
| 149 | /* | |
| 150 | * Load the binary into the buffer. Due to real mode | |
| 151 | * addressing limitations we have to read it in in 64k | |
| 152 | * chunks. | |
| 153 | */ | |
| 984263bc MD |
154 | mov DIR_SIZE(%bx),%eax # Read file length |
| 155 | add $SECTOR_SIZE-1,%eax # Convert length to sectors | |
| f70f1d6b | 156 | shr $SECTOR_SHIFT,%eax |
| 984263bc MD |
157 | cmp $BUFFER_LEN,%eax |
| 158 | jbe load_sizeok | |
| 159 | mov $msg_load2big,%si # Error message | |
| 160 | call error | |
| f70f1d6b | 161 | load_sizeok: mov %al,%cl |
| 984263bc MD |
162 | mov DIR_EXTENT(%bx),%eax # Load extent |
| 163 | xor %edx,%edx | |
| 164 | mov DIR_EA_LEN(%bx),%dl | |
| 165 | add %edx,%eax # Skip extended | |
| 166 | mov $MEM_READ_BUFFER,%ebx # Read into the buffer | |
| 167 | load_loop: mov %cl,%dh | |
| 168 | cmp $MAX_READ_SEC,%cl # Truncate to max read size | |
| 169 | jbe load_notrunc | |
| 170 | mov $MAX_READ_SEC,%dh | |
| 171 | load_notrunc: sub %dh,%cl # Update count | |
| 984263bc | 172 | call read # Read it in |
| 984263bc MD |
173 | add $MAX_READ_SEC,%eax # Update LBA |
| 174 | add $MAX_READ,%ebx # Update dest addr | |
| f70f1d6b MD |
175 | testb %cl,%cl |
| 176 | jnz load_loop | |
| 984263bc | 177 | load_done: |
| 409cbc03 MD |
178 | /* |
| 179 | * Turn on the A20 address line. | |
| 180 | */ | |
| 984263bc | 181 | call seta20 # Turn A20 on |
| 409cbc03 MD |
182 | |
| 183 | /* | |
| 184 | * Relocate the loader and BTX using a very lazy | |
| 185 | * protected mode. | |
| 186 | */ | |
| 984263bc MD |
187 | mov $msg_relocate,%si # Display the |
| 188 | call putstr # relocation message | |
| 189 | mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination | |
| 190 | mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is | |
| 191 | # the start of the text | |
| 192 | # segment | |
| 193 | mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text | |
| 194 | # segment | |
| 195 | push %edi # Save entry point for later | |
| 196 | lgdt gdtdesc # setup our own gdt | |
| 197 | cli # turn off interrupts | |
| 198 | mov %cr0,%eax # Turn on | |
| 199 | or $0x1,%al # protected | |
| 200 | mov %eax,%cr0 # mode | |
| 201 | ljmp $SEL_SCODE,$pm_start # long jump to clear the | |
| 202 | # instruction pre-fetch queue | |
| 203 | .code32 | |
| 204 | pm_start: mov $SEL_SDATA,%ax # Initialize | |
| 205 | mov %ax,%ds # %ds and | |
| 206 | mov %ax,%es # %es to a flat selector | |
| 207 | rep # Relocate the | |
| 208 | movsb # text segment | |
| 209 | add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page | |
| 210 | and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment | |
| 211 | mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment | |
| 212 | rep # Relocate the | |
| 213 | movsb # data segment | |
| 214 | mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss | |
| 215 | xor %eax,%eax # zero %eax | |
| 216 | add $3,%cl # round %ecx up to | |
| 217 | shr $2,%ecx # a multiple of 4 | |
| 218 | rep # zero the | |
| 219 | stosl # bss | |
| 220 | mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader | |
| 409cbc03 MD |
221 | add $MEM_BTX_LDR_OFF,%esi # %esi -> BTX in the loader |
| 222 | mov $MEM_BTX_ORG,%edi # %edi -> where BTX needs to go | |
| 984263bc MD |
223 | movzwl 0xa(%esi),%ecx # %ecx -> length of BTX |
| 224 | rep # Relocate | |
| 225 | movsb # BTX | |
| 226 | ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM | |
| 227 | .code16 | |
| 228 | pm_16: mov $SEL_RDATA,%ax # Initialize | |
| 229 | mov %ax,%ds # %ds and | |
| 230 | mov %ax,%es # %es to a real mode selector | |
| 231 | mov %cr0,%eax # Turn off | |
| 232 | and $~0x1,%al # protected | |
| 233 | mov %eax,%cr0 # mode | |
| 234 | ljmp $0,$pm_end # Long jump to clear the | |
| 235 | # instruction pre-fetch queue | |
| 236 | pm_end: sti # Turn interrupts back on now | |
| 409cbc03 MD |
237 | |
| 238 | /* | |
| 239 | * Copy the BTX client to MEM_BTX_USR. | |
| 240 | */ | |
| 984263bc MD |
241 | xor %ax,%ax # zero %ax and set |
| 242 | mov %ax,%ds # %ds and %es | |
| 243 | mov %ax,%es # to segment 0 | |
| 409cbc03 | 244 | mov $MEM_BTX_USR,%di # Prepare to relocate |
| 984263bc MD |
245 | mov $btx_client,%si # the simple btx client |
| 246 | mov $(btx_client_end-btx_client),%cx # length of btx client | |
| 247 | rep # Relocate the | |
| 248 | movsb # simple BTX client | |
| 409cbc03 MD |
249 | |
| 250 | /* | |
| 251 | * Copy the boot[12] args to where the BTX client | |
| 252 | * can see them. | |
| 253 | */ | |
| 984263bc | 254 | mov $MEM_ARG,%si # where the args are at now |
| cacaceec | 255 | mov $MEM_BTX_USR_ARG,%di # where the args are moving to |
| 984263bc MD |
256 | mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs |
| 257 | rep # Relocate | |
| 258 | movsl # the words | |
| 409cbc03 MD |
259 | |
| 260 | /* | |
| 261 | * Save the entry point so the client can get to it | |
| 262 | * later on | |
| 263 | */ | |
| 984263bc MD |
264 | pop %eax # Restore saved entry point |
| 265 | stosl # and add it to the end of | |
| 266 | # the arguments | |
| 409cbc03 MD |
267 | /* |
| 268 | * Now we just start up BTX and let it do the rest | |
| 269 | */ | |
| 984263bc MD |
270 | mov $msg_jump,%si # Display the |
| 271 | call putstr # jump message | |
| 272 | ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point | |
| 273 | ||
| 409cbc03 MD |
274 | /* |
| 275 | * Lookup the file in the path at [SI] from the root | |
| 276 | * directory. | |
| 277 | * | |
| 278 | * Trashes: All but BX | |
| 279 | * Returns: BX = pointer to record | |
| 280 | */ | |
| 984263bc MD |
281 | lookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record |
| 282 | push %si | |
| 283 | mov $msg_lookup,%si # Display lookup message | |
| 284 | call putstr | |
| 285 | pop %si | |
| 286 | push %si | |
| 287 | call putstr | |
| 288 | mov $msg_lookup2,%si | |
| 289 | call putstr | |
| 290 | pop %si | |
| 291 | lookup_dir: lodsb # Get first char of path | |
| 292 | cmp $0,%al # Are we done? | |
| 293 | je lookup_done # Yes | |
| 294 | cmp $'/',%al # Skip path separator. | |
| 295 | je lookup_dir | |
| 296 | dec %si # Undo lodsb side effect | |
| 297 | call find_file # Lookup first path item | |
| 298 | jnc lookup_dir # Try next component | |
| 299 | mov $msg_lookupfail,%si # Not found. | |
| 300 | jmp error | |
| 301 | lookup_done: mov $msg_lookupok,%si # Success message | |
| 302 | call putstr | |
| 303 | ret | |
| 304 | ||
| 409cbc03 MD |
305 | /* |
| 306 | * Lookup file at [SI] in directory whose record is at [BX]. | |
| 307 | * | |
| 308 | * Trashes: All but returns | |
| 309 | * | |
| 310 | * Returns: CF = 0 (success) | |
| 311 | * BX = pointer to record, | |
| 312 | * SX = next path item | |
| 313 | * CF = 1 (not found) | |
| 314 | * SI = preserved | |
| 315 | */ | |
| 984263bc MD |
316 | find_file: mov DIR_EXTENT(%bx),%eax # Load extent |
| 317 | xor %edx,%edx | |
| 318 | mov DIR_EA_LEN(%bx),%dl | |
| 319 | add %edx,%eax # Skip extended attributes | |
| 320 | mov %eax,rec_lba # Save LBA | |
| 321 | mov DIR_SIZE(%bx),%eax # Save size | |
| 322 | mov %eax,rec_size | |
| 323 | xor %cl,%cl # Zero length | |
| 324 | push %si # Save | |
| 325 | ff.namelen: inc %cl # Update length | |
| 326 | lodsb # Read char | |
| 327 | cmp $0,%al # Nul? | |
| 328 | je ff.namedone # Yes | |
| 329 | cmp $'/',%al # Path separator? | |
| 330 | jnz ff.namelen # No, keep going | |
| 331 | ff.namedone: dec %cl # Adjust length and save | |
| 332 | mov %cl,name_len | |
| 333 | pop %si # Restore | |
| 334 | ff.load: mov rec_lba,%eax # Load LBA | |
| 335 | mov $MEM_DIR,%ebx # Address buffer | |
| 336 | mov $1,%dh # One sector | |
| 337 | call read # Read directory block | |
| 338 | incl rec_lba # Update LBA to next block | |
| 339 | ff.scan: mov %ebx,%edx # Check for EOF | |
| 340 | sub $MEM_DIR,%edx | |
| 341 | cmp %edx,rec_size | |
| 342 | ja ff.scan.1 | |
| 343 | stc # EOF reached | |
| 344 | ret | |
| 345 | ff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block? | |
| 346 | je ff.nextblock | |
| 347 | push %si # Save | |
| 348 | movzbw DIR_NAMELEN(%bx),%si # Find end of string | |
| 349 | ff.checkver: cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'? | |
| 350 | jb ff.checkver.1 | |
| 351 | cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'? | |
| 352 | ja ff.checkver.1 | |
| 353 | dec %si # Next char | |
| 354 | jnz ff.checkver | |
| 355 | jmp ff.checklen # All numbers in name, so | |
| 356 | # no version | |
| 357 | ff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx | |
| 358 | cmp %cx,%si # Did we find any digits? | |
| 359 | je ff.checkdot # No | |
| 360 | cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon | |
| 361 | jne ff.checkver.2 | |
| 362 | dec %si # Skip semicolon | |
| 363 | mov %si,%cx | |
| 364 | mov %cl,DIR_NAMELEN(%bx) # Adjust length | |
| 365 | jmp ff.checkdot | |
| 366 | ff.checkver.2: mov %cx,%si # Restore %si to end of string | |
| 367 | ff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot? | |
| 368 | jne ff.checklen # No | |
| 369 | decb DIR_NAMELEN(%bx) # Adjust length | |
| 370 | ff.checklen: pop %si # Restore | |
| 371 | movzbw name_len,%cx # Load length of name | |
| 372 | cmp %cl,DIR_NAMELEN(%bx) # Does length match? | |
| 373 | je ff.checkname # Yes, check name | |
| 374 | ff.nextrec: add DIR_LEN(%bx),%bl # Next record | |
| 375 | adc $0,%bh | |
| 376 | jmp ff.scan | |
| 377 | ff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size | |
| 378 | jnc ff.load # If subtract ok, keep going | |
| 379 | ret # End of file, so not found | |
| 380 | ff.checkname: lea DIR_NAME(%bx),%di # Address name in record | |
| 381 | push %si # Save | |
| 382 | repe cmpsb # Compare name | |
| f70f1d6b MD |
383 | testw %cx,%cx |
| 384 | jz ff.match # We have a winner! | |
| 984263bc MD |
385 | pop %si # Restore |
| 386 | jmp ff.nextrec # Keep looking. | |
| 387 | ff.match: add $2,%sp # Discard saved %si | |
| 388 | clc # Clear carry | |
| 389 | ret | |
| 390 | ||
| 409cbc03 MD |
391 | /* |
| 392 | * Load DH sectors starting at LBA EAX into [EBX]. No | |
| 393 | * registers are destroyed. Don't trust the BIOS, especially | |
| 394 | * with regards to the msb 16 bits of our registers. | |
| 395 | */ | |
| 396 | read: pushal # dont screw around | |
| 984263bc MD |
397 | mov %eax,edd_lba # LBA to read from |
| 398 | mov %ebx,%eax # Convert address | |
| 399 | shr $4,%eax # to segment | |
| 400 | mov %ax,edd_addr+0x2 # and store | |
| f70f1d6b | 401 | call twiddle # Entertain the user |
| 984263bc MD |
402 | mov $edd_packet,%si # Address Packet |
| 403 | mov %dh,edd_len # Set length | |
| 404 | mov drive,%dl # BIOS Device | |
| 405 | mov $0x42,%ah # BIOS: Extended Read | |
| 406 | int $0x13 # Call BIOS | |
| 984263bc | 407 | jc read.fail # Worked? |
| 15facac8 | 408 | popal |
| 984263bc | 409 | ret # Return |
| f70f1d6b MD |
410 | read.fail: cmp $ERROR_TIMEOUT,%ah |
| 411 | jne read.error | |
| 412 | ||
| 413 | # Tell the user what is going on | |
| 414 | # | |
| 415 | mov $msg_timeout,%si | |
| 416 | call putstr | |
| 417 | ||
| 418 | # If an error occurs wait a second and try again. Also | |
| 419 | # reload the packet. In early boot CDs can timeout for | |
| 420 | # a lengthy period and if we do not delay the retry | |
| 421 | # the BIOS can get seriously confused and wind up returning | |
| 422 | # endless timeouts. | |
| 423 | # | |
| 424 | movw $0200,%ax | |
| 425 | int $0x1a # returns seconds in %dh | |
| 426 | mov %dx,%ax | |
| 427 | 1: push %ax | |
| 428 | movw $0200,%ax # returns seconds in %dh | |
| 429 | int $0x1a | |
| 430 | pop %ax | |
| 431 | cmp %ah,%dh # wait for seconds to cycle | |
| 432 | je 1b | |
| 433 | popal | |
| 434 | jmp read | |
| 435 | ||
| 984263bc MD |
436 | read.error: mov %ah,%al # Save error |
| 437 | mov $hex_error,%di # Format it | |
| 438 | call hex8 # as hex | |
| 439 | mov $msg_badread,%si # Display Read error message | |
| 440 | ||
| 409cbc03 MD |
441 | /* |
| 442 | * Display error message at [SI] and halt. | |
| 443 | */ | |
| 984263bc MD |
444 | error: call putstr # Display message |
| 445 | halt: hlt | |
| 446 | jmp halt # Spin | |
| 447 | ||
| 409cbc03 MD |
448 | /* |
| 449 | * Display a null-terminated string. | |
| 450 | * | |
| 451 | * Trashes: AX, SI | |
| 452 | */ | |
| 15facac8 | 453 | putstr: |
| 984263bc MD |
454 | putstr.load: lodsb # load %al from %ds:(%si) |
| 455 | test %al,%al # stop at null | |
| 456 | jnz putstr.putc # if the char != null, output it | |
| 984263bc MD |
457 | ret # return when null is hit |
| 458 | putstr.putc: call putc # output char | |
| 459 | jmp putstr.load # next char | |
| 460 | ||
| 409cbc03 MD |
461 | /* |
| 462 | * Display a single char(%al). Don't trust the bios to save | |
| 463 | * our regs. | |
| 464 | */ | |
| 15facac8 MD |
465 | putc: pushal |
| 466 | mov $0x7,%bx # attribute for output | |
| 984263bc MD |
467 | mov $0xe,%ah # BIOS: put_char |
| 468 | int $0x10 # call BIOS, print char in %al | |
| 15facac8 | 469 | popal |
| 984263bc MD |
470 | ret # Return to caller |
| 471 | ||
| 409cbc03 MD |
472 | /* |
| 473 | * Output the "twiddle" | |
| 474 | */ | |
| 984263bc MD |
475 | twiddle: push %ax # Save |
| 476 | push %bx # Save | |
| 477 | mov twiddle_index,%al # Load index | |
| 2ce963b6 | 478 | mov $twiddle_chars,%bx # Address table |
| 984263bc MD |
479 | inc %al # Next |
| 480 | and $3,%al # char | |
| 15facac8 | 481 | mov %al,twiddle_index # Save index for next call |
| 984263bc MD |
482 | xlat # Get char |
| 483 | call putc # Output it | |
| 484 | mov $8,%al # Backspace | |
| 485 | call putc # Output it | |
| 486 | pop %bx # Restore | |
| 487 | pop %ax # Restore | |
| 488 | ret | |
| 489 | ||
| 409cbc03 | 490 | /* |
| 65f5a4df MD |
491 | * Enable A20. Put upper limit on amount of time we wait for the |
| 492 | * keyboard controller to get ready (65K x ISA access time). If | |
| 493 | * we wait more than that amount it's likely that the hardware | |
| 494 | * is legacy-free and simply doesn't have keyboard controller | |
| 495 | * and don't need enabling A20 at all. | |
| 409cbc03 | 496 | */ |
| 984263bc | 497 | seta20: cli # Disable interrupts |
| 65f5a4df MD |
498 | xor %cx,%cx # Clear |
| 499 | seta20.1: inc %cx # Increment, overflow? | |
| 500 | jz seta20.3 # Yes | |
| 501 | in $0x64,%al # Get status | |
| 984263bc MD |
502 | test $0x2,%al # Busy? |
| 503 | jnz seta20.1 # Yes | |
| 504 | mov $0xd1,%al # Command: Write | |
| 505 | out %al,$0x64 # output port | |
| 506 | seta20.2: in $0x64,%al # Get status | |
| 507 | test $0x2,%al # Busy? | |
| 508 | jnz seta20.2 # Yes | |
| 509 | mov $0xdf,%al # Enable | |
| 510 | out %al,$0x60 # A20 | |
| 65f5a4df | 511 | seta20.3: sti # Enable interrupts |
| 984263bc MD |
512 | ret # To caller |
| 513 | ||
| 409cbc03 MD |
514 | /* |
| 515 | * Convert AL to hex, saving the result to [EDI]. | |
| 516 | */ | |
| 984263bc MD |
517 | hex8: pushl %eax # Save |
| 518 | shrb $0x4,%al # Do upper | |
| 519 | call hex8.1 # 4 | |
| 520 | popl %eax # Restore | |
| 521 | hex8.1: andb $0xf,%al # Get lower 4 | |
| 522 | cmpb $0xa,%al # Convert | |
| 523 | sbbb $0x69,%al # to hex | |
| 524 | das # digit | |
| 525 | orb $0x20,%al # To lower case | |
| 526 | stosb # Save char | |
| 527 | ret # (Recursive) | |
| 528 | ||
| 409cbc03 MD |
529 | /* |
| 530 | * BTX client to start btxldr | |
| 531 | */ | |
| 984263bc | 532 | .code32 |
| 409cbc03 | 533 | btx_client: mov $(MEM_BTX_USR_ARG-MEM_BTX_USR+MEM_ARG_SIZE-4), %esi |
| 984263bc MD |
534 | # %ds:(%esi) -> end |
| 535 | # of boot[12] args | |
| 536 | mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push | |
| 537 | std # Go backwards | |
| 538 | push_arg: lodsl # Read argument | |
| 539 | push %eax # Push it onto the stack | |
| 540 | loop push_arg # Push all of the arguments | |
| 541 | cld # In case anyone depends on this | |
| 409cbc03 | 542 | pushl MEM_BTX_USR_ARG-MEM_BTX_USR+MEM_ARG_SIZE # Entry point of |
| 984263bc MD |
543 | # the loader |
| 544 | push %eax # Emulate a near call | |
| 409cbc03 | 545 | mov $0x1,%eax # "exec" system call |
| 984263bc MD |
546 | int $INT_SYS # BTX system call |
| 547 | btx_client_end: | |
| 548 | .code16 | |
| 549 | ||
| 550 | .p2align 4 | |
| 409cbc03 MD |
551 | |
| 552 | /* | |
| 553 | * Global descriptor table. | |
| 554 | */ | |
| 984263bc MD |
555 | gdt: .word 0x0,0x0,0x0,0x0 # Null entry |
| 556 | .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA | |
| 557 | .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA | |
| 558 | .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) | |
| 559 | .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) | |
| 560 | gdt.1: | |
| 409cbc03 MD |
561 | |
| 562 | /* | |
| 563 | * Pseudo-descriptors. | |
| 564 | */ | |
| 984263bc MD |
565 | gdtdesc: .word gdt.1-gdt-1 # Limit |
| 566 | .long gdt # Base | |
| 409cbc03 MD |
567 | |
| 568 | /* | |
| 569 | * EDD Packet | |
| 570 | */ | |
| 984263bc MD |
571 | edd_packet: .byte 0x10 # Length |
| 572 | .byte 0 # Reserved | |
| 573 | edd_len: .byte 0x0 # Num to read | |
| 574 | .byte 0 # Reserved | |
| 575 | edd_addr: .word 0x0,0x0 # Seg:Off | |
| 576 | edd_lba: .quad 0x0 # LBA | |
| 577 | ||
| 578 | drive: .byte 0 | |
| 579 | ||
| 409cbc03 MD |
580 | /* |
| 581 | * State for searching dir | |
| 582 | */ | |
| 984263bc MD |
583 | rec_lba: .long 0x0 # LBA (adjusted for EA) |
| 584 | rec_size: .long 0x0 # File size | |
| 585 | name_len: .byte 0x0 # Length of current name | |
| 586 | ||
| 587 | twiddle_index: .byte 0x0 | |
| 588 | ||
| 589 | msg_welcome: .asciz "CD Loader 1.01\r\n\n" | |
| 590 | msg_bootinfo: .asciz "Building the boot loader arguments\r\n" | |
| 591 | msg_relocate: .asciz "Relocating the loader and the BTX\r\n" | |
| 592 | msg_jump: .asciz "Starting the BTX loader\r\n" | |
| 593 | msg_badread: .ascii "Read Error: 0x" | |
| f70f1d6b | 594 | hex_error: .asciz "00\r\n" |
| 984263bc MD |
595 | msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n" |
| 596 | msg_lookup: .asciz "Looking up " | |
| 597 | msg_lookup2: .asciz "... " | |
| 598 | msg_lookupok: .asciz "Found\r\n" | |
| 599 | msg_lookupfail: .asciz "File not found\r\n" | |
| 600 | msg_load2big: .asciz "File too big\r\n" | |
| f70f1d6b | 601 | msg_timeout: .asciz "Drive not ready, retry\r\n" |
| 984263bc MD |
602 | loader_path: .asciz "/BOOT/LOADER" |
| 603 | twiddle_chars: .ascii "|/-\\" | |
| 604 |