[CONTROL] Use the new header with SPDX license identifier.
[reactos.git] / reactos / boot / freeldr / bootsect / isoboot.S
1 /*
2 * PROJECT: ReactOS Boot Sector for ISO file system (based on ISOLINUX)
3 * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
4 * PROGRAMMERS: H. Peter Anvin
5 * Michael K. Ter Louw
6 * Eric Kohl
7 * Timo Kreuzer <timo.kreuzer@reactos.org>
8 * Colin Finck <colin@reactos.org>
9 *
10 *****************************************************************************
11 *
12 * isolinux.asm
13 *
14 * A program to boot Linux kernels off a CD-ROM using the El Torito
15 * boot standard in "no emulation" mode, making the entire filesystem
16 * available. It is based on the SYSLINUX boot loader for MS-DOS
17 * floppies.
18 *
19 * Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
20 * Copyright 2009 Intel Corporation *author: H. Peter Anvin
21 *
22 * This program is free software *you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
25 * Boston MA 02111-1307, USA *either version 2 of the License, or
26 * (at your option) any later version *incorporated herein by reference.
27 *
28 *****************************************************************************/
29
30 /* INCLUDES ******************************************************************/
31 #include <asm.inc>
32 #include <freeldr/include/arch/pc/x86common.h>
33
34 #ifndef ROS_REGTEST
35 #define WAIT_FOR_KEY
36 #endif
37
38
39 .code16
40 ASSUME CS:.text, DS:.text, ES:.text
41
42 /* CONSTANTS ******************************************************************/
43 BIOS_timer = HEX(046C) // Timer ticks (1 word)
44 BIOS_magic = HEX(0472) // BIOS reset magic (1 word)
45
46 // Memory below this point is reserved for the BIOS and the MBR
47 trackbuf = HEX(1000) // Track buffer goes here (8192 bytes)
48 trackbufsize = 8192 // trackbuf ends at 3000h
49
50 // struct open_file_t
51 file_sector = 0 // Sector pointer (0 = structure free)
52 file_bytesleft = 4 // Number of bytes left
53 file_left = 8 // Number of sectors left
54 // Another unused DWORD follows here in ISOLINUX
55 #define open_file_t_size 16
56
57 // struct dir_t
58 dir_lba = 0 // Directory start (LBA)
59 dir_len = 4 // Length in bytes
60 dir_clust = 8 // Length in clusters
61 #define dir_t_size 12
62
63 MAX_OPEN_LG2 = 2 // log2(Max number of open files)
64 MAX_OPEN = 4
65 SECTOR_SHIFT = 11 // 2048 bytes/sector (El Torito requirement)
66 SECTOR_SIZE = 2048
67 retry_count = 6 // How patient are we with the BIOS?
68
69 /* UNINITIALIZED VARIABLES ****************************************************/
70 absolute HEX(5000) // Here we keep our BSS stuff
71
72 resb ISOFileName, 64 // ISO filename canonicalization buffer
73 resb ISOFileNameEnd, 1
74 resb CurrentDir, dir_t_size // Current directory
75 resb RootDir, dir_t_size // Root directory
76 resb DiskSys, 2 // Last INT 13h call
77 resb GetlinsecPtr, 2 // The sector-read pointer
78 resb DiskError, 1 // Error code for disk I/O
79 resb DriveNumber, 1 // CD-ROM BIOS drive number
80 resb ISOFlags, 1 // Flags for ISO directory search
81 resb RetryCount, 1 // Used for disk access retries
82
83 //align open_file_t_size
84 absolute HEX(5070)
85 resb Files, (MAX_OPEN * open_file_t_size)
86
87
88 /* ENTRY POINTS ***************************************************************/
89
90 // Entry point when booted from CD (El Torito standard)
91 start:
92 mov bx, offset getlinsec_cdrom
93 // Fall through
94
95 start_common:
96 // Set up our stack and a flat addressing model.
97 cli
98 xor ax, ax
99 mov ss, ax
100 mov sp, offset start
101 mov ds, ax
102 mov es, ax
103 mov fs, ax
104 mov gs, ax
105 sti
106
107 // Our boot sector has been loaded to address 0x7C00.
108 // Relocate our 2048 bytes boot sector to the given base address (should be 0x7000).
109 cld
110 mov cx, 2048 / 4
111 mov si, HEX(7C00)
112 mov di, offset start
113 rep movsd
114
115 ljmp16 0, relocated // jump into relocated code
116
117 .org 64
118 hybrid_signature:
119 .long HEX(7078c0fb)
120
121 // Entry point when booted through ISOMBR from a drive (isohybrid mode)
122 start_hybrid:
123 mov bx, offset getlinsec_ebios
124 jmp start_common
125
126 relocated:
127 // Save our passed variables (BX from the entry point, DL from the BIOS) before anybody clobbers the registers.
128 mov word ptr ds:[GetlinsecPtr], bx
129 mov byte ptr ds:[DriveNumber], dl
130
131 // Make sure the keyboard buffer is empty
132 call pollchar_and_empty
133
134 // If we're booting in hybrid mode and our boot drive is the first HDD (drive 80h),
135 // we have no other option than booting into SETUPLDR.
136 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
137 jne .read_mbr
138 cmp byte ptr ds:[DriveNumber], HEX(80)
139 je .boot_setupldr
140
141 .read_mbr:
142 // Read the first sector (MBR) from the first hard disk (drive 80h) to 7C00h.
143 // If we then decide to boot from HDD, we already have it at the right place.
144 // In case of an error (indicated by the Carry Flag), just boot SETUPLDR from our ReactOS medium.
145 mov ax, HEX(0201)
146 mov dx, HEX(0080)
147 mov cx, HEX(0001)
148 mov bx, HEX(7C00)
149 call int13
150 jc .boot_setupldr
151
152 // Verify the signature of the read MBR.
153 // If it's invalid, there is probably no OS installed and we just boot SETUPLDR from our ReactOS medium.
154 mov ax, word ptr ds:[HEX(7C00)+510]
155 cmp ax, HEX(AA55)
156 jne .boot_setupldr
157
158 #ifdef WAIT_FOR_KEY
159 // We could either boot from the ReactOS medium or from hard disk. Let the user decide!
160 // Display the 'Press key' message.
161 call crlf_early
162 mov si, offset presskey_msg
163 call writestr_early
164
165 // Count down 5 seconds.
166 mov cx, 5
167
168 .next_second:
169 // Count in seconds using the BIOS Timer, which runs roughly at 19 ticks per second.
170 // Load its value plus one second into EAX for comparison later.
171 mov eax, ds:[BIOS_timer]
172 add eax, 19
173
174 .poll_again:
175 // Check for a keypress, boot SETUPLDR from our ReactOS medium if a key was pressed.
176 call pollchar_and_empty
177 jnz .boot_setupldr
178
179 // Check if another second has passed (in BIOS Timer ticks).
180 mov ebx, ds:[BIOS_timer]
181 cmp eax, ebx
182 jnz .poll_again
183
184 // Another second has passed, so print the dot and decrement the second counter.
185 // If the user hasn't pressed a key after the entire 5 seconds have elapsed, just boot from the first hard disk.
186 mov si, offset dot_msg
187 call writestr_early
188 dec cx
189 jz .boot_harddisk
190 jmp .next_second
191 #endif
192
193 .boot_harddisk:
194 // Restore a clean context for the hard disk MBR and boot the already loaded MBR.
195 call crlf_early
196 mov ax, cs
197 mov ds, ax
198 mov es, ax
199 mov fs, ax
200 mov gs, ax
201 mov dx, HEX(0080)
202
203 ljmp16 0, HEX(7C00)
204
205 .boot_setupldr:
206 #ifdef WAIT_FOR_KEY
207 call crlf_early
208 call crlf_early
209 #endif
210
211 // The BIOS gave us a boot drive number, so in a perfect world we could just use that one now.
212 // Unfortunately, there are many broken BIOSes around, which is why ISOLINUX verifies it and applies some hacks if the number is wrong.
213 // Let's do exactly the same here to achieve maximum compatibility.
214
215 // Don't do this if we are running in hybrid mode.
216 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
217 je found_drive
218
219 // Use the INT 13 function 4B01h (Get Disk Emulation Status) to fetch the El Torito Spec Packet.
220 // We can use this information to verify that our passed boot drive number really belongs to our CD.
221 mov ax, HEX(4B01)
222 mov dl, byte ptr ds:[DriveNumber]
223 mov si, offset spec_packet
224 call int13
225
226 // If this INT 13 function yields an error, we may be on a broken AWARD BIOS.
227 // Check this and patch if possible.
228 jc award_hack
229
230 // Check that our passed boot drive number and the number in the Spec Packet match.
231 // If not, try some workarounds to find our drive anyway.
232 mov dl, byte ptr ds:[DriveNumber]
233 cmp byte ptr ds:[sp_drive], dl
234 jne spec_query_failed
235
236 found_drive:
237 // Clear Files structures
238 mov di, Files
239 mov cx, (MAX_OPEN*open_file_t_size)/4
240 xor eax, eax
241 rep stosd
242
243 // Read the entire 2K-sized ISO9660 Primary Volume Descriptor at sector 16 (32K).
244 // This calculation only holds for single-session ISOs, but we should never encounter anything else.
245 mov eax, 16
246 mov bx, trackbuf
247 call getonesec
248
249 // Read the LBA address (offset 2 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor).
250 mov eax, dword ptr ds:[trackbuf+156+2]
251 mov dword ptr ds:[RootDir+dir_lba], eax
252 mov dword ptr ds:[CurrentDir+dir_lba], eax
253
254 // Read the data length (offset 10 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor).
255 mov eax, dword ptr ds:[trackbuf+156+10]
256 mov dword ptr ds:[RootDir+dir_len], eax
257 mov dword ptr ds:[CurrentDir+dir_len], eax
258
259 // Calculate the number of clusters and write that to our RootDir and CurrentDir structures.
260 add eax, SECTOR_SIZE-1
261 shr eax, SECTOR_SHIFT
262 mov dword ptr ds:[RootDir+dir_clust],eax
263 mov dword ptr ds:[CurrentDir+dir_clust],eax
264
265 // Look for the "LOADER" directory (directory is indicated by AL = 2 when using searchdir_iso).
266 mov di, offset loader_dir
267 mov al, 2
268 call searchdir_iso
269 jnz .dir_found
270
271 // No directory was found, so bail out with an error message.
272 mov si, offset no_dir_msg
273 call writemsg
274 jmp kaboom
275
276 .dir_found:
277 // The directory was found, so update the information in our CurrentDir structure.
278 // Free the file pointer entry at SI in the process.
279 mov dword ptr ds:[CurrentDir+dir_len], eax
280 mov eax, dword ptr ds:[si+file_left]
281 mov dword ptr ds:[CurrentDir+dir_clust], eax
282 xor eax, eax
283 xchg eax, dword ptr ds:[si+file_sector]
284 mov dword ptr ds:[CurrentDir+dir_lba], eax
285
286 // Look for the "SETUPLDR.SYS" file.
287 mov di, offset setupldr_sys
288 call searchdir
289 jnz .setupldr_found
290
291 // The SETUPLDR file was not found, so bail out with an error message.
292 mov si, offset no_setupldr_msg
293 call writemsg
294 jmp kaboom
295
296 .setupldr_found:
297 // Calculate the rounded up number of 2K sectors that need to be read.
298 mov ecx, eax
299 shr ecx, SECTOR_SHIFT
300 test eax, HEX(7FF)
301 jz .load_setupldr
302 inc ecx
303
304 .load_setupldr:
305 // Load the entire SETUPLDR.SYS (parameter CX = FFFFh) to its designated base address FREELDR_BASE.
306 // Using a high segment address with offset 0 instead of segment 0 with offset FREELDR_BASE apparently increases compatibility with some BIOSes.
307 mov bx, FREELDR_BASE / 16
308 mov es, bx
309 xor ebx, ebx
310 mov cx, HEX(FFFF)
311 call getfssec
312
313 // Pass two parameters to SETUPLDR:
314 // DL = BIOS Drive Number
315 // DH = Boot Partition (0 for HDD booting in hybrid mode, FFh for CD booting)
316 movzx dx, byte ptr ds:[DriveNumber]
317 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
318 je .jump_to_setupldr
319 mov dh, HEX(FF)
320
321 .jump_to_setupldr:
322 // Transfer execution to the bootloader.
323 ljmp16 0, FREELDR_BASE
324
325
326 /* FUNCTIONS *****************************************************************/
327
328 ///////////////////////////////////////////////////////////////////////////////
329 // Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de
330 ///////////////////////////////////////////////////////////////////////////////
331 //
332 // There is a problem with certain versions of the AWARD BIOS ...
333 // the boot sector will be loaded and executed correctly, but, because the
334 // int 13 vector points to the wrong code in the BIOS, every attempt to
335 // load the spec packet will fail. We scan for the equivalent of
336 //
337 // mov ax,0201h
338 // mov bx,7c00h
339 // mov cx,0006h
340 // mov dx,0180h
341 // pushf
342 // call <direct far>
343 //
344 // and use <direct far> as the new vector for int 13. The code above is
345 // used to load the boot code into ram, and there should be no reason
346 // for anybody to change it now or in the future. There are no opcodes
347 // that use encodings relativ to IP, so scanning is easy. If we find the
348 // code above in the BIOS code we can be pretty sure to run on a machine
349 // with an broken AWARD BIOS ...
350 //
351 ///////////////////////////////////////////////////////////////////////////////
352 award_oldint13:
353 .long 0
354 award_string:
355 .byte HEX(0b8),1,2,HEX(0bb),0,HEX(7c),HEX(0b9),6,0,HEX(0ba),HEX(80),1,HEX(09c),HEX(09a)
356
357 award_hack:
358 mov si, offset spec_err_msg // Moved to this place from
359 call writemsg // spec_query_failed
360
361 mov eax, dword ptr ds:[HEX(13)*4]
362 mov dword ptr ds:[award_oldint13], eax
363
364 push es
365 mov ax, HEX(F000) // ES = BIOS Seg
366 mov es, ax
367 cld
368 xor di, di // start at ES:DI = f000:0
369 award_loop:
370 push di // save DI
371 mov si, offset award_string // scan for award_string
372 mov cx, 7 // length of award_string = 7dw
373 repz cmpsw // compare
374 pop di // restore DI
375 jcxz award_found // jmp if found
376 inc di // not found, inc di
377 jno award_loop
378
379 award_failed:
380 pop es // No, not this way :-((
381 award_fail2:
382 mov eax, dword ptr ds:[award_oldint13] // restore the original int
383 or eax, eax // 13 vector if there is one
384 jz spec_query_failed // and try other workarounds
385 mov dword ptr ds:[HEX(13)*4], eax
386 jmp spec_query_failed
387
388 award_found:
389 mov eax, dword ptr es:[di+HEX(0e)] // load possible int 13 addr
390 pop es // restore ES
391
392 cmp eax, dword ptr ds:[award_oldint13] // give up if this is the
393 jz award_failed // active int 13 vector,
394 mov dword ptr ds:[HEX(13)*4], eax // otherwise change 0:13h*4
395
396 mov ax, HEX(4B01) // try to read the spec packet
397 mov dl, byte ptr ds:[DriveNumber] // now ... it should not fail
398 mov si, offset spec_packet // any longer
399 int HEX(13)
400 jc award_fail2
401
402 jmp found_drive // and leave error recovery code
403 ///////////////////////////////////////////////////////////////////////////////
404 // End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de
405 ///////////////////////////////////////////////////////////////////////////////
406
407
408 // INT 13h, AX=4B01h, DL=<passed in value> failed.
409 // Try to scan the entire 80h-FFh from the end.
410 spec_query_failed:
411 // some code moved to BrokenAwardHack
412
413 mov dl, HEX(FF)
414
415 .test_loop:
416 pusha
417 mov ax, HEX(4B01)
418 mov si, offset spec_packet
419 mov byte ptr ds:[si], HEX(13) // Size of buffer
420 call int13
421 popa
422 jc .still_broken
423
424 mov si, offset maybe_msg
425 call writemsg
426 mov al, dl
427 call writehex2
428 call crlf_early
429
430 cmp byte ptr ds:[sp_drive], dl
431 jne .maybe_broken
432
433 // Okay, good enough...
434 mov si, offset alright_msg
435 call writemsg
436 .found_drive0:
437 mov byte ptr ds:[DriveNumber], dl
438 .found_drive:
439 jmp found_drive
440
441 // Award BIOS 4.51 apparently passes garbage in sp_drive,
442 // but if this was the drive number originally passed in
443 // DL then consider it "good enough"
444 .maybe_broken:
445 mov al, byte ptr ds:[DriveNumber]
446 cmp al, dl
447 je .found_drive
448
449 // Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02
450 // passes garbage in sp_drive, and the drive number originally
451 // passed in DL does not have 80h bit set.
452 or al, HEX(80)
453 cmp al, dl
454 je .found_drive0
455
456 .still_broken:
457 dec dx
458 cmp dl, HEX(80)
459 jnb .test_loop
460
461 // No spec packet anywhere. Some particularly pathetic
462 // BIOSes apparently don't even implement function
463 // 4B01h, so we can't query a spec packet no matter
464 // what. If we got a drive number in DL, then try to
465 // use it, and if it works, then well...
466 mov dl, byte ptr ds:[DriveNumber]
467 cmp dl, HEX(81) // Should be 81-FF at least
468 jb fatal_error // If not, it's hopeless
469
470 // Write a warning to indicate we're on *very* thin ice now
471 mov si, offset nospec_msg
472 call writemsg
473 mov al, dl
474 call writehex2
475 call crlf_early
476 jmp .found_drive // Pray that this works...
477
478 fatal_error:
479 mov si, offset nothing_msg
480 call writemsg
481
482 .norge:
483 jmp short .norge
484
485 //
486 // searchdir:
487 //
488 // Open a file
489 //
490 // On entry:
491 // DS:DI = filename
492 // If successful:
493 // ZF clear
494 // SI = file pointer
495 // EAX = file length in bytes
496 // If unsuccessful
497 // ZF set
498 //
499 // Assumes CS == DS == ES, and trashes BX and CX.
500 //
501 // searchdir_iso is a special entry point for ISOLINUX only. In addition
502 // to the above, searchdir_iso passes a file flag mask in AL. This is useful
503 // for searching for directories.
504 //
505 alloc_failure:
506 xor ax, ax // ZF <- 1
507 ret
508
509 searchdir:
510 xor al, al
511 searchdir_iso:
512 mov byte ptr ds:[ISOFlags], al
513 call allocate_file // Temporary file structure for directory
514 jnz alloc_failure
515 push es
516 push ds
517 pop es // ES = DS
518 mov si, offset CurrentDir
519 cmp byte ptr ds:[di], '/' // If filename begins with slash
520 jne .not_rooted
521 inc di // Skip leading slash
522 mov si, offset RootDir // Reference root directory instead
523 .not_rooted:
524 mov eax, dword ptr ds:[si+dir_clust]
525 mov dword ptr ds:[bx+file_left], eax
526 shl eax, SECTOR_SHIFT
527 mov dword ptr ds:[bx+file_bytesleft], eax
528 mov eax, dword ptr ds:[si+dir_lba]
529 mov dword ptr ds:[bx+file_sector], eax
530 mov edx, dword ptr ds:[si+dir_len]
531
532 .look_for_slash:
533 mov ax, di
534 .scan:
535 mov cl, byte ptr ds:[di]
536 inc di
537 and cl, cl
538 jz .isfile
539 cmp cl, '/'
540 jne .scan
541 mov byte ptr ds:[di-1], 0 // Terminate at directory name
542 mov cl, 2 // Search for directory
543 xchg cl, byte ptr ds:[ISOFlags]
544
545 push di // Save these...
546 push cx
547
548 // Create recursion stack frame...
549 push offset .resume // Where to "return" to
550 push es
551 .isfile:
552 xchg ax, di
553
554 .getsome:
555 // Get a chunk of the directory
556 // This relies on the fact that ISOLINUX doesn't change SI
557 mov si, trackbuf
558 pushad
559 xchg bx, si
560 mov cx, word ptr ds:[BufSafe]
561 call getfssec
562 popad
563
564 .compare:
565 movzx eax, byte ptr ds:[si] // Length of directory entry
566 cmp al, 33
567 jb .next_sector
568 mov cl, byte ptr ds:[si+25]
569 xor cl, byte ptr ds:[ISOFlags]
570 test cl, HEX(8E) // Unwanted file attributes!
571 jnz .not_file
572 pusha
573 movzx cx, byte ptr ds:[si+32] // File identifier length
574 add si, 33 // File identifier offset
575 call iso_compare_names
576 popa
577 je .success
578 .not_file:
579 sub edx, eax // Decrease bytes left
580 jbe .failure
581 add si, ax // Advance pointer
582
583 .check_overrun:
584 // Did we finish the buffer?
585 cmp si, trackbuf+trackbufsize
586 jb .compare // No, keep going
587
588 jmp short .getsome // Get some more directory
589
590 .next_sector:
591 // Advance to the beginning of next sector
592 lea ax, [si+SECTOR_SIZE-1]
593 and ax, not (SECTOR_SIZE-1)
594 sub ax, si
595 jmp short .not_file // We still need to do length checks
596
597 .failure:
598 xor eax, eax // ZF = 1
599 mov dword ptr ds:[bx+file_sector], eax
600 pop es
601 ret
602
603 .success:
604 mov eax, dword ptr ds:[si+2] // Location of extent
605 mov dword ptr ds:[bx+file_sector], eax
606 mov eax, dword ptr ds:[si+10] // Data length
607 mov dword ptr ds:[bx+file_bytesleft], eax
608 push eax
609 add eax, SECTOR_SIZE-1
610 shr eax, SECTOR_SHIFT
611 mov dword ptr ds:[bx+file_left], eax
612 pop eax
613 jz .failure // Empty file?
614 // ZF = 0
615 mov si, bx
616 pop es
617 ret
618
619 .resume:
620 // We get here if we were only doing part of a lookup
621 // This relies on the fact that .success returns bx == si
622 xchg edx, eax // Directory length in edx
623 pop cx // Old ISOFlags
624 pop di // Next filename pointer
625 mov byte ptr ds:[di-1], '/' // Restore slash
626 mov byte ptr ds:[ISOFlags], cl // Restore the flags
627 jz .failure // Did we fail? If so fail for real!
628 jmp .look_for_slash // Otherwise, next level
629
630 //
631 // allocate_file: Allocate a file structure
632 //
633 // If successful:
634 // ZF set
635 // BX = file pointer
636 // In unsuccessful:
637 // ZF clear
638 //
639 allocate_file:
640 push cx
641 mov bx, Files
642 mov cx, MAX_OPEN
643 .check:
644 cmp dword ptr ds:[bx], 0
645 je .found
646 add bx, open_file_t_size // ZF = 0
647 loop .check
648 // ZF = 0 if we fell out of the loop
649 .found:
650 pop cx
651 ret
652
653 //
654 // iso_compare_names:
655 // Compare the names DS:SI and DS:DI and report if they are
656 // equal from an ISO 9660 perspective. SI is the name from
657 // the filesystem; CX indicates its length, and ';' terminates.
658 // DI is expected to end with a null.
659 //
660 // Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
661 //
662 iso_compare_names:
663 // First, terminate and canonicalize input filename
664 push di
665 mov di, offset ISOFileName
666 .canon_loop:
667 jcxz .canon_end
668 lodsb
669 dec cx
670 cmp al, ';'
671 je .canon_end
672 and al, al
673 je .canon_end
674 stosb
675 cmp di, offset ISOFileNameEnd-1 // Guard against buffer overrun
676 jb .canon_loop
677 .canon_end:
678 cmp di, ISOFileName
679 jbe .canon_done
680 cmp byte ptr ds:[di-1], '.' // Remove terminal dots
681 jne .canon_done
682 dec di
683 jmp short .canon_end
684 .canon_done:
685 mov byte ptr ds:[di], 0 // Null-terminate string
686 pop di
687 mov si, ISOFileName
688 .compare2:
689 lodsb
690 mov ah, byte ptr ds:[di]
691 inc di
692 and ax, ax
693 jz .success2 // End of string for both
694 and al, al // Is either one end of string?
695 jz .failure2 // If so, failure
696 and ah, ah
697 jz .failure2
698 or ax, HEX(2020) // Convert to lower case
699 cmp al, ah
700 je .compare2
701 .failure2:
702 and ax, ax // ZF = 0 (at least one will be nonzero)
703 .success2:
704 ret
705
706 //
707 // getfssec: Get multiple clusters from a file, given the file pointer.
708 //
709 // On entry:
710 // ES:BX -> Buffer
711 // SI -> File pointer
712 // CX -> Cluster count
713 // On exit:
714 // SI -> File pointer (or 0 on EOF)
715 // CF = 1 -> Hit EOF
716 // ECX -> Bytes actually read
717 //
718 getfssec:
719 push ds
720 push cs
721 pop ds // DS <- CS
722
723 movzx ecx, cx
724 cmp ecx, dword ptr ds:[si+file_left]
725 jna .ok_size
726 mov ecx, dword ptr ds:[si+file_left]
727 .ok_size:
728 pushad
729 mov eax, dword ptr ds:[si+file_sector]
730 mov bp, cx
731 call getlinsec
732 popad
733
734 // ECX[31:16] == 0 here...
735 add dword ptr ds:[si+file_sector], ecx
736 sub dword ptr ds:[si+file_left], ecx
737 shl ecx, SECTOR_SHIFT // Convert to bytes
738 cmp ecx, dword ptr ds:[si+file_bytesleft]
739 jb .not_all
740 mov ecx, dword ptr ds:[si+file_bytesleft]
741 .not_all:
742 sub dword ptr ds:[si+file_bytesleft], ecx
743 jnz .ret // CF = 0 in this case...
744 push eax
745 xor eax, eax
746 mov dword ptr ds:[si+file_sector], eax // Unused
747 mov si, ax
748 pop eax
749 stc
750 .ret:
751 pop ds
752 ret
753
754 //
755 // Information message (DS:SI) output
756 // Prefix with "ISOBOOT: "
757 //
758 writemsg:
759 push ax
760 push si
761 mov si, offset isoboot_str
762 call writestr_early
763 pop si
764 call writestr_early
765 pop ax
766 ret
767
768 writestr_early:
769 pushfd
770 pushad
771 .top:
772 lodsb
773 and al, al
774 jz .end_writestr
775 call writechr
776 jmp short .top
777 .end_writestr:
778 popad
779 popfd
780 ret
781
782 crlf_early:
783 push ax
784 mov al, 13
785 call writechr
786 mov al, 10
787 call writechr
788 pop ax
789 ret
790
791 //
792 // writechr: Write a character to the screen.
793 //
794 writechr:
795 pushfd
796 pushad
797 mov ah, HEX(0E)
798 xor bx, bx
799 int HEX(10)
800 popad
801 popfd
802 ret
803
804 //
805 // int13: save all the segment registers and call INT 13h.
806 // Some CD-ROM BIOSes have been found to corrupt segment registers
807 // and/or disable interrupts.
808 //
809 int13:
810 pushf
811 push bp
812 push ds
813 push es
814 push fs
815 push gs
816 int HEX(13)
817 mov bp, sp
818 setc byte ptr ds:[bp+10] // Propagate CF to the caller
819 pop gs
820 pop fs
821 pop es
822 pop ds
823 pop bp
824 popf
825 ret
826
827 //
828 // Get one sector. Convenience entry point.
829 //
830 getonesec:
831 mov bp, 1
832 // Fall through to getlinsec
833
834 //
835 // Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
836 //
837 getlinsec:
838 jmp word ptr cs:[GetlinsecPtr]
839
840 //
841 // getlinsec_ebios:
842 //
843 // getlinsec implementation for floppy/HDD EBIOS (EDD)
844 //
845 getlinsec_ebios:
846 xor edx, edx
847 shld edx, eax, 2
848 shl eax, 2 // Convert to HDD sectors
849 shl bp, 2
850
851 .loop_ebios:
852 push bp // Sectors left
853 .retry2:
854 call maxtrans // Enforce maximum transfer size
855 movzx edi, bp // Sectors we are about to read
856 mov cx, retry_count
857 .retry:
858 // Form DAPA on stack
859 push edx
860 push eax
861 push es
862 push bx
863 push di
864 push 16
865 mov si, sp
866 pushad
867 mov dl, byte ptr ds:[DriveNumber]
868 push ds
869 push ss
870 pop ds // DS <- SS
871 mov ah, HEX(42) // Extended Read
872 call int13
873 pop ds
874 popad
875 lea sp, [si+16] // Remove DAPA
876 jc .error_ebios
877 pop bp
878 add eax, edi // Advance sector pointer
879 adc edx, 0
880 sub bp, di // Sectors left
881 shl di, 9 // 512-byte sectors
882 add bx, di // Advance buffer pointer
883 jnc .no_overflow // Check if we have read more than 64K and need to adjust ES
884 mov di, es
885 add di, HEX(1000) // Adjust segment by 64K (1000h * 16 = 10000h = 64K + 1)
886 mov es, di
887 .no_overflow:
888 and bp, bp
889 jnz .loop_ebios
890
891 ret
892
893 .error_ebios:
894 pushad // Try resetting the device
895 xor ax, ax
896 mov dl, byte ptr ds:[DriveNumber]
897 call int13
898 popad
899 loop .retry // CX-- and jump if not zero
900
901 // Total failure.
902 jmp kaboom
903
904 //
905 // Truncate BP to MaxTransfer
906 //
907 maxtrans:
908 cmp bp, word ptr ds:[MaxTransfer]
909 jna .ok
910 mov bp, word ptr ds:[MaxTransfer]
911 .ok:
912 ret
913
914 //
915 // This is the variant we use for real CD-ROMs:
916 // LBA, 2K sectors, some special error handling.
917 //
918 getlinsec_cdrom:
919 mov si, offset dapa // Load up the DAPA
920 mov word ptr ds:[si+4], bx
921 mov word ptr ds:[si+6], es
922 mov dword ptr ds:[si+8], eax
923 .loop_cdrom:
924 push bp // Sectors left
925 cmp bp, word ptr ds:[MaxTransferCD]
926 jbe .bp_ok
927 mov bp, word ptr ds:[MaxTransferCD]
928 .bp_ok:
929 mov word ptr ds:[si+2], bp
930 push si
931 mov dl, byte ptr ds:[DriveNumber]
932 mov ah, HEX(42) // Extended Read
933 call xint13
934 pop si
935 pop bp
936 movzx eax, word ptr ds:[si+2] // Sectors we read
937 add dword ptr ds:[si+8], eax // Advance sector pointer
938 sub bp, ax // Sectors left
939 shl ax, SECTOR_SHIFT-4 // 2048-byte sectors -> segment
940 add word ptr ds:[si+6], ax // Advance buffer pointer
941 and bp, bp
942 jnz .loop_cdrom
943 mov eax, dword ptr ds:[si+8] // Next sector
944 ret
945
946 // INT 13h with retry
947 xint13:
948 mov byte ptr ds:[RetryCount], retry_count
949 .try:
950 pushad
951 call int13
952 jc .error_cdrom
953 add sp, 8*4 // Clean up stack
954 ret
955 .error_cdrom:
956 mov byte ptr ds:[DiskError], ah // Save error code
957 popad
958 mov word ptr ds:[DiskSys], ax // Save system call number
959 dec byte ptr ds:[RetryCount]
960 jz .real_error
961 push ax
962 mov al, byte ptr ds:[RetryCount]
963 mov ah, byte ptr ds:[dapa+2] // Sector transfer count
964 cmp al, 2 // Only 2 attempts left
965 ja .nodanger
966 mov ah, 1 // Drop transfer size to 1
967 jmp short .setsize
968 .nodanger:
969 cmp al, retry_count-2
970 ja .again // First time, just try again
971 shr ah, 1 // Otherwise, try to reduce
972 adc ah, 0 // the max transfer size, but not to 0
973 .setsize:
974 mov byte ptr ds:[MaxTransferCD], ah
975 mov byte ptr ds:[dapa+2], ah
976 .again:
977 pop ax
978 jmp .try
979
980 .real_error:
981 mov si, offset diskerr_msg
982 call writemsg
983 mov al, byte ptr ds:[DiskError]
984 call writehex2
985 mov si, offset oncall_str
986 call writestr_early
987 mov ax, word ptr ds:[DiskSys]
988 call writehex4
989 mov si, offset ondrive_str
990 call writestr_early
991 mov al, dl
992 call writehex2
993 call crlf_early
994 // Fall through to kaboom
995
996 //
997 // kaboom: write a message and bail out. Wait for a user keypress,
998 // then do a hard reboot.
999 //
1000 kaboom:
1001 // Restore a clean context.
1002 mov ax, cs
1003 mov ds, ax
1004 mov es, ax
1005 mov fs, ax
1006 mov gs, ax
1007 sti
1008
1009 // Display the failure message.
1010 mov si, offset err_bootfailed
1011 call writestr_early
1012
1013 // Wait for a keypress.
1014 xor ax, ax
1015 int HEX(16)
1016
1017 // Disable interrupts and reset the system through a magic BIOS call.
1018 cli
1019 mov word ptr ds:[BIOS_magic], 0
1020 ljmp16 HEX(0F000), HEX(0FFF0)
1021
1022 //
1023 // writehex[248]: Write a hex number in (AL, AX, EAX) to the console
1024 //
1025 writehex2:
1026 pushfd
1027 pushad
1028 rol eax, 24
1029 mov cx,2
1030 jmp short writehex_common
1031 writehex4:
1032 pushfd
1033 pushad
1034 rol eax, 16
1035 mov cx, 4
1036 jmp short writehex_common
1037 writehex8:
1038 pushfd
1039 pushad
1040 mov cx, 8
1041 writehex_common:
1042 .loop_writehex:
1043 rol eax, 4
1044 push eax
1045 and al, HEX(0F)
1046 cmp al, 10
1047 jae .high
1048 .low:
1049 add al, '0'
1050 jmp short .ischar
1051 .high:
1052 add al, 'A'-10
1053 .ischar:
1054 call writechr
1055 pop eax
1056 loop .loop_writehex
1057 popad
1058 popfd
1059 ret
1060
1061 //
1062 // pollchar_and_empty: Check if we have an input character pending (ZF = 0)
1063 // and empty the input buffer afterwards.
1064 //
1065 pollchar_and_empty:
1066 pushad
1067 mov ah, 1 // Did the user press a key?
1068 int HEX(16)
1069 jz .end_pollchar // No, then we're done
1070 mov ah, 0 // Otherwise empty the buffer by reading it
1071 int HEX(16)
1072 .end_pollchar:
1073 popad
1074 ret
1075
1076
1077 /* INITIALIZED VARIABLES *****************************************************/
1078 presskey_msg:
1079 .ascii "Press any key to boot from the ReactOS medium", NUL
1080 dot_msg:
1081 .ascii ".", NUL
1082 isoboot_str:
1083 .ascii "ISOBOOT: ", NUL
1084 spec_err_msg:
1085 .ascii "Loading spec packet failed, trying to wing it...", CR, LF, NUL
1086 maybe_msg:
1087 .ascii "Found something at drive = ", NUL
1088 alright_msg:
1089 .ascii "Looks reasonable, continuing...", CR, LF, NUL
1090 nospec_msg:
1091 .ascii "Extremely broken BIOS detected, last attempt with drive = ", NUL
1092 nothing_msg:
1093 .ascii "Failed to locate CD-ROM device; boot failed.", CR, LF, NUL
1094 diskerr_msg:
1095 .ascii "Disk error ", NUL
1096 oncall_str:
1097 .ascii ", AX = ", NUL
1098 ondrive_str:
1099 .ascii ", drive ", NUL
1100 err_bootfailed:
1101 .ascii CR, LF, "Boot failed: press a key to retry...", NUL
1102 loader_dir:
1103 .ascii "/LOADER", NUL
1104 no_dir_msg:
1105 .ascii "LOADER dir not found.", CR, LF, NUL
1106 setupldr_sys:
1107 .ascii "SETUPLDR.SYS", NUL
1108 no_setupldr_msg:
1109 .ascii "SETUPLDR.SYS not found.", CR, LF, NUL
1110
1111 .align 4
1112 BufSafe:
1113 .word trackbufsize/SECTOR_SIZE // Clusters we can load into trackbuf
1114
1115 // Maximum transfer size
1116 .align 4
1117 MaxTransfer:
1118 .word 127 // Hard disk modes
1119 MaxTransferCD:
1120 .word 32 // CD mode
1121
1122 //
1123 // El Torito spec packet
1124 //
1125 .align 8
1126 spec_packet:
1127 .byte HEX(13) // Size of packet
1128 sp_media:
1129 .byte 0 // Media type
1130 sp_drive:
1131 .byte 0 // Drive number
1132 sp_controller:
1133 .byte 0 // Controller index
1134 sp_lba:
1135 .long 0 // LBA for emulated disk image
1136 sp_devspec:
1137 .word 0 // IDE/SCSI information
1138 sp_buffer:
1139 .word 0 // User-provided buffer
1140 sp_loadseg:
1141 .word 0 // Load segment
1142 sp_sectors:
1143 .word 0 // Sector count
1144 sp_chs:
1145 .byte 0,0,0 // Simulated CHS geometry
1146 sp_dummy:
1147 .byte 0 // Scratch, safe to overwrite
1148
1149 //
1150 // EBIOS disk address packet
1151 //
1152 .align 8
1153 dapa:
1154 .word 16 // Packet size
1155 .count:
1156 .word 0 // Block count
1157 .off:
1158 .word 0 // Offset of buffer
1159 .seg:
1160 .word 0 // Segment of buffer
1161 .lba:
1162 .long 0 // LBA (LSW)
1163 .long 0 // LBA (MSW)
1164
1165
1166 // Extend the size to cover one 2K-sized sector
1167 .org 2047
1168 .byte 0
1169
1170 .endcode16
1171
1172 END