Annotation of /tags/mkinitrd-6_2_2/isolinux/ldlinux.asm
Parent Directory | Revision Log
Revision 532 -
(hide annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
Original Path: trunk/mkinitrd-magellan/isolinux/ldlinux.asm
File size: 36902 byte(s)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
Original Path: trunk/mkinitrd-magellan/isolinux/ldlinux.asm
File size: 36902 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | niro | 532 | ; -*- fundamental -*- (asm-mode sucks) |
2 | ; $Id: ldlinux.asm,v 1.1 2007-09-01 22:44:05 niro Exp $ | ||
3 | ; **************************************************************************** | ||
4 | ; | ||
5 | ; ldlinux.asm | ||
6 | ; | ||
7 | ; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This | ||
8 | ; functionality is good to have for installation floppies, where it may | ||
9 | ; be hard to find a functional Linux system to run LILO off. | ||
10 | ; | ||
11 | ; This program allows manipulation of the disk to take place entirely | ||
12 | ; from MS-LOSS, and can be especially useful in conjunction with the | ||
13 | ; umsdos filesystem. | ||
14 | ; | ||
15 | ; Copyright (C) 1994-2005 H. Peter Anvin | ||
16 | ; | ||
17 | ; This program is free software; you can redistribute it and/or modify | ||
18 | ; it under the terms of the GNU General Public License as published by | ||
19 | ; the Free Software Foundation, Inc., 53 Temple Place Ste 330, | ||
20 | ; Boston MA 02111-1307, USA; either version 2 of the License, or | ||
21 | ; (at your option) any later version; incorporated herein by reference. | ||
22 | ; | ||
23 | ; **************************************************************************** | ||
24 | |||
25 | %ifndef IS_MDSLINUX | ||
26 | %define IS_SYSLINUX 1 | ||
27 | %endif | ||
28 | %include "macros.inc" | ||
29 | %include "config.inc" | ||
30 | %include "kernel.inc" | ||
31 | %include "bios.inc" | ||
32 | %include "tracers.inc" | ||
33 | %include "layout.inc" | ||
34 | ; | ||
35 | ; Some semi-configurable constants... change on your own risk. | ||
36 | ; | ||
37 | my_id equ syslinux_id | ||
38 | FILENAME_MAX_LG2 equ 4 ; log2(Max filename size Including final null) | ||
39 | FILENAME_MAX equ 11 ; Max mangled filename size | ||
40 | NULLFILE equ ' ' ; First char space == null filename | ||
41 | NULLOFFSET equ 0 ; Position in which to look | ||
42 | retry_count equ 16 ; How patient are we with the disk? | ||
43 | %assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top | ||
44 | LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with | ||
45 | |||
46 | MAX_OPEN_LG2 equ 6 ; log2(Max number of open files) | ||
47 | MAX_OPEN equ (1 << MAX_OPEN_LG2) | ||
48 | |||
49 | SECTOR_SHIFT equ 9 | ||
50 | SECTOR_SIZE equ (1 << SECTOR_SHIFT) | ||
51 | |||
52 | ; | ||
53 | ; This is what we need to do when idle | ||
54 | ; | ||
55 | %macro RESET_IDLE 0 | ||
56 | ; Nothing | ||
57 | %endmacro | ||
58 | %macro DO_IDLE 0 | ||
59 | ; Nothing | ||
60 | %endmacro | ||
61 | |||
62 | ; | ||
63 | ; The following structure is used for "virtual kernels"; i.e. LILO-style | ||
64 | ; option labels. The options we permit here are `kernel' and `append | ||
65 | ; Since there is no room in the bottom 64K for all of these, we | ||
66 | ; stick them at vk_seg:0000 and copy them down before we need them. | ||
67 | ; | ||
68 | struc vkernel | ||
69 | vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!** | ||
70 | vk_rname: resb FILENAME_MAX ; Real name | ||
71 | vk_appendlen: resw 1 | ||
72 | alignb 4 | ||
73 | vk_append: resb max_cmd_len+1 ; Command line | ||
74 | alignb 4 | ||
75 | vk_end: equ $ ; Should be <= vk_size | ||
76 | endstruc | ||
77 | |||
78 | ; | ||
79 | ; Segment assignments in the bottom 640K | ||
80 | ; Stick to the low 512K in case we're using something like M-systems flash | ||
81 | ; which load a driver into low RAM (evil!!) | ||
82 | ; | ||
83 | ; 0000h - main code/data segment (and BIOS segment) | ||
84 | ; | ||
85 | real_mode_seg equ 4000h | ||
86 | cache_seg equ 3000h ; 64K area for metadata cache | ||
87 | vk_seg equ 2000h ; Virtual kernels | ||
88 | xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem | ||
89 | comboot_seg equ real_mode_seg ; COMBOOT image loading zone | ||
90 | |||
91 | ; | ||
92 | ; File structure. This holds the information for each currently open file. | ||
93 | ; | ||
94 | struc open_file_t | ||
95 | file_sector resd 1 ; Sector pointer (0 = structure free) | ||
96 | file_left resd 1 ; Number of sectors left | ||
97 | endstruc | ||
98 | |||
99 | %ifndef DEPEND | ||
100 | %if (open_file_t_size & (open_file_t_size-1)) | ||
101 | %error "open_file_t is not a power of 2" | ||
102 | %endif | ||
103 | %endif | ||
104 | |||
105 | ; --------------------------------------------------------------------------- | ||
106 | ; BEGIN CODE | ||
107 | ; --------------------------------------------------------------------------- | ||
108 | |||
109 | ; | ||
110 | ; Memory below this point is reserved for the BIOS and the MBR | ||
111 | ; | ||
112 | section .earlybss | ||
113 | trackbufsize equ 8192 | ||
114 | trackbuf resb trackbufsize ; Track buffer goes here | ||
115 | getcbuf resb trackbufsize | ||
116 | ; ends at 4800h | ||
117 | |||
118 | section .bss | ||
119 | alignb 8 | ||
120 | |||
121 | ; Expanded superblock | ||
122 | SuperInfo equ $ | ||
123 | resq 16 ; The first 16 bytes expanded 8 times | ||
124 | FAT resd 1 ; Location of (first) FAT | ||
125 | RootDirArea resd 1 ; Location of root directory area | ||
126 | RootDir resd 1 ; Location of root directory proper | ||
127 | DataArea resd 1 ; Location of data area | ||
128 | RootDirSize resd 1 ; Root dir size in sectors | ||
129 | TotalSectors resd 1 ; Total number of sectors | ||
130 | EndSector resd 1 ; Location of filesystem end | ||
131 | ClustSize resd 1 ; Bytes/cluster | ||
132 | ClustMask resd 1 ; Sectors/cluster - 1 | ||
133 | CopySuper resb 1 ; Distinguish .bs versus .bss | ||
134 | DriveNumber resb 1 ; BIOS drive number | ||
135 | ClustShift resb 1 ; Shift count for sectors/cluster | ||
136 | ClustByteShift resb 1 ; Shift count for bytes/cluster | ||
137 | |||
138 | alignb open_file_t_size | ||
139 | Files resb MAX_OPEN*open_file_t_size | ||
140 | |||
141 | ; | ||
142 | ; Constants for the xfer_buf_seg | ||
143 | ; | ||
144 | ; The xfer_buf_seg is also used to store message file buffers. We | ||
145 | ; need two trackbuffers (text and graphics), plus a work buffer | ||
146 | ; for the graphics decompressor. | ||
147 | ; | ||
148 | xbs_textbuf equ 0 ; Also hard-coded, do not change | ||
149 | xbs_vgabuf equ trackbufsize | ||
150 | xbs_vgatmpbuf equ 2*trackbufsize | ||
151 | |||
152 | |||
153 | section .text | ||
154 | ; | ||
155 | ; Some of the things that have to be saved very early are saved | ||
156 | ; "close" to the initial stack pointer offset, in order to | ||
157 | ; reduce the code size... | ||
158 | ; | ||
159 | StackBuf equ $-44-32 ; Start the stack here (grow down - 4K) | ||
160 | PartInfo equ StackBuf ; Saved partition table entry | ||
161 | FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo) | ||
162 | OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack | ||
163 | |||
164 | ; | ||
165 | ; Primary entry point. Tempting as though it may be, we can't put the | ||
166 | ; initial "cli" here; the jmp opcode in the first byte is part of the | ||
167 | ; "magic number" (using the term very loosely) for the DOS superblock. | ||
168 | ; | ||
169 | bootsec equ $ | ||
170 | jmp short start ; 2 bytes | ||
171 | nop ; 1 byte | ||
172 | ; | ||
173 | ; "Superblock" follows -- it's in the boot sector, so it's already | ||
174 | ; loaded and ready for us | ||
175 | ; | ||
176 | bsOemName db 'SYSLINUX' ; The SYS command sets this, so... | ||
177 | ; | ||
178 | ; These are the fields we actually care about. We end up expanding them | ||
179 | ; all to dword size early in the code, so generate labels for both | ||
180 | ; the expanded and unexpanded versions. | ||
181 | ; | ||
182 | %macro superb 1 | ||
183 | bx %+ %1 equ SuperInfo+($-superblock)*8+4 | ||
184 | bs %+ %1 equ $ | ||
185 | zb 1 | ||
186 | %endmacro | ||
187 | %macro superw 1 | ||
188 | bx %+ %1 equ SuperInfo+($-superblock)*8 | ||
189 | bs %+ %1 equ $ | ||
190 | zw 1 | ||
191 | %endmacro | ||
192 | %macro superd 1 | ||
193 | bx %+ %1 equ $ ; no expansion for dwords | ||
194 | bs %+ %1 equ $ | ||
195 | zd 1 | ||
196 | %endmacro | ||
197 | superblock equ $ | ||
198 | superw BytesPerSec | ||
199 | superb SecPerClust | ||
200 | superw ResSectors | ||
201 | superb FATs | ||
202 | superw RootDirEnts | ||
203 | superw Sectors | ||
204 | superb Media | ||
205 | superw FATsecs | ||
206 | superw SecPerTrack | ||
207 | superw Heads | ||
208 | superinfo_size equ ($-superblock)-1 ; How much to expand | ||
209 | superd Hidden | ||
210 | superd HugeSectors | ||
211 | ; | ||
212 | ; This is as far as FAT12/16 and FAT32 are consistent | ||
213 | ; | ||
214 | zb 54 ; FAT12/16 need 26 more bytes, | ||
215 | ; FAT32 need 54 more bytes | ||
216 | superblock_len equ $-superblock | ||
217 | |||
218 | SecPerClust equ bxSecPerClust | ||
219 | ; | ||
220 | ; Note we don't check the constraints above now; we did that at install | ||
221 | ; time (we hope!) | ||
222 | ; | ||
223 | start: | ||
224 | cli ; No interrupts yet, please | ||
225 | cld ; Copy upwards | ||
226 | ; | ||
227 | ; Set up the stack | ||
228 | ; | ||
229 | xor ax,ax | ||
230 | mov ss,ax | ||
231 | mov sp,StackBuf ; Just below BSS | ||
232 | mov es,ax | ||
233 | ; | ||
234 | ; DS:SI may contain a partition table entry. Preserve it for us. | ||
235 | ; | ||
236 | mov cx,8 ; Save partition info | ||
237 | mov di,sp | ||
238 | rep movsw | ||
239 | |||
240 | mov ds,ax ; Now we can initialize DS... | ||
241 | |||
242 | ; | ||
243 | ; Now sautee the BIOS floppy info block to that it will support decent- | ||
244 | ; size transfers; the floppy block is 11 bytes and is stored in the | ||
245 | ; INT 1Eh vector (brilliant waste of resources, eh?) | ||
246 | ; | ||
247 | ; Of course, if BIOSes had been properly programmed, we wouldn't have | ||
248 | ; had to waste precious space with this code. | ||
249 | ; | ||
250 | mov bx,fdctab | ||
251 | lfs si,[bx] ; FS:SI -> original fdctab | ||
252 | push fs ; Save on stack in case we need to bail | ||
253 | push si | ||
254 | |||
255 | ; Save the old fdctab even if hard disk so the stack layout | ||
256 | ; is the same. The instructions above do not change the flags | ||
257 | mov [DriveNumber],dl ; Save drive number in DL | ||
258 | and dl,dl ; If floppy disk (00-7F), assume no | ||
259 | ; partition table | ||
260 | js harddisk | ||
261 | |||
262 | floppy: | ||
263 | mov cl,6 ; 12 bytes (CX == 0) | ||
264 | ; es:di -> FloppyTable already | ||
265 | ; This should be safe to do now, interrupts are off... | ||
266 | mov [bx],di ; FloppyTable | ||
267 | mov [bx+2],ax ; Segment 0 | ||
268 | fs rep movsw ; Faster to move words | ||
269 | mov cl,[bsSecPerTrack] ; Patch the sector count | ||
270 | mov [di-8],cl | ||
271 | ; AX == 0 here | ||
272 | int 13h ; Some BIOSes need this | ||
273 | |||
274 | jmp short not_harddisk | ||
275 | ; | ||
276 | ; The drive number and possibly partition information was passed to us | ||
277 | ; by the BIOS or previous boot loader (MBR). Current "best practice" is to | ||
278 | ; trust that rather than what the superblock contains. | ||
279 | ; | ||
280 | ; Would it be better to zero out bsHidden if we don't have a partition table? | ||
281 | ; | ||
282 | ; Note: di points to beyond the end of PartInfo | ||
283 | ; | ||
284 | harddisk: | ||
285 | test byte [di-16],7Fh ; Sanity check: "active flag" should | ||
286 | jnz no_partition ; be 00 or 80 | ||
287 | mov eax,[di-8] ; Partition offset (dword) | ||
288 | mov [bsHidden],eax | ||
289 | no_partition: | ||
290 | ; | ||
291 | ; Get disk drive parameters (don't trust the superblock.) Don't do this for | ||
292 | ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about | ||
293 | ; what the *drive* supports, not about the *media*. Fortunately floppy disks | ||
294 | ; tend to have a fixed, well-defined geometry which is stored in the superblock. | ||
295 | ; | ||
296 | ; DL == drive # still | ||
297 | mov ah,08h | ||
298 | int 13h | ||
299 | jc no_driveparm | ||
300 | and ah,ah | ||
301 | jnz no_driveparm | ||
302 | shr dx,8 | ||
303 | inc dx ; Contains # of heads - 1 | ||
304 | mov [bsHeads],dx | ||
305 | and cx,3fh | ||
306 | mov [bsSecPerTrack],cx | ||
307 | no_driveparm: | ||
308 | not_harddisk: | ||
309 | ; | ||
310 | ; Ready to enable interrupts, captain | ||
311 | ; | ||
312 | sti | ||
313 | |||
314 | ; | ||
315 | ; Do we have EBIOS (EDD)? | ||
316 | ; | ||
317 | eddcheck: | ||
318 | mov bx,55AAh | ||
319 | mov ah,41h ; EDD existence query | ||
320 | mov dl,[DriveNumber] | ||
321 | int 13h | ||
322 | jc .noedd | ||
323 | cmp bx,0AA55h | ||
324 | jne .noedd | ||
325 | test cl,1 ; Extended disk access functionality set | ||
326 | jz .noedd | ||
327 | ; | ||
328 | ; We have EDD support... | ||
329 | ; | ||
330 | mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2)) | ||
331 | .noedd: | ||
332 | |||
333 | ; | ||
334 | ; Load the first sector of LDLINUX.SYS; this used to be all proper | ||
335 | ; with parsing the superblock and root directory; it doesn't fit | ||
336 | ; together with EBIOS support, unfortunately. | ||
337 | ; | ||
338 | mov eax,[FirstSector] ; Sector start | ||
339 | mov bx,ldlinux_sys ; Where to load it | ||
340 | call getonesec | ||
341 | |||
342 | ; Some modicum of integrity checking | ||
343 | cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE | ||
344 | jne kaboom | ||
345 | |||
346 | ; Go for it... | ||
347 | jmp ldlinux_ent | ||
348 | |||
349 | ; | ||
350 | ; getonesec: get one disk sector | ||
351 | ; | ||
352 | getonesec: | ||
353 | mov bp,1 ; One sector | ||
354 | ; Fall through | ||
355 | |||
356 | ; | ||
357 | ; getlinsec: load a sequence of BP floppy sector given by the linear sector | ||
358 | ; number in EAX into the buffer at ES:BX. We try to optimize | ||
359 | ; by loading up to a whole track at a time, but the user | ||
360 | ; is responsible for not crossing a 64K boundary. | ||
361 | ; (Yes, BP is weird for a count, but it was available...) | ||
362 | ; | ||
363 | ; On return, BX points to the first byte after the transferred | ||
364 | ; block. | ||
365 | ; | ||
366 | ; This routine assumes CS == DS, and trashes most registers. | ||
367 | ; | ||
368 | ; Stylistic note: use "xchg" instead of "mov" when the source is a register | ||
369 | ; that is dead from that point; this saves space. However, please keep | ||
370 | ; the order to dst,src to keep things sane. | ||
371 | ; | ||
372 | getlinsec: | ||
373 | add eax,[bsHidden] ; Add partition offset | ||
374 | xor edx,edx ; Zero-extend LBA (eventually allow 64 bits) | ||
375 | |||
376 | .jmp: jmp strict short getlinsec_cbios | ||
377 | |||
378 | ; | ||
379 | ; getlinsec_ebios: | ||
380 | ; | ||
381 | ; getlinsec implementation for EBIOS (EDD) | ||
382 | ; | ||
383 | getlinsec_ebios: | ||
384 | .loop: | ||
385 | push bp ; Sectors left | ||
386 | .retry2: | ||
387 | call maxtrans ; Enforce maximum transfer size | ||
388 | movzx edi,bp ; Sectors we are about to read | ||
389 | mov cx,retry_count | ||
390 | .retry: | ||
391 | |||
392 | ; Form DAPA on stack | ||
393 | push edx | ||
394 | push eax | ||
395 | push es | ||
396 | push bx | ||
397 | push di | ||
398 | push word 16 | ||
399 | mov si,sp | ||
400 | pushad | ||
401 | mov dl,[DriveNumber] | ||
402 | push ds | ||
403 | push ss | ||
404 | pop ds ; DS <- SS | ||
405 | mov ah,42h ; Extended Read | ||
406 | int 13h | ||
407 | pop ds | ||
408 | popad | ||
409 | lea sp,[si+16] ; Remove DAPA | ||
410 | jc .error | ||
411 | pop bp | ||
412 | add eax,edi ; Advance sector pointer | ||
413 | sub bp,di ; Sectors left | ||
414 | shl di,SECTOR_SHIFT ; 512-byte sectors | ||
415 | add bx,di ; Advance buffer pointer | ||
416 | and bp,bp | ||
417 | jnz .loop | ||
418 | |||
419 | ret | ||
420 | |||
421 | .error: | ||
422 | ; Some systems seem to get "stuck" in an error state when | ||
423 | ; using EBIOS. Doesn't happen when using CBIOS, which is | ||
424 | ; good, since some other systems get timeout failures | ||
425 | ; waiting for the floppy disk to spin up. | ||
426 | |||
427 | pushad ; Try resetting the device | ||
428 | xor ax,ax | ||
429 | mov dl,[DriveNumber] | ||
430 | int 13h | ||
431 | popad | ||
432 | loop .retry ; CX-- and jump if not zero | ||
433 | |||
434 | ;shr word [MaxTransfer],1 ; Reduce the transfer size | ||
435 | ;jnz .retry2 | ||
436 | |||
437 | ; Total failure. Try falling back to CBIOS. | ||
438 | mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2)) | ||
439 | ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer | ||
440 | |||
441 | pop bp | ||
442 | ; ... fall through ... | ||
443 | |||
444 | ; | ||
445 | ; getlinsec_cbios: | ||
446 | ; | ||
447 | ; getlinsec implementation for legacy CBIOS | ||
448 | ; | ||
449 | getlinsec_cbios: | ||
450 | .loop: | ||
451 | push edx | ||
452 | push eax | ||
453 | push bp | ||
454 | push bx | ||
455 | |||
456 | movzx esi,word [bsSecPerTrack] | ||
457 | movzx edi,word [bsHeads] | ||
458 | ; | ||
459 | ; Dividing by sectors to get (track,sector): we may have | ||
460 | ; up to 2^18 tracks, so we need to use 32-bit arithmetric. | ||
461 | ; | ||
462 | div esi | ||
463 | xor cx,cx | ||
464 | xchg cx,dx ; CX <- sector index (0-based) | ||
465 | ; EDX <- 0 | ||
466 | ; eax = track # | ||
467 | div edi ; Convert track to head/cyl | ||
468 | |||
469 | ; We should test this, but it doesn't fit... | ||
470 | ; cmp eax,1023 | ||
471 | ; ja .error | ||
472 | |||
473 | ; | ||
474 | ; Now we have AX = cyl, DX = head, CX = sector (0-based), | ||
475 | ; BP = sectors to transfer, SI = bsSecPerTrack, | ||
476 | ; ES:BX = data target | ||
477 | ; | ||
478 | |||
479 | call maxtrans ; Enforce maximum transfer size | ||
480 | |||
481 | ; Must not cross track boundaries, so BP <= SI-CX | ||
482 | sub si,cx | ||
483 | cmp bp,si | ||
484 | jna .bp_ok | ||
485 | mov bp,si | ||
486 | .bp_ok: | ||
487 | |||
488 | shl ah,6 ; Because IBM was STOOPID | ||
489 | ; and thought 8 bits were enough | ||
490 | ; then thought 10 bits were enough... | ||
491 | inc cx ; Sector numbers are 1-based, sigh | ||
492 | or cl,ah | ||
493 | mov ch,al | ||
494 | mov dh,dl | ||
495 | mov dl,[DriveNumber] | ||
496 | xchg ax,bp ; Sector to transfer count | ||
497 | mov ah,02h ; Read sectors | ||
498 | mov bp,retry_count | ||
499 | .retry: | ||
500 | pushad | ||
501 | int 13h | ||
502 | popad | ||
503 | jc .error | ||
504 | .resume: | ||
505 | movzx ecx,al ; ECX <- sectors transferred | ||
506 | shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX | ||
507 | pop bx | ||
508 | add bx,ax | ||
509 | pop bp | ||
510 | pop eax | ||
511 | pop edx | ||
512 | add eax,ecx | ||
513 | sub bp,cx | ||
514 | jnz .loop | ||
515 | ret | ||
516 | |||
517 | .error: | ||
518 | dec bp | ||
519 | jnz .retry | ||
520 | |||
521 | xchg ax,bp ; Sectors transferred <- 0 | ||
522 | shr word [MaxTransfer],1 | ||
523 | jnz .resume | ||
524 | ; Fall through to disk_error | ||
525 | |||
526 | ; | ||
527 | ; kaboom: write a message and bail out. | ||
528 | ; | ||
529 | disk_error: | ||
530 | kaboom: | ||
531 | xor si,si | ||
532 | mov ss,si | ||
533 | mov sp,StackBuf-4 ; Reset stack | ||
534 | mov ds,si ; Reset data segment | ||
535 | pop dword [fdctab] ; Restore FDC table | ||
536 | .patch: ; When we have full code, intercept here | ||
537 | mov si,bailmsg | ||
538 | |||
539 | ; Write error message, this assumes screen page 0 | ||
540 | .loop: lodsb | ||
541 | and al,al | ||
542 | jz .done | ||
543 | mov ah,0Eh ; Write to screen as TTY | ||
544 | mov bx,0007h ; Attribute | ||
545 | int 10h | ||
546 | jmp short .loop | ||
547 | .done: | ||
548 | cbw ; AH <- 0 | ||
549 | int 16h ; Wait for keypress | ||
550 | int 19h ; And try once more to boot... | ||
551 | .norge: jmp short .norge ; If int 19h returned; this is the end | ||
552 | |||
553 | ; | ||
554 | ; Truncate BP to MaxTransfer | ||
555 | ; | ||
556 | maxtrans: | ||
557 | cmp bp,[MaxTransfer] | ||
558 | jna .ok | ||
559 | mov bp,[MaxTransfer] | ||
560 | .ok: ret | ||
561 | |||
562 | ; | ||
563 | ; Error message on failure | ||
564 | ; | ||
565 | bailmsg: db 'Boot error', 0Dh, 0Ah, 0 | ||
566 | |||
567 | ; This fails if the boot sector overflows | ||
568 | zb 1F8h-($-$$) | ||
569 | |||
570 | FirstSector dd 0xDEADBEEF ; Location of sector 1 | ||
571 | MaxTransfer dw 0x007F ; Max transfer size | ||
572 | bootsignature dw 0AA55h | ||
573 | |||
574 | ; | ||
575 | ; =========================================================================== | ||
576 | ; End of boot sector | ||
577 | ; =========================================================================== | ||
578 | ; Start of LDLINUX.SYS | ||
579 | ; =========================================================================== | ||
580 | |||
581 | ldlinux_sys: | ||
582 | |||
583 | syslinux_banner db 0Dh, 0Ah | ||
584 | %if IS_MDSLINUX | ||
585 | db 'MDSLINUX ' | ||
586 | %else | ||
587 | db 'SYSLINUX ' | ||
588 | %endif | ||
589 | db version_str, ' ', date, ' ', 0 | ||
590 | db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS | ||
591 | |||
592 | align 8, db 0 | ||
593 | ldlinux_magic dd LDLINUX_MAGIC | ||
594 | dd LDLINUX_MAGIC^HEXDATE | ||
595 | |||
596 | ; | ||
597 | ; This area is patched by the installer. It is found by looking for | ||
598 | ; LDLINUX_MAGIC, plus 8 bytes. | ||
599 | ; | ||
600 | patch_area: | ||
601 | LDLDwords dw 0 ; Total dwords starting at ldlinux_sys | ||
602 | LDLSectors dw 0 ; Number of sectors - (bootsec+this sec) | ||
603 | CheckSum dd 0 ; Checksum starting at ldlinux_sys | ||
604 | ; value = LDLINUX_MAGIC - [sum of dwords] | ||
605 | |||
606 | ; Space for up to 64 sectors, the theoretical maximum | ||
607 | SectorPtrs times 64 dd 0 | ||
608 | |||
609 | ldlinux_ent: | ||
610 | ; | ||
611 | ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000 | ||
612 | ; instead of 0000:7C00 and the like. We don't want to add anything | ||
613 | ; more to the boot sector, so it is written to not assume a fixed | ||
614 | ; value in CS, but we don't want to deal with that anymore from now | ||
615 | ; on. | ||
616 | ; | ||
617 | jmp 0:.next | ||
618 | .next: | ||
619 | |||
620 | ; | ||
621 | ; Tell the user we got this far | ||
622 | ; | ||
623 | mov si,syslinux_banner | ||
624 | call writestr | ||
625 | |||
626 | ; | ||
627 | ; Tell the user if we're using EBIOS or CBIOS | ||
628 | ; | ||
629 | print_bios: | ||
630 | mov si,cbios_name | ||
631 | cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2)) | ||
632 | jne .cbios | ||
633 | mov si,ebios_name | ||
634 | .cbios: | ||
635 | mov [BIOSName],si | ||
636 | call writestr | ||
637 | |||
638 | section .bss | ||
639 | %define HAVE_BIOSNAME 1 | ||
640 | BIOSName resw 1 | ||
641 | |||
642 | section .text | ||
643 | ; | ||
644 | ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first | ||
645 | ; sector again, though. | ||
646 | ; | ||
647 | load_rest: | ||
648 | mov si,SectorPtrs | ||
649 | mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading | ||
650 | mov cx,[LDLSectors] | ||
651 | |||
652 | .get_chunk: | ||
653 | jcxz .done | ||
654 | xor bp,bp | ||
655 | lodsd ; First sector of this chunk | ||
656 | |||
657 | mov edx,eax | ||
658 | |||
659 | .make_chunk: | ||
660 | inc bp | ||
661 | dec cx | ||
662 | jz .chunk_ready | ||
663 | inc edx ; Next linear sector | ||
664 | cmp [si],edx ; Does it match | ||
665 | jnz .chunk_ready ; If not, this is it | ||
666 | add si,4 ; If so, add sector to chunk | ||
667 | jmp short .make_chunk | ||
668 | |||
669 | .chunk_ready: | ||
670 | call getlinsecsr | ||
671 | shl bp,SECTOR_SHIFT | ||
672 | add bx,bp | ||
673 | jmp .get_chunk | ||
674 | |||
675 | .done: | ||
676 | |||
677 | ; | ||
678 | ; All loaded up, verify that we got what we needed. | ||
679 | ; Note: the checksum field is embedded in the checksum region, so | ||
680 | ; by the time we get to the end it should all cancel out. | ||
681 | ; | ||
682 | verify_checksum: | ||
683 | mov si,ldlinux_sys | ||
684 | mov cx,[LDLDwords] | ||
685 | mov edx,-LDLINUX_MAGIC | ||
686 | .checksum: | ||
687 | lodsd | ||
688 | add edx,eax | ||
689 | loop .checksum | ||
690 | |||
691 | and edx,edx ; Should be zero | ||
692 | jz all_read ; We're cool, go for it! | ||
693 | |||
694 | ; | ||
695 | ; Uh-oh, something went bad... | ||
696 | ; | ||
697 | mov si,checksumerr_msg | ||
698 | call writestr | ||
699 | jmp kaboom | ||
700 | |||
701 | ; | ||
702 | ; ----------------------------------------------------------------------------- | ||
703 | ; Subroutines that have to be in the first sector | ||
704 | ; ----------------------------------------------------------------------------- | ||
705 | |||
706 | ; | ||
707 | ; | ||
708 | ; writestr: write a null-terminated string to the console | ||
709 | ; This assumes we're on page 0. This is only used for early | ||
710 | ; messages, so it should be OK. | ||
711 | ; | ||
712 | writestr: | ||
713 | .loop: lodsb | ||
714 | and al,al | ||
715 | jz .return | ||
716 | mov ah,0Eh ; Write to screen as TTY | ||
717 | mov bx,0007h ; Attribute | ||
718 | int 10h | ||
719 | jmp short .loop | ||
720 | .return: ret | ||
721 | |||
722 | |||
723 | ; getlinsecsr: save registers, call getlinsec, restore registers | ||
724 | ; | ||
725 | getlinsecsr: pushad | ||
726 | call getlinsec | ||
727 | popad | ||
728 | ret | ||
729 | |||
730 | ; | ||
731 | ; Checksum error message | ||
732 | ; | ||
733 | checksumerr_msg db ' Load error - ', 0 ; Boot failed appended | ||
734 | |||
735 | ; | ||
736 | ; BIOS type string | ||
737 | ; | ||
738 | cbios_name db 'CBIOS', 0 | ||
739 | ebios_name db 'EBIOS', 0 | ||
740 | |||
741 | ; | ||
742 | ; Debug routine | ||
743 | ; | ||
744 | %ifdef debug | ||
745 | safedumpregs: | ||
746 | cmp word [Debug_Magic],0D00Dh | ||
747 | jnz nc_return | ||
748 | jmp dumpregs | ||
749 | %endif | ||
750 | |||
751 | rl_checkpt equ $ ; Must be <= 8000h | ||
752 | |||
753 | rl_checkpt_off equ ($-$$) | ||
754 | %ifndef DEPEND | ||
755 | %if rl_checkpt_off > 400h | ||
756 | %error "Sector 1 overflow" | ||
757 | %endif | ||
758 | %endif | ||
759 | |||
760 | ; ---------------------------------------------------------------------------- | ||
761 | ; End of code and data that have to be in the first sector | ||
762 | ; ---------------------------------------------------------------------------- | ||
763 | |||
764 | all_read: | ||
765 | ; | ||
766 | ; Let the user (and programmer!) know we got this far. This used to be | ||
767 | ; in Sector 1, but makes a lot more sense here. | ||
768 | ; | ||
769 | mov si,copyright_str | ||
770 | call writestr | ||
771 | |||
772 | |||
773 | ; | ||
774 | ; Insane hack to expand the superblock to dwords | ||
775 | ; | ||
776 | expand_super: | ||
777 | xor eax,eax | ||
778 | mov si,superblock | ||
779 | mov di,SuperInfo | ||
780 | mov cx,superinfo_size | ||
781 | .loop: | ||
782 | lodsw | ||
783 | dec si | ||
784 | stosd ; Store expanded word | ||
785 | xor ah,ah | ||
786 | stosd ; Store expanded byte | ||
787 | loop .loop | ||
788 | |||
789 | ; | ||
790 | ; Compute some information about this filesystem. | ||
791 | ; | ||
792 | |||
793 | ; First, generate the map of regions | ||
794 | genfatinfo: | ||
795 | mov edx,[bxSectors] | ||
796 | and dx,dx | ||
797 | jnz .have_secs | ||
798 | mov edx,[bsHugeSectors] | ||
799 | .have_secs: | ||
800 | mov [TotalSectors],edx | ||
801 | |||
802 | add edx,eax | ||
803 | mov [EndSector],edx | ||
804 | |||
805 | mov eax,[bxResSectors] | ||
806 | mov [FAT],eax ; Beginning of FAT | ||
807 | mov edx,[bxFATsecs] | ||
808 | and dx,dx | ||
809 | jnz .have_fatsecs | ||
810 | mov edx,[bootsec+36] ; FAT32 BPB_FATsz32 | ||
811 | .have_fatsecs: | ||
812 | imul edx,[bxFATs] | ||
813 | add eax,edx | ||
814 | mov [RootDirArea],eax ; Beginning of root directory | ||
815 | mov [RootDir],eax ; For FAT12/16 == root dir location | ||
816 | |||
817 | mov edx,[bxRootDirEnts] | ||
818 | add dx,SECTOR_SIZE/32-1 | ||
819 | shr dx,SECTOR_SHIFT-5 | ||
820 | mov [RootDirSize],edx | ||
821 | add eax,edx | ||
822 | mov [DataArea],eax ; Beginning of data area | ||
823 | |||
824 | ; Next, generate a cluster size shift count and mask | ||
825 | mov eax,[bxSecPerClust] | ||
826 | bsr cx,ax | ||
827 | mov [ClustShift],cl | ||
828 | push cx | ||
829 | add cl,9 | ||
830 | mov [ClustByteShift],cl | ||
831 | pop cx | ||
832 | dec ax | ||
833 | mov [ClustMask],eax | ||
834 | inc ax | ||
835 | shl eax,9 | ||
836 | mov [ClustSize],eax | ||
837 | |||
838 | ; | ||
839 | ; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous. | ||
840 | ; | ||
841 | getfattype: | ||
842 | mov eax,[EndSector] | ||
843 | sub eax,[DataArea] | ||
844 | shr eax,cl ; cl == ClustShift | ||
845 | mov cl,nextcluster_fat12-(nextcluster+2) | ||
846 | cmp eax,4085 ; FAT12 limit | ||
847 | jb .setsize | ||
848 | mov cl,nextcluster_fat16-(nextcluster+2) | ||
849 | cmp eax,65525 ; FAT16 limit | ||
850 | jb .setsize | ||
851 | ; | ||
852 | ; FAT32, root directory is a cluster chain | ||
853 | ; | ||
854 | mov cl,[ClustShift] | ||
855 | mov eax,[bootsec+44] ; Root directory cluster | ||
856 | sub eax,2 | ||
857 | shl eax,cl | ||
858 | add eax,[DataArea] | ||
859 | mov [RootDir],eax | ||
860 | mov cl,nextcluster_fat28-(nextcluster+2) | ||
861 | .setsize: | ||
862 | mov byte [nextcluster+1],cl | ||
863 | |||
864 | ; | ||
865 | ; Common initialization code | ||
866 | ; | ||
867 | %include "cpuinit.inc" | ||
868 | %include "init.inc" | ||
869 | |||
870 | ; | ||
871 | ; Clear Files structures | ||
872 | ; | ||
873 | mov di,Files | ||
874 | mov cx,(MAX_OPEN*open_file_t_size)/4 | ||
875 | xor eax,eax | ||
876 | rep stosd | ||
877 | |||
878 | ; | ||
879 | ; Initialize the metadata cache | ||
880 | ; | ||
881 | call initcache | ||
882 | |||
883 | ; | ||
884 | ; Now, everything is "up and running"... patch kaboom for more | ||
885 | ; verbosity and using the full screen system | ||
886 | ; | ||
887 | ; E9 = JMP NEAR | ||
888 | mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8) | ||
889 | |||
890 | ; | ||
891 | ; Now we're all set to start with our *real* business. First load the | ||
892 | ; configuration file (if any) and parse it. | ||
893 | ; | ||
894 | ; In previous versions I avoided using 32-bit registers because of a | ||
895 | ; rumour some BIOSes clobbered the upper half of 32-bit registers at | ||
896 | ; random. I figure, though, that if there are any of those still left | ||
897 | ; they probably won't be trying to install Linux on them... | ||
898 | ; | ||
899 | ; The code is still ripe with 16-bitisms, though. Not worth the hassle | ||
900 | ; to take'm out. In fact, we may want to put them back if we're going | ||
901 | ; to boot ELKS at some point. | ||
902 | ; | ||
903 | |||
904 | ; | ||
905 | ; Load configuration file | ||
906 | ; | ||
907 | mov di,syslinux_cfg | ||
908 | call open | ||
909 | jz no_config_file | ||
910 | |||
911 | ; | ||
912 | ; Now we have the config file open. Parse the config file and | ||
913 | ; run the user interface. | ||
914 | ; | ||
915 | %include "ui.inc" | ||
916 | |||
917 | ; | ||
918 | ; Linux kernel loading code is common. | ||
919 | ; | ||
920 | %include "runkernel.inc" | ||
921 | |||
922 | ; | ||
923 | ; COMBOOT-loading code | ||
924 | ; | ||
925 | %include "comboot.inc" | ||
926 | %include "com32.inc" | ||
927 | %include "cmdline.inc" | ||
928 | |||
929 | ; | ||
930 | ; Boot sector loading code | ||
931 | ; | ||
932 | %include "bootsect.inc" | ||
933 | |||
934 | ; | ||
935 | ; Abort loading code | ||
936 | ; | ||
937 | %include "abort.inc" | ||
938 | |||
939 | ; | ||
940 | ; allocate_file: Allocate a file structure | ||
941 | ; | ||
942 | ; If successful: | ||
943 | ; ZF set | ||
944 | ; BX = file pointer | ||
945 | ; In unsuccessful: | ||
946 | ; ZF clear | ||
947 | ; | ||
948 | allocate_file: | ||
949 | TRACER 'a' | ||
950 | push cx | ||
951 | mov bx,Files | ||
952 | mov cx,MAX_OPEN | ||
953 | .check: cmp dword [bx], byte 0 | ||
954 | je .found | ||
955 | add bx,open_file_t_size ; ZF = 0 | ||
956 | loop .check | ||
957 | ; ZF = 0 if we fell out of the loop | ||
958 | .found: pop cx | ||
959 | ret | ||
960 | |||
961 | ; | ||
962 | ; searchdir: | ||
963 | ; Search the root directory for a pre-mangled filename in DS:DI. | ||
964 | ; | ||
965 | ; NOTE: This file considers finding a zero-length file an | ||
966 | ; error. This is so we don't have to deal with that special | ||
967 | ; case elsewhere in the program (most loops have the test | ||
968 | ; at the end). | ||
969 | ; | ||
970 | ; If successful: | ||
971 | ; ZF clear | ||
972 | ; SI = file pointer | ||
973 | ; DX:AX = file length in bytes | ||
974 | ; If unsuccessful | ||
975 | ; ZF set | ||
976 | ; | ||
977 | |||
978 | searchdir: | ||
979 | push bx | ||
980 | call allocate_file | ||
981 | jnz .alloc_failure | ||
982 | |||
983 | push cx | ||
984 | push gs | ||
985 | push es | ||
986 | push ds | ||
987 | pop es ; ES = DS | ||
988 | |||
989 | mov eax,[RootDir] ; First root directory sector | ||
990 | |||
991 | .scansector: | ||
992 | call getcachesector | ||
993 | ; GS:SI now points to this sector | ||
994 | |||
995 | mov cx,SECTOR_SIZE/32 ; 32 == directory entry size | ||
996 | .scanentry: | ||
997 | cmp byte [gs:si],0 | ||
998 | jz .failure ; Hit directory high water mark | ||
999 | push cx | ||
1000 | push si | ||
1001 | push di | ||
1002 | mov cx,11 | ||
1003 | gs repe cmpsb | ||
1004 | pop di | ||
1005 | pop si | ||
1006 | pop cx | ||
1007 | jz .found | ||
1008 | add si,32 | ||
1009 | loop .scanentry | ||
1010 | |||
1011 | call nextsector | ||
1012 | jnc .scansector ; CF is set if we're at end | ||
1013 | |||
1014 | ; If we get here, we failed | ||
1015 | .failure: | ||
1016 | pop es | ||
1017 | pop gs | ||
1018 | pop cx | ||
1019 | .alloc_failure: | ||
1020 | pop bx | ||
1021 | xor eax,eax ; ZF <- 1 | ||
1022 | ret | ||
1023 | .found: | ||
1024 | mov eax,[gs:si+28] ; File size | ||
1025 | add eax,SECTOR_SIZE-1 | ||
1026 | shr eax,SECTOR_SHIFT | ||
1027 | jz .failure ; Zero-length file | ||
1028 | mov [bx+4],eax | ||
1029 | |||
1030 | mov cl,[ClustShift] | ||
1031 | mov dx,[gs:si+20] ; High cluster word | ||
1032 | shl edx,16 | ||
1033 | mov dx,[gs:si+26] ; Low cluster word | ||
1034 | sub edx,2 | ||
1035 | shl edx,cl | ||
1036 | add edx,[DataArea] | ||
1037 | mov [bx],edx ; Starting sector | ||
1038 | |||
1039 | mov eax,[gs:si+28] ; File length again | ||
1040 | mov dx,[gs:si+30] ; 16-bitism, sigh | ||
1041 | mov si,bx | ||
1042 | and eax,eax ; ZF <- 0 | ||
1043 | |||
1044 | pop es | ||
1045 | pop gs | ||
1046 | pop cx | ||
1047 | pop bx | ||
1048 | ret | ||
1049 | |||
1050 | ; | ||
1051 | ; writechr: Write a single character in AL to the console without | ||
1052 | ; mangling any registers; handle video pages correctly. | ||
1053 | ; | ||
1054 | writechr: | ||
1055 | call write_serial ; write to serial port if needed | ||
1056 | pushfd | ||
1057 | test byte [cs:DisplayCon], 01h | ||
1058 | jz .nothing | ||
1059 | pushad | ||
1060 | mov ah,0Eh | ||
1061 | mov bl,07h ; attribute | ||
1062 | mov bh,[cs:BIOS_page] ; current page | ||
1063 | int 10h | ||
1064 | popad | ||
1065 | .nothing: | ||
1066 | popfd | ||
1067 | ret | ||
1068 | |||
1069 | ; | ||
1070 | ; | ||
1071 | ; kaboom2: once everything is loaded, replace the part of kaboom | ||
1072 | ; starting with "kaboom.patch" with this part | ||
1073 | |||
1074 | kaboom2: | ||
1075 | mov si,err_bootfailed | ||
1076 | call cwritestr | ||
1077 | call getchar | ||
1078 | call vgaclearmode | ||
1079 | int 19h ; And try once more to boot... | ||
1080 | .norge: jmp short .norge ; If int 19h returned; this is the end | ||
1081 | |||
1082 | ; | ||
1083 | ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed | ||
1084 | ; to by ES:DI; ends on encountering any whitespace | ||
1085 | ; | ||
1086 | |||
1087 | mangle_name: | ||
1088 | mov cx,11 ; # of bytes to write | ||
1089 | mn_loop: | ||
1090 | lodsb | ||
1091 | cmp al,' ' ; If control or space, end | ||
1092 | jna mn_end | ||
1093 | cmp al,'.' ; Period -> space-fill | ||
1094 | je mn_is_period | ||
1095 | cmp al,'a' | ||
1096 | jb mn_not_lower | ||
1097 | cmp al,'z' | ||
1098 | ja mn_not_uslower | ||
1099 | sub al,020h | ||
1100 | jmp short mn_not_lower | ||
1101 | mn_is_period: mov al,' ' ; We need to space-fill | ||
1102 | mn_period_loop: cmp cx,3 ; If <= 3 characters left | ||
1103 | jbe mn_loop ; Just ignore it | ||
1104 | stosb ; Otherwise, write a period | ||
1105 | loop mn_period_loop ; Dec CX and (always) jump | ||
1106 | mn_not_uslower: cmp al,ucase_low | ||
1107 | jb mn_not_lower | ||
1108 | cmp al,ucase_high | ||
1109 | ja mn_not_lower | ||
1110 | mov bx,ucase_tab-ucase_low | ||
1111 | cs xlatb | ||
1112 | mn_not_lower: stosb | ||
1113 | loop mn_loop ; Don't continue if too long | ||
1114 | mn_end: | ||
1115 | mov al,' ' ; Space-fill name | ||
1116 | rep stosb ; Doesn't do anything if CX=0 | ||
1117 | ret ; Done | ||
1118 | |||
1119 | ; | ||
1120 | ; Upper-case table for extended characters; this is technically code page 865, | ||
1121 | ; but code page 437 users will probably not miss not being able to use the | ||
1122 | ; cent sign in kernel images too much :-) | ||
1123 | ; | ||
1124 | ; The table only covers the range 129 to 164; the rest we can deal with. | ||
1125 | ; | ||
1126 | ucase_low equ 129 | ||
1127 | ucase_high equ 164 | ||
1128 | ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII' | ||
1129 | db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154 | ||
1130 | db 157, 156, 157, 158, 159, 'AIOU', 165 | ||
1131 | |||
1132 | ; | ||
1133 | ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled | ||
1134 | ; filename to the conventional representation. This is needed | ||
1135 | ; for the BOOT_IMAGE= parameter for the kernel. | ||
1136 | ; NOTE: A 13-byte buffer is mandatory, even if the string is | ||
1137 | ; known to be shorter. | ||
1138 | ; | ||
1139 | ; DS:SI -> input mangled file name | ||
1140 | ; ES:DI -> output buffer | ||
1141 | ; | ||
1142 | ; On return, DI points to the first byte after the output name, | ||
1143 | ; which is set to a null byte. | ||
1144 | ; | ||
1145 | unmangle_name: | ||
1146 | push si ; Save pointer to original name | ||
1147 | mov cx,8 | ||
1148 | mov bp,di | ||
1149 | un_copy_body: lodsb | ||
1150 | call lower_case | ||
1151 | stosb | ||
1152 | cmp al,' ' | ||
1153 | jbe un_cb_space | ||
1154 | mov bp,di ; Position of last nonblank+1 | ||
1155 | un_cb_space: loop un_copy_body | ||
1156 | mov di,bp | ||
1157 | mov al,'.' ; Don't save | ||
1158 | stosb | ||
1159 | mov cx,3 | ||
1160 | un_copy_ext: lodsb | ||
1161 | call lower_case | ||
1162 | stosb | ||
1163 | cmp al,' ' | ||
1164 | jbe un_ce_space | ||
1165 | mov bp,di | ||
1166 | un_ce_space: loop un_copy_ext | ||
1167 | mov di,bp | ||
1168 | mov byte [es:di], 0 | ||
1169 | pop si | ||
1170 | ret | ||
1171 | |||
1172 | ; | ||
1173 | ; lower_case: Lower case a character in AL | ||
1174 | ; | ||
1175 | lower_case: | ||
1176 | cmp al,'A' | ||
1177 | jb lc_ret | ||
1178 | cmp al,'Z' | ||
1179 | ja lc_1 | ||
1180 | or al,20h | ||
1181 | ret | ||
1182 | lc_1: cmp al,lcase_low | ||
1183 | jb lc_ret | ||
1184 | cmp al,lcase_high | ||
1185 | ja lc_ret | ||
1186 | push bx | ||
1187 | mov bx,lcase_tab-lcase_low | ||
1188 | cs xlatb | ||
1189 | pop bx | ||
1190 | lc_ret: ret | ||
1191 | |||
1192 | ; | ||
1193 | ; getfssec_edx: Get multiple sectors from a file | ||
1194 | ; | ||
1195 | ; This routine makes sure the subtransfers do not cross a 64K boundary, | ||
1196 | ; and will correct the situation if it does, UNLESS *sectors* cross | ||
1197 | ; 64K boundaries. | ||
1198 | ; | ||
1199 | ; ES:BX -> Buffer | ||
1200 | ; EDX -> Current sector number | ||
1201 | ; CX -> Sector count (0FFFFh = until end of file) | ||
1202 | ; Must not exceed the ES segment | ||
1203 | ; Returns EDX=0, CF=1 on EOF (not necessarily error) | ||
1204 | ; All arguments are advanced to reflect data read. | ||
1205 | ; | ||
1206 | getfssec_edx: | ||
1207 | push ebp | ||
1208 | push eax | ||
1209 | .getfragment: | ||
1210 | xor ebp,ebp ; Fragment sector count | ||
1211 | push edx ; Starting sector pointer | ||
1212 | .getseccnt: | ||
1213 | inc bp | ||
1214 | dec cx | ||
1215 | jz .do_read | ||
1216 | xor eax,eax | ||
1217 | mov ax,es | ||
1218 | shl ax,4 | ||
1219 | add ax,bx ; Now AX = how far into 64K block we are | ||
1220 | not ax ; Bytes left in 64K block | ||
1221 | inc eax | ||
1222 | shr eax,SECTOR_SHIFT ; Sectors left in 64K block | ||
1223 | cmp bp,ax | ||
1224 | jnb .do_read ; Unless there is at least 1 more sector room... | ||
1225 | mov eax,edx ; Current sector | ||
1226 | inc edx ; Predict it's the linearly next sector | ||
1227 | call nextsector | ||
1228 | jc .do_read | ||
1229 | cmp edx,eax ; Did it match? | ||
1230 | jz .getseccnt | ||
1231 | .do_read: | ||
1232 | pop eax ; Starting sector pointer | ||
1233 | call getlinsecsr | ||
1234 | lea eax,[eax+ebp-1] ; This is the last sector actually read | ||
1235 | shl bp,9 | ||
1236 | add bx,bp ; Adjust buffer pointer | ||
1237 | call nextsector | ||
1238 | jc .eof | ||
1239 | mov edx,eax | ||
1240 | and cx,cx | ||
1241 | jnz .getfragment | ||
1242 | .done: | ||
1243 | pop eax | ||
1244 | pop ebp | ||
1245 | ret | ||
1246 | .eof: | ||
1247 | xor edx,edx | ||
1248 | stc | ||
1249 | jmp .done | ||
1250 | |||
1251 | ; | ||
1252 | ; getfssec: Get multiple sectors from a file | ||
1253 | ; | ||
1254 | ; Same as above, except SI is a pointer to a open_file_t | ||
1255 | ; | ||
1256 | ; ES:BX -> Buffer | ||
1257 | ; DS:SI -> Pointer to open_file_t | ||
1258 | ; CX -> Sector count (0FFFFh = until end of file) | ||
1259 | ; Must not exceed the ES segment | ||
1260 | ; Returns CF=1 on EOF (not necessarily error) | ||
1261 | ; All arguments are advanced to reflect data read. | ||
1262 | ; | ||
1263 | getfssec: | ||
1264 | push edx | ||
1265 | movzx edx,cx | ||
1266 | cmp edx,[si+4] | ||
1267 | jbe .sizeok | ||
1268 | mov edx,[si+4] | ||
1269 | mov cx,dx | ||
1270 | .sizeok: | ||
1271 | sub [si+4],edx | ||
1272 | mov edx,[si] | ||
1273 | call getfssec_edx | ||
1274 | mov [si],edx | ||
1275 | pop edx | ||
1276 | ret | ||
1277 | |||
1278 | ; | ||
1279 | ; nextcluster: Advance a cluster pointer in EDI to the next cluster | ||
1280 | ; pointed at in the FAT tables. CF=0 on return if end of file. | ||
1281 | ; | ||
1282 | nextcluster: | ||
1283 | jmp strict short nextcluster_fat28 ; This gets patched | ||
1284 | |||
1285 | nextcluster_fat12: | ||
1286 | push eax | ||
1287 | push edx | ||
1288 | push bx | ||
1289 | push cx | ||
1290 | push si | ||
1291 | mov edx,edi | ||
1292 | shr edi,1 | ||
1293 | pushf ; Save the shifted-out LSB (=CF) | ||
1294 | add edx,edi | ||
1295 | mov eax,edx | ||
1296 | shr eax,9 | ||
1297 | call getfatsector | ||
1298 | mov bx,dx | ||
1299 | and bx,1FFh | ||
1300 | mov cl,[gs:si+bx] | ||
1301 | inc edx | ||
1302 | mov eax,edx | ||
1303 | shr eax,9 | ||
1304 | call getfatsector | ||
1305 | mov bx,dx | ||
1306 | and bx,1FFh | ||
1307 | mov ch,[gs:si+bx] | ||
1308 | popf | ||
1309 | jnc .even | ||
1310 | shr cx,4 | ||
1311 | .even: and cx,0FFFh | ||
1312 | movzx edi,cx | ||
1313 | cmp di,0FF0h | ||
1314 | pop si | ||
1315 | pop cx | ||
1316 | pop bx | ||
1317 | pop edx | ||
1318 | pop eax | ||
1319 | ret | ||
1320 | |||
1321 | ; | ||
1322 | ; FAT16 decoding routine. | ||
1323 | ; | ||
1324 | nextcluster_fat16: | ||
1325 | push eax | ||
1326 | push si | ||
1327 | push bx | ||
1328 | mov eax,edi | ||
1329 | shr eax,SECTOR_SHIFT-1 | ||
1330 | call getfatsector | ||
1331 | mov bx,di | ||
1332 | add bx,bx | ||
1333 | and bx,1FEh | ||
1334 | movzx edi,word [gs:si+bx] | ||
1335 | cmp di,0FFF0h | ||
1336 | pop bx | ||
1337 | pop si | ||
1338 | pop eax | ||
1339 | ret | ||
1340 | ; | ||
1341 | ; FAT28 ("FAT32") decoding routine. | ||
1342 | ; | ||
1343 | nextcluster_fat28: | ||
1344 | push eax | ||
1345 | push si | ||
1346 | push bx | ||
1347 | mov eax,edi | ||
1348 | shr eax,SECTOR_SHIFT-2 | ||
1349 | call getfatsector | ||
1350 | mov bx,di | ||
1351 | add bx,bx | ||
1352 | add bx,bx | ||
1353 | and bx,1FCh | ||
1354 | mov edi,dword [gs:si+bx] | ||
1355 | and edi,0FFFFFFFh ; 28 bits only | ||
1356 | cmp edi,0FFFFFF0h | ||
1357 | pop bx | ||
1358 | pop si | ||
1359 | pop eax | ||
1360 | ret | ||
1361 | |||
1362 | ; | ||
1363 | ; nextsector: Given a sector in EAX on input, return the next sector | ||
1364 | ; of the same filesystem object, which may be the root | ||
1365 | ; directory or a cluster chain. Returns EOF. | ||
1366 | ; | ||
1367 | ; Assumes CS == DS. | ||
1368 | ; | ||
1369 | nextsector: | ||
1370 | push edi | ||
1371 | push edx | ||
1372 | mov edx,[DataArea] | ||
1373 | mov edi,eax | ||
1374 | sub edi,edx | ||
1375 | jae .isdata | ||
1376 | |||
1377 | ; Root directory | ||
1378 | inc eax | ||
1379 | cmp eax,edx | ||
1380 | cmc | ||
1381 | jmp .done | ||
1382 | |||
1383 | .isdata: | ||
1384 | not edi | ||
1385 | test edi,[ClustMask] | ||
1386 | jz .endcluster | ||
1387 | |||
1388 | ; It's not the final sector in a cluster | ||
1389 | inc eax | ||
1390 | jmp .done | ||
1391 | |||
1392 | .endcluster: | ||
1393 | push gs ; nextcluster trashes gs | ||
1394 | push cx | ||
1395 | not edi | ||
1396 | mov cl,[ClustShift] | ||
1397 | shr edi,cl | ||
1398 | add edi,2 | ||
1399 | |||
1400 | ; Now EDI contains the cluster number | ||
1401 | call nextcluster | ||
1402 | cmc | ||
1403 | jc .exit ; There isn't anything else... | ||
1404 | |||
1405 | ; New cluster number now in EDI | ||
1406 | sub edi,2 | ||
1407 | shl edi,cl ; CF <- 0, unless something is very wrong | ||
1408 | lea eax,[edi+edx] | ||
1409 | .exit: | ||
1410 | pop cx | ||
1411 | pop gs | ||
1412 | .done: | ||
1413 | pop edx | ||
1414 | pop edi | ||
1415 | ret | ||
1416 | |||
1417 | ; | ||
1418 | ; getfatsector: Check for a particular sector (in EAX) in the FAT cache, | ||
1419 | ; and return a pointer in GS:SI, loading it if needed. | ||
1420 | ; | ||
1421 | ; Assumes CS == DS. | ||
1422 | ; | ||
1423 | getfatsector: | ||
1424 | add eax,[FAT] ; FAT starting address | ||
1425 | jmp getcachesector | ||
1426 | |||
1427 | ; ----------------------------------------------------------------------------- | ||
1428 | ; Common modules | ||
1429 | ; ----------------------------------------------------------------------------- | ||
1430 | |||
1431 | %include "getc.inc" ; getc et al | ||
1432 | %include "conio.inc" ; Console I/O | ||
1433 | %include "writestr.inc" ; String output | ||
1434 | %include "parseconfig.inc" ; High-level config file handling | ||
1435 | %include "parsecmd.inc" ; Low-level config file handling | ||
1436 | %include "bcopy32.inc" ; 32-bit bcopy | ||
1437 | %include "loadhigh.inc" ; Load a file into high memory | ||
1438 | %include "font.inc" ; VGA font stuff | ||
1439 | %include "graphics.inc" ; VGA graphics | ||
1440 | %include "highmem.inc" ; High memory sizing | ||
1441 | %include "strcpy.inc" ; strcpy() | ||
1442 | %include "cache.inc" ; Metadata disk cache | ||
1443 | |||
1444 | ; ----------------------------------------------------------------------------- | ||
1445 | ; Begin data section | ||
1446 | ; ----------------------------------------------------------------------------- | ||
1447 | |||
1448 | section .data | ||
1449 | ; | ||
1450 | ; Lower-case table for codepage 865 | ||
1451 | ; | ||
1452 | lcase_low equ 128 | ||
1453 | lcase_high equ 165 | ||
1454 | lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138 | ||
1455 | db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149 | ||
1456 | db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160 | ||
1457 | db 161, 162, 163, 164, 164 | ||
1458 | |||
1459 | copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin' | ||
1460 | db CR, LF, 0 | ||
1461 | boot_prompt db 'boot: ', 0 | ||
1462 | wipe_char db BS, ' ', BS, 0 | ||
1463 | err_notfound db 'Could not find kernel image: ',0 | ||
1464 | err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0 | ||
1465 | err_noram db 'It appears your computer has less than ' | ||
1466 | asciidec dosram_k | ||
1467 | db 'K of low ("DOS")' | ||
1468 | db CR, LF | ||
1469 | db 'RAM. Linux needs at least this amount to boot. If you get' | ||
1470 | db CR, LF | ||
1471 | db 'this message in error, hold down the Ctrl key while' | ||
1472 | db CR, LF | ||
1473 | db 'booting, and I will take your word for it.', CR, LF, 0 | ||
1474 | err_badcfg db 'Unknown keyword in syslinux.cfg.', CR, LF, 0 | ||
1475 | err_noparm db 'Missing parameter in syslinux.cfg.', CR, LF, 0 | ||
1476 | err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0 | ||
1477 | err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0 | ||
1478 | err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0 | ||
1479 | err_oldkernel db 'Cannot load a ramdisk with an old kernel image.' | ||
1480 | db CR, LF, 0 | ||
1481 | err_notdos db ': attempted DOS system call', CR, LF, 0 | ||
1482 | err_comlarge db 'COMBOOT image too large.', CR, LF, 0 | ||
1483 | err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0 | ||
1484 | err_bootfailed db CR, LF, 'Boot failed: please change disks and press ' | ||
1485 | db 'a key to continue.', CR, LF, 0 | ||
1486 | ready_msg db 'Ready.', CR, LF, 0 | ||
1487 | crlfloading_msg db CR, LF | ||
1488 | loading_msg db 'Loading ', 0 | ||
1489 | dotdot_msg db '.' | ||
1490 | dot_msg db '.', 0 | ||
1491 | aborted_msg db ' aborted.' ; Fall through to crlf_msg! | ||
1492 | crlf_msg db CR, LF | ||
1493 | null_msg db 0 | ||
1494 | crff_msg db CR, FF, 0 | ||
1495 | syslinux_cfg db 'SYSLINUXCFG' ; Mangled form | ||
1496 | ConfigName db 'syslinux.cfg',0 ; Unmangled form | ||
1497 | %if IS_MDSLINUX | ||
1498 | manifest db 'MANIFEST ' | ||
1499 | %endif | ||
1500 | ; | ||
1501 | ; Command line options we'd like to take a look at | ||
1502 | ; | ||
1503 | ; mem= and vga= are handled as normal 32-bit integer values | ||
1504 | initrd_cmd db 'initrd=' | ||
1505 | initrd_cmd_len equ 7 | ||
1506 | |||
1507 | ; | ||
1508 | ; Config file keyword table | ||
1509 | ; | ||
1510 | %include "keywords.inc" | ||
1511 | |||
1512 | ; | ||
1513 | ; Extensions to search for (in *forward* order). | ||
1514 | ; | ||
1515 | exten_table: db 'CBT',0 ; COMBOOT (specific) | ||
1516 | db 'BSS',0 ; Boot Sector (add superblock) | ||
1517 | db 'BS ',0 ; Boot Sector | ||
1518 | db 'COM',0 ; COMBOOT (same as DOS) | ||
1519 | db 'C32',0 ; COM32 | ||
1520 | exten_table_end: | ||
1521 | dd 0, 0 ; Need 8 null bytes here | ||
1522 | |||
1523 | ; | ||
1524 | ; Misc initialized (data) variables | ||
1525 | ; | ||
1526 | %ifdef debug ; This code for debugging only | ||
1527 | debug_magic dw 0D00Dh ; Debug code sentinel | ||
1528 | %endif | ||
1529 | |||
1530 | alignb 4, db 0 | ||
1531 | BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf | ||
1532 | BufSafeSec dw trackbufsize/SECTOR_SIZE ; = how many sectors? | ||
1533 | BufSafeBytes dw trackbufsize ; = how many bytes? | ||
1534 | EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes | ||
1535 | %ifndef DEPEND | ||
1536 | %if ( trackbufsize % SECTOR_SIZE ) != 0 | ||
1537 | %error trackbufsize must be a multiple of SECTOR_SIZE | ||
1538 | %endif | ||
1539 | %endif |