[BOOTDATA]
[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 // Fetch our stored drive number to DL and set the boot partition to 0 in DH.
314 mov dl, byte ptr ds:[DriveNumber]
315 mov dh, 0
316
317 // Transfer execution to the bootloader.
318 ljmp16 0, FREELDR_BASE
319
320
321 /* FUNCTIONS *****************************************************************/
322
323 ///////////////////////////////////////////////////////////////////////////////
324 // Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de
325 ///////////////////////////////////////////////////////////////////////////////
326 //
327 // There is a problem with certain versions of the AWARD BIOS ...
328 // the boot sector will be loaded and executed correctly, but, because the
329 // int 13 vector points to the wrong code in the BIOS, every attempt to
330 // load the spec packet will fail. We scan for the equivalent of
331 //
332 // mov ax,0201h
333 // mov bx,7c00h
334 // mov cx,0006h
335 // mov dx,0180h
336 // pushf
337 // call <direct far>
338 //
339 // and use <direct far> as the new vector for int 13. The code above is
340 // used to load the boot code into ram, and there should be no reason
341 // for anybody to change it now or in the future. There are no opcodes
342 // that use encodings relativ to IP, so scanning is easy. If we find the
343 // code above in the BIOS code we can be pretty sure to run on a machine
344 // with an broken AWARD BIOS ...
345 //
346 ///////////////////////////////////////////////////////////////////////////////
347 award_oldint13:
348 .long 0
349 award_string:
350 .byte HEX(0b8),1,2,HEX(0bb),0,HEX(7c),HEX(0b9),6,0,HEX(0ba),HEX(80),1,HEX(09c),HEX(09a)
351
352 award_hack:
353 mov si, offset spec_err_msg // Moved to this place from
354 call writemsg // spec_query_failed
355
356 mov eax, dword ptr ds:[HEX(13)*4]
357 mov dword ptr ds:[award_oldint13], eax
358
359 push es
360 mov ax, HEX(F000) // ES = BIOS Seg
361 mov es, ax
362 cld
363 xor di, di // start at ES:DI = f000:0
364 award_loop:
365 push di // save DI
366 mov si, offset award_string // scan for award_string
367 mov cx, 7 // length of award_string = 7dw
368 repz cmpsw // compare
369 pop di // restore DI
370 jcxz award_found // jmp if found
371 inc di // not found, inc di
372 jno award_loop
373
374 award_failed:
375 pop es // No, not this way :-((
376 award_fail2:
377 mov eax, dword ptr ds:[award_oldint13] // restore the original int
378 or eax, eax // 13 vector if there is one
379 jz spec_query_failed // and try other workarounds
380 mov dword ptr ds:[HEX(13)*4], eax
381 jmp spec_query_failed
382
383 award_found:
384 mov eax, dword ptr es:[di+HEX(0e)] // load possible int 13 addr
385 pop es // restore ES
386
387 cmp eax, dword ptr ds:[award_oldint13] // give up if this is the
388 jz award_failed // active int 13 vector,
389 mov dword ptr ds:[HEX(13)*4], eax // otherwise change 0:13h*4
390
391 mov ax, HEX(4B01) // try to read the spec packet
392 mov dl, byte ptr ds:[DriveNumber] // now ... it should not fail
393 mov si, offset spec_packet // any longer
394 int HEX(13)
395 jc award_fail2
396
397 jmp found_drive // and leave error recovery code
398 ///////////////////////////////////////////////////////////////////////////////
399 // End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de
400 ///////////////////////////////////////////////////////////////////////////////
401
402
403 // INT 13h, AX=4B01h, DL=<passed in value> failed.
404 // Try to scan the entire 80h-FFh from the end.
405 spec_query_failed:
406 // some code moved to BrokenAwardHack
407
408 mov dl, HEX(FF)
409
410 .test_loop:
411 pusha
412 mov ax, HEX(4B01)
413 mov si, offset spec_packet
414 mov byte ptr ds:[si], HEX(13) // Size of buffer
415 call int13
416 popa
417 jc .still_broken
418
419 mov si, offset maybe_msg
420 call writemsg
421 mov al, dl
422 call writehex2
423 call crlf_early
424
425 cmp byte ptr ds:[sp_drive], dl
426 jne .maybe_broken
427
428 // Okay, good enough...
429 mov si, offset alright_msg
430 call writemsg
431 .found_drive0:
432 mov byte ptr ds:[DriveNumber], dl
433 .found_drive:
434 jmp found_drive
435
436 // Award BIOS 4.51 apparently passes garbage in sp_drive,
437 // but if this was the drive number originally passed in
438 // DL then consider it "good enough"
439 .maybe_broken:
440 mov al, byte ptr ds:[DriveNumber]
441 cmp al, dl
442 je .found_drive
443
444 // Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02
445 // passes garbage in sp_drive, and the drive number originally
446 // passed in DL does not have 80h bit set.
447 or al, HEX(80)
448 cmp al, dl
449 je .found_drive0
450
451 .still_broken:
452 dec dx
453 cmp dl, HEX(80)
454 jnb .test_loop
455
456 // No spec packet anywhere. Some particularly pathetic
457 // BIOSes apparently don't even implement function
458 // 4B01h, so we can't query a spec packet no matter
459 // what. If we got a drive number in DL, then try to
460 // use it, and if it works, then well...
461 mov dl, byte ptr ds:[DriveNumber]
462 cmp dl, HEX(81) // Should be 81-FF at least
463 jb fatal_error // If not, it's hopeless
464
465 // Write a warning to indicate we're on *very* thin ice now
466 mov si, offset nospec_msg
467 call writemsg
468 mov al, dl
469 call writehex2
470 call crlf_early
471 jmp .found_drive // Pray that this works...
472
473 fatal_error:
474 mov si, offset nothing_msg
475 call writemsg
476
477 .norge:
478 jmp short .norge
479
480 //
481 // searchdir:
482 //
483 // Open a file
484 //
485 // On entry:
486 // DS:DI = filename
487 // If successful:
488 // ZF clear
489 // SI = file pointer
490 // EAX = file length in bytes
491 // If unsuccessful
492 // ZF set
493 //
494 // Assumes CS == DS == ES, and trashes BX and CX.
495 //
496 // searchdir_iso is a special entry point for ISOLINUX only. In addition
497 // to the above, searchdir_iso passes a file flag mask in AL. This is useful
498 // for searching for directories.
499 //
500 alloc_failure:
501 xor ax, ax // ZF <- 1
502 ret
503
504 searchdir:
505 xor al, al
506 searchdir_iso:
507 mov byte ptr ds:[ISOFlags], al
508 call allocate_file // Temporary file structure for directory
509 jnz alloc_failure
510 push es
511 push ds
512 pop es // ES = DS
513 mov si, offset CurrentDir
514 cmp byte ptr ds:[di], '/' // If filename begins with slash
515 jne .not_rooted
516 inc di // Skip leading slash
517 mov si, offset RootDir // Reference root directory instead
518 .not_rooted:
519 mov eax, dword ptr ds:[si+dir_clust]
520 mov dword ptr ds:[bx+file_left], eax
521 shl eax, SECTOR_SHIFT
522 mov dword ptr ds:[bx+file_bytesleft], eax
523 mov eax, dword ptr ds:[si+dir_lba]
524 mov dword ptr ds:[bx+file_sector], eax
525 mov edx, dword ptr ds:[si+dir_len]
526
527 .look_for_slash:
528 mov ax, di
529 .scan:
530 mov cl, byte ptr ds:[di]
531 inc di
532 and cl, cl
533 jz .isfile
534 cmp cl, '/'
535 jne .scan
536 mov byte ptr ds:[di-1], 0 // Terminate at directory name
537 mov cl, 2 // Search for directory
538 xchg cl, byte ptr ds:[ISOFlags]
539
540 push di // Save these...
541 push cx
542
543 // Create recursion stack frame...
544 push offset .resume // Where to "return" to
545 push es
546 .isfile:
547 xchg ax, di
548
549 .getsome:
550 // Get a chunk of the directory
551 // This relies on the fact that ISOLINUX doesn't change SI
552 mov si, trackbuf
553 pushad
554 xchg bx, si
555 mov cx, word ptr ds:[BufSafe]
556 call getfssec
557 popad
558
559 .compare:
560 movzx eax, byte ptr ds:[si] // Length of directory entry
561 cmp al, 33
562 jb .next_sector
563 mov cl, byte ptr ds:[si+25]
564 xor cl, byte ptr ds:[ISOFlags]
565 test cl, HEX(8E) // Unwanted file attributes!
566 jnz .not_file
567 pusha
568 movzx cx, byte ptr ds:[si+32] // File identifier length
569 add si, 33 // File identifier offset
570 call iso_compare_names
571 popa
572 je .success
573 .not_file:
574 sub edx, eax // Decrease bytes left
575 jbe .failure
576 add si, ax // Advance pointer
577
578 .check_overrun:
579 // Did we finish the buffer?
580 cmp si, trackbuf+trackbufsize
581 jb .compare // No, keep going
582
583 jmp short .getsome // Get some more directory
584
585 .next_sector:
586 // Advance to the beginning of next sector
587 lea ax, [si+SECTOR_SIZE-1]
588 and ax, not (SECTOR_SIZE-1)
589 sub ax, si
590 jmp short .not_file // We still need to do length checks
591
592 .failure:
593 xor eax, eax // ZF = 1
594 mov dword ptr ds:[bx+file_sector], eax
595 pop es
596 ret
597
598 .success:
599 mov eax, dword ptr ds:[si+2] // Location of extent
600 mov dword ptr ds:[bx+file_sector], eax
601 mov eax, dword ptr ds:[si+10] // Data length
602 mov dword ptr ds:[bx+file_bytesleft], eax
603 push eax
604 add eax, SECTOR_SIZE-1
605 shr eax, SECTOR_SHIFT
606 mov dword ptr ds:[bx+file_left], eax
607 pop eax
608 jz .failure // Empty file?
609 // ZF = 0
610 mov si, bx
611 pop es
612 ret
613
614 .resume:
615 // We get here if we were only doing part of a lookup
616 // This relies on the fact that .success returns bx == si
617 xchg edx, eax // Directory length in edx
618 pop cx // Old ISOFlags
619 pop di // Next filename pointer
620 mov byte ptr ds:[di-1], '/' // Restore slash
621 mov byte ptr ds:[ISOFlags], cl // Restore the flags
622 jz .failure // Did we fail? If so fail for real!
623 jmp .look_for_slash // Otherwise, next level
624
625 //
626 // allocate_file: Allocate a file structure
627 //
628 // If successful:
629 // ZF set
630 // BX = file pointer
631 // In unsuccessful:
632 // ZF clear
633 //
634 allocate_file:
635 push cx
636 mov bx, Files
637 mov cx, MAX_OPEN
638 .check:
639 cmp dword ptr ds:[bx], 0
640 je .found
641 add bx, open_file_t_size // ZF = 0
642 loop .check
643 // ZF = 0 if we fell out of the loop
644 .found:
645 pop cx
646 ret
647
648 //
649 // iso_compare_names:
650 // Compare the names DS:SI and DS:DI and report if they are
651 // equal from an ISO 9660 perspective. SI is the name from
652 // the filesystem; CX indicates its length, and ';' terminates.
653 // DI is expected to end with a null.
654 //
655 // Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
656 //
657 iso_compare_names:
658 // First, terminate and canonicalize input filename
659 push di
660 mov di, offset ISOFileName
661 .canon_loop:
662 jcxz .canon_end
663 lodsb
664 dec cx
665 cmp al, ';'
666 je .canon_end
667 and al, al
668 je .canon_end
669 stosb
670 cmp di, offset ISOFileNameEnd-1 // Guard against buffer overrun
671 jb .canon_loop
672 .canon_end:
673 cmp di, ISOFileName
674 jbe .canon_done
675 cmp byte ptr ds:[di-1], '.' // Remove terminal dots
676 jne .canon_done
677 dec di
678 jmp short .canon_end
679 .canon_done:
680 mov byte ptr ds:[di], 0 // Null-terminate string
681 pop di
682 mov si, ISOFileName
683 .compare2:
684 lodsb
685 mov ah, byte ptr ds:[di]
686 inc di
687 and ax, ax
688 jz .success2 // End of string for both
689 and al, al // Is either one end of string?
690 jz .failure2 // If so, failure
691 and ah, ah
692 jz .failure2
693 or ax, HEX(2020) // Convert to lower case
694 cmp al, ah
695 je .compare2
696 .failure2:
697 and ax, ax // ZF = 0 (at least one will be nonzero)
698 .success2:
699 ret
700
701 //
702 // getfssec: Get multiple clusters from a file, given the file pointer.
703 //
704 // On entry:
705 // ES:BX -> Buffer
706 // SI -> File pointer
707 // CX -> Cluster count
708 // On exit:
709 // SI -> File pointer (or 0 on EOF)
710 // CF = 1 -> Hit EOF
711 // ECX -> Bytes actually read
712 //
713 getfssec:
714 push ds
715 push cs
716 pop ds // DS <- CS
717
718 movzx ecx, cx
719 cmp ecx, dword ptr ds:[si+file_left]
720 jna .ok_size
721 mov ecx, dword ptr ds:[si+file_left]
722 .ok_size:
723 pushad
724 mov eax, dword ptr ds:[si+file_sector]
725 mov bp, cx
726 call getlinsec
727 popad
728
729 // ECX[31:16] == 0 here...
730 add dword ptr ds:[si+file_sector], ecx
731 sub dword ptr ds:[si+file_left], ecx
732 shl ecx, SECTOR_SHIFT // Convert to bytes
733 cmp ecx, dword ptr ds:[si+file_bytesleft]
734 jb .not_all
735 mov ecx, dword ptr ds:[si+file_bytesleft]
736 .not_all:
737 sub dword ptr ds:[si+file_bytesleft], ecx
738 jnz .ret // CF = 0 in this case...
739 push eax
740 xor eax, eax
741 mov dword ptr ds:[si+file_sector], eax // Unused
742 mov si, ax
743 pop eax
744 stc
745 .ret:
746 pop ds
747 ret
748
749 //
750 // Information message (DS:SI) output
751 // Prefix with "ISOBOOT: "
752 //
753 writemsg:
754 push ax
755 push si
756 mov si, offset isoboot_str
757 call writestr_early
758 pop si
759 call writestr_early
760 pop ax
761 ret
762
763 writestr_early:
764 pushfd
765 pushad
766 .top:
767 lodsb
768 and al, al
769 jz .end_writestr
770 call writechr
771 jmp short .top
772 .end_writestr:
773 popad
774 popfd
775 ret
776
777 crlf_early:
778 push ax
779 mov al, 13
780 call writechr
781 mov al, 10
782 call writechr
783 pop ax
784 ret
785
786 //
787 // writechr: Write a character to the screen.
788 //
789 writechr:
790 pushfd
791 pushad
792 mov ah, HEX(0E)
793 xor bx, bx
794 int HEX(10)
795 popad
796 popfd
797 ret
798
799 //
800 // int13: save all the segment registers and call INT 13h.
801 // Some CD-ROM BIOSes have been found to corrupt segment registers
802 // and/or disable interrupts.
803 //
804 int13:
805 pushf
806 push bp
807 push ds
808 push es
809 push fs
810 push gs
811 int HEX(13)
812 mov bp, sp
813 setc byte ptr ds:[bp+10] // Propagate CF to the caller
814 pop gs
815 pop fs
816 pop es
817 pop ds
818 pop bp
819 popf
820 ret
821
822 //
823 // Get one sector. Convenience entry point.
824 //
825 getonesec:
826 mov bp, 1
827 // Fall through to getlinsec
828
829 //
830 // Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
831 //
832 getlinsec:
833 jmp word ptr cs:[GetlinsecPtr]
834
835 //
836 // getlinsec_ebios:
837 //
838 // getlinsec implementation for floppy/HDD EBIOS (EDD)
839 //
840 getlinsec_ebios:
841 xor edx, edx
842 shld edx, eax, 2
843 shl eax, 2 // Convert to HDD sectors
844 shl bp, 2
845
846 .loop_ebios:
847 push bp // Sectors left
848 .retry2:
849 call maxtrans // Enforce maximum transfer size
850 movzx edi, bp // Sectors we are about to read
851 mov cx, retry_count
852 .retry:
853 // Form DAPA on stack
854 push edx
855 push eax
856 push es
857 push bx
858 push di
859 push 16
860 mov si, sp
861 pushad
862 mov dl, byte ptr ds:[DriveNumber]
863 push ds
864 push ss
865 pop ds // DS <- SS
866 mov ah, HEX(42) // Extended Read
867 call int13
868 pop ds
869 popad
870 lea sp, [si+16] // Remove DAPA
871 jc .error_ebios
872 pop bp
873 add eax, edi // Advance sector pointer
874 adc edx, 0
875 sub bp, di // Sectors left
876 shl di, 9 // 512-byte sectors
877 add bx, di // Advance buffer pointer
878 jnc .no_overflow // Check if we have read more than 64K and need to adjust ES
879 mov di, es
880 add di, HEX(1000) // Adjust segment by 64K (1000h * 16 = 10000h = 64K + 1)
881 mov es, di
882 .no_overflow:
883 and bp, bp
884 jnz .loop_ebios
885
886 ret
887
888 .error_ebios:
889 pushad // Try resetting the device
890 xor ax, ax
891 mov dl, byte ptr ds:[DriveNumber]
892 call int13
893 popad
894 loop .retry // CX-- and jump if not zero
895
896 // Total failure.
897 jmp kaboom
898
899 //
900 // Truncate BP to MaxTransfer
901 //
902 maxtrans:
903 cmp bp, word ptr ds:[MaxTransfer]
904 jna .ok
905 mov bp, word ptr ds:[MaxTransfer]
906 .ok:
907 ret
908
909 //
910 // This is the variant we use for real CD-ROMs:
911 // LBA, 2K sectors, some special error handling.
912 //
913 getlinsec_cdrom:
914 mov si, offset dapa // Load up the DAPA
915 mov word ptr ds:[si+4], bx
916 mov word ptr ds:[si+6], es
917 mov dword ptr ds:[si+8], eax
918 .loop_cdrom:
919 push bp // Sectors left
920 cmp bp, word ptr ds:[MaxTransferCD]
921 jbe .bp_ok
922 mov bp, word ptr ds:[MaxTransferCD]
923 .bp_ok:
924 mov word ptr ds:[si+2], bp
925 push si
926 mov dl, byte ptr ds:[DriveNumber]
927 mov ah, HEX(42) // Extended Read
928 call xint13
929 pop si
930 pop bp
931 movzx eax, word ptr ds:[si+2] // Sectors we read
932 add dword ptr ds:[si+8], eax // Advance sector pointer
933 sub bp, ax // Sectors left
934 shl ax, SECTOR_SHIFT-4 // 2048-byte sectors -> segment
935 add word ptr ds:[si+6], ax // Advance buffer pointer
936 and bp, bp
937 jnz .loop_cdrom
938 mov eax, dword ptr ds:[si+8] // Next sector
939 ret
940
941 // INT 13h with retry
942 xint13:
943 mov byte ptr ds:[RetryCount], retry_count
944 .try:
945 pushad
946 call int13
947 jc .error_cdrom
948 add sp, 8*4 // Clean up stack
949 ret
950 .error_cdrom:
951 mov byte ptr ds:[DiskError], ah // Save error code
952 popad
953 mov word ptr ds:[DiskSys], ax // Save system call number
954 dec byte ptr ds:[RetryCount]
955 jz .real_error
956 push ax
957 mov al, byte ptr ds:[RetryCount]
958 mov ah, byte ptr ds:[dapa+2] // Sector transfer count
959 cmp al, 2 // Only 2 attempts left
960 ja .nodanger
961 mov ah, 1 // Drop transfer size to 1
962 jmp short .setsize
963 .nodanger:
964 cmp al, retry_count-2
965 ja .again // First time, just try again
966 shr ah, 1 // Otherwise, try to reduce
967 adc ah, 0 // the max transfer size, but not to 0
968 .setsize:
969 mov byte ptr ds:[MaxTransferCD], ah
970 mov byte ptr ds:[dapa+2], ah
971 .again:
972 pop ax
973 jmp .try
974
975 .real_error:
976 mov si, offset diskerr_msg
977 call writemsg
978 mov al, byte ptr ds:[DiskError]
979 call writehex2
980 mov si, offset oncall_str
981 call writestr_early
982 mov ax, word ptr ds:[DiskSys]
983 call writehex4
984 mov si, offset ondrive_str
985 call writestr_early
986 mov al, dl
987 call writehex2
988 call crlf_early
989 // Fall through to kaboom
990
991 //
992 // kaboom: write a message and bail out. Wait for a user keypress,
993 // then do a hard reboot.
994 //
995 kaboom:
996 // Restore a clean context.
997 mov ax, cs
998 mov ds, ax
999 mov es, ax
1000 mov fs, ax
1001 mov gs, ax
1002 sti
1003
1004 // Display the failure message.
1005 mov si, offset err_bootfailed
1006 call writestr_early
1007
1008 // Wait for a keypress.
1009 xor ax, ax
1010 int HEX(16)
1011
1012 // Disable interrupts and reset the system through a magic BIOS call.
1013 cli
1014 mov word ptr ds:[BIOS_magic], 0
1015 ljmp16 HEX(0F000), HEX(0FFF0)
1016
1017 //
1018 // writehex[248]: Write a hex number in (AL, AX, EAX) to the console
1019 //
1020 writehex2:
1021 pushfd
1022 pushad
1023 rol eax, 24
1024 mov cx,2
1025 jmp short writehex_common
1026 writehex4:
1027 pushfd
1028 pushad
1029 rol eax, 16
1030 mov cx, 4
1031 jmp short writehex_common
1032 writehex8:
1033 pushfd
1034 pushad
1035 mov cx, 8
1036 writehex_common:
1037 .loop_writehex:
1038 rol eax, 4
1039 push eax
1040 and al, HEX(0F)
1041 cmp al, 10
1042 jae .high
1043 .low:
1044 add al, '0'
1045 jmp short .ischar
1046 .high:
1047 add al, 'A'-10
1048 .ischar:
1049 call writechr
1050 pop eax
1051 loop .loop_writehex
1052 popad
1053 popfd
1054 ret
1055
1056 //
1057 // pollchar_and_empty: Check if we have an input character pending (ZF = 0)
1058 // and empty the input buffer afterwards.
1059 //
1060 pollchar_and_empty:
1061 pushad
1062 mov ah, 1 // Did the user press a key?
1063 int HEX(16)
1064 jz .end_pollchar // No, then we're done
1065 mov ah, 0 // Otherwise empty the buffer by reading it
1066 int HEX(16)
1067 .end_pollchar:
1068 popad
1069 ret
1070
1071
1072 /* INITIALIZED VARIABLES *****************************************************/
1073 presskey_msg:
1074 .ascii "Press any key to boot from the ReactOS medium", NUL
1075 dot_msg:
1076 .ascii ".", NUL
1077 isoboot_str:
1078 .ascii "ISOBOOT: ", NUL
1079 spec_err_msg:
1080 .ascii "Loading spec packet failed, trying to wing it...", CR, LF, NUL
1081 maybe_msg:
1082 .ascii "Found something at drive = ", NUL
1083 alright_msg:
1084 .ascii "Looks reasonable, continuing...", CR, LF, NUL
1085 nospec_msg:
1086 .ascii "Extremely broken BIOS detected, last attempt with drive = ", NUL
1087 nothing_msg:
1088 .ascii "Failed to locate CD-ROM device; boot failed.", CR, LF, NUL
1089 diskerr_msg:
1090 .ascii "Disk error ", NUL
1091 oncall_str:
1092 .ascii ", AX = ", NUL
1093 ondrive_str:
1094 .ascii ", drive ", NUL
1095 err_bootfailed:
1096 .ascii CR, LF, "Boot failed: press a key to retry...", NUL
1097 loader_dir:
1098 .ascii "/LOADER", NUL
1099 no_dir_msg:
1100 .ascii "LOADER dir not found.", CR, LF, NUL
1101 setupldr_sys:
1102 .ascii "SETUPLDR.SYS", NUL
1103 no_setupldr_msg:
1104 .ascii "SETUPLDR.SYS not found.", CR, LF, NUL
1105
1106 .align 4
1107 BufSafe:
1108 .word trackbufsize/SECTOR_SIZE // Clusters we can load into trackbuf
1109
1110 // Maximum transfer size
1111 .align 4
1112 MaxTransfer:
1113 .word 127 // Hard disk modes
1114 MaxTransferCD:
1115 .word 32 // CD mode
1116
1117 //
1118 // El Torito spec packet
1119 //
1120 .align 8
1121 spec_packet:
1122 .byte HEX(13) // Size of packet
1123 sp_media:
1124 .byte 0 // Media type
1125 sp_drive:
1126 .byte 0 // Drive number
1127 sp_controller:
1128 .byte 0 // Controller index
1129 sp_lba:
1130 .long 0 // LBA for emulated disk image
1131 sp_devspec:
1132 .word 0 // IDE/SCSI information
1133 sp_buffer:
1134 .word 0 // User-provided buffer
1135 sp_loadseg:
1136 .word 0 // Load segment
1137 sp_sectors:
1138 .word 0 // Sector count
1139 sp_chs:
1140 .byte 0,0,0 // Simulated CHS geometry
1141 sp_dummy:
1142 .byte 0 // Scratch, safe to overwrite
1143
1144 //
1145 // EBIOS disk address packet
1146 //
1147 .align 8
1148 dapa:
1149 .word 16 // Packet size
1150 .count:
1151 .word 0 // Block count
1152 .off:
1153 .word 0 // Offset of buffer
1154 .seg:
1155 .word 0 // Segment of buffer
1156 .lba:
1157 .long 0 // LBA (LSW)
1158 .long 0 // LBA (MSW)
1159
1160
1161 // Extend the size to cover one 2K-sized sector
1162 .org 2047
1163 .byte 0
1164
1165 .endcode16
1166
1167 END