Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


imported SEABIOS source tree
[palacios.git] / bios / seabios / 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     if (regs->dl < EXTSTART_HD)
27         SET_BDA(floppy_last_status, code);
28     else
29         SET_BDA(disk_last_status, code);
30     if (code)
31         __set_code_invalid(regs, linecode, fname);
32     else
33         set_code_success(regs);
34 }
35
36 void
37 __disk_ret_unimplemented(struct bregs *regs, u32 linecode, const char *fname)
38 {
39     u8 code = linecode;
40     if (regs->dl < EXTSTART_HD)
41         SET_BDA(floppy_last_status, code);
42     else
43         SET_BDA(disk_last_status, code);
44     __set_code_unimplemented(regs, linecode, fname);
45 }
46
47 static void
48 __disk_stub(struct bregs *regs, int lineno, const char *fname)
49 {
50     __warn_unimplemented(regs, lineno, fname);
51     __disk_ret(regs, DISK_RET_SUCCESS | (lineno << 8), fname);
52 }
53
54 #define DISK_STUB(regs)                         \
55     __disk_stub((regs), __LINE__, __func__)
56
57 // Get the cylinders/heads/sectors for the given drive.
58 static void
59 fillLCHS(struct drive_s *drive_g, u16 *nlc, u16 *nlh, u16 *nlspt)
60 {
61     if (CONFIG_CDROM_EMU
62         && drive_g == GLOBALFLAT2GLOBAL(GET_GLOBAL(cdemu_drive_gf))) {
63         // Emulated drive - get info from ebda.  (It's not possible to
64         // populate the geometry directly in the driveid because the
65         // geometry is only known after the bios segment is made
66         // read-only).
67         u16 ebda_seg = get_ebda_seg();
68         *nlc = GET_EBDA2(ebda_seg, cdemu.lchs.cylinders);
69         *nlh = GET_EBDA2(ebda_seg, cdemu.lchs.heads);
70         *nlspt = GET_EBDA2(ebda_seg, cdemu.lchs.spt);
71         return;
72     }
73     *nlc = GET_GLOBAL(drive_g->lchs.cylinders);
74     *nlh = GET_GLOBAL(drive_g->lchs.heads);
75     *nlspt = GET_GLOBAL(drive_g->lchs.spt);
76 }
77
78 // Perform read/write/verify using old-style chs accesses
79 static void
80 basic_access(struct bregs *regs, struct drive_s *drive_g, u16 command)
81 {
82     struct disk_op_s dop;
83     dop.drive_g = drive_g;
84     dop.command = command;
85
86     u8 count = regs->al;
87     u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300);
88     u16 sector = regs->cl & 0x3f;
89     u16 head = regs->dh;
90
91     if (count > 128 || count == 0 || sector == 0) {
92         warn_invalid(regs);
93         disk_ret(regs, DISK_RET_EPARAM);
94         return;
95     }
96     dop.count = count;
97
98     u16 nlc, nlh, nlspt;
99     fillLCHS(drive_g, &nlc, &nlh, &nlspt);
100
101     // sanity check on cyl heads, sec
102     if (cylinder >= nlc || head >= nlh || sector > nlspt) {
103         warn_invalid(regs);
104         disk_ret(regs, DISK_RET_EPARAM);
105         return;
106     }
107
108     // translate lchs to lba
109     dop.lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
110                + (u32)sector - 1);
111
112     dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
113
114     int status = send_disk_op(&dop);
115
116     regs->al = dop.count;
117
118     disk_ret(regs, status);
119 }
120
121 // Perform read/write/verify using new-style "int13ext" accesses.
122 static void
123 extended_access(struct bregs *regs, struct drive_s *drive_g, u16 command)
124 {
125     struct disk_op_s dop;
126     // Get lba and check.
127     dop.lba = GET_INT13EXT(regs, lba);
128     dop.command = command;
129     dop.drive_g = drive_g;
130     if (dop.lba >= GET_GLOBAL(drive_g->sectors)) {
131         warn_invalid(regs);
132         disk_ret(regs, DISK_RET_EPARAM);
133         return;
134     }
135
136     dop.buf_fl = SEGOFF_TO_FLATPTR(GET_INT13EXT(regs, data));
137     dop.count = GET_INT13EXT(regs, count);
138
139     int status = send_disk_op(&dop);
140
141     SET_INT13EXT(regs, count, dop.count);
142
143     disk_ret(regs, status);
144 }
145
146
147 /****************************************************************
148  * Hard Drive functions
149  ****************************************************************/
150
151 // disk controller reset
152 static void
153 disk_1300(struct bregs *regs, struct drive_s *drive_g)
154 {
155     struct disk_op_s dop;
156     dop.drive_g = drive_g;
157     dop.command = CMD_RESET;
158     int status = send_disk_op(&dop);
159     disk_ret(regs, status);
160 }
161
162 // read disk status
163 static void
164 disk_1301(struct bregs *regs, struct drive_s *drive_g)
165 {
166     u8 v;
167     if (regs->dl < EXTSTART_HD)
168         // Floppy
169         v = GET_BDA(floppy_last_status);
170     else
171         v = GET_BDA(disk_last_status);
172     regs->ah = v;
173     set_cf(regs, v);
174     // XXX - clear disk_last_status?
175 }
176
177 // read disk sectors
178 static void
179 disk_1302(struct bregs *regs, struct drive_s *drive_g)
180 {
181     basic_access(regs, drive_g, CMD_READ);
182 }
183
184 // write disk sectors
185 static void
186 disk_1303(struct bregs *regs, struct drive_s *drive_g)
187 {
188     basic_access(regs, drive_g, CMD_WRITE);
189 }
190
191 // verify disk sectors
192 static void
193 disk_1304(struct bregs *regs, struct drive_s *drive_g)
194 {
195     basic_access(regs, drive_g, CMD_VERIFY);
196 }
197
198 // format disk track
199 static void
200 disk_1305(struct bregs *regs, struct drive_s *drive_g)
201 {
202     debug_stub(regs);
203
204     u16 nlc, nlh, nlspt;
205     fillLCHS(drive_g, &nlc, &nlh, &nlspt);
206
207     u8 num_sectors = regs->al;
208     u8 head        = regs->dh;
209
210     if (head >= nlh || num_sectors == 0 || num_sectors > nlspt) {
211         disk_ret(regs, DISK_RET_EPARAM);
212         return;
213     }
214
215     struct disk_op_s dop;
216     dop.drive_g = drive_g;
217     dop.command = CMD_FORMAT;
218     dop.lba = head;
219     dop.count = num_sectors;
220     dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
221     int status = send_disk_op(&dop);
222     disk_ret(regs, status);
223 }
224
225 // read disk drive parameters
226 static void
227 disk_1308(struct bregs *regs, struct drive_s *drive_g)
228 {
229     u16 ebda_seg = get_ebda_seg();
230     // Get logical geometry from table
231     u16 nlc, nlh, nlspt;
232     fillLCHS(drive_g, &nlc, &nlh, &nlspt);
233     nlc--;
234     nlh--;
235     u8 count;
236     if (regs->dl < EXTSTART_HD) {
237         // Floppy
238         count = GET_GLOBAL(FloppyCount);
239
240         if (CONFIG_CDROM_EMU
241             && drive_g == GLOBALFLAT2GLOBAL(GET_GLOBAL(cdemu_drive_gf)))
242             regs->bx = GET_EBDA2(ebda_seg, cdemu.media) * 2;
243         else
244             regs->bx = GET_GLOBAL(drive_g->floppy_type);
245
246         // set es & di to point to 11 byte diskette param table in ROM
247         regs->es = SEG_BIOS;
248         regs->di = (u32)&diskette_param_table2;
249     } else if (regs->dl < EXTSTART_CD) {
250         // Hard drive
251         count = GET_BDA(hdcount);
252         nlc--;  // last sector reserved
253     } else {
254         // Not supported on CDROM
255         disk_ret(regs, DISK_RET_EPARAM);
256         return;
257     }
258
259     if (CONFIG_CDROM_EMU && GET_EBDA2(ebda_seg, cdemu.active)) {
260         u8 emudrive = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
261         if (((emudrive ^ regs->dl) & 0x80) == 0)
262             // Note extra drive due to emulation.
263             count++;
264         if (regs->dl < EXTSTART_HD && count > 2)
265             // Max of two floppy drives.
266             count = 2;
267     }
268
269     regs->al = 0;
270     regs->ch = nlc & 0xff;
271     regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
272     regs->dh = nlh;
273
274     disk_ret(regs, DISK_RET_SUCCESS);
275     regs->dl = count;
276 }
277
278 // initialize drive parameters
279 static void
280 disk_1309(struct bregs *regs, struct drive_s *drive_g)
281 {
282     DISK_STUB(regs);
283 }
284
285 // seek to specified cylinder
286 static void
287 disk_130c(struct bregs *regs, struct drive_s *drive_g)
288 {
289     DISK_STUB(regs);
290 }
291
292 // alternate disk reset
293 static void
294 disk_130d(struct bregs *regs, struct drive_s *drive_g)
295 {
296     DISK_STUB(regs);
297 }
298
299 // check drive ready
300 static void
301 disk_1310(struct bregs *regs, struct drive_s *drive_g)
302 {
303     // should look at 40:8E also???
304
305     struct disk_op_s dop;
306     dop.drive_g = drive_g;
307     dop.command = CMD_ISREADY;
308     int status = send_disk_op(&dop);
309     disk_ret(regs, status);
310 }
311
312 // recalibrate
313 static void
314 disk_1311(struct bregs *regs, struct drive_s *drive_g)
315 {
316     DISK_STUB(regs);
317 }
318
319 // controller internal diagnostic
320 static void
321 disk_1314(struct bregs *regs, struct drive_s *drive_g)
322 {
323     DISK_STUB(regs);
324 }
325
326 // read disk drive size
327 static void
328 disk_1315(struct bregs *regs, struct drive_s *drive_g)
329 {
330     disk_ret(regs, DISK_RET_SUCCESS);
331     if (regs->dl < EXTSTART_HD || regs->dl >= EXTSTART_CD) {
332         // Floppy or cdrom
333         regs->ah = 1;
334         return;
335     }
336     // Hard drive
337
338     // Get logical geometry from table
339     u16 nlc, nlh, nlspt;
340     fillLCHS(drive_g, &nlc, &nlh, &nlspt);
341
342     // Compute sector count seen by int13
343     u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
344     regs->cx = lba >> 16;
345     regs->dx = lba & 0xffff;
346     regs->ah = 3; // hard disk accessible
347 }
348
349 static void
350 disk_1316(struct bregs *regs, struct drive_s *drive_g)
351 {
352     if (regs->dl >= EXTSTART_HD) {
353         // Hard drive
354         disk_ret(regs, DISK_RET_EPARAM);
355         return;
356     }
357     disk_ret(regs, DISK_RET_ECHANGED);
358 }
359
360 // IBM/MS installation check
361 static void
362 disk_1341(struct bregs *regs, struct drive_s *drive_g)
363 {
364     regs->bx = 0xaa55;  // install check
365     regs->cx = 0x0007;  // ext disk access and edd, removable supported
366     disk_ret(regs, DISK_RET_SUCCESS);
367     regs->ah = 0x30;    // EDD 3.0
368 }
369
370 // IBM/MS extended read
371 static void
372 disk_1342(struct bregs *regs, struct drive_s *drive_g)
373 {
374     extended_access(regs, drive_g, CMD_READ);
375 }
376
377 // IBM/MS extended write
378 static void
379 disk_1343(struct bregs *regs, struct drive_s *drive_g)
380 {
381     extended_access(regs, drive_g, CMD_WRITE);
382 }
383
384 // IBM/MS verify
385 static void
386 disk_1344(struct bregs *regs, struct drive_s *drive_g)
387 {
388     extended_access(regs, drive_g, CMD_VERIFY);
389 }
390
391 // lock
392 static void
393 disk_134500(struct bregs *regs, struct drive_s *drive_g)
394 {
395     u16 ebda_seg = get_ebda_seg();
396     int cdid = regs->dl - EXTSTART_CD;
397     u8 locks = GET_EBDA2(ebda_seg, cdrom_locks[cdid]);
398     if (locks == 0xff) {
399         regs->al = 1;
400         disk_ret(regs, DISK_RET_ETOOMANYLOCKS);
401         return;
402     }
403     SET_EBDA2(ebda_seg, cdrom_locks[cdid], locks + 1);
404     regs->al = 1;
405     disk_ret(regs, DISK_RET_SUCCESS);
406 }
407
408 // unlock
409 static void
410 disk_134501(struct bregs *regs, struct drive_s *drive_g)
411 {
412     u16 ebda_seg = get_ebda_seg();
413     int cdid = regs->dl - EXTSTART_CD;
414     u8 locks = GET_EBDA2(ebda_seg, cdrom_locks[cdid]);
415     if (locks == 0x00) {
416         regs->al = 0;
417         disk_ret(regs, DISK_RET_ENOTLOCKED);
418         return;
419     }
420     locks--;
421     SET_EBDA2(ebda_seg, cdrom_locks[cdid], locks);
422     regs->al = (locks ? 1 : 0);
423     disk_ret(regs, DISK_RET_SUCCESS);
424 }
425
426 // status
427 static void
428 disk_134502(struct bregs *regs, struct drive_s *drive_g)
429 {
430     int cdid = regs->dl - EXTSTART_CD;
431     u8 locks = GET_EBDA(cdrom_locks[cdid]);
432     regs->al = (locks ? 1 : 0);
433     disk_ret(regs, DISK_RET_SUCCESS);
434 }
435
436 static void
437 disk_1345XX(struct bregs *regs, struct drive_s *drive_g)
438 {
439     disk_ret_unimplemented(regs, DISK_RET_EPARAM);
440 }
441
442 // IBM/MS lock/unlock drive
443 static void
444 disk_1345(struct bregs *regs, struct drive_s *drive_g)
445 {
446     if (regs->dl < EXTSTART_CD) {
447         // Always success for HD
448         disk_ret(regs, DISK_RET_SUCCESS);
449         return;
450     }
451
452     switch (regs->al) {
453     case 0x00: disk_134500(regs, drive_g); break;
454     case 0x01: disk_134501(regs, drive_g); break;
455     case 0x02: disk_134502(regs, drive_g); break;
456     default:   disk_1345XX(regs, drive_g); break;
457     }
458 }
459
460 // IBM/MS eject media
461 static void
462 disk_1346(struct bregs *regs, struct drive_s *drive_g)
463 {
464     if (regs->dl < EXTSTART_CD) {
465         // Volume Not Removable
466         disk_ret(regs, DISK_RET_ENOTREMOVABLE);
467         return;
468     }
469
470     int cdid = regs->dl - EXTSTART_CD;
471     u8 locks = GET_EBDA(cdrom_locks[cdid]);
472     if (locks != 0) {
473         disk_ret(regs, DISK_RET_ELOCKED);
474         return;
475     }
476
477     // FIXME should handle 0x31 no media in device
478     // FIXME should handle 0xb5 valid request failed
479
480     // Call removable media eject
481     struct bregs br;
482     memset(&br, 0, sizeof(br));
483     br.ah = 0x52;
484     br.dl = regs->dl;
485     call16_int(0x15, &br);
486
487     if (br.ah || br.flags & F_CF) {
488         disk_ret(regs, DISK_RET_ELOCKED);
489         return;
490     }
491     disk_ret(regs, DISK_RET_SUCCESS);
492 }
493
494 // IBM/MS extended seek
495 static void
496 disk_1347(struct bregs *regs, struct drive_s *drive_g)
497 {
498     extended_access(regs, drive_g, CMD_SEEK);
499 }
500
501 // IBM/MS get drive parameters
502 static void
503 disk_1348(struct bregs *regs, struct drive_s *drive_g)
504 {
505     u16 size = GET_INT13DPT(regs, size);
506     u16 t13 = size == 74;
507
508     // Buffer is too small
509     if (size < 26) {
510         disk_ret(regs, DISK_RET_EPARAM);
511         return;
512     }
513
514     // EDD 1.x
515
516     u8  type    = GET_GLOBAL(drive_g->type);
517     u16 npc     = GET_GLOBAL(drive_g->pchs.cylinders);
518     u16 nph     = GET_GLOBAL(drive_g->pchs.heads);
519     u16 npspt   = GET_GLOBAL(drive_g->pchs.spt);
520     u64 lba     = GET_GLOBAL(drive_g->sectors);
521     u16 blksize = GET_GLOBAL(drive_g->blksize);
522
523     dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n"
524             , size, type, npc, nph, npspt, (u32)lba, blksize);
525
526     SET_INT13DPT(regs, size, 26);
527     if (type == DTYPE_ATAPI) {
528         // 0x74 = removable, media change, lockable, max values
529         SET_INT13DPT(regs, infos, 0x74);
530         SET_INT13DPT(regs, cylinders, 0xffffffff);
531         SET_INT13DPT(regs, heads, 0xffffffff);
532         SET_INT13DPT(regs, spt, 0xffffffff);
533         SET_INT13DPT(regs, sector_count, (u64)-1);
534     } else {
535         if (lba > (u64)npspt*nph*0x3fff) {
536             SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
537             SET_INT13DPT(regs, cylinders, 0x3fff);
538         } else {
539             SET_INT13DPT(regs, infos, 0x02); // geometry is valid
540             SET_INT13DPT(regs, cylinders, (u32)npc);
541         }
542         SET_INT13DPT(regs, heads, (u32)nph);
543         SET_INT13DPT(regs, spt, (u32)npspt);
544         SET_INT13DPT(regs, sector_count, lba);
545     }
546     SET_INT13DPT(regs, blksize, blksize);
547
548     if (size < 30 ||
549         (type != DTYPE_ATA && type != DTYPE_ATAPI && type != DTYPE_VIRTIO)) {
550         disk_ret(regs, DISK_RET_SUCCESS);
551         return;
552     }
553
554     // EDD 2.x
555
556     int bdf;
557     u16 iobase1 = 0;
558     u64 device_path = 0;
559     u8 channel = 0;
560     SET_INT13DPT(regs, size, 30);
561     if (type == DTYPE_ATA || type == DTYPE_ATAPI) {
562         u16 ebda_seg = get_ebda_seg();
563
564         SET_INT13DPT(regs, dpte_segment, ebda_seg);
565         SET_INT13DPT(regs, dpte_offset
566                      , offsetof(struct extended_bios_data_area_s, dpte));
567
568         // Fill in dpte
569         struct atadrive_s *adrive_g = container_of(
570             drive_g, struct atadrive_s, drive);
571         struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
572         u8 slave = GET_GLOBAL(adrive_g->slave);
573         u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
574         u8 irq = GET_GLOBALFLAT(chan_gf->irq);
575         iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
576         bdf = GET_GLOBALFLAT(chan_gf->pci_bdf);
577         device_path = slave;
578         channel = GET_GLOBALFLAT(chan_gf->chanid);
579
580         u16 options = 0;
581         if (type == DTYPE_ATA) {
582             u8 translation = GET_GLOBAL(drive_g->translation);
583             if (translation != TRANSLATION_NONE) {
584                 options |= 1<<3; // CHS translation
585                 if (translation == TRANSLATION_LBA)
586                     options |= 1<<9;
587                 if (translation == TRANSLATION_RECHS)
588                     options |= 3<<9;
589             }
590         } else {
591             // ATAPI
592             options |= 1<<5; // removable device
593             options |= 1<<6; // atapi device
594         }
595         options |= 1<<4; // lba translation
596         if (CONFIG_ATA_PIO32)
597             options |= 1<<7;
598
599         SET_EBDA2(ebda_seg, dpte.iobase1, iobase1);
600         SET_EBDA2(ebda_seg, dpte.iobase2, iobase2 + ATA_CB_DC);
601         SET_EBDA2(ebda_seg, dpte.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)
602                                           | ATA_CB_DH_LBA));
603         SET_EBDA2(ebda_seg, dpte.unused, 0xcb);
604         SET_EBDA2(ebda_seg, dpte.irq, irq);
605         SET_EBDA2(ebda_seg, dpte.blkcount, 1);
606         SET_EBDA2(ebda_seg, dpte.dma, 0);
607         SET_EBDA2(ebda_seg, dpte.pio, 0);
608         SET_EBDA2(ebda_seg, dpte.options, options);
609         SET_EBDA2(ebda_seg, dpte.reserved, 0);
610         SET_EBDA2(ebda_seg, dpte.revision, 0x11);
611
612         u8 sum = checksum_far(
613             ebda_seg, (void*)offsetof(struct extended_bios_data_area_s, dpte), 15);
614         SET_EBDA2(ebda_seg, dpte.checksum, -sum);
615     } else {
616         SET_INT13DPT(regs, dpte_segment, 0);
617         SET_INT13DPT(regs, dpte_offset, 0);
618         bdf = GET_GLOBAL(drive_g->cntl_id);
619     }
620
621     if (size < 66) {
622         disk_ret(regs, DISK_RET_SUCCESS);
623         return;
624     }
625
626     // EDD 3.x
627     SET_INT13DPT(regs, key, 0xbedd);
628     SET_INT13DPT(regs, dpi_length, t13 ? 44 : 36);
629     SET_INT13DPT(regs, reserved1, 0);
630     SET_INT13DPT(regs, reserved2, 0);
631
632     if (bdf != -1) {
633         SET_INT13DPT(regs, host_bus[0], 'P');
634         SET_INT13DPT(regs, host_bus[1], 'C');
635         SET_INT13DPT(regs, host_bus[2], 'I');
636         SET_INT13DPT(regs, host_bus[3], ' ');
637
638         u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8)
639                     | (pci_bdf_to_fn(bdf) << 16));
640         if (t13)
641             path |= channel << 24;
642
643         SET_INT13DPT(regs, iface_path, path);
644     } else {
645         // ISA
646         SET_INT13DPT(regs, host_bus[0], 'I');
647         SET_INT13DPT(regs, host_bus[1], 'S');
648         SET_INT13DPT(regs, host_bus[2], 'A');
649         SET_INT13DPT(regs, host_bus[3], ' ');
650
651         SET_INT13DPT(regs, iface_path, iobase1);
652     }
653
654     if (type != DTYPE_VIRTIO) {
655         SET_INT13DPT(regs, iface_type[0], 'A');
656         SET_INT13DPT(regs, iface_type[1], 'T');
657         SET_INT13DPT(regs, iface_type[2], 'A');
658         SET_INT13DPT(regs, iface_type[3], ' ');
659     } else {
660         SET_INT13DPT(regs, iface_type[0], 'S');
661         SET_INT13DPT(regs, iface_type[1], 'C');
662         SET_INT13DPT(regs, iface_type[2], 'S');
663         SET_INT13DPT(regs, iface_type[3], 'I');
664     }
665     SET_INT13DPT(regs, iface_type[4], ' ');
666     SET_INT13DPT(regs, iface_type[5], ' ');
667     SET_INT13DPT(regs, iface_type[6], ' ');
668     SET_INT13DPT(regs, iface_type[7], ' ');
669
670     if (t13) {
671         SET_INT13DPT(regs, t13.device_path[0], device_path);
672         SET_INT13DPT(regs, t13.device_path[1], 0);
673
674         SET_INT13DPT(regs, t13.checksum
675                      , -checksum_far(regs->ds, (void*)(regs->si+30), 43));
676     } else {
677         SET_INT13DPT(regs, phoenix.device_path, device_path);
678
679         SET_INT13DPT(regs, phoenix.checksum
680                      , -checksum_far(regs->ds, (void*)(regs->si+30), 35));
681     }
682
683     disk_ret(regs, DISK_RET_SUCCESS);
684 }
685
686 // IBM/MS extended media change
687 static void
688 disk_1349(struct bregs *regs, struct drive_s *drive_g)
689 {
690     if (regs->dl < EXTSTART_CD) {
691         // Always success for HD
692         disk_ret(regs, DISK_RET_SUCCESS);
693         return;
694     }
695     set_invalid(regs);
696     // always send changed ??
697     regs->ah = DISK_RET_ECHANGED;
698 }
699
700 static void
701 disk_134e01(struct bregs *regs, struct drive_s *drive_g)
702 {
703     disk_ret(regs, DISK_RET_SUCCESS);
704 }
705
706 static void
707 disk_134e03(struct bregs *regs, struct drive_s *drive_g)
708 {
709     disk_ret(regs, DISK_RET_SUCCESS);
710 }
711
712 static void
713 disk_134e04(struct bregs *regs, struct drive_s *drive_g)
714 {
715     disk_ret(regs, DISK_RET_SUCCESS);
716 }
717
718 static void
719 disk_134e06(struct bregs *regs, struct drive_s *drive_g)
720 {
721     disk_ret(regs, DISK_RET_SUCCESS);
722 }
723
724 static void
725 disk_134eXX(struct bregs *regs, struct drive_s *drive_g)
726 {
727     disk_ret(regs, DISK_RET_EPARAM);
728 }
729
730 // IBM/MS set hardware configuration
731 static void
732 disk_134e(struct bregs *regs, struct drive_s *drive_g)
733 {
734     switch (regs->al) {
735     case 0x01: disk_134e01(regs, drive_g); break;
736     case 0x03: disk_134e03(regs, drive_g); break;
737     case 0x04: disk_134e04(regs, drive_g); break;
738     case 0x06: disk_134e06(regs, drive_g); break;
739     default:   disk_134eXX(regs, drive_g); break;
740     }
741 }
742
743 static void
744 disk_13XX(struct bregs *regs, struct drive_s *drive_g)
745 {
746     disk_ret_unimplemented(regs, DISK_RET_EPARAM);
747 }
748
749 static void
750 disk_13(struct bregs *regs, struct drive_s *drive_g)
751 {
752     //debug_stub(regs);
753
754     // clear completion flag
755     SET_BDA(disk_interrupt_flag, 0);
756
757     switch (regs->ah) {
758     case 0x00: disk_1300(regs, drive_g); break;
759     case 0x01: disk_1301(regs, drive_g); break;
760     case 0x02: disk_1302(regs, drive_g); break;
761     case 0x03: disk_1303(regs, drive_g); break;
762     case 0x04: disk_1304(regs, drive_g); break;
763     case 0x05: disk_1305(regs, drive_g); break;
764     case 0x08: disk_1308(regs, drive_g); break;
765     case 0x09: disk_1309(regs, drive_g); break;
766     case 0x0c: disk_130c(regs, drive_g); break;
767     case 0x0d: disk_130d(regs, drive_g); break;
768     case 0x10: disk_1310(regs, drive_g); break;
769     case 0x11: disk_1311(regs, drive_g); break;
770     case 0x14: disk_1314(regs, drive_g); break;
771     case 0x15: disk_1315(regs, drive_g); break;
772     case 0x16: disk_1316(regs, drive_g); break;
773     case 0x41: disk_1341(regs, drive_g); break;
774     case 0x42: disk_1342(regs, drive_g); break;
775     case 0x43: disk_1343(regs, drive_g); break;
776     case 0x44: disk_1344(regs, drive_g); break;
777     case 0x45: disk_1345(regs, drive_g); break;
778     case 0x46: disk_1346(regs, drive_g); break;
779     case 0x47: disk_1347(regs, drive_g); break;
780     case 0x48: disk_1348(regs, drive_g); break;
781     case 0x49: disk_1349(regs, drive_g); break;
782     case 0x4e: disk_134e(regs, drive_g); break;
783     default:   disk_13XX(regs, drive_g); break;
784     }
785 }
786
787 static void
788 floppy_13(struct bregs *regs, struct drive_s *drive_g)
789 {
790     // Only limited commands are supported on floppies.
791     switch (regs->ah) {
792     case 0x00:
793     case 0x01:
794     case 0x02:
795     case 0x03:
796     case 0x04:
797     case 0x05:
798     case 0x08:
799     case 0x15:
800     case 0x16:
801         disk_13(regs, drive_g);
802         break;
803     default:   disk_13XX(regs, drive_g); break;
804     }
805 }
806
807
808 /****************************************************************
809  * Entry points
810  ****************************************************************/
811
812 static void
813 handle_legacy_disk(struct bregs *regs, u8 extdrive)
814 {
815     if (! CONFIG_DRIVES) {
816         // XXX - support handle_1301 anyway?
817         disk_ret(regs, DISK_RET_EPARAM);
818         return;
819     }
820
821     if (extdrive < EXTSTART_HD) {
822         struct drive_s *drive_g = getDrive(EXTTYPE_FLOPPY, extdrive);
823         if (!drive_g)
824             goto fail;
825         floppy_13(regs, drive_g);
826         return;
827     }
828
829     struct drive_s *drive_g;
830     if (extdrive >= EXTSTART_CD)
831         drive_g = getDrive(EXTTYPE_CD, extdrive - EXTSTART_CD);
832     else
833         drive_g = getDrive(EXTTYPE_HD, extdrive - EXTSTART_HD);
834     if (!drive_g)
835         goto fail;
836     disk_13(regs, drive_g);
837     return;
838
839 fail:
840     // XXX - support 1301/1308/1315 anyway?
841     disk_ret(regs, DISK_RET_EPARAM);
842 }
843
844 void VISIBLE16
845 handle_40(struct bregs *regs)
846 {
847     debug_enter(regs, DEBUG_HDL_40);
848     handle_legacy_disk(regs, regs->dl);
849 }
850
851 // INT 13h Fixed Disk Services Entry Point
852 void VISIBLE16
853 handle_13(struct bregs *regs)
854 {
855     debug_enter(regs, DEBUG_HDL_13);
856     u8 extdrive = regs->dl;
857
858     if (CONFIG_CDROM_EMU) {
859         if (regs->ah == 0x4b) {
860             cdemu_134b(regs);
861             return;
862         }
863         u16 ebda_seg = get_ebda_seg();
864         if (GET_EBDA2(ebda_seg, cdemu.active)) {
865             u8 emudrive = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
866             if (extdrive == emudrive) {
867                 // Access to an emulated drive.
868                 struct drive_s *cdemu_g;
869                 cdemu_g = GLOBALFLAT2GLOBAL(GET_GLOBAL(cdemu_drive_gf));
870                 if (regs->ah > 0x16) {
871                     // Only old-style commands supported.
872                     disk_13XX(regs, cdemu_g);
873                     return;
874                 }
875                 disk_13(regs, cdemu_g);
876                 return;
877             }
878             if (extdrive < EXTSTART_CD && ((emudrive ^ extdrive) & 0x80) == 0)
879                 // Adjust id to make room for emulated drive.
880                 extdrive--;
881         }
882     }
883     handle_legacy_disk(regs, extdrive);
884 }
885
886 // record completion in BIOS task complete flag
887 void VISIBLE16
888 handle_76(void)
889 {
890     debug_isr(DEBUG_ISR_76);
891     SET_BDA(disk_interrupt_flag, 0xff);
892     eoi_pic2();
893 }
894
895 // Old Fixed Disk Parameter Table (newer tables are in the ebda).
896 struct fdpt_s OldFDPT VAR16FIXED(0xe401);