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