[FREELDR] Introduce a MSVC "linker script" file that centralizes the commands for...
[reactos.git] / boot / freeldr / bootsect / faty.S
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Bootsector
4 * FILE: boot/freeldr/bootsect/faty.S
5 * PURPOSE: Combined FAT12, FAT16 and FAT32 boot sector
6 * PROGRAMMERS: Brian Palmer
7 * Timo Kreuzer
8 */
9
10 #define DISKREADBUFFER HEX(8E000)
11
12 /*
13 * Layout of a FAT volume:
14 *
15 * |---------------------------------------------------------
16 * | * BootSector |
17 * | * FS Information Sector (FAT32 only) | ReservedSectors
18 * | * ... more reserved sectors ... |
19 * |--------------------------------------------------------
20 * | * FAT 1 | NumberOfFats
21 * | * FAT 2 | *
22 * | * [more FATs] | SectorsPerFat
23 * |---------------------------------------------------------
24 * | * Root Directory (FAT12/FAT16 only) | MaxRootEntries / 16
25 * |---------------------------------------------------------
26 * | * File data |
27 * | .... |
28 * |----------------------------------------
29 */
30
31 /* INCLUDES ******************************************************************/
32
33 #include <asm.inc>
34 #include <freeldr/include/arch/pc/x86common.h>
35
36 #define ADDRESS_FOR_DIRENTRIES HEX(10000)
37
38 SizeOfDataArea = 32
39
40 /* Put the stack below the data area */
41 BootSectorStackTop = (HEX(7c00) - SizeOfDataArea)
42
43 /* Data area offsets for uninitialized data */
44 DataAreaStart = BootSectorStackTop + 0 /* dword */
45 #ifndef FAT32
46 RootDirStartSector = BootSectorStackTop + 4 /* dword */
47 #endif
48 BiosCHSDriveSize = BootSectorStackTop + 8 /* dword */
49 LBASectorsRead = BootSectorStackTop + 12 /* dword */
50 ReadSectorsOffset = BootSectorStackTop + 16 /* word */
51 ReadClusterOffset = BootSectorStackTop + 18 /* word */
52 PutCharsOffset = BootSectorStackTop + 20 /* word */
53
54 /* Macro for bp relative memory access to reduce code size */
55 #define BP_REL(x) ss:[bp + x - BootSectorStackTop]
56
57 /* The code starts at 0x7c00 */
58 // org 7c00h
59
60 .code16
61
62 /******************************************************************************
63 * BIOS Parameter Block (BPB) *
64 ******************************************************************************/
65 /* We have 3 bytes at the entry point to jump over the data area */
66 start:
67 jmp short main // FIXME: When compiling FAT32, assembler will complain
68 // that the label is too far... Need investigation!
69 nop
70
71 /* Here starts the BIOS Parameter Block (BPB) data.
72 The real data will be copied during install */
73 OEMName:
74 .ascii "FrLdr1.0"
75 BytesPerSector:
76 .word 512
77 SectorsPerCluster:
78 .byte 0
79 ReservedSectors:
80 .word 32
81 NumberOfFats:
82 .byte 2
83 MaxRootEntries:
84 .word 0 // Always zero for FAT32 volumes
85 TotalSectors:
86 .word 0 // Always zero for FAT32 volumes
87 MediaDescriptor:
88 .byte HEX(0f8)
89 SectorsPerFat:
90 .word 0 // Always zero for FAT32 volumes
91 SectorsPerTrack:
92 .word 0
93 NumberOfHeads:
94 .word 0
95 HiddenSectors:
96 .long 0
97 TotalSectorsBig:
98 .long 0
99
100 /* Extra data for FAT32 volumes */
101 #ifdef FAT32
102 SectorsPerFatBig:
103 .long 0
104 ExtendedFlags:
105 .word 0
106 FSVersion:
107 .word 0
108 RootDirStartCluster:
109 .long 0
110 FSInfoSector:
111 .word 0
112 BackupBootSector:
113 .word 6
114 Reserved1:
115 .space 12, 0
116 #endif // FAT32
117
118 BootDrive:
119 .byte 0
120 Reserved:
121 .byte 0
122 ExtendSig:
123 .byte HEX(29)
124 SerialNumber:
125 .long 0
126 VolumeLabel:
127 .ascii "NO NAME "
128 FileSystem:
129 .ascii "FATxx "
130
131
132 /******************************************************************************
133 * String data *
134 ******************************************************************************/
135
136 filename:
137 .ascii "FREELDR SYS"
138
139 msgBootFailure:
140 .ascii "Load failed!", CR, LF, NUL
141
142 msgAnyKey:
143 .ascii "Press any key to reboot...", NUL
144
145
146 /******************************************************************************
147 * Main code entry *
148 * Input: DL = Boot drive *
149 ******************************************************************************/
150 main:
151 /* First setup the segment registers */
152 xor ax, ax
153 mov ds, ax
154 mov ss, ax
155
156 /* Load the stack pointer */
157 mov sp, BootSectorStackTop
158
159 /* Load bp for relative memory access, which saves us some bytes of code
160 size, when used with 32 bit instructions */
161 mov bp, sp
162
163 /* Load the boot drive from the BPB into al */
164 mov al, byte ptr ds:[BootDrive]
165
166 /* Check if it's valid */
167 cmp al, HEX(0ff)
168 je .SaveBootDrive
169
170 /* Copy it into dl */
171 mov dl, al
172
173 .SaveBootDrive:
174 /* Save the bootdrive in the BPB */
175 mov byte ptr ds:[BootDrive], dl
176
177
178 /******************************************************************************
179 * Get drive parameters *
180 ******************************************************************************/
181
182 /* Call INT 13 to get the drive parameters:
183 AH = 08h
184 DL = drive (bit 7 set for hard disk)
185 ES:DI = 0000h:0000h to guard against BIOS bugs */
186 xor di, di
187 mov ah, 8
188 int HEX(13)
189
190 /* Return from INT 13h/08h:
191 CF set on error -> AH = status (07h)
192 CF clear if successful -> AH = 00h
193 AL = 00h on at least some BIOSes
194 BL = drive type (AT/PS2 floppies only)
195 CH = low eight bits of maximum cylinder number
196 CL = bits 0:5 maximum sector number, bits 7:8 high two bits of maximum cylinder number
197 DH = maximum head number
198 DL = number of drives
199 ES:DI -> drive parameter table (floppies only) */
200
201 /* Check for failure */
202 jc BootFailure
203
204
205 /******************************************************************************
206 * Calculate drive size *
207 ******************************************************************************/
208
209 movzx ebx, ch // Put the low 8-bits of the cylinder count into EBX
210 mov bh, cl // Put the high 2-bits in BH
211 shr bh, 6 // Shift them into position, now BX contains the cylinder count
212
213 and cl, HEX(3f) // Mask off cylinder bits from sector count
214 movzx ecx, cl // Move the sectors per track into ECX
215
216 movzx eax, dh // Move the heads into EAX
217
218 inc eax // Make it one based because the bios returns it zero based
219 inc ebx // Make the cylinder count one based also
220 mul ecx // Multiply heads with the sectors per track, result in edx:eax
221 mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
222
223 // We now have the total number of sectors as reported
224 // by the bios in eax, so store it in our variable
225 mov dword ptr BP_REL(BiosCHSDriveSize), eax
226
227
228 /******************************************************************************
229 * Load the FAT *
230 ******************************************************************************/
231
232 /* Load the number of first sector of the FAT into eax */
233 movzx eax, word ptr BP_REL(ReservedSectors)
234 add eax, dword ptr BP_REL(HiddenSectors)
235
236 /* Load sector count into ecx */
237 #ifdef FAT32
238 mov ecx, dword ptr BP_REL(SectorsPerFatBig)
239 #else
240 movzx ecx, word ptr BP_REL(SectorsPerFat)
241 #endif
242
243 /* Save FAT sector and size for later use */
244 pushad
245
246 /* Point ES:DI to the memory that is later the disk read buffer for freeldr.
247 This way we cannot overwrite our FAT with freeldr data */
248 mov bx, DISKREADBUFFER / 16
249 mov es,bx
250 xor di, di
251
252 /* Read the sectors */
253 call ReadSectors
254
255 /* Restore FAT sector and size */
256 popad
257
258
259 /******************************************************************************
260 * Get root directory / data area start *
261 ******************************************************************************/
262
263 /* Copy reserved + hidden sectors to EBX */
264 mov ebx, eax
265
266 /* Calculate (NumberOfFats * SectorsPerFat) */
267 movzx eax, byte ptr BP_REL(NumberOfFats)
268 mul ecx
269
270 /* Add reserved sectors and hidden sectors */
271 add eax, ebx
272
273 #ifndef FAT32
274 /* Save the starting sector of the root directory */
275 mov dword ptr BP_REL(RootDirStartSector), eax
276
277 /* Calculate number of sectors for the root dir:
278 sectors = MaxRootEntries * 32 / 512 (rounded up!) */
279 movzx ebx, word ptr BP_REL(MaxRootEntries)
280 add ebx, 15
281 shr ebx, 4
282
283 /* Add the root dir start sector and save it as DataAreaStart */
284 add ebx, eax
285 mov dword ptr BP_REL(DataAreaStart), ebx
286 #else
287 mov dword ptr BP_REL(DataAreaStart), eax
288
289 /* On FAT32 volumes the root dir start cluster is stored in the BPB */
290 mov eax, dword ptr BP_REL(RootDirStartCluster)
291 #endif
292
293
294 /******************************************************************************
295 * Search the root directory for freeldr *
296 ******************************************************************************/
297 .SearchForFreeldr:
298
299 /* Load ES with the segment where we put the dir entries */
300 mov bx, ADDRESS_FOR_DIRENTRIES / 16
301 mov es, bx
302
303 /* Set the address offset to 0 */
304 xor di, di
305
306 #ifdef FAT32
307 /* Read the dir cluster. This loads the next cluster into EAX */
308 call ReadCluster
309
310 /* Calculate the numer of dir entries in this cluster:
311 dx = SectorsPerCluster * 512 / 32 */
312 movzx dx, byte ptr ds:[SectorsPerCluster]
313 shl dx, 4
314 #else
315 /* Set the number of sectors to read to 1 */
316 xor cx, cx
317 inc cx
318
319 /* Read the sector, but preserve ES */
320 push es
321 call ReadSectors
322 pop es
323
324 /* Set entry count to entries per sector */
325 mov dx, (512 / 32)
326 #endif
327
328 /* Load the start offset of the dir entries into ebx */
329 xor bx, bx
330
331 .CheckDirEntry:
332 /* Load the address of the name into di */
333 mov di, bx
334
335 /* If the first byte of the entry is 0 then we have reached the end */
336 cmp byte ptr es:[di], ch
337 jz BootFailure
338
339 /* Compare with freeldr file name */
340 mov si, offset filename
341 mov cx, 11
342 repe cmpsb
343
344 /* Check if we found the file */
345 jz .FoundFreeLoader
346
347 /* File didn't match, go to next entry */
348 add bx, 32
349
350 /* Decrement entry count and check if we reached the end */
351 dec dx
352 jnz .CheckDirEntry
353
354 #ifdef FAT32
355 /* Check to see if this was the last cluster in the chain */
356 cmp eax, HEX(0ffffff8)
357 jnb BootFailure
358 #endif
359
360 /* Repeat the search process with the next sector / cluster.
361 eax is already incremented in ReadSectors / ReadCluster */
362 jmp .SearchForFreeldr
363
364
365 /******************************************************************************
366 * Load freeldr *
367 ******************************************************************************/
368 .FoundFreeLoader:
369
370 /* Load the cluster number of freeldr into eax */
371 #ifdef FAT32
372 #error unsupported
373 #else
374 movzx eax, word ptr es:[bx + HEX(1A)]
375 #endif
376
377 /* Load es:di with the freeldr start address */
378 mov dx, FREELDR_BASE / 16
379 mov es, dx
380 xor di, di
381
382 .LoadNextCluster:
383 /* Load the cluster to the current address. EAX is adjusted to the next
384 cluster and ES is adjusted for the next read */
385 call ReadCluster
386
387 /* Check if this is the last cluster in the chain */
388 #if defined(FAT32)
389 cmp eax, HEX(0ffffff8)
390 #elif defined(FAT12)
391 cmp ax, HEX(0ff8)
392 #else
393 cmp ax, HEX(0fff8)
394 #endif
395 jb .LoadNextCluster
396
397 /* Load boot drive into DL, boot partition into DH */
398 mov dl, byte ptr ds:[BootDrive]
399 mov dh, byte ptr ds:[BootPartition]
400
401 /* Now the complete freeldr imag is loaded.
402 Jump to the realmode entry point. */
403 ljmp16 0, FREELDR_BASE
404
405
406
407 BootFailure:
408 mov si, offset msgBootFailure
409 call PutChars
410
411
412 Reboot:
413 /* Output "Press any key to reboot" message */
414 mov si, offset msgAnyKey
415 call PutChars
416
417 /* Wait for a keypress */
418 xor ax, ax
419 int HEX(16)
420
421 /* Reboot */
422 int HEX(19)
423
424
425 /******************************************************************************
426 * PROCEDURE ReadCluster *
427 * Input: EAX = Cluster number, ES:DI = Target *
428 * Modifies: EAX (next cluster number), BX, DX (undefined) *
429 ******************************************************************************/
430 ReadCluster:
431
432 pushad
433
434 // StartSector = ((Cluster - 2) * SectorsPerCluster) + SectorsForFat + ReservedSectors + HiddenSectors
435 // StartSector = ((Cluster - 2) * SectorsPerCluster) + DataAreaStart
436
437 /* Substract 2 */
438 dec eax
439 dec eax
440
441 /* Multiply with SectorsPerCluster */
442 movzx ecx, byte ptr BP_REL(SectorsPerCluster)
443 mul ecx
444
445 /* Add DataAreaStart */
446 add eax, dword ptr BP_REL(DataAreaStart)
447
448 /* Call ReadSectors. EAX = SectorNumber, ECX = SectorsPerCluster */
449 call ReadSectors
450
451 /* Restore the cluster number */
452 popad
453
454 /* Save ES */
455 push es
456
457 #if defined(FAT32)
458 #error FAT32 not implemented
459 #elif defined(FAT12)
460 #error FAT12 not implemented
461 #else
462 /* DX:AX = AX * 2 (since FAT16 entries are 2 bytes) */
463 mov bx, 2
464 mul bx
465
466 /* Shift DX, so that it is the segment offset: DX = DX * (64K / 16) */
467 shl dx, 12
468
469 /* Put segment address of FAT into ES */
470 add dx, DISKREADBUFFER / 16
471 mov es, dx
472
473 /* Put the FAT entry offset into EBX for indirect mov */
474 mov bx, ax
475
476 /* Put the content of the FAT entry into AX */
477 mov ax, es:[bx]
478 #endif
479
480 /* Restore ES and return */
481 pop es
482 ret
483
484
485 /******************************************************************************
486 * PROCEDURE ReadSectors *
487 * Input: EAX = Sector start number, ECX = number of sectors, ES:DI = Target *
488 * Modifies: EAX (incremented by sector count), CX = 0, ES (incremented), *
489 * EBX undefined *
490 ******************************************************************************/
491 ReadSectors:
492 /* We could possibly also implement CHS, but it's currently unimplemented */
493 //jmp $
494 ReadSectorsLBA:
495
496 /* Copy number of sectors to ebx */
497 movzx ebx, cx
498
499 /* Since the LBA calls only support 0x7F sectors at a time,
500 we will limit ourselves to 64 */
501 cmp bx, 64
502 jbe .ReadSectorsLBA2
503 mov bx, 64
504
505 .ReadSectorsLBA2:
506
507 /* Save logical sector number & sector count */
508 pushad
509
510 /* Setup the disk address packet on the stack */
511 .byte HEX(66) // size overwrite prefix for next push
512 push 0 // Put 64-bit logical block address (high part) on stack
513 push eax // Put 64-bit logical block address (low part) on stack
514 push es // Put transfer segment on stack
515 push di // Put transfer offset on stack
516 push bx // Set transfer count (for this round)
517 push 16 // Set size of packet to 16
518
519 /* Point si to the disk address packet on stack */
520 mov si, sp
521
522 /* Set the drive number */
523 mov dl, byte ptr ds:[BootDrive]
524 //jmp $
525 /* Call INT 13h, AH = 42h - Extended Read
526 Input: ...
527 Modifies: ... */
528 mov ah, HEX(42)
529 int HEX(13)
530 //jmp $
531 /* Check for failure */
532 jc BootFailure
533
534 /* Remove disk address packet from stack */
535 add sp, 16
536
537 /* Adjust ES to point to the next sector */
538 shl bx, 5
539 mov ax, es
540 add ax, bx
541 mov es, ax
542
543 /* Restore sector count & logical sector number */
544 popad
545
546 /* Adjust the sector number to the next sector we need to read
547 by adding the number of sectors that we read */
548 add eax, ebx
549
550 /* Adjust remaining sectors */
551 sub cx, bx
552 jnz ReadSectorsLBA
553
554 /* return */
555 ret
556
557
558
559 /******************************************************************************
560 * PROCEDURE PutChars *
561 * Input: ESI = Points to string to be printed *
562 * Modifies: AL, AH, SI *
563 ******************************************************************************/
564 PutChars2:
565 mov ah, HEX(0e)
566 mov bx, 7
567 int HEX(10)
568 PutChars:
569 lodsb
570 or al, al
571 jnz short PutChars2
572 ret
573
574
575 /******************************************************************************
576 * Padding and boot sector signature *
577 ******************************************************************************/
578 /* Pad to 509 bytes */
579 .org 509
580
581 BootPartition:
582 .byte 0
583
584 BootSignature:
585 .word HEX(0aa55) // BootSector signature
586
587 .endcode16
588
589 END