Contents of /tags/mkinitrd-6_3_1/isolinux/isolinux.asm
Parent Directory | Revision Log
Revision 1133 -
(show annotations)
(download)
Thu Aug 19 09:50:43 2010 UTC (14 years, 1 month ago) by niro
Original Path: trunk/mkinitrd-magellan/isolinux/isolinux.asm
File size: 44203 byte(s)
Thu Aug 19 09:50:43 2010 UTC (14 years, 1 month ago) by niro
Original Path: trunk/mkinitrd-magellan/isolinux/isolinux.asm
File size: 44203 byte(s)
-updated to isolinux-3.86
1 | ; -*- fundamental -*- (asm-mode sucks) |
2 | ; **************************************************************************** |
3 | ; |
4 | ; isolinux.asm |
5 | ; |
6 | ; A program to boot Linux kernels off a CD-ROM using the El Torito |
7 | ; boot standard in "no emulation" mode, making the entire filesystem |
8 | ; available. It is based on the SYSLINUX boot loader for MS-DOS |
9 | ; floppies. |
10 | ; |
11 | ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved |
12 | ; Copyright 2009 Intel Corporation; author: H. Peter Anvin |
13 | ; |
14 | ; This program is free software; you can redistribute it and/or modify |
15 | ; it under the terms of the GNU General Public License as published by |
16 | ; the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
17 | ; Boston MA 02111-1307, USA; either version 2 of the License, or |
18 | ; (at your option) any later version; incorporated herein by reference. |
19 | ; |
20 | ; **************************************************************************** |
21 | |
22 | %define IS_ISOLINUX 1 |
23 | %include "head.inc" |
24 | |
25 | ; |
26 | ; Some semi-configurable constants... change on your own risk. |
27 | ; |
28 | my_id equ isolinux_id |
29 | FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null) |
30 | FILENAME_MAX equ (1 << FILENAME_MAX_LG2) |
31 | NULLFILE equ 0 ; Zero byte == null file name |
32 | NULLOFFSET equ 0 ; Position in which to look |
33 | retry_count equ 6 ; How patient are we with the BIOS? |
34 | %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top |
35 | MAX_OPEN_LG2 equ 6 ; log2(Max number of open files) |
36 | MAX_OPEN equ (1 << MAX_OPEN_LG2) |
37 | SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement) |
38 | SECTOR_SIZE equ (1 << SECTOR_SHIFT) |
39 | |
40 | ROOT_DIR_WORD equ 0x002F |
41 | |
42 | ; |
43 | ; The following structure is used for "virtual kernels"; i.e. LILO-style |
44 | ; option labels. The options we permit here are `kernel' and `append |
45 | ; Since there is no room in the bottom 64K for all of these, we |
46 | ; stick them in high memory and copy them down before we need them. |
47 | ; |
48 | struc vkernel |
49 | vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!** |
50 | vk_rname: resb FILENAME_MAX ; Real name |
51 | vk_appendlen: resw 1 |
52 | vk_type: resb 1 ; Type of file |
53 | alignb 4 |
54 | vk_append: resb max_cmd_len+1 ; Command line |
55 | alignb 4 |
56 | vk_end: equ $ ; Should be <= vk_size |
57 | endstruc |
58 | |
59 | ; |
60 | ; File structure. This holds the information for each currently open file. |
61 | ; |
62 | struc open_file_t |
63 | file_sector resd 1 ; Sector pointer (0 = structure free) |
64 | file_bytesleft resd 1 ; Number of bytes left |
65 | file_left resd 1 ; Number of sectors left |
66 | resd 1 ; Unused |
67 | endstruc |
68 | |
69 | %ifndef DEPEND |
70 | %if (open_file_t_size & (open_file_t_size-1)) |
71 | %error "open_file_t is not a power of 2" |
72 | %endif |
73 | %endif |
74 | |
75 | struc dir_t |
76 | dir_lba resd 1 ; Directory start (LBA) |
77 | dir_len resd 1 ; Length in bytes |
78 | dir_clust resd 1 ; Length in clusters |
79 | endstruc |
80 | |
81 | ; --------------------------------------------------------------------------- |
82 | ; BEGIN CODE |
83 | ; --------------------------------------------------------------------------- |
84 | |
85 | ; |
86 | ; Memory below this point is reserved for the BIOS and the MBR |
87 | ; |
88 | section .earlybss |
89 | trackbufsize equ 8192 |
90 | trackbuf resb trackbufsize ; Track buffer goes here |
91 | ; ends at 2800h |
92 | |
93 | ; Some of these are touched before the whole image |
94 | ; is loaded. DO NOT move this to .uibss. |
95 | section .bss2 |
96 | alignb 4 |
97 | ISOFileName resb 64 ; ISO filename canonicalization buffer |
98 | ISOFileNameEnd equ $ |
99 | CurrentDir resb dir_t_size ; Current directory |
100 | RootDir resb dir_t_size ; Root directory |
101 | FirstSecSum resd 1 ; Checksum of bytes 64-2048 |
102 | ImageDwords resd 1 ; isolinux.bin size, dwords |
103 | InitStack resd 1 ; Initial stack pointer (SS:SP) |
104 | DiskSys resw 1 ; Last INT 13h call |
105 | ImageSectors resw 1 ; isolinux.bin size, sectors |
106 | ; These following two are accessed as a single dword... |
107 | GetlinsecPtr resw 1 ; The sector-read pointer |
108 | BIOSName resw 1 ; Display string for BIOS type |
109 | %define HAVE_BIOSNAME 1 |
110 | BIOSType resw 1 |
111 | DiskError resb 1 ; Error code for disk I/O |
112 | DriveNumber resb 1 ; CD-ROM BIOS drive number |
113 | ISOFlags resb 1 ; Flags for ISO directory search |
114 | RetryCount resb 1 ; Used for disk access retries |
115 | |
116 | alignb 8 |
117 | bsHidden resq 1 ; Used in hybrid mode |
118 | bsSecPerTrack resw 1 ; Used in hybrid mode |
119 | bsHeads resw 1 ; Used in hybrid mode |
120 | |
121 | |
122 | ; |
123 | ; El Torito spec packet |
124 | ; |
125 | |
126 | alignb 8 |
127 | _spec_start equ $ |
128 | spec_packet: resb 1 ; Size of packet |
129 | sp_media: resb 1 ; Media type |
130 | sp_drive: resb 1 ; Drive number |
131 | sp_controller: resb 1 ; Controller index |
132 | sp_lba: resd 1 ; LBA for emulated disk image |
133 | sp_devspec: resw 1 ; IDE/SCSI information |
134 | sp_buffer: resw 1 ; User-provided buffer |
135 | sp_loadseg: resw 1 ; Load segment |
136 | sp_sectors: resw 1 ; Sector count |
137 | sp_chs: resb 3 ; Simulated CHS geometry |
138 | sp_dummy: resb 1 ; Scratch, safe to overwrite |
139 | |
140 | ; |
141 | ; EBIOS drive parameter packet |
142 | ; |
143 | alignb 8 |
144 | drive_params: resw 1 ; Buffer size |
145 | dp_flags: resw 1 ; Information flags |
146 | dp_cyl: resd 1 ; Physical cylinders |
147 | dp_head: resd 1 ; Physical heads |
148 | dp_sec: resd 1 ; Physical sectors/track |
149 | dp_totalsec: resd 2 ; Total sectors |
150 | dp_secsize: resw 1 ; Bytes per sector |
151 | dp_dpte: resd 1 ; Device Parameter Table |
152 | dp_dpi_key: resw 1 ; 0BEDDh if rest valid |
153 | dp_dpi_len: resb 1 ; DPI len |
154 | resb 1 |
155 | resw 1 |
156 | dp_bus: resb 4 ; Host bus type |
157 | dp_interface: resb 8 ; Interface type |
158 | db_i_path: resd 2 ; Interface path |
159 | db_d_path: resd 2 ; Device path |
160 | resb 1 |
161 | db_dpi_csum: resb 1 ; Checksum for DPI info |
162 | |
163 | ; |
164 | ; EBIOS disk address packet |
165 | ; |
166 | alignb 8 |
167 | dapa: resw 1 ; Packet size |
168 | .count: resw 1 ; Block count |
169 | .off: resw 1 ; Offset of buffer |
170 | .seg: resw 1 ; Segment of buffer |
171 | .lba: resd 2 ; LBA (LSW, MSW) |
172 | |
173 | ; |
174 | ; Spec packet for disk image emulation |
175 | ; |
176 | alignb 8 |
177 | dspec_packet: resb 1 ; Size of packet |
178 | dsp_media: resb 1 ; Media type |
179 | dsp_drive: resb 1 ; Drive number |
180 | dsp_controller: resb 1 ; Controller index |
181 | dsp_lba: resd 1 ; LBA for emulated disk image |
182 | dsp_devspec: resw 1 ; IDE/SCSI information |
183 | dsp_buffer: resw 1 ; User-provided buffer |
184 | dsp_loadseg: resw 1 ; Load segment |
185 | dsp_sectors: resw 1 ; Sector count |
186 | dsp_chs: resb 3 ; Simulated CHS geometry |
187 | dsp_dummy: resb 1 ; Scratch, safe to overwrite |
188 | |
189 | alignb 4 |
190 | _spec_end equ $ |
191 | _spec_len equ _spec_end - _spec_start |
192 | |
193 | alignb open_file_t_size |
194 | Files resb MAX_OPEN*open_file_t_size |
195 | |
196 | section .text |
197 | ;; |
198 | ;; Primary entry point. Because BIOSes are buggy, we only load the first |
199 | ;; CD-ROM sector (2K) of the file, so the number one priority is actually |
200 | ;; loading the rest. |
201 | ;; |
202 | StackBuf equ STACK_TOP-44 ; 44 bytes needed for |
203 | ; the bootsector chainloading |
204 | ; code! |
205 | OrigESDI equ StackBuf-4 ; The high dword on the stack |
206 | |
207 | bootsec equ $ |
208 | |
209 | _start: ; Far jump makes sure we canonicalize the address |
210 | cli |
211 | jmp 0:_start1 |
212 | times 8-($-$$) nop ; Pad to file offset 8 |
213 | |
214 | ; This table hopefully gets filled in by mkisofs using the |
215 | ; -boot-info-table option. If not, the values in this |
216 | ; table are default values that we can use to get us what |
217 | ; we need, at least under a certain set of assumptions. |
218 | bi_pvd: dd 16 ; LBA of primary volume descriptor |
219 | bi_file: dd 0 ; LBA of boot file |
220 | bi_length: dd 0xdeadbeef ; Length of boot file |
221 | bi_csum: dd 0xdeadbeef ; Checksum of boot file |
222 | bi_reserved: times 10 dd 0xdeadbeef ; Reserved |
223 | bi_end: |
224 | |
225 | ; Custom entry point for the hybrid-mode disk. |
226 | ; The following values will have been pushed onto the |
227 | ; entry stack: |
228 | ; - partition offset (qword) |
229 | ; - ES |
230 | ; - DI |
231 | ; - DX (including drive number) |
232 | ; - CBIOS Heads |
233 | ; - CBIOS Sectors |
234 | ; - EBIOS flag |
235 | ; (top of stack) |
236 | ; |
237 | ; If we had an old isohybrid, the partition offset will |
238 | ; be missing; we can check for that with sp >= 0x7c00. |
239 | ; Serious hack alert. |
240 | %ifndef DEBUG_MESSAGES |
241 | _hybrid_signature: |
242 | dd 0x7078c0fb ; An arbitrary number... |
243 | |
244 | _start_hybrid: |
245 | pop cx ; EBIOS flag |
246 | pop word [cs:bsSecPerTrack] |
247 | pop word [cs:bsHeads] |
248 | pop dx |
249 | pop di |
250 | pop es |
251 | xor eax,eax |
252 | xor ebx,ebx |
253 | cmp sp,7C00h |
254 | jae .nooffset |
255 | pop eax |
256 | pop ebx |
257 | .nooffset: |
258 | mov [cs:bsHidden],eax |
259 | mov [cs:bsHidden+4],ebx |
260 | |
261 | mov si,bios_cbios |
262 | jcxz _start_common |
263 | mov si,bios_ebios |
264 | jmp _start_common |
265 | %endif |
266 | |
267 | _start1: |
268 | mov si,bios_cdrom |
269 | _start_common: |
270 | mov [cs:InitStack],sp ; Save initial stack pointer |
271 | mov [cs:InitStack+2],ss |
272 | xor ax,ax |
273 | mov ss,ax |
274 | mov sp,StackBuf ; Set up stack |
275 | push es ; Save initial ES:DI -> $PnP pointer |
276 | push di |
277 | mov ds,ax |
278 | mov es,ax |
279 | mov fs,ax |
280 | mov gs,ax |
281 | sti |
282 | cld |
283 | |
284 | mov [BIOSType],si |
285 | mov eax,[si] |
286 | mov [GetlinsecPtr],eax |
287 | |
288 | ; Show signs of life |
289 | mov si,syslinux_banner |
290 | call writestr_early |
291 | %ifdef DEBUG_MESSAGES |
292 | mov si,copyright_str |
293 | %else |
294 | mov si,[BIOSName] |
295 | %endif |
296 | call writestr_early |
297 | |
298 | ; |
299 | ; Before modifying any memory, get the checksum of bytes |
300 | ; 64-2048 |
301 | ; |
302 | initial_csum: xor edi,edi |
303 | mov si,bi_end |
304 | mov cx,(SECTOR_SIZE-64) >> 2 |
305 | .loop: lodsd |
306 | add edi,eax |
307 | loop .loop |
308 | mov [FirstSecSum],edi |
309 | |
310 | mov [DriveNumber],dl |
311 | %ifdef DEBUG_MESSAGES |
312 | mov si,startup_msg |
313 | call writemsg |
314 | mov al,dl |
315 | call writehex2 |
316 | call crlf |
317 | %endif |
318 | ; |
319 | ; Initialize spec packet buffers |
320 | ; |
321 | mov di,_spec_start |
322 | mov cx,_spec_len >> 2 |
323 | xor eax,eax |
324 | rep stosd |
325 | |
326 | ; Initialize length field of the various packets |
327 | mov byte [spec_packet],13h |
328 | mov byte [drive_params],30 |
329 | mov byte [dapa],16 |
330 | mov byte [dspec_packet],13h |
331 | |
332 | ; Other nonzero fields |
333 | inc word [dsp_sectors] |
334 | |
335 | ; Are we just pretending to be a CD-ROM? |
336 | cmp word [BIOSType],bios_cdrom |
337 | jne found_drive ; If so, no spec packet... |
338 | |
339 | ; Now figure out what we're actually doing |
340 | ; Note: use passed-in DL value rather than 7Fh because |
341 | ; at least some BIOSes will get the wrong value otherwise |
342 | mov ax,4B01h ; Get disk emulation status |
343 | mov dl,[DriveNumber] |
344 | mov si,spec_packet |
345 | call int13 |
346 | jc award_hack ; changed for BrokenAwardHack |
347 | mov dl,[DriveNumber] |
348 | cmp [sp_drive],dl ; Should contain the drive number |
349 | jne spec_query_failed |
350 | |
351 | %ifdef DEBUG_MESSAGES |
352 | mov si,spec_ok_msg |
353 | call writemsg |
354 | mov al,byte [sp_drive] |
355 | call writehex2 |
356 | call crlf |
357 | %endif |
358 | |
359 | found_drive: |
360 | ; Alright, we have found the drive. Now, try to find the |
361 | ; boot file itself. If we have a boot info table, life is |
362 | ; good; if not, we have to make some assumptions, and try |
363 | ; to figure things out ourselves. In particular, the |
364 | ; assumptions we have to make are: |
365 | ; - single session only |
366 | ; - only one boot entry (no menu or other alternatives) |
367 | |
368 | cmp dword [bi_file],0 ; Address of code to load |
369 | jne found_file ; Boot info table present :) |
370 | |
371 | %ifdef DEBUG_MESSAGES |
372 | mov si,noinfotable_msg |
373 | call writemsg |
374 | %endif |
375 | |
376 | ; No such luck. See if the spec packet contained one. |
377 | mov eax,[sp_lba] |
378 | and eax,eax |
379 | jz set_file ; Good enough |
380 | |
381 | %ifdef DEBUG_MESSAGES |
382 | mov si,noinfoinspec_msg |
383 | call writemsg |
384 | %endif |
385 | |
386 | ; No such luck. Get the Boot Record Volume, assuming single |
387 | ; session disk, and that we're the first entry in the chain. |
388 | mov eax,17 ; Assumed address of BRV |
389 | mov bx,trackbuf |
390 | call getonesec |
391 | |
392 | mov eax,[trackbuf+47h] ; Get boot catalog address |
393 | mov bx,trackbuf |
394 | call getonesec ; Get boot catalog |
395 | |
396 | mov eax,[trackbuf+28h] ; First boot entry |
397 | ; And hope and pray this is us... |
398 | |
399 | ; Some BIOSes apparently have limitations on the size |
400 | ; that may be loaded (despite the El Torito spec being very |
401 | ; clear on the fact that it must all be loaded.) Therefore, |
402 | ; we load it ourselves, and *bleep* the BIOS. |
403 | |
404 | set_file: |
405 | mov [bi_file],eax |
406 | |
407 | found_file: |
408 | ; Set up boot file sizes |
409 | mov eax,[bi_length] |
410 | sub eax,SECTOR_SIZE-3 ; ... minus sector loaded |
411 | shr eax,2 ; bytes->dwords |
412 | mov [ImageDwords],eax ; boot file dwords |
413 | add eax,(2047 >> 2) |
414 | shr eax,9 ; dwords->sectors |
415 | mov [ImageSectors],ax ; boot file sectors |
416 | |
417 | mov eax,[bi_file] ; Address of code to load |
418 | inc eax ; Don't reload bootstrap code |
419 | %ifdef DEBUG_MESSAGES |
420 | mov si,offset_msg |
421 | call writemsg |
422 | call writehex8 |
423 | call crlf |
424 | %endif |
425 | |
426 | ; Just in case some BIOSes have problems with |
427 | ; segment wraparound, use the normalized address |
428 | mov bx,((7C00h+2048) >> 4) |
429 | mov es,bx |
430 | xor bx,bx |
431 | mov bp,[ImageSectors] |
432 | %ifdef DEBUG_MESSAGES |
433 | push ax |
434 | mov si,size_msg |
435 | call writemsg |
436 | mov ax,bp |
437 | call writehex4 |
438 | call crlf |
439 | pop ax |
440 | %endif |
441 | call getlinsec |
442 | |
443 | push ds |
444 | pop es |
445 | |
446 | %ifdef DEBUG_MESSAGES |
447 | mov si,loaded_msg |
448 | call writemsg |
449 | %endif |
450 | |
451 | ; Verify the checksum on the loaded image. |
452 | verify_image: |
453 | mov si,7C00h+2048 |
454 | mov bx,es |
455 | mov ecx,[ImageDwords] |
456 | mov edi,[FirstSecSum] ; First sector checksum |
457 | .loop es lodsd |
458 | add edi,eax |
459 | dec ecx |
460 | jz .done |
461 | and si,si |
462 | jnz .loop |
463 | ; SI wrapped around, advance ES |
464 | add bx,1000h |
465 | mov es,bx |
466 | jmp short .loop |
467 | .done: mov ax,ds |
468 | mov es,ax |
469 | cmp [bi_csum],edi |
470 | je integrity_ok |
471 | |
472 | mov si,checkerr_msg |
473 | call writemsg |
474 | jmp kaboom |
475 | |
476 | integrity_ok: |
477 | %ifdef DEBUG_MESSAGES |
478 | mov si,allread_msg |
479 | call writemsg |
480 | %endif |
481 | jmp all_read ; Jump to main code |
482 | |
483 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
484 | ;; Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de |
485 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
486 | ;; |
487 | ;; There is a problem with certain versions of the AWARD BIOS ... |
488 | ;; the boot sector will be loaded and executed correctly, but, because the |
489 | ;; int 13 vector points to the wrong code in the BIOS, every attempt to |
490 | ;; load the spec packet will fail. We scan for the equivalent of |
491 | ;; |
492 | ;; mov ax,0201h |
493 | ;; mov bx,7c00h |
494 | ;; mov cx,0006h |
495 | ;; mov dx,0180h |
496 | ;; pushf |
497 | ;; call <direct far> |
498 | ;; |
499 | ;; and use <direct far> as the new vector for int 13. The code above is |
500 | ;; used to load the boot code into ram, and there should be no reason |
501 | ;; for anybody to change it now or in the future. There are no opcodes |
502 | ;; that use encodings relativ to IP, so scanning is easy. If we find the |
503 | ;; code above in the BIOS code we can be pretty sure to run on a machine |
504 | ;; with an broken AWARD BIOS ... |
505 | ;; |
506 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
507 | ;; |
508 | %ifdef DEBUG_MESSAGES ;; |
509 | ;; |
510 | award_notice db "Trying BrokenAwardHack first ...",CR,LF,0 ;; |
511 | award_not_orig db "BAH: Original Int 13 vector : ",0 ;; |
512 | award_not_new db "BAH: Int 13 vector changed to : ",0 ;; |
513 | award_not_succ db "BAH: SUCCESS",CR,LF,0 ;; |
514 | award_not_fail db "BAH: FAILURE" ;; |
515 | award_not_crlf db CR,LF,0 ;; |
516 | ;; |
517 | %endif ;; |
518 | ;; |
519 | award_oldint13 dd 0 ;; |
520 | award_string db 0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah ;; |
521 | ;; |
522 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
523 | award_hack: mov si,spec_err_msg ; Moved to this place from |
524 | call writemsg ; spec_query_faild |
525 | ; |
526 | %ifdef DEBUG_MESSAGES ; |
527 | ; |
528 | mov si,award_notice ; display our plan |
529 | call writemsg ; |
530 | mov si,award_not_orig ; display original int 13 |
531 | call writemsg ; vector |
532 | %endif ; |
533 | mov eax,[13h*4] ; |
534 | mov [award_oldint13],eax ; |
535 | ; |
536 | %ifdef DEBUG_MESSAGES ; |
537 | ; |
538 | call writehex8 ; |
539 | mov si,award_not_crlf ; |
540 | call writestr_early ; |
541 | %endif ; |
542 | push es ; save ES |
543 | mov ax,0f000h ; ES = BIOS Seg |
544 | mov es,ax ; |
545 | cld ; |
546 | xor di,di ; start at ES:DI = f000:0 |
547 | award_loop: push di ; save DI |
548 | mov si,award_string ; scan for award_string |
549 | mov cx,7 ; length of award_string = 7dw |
550 | repz cmpsw ; compare |
551 | pop di ; restore DI |
552 | jcxz award_found ; jmp if found |
553 | inc di ; not found, inc di |
554 | jno award_loop ; |
555 | ; |
556 | award_failed: pop es ; No, not this way :-(( |
557 | award_fail2: ; |
558 | ; |
559 | %ifdef DEBUG_MESSAGES ; |
560 | ; |
561 | mov si,award_not_fail ; display failure ... |
562 | call writemsg ; |
563 | %endif ; |
564 | mov eax,[award_oldint13] ; restore the original int |
565 | or eax,eax ; 13 vector if there is one |
566 | jz spec_query_failed ; and try other workarounds |
567 | mov [13h*4],eax ; |
568 | jmp spec_query_failed ; |
569 | ; |
570 | award_found: mov eax,[es:di+0eh] ; load possible int 13 addr |
571 | pop es ; restore ES |
572 | ; |
573 | cmp eax,[award_oldint13] ; give up if this is the |
574 | jz award_failed ; active int 13 vector, |
575 | mov [13h*4],eax ; otherwise change 0:13h*4 |
576 | ; |
577 | ; |
578 | %ifdef DEBUG_MESSAGES ; |
579 | ; |
580 | push eax ; display message and |
581 | mov si,award_not_new ; new vector address |
582 | call writemsg ; |
583 | pop eax ; |
584 | call writehex8 ; |
585 | mov si,award_not_crlf ; |
586 | call writestr_early ; |
587 | %endif ; |
588 | mov ax,4B01h ; try to read the spec packet |
589 | mov dl,[DriveNumber] ; now ... it should not fail |
590 | mov si,spec_packet ; any longer |
591 | int 13h ; |
592 | jc award_fail2 ; |
593 | ; |
594 | %ifdef DEBUG_MESSAGES ; |
595 | ; |
596 | mov si,award_not_succ ; display our SUCCESS |
597 | call writemsg ; |
598 | %endif ; |
599 | jmp found_drive ; and leave error recovery code |
600 | ; |
601 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
602 | ;; End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de |
603 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
604 | |
605 | |
606 | ; INT 13h, AX=4B01h, DL=<passed in value> failed. |
607 | ; Try to scan the entire 80h-FFh from the end. |
608 | |
609 | spec_query_failed: |
610 | |
611 | ; some code moved to BrokenAwardHack |
612 | |
613 | mov dl,0FFh |
614 | .test_loop: pusha |
615 | mov ax,4B01h |
616 | mov si,spec_packet |
617 | mov byte [si],13h ; Size of buffer |
618 | call int13 |
619 | popa |
620 | jc .still_broken |
621 | |
622 | mov si,maybe_msg |
623 | call writemsg |
624 | mov al,dl |
625 | call writehex2 |
626 | call crlf |
627 | |
628 | cmp byte [sp_drive],dl |
629 | jne .maybe_broken |
630 | |
631 | ; Okay, good enough... |
632 | mov si,alright_msg |
633 | call writemsg |
634 | .found_drive0: mov [DriveNumber],dl |
635 | .found_drive: jmp found_drive |
636 | |
637 | ; Award BIOS 4.51 apparently passes garbage in sp_drive, |
638 | ; but if this was the drive number originally passed in |
639 | ; DL then consider it "good enough" |
640 | .maybe_broken: |
641 | mov al,[DriveNumber] |
642 | cmp al,dl |
643 | je .found_drive |
644 | |
645 | ; Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02 |
646 | ; passes garbage in sp_drive, and the drive number originally |
647 | ; passed in DL does not have 80h bit set. |
648 | or al,80h |
649 | cmp al,dl |
650 | je .found_drive0 |
651 | |
652 | .still_broken: dec dx |
653 | cmp dl, 80h |
654 | jnb .test_loop |
655 | |
656 | ; No spec packet anywhere. Some particularly pathetic |
657 | ; BIOSes apparently don't even implement function |
658 | ; 4B01h, so we can't query a spec packet no matter |
659 | ; what. If we got a drive number in DL, then try to |
660 | ; use it, and if it works, then well... |
661 | mov dl,[DriveNumber] |
662 | cmp dl,81h ; Should be 81-FF at least |
663 | jb fatal_error ; If not, it's hopeless |
664 | |
665 | ; Write a warning to indicate we're on *very* thin ice now |
666 | mov si,nospec_msg |
667 | call writemsg |
668 | mov al,dl |
669 | call writehex2 |
670 | call crlf |
671 | mov si,trysbm_msg |
672 | call writemsg |
673 | jmp .found_drive ; Pray that this works... |
674 | |
675 | fatal_error: |
676 | mov si,nothing_msg |
677 | call writemsg |
678 | |
679 | .norge: jmp short .norge |
680 | |
681 | ; Information message (DS:SI) output |
682 | ; Prefix with "isolinux: " |
683 | ; |
684 | writemsg: push ax |
685 | push si |
686 | mov si,isolinux_str |
687 | call writestr_early |
688 | pop si |
689 | call writestr_early |
690 | pop ax |
691 | ret |
692 | |
693 | ; |
694 | ; Write a character to the screen. There is a more "sophisticated" |
695 | ; version of this in the subsequent code, so we patch the pointer |
696 | ; when appropriate. |
697 | ; |
698 | |
699 | writechr: |
700 | jmp near writechr_simple ; 3-byte jump |
701 | |
702 | writechr_simple: |
703 | pushfd |
704 | pushad |
705 | mov ah,0Eh |
706 | xor bx,bx |
707 | int 10h |
708 | popad |
709 | popfd |
710 | ret |
711 | |
712 | ; |
713 | ; int13: save all the segment registers and call INT 13h. |
714 | ; Some CD-ROM BIOSes have been found to corrupt segment registers |
715 | ; and/or disable interrupts. |
716 | ; |
717 | int13: |
718 | pushf |
719 | push bp |
720 | push ds |
721 | push es |
722 | push fs |
723 | push gs |
724 | int 13h |
725 | mov bp,sp |
726 | setc [bp+10] ; Propagate CF to the caller |
727 | pop gs |
728 | pop fs |
729 | pop es |
730 | pop ds |
731 | pop bp |
732 | popf |
733 | ret |
734 | |
735 | ; |
736 | ; Get one sector. Convenience entry point. |
737 | ; |
738 | getonesec: |
739 | mov bp,1 |
740 | ; Fall through to getlinsec |
741 | |
742 | ; |
743 | ; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors. |
744 | ; |
745 | ; Input: |
746 | ; EAX - Linear sector number |
747 | ; ES:BX - Target buffer |
748 | ; BP - Sector count |
749 | ; |
750 | getlinsec: jmp word [cs:GetlinsecPtr] |
751 | |
752 | %ifndef DEBUG_MESSAGES |
753 | |
754 | ; |
755 | ; First, the variants that we use when actually loading off a disk |
756 | ; (hybrid mode.) These are adapted versions of the equivalent routines |
757 | ; in ldlinux.asm. |
758 | ; |
759 | |
760 | ; |
761 | ; getlinsec_ebios: |
762 | ; |
763 | ; getlinsec implementation for floppy/HDD EBIOS (EDD) |
764 | ; |
765 | getlinsec_ebios: |
766 | xor edx,edx |
767 | shld edx,eax,2 |
768 | shl eax,2 ; Convert to HDD sectors |
769 | add eax,[bsHidden] |
770 | adc edx,[bsHidden+4] |
771 | shl bp,2 |
772 | |
773 | .loop: |
774 | push bp ; Sectors left |
775 | .retry2: |
776 | call maxtrans ; Enforce maximum transfer size |
777 | movzx edi,bp ; Sectors we are about to read |
778 | mov cx,retry_count |
779 | .retry: |
780 | |
781 | ; Form DAPA on stack |
782 | push edx |
783 | push eax |
784 | push es |
785 | push bx |
786 | push di |
787 | push word 16 |
788 | mov si,sp |
789 | pushad |
790 | mov dl,[DriveNumber] |
791 | push ds |
792 | push ss |
793 | pop ds ; DS <- SS |
794 | mov ah,42h ; Extended Read |
795 | call int13 |
796 | pop ds |
797 | popad |
798 | lea sp,[si+16] ; Remove DAPA |
799 | jc .error |
800 | pop bp |
801 | add eax,edi ; Advance sector pointer |
802 | adc edx,0 |
803 | sub bp,di ; Sectors left |
804 | shl di,9 ; 512-byte sectors |
805 | add bx,di ; Advance buffer pointer |
806 | and bp,bp |
807 | jnz .loop |
808 | |
809 | ret |
810 | |
811 | .error: |
812 | ; Some systems seem to get "stuck" in an error state when |
813 | ; using EBIOS. Doesn't happen when using CBIOS, which is |
814 | ; good, since some other systems get timeout failures |
815 | ; waiting for the floppy disk to spin up. |
816 | |
817 | pushad ; Try resetting the device |
818 | xor ax,ax |
819 | mov dl,[DriveNumber] |
820 | call int13 |
821 | popad |
822 | loop .retry ; CX-- and jump if not zero |
823 | |
824 | ;shr word [MaxTransfer],1 ; Reduce the transfer size |
825 | ;jnz .retry2 |
826 | |
827 | ; Total failure. Try falling back to CBIOS. |
828 | mov word [GetlinsecPtr], getlinsec_cbios |
829 | ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer |
830 | |
831 | pop bp |
832 | jmp getlinsec_cbios.loop |
833 | |
834 | ; |
835 | ; getlinsec_cbios: |
836 | ; |
837 | ; getlinsec implementation for legacy CBIOS |
838 | ; |
839 | getlinsec_cbios: |
840 | xor edx,edx |
841 | shl eax,2 ; Convert to HDD sectors |
842 | add eax,[bsHidden] |
843 | shl bp,2 |
844 | |
845 | .loop: |
846 | push edx |
847 | push eax |
848 | push bp |
849 | push bx |
850 | |
851 | movzx esi,word [bsSecPerTrack] |
852 | movzx edi,word [bsHeads] |
853 | ; |
854 | ; Dividing by sectors to get (track,sector): we may have |
855 | ; up to 2^18 tracks, so we need to use 32-bit arithmetric. |
856 | ; |
857 | div esi |
858 | xor cx,cx |
859 | xchg cx,dx ; CX <- sector index (0-based) |
860 | ; EDX <- 0 |
861 | ; eax = track # |
862 | div edi ; Convert track to head/cyl |
863 | |
864 | ; We should test this, but it doesn't fit... |
865 | ; cmp eax,1023 |
866 | ; ja .error |
867 | |
868 | ; |
869 | ; Now we have AX = cyl, DX = head, CX = sector (0-based), |
870 | ; BP = sectors to transfer, SI = bsSecPerTrack, |
871 | ; ES:BX = data target |
872 | ; |
873 | |
874 | call maxtrans ; Enforce maximum transfer size |
875 | |
876 | ; Must not cross track boundaries, so BP <= SI-CX |
877 | sub si,cx |
878 | cmp bp,si |
879 | jna .bp_ok |
880 | mov bp,si |
881 | .bp_ok: |
882 | |
883 | shl ah,6 ; Because IBM was STOOPID |
884 | ; and thought 8 bits were enough |
885 | ; then thought 10 bits were enough... |
886 | inc cx ; Sector numbers are 1-based, sigh |
887 | or cl,ah |
888 | mov ch,al |
889 | mov dh,dl |
890 | mov dl,[DriveNumber] |
891 | xchg ax,bp ; Sector to transfer count |
892 | mov ah,02h ; Read sectors |
893 | mov bp,retry_count |
894 | .retry: |
895 | pushad |
896 | call int13 |
897 | popad |
898 | jc .error |
899 | .resume: |
900 | movzx ecx,al ; ECX <- sectors transferred |
901 | shl ax,9 ; Convert sectors in AL to bytes in AX |
902 | pop bx |
903 | add bx,ax |
904 | pop bp |
905 | pop eax |
906 | pop edx |
907 | add eax,ecx |
908 | sub bp,cx |
909 | jnz .loop |
910 | ret |
911 | |
912 | .error: |
913 | dec bp |
914 | jnz .retry |
915 | |
916 | xchg ax,bp ; Sectors transferred <- 0 |
917 | shr word [MaxTransfer],1 |
918 | jnz .resume |
919 | jmp disk_error |
920 | |
921 | ; |
922 | ; Truncate BP to MaxTransfer |
923 | ; |
924 | maxtrans: |
925 | cmp bp,[MaxTransfer] |
926 | jna .ok |
927 | mov bp,[MaxTransfer] |
928 | .ok: ret |
929 | |
930 | %endif |
931 | |
932 | ; |
933 | ; This is the variant we use for real CD-ROMs: |
934 | ; LBA, 2K sectors, some special error handling. |
935 | ; |
936 | getlinsec_cdrom: |
937 | mov si,dapa ; Load up the DAPA |
938 | mov [si+4],bx |
939 | mov [si+6],es |
940 | mov [si+8],eax |
941 | .loop: |
942 | push bp ; Sectors left |
943 | cmp bp,[MaxTransferCD] |
944 | jbe .bp_ok |
945 | mov bp,[MaxTransferCD] |
946 | .bp_ok: |
947 | mov [si+2],bp |
948 | push si |
949 | mov dl,[DriveNumber] |
950 | mov ah,42h ; Extended Read |
951 | call xint13 |
952 | pop si |
953 | pop bp |
954 | movzx eax,word [si+2] ; Sectors we read |
955 | add [si+8],eax ; Advance sector pointer |
956 | sub bp,ax ; Sectors left |
957 | shl ax,SECTOR_SHIFT-4 ; 2048-byte sectors -> segment |
958 | add [si+6],ax ; Advance buffer pointer |
959 | and bp,bp |
960 | jnz .loop |
961 | mov eax,[si+8] ; Next sector |
962 | ret |
963 | |
964 | ; INT 13h with retry |
965 | xint13: mov byte [RetryCount],retry_count |
966 | .try: pushad |
967 | call int13 |
968 | jc .error |
969 | add sp,byte 8*4 ; Clean up stack |
970 | ret |
971 | .error: |
972 | mov [DiskError],ah ; Save error code |
973 | popad |
974 | mov [DiskSys],ax ; Save system call number |
975 | dec byte [RetryCount] |
976 | jz .real_error |
977 | push ax |
978 | mov al,[RetryCount] |
979 | mov ah,[dapa+2] ; Sector transfer count |
980 | cmp al,2 ; Only 2 attempts left |
981 | ja .nodanger |
982 | mov ah,1 ; Drop transfer size to 1 |
983 | jmp short .setsize |
984 | .nodanger: |
985 | cmp al,retry_count-2 |
986 | ja .again ; First time, just try again |
987 | shr ah,1 ; Otherwise, try to reduce |
988 | adc ah,0 ; the max transfer size, but not to 0 |
989 | .setsize: |
990 | mov [MaxTransferCD],ah |
991 | mov [dapa+2],ah |
992 | .again: |
993 | pop ax |
994 | jmp .try |
995 | |
996 | .real_error: mov si,diskerr_msg |
997 | call writemsg |
998 | mov al,[DiskError] |
999 | call writehex2 |
1000 | mov si,oncall_str |
1001 | call writestr_early |
1002 | mov ax,[DiskSys] |
1003 | call writehex4 |
1004 | mov si,ondrive_str |
1005 | call writestr_early |
1006 | mov al,dl |
1007 | call writehex2 |
1008 | call crlf |
1009 | ; Fall through to kaboom |
1010 | |
1011 | ; |
1012 | ; kaboom: write a message and bail out. Wait for a user keypress, |
1013 | ; then do a hard reboot. |
1014 | ; |
1015 | disk_error: |
1016 | kaboom: |
1017 | RESET_STACK_AND_SEGS AX |
1018 | mov si,err_bootfailed |
1019 | call writestr |
1020 | call getchar |
1021 | cli |
1022 | mov word [BIOS_magic],0 ; Cold reboot |
1023 | jmp 0F000h:0FFF0h ; Reset vector address |
1024 | |
1025 | ; ----------------------------------------------------------------------------- |
1026 | ; Common modules needed in the first sector |
1027 | ; ----------------------------------------------------------------------------- |
1028 | |
1029 | %include "writestr.inc" ; String output |
1030 | writestr_early equ writestr |
1031 | %include "writehex.inc" ; Hexadecimal output |
1032 | |
1033 | ; ----------------------------------------------------------------------------- |
1034 | ; Data that needs to be in the first sector |
1035 | ; ----------------------------------------------------------------------------- |
1036 | |
1037 | syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0 |
1038 | copyright_str db ' Copyright (C) 1994-' |
1039 | asciidec YEAR |
1040 | db ' H. Peter Anvin et al', CR, LF, 0 |
1041 | isolinux_str db 'isolinux: ', 0 |
1042 | %ifdef DEBUG_MESSAGES |
1043 | startup_msg: db 'Starting up, DL = ', 0 |
1044 | spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0 |
1045 | secsize_msg: db 'Sector size ', 0 |
1046 | offset_msg: db 'Main image LBA = ', 0 |
1047 | size_msg: db 'Sectors to load = ', 0 |
1048 | loaded_msg: db 'Loaded boot image, verifying...', CR, LF, 0 |
1049 | verify_msg: db 'Image checksum verified.', CR, LF, 0 |
1050 | allread_msg db 'Main image read, jumping to main code...', CR, LF, 0 |
1051 | %endif |
1052 | noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0 |
1053 | noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', CR, LF, 0 |
1054 | spec_err_msg: db 'Loading spec packet failed, trying to wing it...', CR, LF, 0 |
1055 | maybe_msg: db 'Found something at drive = ', 0 |
1056 | alright_msg: db 'Looks reasonable, continuing...', CR, LF, 0 |
1057 | nospec_msg db 'Extremely broken BIOS detected, last attempt with drive = ', 0 |
1058 | nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF |
1059 | trysbm_msg db 'See http://syslinux.zytor.com/sbm for more information.', CR, LF, 0 |
1060 | diskerr_msg: db 'Disk error ', 0 |
1061 | oncall_str: db ', AX = ',0 |
1062 | ondrive_str: db ', drive ', 0 |
1063 | checkerr_msg: db 'Image checksum error, sorry...', CR, LF, 0 |
1064 | |
1065 | err_bootfailed db CR, LF, 'Boot failed: press a key to retry...' |
1066 | bailmsg equ err_bootfailed |
1067 | crlf_msg db CR, LF |
1068 | null_msg db 0 |
1069 | |
1070 | bios_cdrom_str db 'ETCD', 0 |
1071 | %ifndef DEBUG_MESSAGES |
1072 | bios_cbios_str db 'CHDD', 0 |
1073 | bios_ebios_str db 'EHDD' ,0 |
1074 | %endif |
1075 | |
1076 | alignz 4 |
1077 | bios_cdrom: dw getlinsec_cdrom, bios_cdrom_str |
1078 | %ifndef DEBUG_MESSAGES |
1079 | bios_cbios: dw getlinsec_cbios, bios_cbios_str |
1080 | bios_ebios: dw getlinsec_ebios, bios_ebios_str |
1081 | %endif |
1082 | |
1083 | ; Maximum transfer size |
1084 | MaxTransfer dw 127 ; Hard disk modes |
1085 | MaxTransferCD dw 32 ; CD mode |
1086 | |
1087 | rl_checkpt equ $ ; Must be <= 800h |
1088 | |
1089 | ; This pads to the end of sector 0 and errors out on |
1090 | ; overflow. |
1091 | times 2048-($-$$) db 0 |
1092 | |
1093 | ; ---------------------------------------------------------------------------- |
1094 | ; End of code and data that have to be in the first sector |
1095 | ; ---------------------------------------------------------------------------- |
1096 | |
1097 | all_read: |
1098 | |
1099 | ; Test tracers |
1100 | TRACER 'T' |
1101 | TRACER '>' |
1102 | |
1103 | ; |
1104 | ; Common initialization code |
1105 | ; |
1106 | %include "init.inc" |
1107 | %include "cpuinit.inc" |
1108 | |
1109 | ; Patch the writechr routine to point to the full code |
1110 | mov word [writechr+1], writechr_full-(writechr+3) |
1111 | |
1112 | ; Tell the user we got this far... |
1113 | %ifndef DEBUG_MESSAGES ; Gets messy with debugging on |
1114 | mov si,copyright_str |
1115 | call writestr_early |
1116 | %endif |
1117 | |
1118 | ; |
1119 | ; Now we're all set to start with our *real* business. First load the |
1120 | ; configuration file (if any) and parse it. |
1121 | ; |
1122 | ; In previous versions I avoided using 32-bit registers because of a |
1123 | ; rumour some BIOSes clobbered the upper half of 32-bit registers at |
1124 | ; random. I figure, though, that if there are any of those still left |
1125 | ; they probably won't be trying to install Linux on them... |
1126 | ; |
1127 | ; The code is still ripe with 16-bitisms, though. Not worth the hassle |
1128 | ; to take'm out. In fact, we may want to put them back if we're going |
1129 | ; to boot ELKS at some point. |
1130 | ; |
1131 | |
1132 | ; |
1133 | ; Now, we need to sniff out the actual filesystem data structures. |
1134 | ; mkisofs gave us a pointer to the primary volume descriptor |
1135 | ; (which will be at 16 only for a single-session disk!); from the PVD |
1136 | ; we should be able to find the rest of what we need to know. |
1137 | ; |
1138 | get_fs_structures: |
1139 | mov eax,[bi_pvd] |
1140 | mov bx,trackbuf |
1141 | call getonesec |
1142 | |
1143 | mov eax,[trackbuf+156+2] |
1144 | mov [RootDir+dir_lba],eax |
1145 | mov [CurrentDir+dir_lba],eax |
1146 | %ifdef DEBUG_MESSAGES |
1147 | mov si,dbg_rootdir_msg |
1148 | call writemsg |
1149 | call writehex8 |
1150 | call crlf |
1151 | %endif |
1152 | mov eax,[trackbuf+156+10] |
1153 | mov [RootDir+dir_len],eax |
1154 | mov [CurrentDir+dir_len],eax |
1155 | add eax,SECTOR_SIZE-1 |
1156 | shr eax,SECTOR_SHIFT |
1157 | mov [RootDir+dir_clust],eax |
1158 | mov [CurrentDir+dir_clust],eax |
1159 | |
1160 | ; Look for an isolinux directory, and if found, |
1161 | ; make it the current directory instead of the root |
1162 | ; directory. |
1163 | ; Also copy the name of the directory to CurrentDirName |
1164 | mov word [CurrentDirName],ROOT_DIR_WORD ; Write '/',0 to the CurrentDirName |
1165 | mov di,boot_dir ; Search for /boot/isolinux |
1166 | mov al,02h |
1167 | push di |
1168 | call searchdir_iso |
1169 | pop di |
1170 | jnz .found_dir |
1171 | mov di,isolinux_dir |
1172 | mov al,02h ; Search for /isolinux |
1173 | push di |
1174 | call searchdir_iso |
1175 | pop di |
1176 | jz .no_isolinux_dir |
1177 | .found_dir: |
1178 | ; Copy current directory name to CurrentDirName |
1179 | push si |
1180 | push di |
1181 | mov si,di |
1182 | mov di,CurrentDirName |
1183 | call strcpy |
1184 | mov byte [di],0 ;done in case it's not word aligned |
1185 | dec di |
1186 | mov byte [di],'/' |
1187 | pop di |
1188 | pop si |
1189 | |
1190 | mov [CurrentDir+dir_len],eax |
1191 | mov eax,[si+file_left] |
1192 | mov [CurrentDir+dir_clust],eax |
1193 | xor eax,eax ; Free this file pointer entry |
1194 | xchg eax,[si+file_sector] |
1195 | mov [CurrentDir+dir_lba],eax |
1196 | %ifdef DEBUG_MESSAGES |
1197 | push si |
1198 | mov si,dbg_isodir_msg |
1199 | call writemsg |
1200 | pop si |
1201 | call writehex8 |
1202 | call crlf |
1203 | %endif |
1204 | .no_isolinux_dir: |
1205 | |
1206 | ; |
1207 | ; Locate the configuration file |
1208 | ; |
1209 | load_config: |
1210 | %ifdef DEBUG_MESSAGES |
1211 | mov si,dbg_config_msg |
1212 | call writemsg |
1213 | %endif |
1214 | |
1215 | mov si,config_name |
1216 | mov di,ConfigName |
1217 | call strcpy |
1218 | |
1219 | mov di,ConfigName |
1220 | call open |
1221 | jz no_config_file ; Not found or empty |
1222 | |
1223 | %ifdef DEBUG_MESSAGES |
1224 | mov si,dbg_configok_msg |
1225 | call writemsg |
1226 | %endif |
1227 | |
1228 | ; |
1229 | ; Now we have the config file open. Parse the config file and |
1230 | ; run the user interface. |
1231 | ; |
1232 | %include "ui.inc" |
1233 | |
1234 | ; |
1235 | ; Enable disk emulation. The kind of disk we emulate is dependent on the |
1236 | ; size of the file: 1200K, 1440K or 2880K floppy, otherwise harddisk. |
1237 | ; |
1238 | is_disk_image: |
1239 | TRACER CR |
1240 | TRACER LF |
1241 | TRACER 'D' |
1242 | TRACER ':' |
1243 | |
1244 | mov edx,eax ; File size |
1245 | mov di,img_table |
1246 | mov cx,img_table_count |
1247 | mov eax,[si+file_sector] ; Starting LBA of file |
1248 | mov [dsp_lba],eax ; Location of file |
1249 | mov byte [dsp_drive], 0 ; 00h floppy, 80h hard disk |
1250 | .search_table: |
1251 | TRACER 't' |
1252 | mov eax,[di+4] |
1253 | cmp edx,[di] |
1254 | je .type_found |
1255 | add di,8 |
1256 | loop .search_table |
1257 | |
1258 | ; Hard disk image. Need to examine the partition table |
1259 | ; in order to deduce the C/H/S geometry. Sigh. |
1260 | .hard_disk_image: |
1261 | TRACER 'h' |
1262 | cmp edx,512 |
1263 | jb .bad_image |
1264 | |
1265 | mov bx,trackbuf |
1266 | mov cx,1 ; Load 1 sector |
1267 | call getfssec |
1268 | |
1269 | cmp word [trackbuf+510],0aa55h ; Boot signature |
1270 | jne .bad_image ; Image not bootable |
1271 | |
1272 | mov cx,4 ; 4 partition entries |
1273 | mov di,trackbuf+446 ; Start of partition table |
1274 | |
1275 | xor ax,ax ; Highest sector(al) head(ah) |
1276 | |
1277 | .part_scan: |
1278 | cmp byte [di+4], 0 |
1279 | jz .part_loop |
1280 | lea si,[di+1] |
1281 | call .hs_check |
1282 | add si,byte 4 |
1283 | call .hs_check |
1284 | .part_loop: |
1285 | add di,byte 16 |
1286 | loop .part_scan |
1287 | |
1288 | push eax ; H/S |
1289 | push edx ; File size |
1290 | mov bl,ah |
1291 | xor bh,bh |
1292 | inc bx ; # of heads in BX |
1293 | xor ah,ah ; # of sectors in AX |
1294 | cwde ; EAX[31:16] <- 0 |
1295 | mul bx |
1296 | shl eax,9 ; Convert to bytes |
1297 | ; Now eax contains the number of bytes per cylinder |
1298 | pop ebx ; File size |
1299 | xor edx,edx |
1300 | div ebx |
1301 | and edx,edx |
1302 | jz .no_remainder |
1303 | inc eax ; Fractional cylinder... |
1304 | ; Now (e)ax contains the number of cylinders |
1305 | .no_remainder: cmp eax,1024 |
1306 | jna .ok_cyl |
1307 | mov ax,1024 ; Max possible # |
1308 | .ok_cyl: dec ax ; Convert to max cylinder no |
1309 | pop ebx ; S(bl) H(bh) |
1310 | shl ah,6 |
1311 | or bl,ah |
1312 | xchg ax,bx |
1313 | shl eax,16 |
1314 | mov ah,bl |
1315 | mov al,4 ; Hard disk boot |
1316 | mov byte [dsp_drive], 80h ; Drive 80h = hard disk |
1317 | |
1318 | .type_found: |
1319 | TRACER 'T' |
1320 | mov bl,[sp_media] |
1321 | and bl,0F0h ; Copy controller info bits |
1322 | or al,bl |
1323 | mov [dsp_media],al ; Emulation type |
1324 | shr eax,8 |
1325 | mov [dsp_chs],eax ; C/H/S geometry |
1326 | mov ax,[sp_devspec] ; Copy device spec |
1327 | mov [dsp_devspec],ax |
1328 | mov al,[sp_controller] ; Copy controller index |
1329 | mov [dsp_controller],al |
1330 | |
1331 | TRACER 'V' |
1332 | call vgaclearmode ; Reset video |
1333 | |
1334 | mov ax,4C00h ; Enable emulation and boot |
1335 | mov si,dspec_packet |
1336 | mov dl,[DriveNumber] |
1337 | lss sp,[InitStack] |
1338 | TRACER 'X' |
1339 | |
1340 | call int13 |
1341 | |
1342 | ; If this returns, we have problems |
1343 | .bad_image: |
1344 | mov si,err_disk_image |
1345 | call writestr |
1346 | jmp enter_command |
1347 | |
1348 | ; |
1349 | ; Look for the highest seen H/S geometry |
1350 | ; We compute cylinders separately |
1351 | ; |
1352 | .hs_check: |
1353 | mov bl,[si] ; Head # |
1354 | cmp bl,ah |
1355 | jna .done_track |
1356 | mov ah,bl ; New highest head # |
1357 | .done_track: mov bl,[si+1] |
1358 | and bl,3Fh ; Sector # |
1359 | cmp bl,al |
1360 | jna .done_sector |
1361 | mov al,bl |
1362 | .done_sector: ret |
1363 | |
1364 | ; |
1365 | ; close_file: |
1366 | ; Deallocates a file structure (pointer in SI) |
1367 | ; Assumes CS == DS. |
1368 | ; |
1369 | close_file: |
1370 | and si,si |
1371 | jz .closed |
1372 | mov dword [si],0 ; First dword == file_left |
1373 | xor si,si |
1374 | .closed: ret |
1375 | |
1376 | ; |
1377 | ; searchdir: |
1378 | ; |
1379 | ; Open a file |
1380 | ; |
1381 | ; On entry: |
1382 | ; DS:DI = filename |
1383 | ; If successful: |
1384 | ; ZF clear |
1385 | ; SI = file pointer |
1386 | ; EAX = file length in bytes |
1387 | ; If unsuccessful |
1388 | ; ZF set |
1389 | ; |
1390 | ; Assumes CS == DS == ES, and trashes BX and CX. |
1391 | ; |
1392 | ; searchdir_iso is a special entry point for ISOLINUX only. In addition |
1393 | ; to the above, searchdir_iso passes a file flag mask in AL. This is useful |
1394 | ; for searching for directories. |
1395 | ; |
1396 | alloc_failure: |
1397 | xor ax,ax ; ZF <- 1 |
1398 | ret |
1399 | |
1400 | searchdir: |
1401 | xor al,al |
1402 | searchdir_iso: |
1403 | mov [ISOFlags],al |
1404 | TRACER 'S' |
1405 | call allocate_file ; Temporary file structure for directory |
1406 | jnz alloc_failure |
1407 | push es |
1408 | push ds |
1409 | pop es ; ES = DS |
1410 | mov si,CurrentDir |
1411 | cmp byte [di],'/' ; If filename begins with slash |
1412 | jne .not_rooted |
1413 | inc di ; Skip leading slash |
1414 | mov si,RootDir ; Reference root directory instead |
1415 | .not_rooted: |
1416 | mov eax,[si+dir_clust] |
1417 | mov [bx+file_left],eax |
1418 | shl eax,SECTOR_SHIFT |
1419 | mov [bx+file_bytesleft],eax |
1420 | mov eax,[si+dir_lba] |
1421 | mov [bx+file_sector],eax |
1422 | mov edx,[si+dir_len] |
1423 | |
1424 | .look_for_slash: |
1425 | mov ax,di |
1426 | .scan: |
1427 | mov cl,[di] |
1428 | inc di |
1429 | and cl,cl |
1430 | jz .isfile |
1431 | cmp cl,'/' |
1432 | jne .scan |
1433 | mov [di-1],byte 0 ; Terminate at directory name |
1434 | mov cl,02h ; Search for directory |
1435 | xchg cl,[ISOFlags] |
1436 | |
1437 | push di ; Save these... |
1438 | push cx |
1439 | |
1440 | ; Create recursion stack frame... |
1441 | push word .resume ; Where to "return" to |
1442 | push es |
1443 | .isfile: xchg ax,di |
1444 | |
1445 | .getsome: |
1446 | ; Get a chunk of the directory |
1447 | ; This relies on the fact that ISOLINUX doesn't change SI |
1448 | mov si,trackbuf |
1449 | TRACER 'g' |
1450 | pushad |
1451 | xchg bx,si |
1452 | mov cx,[BufSafe] |
1453 | call getfssec |
1454 | popad |
1455 | |
1456 | .compare: |
1457 | movzx eax,byte [si] ; Length of directory entry |
1458 | cmp al,33 |
1459 | jb .next_sector |
1460 | TRACER 'c' |
1461 | mov cl,[si+25] |
1462 | xor cl,[ISOFlags] |
1463 | test cl, byte 8Eh ; Unwanted file attributes! |
1464 | jnz .not_file |
1465 | pusha |
1466 | movzx cx,byte [si+32] ; File identifier length |
1467 | add si,byte 33 ; File identifier offset |
1468 | TRACER 'i' |
1469 | call iso_compare_names |
1470 | popa |
1471 | je .success |
1472 | .not_file: |
1473 | sub edx,eax ; Decrease bytes left |
1474 | jbe .failure |
1475 | add si,ax ; Advance pointer |
1476 | |
1477 | .check_overrun: |
1478 | ; Did we finish the buffer? |
1479 | cmp si,trackbuf+trackbufsize |
1480 | jb .compare ; No, keep going |
1481 | |
1482 | jmp short .getsome ; Get some more directory |
1483 | |
1484 | .next_sector: |
1485 | ; Advance to the beginning of next sector |
1486 | lea ax,[si+SECTOR_SIZE-1] |
1487 | and ax,~(SECTOR_SIZE-1) |
1488 | sub ax,si |
1489 | jmp short .not_file ; We still need to do length checks |
1490 | |
1491 | .failure: xor eax,eax ; ZF = 1 |
1492 | mov [bx+file_sector],eax |
1493 | pop es |
1494 | ret |
1495 | |
1496 | .success: |
1497 | mov eax,[si+2] ; Location of extent |
1498 | mov [bx+file_sector],eax |
1499 | mov eax,[si+10] ; Data length |
1500 | mov [bx+file_bytesleft],eax |
1501 | push eax |
1502 | add eax,SECTOR_SIZE-1 |
1503 | shr eax,SECTOR_SHIFT |
1504 | mov [bx+file_left],eax |
1505 | pop eax |
1506 | jz .failure ; Empty file? |
1507 | ; ZF = 0 |
1508 | mov si,bx |
1509 | pop es |
1510 | ret |
1511 | |
1512 | .resume: ; We get here if we were only doing part of a lookup |
1513 | ; This relies on the fact that .success returns bx == si |
1514 | xchg edx,eax ; Directory length in edx |
1515 | pop cx ; Old ISOFlags |
1516 | pop di ; Next filename pointer |
1517 | mov byte [di-1], '/' ; Restore slash |
1518 | mov [ISOFlags],cl ; Restore the flags |
1519 | jz .failure ; Did we fail? If so fail for real! |
1520 | jmp .look_for_slash ; Otherwise, next level |
1521 | |
1522 | ; |
1523 | ; allocate_file: Allocate a file structure |
1524 | ; |
1525 | ; If successful: |
1526 | ; ZF set |
1527 | ; BX = file pointer |
1528 | ; In unsuccessful: |
1529 | ; ZF clear |
1530 | ; |
1531 | allocate_file: |
1532 | TRACER 'a' |
1533 | push cx |
1534 | mov bx,Files |
1535 | mov cx,MAX_OPEN |
1536 | .check: cmp dword [bx], byte 0 |
1537 | je .found |
1538 | add bx,open_file_t_size ; ZF = 0 |
1539 | loop .check |
1540 | ; ZF = 0 if we fell out of the loop |
1541 | .found: pop cx |
1542 | ret |
1543 | |
1544 | ; |
1545 | ; iso_compare_names: |
1546 | ; Compare the names DS:SI and DS:DI and report if they are |
1547 | ; equal from an ISO 9660 perspective. SI is the name from |
1548 | ; the filesystem; CX indicates its length, and ';' terminates. |
1549 | ; DI is expected to end with a null. |
1550 | ; |
1551 | ; Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment |
1552 | ; |
1553 | |
1554 | iso_compare_names: |
1555 | ; First, terminate and canonicalize input filename |
1556 | push di |
1557 | mov di,ISOFileName |
1558 | .canon_loop: jcxz .canon_end |
1559 | lodsb |
1560 | dec cx |
1561 | cmp al,';' |
1562 | je .canon_end |
1563 | and al,al |
1564 | je .canon_end |
1565 | stosb |
1566 | cmp di,ISOFileNameEnd-1 ; Guard against buffer overrun |
1567 | jb .canon_loop |
1568 | .canon_end: |
1569 | cmp di,ISOFileName |
1570 | jbe .canon_done |
1571 | cmp byte [di-1],'.' ; Remove terminal dots |
1572 | jne .canon_done |
1573 | dec di |
1574 | jmp short .canon_end |
1575 | .canon_done: |
1576 | mov [di],byte 0 ; Null-terminate string |
1577 | pop di |
1578 | mov si,ISOFileName |
1579 | .compare: |
1580 | lodsb |
1581 | mov ah,[di] |
1582 | inc di |
1583 | and ax,ax |
1584 | jz .success ; End of string for both |
1585 | and al,al ; Is either one end of string? |
1586 | jz .failure ; If so, failure |
1587 | and ah,ah |
1588 | jz .failure |
1589 | or ax,2020h ; Convert to lower case |
1590 | cmp al,ah |
1591 | je .compare |
1592 | .failure: and ax,ax ; ZF = 0 (at least one will be nonzero) |
1593 | .success: ret |
1594 | |
1595 | ; |
1596 | ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed |
1597 | ; to by ES:DI; ends on encountering any whitespace. |
1598 | ; DI is preserved. |
1599 | ; |
1600 | ; This verifies that a filename is < FILENAME_MAX characters, |
1601 | ; doesn't contain whitespace, zero-pads the output buffer, |
1602 | ; and removes trailing dots and redundant slashes, |
1603 | ; so "repe cmpsb" can do a compare, and the |
1604 | ; path-searching routine gets a bit of an easier job. |
1605 | ; |
1606 | mangle_name: |
1607 | push di |
1608 | push bx |
1609 | xor ax,ax |
1610 | mov cx,FILENAME_MAX-1 |
1611 | mov bx,di |
1612 | |
1613 | .mn_loop: |
1614 | lodsb |
1615 | cmp al,' ' ; If control or space, end |
1616 | jna .mn_end |
1617 | cmp al,ah ; Repeated slash? |
1618 | je .mn_skip |
1619 | xor ah,ah |
1620 | cmp al,'/' |
1621 | jne .mn_ok |
1622 | mov ah,al |
1623 | .mn_ok stosb |
1624 | .mn_skip: loop .mn_loop |
1625 | .mn_end: |
1626 | cmp bx,di ; At the beginning of the buffer? |
1627 | jbe .mn_zero |
1628 | cmp byte [es:di-1],'.' ; Terminal dot? |
1629 | je .mn_kill |
1630 | cmp byte [es:di-1],'/' ; Terminal slash? |
1631 | jne .mn_zero |
1632 | .mn_kill: dec di ; If so, remove it |
1633 | inc cx |
1634 | jmp short .mn_end |
1635 | .mn_zero: |
1636 | inc cx ; At least one null byte |
1637 | xor ax,ax ; Zero-fill name |
1638 | rep stosb |
1639 | pop bx |
1640 | pop di |
1641 | ret ; Done |
1642 | |
1643 | ; |
1644 | ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled |
1645 | ; filename to the conventional representation. This is needed |
1646 | ; for the BOOT_IMAGE= parameter for the kernel. |
1647 | ; |
1648 | ; DS:SI -> input mangled file name |
1649 | ; ES:DI -> output buffer |
1650 | ; |
1651 | ; On return, DI points to the first byte after the output name, |
1652 | ; which is set to a null byte. |
1653 | ; |
1654 | unmangle_name: call strcpy |
1655 | dec di ; Point to final null byte |
1656 | ret |
1657 | |
1658 | ; |
1659 | ; getfssec: Get multiple clusters from a file, given the file pointer. |
1660 | ; |
1661 | ; On entry: |
1662 | ; ES:BX -> Buffer |
1663 | ; SI -> File pointer |
1664 | ; CX -> Cluster count |
1665 | ; On exit: |
1666 | ; SI -> File pointer (or 0 on EOF) |
1667 | ; CF = 1 -> Hit EOF |
1668 | ; ECX -> Bytes actually read |
1669 | ; |
1670 | getfssec: |
1671 | TRACER 'F' |
1672 | push ds |
1673 | push cs |
1674 | pop ds ; DS <- CS |
1675 | |
1676 | movzx ecx,cx |
1677 | cmp ecx,[si+file_left] |
1678 | jna .ok_size |
1679 | mov ecx,[si+file_left] |
1680 | .ok_size: |
1681 | |
1682 | pushad |
1683 | mov eax,[si+file_sector] |
1684 | mov bp,cx |
1685 | TRACER 'l' |
1686 | call getlinsec |
1687 | popad |
1688 | |
1689 | ; ECX[31:16] == 0 here... |
1690 | add [si+file_sector],ecx |
1691 | sub [si+file_left],ecx |
1692 | shl ecx,SECTOR_SHIFT ; Convert to bytes |
1693 | cmp ecx,[si+file_bytesleft] |
1694 | jb .not_all |
1695 | mov ecx,[si+file_bytesleft] |
1696 | .not_all: sub [si+file_bytesleft],ecx |
1697 | jnz .ret ; CF = 0 in this case... |
1698 | push eax |
1699 | xor eax,eax |
1700 | mov [si+file_sector],eax ; Unused |
1701 | mov si,ax |
1702 | pop eax |
1703 | stc |
1704 | .ret: |
1705 | pop ds |
1706 | TRACER 'f' |
1707 | ret |
1708 | |
1709 | ; ----------------------------------------------------------------------------- |
1710 | ; Common modules |
1711 | ; ----------------------------------------------------------------------------- |
1712 | |
1713 | %include "getc.inc" ; getc et al |
1714 | %include "conio.inc" ; Console I/O |
1715 | %include "configinit.inc" ; Initialize configuration |
1716 | %include "parseconfig.inc" ; High-level config file handling |
1717 | %include "parsecmd.inc" ; Low-level config file handling |
1718 | %include "bcopy32.inc" ; 32-bit bcopy |
1719 | %include "loadhigh.inc" ; Load a file into high memory |
1720 | %include "font.inc" ; VGA font stuff |
1721 | %include "graphics.inc" ; VGA graphics |
1722 | %include "highmem.inc" ; High memory sizing |
1723 | %include "strcpy.inc" ; strcpy() |
1724 | %include "rawcon.inc" ; Console I/O w/o using the console functions |
1725 | %include "idle.inc" ; Idle handling |
1726 | %include "adv.inc" ; Auxillary Data Vector |
1727 | %include "localboot.inc" ; Disk-based local boot |
1728 | |
1729 | ; ----------------------------------------------------------------------------- |
1730 | ; Begin data section |
1731 | ; ----------------------------------------------------------------------------- |
1732 | |
1733 | section .data |
1734 | |
1735 | default_str db 'default', 0 |
1736 | default_len equ ($-default_str) |
1737 | boot_dir db '/boot' ; /boot/isolinux |
1738 | isolinux_dir db '/isolinux', 0 |
1739 | config_name db 'isolinux.cfg', 0 |
1740 | err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0 |
1741 | |
1742 | %ifdef DEBUG_MESSAGES |
1743 | dbg_rootdir_msg db 'Root directory at LBA = ', 0 |
1744 | dbg_isodir_msg db 'isolinux directory at LBA = ', 0 |
1745 | dbg_config_msg db 'About to load config file...', CR, LF, 0 |
1746 | dbg_configok_msg db 'Configuration file opened...', CR, LF, 0 |
1747 | %endif |
1748 | |
1749 | ; |
1750 | ; Config file keyword table |
1751 | ; |
1752 | %include "keywords.inc" |
1753 | |
1754 | ; |
1755 | ; Extensions to search for (in *forward* order). |
1756 | ; |
1757 | alignz 4 |
1758 | exten_table: db '.cbt' ; COMBOOT (specific) |
1759 | db '.img' ; Disk image |
1760 | db '.bin' ; CD boot sector |
1761 | db '.com' ; COMBOOT (same as DOS) |
1762 | db '.c32' ; COM32 |
1763 | exten_table_end: |
1764 | dd 0, 0 ; Need 8 null bytes here |
1765 | |
1766 | ; |
1767 | ; Floppy image table |
1768 | ; |
1769 | alignz 4 |
1770 | img_table_count equ 3 |
1771 | img_table: |
1772 | dd 1200*1024 ; 1200K floppy |
1773 | db 1 ; Emulation type |
1774 | db 80-1 ; Max cylinder |
1775 | db 15 ; Max sector |
1776 | db 2-1 ; Max head |
1777 | |
1778 | dd 1440*1024 ; 1440K floppy |
1779 | db 2 ; Emulation type |
1780 | db 80-1 ; Max cylinder |
1781 | db 18 ; Max sector |
1782 | db 2-1 ; Max head |
1783 | |
1784 | dd 2880*1024 ; 2880K floppy |
1785 | db 3 ; Emulation type |
1786 | db 80-1 ; Max cylinder |
1787 | db 36 ; Max sector |
1788 | db 2-1 ; Max head |
1789 | |
1790 | ; |
1791 | ; Misc initialized (data) variables |
1792 | ; |
1793 | |
1794 | ; |
1795 | ; Variables that are uninitialized in SYSLINUX but initialized here |
1796 | ; |
1797 | ; **** ISOLINUX:: We may have to make this flexible, based on what the |
1798 | ; **** BIOS expects our "sector size" to be. |
1799 | ; |
1800 | alignz 4 |
1801 | BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf |
1802 | BufSafeBytes dw trackbufsize ; = how many bytes? |
1803 | %ifndef DEPEND |
1804 | %if ( trackbufsize % SECTOR_SIZE ) != 0 |
1805 | %error trackbufsize must be a multiple of SECTOR_SIZE |
1806 | %endif |
1807 | %endif |