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