Minor cleanups.
[seabios.git] / src / disk.c
1 // 16bit code to access hard drives.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "disk.h" // floppy_13
9 #include "biosvar.h" // struct bregs
10 #include "config.h" // CONFIG_*
11 #include "cmos.h" // inb_cmos
12 #include "util.h" // debug_enter
13 #include "ata.h" // ATA_*
14
15 #define DEBUGF1(fmt, args...) bprintf(0, fmt , ##args)
16 #define DEBUGF(fmt, args...)
17
18
19 /****************************************************************
20  * Helper functions
21  ****************************************************************/
22
23 #define DISK_STUB(regs) do {                    \
24         struct bregs *__regs = (regs);          \
25         debug_stub(__regs);                     \
26         disk_ret(__regs, DISK_RET_SUCCESS);     \
27     } while (0)
28
29 static void
30 basic_access(struct bregs *regs, u8 device, u16 command)
31 {
32     u8 type = GET_EBDA(ata.devices[device].type);
33     u16 nlc, nlh, nlspt;
34     if (type == ATA_TYPE_ATA) {
35         nlc   = GET_EBDA(ata.devices[device].lchs.cylinders);
36         nlh   = GET_EBDA(ata.devices[device].lchs.heads);
37         nlspt = GET_EBDA(ata.devices[device].lchs.spt);
38     } else {
39         // Must be cd emulation.
40         nlc   = GET_EBDA(cdemu.vdevice.cylinders);
41         nlh   = GET_EBDA(cdemu.vdevice.heads);
42         nlspt = GET_EBDA(cdemu.vdevice.spt);
43     }
44
45     u16 count       = regs->al;
46     u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
47     u16 sector      = regs->cl & 0x3f;
48     u16 head        = regs->dh;
49
50     if (count > 128 || count == 0 || sector == 0) {
51         BX_INFO("int13_harddisk: function %02x, parameter out of range!\n"
52                 , regs->ah);
53         disk_ret(regs, DISK_RET_EPARAM);
54         return;
55     }
56
57     // sanity check on cyl heads, sec
58     if (cylinder >= nlc || head >= nlh || sector > nlspt) {
59         BX_INFO("int13_harddisk: function %02x, parameters out of"
60                 " range %04x/%04x/%04x!\n"
61                 , regs->ah, cylinder, head, sector);
62         disk_ret(regs, DISK_RET_EPARAM);
63         return;
64     }
65
66     if (!command) {
67         // If verify or seek
68         disk_ret(regs, DISK_RET_SUCCESS);
69         return;
70     }
71
72     // translate lchs to lba
73     u32 lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
74                + (u32)sector - 1);
75
76     u16 segment = regs->es;
77     u16 offset  = regs->bx;
78     void *far_buffer = MAKE_FARPTR(segment, offset);
79
80     irq_enable();
81
82     int status;
83     if (type == ATA_TYPE_ATA)
84         status = ata_cmd_data(device, command, lba, count, far_buffer);
85     else
86         status = cdrom_read_emu(device, lba, count, far_buffer);
87
88     irq_disable();
89
90     // Set nb of sector transferred
91     regs->al = GET_EBDA(ata.trsfsectors);
92
93     if (status != 0) {
94         BX_INFO("int13_harddisk: function %02x, error %02x !\n"
95                 , regs->ah, status);
96         disk_ret(regs, DISK_RET_EBADTRACK);
97     }
98     disk_ret(regs, DISK_RET_SUCCESS);
99 }
100
101 static void
102 extended_access(struct bregs *regs, u8 device, u16 command)
103 {
104     u16 count = GET_INT13EXT(regs, count);
105
106     // Can't use 64 bits lba
107     u32 lba = GET_INT13EXT(regs, lba2);
108     if (lba != 0L) {
109         BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n"
110                  , regs->ah);
111         disk_ret(regs, DISK_RET_EPARAM);
112         return;
113     }
114
115     u8 type = GET_EBDA(ata.devices[device].type);
116
117     // Get 32 bits lba and check
118     lba = GET_INT13EXT(regs, lba1);
119     if (type == ATA_TYPE_ATA
120         && lba >= GET_EBDA(ata.devices[device].sectors)) {
121         BX_INFO("int13_harddisk: function %02x. LBA out of range\n", regs->ah);
122         disk_ret(regs, DISK_RET_EPARAM);
123         return;
124     }
125
126     if (!command) {
127         // If verify or seek
128         disk_ret(regs, DISK_RET_SUCCESS);
129         return;
130     }
131
132     u16 segment = GET_INT13EXT(regs, segment);
133     u16 offset = GET_INT13EXT(regs, offset);
134     void *far_buffer = MAKE_FARPTR(segment, offset);
135
136     irq_enable();
137
138     u8 status;
139     if (type == ATA_TYPE_ATA)
140         status = ata_cmd_data(device, command, lba, count, far_buffer);
141     else
142         status = cdrom_read(device, lba, count, far_buffer);
143
144     irq_disable();
145
146     SET_INT13EXT(regs, count, GET_EBDA(ata.trsfsectors));
147
148     if (status != 0) {
149         BX_INFO("int13_harddisk: function %02x, error %02x !\n"
150                 , regs->ah, status);
151         disk_ret(regs, DISK_RET_EBADTRACK);
152         return;
153     }
154     disk_ret(regs, DISK_RET_SUCCESS);
155 }
156
157
158 /****************************************************************
159  * Hard Drive functions
160  ****************************************************************/
161
162 // disk controller reset
163 static void
164 disk_1300(struct bregs *regs, u8 device)
165 {
166     ata_reset(device);
167 }
168
169 // read disk status
170 static void
171 disk_1301(struct bregs *regs, u8 device)
172 {
173     u8 v = GET_BDA(disk_last_status);
174     regs->ah = v;
175     set_cf(regs, v);
176     // XXX - clear disk_last_status?
177 }
178
179 // read disk sectors
180 static void
181 disk_1302(struct bregs *regs, u8 device)
182 {
183     basic_access(regs, device, ATA_CMD_READ_SECTORS);
184 }
185
186 // write disk sectors
187 static void
188 disk_1303(struct bregs *regs, u8 device)
189 {
190     basic_access(regs, device, ATA_CMD_WRITE_SECTORS);
191 }
192
193 // verify disk sectors
194 static void
195 disk_1304(struct bregs *regs, u8 device)
196 {
197     basic_access(regs, device, 0);
198     // FIXME verify
199 }
200
201 // format disk track
202 static void
203 disk_1305(struct bregs *regs, u8 device)
204 {
205     DISK_STUB(regs);
206 }
207
208 // read disk drive parameters
209 static void
210 disk_1308(struct bregs *regs, u8 device)
211 {
212     // Get logical geometry from table
213     u16 nlc = GET_EBDA(ata.devices[device].lchs.cylinders);
214     u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
215     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
216     u16 count = GET_EBDA(ata.hdcount);
217
218     nlc = nlc - 2; /* 0 based , last sector not used */
219     regs->al = 0;
220     regs->ch = nlc & 0xff;
221     regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
222     regs->dh = nlh - 1;
223     regs->dl = count; /* FIXME returns 0, 1, or n hard drives */
224
225     // FIXME should set ES & DI
226     disk_ret(regs, DISK_RET_SUCCESS);
227 }
228
229 // initialize drive parameters
230 static void
231 disk_1309(struct bregs *regs, u8 device)
232 {
233     DISK_STUB(regs);
234 }
235
236 // seek to specified cylinder
237 static void
238 disk_130c(struct bregs *regs, u8 device)
239 {
240     DISK_STUB(regs);
241 }
242
243 // alternate disk reset
244 static void
245 disk_130d(struct bregs *regs, u8 device)
246 {
247     DISK_STUB(regs);
248 }
249
250 // check drive ready
251 static void
252 disk_1310(struct bregs *regs, u8 device)
253 {
254     // should look at 40:8E also???
255
256     // Read the status from controller
257     u8 status = inb(GET_EBDA(ata.channels[device/2].iobase1) + ATA_CB_STAT);
258     if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY )
259         disk_ret(regs, DISK_RET_SUCCESS);
260     else
261         disk_ret(regs, DISK_RET_ENOTREADY);
262 }
263
264 // recalibrate
265 static void
266 disk_1311(struct bregs *regs, u8 device)
267 {
268     DISK_STUB(regs);
269 }
270
271 // controller internal diagnostic
272 static void
273 disk_1314(struct bregs *regs, u8 device)
274 {
275     DISK_STUB(regs);
276 }
277
278 // read disk drive size
279 static void
280 disk_1315(struct bregs *regs, u8 device)
281 {
282     // Get logical geometry from table
283     u16 nlc   = GET_EBDA(ata.devices[device].lchs.cylinders);
284     u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
285     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
286
287     // Compute sector count seen by int13
288     u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
289     regs->cx = lba >> 16;
290     regs->dx = lba & 0xffff;
291
292     disk_ret(regs, DISK_RET_SUCCESS);
293     regs->ah = 3; // hard disk accessible
294 }
295
296 // IBM/MS installation check
297 static void
298 disk_1341(struct bregs *regs, u8 device)
299 {
300     regs->bx = 0xaa55;  // install check
301     regs->cx = 0x0007;  // ext disk access and edd, removable supported
302     disk_ret(regs, DISK_RET_SUCCESS);
303     regs->ah = 0x30;    // EDD 3.0
304 }
305
306 // IBM/MS extended read
307 static void
308 disk_1342(struct bregs *regs, u8 device)
309 {
310     extended_access(regs, device, ATA_CMD_READ_SECTORS);
311 }
312
313 // IBM/MS extended write
314 static void
315 disk_1343(struct bregs *regs, u8 device)
316 {
317     extended_access(regs, device, ATA_CMD_WRITE_SECTORS);
318 }
319
320 // IBM/MS verify
321 static void
322 disk_1344(struct bregs *regs, u8 device)
323 {
324     extended_access(regs, device, 0);
325 }
326
327 // IBM/MS lock/unlock drive
328 static void
329 disk_1345(struct bregs *regs, u8 device)
330 {
331     // Always success for HD
332     disk_ret(regs, DISK_RET_SUCCESS);
333 }
334
335 // IBM/MS eject media
336 static void
337 disk_1346(struct bregs *regs, u8 device)
338 {
339     // Volume Not Removable
340     disk_ret(regs, DISK_RET_ENOTREMOVABLE);
341 }
342
343 // IBM/MS extended seek
344 static void
345 disk_1347(struct bregs *regs, u8 device)
346 {
347     extended_access(regs, device, 0);
348 }
349
350 // IBM/MS get drive parameters
351 static void
352 disk_1348(struct bregs *regs, u8 device)
353 {
354     u16 size = GET_INT13DPT(regs, size);
355
356     // Buffer is too small
357     if (size < 0x1a) {
358         disk_ret(regs, DISK_RET_EPARAM);
359         return;
360     }
361
362     // EDD 1.x
363
364     u8  type    = GET_EBDA(ata.devices[device].type);
365     u16 npc     = GET_EBDA(ata.devices[device].pchs.cylinders);
366     u16 nph     = GET_EBDA(ata.devices[device].pchs.heads);
367     u16 npspt   = GET_EBDA(ata.devices[device].pchs.spt);
368     u32 lba     = GET_EBDA(ata.devices[device].sectors);
369     u16 blksize = GET_EBDA(ata.devices[device].blksize);
370
371     SET_INT13DPT(regs, size, 0x1a);
372     if (type == ATA_TYPE_ATA) {
373         if ((lba/npspt)/nph > 0x3fff) {
374             SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
375             SET_INT13DPT(regs, cylinders, 0x3fff);
376         } else {
377             SET_INT13DPT(regs, infos, 0x02); // geometry is valid
378             SET_INT13DPT(regs, cylinders, (u32)npc);
379         }
380         SET_INT13DPT(regs, heads, (u32)nph);
381         SET_INT13DPT(regs, spt, (u32)npspt);
382         SET_INT13DPT(regs, sector_count1, lba);  // FIXME should be Bit64
383         SET_INT13DPT(regs, sector_count2, 0L);
384     } else {
385         // ATAPI
386         // 0x74 = removable, media change, lockable, max values
387         SET_INT13DPT(regs, infos, 0x74);
388         SET_INT13DPT(regs, cylinders, 0xffffffff);
389         SET_INT13DPT(regs, heads, 0xffffffff);
390         SET_INT13DPT(regs, spt, 0xffffffff);
391         SET_INT13DPT(regs, sector_count1, 0xffffffff);  // FIXME should be Bit64
392         SET_INT13DPT(regs, sector_count2, 0xffffffff);
393     }
394     SET_INT13DPT(regs, blksize, blksize);
395
396     if (size < 0x1e) {
397         disk_ret(regs, DISK_RET_SUCCESS);
398         return;
399     }
400
401     // EDD 2.x
402
403     SET_INT13DPT(regs, size, 0x1e);
404
405     SET_INT13DPT(regs, dpte_segment, SEG_EBDA);
406     SET_INT13DPT(regs, dpte_offset
407                  , offsetof(struct extended_bios_data_area_s, ata.dpte));
408
409     // Fill in dpte
410     u8 channel = device / 2;
411     u16 iobase1 = GET_EBDA(ata.channels[channel].iobase1);
412     u16 iobase2 = GET_EBDA(ata.channels[channel].iobase2);
413     u8 irq = GET_EBDA(ata.channels[channel].irq);
414     u8 mode = GET_EBDA(ata.devices[device].mode);
415
416     u16 options;
417     if (type == ATA_TYPE_ATA) {
418         u8 translation = GET_EBDA(ata.devices[device].translation);
419         options  = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
420         options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
421         options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
422     } else {
423         // ATAPI
424         options  = (1<<5); // removable device
425         options |= (1<<6); // atapi device
426     }
427     options |= (1<<4); // lba translation
428     options |= (mode==ATA_MODE_PIO32?1:0)<<7;
429
430     SET_EBDA(ata.dpte.iobase1, iobase1);
431     SET_EBDA(ata.dpte.iobase2, iobase2 + ATA_CB_DC);
432     SET_EBDA(ata.dpte.prefix, (0xe | (device % 2))<<4 );
433     SET_EBDA(ata.dpte.unused, 0xcb );
434     SET_EBDA(ata.dpte.irq, irq );
435     SET_EBDA(ata.dpte.blkcount, 1 );
436     SET_EBDA(ata.dpte.dma, 0 );
437     SET_EBDA(ata.dpte.pio, 0 );
438     SET_EBDA(ata.dpte.options, options);
439     SET_EBDA(ata.dpte.reserved, 0);
440     if (size >= 0x42)
441         SET_EBDA(ata.dpte.revision, 0x11);
442     else
443         SET_EBDA(ata.dpte.revision, 0x10);
444
445     u8 *p = MAKE_FARPTR(SEG_EBDA
446                         , offsetof(struct extended_bios_data_area_s, ata.dpte));
447     u8 sum = checksum(p, 15);
448     SET_EBDA(ata.dpte.checksum, ~sum);
449
450     if (size < 0x42) {
451         disk_ret(regs, DISK_RET_SUCCESS);
452         return;
453     }
454
455     // EDD 3.x
456     channel = device / 2;
457     u8 iface = GET_EBDA(ata.channels[channel].iface);
458     iobase1 = GET_EBDA(ata.channels[channel].iobase1);
459
460     SET_INT13DPT(regs, size, 0x42);
461     SET_INT13DPT(regs, key, 0xbedd);
462     SET_INT13DPT(regs, dpi_length, 0x24);
463     SET_INT13DPT(regs, reserved1, 0);
464     SET_INT13DPT(regs, reserved2, 0);
465
466     if (iface==ATA_IFACE_ISA) {
467         SET_INT13DPT(regs, host_bus[0], 'I');
468         SET_INT13DPT(regs, host_bus[1], 'S');
469         SET_INT13DPT(regs, host_bus[2], 'A');
470         SET_INT13DPT(regs, host_bus[3], 0);
471     } else {
472         // FIXME PCI
473     }
474     SET_INT13DPT(regs, iface_type[0], 'A');
475     SET_INT13DPT(regs, iface_type[1], 'T');
476     SET_INT13DPT(regs, iface_type[2], 'A');
477     SET_INT13DPT(regs, iface_type[3], 0);
478
479     if (iface==ATA_IFACE_ISA) {
480         SET_INT13DPT(regs, iface_path[0], iobase1);
481         SET_INT13DPT(regs, iface_path[2], 0);
482         SET_INT13DPT(regs, iface_path[4], 0L);
483     } else {
484         // FIXME PCI
485     }
486     SET_INT13DPT(regs, device_path[0], device%2);
487     SET_INT13DPT(regs, device_path[1], 0);
488     SET_INT13DPT(regs, device_path[2], 0);
489     SET_INT13DPT(regs, device_path[4], 0L);
490
491     sum = checksum(MAKE_FARPTR(regs->ds, 30), 34);
492     SET_INT13DPT(regs, checksum, ~sum);
493 }
494
495 // IBM/MS extended media change
496 static void
497 disk_1349(struct bregs *regs, u8 device)
498 {
499     // Always success for HD
500     disk_ret(regs, DISK_RET_SUCCESS);
501 }
502
503 static void
504 disk_134e01(struct bregs *regs, u8 device)
505 {
506     disk_ret(regs, DISK_RET_SUCCESS);
507 }
508
509 static void
510 disk_134e03(struct bregs *regs, u8 device)
511 {
512     disk_ret(regs, DISK_RET_SUCCESS);
513 }
514
515 static void
516 disk_134e04(struct bregs *regs, u8 device)
517 {
518     disk_ret(regs, DISK_RET_SUCCESS);
519 }
520
521 static void
522 disk_134e06(struct bregs *regs, u8 device)
523 {
524     disk_ret(regs, DISK_RET_SUCCESS);
525 }
526
527 static void
528 disk_134eXX(struct bregs *regs, u8 device)
529 {
530     disk_ret(regs, DISK_RET_EPARAM);
531 }
532
533 // IBM/MS set hardware configuration
534 static void
535 disk_134e(struct bregs *regs, u8 device)
536 {
537     switch (regs->al) {
538     case 0x01: disk_134e01(regs, device); break;
539     case 0x03: disk_134e03(regs, device); break;
540     case 0x04: disk_134e04(regs, device); break;
541     case 0x06: disk_134e06(regs, device); break;
542     default:   disk_134eXX(regs, device); break;
543     }
544 }
545
546 void
547 disk_13XX(struct bregs *regs, u8 device)
548 {
549     disk_ret(regs, DISK_RET_EPARAM);
550 }
551
552 void
553 disk_13(struct bregs *regs, u8 device)
554 {
555     //debug_stub(regs);
556
557     // clear completion flag
558     SET_BDA(disk_interrupt_flag, 0);
559
560     switch (regs->ah) {
561     case 0x00: disk_1300(regs, device); break;
562     case 0x01: disk_1301(regs, device); break;
563     case 0x02: disk_1302(regs, device); break;
564     case 0x03: disk_1303(regs, device); break;
565     case 0x04: disk_1304(regs, device); break;
566     case 0x05: disk_1305(regs, device); break;
567     case 0x08: disk_1308(regs, device); break;
568     case 0x09: disk_1309(regs, device); break;
569     case 0x0c: disk_130c(regs, device); break;
570     case 0x0d: disk_130d(regs, device); break;
571     case 0x10: disk_1310(regs, device); break;
572     case 0x11: disk_1311(regs, device); break;
573     case 0x14: disk_1314(regs, device); break;
574     case 0x15: disk_1315(regs, device); break;
575     case 0x41: disk_1341(regs, device); break;
576     case 0x42: disk_1342(regs, device); break;
577     case 0x43: disk_1343(regs, device); break;
578     case 0x44: disk_1344(regs, device); break;
579     case 0x45: disk_1345(regs, device); break;
580     case 0x46: disk_1346(regs, device); break;
581     case 0x47: disk_1347(regs, device); break;
582     case 0x48: disk_1348(regs, device); break;
583     case 0x49: disk_1349(regs, device); break;
584     case 0x4e: disk_134e(regs, device); break;
585     default:   disk_13XX(regs, device); break;
586     }
587 }
588
589
590 /****************************************************************
591  * Entry points
592  ****************************************************************/
593
594 static u8
595 get_device(struct bregs *regs, u8 iscd, u8 drive)
596 {
597     // basic check : device has to be defined
598     if (drive >= CONFIG_MAX_ATA_DEVICES) {
599         disk_ret(regs, DISK_RET_EPARAM);
600         return CONFIG_MAX_ATA_DEVICES;
601     }
602
603     // Get the ata channel
604     u8 device = GET_EBDA(ata.idmap[iscd][drive]);
605
606     // basic check : device has to be valid
607     if (device >= CONFIG_MAX_ATA_DEVICES) {
608         disk_ret(regs, DISK_RET_EPARAM);
609         return CONFIG_MAX_ATA_DEVICES;
610     }
611
612     return device;
613 }
614
615 static void
616 handle_legacy_disk(struct bregs *regs, u8 drive)
617 {
618     if (drive < 0x80) {
619         floppy_13(regs, drive);
620         return;
621     }
622
623     if (! CONFIG_ATA) {
624         // XXX - old code had other disk access method.
625         disk_ret(regs, DISK_RET_EPARAM);
626         return;
627     }
628
629     if (drive >= 0xe0) {
630         u8 device = get_device(regs, 1, drive - 0xe0);
631         if (device >= CONFIG_MAX_ATA_DEVICES)
632             return;
633         cdrom_13(regs, device);
634         return;
635     }
636
637     u8 device = get_device(regs, 0, drive - 0x80);
638     if (device >= CONFIG_MAX_ATA_DEVICES)
639         return;
640     disk_13(regs, device);
641 }
642
643 void VISIBLE16
644 handle_40(struct bregs *regs)
645 {
646     debug_enter(regs);
647     handle_legacy_disk(regs, regs->dl);
648 }
649
650 // INT 13h Fixed Disk Services Entry Point
651 void VISIBLE16
652 handle_13(struct bregs *regs)
653 {
654     //debug_enter(regs);
655     u8 drive = regs->dl;
656
657     if (CONFIG_CDROM_EMU) {
658         if (regs->ah == 0x4b) {
659             cdemu_134b(regs);
660             return;
661         }
662         if (GET_EBDA(cdemu.active)) {
663             if (drive == GET_EBDA(cdemu.emulated_drive)) {
664                 cdemu_13(regs);
665                 return;
666             }
667             if (drive < 0xe0)
668                 drive--;
669         }
670     }
671     handle_legacy_disk(regs, drive);
672 }
673
674 // record completion in BIOS task complete flag
675 void VISIBLE16
676 handle_76()
677 {
678     debug_isr();
679     SET_BDA(disk_interrupt_flag, 0xff);
680     eoi_both_pics();
681 }