[SERVICES]
[reactos.git] / reactos / boot / freeldr / bootsect / ext2.S
1 // EXT2.ASM
2 // EXT2 Boot Sector
3 // Copyright (c) 2002, 2003 Brian Palmer
4
5 // [bp-0x04] Here we will store the number of sectors per track
6 // [bp-0x08] Here we will store the number of heads
7 // [bp-0x0c] Here we will store the size of the disk as the BIOS reports in CHS form
8 // [bp-0x10] Here we will store the number of LBA sectors read
9
10 #include <asm.inc>
11 .code16
12
13 SECTORS_PER_TRACK = HEX(04)
14 NUMBER_OF_HEADS = HEX(08)
15 BIOS_CHS_DRIVE_SIZE = HEX(0C)
16 LBA_SECTORS_READ = HEX(10)
17
18
19 EXT2_ROOT_INO = 2
20 EXT2_S_IFMT = HEX(0f0)
21 EXT2_S_IFREG = HEX(080)
22
23
24 //org 7c00h
25
26
27 start:
28 jmp short main
29 nop
30
31 BootDrive:
32 .byte HEX(80)
33 //BootPartition db 0 // Moved to end of boot sector to have a standard format across all boot sectors
34 //SectorsPerTrack db 63 // Moved to [bp-SECTORS_PER_TRACK]
35 //NumberOfHeads dw 16 // Moved to [bp-NUMBER_OF_HEADS]
36 //BiosCHSDriveSize dd (1024 * 1024 * 63) // Moved to [bp-BIOS_CHS_DRIVE_SIZE]
37 //LBASectorsRead dd 0 // Moved to [bp-LBA_SECTORS_READ]
38
39 Ext2VolumeStartSector:
40 .long 263088 // Start sector of the ext2 volume
41 Ext2BlockSize:
42 .long 2 // Block size in sectors
43 Ext2BlockSizeInBytes:
44 .long 1024 // Block size in bytes
45 Ext2PointersPerBlock:
46 .long 256 // Number of block pointers that can be contained in one block
47 Ext2GroupDescPerBlock:
48 .long 32 // Number of group descriptors per block
49 Ext2FirstDataBlock:
50 .long 1 // First data block (1 for 1024-byte blocks, 0 for bigger sizes)
51 Ext2InodesPerGroup:
52 .long 2048 // Number of inodes per group
53 Ext2InodesPerBlock:
54 .long 8 // Number of inodes per block
55
56 Ext2ReadEntireFileLoadSegment:
57 .word 0
58 Ext2InodeIndirectPointer:
59 .long 0
60 Ext2InodeDoubleIndirectPointer:
61 .long 0
62 Ext2BlocksLeftToRead:
63 .long 0
64
65 main:
66 xor ax,ax // Setup segment registers
67 mov ds,ax // Make DS correct
68 mov es,ax // Make ES correct
69 mov ss,ax // Make SS correct
70 mov bp, HEX(7c00)
71 mov sp, HEX(7b00) // Setup a stack
72
73 mov si, offset BootDrive
74 cmp byte ptr [si], HEX(0ff) // If they have specified a boot drive then use it
75 jne GetDriveParameters
76
77 mov [si],dl // Save the boot drive
78
79
80 GetDriveParameters:
81 mov ah, 8
82 mov dl,[si] // Get boot drive in dl
83 int HEX(13) // Request drive parameters from the bios
84 jnc CalcDriveSize // If the call succeeded then calculate the drive size
85
86 // If we get here then the call to the BIOS failed
87 // so just set CHS equal to the maximum addressable
88 // size
89 mov cx, HEX(0ffff)
90 mov dh,cl
91
92 CalcDriveSize:
93 // Now that we have the drive geometry
94 // lets calculate the drive size
95 mov bl,ch // Put the low 8-bits of the cylinder count into BL
96 mov bh,cl // Put the high 2-bits in BH
97 shr bh,6 // Shift them into position, now BX contains the cylinder count
98 and cl, HEX(3f) // Mask off cylinder bits from sector count
99 // CL now contains sectors per track and DH contains head count
100 movzx eax,dh // Move the heads into EAX
101 movzx ebx,bx // Move the cylinders into EBX
102 movzx ecx,cl // Move the sectors per track into ECX
103 inc eax // Make it one based because the bios returns it zero based
104 mov [bp-NUMBER_OF_HEADS],eax // Save number of heads
105 mov [bp-SECTORS_PER_TRACK],ecx // Save number of sectors per track
106 inc ebx // Make the cylinder count one based also
107 mul ecx // Multiply heads with the sectors per track, result in edx:eax
108 mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
109
110 // We now have the total number of sectors as reported
111 // by the bios in eax, so store it in our variable
112 mov [bp-BIOS_CHS_DRIVE_SIZE],eax
113
114
115 LoadExtraBootCode:
116 // First we have to load our extra boot code at
117 // sector 1 into memory at [0000:7e00h]
118 //mov eax,01h
119 xor eax,eax
120 inc eax // Read logical sector 1, EAX now = 1
121 mov cx,1 // Read one sector
122 mov bx, HEX(7e00) // Read sector to [0000:7e00h]
123 call ReadSectors
124
125 jmp LoadRootDirectory
126
127
128
129 // Reads ext2 group descriptor into [7000:8000]
130 // We read it to this arbitrary location so
131 // it will not cross a 64k boundary
132 // EAX has group descriptor number to read
133 Ext2ReadGroupDesc:
134 shl eax,5 // Group = (Group * sizeof(GROUP_DESCRIPTOR) /* 32 */)
135 xor edx,edx
136 div dword ptr [bp+Ext2GroupDescPerBlock] // Group = (Group / Ext2GroupDescPerBlock)
137 add eax, dword ptr [bp+Ext2FirstDataBlock] // Group = Group + Ext2FirstDataBlock + 1
138 inc eax // EAX now has the group descriptor block number
139 // EDX now has the group descriptor offset in the block
140
141 // Adjust the read offset so that the
142 // group descriptor is read to 7000:8000
143 mov ebx, HEX(78000)
144 sub ebx,edx
145 shr ebx,4
146 mov es,bx
147 xor bx,bx
148
149
150 // Everything is now setup to call Ext2ReadBlock
151 // Instead of using the call instruction we will
152 // just put Ext2ReadBlock right after this routine
153
154 // Reads ext2 block into ES:[BX]
155 // EAX has logical block number to read
156 Ext2ReadBlock:
157 mov ecx, dword ptr [bp+Ext2BlockSize]
158 mul ecx
159 jmp ReadSectors
160
161 // Reads ext2 inode into [6000:8000]
162 // We read it to this arbitrary location so
163 // it will not cross a 64k boundary
164 // EAX has inode number to read
165 Ext2ReadInode:
166 dec eax // Inode = Inode - 1
167 xor edx,edx
168 div dword ptr [bp+Ext2InodesPerGroup] // Inode = (Inode / Ext2InodesPerGroup)
169 mov ebx,eax // EBX now has the inode group number
170 mov eax,edx
171 xor edx,edx
172 div dword ptr [bp+Ext2InodesPerBlock] // Inode = (Inode / Ext2InodesPerBlock)
173 shl edx,7 // FIXME: InodeOffset *= 128 (make the array index a byte offset)
174 // EAX now has the inode offset block number from inode table
175 // EDX now has the inode offset in the block
176
177 // Save the inode values and put the group
178 // descriptor number in EAX and read it in
179 push edx
180 push eax
181 mov eax,ebx
182 call Ext2ReadGroupDesc
183
184 // Group descriptor has been read, now
185 // grab the inode table block number from it
186 push HEX(7000)
187 pop es
188 mov di, HEX(8008)
189 pop eax // Restore inode offset block number from stack
190 add eax, es:[di] // Add the inode table start block
191
192 // Adjust the read offset so that the
193 // inode we want is read to 6000:8000
194 pop edx // Restore inode offset in the block from stack
195 mov ebx, HEX(68000)
196 sub ebx,edx
197 shr ebx,4
198 mov es,bx
199 xor bx,bx
200
201 call Ext2ReadBlock
202 ret
203
204
205 // Reads logical sectors into ES:[BX]
206 // EAX has logical sector number to read
207 // CX has number of sectors to read
208 ReadSectors:
209 add eax, dword ptr [bp+Ext2VolumeStartSector] // Add the start of the volume
210 cmp eax, [bp-BIOS_CHS_DRIVE_SIZE] // Check if they are reading a sector outside CHS range
211 jae ReadSectorsLBA // Yes - go to the LBA routine
212 // If at all possible we want to use LBA routines because
213 // They are optimized to read more than 1 sector per read
214
215 pushad // Save logical sector number & sector count
216
217 CheckInt13hExtensions: // Now check if this computer supports extended reads
218 mov ah, HEX(41) // AH = 41h
219 mov bx, HEX(55aa) // BX = 55AAh
220 mov dl, byte ptr [bp+BootDrive] // DL = drive (80h-FFh)
221 int HEX(13) // IBM/MS INT 13 Extensions - INSTALLATION CHECK
222 jc ReadSectorsCHS // CF set on error (extensions not supported)
223 cmp bx, HEX(0aa55) // BX = AA55h if installed
224 jne ReadSectorsCHS
225 test cl,1 // CX = API subset support bitmap
226 jz ReadSectorsCHS // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
227
228 popad // Restore sector count & logical sector number
229
230 ReadSectorsLBA:
231 pushad // Save logical sector number & sector count
232
233 cmp cx, 64 // Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64
234 jbe ReadSectorsSetupDiskAddressPacket // If we are reading less than 65 sectors then just do the read
235 mov cx,64 // Otherwise read only 64 sectors on this loop iteration
236
237 ReadSectorsSetupDiskAddressPacket:
238 mov [bp-LBA_SECTORS_READ],cx
239 mov word ptr [bp-LBA_SECTORS_READ+2],0
240 data32 push 0
241 push eax // Put 64-bit logical block address on stack
242 push es // Put transfer segment on stack
243 push bx // Put transfer offset on stack
244 push cx // Set transfer count
245 push 16 // Set size of packet to 10h
246 mov si,sp // Setup disk address packet on stack
247
248
249 mov dl, byte ptr [bp+BootDrive] // Drive number
250 mov ah, HEX(42) // Int 13h, AH = 42h - Extended Read
251 int HEX(13) // Call BIOS
252 jc PrintDiskError // If the read failed then abort
253
254 add sp, 16 // Remove disk address packet from stack
255
256 popad // Restore sector count & logical sector number
257
258 push bx
259 mov ebx, [bp-LBA_SECTORS_READ]
260 add eax,ebx // Increment sector to read
261 shl ebx,5
262 mov dx,es
263 add dx,bx // Setup read buffer for next sector
264 mov es,dx
265 pop bx
266
267 sub cx,[bp-LBA_SECTORS_READ]
268 jnz ReadSectorsLBA // Read next sector
269
270 ret
271
272
273 // Reads logical sectors into ES:[BX]
274 // EAX has logical sector number to read
275 // CX has number of sectors to read
276 ReadSectorsCHS:
277 popad // Get logical sector number & sector count off stack
278
279 ReadSectorsCHSLoop:
280 pushad
281 xor edx,edx
282 mov ecx, [bp-SECTORS_PER_TRACK]
283 div ecx // Divide logical by SectorsPerTrack
284 inc dl // Sectors numbering starts at 1 not 0
285 mov cl,dl // Sector in CL
286 mov edx,eax
287 shr edx,16
288 div word ptr [bp-NUMBER_OF_HEADS] // Divide logical by number of heads
289 mov dh,dl // Head in DH
290 mov dl, byte ptr [bp+BootDrive] // Drive number in DL
291 mov ch,al // Cylinder in CX
292 ror ah,2 // Low 8 bits of cylinder in CH, high 2 bits
293 // in CL shifted to bits 6 & 7
294 or cl,ah // Or with sector number
295 mov ax, HEX(0201)
296 int HEX(13) // DISK - READ SECTORS INTO MEMORY
297 // AL = number of sectors to read, CH = track, CL = sector
298 // DH = head, DL = drive, ES:BX -> buffer to fill
299 // Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
300
301 jc PrintDiskError // If the read failed then abort
302
303 popad
304
305 inc eax // Increment Sector to Read
306
307 mov dx,es
308 add dx, HEX(20) // Increment read buffer for next sector
309 mov es,dx
310
311 loop ReadSectorsCHSLoop // Read next sector
312
313 ret
314
315
316
317
318 // Displays a disk error message
319 // And reboots
320 PrintDiskError:
321 mov si,msgDiskError // Bad boot disk message
322 call PutChars // Display it
323
324 Reboot:
325 mov si,msgAnyKey // Press any key message
326 call PutChars // Display it
327 xor ax,ax
328 int HEX(16) // Wait for a keypress
329 int HEX(19) // Reboot
330
331 PutChars:
332 lodsb
333 or al,al
334 jz short Done
335 call PutCharsCallBios
336 jmp short PutChars
337 PutCharsCallBios:
338 mov ah, HEX(0e)
339 mov bx, HEX(07)
340 int HEX(10)
341 ret
342 Done:
343 mov al, HEX(0d)
344 call PutCharsCallBios
345 mov al, HEX(0a)
346 call PutCharsCallBios
347 ret
348
349
350
351 msgDiskError:
352 .ascii "Disk error", NUL
353 // Sorry, need the space...
354 //msgAnyKey db 'Press any key to restart',0
355 msgAnyKey:
356 .ascii "Press key", NUL
357
358 // times 509-($-$$) db 0 // Pad to 509 bytes
359 .org 509
360
361 BootPartition:
362 .byte 0
363
364 .word HEX(0aa55) // BootSector signature
365
366
367 // End of bootsector
368 //
369 // Now starts the extra boot code that we will store
370 // at sector 1 on a EXT2 volume
371
372
373
374 LoadRootDirectory:
375
376 mov eax,EXT2_ROOT_INO // Put the root directory inode number in EAX
377 call Ext2ReadInode // Read in the inode
378
379 // Point ES:DI to the inode structure at 6000:8000
380 push HEX(6000)
381 pop es
382 mov di, HEX(8000)
383 push di
384 push es // Save these for later
385
386 // Get root directory size from inode structure
387 mov eax, es:[di+4]
388 push eax
389
390 // Now that the inode has been read in load
391 // the root directory file data to 0000:8000
392 call Ext2ReadEntireFile
393
394 // Since the root directory was loaded to 0000:8000
395 // then add 8000h to the root directory's size
396 pop eax
397 mov edx, HEX(8000) // Set EDX to the current offset in the root directory
398 add eax,edx // Initially add 8000h to the size of the root directory
399
400 SearchRootDirectory:
401 push edx // Save current offset in root directory
402 push eax // Save the size of the root directory
403
404 // Now we have to convert the current offset
405 // in the root directory to a SEGMENT:OFFSET pair
406 mov eax,edx
407 xor edx,edx
408 mov ecx,16
409 div ecx // Now AX:DX has segment & offset
410 mov es,ax
411 mov di,dx
412 push di // Save the start of the directory entry
413 add di, 8 // Add the offset to the filename
414 mov si,filename
415 mov cl,11
416 repe cmpsb // Compare the file names
417 pop di
418 pop eax
419 pop edx
420 jz FoundFile
421
422 // Nope, didn't find it in this entry, keep looking
423 movzx ecx,word ptr es:[di+4]
424 add edx,ecx
425
426 // Check to see if we have reached the
427 // end of the root directory
428 cmp edx,eax
429 jb SearchRootDirectory
430 jmp PrintFileNotFound
431
432 FoundFile:
433 mov eax,es:[di] // Get inode number from directory entry
434 call Ext2ReadInode // Read in the inode
435
436 // Point ES:DI to the inode structure at 6000:8000
437 pop es
438 pop di // These were saved earlier
439
440 mov cx, es:[di] // Get the file mode so we can make sure it's a regular file
441 and ch,EXT2_S_IFMT // Mask off everything but the file type
442 cmp ch,EXT2_S_IFREG // Make sure it's a regular file
443 je LoadFreeLoader
444 jmp PrintRegFileError
445
446 LoadFreeLoader:
447 mov si,msgLoading // "Loading FreeLoader..." message
448 call PutChars // Display it
449
450 call Ext2ReadEntireFile // Read freeldr.sys to 0000:8000
451
452 mov dl, byte ptr [bp+BootDrive]
453 mov dh, byte ptr [bp+BootPartition]
454 push 0 // push segment (0x0000)
455 mov eax, [HEX(8000) + HEX(0A8)] // load the RVA of the EntryPoint into eax
456 add eax, HEX(8000) // RVA -> VA
457 push ax // push offset
458 retf // Transfer control to FreeLoader
459
460
461
462
463
464 // Reads ext2 file data into [0000:8000]
465 // This function assumes that the file's
466 // inode has been read in to 6000:8000 *and*
467 // ES:DI points to 6000:8000
468 // This will load all the blocks up to
469 // and including the double-indirect pointers.
470 // This should be sufficient because it
471 // allows for ~64MB which is much bigger
472 // than we need for a boot loader.
473 Ext2ReadEntireFile:
474
475 // Reset the load segment
476 mov word ptr [bp+Ext2ReadEntireFileLoadSegment], HEX(800)
477
478 // Now we must calculate how
479 // many blocks to read in
480 // We will do this by rounding the
481 // file size up to the next block
482 // size and then dividing by the block size
483 mov eax, dword ptr [bp+Ext2BlockSizeInBytes] // Get the block size in bytes
484 push eax
485 dec eax // Ext2BlockSizeInBytes -= 1
486 add eax, es:[di+4] // Add the file size
487 xor edx,edx
488 pop ecx // Divide by the block size in bytes
489 div ecx // EAX now contains the number of blocks to load
490 push eax
491
492 // Make sure the file size isn't zero
493 cmp eax, 0
494 jnz Ext2ReadEntireFile2
495 jmp PrintFileSizeError
496
497 Ext2ReadEntireFile2:
498 // Save the indirect & double indirect pointers
499 mov edx, es:[di+ HEX(58)] // Get indirect pointer
500 mov dword ptr [bp+Ext2InodeIndirectPointer], edx // Save indirect pointer
501 mov edx, es:[di+ HEX(5c)] // Get double indirect pointer
502 mov dword ptr [bp+Ext2InodeDoubleIndirectPointer],edx // Save double indirect pointer
503
504 // Now copy the direct pointers to 7000:0000
505 // so that we can call Ext2ReadDirectBlocks
506 push ds // Save DS
507 push es
508 push HEX(7000)
509 pop es
510 pop ds
511 mov si, HEX(8028)
512 xor di,di // DS:SI = 6000:8028 ES:DI = 7000:0000
513 mov cx,24 // Moving 24 words of data
514 rep movsw
515 pop ds // Restore DS
516
517 // Now we have all the block pointers in the
518 // right location so read them in
519 pop eax // Restore the total number of blocks in this file
520 xor ecx,ecx // Set the max count of blocks to read to 12
521 mov cl,12 // which is the number of direct block pointers in the inode
522 call Ext2ReadDirectBlockList
523
524 // Check to see if we actually have
525 // blocks left to read
526 cmp eax, 0
527 jz Ext2ReadEntireFileDone
528
529 // Now we have read all the direct blocks in
530 // the inode. So now we have to read the indirect
531 // block and read all it's direct blocks
532 push eax // Save the total block count
533 mov eax, dword ptr [bp+Ext2InodeIndirectPointer] // Get the indirect block pointer
534 push HEX(7000)
535 pop es
536 xor bx,bx // Set the load address to 7000:0000
537 call Ext2ReadBlock // Read the block
538
539 // Now we have all the block pointers from the
540 // indirect block in the right location so read them in
541 pop eax // Restore the total block count
542 mov ecx, dword ptr [bp+Ext2PointersPerBlock] // Get the number of block pointers that one block contains
543 call Ext2ReadDirectBlockList
544
545 // Check to see if we actually have
546 // blocks left to read
547 cmp eax, 0
548 jz Ext2ReadEntireFileDone
549
550 // Now we have read all the direct blocks from
551 // the inode's indirect block pointer. So now
552 // we have to read the double indirect block
553 // and read all it's indirect blocks
554 // (whew, it's a good thing I don't support triple indirect blocks)
555 mov dword ptr [bp+Ext2BlocksLeftToRead],eax // Save the total block count
556 mov eax, dword ptr [bp+Ext2InodeDoubleIndirectPointer] // Get the double indirect block pointer
557 push HEX(7800)
558 pop es
559 push es // Save an extra copy of this value on the stack
560 xor bx,bx // Set the load address to 7000:8000
561 call Ext2ReadBlock // Read the block
562
563 pop es // Put 7800h into ES (saved on the stack already)
564 xor di,di
565
566 Ext2ReadIndirectBlock:
567 mov eax, es:[di] // Get indirect block pointer
568 add di, 4 // Update DI for next array index
569 push es
570 push di
571
572 push HEX(7000)
573 pop es
574 xor bx,bx // Set the load address to 7000:0000
575 call Ext2ReadBlock // Read the indirect block
576
577 // Now we have all the block pointers from the
578 // indirect block in the right location so read them in
579 mov eax, dword ptr [bp+Ext2BlocksLeftToRead] // Restore the total block count
580 mov ecx, dword ptr [bp+Ext2PointersPerBlock] // Get the number of block pointers that one block contains
581 call Ext2ReadDirectBlockList
582 mov dword ptr [bp+Ext2BlocksLeftToRead],eax // Save the total block count
583 pop di
584 pop es
585
586 // Check to see if we actually have
587 // blocks left to read
588 cmp eax, 0
589 jnz Ext2ReadIndirectBlock
590
591 Ext2ReadEntireFileDone:
592 ret
593
594 // Reads a maximum number of blocks
595 // from an array at 7000:0000
596 // and updates the total count
597 // ECX contains the max number of blocks to read
598 // EAX contains the number of blocks left to read
599 // On return:
600 // EAX contians the new number of blocks left to read
601 Ext2ReadDirectBlockList:
602 cmp eax,ecx // Compare it to the maximum number of blocks to read
603 ja CallExt2ReadDirectBlocks // If it will take more blocks then just read all of the blocks
604 mov cx,ax // Otherwise adjust the block count accordingly
605
606 CallExt2ReadDirectBlocks:
607 sub eax,ecx // Subtract the number of blocks being read from the total count
608 push eax // Save the new total count
609 call Ext2ReadDirectBlocks
610 pop eax // Restore the total count
611 ret
612
613
614 // Reads a specified number of blocks
615 // from an array at 7000:0000
616 // CX contains the number of blocks to read
617 Ext2ReadDirectBlocks:
618
619 push HEX(7000)
620 pop es
621 xor di,di // Set ES:DI = 7000:0000
622
623 Ext2ReadDirectBlocksLoop:
624 mov eax,es:[di] // Get direct block pointer from array
625 add di, 4 // Update DI for next array index
626
627 push cx // Save number of direct blocks left
628 push es // Save array segment
629 push di // Save array offset
630 mov es,[bp+Ext2ReadEntireFileLoadSegment]
631 xor bx,bx // Setup load address for next read
632
633 call Ext2ReadBlock // Read the block (this updates ES for the next read)
634
635 mov [bp+Ext2ReadEntireFileLoadSegment],es // Save updated ES
636
637 pop di // Restore the array offset
638 pop es // Restore the array segment
639 pop cx // Restore the number of blocks left
640
641 loop Ext2ReadDirectBlocksLoop
642
643 // At this point all the direct blocks should
644 // be loaded and ES (Ext2ReadEntireFileLoadSegment)
645 // should be ready for the next read.
646 ret
647
648
649
650 // Displays a file not found error message
651 // And reboots
652 PrintFileNotFound:
653 mov si,msgFreeLdr // FreeLdr not found message
654 jmp short DisplayItAndReboot
655
656 // Displays a file size is 0 error
657 // And reboots
658 PrintFileSizeError:
659 mov si,msgFileSize // Error message
660 jmp short DisplayItAndReboot
661
662 // Displays a file is not a regular file error
663 // And reboots
664 PrintRegFileError:
665 mov si,msgRegFile // Error message
666 DisplayItAndReboot:
667 call PutChars // Display it
668 jmp Reboot
669
670 msgFreeLdr:
671 .ascii "freeldr.sys not found", NUL
672 msgFileSize:
673 .ascii "File size 0", NUL
674 msgRegFile:
675 .ascii "freeldr.sys isnt a regular file", NUL
676 filename:
677 .ascii "freeldr.sys"
678 msgLoading:
679 .ascii "Loading...", NUL
680
681 // times 1022-($-$$) db 0 // Pad to 1022 bytes
682 .org 1022
683
684 .word HEX(0aa55) // BootSector signature
685
686 .endcode16
687
688 END