Change send_disk_op() to return the bios status code.
[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 LGPLv3 license.
7
8 #include "disk.h" // floppy_13
9 #include "biosvar.h" // SET_BDA
10 #include "config.h" // CONFIG_*
11 #include "util.h" // debug_enter
12 #include "pic.h" // eoi_pic2
13 #include "bregs.h" // struct bregs
14 #include "pci.h" // pci_bdf_to_bus
15 #include "ata.h" // ATA_CB_DC
16
17
18 /****************************************************************
19  * Helper functions
20  ****************************************************************/
21
22 void
23 __disk_ret(struct bregs *regs, u32 linecode, const char *fname)
24 {
25     u8 code = linecode;
26     SET_BDA(disk_last_status, code);
27     if (code)
28         __set_code_fail(regs, linecode, fname);
29     else
30         set_code_success(regs);
31 }
32
33 static void
34 __disk_stub(struct bregs *regs, int lineno, const char *fname)
35 {
36     __debug_stub(regs, lineno, fname);
37     __disk_ret(regs, DISK_RET_SUCCESS | (lineno << 8), fname);
38 }
39
40 #define DISK_STUB(regs)                         \
41     __disk_stub((regs), __LINE__, __func__)
42
43 // Execute a "disk_op_s" request - this runs on a stack in the ebda.
44 static int
45 __send_disk_op(struct disk_op_s *op_far, u16 op_seg)
46 {
47     struct disk_op_s dop;
48     memcpy_far(GET_SEG(SS), &dop
49                , op_seg, op_far
50                , sizeof(dop));
51
52     dprintf(DEBUG_HDL_13, "disk_op d=%d lba=%d buf=%p count=%d cmd=%d\n"
53             , dop.driveid, (u32)dop.lba, dop.buf_fl
54             , dop.count, dop.command);
55
56     irq_enable();
57
58     int status = 0;
59     u8 type = GET_GLOBAL(Drives.drives[dop.driveid].type);
60     if (type == DTYPE_ATA)
61         status = process_ata_op(&dop);
62     else if (type == DTYPE_ATAPI)
63         status = process_atapi_op(&dop);
64
65     irq_disable();
66
67     // Update count with total sectors transferred.
68     SET_FARVAR(op_seg, op_far->count, dop.count);
69
70     return status;
71 }
72
73 // Execute a "disk_op_s" request by jumping to a stack in the ebda.
74 static int
75 send_disk_op(struct disk_op_s *op)
76 {
77     if (! CONFIG_DRIVES)
78         return -1;
79
80     return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
81 }
82
83 // Obtain the requested disk lba from an old-style chs request.
84 static int
85 legacy_lba(struct bregs *regs, u16 lchs_seg, struct chs_s *lchs_far)
86 {
87     u8 count = regs->al;
88     u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300);
89     u16 sector = regs->cl & 0x3f;
90     u16 head = regs->dh;
91
92     if (count > 128 || count == 0 || sector == 0) {
93         dprintf(1, "int13_harddisk: function %02x, parameter out of range!\n"
94                 , regs->ah);
95         disk_ret(regs, DISK_RET_EPARAM);
96         return -1;
97     }
98
99     u16 nlc = GET_FARVAR(lchs_seg, lchs_far->cylinders);
100     u16 nlh = GET_FARVAR(lchs_seg, lchs_far->heads);
101     u16 nlspt = GET_FARVAR(lchs_seg, lchs_far->spt);
102
103     // sanity check on cyl heads, sec
104     if (cylinder >= nlc || head >= nlh || sector > nlspt) {
105         dprintf(1, "int13_harddisk: function %02x, parameters out of"
106                 " range %04x/%04x/%04x!\n"
107                 , regs->ah, cylinder, head, sector);
108         disk_ret(regs, DISK_RET_EPARAM);
109         return -1;
110     }
111
112     // translate lchs to lba
113     return (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
114             + (u32)sector - 1);
115 }
116
117 // Perform read/write/verify using old-style chs accesses
118 static void
119 basic_access(struct bregs *regs, u8 driveid, u16 command)
120 {
121     struct disk_op_s dop;
122     dop.driveid = driveid;
123     dop.command = command;
124     int lba = legacy_lba(regs, get_global_seg(), &Drives.drives[driveid].lchs);
125     if (lba < 0)
126         return;
127     dop.lba = lba;
128     dop.count = regs->al;
129     dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
130
131     int status = send_disk_op(&dop);
132
133     regs->al = dop.count;
134
135     disk_ret(regs, status);
136 }
137
138 // Perform cdemu read/verify
139 void
140 cdemu_access(struct bregs *regs, u8 driveid, u16 command)
141 {
142     struct disk_op_s dop;
143     dop.driveid = driveid;
144     dop.command = command;
145     u16 ebda_seg = get_ebda_seg();
146     int vlba = legacy_lba(
147         regs, ebda_seg
148         , (void*)offsetof(struct extended_bios_data_area_s, cdemu.lchs));
149     if (vlba < 0)
150         return;
151     dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + vlba / 4;
152     u8 count = regs->al;
153     u8 *cdbuf_far = (void*)offsetof(struct extended_bios_data_area_s, cdemu_buf);
154     u8 *dest_far = (void*)(regs->bx+0);
155     regs->al = 0;
156     int status = DISK_RET_SUCCESS;
157
158     if (vlba & 3) {
159         dop.count = 1;
160         dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
161         status = send_disk_op(&dop);
162         if (status)
163             goto fail;
164         u8 thiscount = 4 - (vlba & 3);
165         if (thiscount > count)
166             thiscount = count;
167         count -= thiscount;
168         memcpy_far(regs->es, dest_far
169                    , ebda_seg, cdbuf_far + (vlba & 3) * 512
170                    , thiscount * 512);
171         dest_far += thiscount * 512;
172         regs->al += thiscount;
173         dop.lba++;
174     }
175
176     if (count > 3) {
177         dop.count = count / 4;
178         dop.buf_fl = MAKE_FLATPTR(regs->es, dest_far);
179         status = send_disk_op(&dop);
180         regs->al += dop.count * 4;
181         if (status)
182             goto fail;
183         u8 thiscount = count & ~3;
184         count &= 3;
185         dest_far += thiscount * 512;
186         dop.lba += thiscount / 4;
187     }
188
189     if (count) {
190         dop.count = 1;
191         dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
192         status = send_disk_op(&dop);
193         if (status)
194             goto fail;
195         u8 thiscount = count;
196         memcpy_far(regs->es, dest_far, ebda_seg, cdbuf_far, thiscount * 512);
197         regs->al += thiscount;
198     }
199
200 fail:
201     disk_ret(regs, status);
202 }
203
204 // Perform read/write/verify using new-style "int13ext" accesses.
205 static void
206 extended_access(struct bregs *regs, u8 driveid, u16 command)
207 {
208     struct disk_op_s dop;
209     // Get lba and check.
210     dop.lba = GET_INT13EXT(regs, lba);
211     dop.command = command;
212     dop.driveid = driveid;
213     if (dop.lba >= GET_GLOBAL(Drives.drives[driveid].sectors)) {
214         dprintf(1, "int13_harddisk: function %02x. LBA out of range\n"
215                 , regs->ah);
216         disk_ret(regs, DISK_RET_EPARAM);
217         return;
218     }
219
220     u16 segment = GET_INT13EXT(regs, segment);
221     u16 offset = GET_INT13EXT(regs, offset);
222     dop.buf_fl = MAKE_FLATPTR(segment, offset);
223     dop.count = GET_INT13EXT(regs, count);
224
225     int status = send_disk_op(&dop);
226
227     SET_INT13EXT(regs, count, dop.count);
228
229     disk_ret(regs, status);
230 }
231
232
233 /****************************************************************
234  * Hard Drive functions
235  ****************************************************************/
236
237 // disk controller reset
238 static void
239 disk_1300(struct bregs *regs, u8 driveid)
240 {
241     struct disk_op_s dop;
242     dop.driveid = driveid;
243     dop.command = CMD_RESET;
244     int status = send_disk_op(&dop);
245     disk_ret(regs, status);
246 }
247
248 // read disk status
249 static void
250 disk_1301(struct bregs *regs, u8 driveid)
251 {
252     u8 v = GET_BDA(disk_last_status);
253     regs->ah = v;
254     set_cf(regs, v);
255     // XXX - clear disk_last_status?
256 }
257
258 // read disk sectors
259 static void
260 disk_1302(struct bregs *regs, u8 driveid)
261 {
262     basic_access(regs, driveid, CMD_READ);
263 }
264
265 // write disk sectors
266 static void
267 disk_1303(struct bregs *regs, u8 driveid)
268 {
269     basic_access(regs, driveid, CMD_WRITE);
270 }
271
272 // verify disk sectors
273 static void
274 disk_1304(struct bregs *regs, u8 driveid)
275 {
276     basic_access(regs, driveid, CMD_VERIFY);
277     // FIXME verify
278 }
279
280 // format disk track
281 static void
282 disk_1305(struct bregs *regs, u8 driveid)
283 {
284     DISK_STUB(regs);
285 }
286
287 // read disk drive parameters
288 static void
289 disk_1308(struct bregs *regs, u8 driveid)
290 {
291     // Get logical geometry from table
292     u16 nlc = GET_GLOBAL(Drives.drives[driveid].lchs.cylinders);
293     u16 nlh = GET_GLOBAL(Drives.drives[driveid].lchs.heads);
294     u16 nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
295     u16 count = GET_BDA(hdcount);
296
297     nlc = nlc - 2; /* 0 based , last sector not used */
298     regs->al = 0;
299     regs->ch = nlc & 0xff;
300     regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
301     regs->dh = nlh - 1;
302     regs->dl = count; /* FIXME returns 0, 1, or n hard drives */
303
304     // FIXME should set ES & DI
305     disk_ret(regs, DISK_RET_SUCCESS);
306 }
307
308 // initialize drive parameters
309 static void
310 disk_1309(struct bregs *regs, u8 driveid)
311 {
312     DISK_STUB(regs);
313 }
314
315 // seek to specified cylinder
316 static void
317 disk_130c(struct bregs *regs, u8 driveid)
318 {
319     DISK_STUB(regs);
320 }
321
322 // alternate disk reset
323 static void
324 disk_130d(struct bregs *regs, u8 driveid)
325 {
326     DISK_STUB(regs);
327 }
328
329 // check drive ready
330 static void
331 disk_1310(struct bregs *regs, u8 driveid)
332 {
333     // should look at 40:8E also???
334
335     struct disk_op_s dop;
336     dop.driveid = driveid;
337     dop.command = CMD_ISREADY;
338     int status = send_disk_op(&dop);
339     disk_ret(regs, status);
340 }
341
342 // recalibrate
343 static void
344 disk_1311(struct bregs *regs, u8 driveid)
345 {
346     DISK_STUB(regs);
347 }
348
349 // controller internal diagnostic
350 static void
351 disk_1314(struct bregs *regs, u8 driveid)
352 {
353     DISK_STUB(regs);
354 }
355
356 // read disk drive size
357 static void
358 disk_1315(struct bregs *regs, u8 driveid)
359 {
360     // Get logical geometry from table
361     u16 nlc   = GET_GLOBAL(Drives.drives[driveid].lchs.cylinders);
362     u16 nlh   = GET_GLOBAL(Drives.drives[driveid].lchs.heads);
363     u16 nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
364
365     // Compute sector count seen by int13
366     u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
367     regs->cx = lba >> 16;
368     regs->dx = lba & 0xffff;
369
370     disk_ret(regs, DISK_RET_SUCCESS);
371     regs->ah = 3; // hard disk accessible
372 }
373
374 // IBM/MS installation check
375 static void
376 disk_1341(struct bregs *regs, u8 driveid)
377 {
378     regs->bx = 0xaa55;  // install check
379     regs->cx = 0x0007;  // ext disk access and edd, removable supported
380     disk_ret(regs, DISK_RET_SUCCESS);
381     regs->ah = 0x30;    // EDD 3.0
382 }
383
384 // IBM/MS extended read
385 static void
386 disk_1342(struct bregs *regs, u8 driveid)
387 {
388     extended_access(regs, driveid, CMD_READ);
389 }
390
391 // IBM/MS extended write
392 static void
393 disk_1343(struct bregs *regs, u8 driveid)
394 {
395     extended_access(regs, driveid, CMD_WRITE);
396 }
397
398 // IBM/MS verify
399 static void
400 disk_1344(struct bregs *regs, u8 driveid)
401 {
402     extended_access(regs, driveid, CMD_VERIFY);
403 }
404
405 // IBM/MS lock/unlock drive
406 static void
407 disk_1345(struct bregs *regs, u8 driveid)
408 {
409     // Always success for HD
410     disk_ret(regs, DISK_RET_SUCCESS);
411 }
412
413 // IBM/MS eject media
414 static void
415 disk_1346(struct bregs *regs, u8 driveid)
416 {
417     // Volume Not Removable
418     disk_ret(regs, DISK_RET_ENOTREMOVABLE);
419 }
420
421 // IBM/MS extended seek
422 static void
423 disk_1347(struct bregs *regs, u8 driveid)
424 {
425     extended_access(regs, driveid, CMD_SEEK);
426 }
427
428 // IBM/MS get drive parameters
429 static void
430 disk_1348(struct bregs *regs, u8 driveid)
431 {
432     u16 size = GET_INT13DPT(regs, size);
433
434     // Buffer is too small
435     if (size < 26) {
436         disk_ret(regs, DISK_RET_EPARAM);
437         return;
438     }
439
440     // EDD 1.x
441
442     u8  type    = GET_GLOBAL(Drives.drives[driveid].type);
443     u16 npc     = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
444     u16 nph     = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
445     u16 npspt   = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
446     u64 lba     = GET_GLOBAL(Drives.drives[driveid].sectors);
447     u16 blksize = GET_GLOBAL(Drives.drives[driveid].blksize);
448
449     dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n"
450             , size, type, npc, nph, npspt, (u32)lba, blksize);
451
452     SET_INT13DPT(regs, size, 26);
453     if (type == DTYPE_ATAPI) {
454         // 0x74 = removable, media change, lockable, max values
455         SET_INT13DPT(regs, infos, 0x74);
456         SET_INT13DPT(regs, cylinders, 0xffffffff);
457         SET_INT13DPT(regs, heads, 0xffffffff);
458         SET_INT13DPT(regs, spt, 0xffffffff);
459         SET_INT13DPT(regs, sector_count, (u64)-1);
460     } else {
461         if (lba > (u64)npspt*nph*0x3fff) {
462             SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
463             SET_INT13DPT(regs, cylinders, 0x3fff);
464         } else {
465             SET_INT13DPT(regs, infos, 0x02); // geometry is valid
466             SET_INT13DPT(regs, cylinders, (u32)npc);
467         }
468         SET_INT13DPT(regs, heads, (u32)nph);
469         SET_INT13DPT(regs, spt, (u32)npspt);
470         SET_INT13DPT(regs, sector_count, lba);
471     }
472     SET_INT13DPT(regs, blksize, blksize);
473
474     if (size < 30 || (type != DTYPE_ATA && type != DTYPE_ATAPI)) {
475         disk_ret(regs, DISK_RET_SUCCESS);
476         return;
477     }
478
479     // EDD 2.x
480
481     u16 ebda_seg = get_ebda_seg();
482     SET_INT13DPT(regs, size, 30);
483
484     SET_INT13DPT(regs, dpte_segment, ebda_seg);
485     SET_INT13DPT(regs, dpte_offset
486                  , offsetof(struct extended_bios_data_area_s, dpte));
487
488     // Fill in dpte
489     u8 ataid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
490     u8 channel = ataid / 2;
491     u8 slave = ataid % 2;
492     u16 iobase1 = GET_GLOBAL(ATA_channels[channel].iobase1);
493     u16 iobase2 = GET_GLOBAL(ATA_channels[channel].iobase2);
494     u8 irq = GET_GLOBAL(ATA_channels[channel].irq);
495
496     u16 options = 0;
497     if (type == DTYPE_ATA) {
498         u8 translation = GET_GLOBAL(Drives.drives[driveid].translation);
499         if (translation != TRANSLATION_NONE) {
500             options |= 1<<3; // CHS translation
501             if (translation == TRANSLATION_LBA)
502                 options |= 1<<9;
503             if (translation == TRANSLATION_RECHS)
504                 options |= 3<<9;
505         }
506     } else {
507         // ATAPI
508         options |= 1<<5; // removable device
509         options |= 1<<6; // atapi device
510     }
511     options |= 1<<4; // lba translation
512     if (CONFIG_ATA_PIO32)
513         options |= 1<<7;
514
515     SET_EBDA2(ebda_seg, dpte.iobase1, iobase1);
516     SET_EBDA2(ebda_seg, dpte.iobase2, iobase2 + ATA_CB_DC);
517     SET_EBDA2(ebda_seg, dpte.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)
518                                       | ATA_CB_DH_LBA));
519     SET_EBDA2(ebda_seg, dpte.unused, 0xcb);
520     SET_EBDA2(ebda_seg, dpte.irq, irq);
521     SET_EBDA2(ebda_seg, dpte.blkcount, 1);
522     SET_EBDA2(ebda_seg, dpte.dma, 0);
523     SET_EBDA2(ebda_seg, dpte.pio, 0);
524     SET_EBDA2(ebda_seg, dpte.options, options);
525     SET_EBDA2(ebda_seg, dpte.reserved, 0);
526     SET_EBDA2(ebda_seg, dpte.revision, 0x11);
527
528     u8 sum = checksum_far(
529         ebda_seg, (void*)offsetof(struct extended_bios_data_area_s, dpte), 15);
530     SET_EBDA2(ebda_seg, dpte.checksum, -sum);
531
532     if (size < 66) {
533         disk_ret(regs, DISK_RET_SUCCESS);
534         return;
535     }
536
537     // EDD 3.x
538     SET_INT13DPT(regs, key, 0xbedd);
539     SET_INT13DPT(regs, dpi_length, 36);
540     SET_INT13DPT(regs, reserved1, 0);
541     SET_INT13DPT(regs, reserved2, 0);
542
543     SET_INT13DPT(regs, host_bus[0], 'P');
544     SET_INT13DPT(regs, host_bus[1], 'C');
545     SET_INT13DPT(regs, host_bus[2], 'I');
546     SET_INT13DPT(regs, host_bus[3], 0);
547
548     u32 bdf = GET_GLOBAL(ATA_channels[channel].pci_bdf);
549     u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8)
550                 | (pci_bdf_to_fn(bdf) << 16));
551     SET_INT13DPT(regs, iface_path, path);
552
553     SET_INT13DPT(regs, iface_type[0], 'A');
554     SET_INT13DPT(regs, iface_type[1], 'T');
555     SET_INT13DPT(regs, iface_type[2], 'A');
556     SET_INT13DPT(regs, iface_type[3], 0);
557     SET_INT13DPT(regs, iface_type[4], 0);
558     SET_INT13DPT(regs, iface_type[5], 0);
559     SET_INT13DPT(regs, iface_type[6], 0);
560     SET_INT13DPT(regs, iface_type[7], 0);
561
562     SET_INT13DPT(regs, device_path, slave);
563
564     SET_INT13DPT(regs, checksum
565                  , -checksum_far(regs->ds, (void*)(regs->si+30), 35));
566
567     disk_ret(regs, DISK_RET_SUCCESS);
568 }
569
570 // IBM/MS extended media change
571 static void
572 disk_1349(struct bregs *regs, u8 driveid)
573 {
574     // Always success for HD
575     disk_ret(regs, DISK_RET_SUCCESS);
576 }
577
578 static void
579 disk_134e01(struct bregs *regs, u8 driveid)
580 {
581     disk_ret(regs, DISK_RET_SUCCESS);
582 }
583
584 static void
585 disk_134e03(struct bregs *regs, u8 driveid)
586 {
587     disk_ret(regs, DISK_RET_SUCCESS);
588 }
589
590 static void
591 disk_134e04(struct bregs *regs, u8 driveid)
592 {
593     disk_ret(regs, DISK_RET_SUCCESS);
594 }
595
596 static void
597 disk_134e06(struct bregs *regs, u8 driveid)
598 {
599     disk_ret(regs, DISK_RET_SUCCESS);
600 }
601
602 static void
603 disk_134eXX(struct bregs *regs, u8 driveid)
604 {
605     disk_ret(regs, DISK_RET_EPARAM);
606 }
607
608 // IBM/MS set hardware configuration
609 static void
610 disk_134e(struct bregs *regs, u8 driveid)
611 {
612     switch (regs->al) {
613     case 0x01: disk_134e01(regs, driveid); break;
614     case 0x03: disk_134e03(regs, driveid); break;
615     case 0x04: disk_134e04(regs, driveid); break;
616     case 0x06: disk_134e06(regs, driveid); break;
617     default:   disk_134eXX(regs, driveid); break;
618     }
619 }
620
621 void
622 disk_13XX(struct bregs *regs, u8 driveid)
623 {
624     disk_ret(regs, DISK_RET_EPARAM);
625 }
626
627 void
628 disk_13(struct bregs *regs, u8 driveid)
629 {
630     //debug_stub(regs);
631
632     // clear completion flag
633     SET_BDA(disk_interrupt_flag, 0);
634
635     switch (regs->ah) {
636     case 0x00: disk_1300(regs, driveid); break;
637     case 0x01: disk_1301(regs, driveid); break;
638     case 0x02: disk_1302(regs, driveid); break;
639     case 0x03: disk_1303(regs, driveid); break;
640     case 0x04: disk_1304(regs, driveid); break;
641     case 0x05: disk_1305(regs, driveid); break;
642     case 0x08: disk_1308(regs, driveid); break;
643     case 0x09: disk_1309(regs, driveid); break;
644     case 0x0c: disk_130c(regs, driveid); break;
645     case 0x0d: disk_130d(regs, driveid); break;
646     case 0x10: disk_1310(regs, driveid); break;
647     case 0x11: disk_1311(regs, driveid); break;
648     case 0x14: disk_1314(regs, driveid); break;
649     case 0x15: disk_1315(regs, driveid); break;
650     case 0x41: disk_1341(regs, driveid); break;
651     case 0x42: disk_1342(regs, driveid); break;
652     case 0x43: disk_1343(regs, driveid); break;
653     case 0x44: disk_1344(regs, driveid); break;
654     case 0x45: disk_1345(regs, driveid); break;
655     case 0x46: disk_1346(regs, driveid); break;
656     case 0x47: disk_1347(regs, driveid); break;
657     case 0x48: disk_1348(regs, driveid); break;
658     case 0x49: disk_1349(regs, driveid); break;
659     case 0x4e: disk_134e(regs, driveid); break;
660     default:   disk_13XX(regs, driveid); break;
661     }
662 }
663
664
665 /****************************************************************
666  * Entry points
667  ****************************************************************/
668
669 static int
670 get_driveid(struct bregs *regs, u8 exttype, u8 extdriveoffset)
671 {
672     // basic check : device has to be defined
673     if (extdriveoffset >= ARRAY_SIZE(Drives.idmap[0]))
674         return -1;
675
676     // Get the ata channel
677     u8 driveid = GET_GLOBAL(Drives.idmap[exttype][extdriveoffset]);
678
679     // basic check : device has to be valid
680     if (driveid >= ARRAY_SIZE(Drives.drives))
681         return -1;
682
683     return driveid;
684 }
685
686 static void
687 handle_legacy_disk(struct bregs *regs, u8 extdrive)
688 {
689     if (! CONFIG_DRIVES) {
690         // XXX - support handle_1301 anyway?
691         disk_ret(regs, DISK_RET_EPARAM);
692         return;
693     }
694
695     if (extdrive < 0x80) {
696         int driveid = get_driveid(regs, EXTTYPE_FLOPPY, extdrive);
697         if (driveid < 0)
698             goto fail;
699         floppy_13(regs, driveid);
700         return;
701     }
702
703     if (extdrive >= 0xe0) {
704         int driveid = get_driveid(regs, EXTTYPE_CD, extdrive - 0xe0);
705         if (driveid < 0)
706             goto fail;
707         cdrom_13(regs, driveid);
708         return;
709     }
710
711     int driveid = get_driveid(regs, EXTTYPE_HD, extdrive - 0x80);
712     if (driveid < 0)
713         goto fail;
714     disk_13(regs, driveid);
715     return;
716
717 fail:
718     // XXX - support 1301/1308/1315 anyway?
719     disk_ret(regs, DISK_RET_EPARAM);
720 }
721
722 void VISIBLE16
723 handle_40(struct bregs *regs)
724 {
725     debug_enter(regs, DEBUG_HDL_40);
726     handle_legacy_disk(regs, regs->dl);
727 }
728
729 // INT 13h Fixed Disk Services Entry Point
730 void VISIBLE16
731 handle_13(struct bregs *regs)
732 {
733     debug_enter(regs, DEBUG_HDL_13);
734     u8 extdrive = regs->dl;
735
736     if (CONFIG_CDROM_EMU) {
737         if (regs->ah == 0x4b) {
738             cdemu_134b(regs);
739             return;
740         }
741         u16 ebda_seg = get_ebda_seg();
742         if (GET_EBDA2(ebda_seg, cdemu.active)) {
743             u8 emudrive = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
744             if (extdrive == emudrive) {
745                 cdemu_13(regs);
746                 return;
747             }
748             if (extdrive < 0xe0 && ((emudrive ^ extdrive) & 0x80) == 0)
749                 extdrive--;
750         }
751     }
752     handle_legacy_disk(regs, extdrive);
753 }
754
755 // record completion in BIOS task complete flag
756 void VISIBLE16
757 handle_76()
758 {
759     debug_isr(DEBUG_ISR_76);
760     SET_BDA(disk_interrupt_flag, 0xff);
761     eoi_pic2();
762 }
763
764 // Old Fixed Disk Parameter Table (newer tables are in the ebda).
765 struct fdpt_s OldFDPT VAR16FIXED(0xe401);