Fixed a bug in the LBA extensions detection code.
[reactos.git] / freeldr / freeldr / arch / i386 / disk.S
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2002 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 .text
21 .code16
22
23 #define ASM
24 #include "arch.h"
25
26
27 /*
28 * BOOL BiosInt13Read(ULONG Drive, ULONG Head, ULONG Track, ULONG Sector, ULONG SectorCount, PVOID Buffer);
29 */
30 _biosdisk_drive:
31 .long 0
32 _biosdisk_head:
33 .long 0
34 _biosdisk_track:
35 .long 0
36 _biosdisk_sector:
37 .long 0
38 _biosdisk_nsects:
39 .long 0
40 _biosdisk_buffer:
41 .long 0
42 _biosdisk_retval:
43 .long 0
44 _biosdisk_retrycount:
45 .byte 0
46 _biosdisk_error_code:
47 .byte 0
48 EXTERN(_BiosInt13Read)
49 .code32
50
51 push %ebp
52 push %esi
53 push %edi
54 push %ebx
55 push %ecx
56 push %edx
57
58 /* Get parameters */
59 movl 0x1c(%esp),%eax
60 movl %eax,_biosdisk_drive
61 movl 0x20(%esp),%eax
62 movl %eax,_biosdisk_head
63 movl 0x24(%esp),%eax
64 movl %eax,_biosdisk_track
65 movl 0x28(%esp),%eax
66 movl %eax,_biosdisk_sector
67 movl 0x2c(%esp),%eax
68 movl %eax,_biosdisk_nsects
69 movl 0x30(%esp),%eax
70 movl %eax,_biosdisk_buffer
71
72 call switch_to_real
73
74 .code16
75 pushw %es // Save this just in case
76 movb $3,_biosdisk_retrycount // Set the retry count to 3
77
78 _biosdisk_read:
79 movl _biosdisk_buffer,%eax // Get buffer address in eax
80 shrl $4,%eax // Make linear address into segment
81 movw %ax,%es // Load ES with segment
82 movl _biosdisk_buffer,%ebx // and BX with offset
83 andl $0x0f,%ebx // so that data gets loaded to [ES:BX]
84 movb _biosdisk_sector,%cl // Get the sector in CL
85 movw _biosdisk_track,%ax // Cylinder in AX
86 movb %al,%ch // Now put it in CH
87 rorb $1,%ah // Low 8 bits of cylinder in CH, high 2 bits
88 rorb $1,%ah // in CL shifted to bits 6 & 7
89 andb $0xc0,%ah // Clear out low six bits
90 orb %ah,%cl // Or with sector number
91 movb _biosdisk_head,%dh // Get the head
92 movb _biosdisk_drive,%dl // Get the drive
93 movb $2,%ah // BIOS int 0x13, function 2 - Read Disk Sectors
94 movb _biosdisk_nsects,%al // Number of sectors to read
95 int $0x13 // Read a sector
96
97 // I have recently learned that not all bioses return
98 // the sector read count in the AL register (at least mine doesn't)
99 // even if the sectors were read correctly. So instead
100 // of checking the sector read count we will rely solely
101 // on the carry flag being set on error
102
103 //jmp _biosdisk_done
104 //cmpb _biosdisk_nsects,%al // See how many sectors we actually read
105 //jne _biosdisk_error // Jump if no error
106
107 movb $1,%al // Set the return value to be one (will be set to zero later if needed)
108 jc _biosdisk_error // Jump if error (CF = 1 on error)
109 jmp _biosdisk_done
110
111
112 _biosdisk_error:
113 movb %ah,_biosdisk_error_code// Save the error code
114
115 cmpb $0x11,%ah // Check and see if it was a corrected ECC error
116 je _biosdisk_done // If so then the data is still good, if not fail
117
118 movb _biosdisk_retrycount,%al// Get the current retry count
119 decb %al // Decrement it
120 movb %al,_biosdisk_retrycount// Save it
121 cmpb $0,%al // Is it zero?
122 jz _biosdisk_zero // Yes, return zero
123
124 movb $0,%ah // BIOS int 0x13, function 0 - Reset Disk System
125 movb _biosdisk_drive,%dl // Get the drive
126 int $0x13 // Reset the disk system
127 jmp _biosdisk_read // Try reading again
128
129 _biosdisk_zero:
130 movb $0,%al // We will return zero
131
132 _biosdisk_done:
133 movzbl %al,%eax // Put the number of sectors read into EAX
134 movl %eax,_biosdisk_retval // Save it as the return value
135
136 popw %es // Restore ES
137 call switch_to_prot
138
139 .code32
140
141 movl _biosdisk_retval,%eax // Get return value
142 //movl $1,%eax
143
144 pop %edx
145 pop %ecx
146 pop %ebx
147 pop %edi
148 pop %esi
149 pop %ebp
150 ret
151
152 /*
153 * BOOL BiosInt13ReadExtended(ULONG Drive, ULONG Sector, ULONG SectorCount, PVOID Buffer);
154 */
155 _disk_address_packet:
156 _packet_size:
157 .byte 0x10
158 _packet_reserved:
159 .byte 0
160 _packet_sector_count:
161 .word 0
162 _packet_transfer_buffer_segment:
163 .word 0
164 _packet_transfer_buffer_offset:
165 .word 0
166 _packet_lba_sector_number:
167 .quad 0
168 _packet_64bit_flat_address:
169 .quad 0
170 _int13_extended_drive:
171 .long 0
172 _int13_extended_sector_count:
173 .long 0
174 _int13_extended_retval:
175 .long 0
176 _int13_extended_retrycount:
177 .byte 0
178 EXTERN(_BiosInt13ReadExtended)
179 .code32
180
181 push %ebp
182 push %esi
183 push %edi
184 push %ebx
185 push %ecx
186 push %edx
187
188 /* Get parameters */
189 movl 0x1c(%esp),%eax
190 movl %eax,_int13_extended_drive
191 movl 0x20(%esp),%eax
192 movl %eax,_packet_lba_sector_number
193 movl 0x24(%esp),%eax
194 movw %ax,_packet_sector_count
195 movl %eax,_int13_extended_sector_count
196 movl 0x28(%esp),%eax // Get buffer address in eax
197 shrl $4,%eax // Make linear address into segment
198 movw %ax,_packet_transfer_buffer_segment // Save segment
199 movl 0x28(%esp),%eax // Get buffer address in eax
200 andl $0x0f,%eax // Make linear address into offset
201 movw %ax,_packet_transfer_buffer_offset // Save offset
202
203 call switch_to_real
204
205 .code16
206 pushw %es // Save this just in case
207 movb $3,_int13_extended_retrycount // Set the retry count to 3
208
209 _int13_extended_read:
210 movb _int13_extended_drive,%dl // Get the drive
211 movb $42,%ah // BIOS int 0x13, function 42h - IBM/MS INT 13 Extensions - EXTENDED READ
212 movw $_disk_address_packet,%si // DS:SI -> disk address packet
213 int $0x13 // Read sectors
214
215 movb $1,%al // Set the return value to be one (will be set to zero later if needed)
216 jc _int13_extended_error // Jump if error (CF = 1 on error)
217
218 movl _int13_extended_sector_count,%eax // Get the sector count in eax
219 cmpw _packet_sector_count,%ax // See how many sectors we actually read (returned in disk address packet sector count)
220 jne _int13_extended_error // Jump if not equal
221
222 jmp _int13_extended_done
223
224
225 _int13_extended_error:
226 movb %ah,_biosdisk_error_code // Save the error code
227
228 cmpb $0x11,%ah // Check and see if it was a corrected ECC error
229 je _int13_extended_done // If so then the data is still good, if not fail
230
231 movb _int13_extended_retrycount,%al // Get the current retry count
232 decb %al // Decrement it
233 movb %al,_int13_extended_retrycount // Save it
234 cmpb $0,%al // Is it zero?
235 jz _int13_extended_zero // Yes, return zero
236
237 movb $0,%ah // BIOS int 0x13, function 0 - Reset Disk System
238 movb _int13_extended_drive,%dl // Get the drive
239 int $0x13 // Reset the disk system
240 jmp _int13_extended_read // Try reading again
241
242 _int13_extended_zero:
243 movb $0,%al // We will return zero
244
245 _int13_extended_done:
246 movzbl %al,%eax // Put the number of sectors read into EAX
247 movl %eax,_int13_extended_retval // Save it as the return value
248
249 popw %es // Restore ES
250 call switch_to_prot
251
252 .code32
253
254 movl _int13_extended_retval,%eax // Get return value
255 //movl $1,%eax
256
257 pop %edx
258 pop %ecx
259 pop %ebx
260 pop %edi
261 pop %esi
262 pop %ebp
263 ret
264
265 /*
266 * BOOL BiosInt13ExtensionsSupported(ULONG Drive);
267 */
268 _int13_extension_check_drive:
269 .long 0
270 _int13_extension_check_retval:
271 .long 0
272 EXTERN(_BiosInt13ExtensionsSupported)
273 .code32
274
275 push %ebp
276 push %esi
277 push %edi
278 push %ebx
279 push %ecx
280 push %edx
281
282 /* Get parameters */
283 movl 0x1c(%esp),%eax
284 movl %eax,_int13_extension_check_drive
285
286 call switch_to_real
287
288 .code16
289 // Now make sure this computer supports extended reads
290 movb $0x41,%ah // AH = 41h
291 movw $0x55aa,%bx // BX = 55AAh
292 movb _int13_extension_check_drive,%dl // DL = drive (80h-FFh)
293 int $0x13 // IBM/MS INT 13 Extensions - INSTALLATION CHECK
294 jc _int13_extension_check_error // CF set on error (extensions not supported)
295 cmpw $0xaa55,%bx // BX = AA55h if installed
296 jne _int13_extension_check_error
297 testb $1,%cl // CX = API subset support bitmap
298 jz _int13_extension_check_error // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
299
300 // If we get here then we passed all the int13 extension tests
301 movl $1,_int13_extension_check_retval // Set return value to TRUE
302 jmp _int13_extension_check_done
303
304 _int13_extension_check_error:
305
306 movl $0,_int13_extension_check_retval // The tests failed so return FALSE
307
308 _int13_extension_check_done:
309
310 call switch_to_prot
311
312 .code32
313
314 movl _int13_extension_check_retval,%eax // Get return value
315
316 pop %edx
317 pop %ecx
318 pop %ebx
319 pop %edi
320 pop %esi
321 pop %ebp
322 ret
323
324 /*
325 * ULONG BiosInt13GetLastErrorCode(VOID);
326 */
327 EXTERN(_BiosInt13GetLastErrorCode)
328 .code32
329
330 movzbl _biosdisk_error_code,%eax // Get return value
331
332 ret
333
334
335 /*
336 * void stop_floppy(void);
337 *
338 * Stops the floppy drive from spinning, so that other software is
339 * jumped to with a known state.
340 */
341 EXTERN(_stop_floppy)
342 .code32
343
344 push %eax
345 push %edx
346
347 call switch_to_real
348
349 .code16
350
351 movw $0x3F2, %dx
352 xorb %al, %al
353 outb %al, %dx
354
355 call switch_to_prot
356
357 .code32
358
359 pop %edx
360 pop %eax
361 ret
362
363 /*
364 * int get_heads(int drive);
365 */
366 EXTERN(_get_heads)
367 .code32
368
369 push %ebx
370 push %ecx
371 push %edx
372 push %edi
373 push %es
374
375 /* Get drive */
376 movl 0x18(%esp),%eax
377 movl %eax,_biosdisk_drive
378
379 call switch_to_real
380
381 .code16
382
383 movb $0x08,%ah
384 movb _biosdisk_drive,%dl
385 int $0x13
386 jc _get_heads_error
387
388 movzbl %dh,%edx
389 incl %edx
390 movl %edx,_biosdisk_retval
391 jmp _get_heads_done
392
393 _get_heads_error:
394 movl $0xff,_biosdisk_retval
395
396 _get_heads_done:
397
398 call switch_to_prot
399
400 .code32
401
402 movl _biosdisk_retval,%eax // Get return value
403
404 pop %es
405 pop %edi
406 pop %edx
407 pop %ecx
408 pop %ebx
409
410 ret
411
412 /*
413 * int get_cylinders(int drive);
414 */
415 EXTERN(_get_cylinders)
416 .code32
417
418 push %ebx
419 push %ecx
420 push %edx
421 push %edi
422 push %es
423
424 /* Get drive */
425 movl 0x18(%esp),%eax
426 movl %eax,_biosdisk_drive
427
428 call switch_to_real
429
430 .code16
431
432 movb $0x08,%ah
433 movb _biosdisk_drive,%dl
434 int $0x13
435 jc _get_cylinders_error
436
437 xorl %edx,%edx
438 andb $0xc0,%cl
439 shrb $0x06,%cl
440 movb %cl,%dh
441 movb %ch,%dl
442 incl %edx
443 movl %edx,_biosdisk_retval
444 jmp _get_cylinders_done
445
446 _get_cylinders_error:
447 movl $0xff,_biosdisk_retval
448
449 _get_cylinders_done:
450
451 call switch_to_prot
452
453 .code32
454
455 movl _biosdisk_retval,%eax // Get return value
456
457 pop %es
458 pop %edi
459 pop %edx
460 pop %ecx
461 pop %ebx
462
463 ret
464
465 /*
466 * int get_sectors(int drive);
467 */
468 EXTERN(_get_sectors)
469 .code32
470
471 push %ebx
472 push %ecx
473 push %edx
474 push %edi
475 push %es
476
477 /* Get drive */
478 movl 0x18(%esp),%eax
479 movl %eax,_biosdisk_drive
480
481 call switch_to_real
482
483 .code16
484
485 movb $0x08,%ah
486 movb _biosdisk_drive,%dl
487 int $0x13
488 jc _get_sectors_error
489
490 andb $0x3f,%cl
491 movzbl %cl,%ecx
492 movl %ecx,_biosdisk_retval
493 jmp _get_sectors_done
494
495 _get_sectors_error:
496 movl $0xff,_biosdisk_retval
497
498 _get_sectors_done:
499
500 call switch_to_prot
501
502 .code32
503
504 movl _biosdisk_retval,%eax // Get return value
505
506 pop %es
507 pop %edi
508 pop %edx
509 pop %ecx
510 pop %ebx
511
512 ret
513
514
515 /*
516 * BOOL BiosInt13GetDriveParameters(ULONG Drive, PGEOMETRY Geometry);
517 */
518 _bios_int13_cylinders:
519 .long 0
520 _bios_int13_heads:
521 .long 0
522 _bios_int13_sectors:
523 .long 0
524 _bios_int13_bytes_per_sector:
525 .long 0
526 _bios_int13_drive_parameters_struct_address:
527 .long 0
528 EXTERN(_BiosInt13GetDriveParameters)
529 .code32
530
531 push %ebx
532 push %ecx
533 push %edx
534 push %edi
535 push %esi
536 push %es
537
538 /* Get drive */
539 movl 0x1c(%esp),%eax
540 movl %eax,_biosdisk_drive
541 movl 0x20(%esp),%eax
542 movl %eax,_bios_int13_drive_parameters_struct_address
543
544 call switch_to_real
545
546 .code16
547
548 movb $0x08,%ah
549 movb _biosdisk_drive,%dl
550 int $0x13
551 jc _BiosInt13GetDriveParameters_Error
552
553 // Get the heads
554 movzbl %dh,%eax
555 incl %eax
556 movl %eax,_bios_int13_heads
557
558 // Get the sectors
559 movw %cx,%dx
560 andb $0x3f,%dl
561 movzbl %dl,%edx
562 movl %edx,_bios_int13_sectors
563
564 // Get the cylinders
565 xorl %edx,%edx
566 andb $0xc0,%cl
567 shrb $0x06,%cl
568 movb %cl,%dh
569 movb %ch,%dl
570 incl %edx
571 movl %edx,_bios_int13_cylinders
572
573 // Get the bytes per sector
574 movl $512,_bios_int13_bytes_per_sector // Just assume 512 bytes per sector
575 movl $0x01,_biosdisk_retval
576 jmp _BiosInt13GetDriveParameters_Done
577
578 _BiosInt13GetDriveParameters_Error:
579 movl $0x00,_biosdisk_retval
580
581 _BiosInt13GetDriveParameters_Done:
582
583 call switch_to_prot
584
585 .code32
586
587 // Copy drive parameters to structure
588 movl $_bios_int13_cylinders,%esi
589 movl _bios_int13_drive_parameters_struct_address,%edi
590 movl $0x04,%ecx
591 cld
592 rep movsl
593
594 movl _biosdisk_retval,%eax // Get return value
595
596 pop %es
597 pop %esi
598 pop %edi
599 pop %edx
600 pop %ecx
601 pop %ebx
602
603 ret
604
605