Contents of /tags/mkinitrd-6_1_2/isolinux/ldlinux.asm
Parent Directory | Revision Log
Revision 844 -
(show annotations)
(download)
Mon May 4 17:23:09 2009 UTC (15 years, 4 months ago) by niro
File size: 36902 byte(s)
Mon May 4 17:23:09 2009 UTC (15 years, 4 months ago) by niro
File size: 36902 byte(s)
tagged 'mkinitrd-6_1_2'
1 | ; -*- 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 |