74c1acad31094a05fac6149adf416ff1d8408b82
[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
16 /****************************************************************
17  * Helper functions
18  ****************************************************************/
19
20 static inline void
21 disk_ret(struct bregs *regs, u8 code)
22 {
23     regs->ah = code;
24     SET_BDA(disk_last_status, code);
25     set_cf(regs, code);
26 }
27
28 #define DISK_STUB(regs) do {                    \
29         struct bregs *__regs = (regs);          \
30         debug_stub(__regs);                     \
31         disk_ret(__regs, DISK_RET_SUCCESS);     \
32     } while (0)
33
34 static u8
35 checksum_seg(u16 seg, u16 offset, u32 len)
36 {
37     u32 i;
38     u8 sum = 0;
39     for (i=0; i<len; i++)
40         sum += GET_FARVAR(seg, *(u8*)(offset+i));
41     return sum;
42 }
43
44 static void
45 basic_access(struct bregs *regs, u8 device, u16 command)
46 {
47     u16 count       = regs->al;
48     u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
49     u16 sector      = regs->cl & 0x3f;
50     u16 head        = regs->dh;
51
52     if ((count > 128) || (count == 0) || (sector == 0)) {
53         BX_INFO("int13_harddisk: function %02x, parameter out of range!\n"
54                 , regs->ah);
55         disk_ret(regs, DISK_RET_EPARAM);
56         return;
57     }
58
59     u16 nlc   = GET_EBDA(ata.devices[device].lchs.cylinders);
60     u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
61     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
62     u16 nph   = GET_EBDA(ata.devices[device].pchs.heads);
63     u16 npspt = GET_EBDA(ata.devices[device].pchs.spt);
64
65     // sanity check on cyl heads, sec
66     if ( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
67         BX_INFO("int13_harddisk: function %02x, parameters out of"
68                 " range %04x/%04x/%04x!\n"
69                 , regs->ah, cylinder, head, sector);
70         disk_ret(regs, DISK_RET_EPARAM);
71         return;
72     }
73
74     u32 lba = 0;
75     // if needed, translate lchs to lba, and execute command
76     if ( (nph != nlh) || (npspt != nlspt)) {
77         lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
78                + (u32)sector - 1);
79         sector = 0; // this forces the command to be lba
80     }
81
82     u16 segment = regs->es;
83     u16 offset  = regs->bx;
84
85     u8 status;
86     switch (command) {
87     case ATA_CMD_READ_SECTORS:
88         status = ata_cmd_data_in(device, ATA_CMD_READ_SECTORS
89                                  , count, cylinder, head, sector
90                                  , lba, segment, offset);
91         break;
92     case ATA_CMD_WRITE_SECTORS:
93         status = ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS
94                                   , count, cylinder, head, sector
95                                   , lba, segment, offset);
96         break;
97     default:
98         disk_ret(regs, DISK_RET_SUCCESS);
99         return;
100     }
101
102     // Set nb of sector transferred
103     regs->al = GET_EBDA(ata.trsfsectors);
104
105     if (status != 0) {
106         BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
107         disk_ret(regs, DISK_RET_EBADTRACK);
108     }
109     disk_ret(regs, DISK_RET_SUCCESS);
110 }
111
112 static void
113 extended_access(struct bregs *regs, u8 device, u16 command)
114 {
115     u16 count = GET_INT13EXT(regs, count);
116     u16 segment = GET_INT13EXT(regs, segment);
117     u16 offset = GET_INT13EXT(regs, offset);
118
119     // Can't use 64 bits lba
120     u32 lba = GET_INT13EXT(regs, lba2);
121     if (lba != 0L) {
122         BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n"
123                  , regs->ah);
124         disk_ret(regs, DISK_RET_EPARAM);
125         return;
126     }
127
128     u8 type = GET_EBDA(ata.devices[device].type);
129
130     // Get 32 bits lba and check
131     lba = GET_INT13EXT(regs, lba1);
132     if (type == ATA_TYPE_ATA
133         && lba >= GET_EBDA(ata.devices[device].sectors)) {
134         BX_INFO("int13_harddisk: function %02x. LBA out of range\n", regs->ah);
135         disk_ret(regs, DISK_RET_EPARAM);
136         return;
137     }
138
139     u8 status;
140     switch (command) {
141     case ATA_CMD_READ_SECTORS:
142         if (type == ATA_TYPE_ATA) {
143             status = ata_cmd_data_in(device, ATA_CMD_READ_SECTORS
144                                      , count, 0, 0, 0
145                                      , lba, segment, offset);
146         } else {
147             u8 atacmd[12];
148             memset(atacmd, 0, sizeof(atacmd));
149             atacmd[0]=0x28;                      // READ command
150             atacmd[7]=(count & 0xff00) >> 8;     // Sectors
151             atacmd[8]=(count & 0x00ff);          // Sectors
152             atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
153             atacmd[3]=(lba & 0x00ff0000) >> 16;
154             atacmd[4]=(lba & 0x0000ff00) >> 8;
155             atacmd[5]=(lba & 0x000000ff);
156
157             status = ata_cmd_packet(device, (u32)atacmd, sizeof(atacmd)
158                                     , 0, count*2048L
159                                     , ATA_DATA_IN, segment, offset);
160         }
161         break;
162     case ATA_CMD_WRITE_SECTORS:
163         status = ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS
164                                   , count, 0, 0, 0
165                                   , lba, segment, offset);
166         break;
167     default:
168         // If verify or seek
169         disk_ret(regs, DISK_RET_SUCCESS);
170         return;
171     }
172
173     SET_INT13EXT(regs, count, GET_EBDA(ata.trsfsectors));
174
175     if (status != 0) {
176         BX_INFO("int13_harddisk: function %02x, error %02x !\n"
177                 , regs->ah, status);
178         disk_ret(regs, DISK_RET_EBADTRACK);
179         return;
180     }
181     disk_ret(regs, DISK_RET_SUCCESS);
182 }
183
184
185 /****************************************************************
186  * Hard Drive functions
187  ****************************************************************/
188
189 // disk controller reset
190 static void
191 disk_1300(struct bregs *regs, u8 device)
192 {
193     ata_reset(device);
194 }
195
196 // read disk status
197 static void
198 disk_1301(struct bregs *regs, u8 device)
199 {
200     regs->ah = GET_BDA(disk_last_status);
201     disk_ret(regs, DISK_RET_SUCCESS);
202 }
203
204 // read disk sectors
205 static void
206 disk_1302(struct bregs *regs, u8 device)
207 {
208     basic_access(regs, device, ATA_CMD_READ_SECTORS);
209 }
210
211 // write disk sectors
212 static void
213 disk_1303(struct bregs *regs, u8 device)
214 {
215     basic_access(regs, device, ATA_CMD_WRITE_SECTORS);
216 }
217
218 // verify disk sectors
219 static void
220 disk_1304(struct bregs *regs, u8 device)
221 {
222     basic_access(regs, device, 0);
223     // FIXME verify
224 }
225
226 // format disk track
227 static void
228 disk_1305(struct bregs *regs, u8 device)
229 {
230     DISK_STUB(regs);
231 }
232
233 // read disk drive parameters
234 static void
235 disk_1308(struct bregs *regs, u8 device)
236 {
237     // Get logical geometry from table
238     u16 nlc = GET_EBDA(ata.devices[device].lchs.cylinders);
239     u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
240     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
241     u16 count = GET_EBDA(ata.hdcount);
242
243     nlc = nlc - 2; /* 0 based , last sector not used */
244     regs->al = 0;
245     regs->ch = nlc & 0xff;
246     regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
247     regs->dh = nlh - 1;
248     regs->dl = count; /* FIXME returns 0, 1, or n hard drives */
249
250     // FIXME should set ES & DI
251     disk_ret(regs, DISK_RET_SUCCESS);
252 }
253
254 // initialize drive parameters
255 static void
256 disk_1309(struct bregs *regs, u8 device)
257 {
258     DISK_STUB(regs);
259 }
260
261 // seek to specified cylinder
262 static void
263 disk_130c(struct bregs *regs, u8 device)
264 {
265     DISK_STUB(regs);
266 }
267
268 // alternate disk reset
269 static void
270 disk_130d(struct bregs *regs, u8 device)
271 {
272     DISK_STUB(regs);
273 }
274
275 // check drive ready
276 static void
277 disk_1310(struct bregs *regs, u8 device)
278 {
279     // should look at 40:8E also???
280
281     // Read the status from controller
282     u8 status = inb(GET_EBDA(ata.channels[device/2].iobase1) + ATA_CB_STAT);
283     if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY )
284         disk_ret(regs, DISK_RET_SUCCESS);
285     else
286         disk_ret(regs, DISK_RET_ENOTREADY);
287 }
288
289 // recalibrate
290 static void
291 disk_1311(struct bregs *regs, u8 device)
292 {
293     DISK_STUB(regs);
294 }
295
296 // controller internal diagnostic
297 static void
298 disk_1314(struct bregs *regs, u8 device)
299 {
300     DISK_STUB(regs);
301 }
302
303 // read disk drive size
304 static void
305 disk_1315(struct bregs *regs, u8 device)
306 {
307     // Get logical geometry from table
308     u16 nlc   = GET_EBDA(ata.devices[device].lchs.cylinders);
309     u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
310     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
311
312     // Compute sector count seen by int13
313     u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
314     regs->cx = lba >> 16;
315     regs->dx = lba & 0xffff;
316
317     disk_ret(regs, 0);
318     regs->ah = 3; // hard disk accessible
319 }
320
321 // IBM/MS installation check
322 static void
323 disk_1341(struct bregs *regs, u8 device)
324 {
325     regs->bx = 0xaa55;  // install check
326     regs->cx = 0x0007;  // ext disk access and edd, removable supported
327     disk_ret(regs, DISK_RET_SUCCESS);
328     regs->ah = 0x30;    // EDD 3.0
329 }
330
331 // IBM/MS extended read
332 static void
333 disk_1342(struct bregs *regs, u8 device)
334 {
335     extended_access(regs, device, ATA_CMD_READ_SECTORS);
336 }
337
338 // IBM/MS extended write
339 static void
340 disk_1343(struct bregs *regs, u8 device)
341 {
342     extended_access(regs, device, ATA_CMD_WRITE_SECTORS);
343 }
344
345 // IBM/MS verify
346 static void
347 disk_1344(struct bregs *regs, u8 device)
348 {
349     extended_access(regs, device, 0);
350 }
351
352 // IBM/MS lock/unlock drive
353 static void
354 disk_1345(struct bregs *regs, u8 device)
355 {
356     // Always success for HD
357     disk_ret(regs, DISK_RET_SUCCESS);
358 }
359
360 // IBM/MS eject media
361 static void
362 disk_1346(struct bregs *regs, u8 device)
363 {
364     // Volume Not Removable
365     disk_ret(regs, DISK_RET_ENOTREMOVABLE);
366 }
367
368 // IBM/MS extended seek
369 static void
370 disk_1347(struct bregs *regs, u8 device)
371 {
372     extended_access(regs, device, 0);
373 }
374
375 // IBM/MS get drive parameters
376 static void
377 disk_1348(struct bregs *regs, u8 device)
378 {
379     u16 size = GET_INT13DPT(regs, size);
380
381     // Buffer is too small
382     if (size < 0x1a) {
383         disk_ret(regs, DISK_RET_EPARAM);
384         return;
385     }
386
387     // EDD 1.x
388
389     u8  type    = GET_EBDA(ata.devices[device].type);
390     u16 npc     = GET_EBDA(ata.devices[device].pchs.cylinders);
391     u16 nph     = GET_EBDA(ata.devices[device].pchs.heads);
392     u16 npspt   = GET_EBDA(ata.devices[device].pchs.spt);
393     u32 lba     = GET_EBDA(ata.devices[device].sectors);
394     u16 blksize = GET_EBDA(ata.devices[device].blksize);
395
396     SET_INT13DPT(regs, size, 0x1a);
397     if (type == ATA_TYPE_ATA) {
398         if ((lba/npspt)/nph > 0x3fff) {
399             SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
400             SET_INT13DPT(regs, cylinders, 0x3fff);
401         } else {
402             SET_INT13DPT(regs, infos, 0x02); // geometry is valid
403             SET_INT13DPT(regs, cylinders, (u32)npc);
404         }
405         SET_INT13DPT(regs, heads, (u32)nph);
406         SET_INT13DPT(regs, spt, (u32)npspt);
407         SET_INT13DPT(regs, sector_count1, lba);  // FIXME should be Bit64
408         SET_INT13DPT(regs, sector_count2, 0L);
409     } else {
410         // ATAPI
411         // 0x74 = removable, media change, lockable, max values
412         SET_INT13DPT(regs, infos, 0x74);
413         SET_INT13DPT(regs, cylinders, 0xffffffff);
414         SET_INT13DPT(regs, heads, 0xffffffff);
415         SET_INT13DPT(regs, spt, 0xffffffff);
416         SET_INT13DPT(regs, sector_count1, 0xffffffff);  // FIXME should be Bit64
417         SET_INT13DPT(regs, sector_count2, 0xffffffff);
418     }
419     SET_INT13DPT(regs, blksize, blksize);
420
421     if (size < 0x1e) {
422         disk_ret(regs, DISK_RET_SUCCESS);
423         return;
424     }
425
426     // EDD 2.x
427
428     SET_INT13DPT(regs, size, 0x1e);
429
430     SET_INT13DPT(regs, dpte_segment, EBDA_SEG);
431     SET_INT13DPT(regs, dpte_offset
432                  , offsetof(struct extended_bios_data_area_s, ata.dpte));
433
434     // Fill in dpte
435     u8 channel = device / 2;
436     u16 iobase1 = GET_EBDA(ata.channels[channel].iobase1);
437     u16 iobase2 = GET_EBDA(ata.channels[channel].iobase2);
438     u8 irq = GET_EBDA(ata.channels[channel].irq);
439     u8 mode = GET_EBDA(ata.devices[device].mode);
440
441     u16 options;
442     if (type == ATA_TYPE_ATA) {
443         u8 translation = GET_EBDA(ata.devices[device].translation);
444         options  = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
445         options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
446         options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
447     } else {
448         // ATAPI
449         options  = (1<<5); // removable device
450         options |= (1<<6); // atapi device
451     }
452     options |= (1<<4); // lba translation
453     options |= (mode==ATA_MODE_PIO32?1:0)<<7;
454
455     SET_EBDA(ata.dpte.iobase1, iobase1);
456     SET_EBDA(ata.dpte.iobase2, iobase2 + ATA_CB_DC);
457     SET_EBDA(ata.dpte.prefix, (0xe | (device % 2))<<4 );
458     SET_EBDA(ata.dpte.unused, 0xcb );
459     SET_EBDA(ata.dpte.irq, irq );
460     SET_EBDA(ata.dpte.blkcount, 1 );
461     SET_EBDA(ata.dpte.dma, 0 );
462     SET_EBDA(ata.dpte.pio, 0 );
463     SET_EBDA(ata.dpte.options, options);
464     SET_EBDA(ata.dpte.reserved, 0);
465     if (size >= 0x42)
466         SET_EBDA(ata.dpte.revision, 0x11);
467     else
468         SET_EBDA(ata.dpte.revision, 0x10);
469
470     u8 sum = checksum_seg(EBDA_SEG
471                           , offsetof(struct extended_bios_data_area_s, ata.dpte)
472                           , 15);
473     SET_EBDA(ata.dpte.checksum, ~sum);
474
475     if (size < 0x42) {
476         disk_ret(regs, DISK_RET_SUCCESS);
477         return;
478     }
479
480     // EDD 3.x
481     channel = device / 2;
482     u8 iface = GET_EBDA(ata.channels[channel].iface);
483     iobase1 = GET_EBDA(ata.channels[channel].iobase1);
484
485     SET_INT13DPT(regs, size, 0x42);
486     SET_INT13DPT(regs, key, 0xbedd);
487     SET_INT13DPT(regs, dpi_length, 0x24);
488     SET_INT13DPT(regs, reserved1, 0);
489     SET_INT13DPT(regs, reserved2, 0);
490
491     if (iface==ATA_IFACE_ISA) {
492         SET_INT13DPT(regs, host_bus[0], 'I');
493         SET_INT13DPT(regs, host_bus[1], 'S');
494         SET_INT13DPT(regs, host_bus[2], 'A');
495         SET_INT13DPT(regs, host_bus[3], 0);
496     } else {
497         // FIXME PCI
498     }
499     SET_INT13DPT(regs, iface_type[0], 'A');
500     SET_INT13DPT(regs, iface_type[1], 'T');
501     SET_INT13DPT(regs, iface_type[2], 'A');
502     SET_INT13DPT(regs, iface_type[3], 0);
503
504     if (iface==ATA_IFACE_ISA) {
505         SET_INT13DPT(regs, iface_path[0], iobase1);
506         SET_INT13DPT(regs, iface_path[2], 0);
507         SET_INT13DPT(regs, iface_path[4], 0L);
508     } else {
509         // FIXME PCI
510     }
511     SET_INT13DPT(regs, device_path[0], device%2);
512     SET_INT13DPT(regs, device_path[1], 0);
513     SET_INT13DPT(regs, device_path[2], 0);
514     SET_INT13DPT(regs, device_path[4], 0L);
515
516     sum = checksum_seg(regs->ds, 30, 34);
517     SET_INT13DPT(regs, checksum, ~sum);
518 }
519
520 // IBM/MS extended media change
521 static void
522 disk_1349(struct bregs *regs, u8 device)
523 {
524     // Always success for HD
525     disk_ret(regs, DISK_RET_SUCCESS);
526 }
527
528 static void
529 disk_134e01(struct bregs *regs, u8 device)
530 {
531     disk_ret(regs, DISK_RET_SUCCESS);
532 }
533
534 static void
535 disk_134e03(struct bregs *regs, u8 device)
536 {
537     disk_ret(regs, DISK_RET_SUCCESS);
538 }
539
540 static void
541 disk_134e04(struct bregs *regs, u8 device)
542 {
543     disk_ret(regs, DISK_RET_SUCCESS);
544 }
545
546 static void
547 disk_134e06(struct bregs *regs, u8 device)
548 {
549     disk_ret(regs, DISK_RET_SUCCESS);
550 }
551
552 static void
553 disk_134eXX(struct bregs *regs, u8 device)
554 {
555     debug_stub(regs);
556     disk_ret(regs, DISK_RET_EPARAM);
557 }
558
559 // IBM/MS set hardware configuration
560 static void
561 disk_134e(struct bregs *regs, u8 device)
562 {
563     switch (regs->al) {
564     case 0x01: disk_134e01(regs, device); break;
565     case 0x03: disk_134e03(regs, device); break;
566     case 0x04: disk_134e04(regs, device); break;
567     case 0x06: disk_134e06(regs, device); break;
568     default:   disk_134eXX(regs, device); break;
569     }
570 }
571
572 static void
573 disk_13XX(struct bregs *regs, u8 device)
574 {
575     debug_stub(regs);
576     disk_ret(regs, DISK_RET_EPARAM);
577 }
578
579 static void
580 disk_13(struct bregs *regs, u8 device)
581 {
582     //debug_stub(regs);
583
584     // clear completion flag
585     SET_BDA(disk_interrupt_flag, 0);
586
587     switch (regs->ah) {
588     case 0x00: disk_1300(regs, device); break;
589     case 0x01: disk_1301(regs, device); break;
590     case 0x02: disk_1302(regs, device); break;
591     case 0x03: disk_1303(regs, device); break;
592     case 0x04: disk_1304(regs, device); break;
593     case 0x05: disk_1305(regs, device); break;
594     case 0x08: disk_1308(regs, device); break;
595     case 0x09: disk_1309(regs, device); break;
596     case 0x0c: disk_130c(regs, device); break;
597     case 0x0d: disk_130d(regs, device); break;
598     case 0x10: disk_1310(regs, device); break;
599     case 0x11: disk_1311(regs, device); break;
600     case 0x14: disk_1314(regs, device); break;
601     case 0x15: disk_1315(regs, device); break;
602     case 0x41: disk_1341(regs, device); break;
603     case 0x42: disk_1342(regs, device); break;
604     case 0x43: disk_1343(regs, device); break;
605     case 0x44: disk_1344(regs, device); break;
606     case 0x45: disk_1345(regs, device); break;
607     case 0x46: disk_1346(regs, device); break;
608     case 0x47: disk_1347(regs, device); break;
609     case 0x48: disk_1348(regs, device); break;
610     case 0x49: disk_1349(regs, device); break;
611     case 0x4e: disk_134e(regs, device); break;
612     default:   disk_13XX(regs, device); break;
613     }
614 }
615
616
617 /****************************************************************
618  * CDROM functions
619  ****************************************************************/
620
621 // read disk drive size
622 static void
623 cdrom_1315(struct bregs *regs, u8 device)
624 {
625     disk_ret(regs, DISK_RET_EADDRNOTFOUND);
626 }
627
628 // lock
629 static void
630 cdrom_134500(struct bregs *regs, u8 device)
631 {
632     u8 locks = GET_EBDA(ata.devices[device].lock);
633     if (locks == 0xff) {
634         regs->al = 1;
635         disk_ret(regs, DISK_RET_ETOOMANYLOCKS);
636         return;
637     }
638     SET_EBDA(ata.devices[device].lock, locks + 1);
639     regs->al = 1;
640     disk_ret(regs, DISK_RET_SUCCESS);
641 }
642
643 // unlock
644 static void
645 cdrom_134501(struct bregs *regs, u8 device)
646 {
647     u8 locks = GET_EBDA(ata.devices[device].lock);
648     if (locks == 0x00) {
649         regs->al = 0;
650         disk_ret(regs, DISK_RET_ENOTLOCKED);
651         return;
652     }
653     locks--;
654     SET_EBDA(ata.devices[device].lock, locks);
655     regs->al = (locks ? 1 : 0);
656     disk_ret(regs, DISK_RET_SUCCESS);
657 }
658
659 // status
660 static void
661 cdrom_134502(struct bregs *regs, u8 device)
662 {
663     u8 locks = GET_EBDA(ata.devices[device].lock);
664     regs->al = (locks ? 1 : 0);
665     disk_ret(regs, DISK_RET_SUCCESS);
666 }
667
668 static void
669 cdrom_1345XX(struct bregs *regs, u8 device)
670 {
671     disk_ret(regs, DISK_RET_EPARAM);
672 }
673
674 // IBM/MS lock/unlock drive
675 static void
676 cdrom_1345(struct bregs *regs, u8 device)
677 {
678     switch (regs->al) {
679     case 0x00: cdrom_134500(regs, device); break;
680     case 0x01: cdrom_134501(regs, device); break;
681     case 0x02: cdrom_134502(regs, device); break;
682     default:   cdrom_1345XX(regs, device); break;
683     }
684 }
685
686 // IBM/MS eject media
687 static void
688 cdrom_1346(struct bregs *regs, u8 device)
689 {
690     u8 locks = GET_EBDA(ata.devices[device].lock);
691     if (locks != 0) {
692         disk_ret(regs, DISK_RET_ELOCKED);
693         return;
694     }
695
696     // FIXME should handle 0x31 no media in device
697     // FIXME should handle 0xb5 valid request failed
698
699     // Call removable media eject
700     struct bregs br;
701     memset(&br, 0, sizeof(br));
702     br.ah = 0x52;
703     call16_int(0x15, &br);
704
705     if (br.ah || br.flags & F_CF) {
706         disk_ret(regs, DISK_RET_ELOCKED);
707         return;
708     }
709     disk_ret(regs, DISK_RET_SUCCESS);
710 }
711
712 // IBM/MS extended media change
713 static void
714 cdrom_1349(struct bregs *regs, u8 device)
715 {
716     // always send changed ??
717     regs->ah = DISK_RET_ECHANGED;
718     set_cf(regs, 1);
719 }
720
721 static void
722 cdrom_ok(struct bregs *regs, u8 device)
723 {
724     disk_ret(regs, DISK_RET_SUCCESS);
725 }
726
727 static void
728 cdrom_wp(struct bregs *regs, u8 device)
729 {
730     disk_ret(regs, DISK_RET_EWRITEPROTECT);
731 }
732
733 void
734 cdrom_13(struct bregs *regs, u8 device)
735 {
736     //debug_stub(regs);
737
738     switch (regs->ah) {
739     case 0x15: cdrom_1315(regs, device); break;
740     case 0x45: cdrom_1345(regs, device); break;
741     case 0x46: cdrom_1346(regs, device); break;
742     case 0x49: cdrom_1349(regs, device); break;
743
744     // These functions are the same as for hard disks
745     case 0x01: disk_1301(regs, device); break;
746     case 0x41: disk_1341(regs, device); break;
747     case 0x42: disk_1342(regs, device); break;
748     case 0x44: disk_1344(regs, device); break;
749     case 0x47: disk_1347(regs, device); break;
750     case 0x48: disk_1348(regs, device); break;
751     case 0x4e: disk_134e(regs, device); break;
752
753     // all these functions return SUCCESS
754     case 0x00: cdrom_ok(regs, device); break; // disk controller reset
755     case 0x09: cdrom_ok(regs, device); break; // initialize drive parameters
756     case 0x0c: cdrom_ok(regs, device); break; // seek to specified cylinder
757     case 0x0d: cdrom_ok(regs, device); break; // alternate disk reset
758     case 0x10: cdrom_ok(regs, device); break; // check drive ready
759     case 0x11: cdrom_ok(regs, device); break; // recalibrate
760     case 0x14: cdrom_ok(regs, device); break; // controller internal diagnostic
761     case 0x16: cdrom_ok(regs, device); break; // detect disk change
762
763     // all these functions return disk write-protected
764     case 0x03: cdrom_wp(regs, device); break; // write disk sectors
765     case 0x05: cdrom_wp(regs, device); break; // format disk track
766     case 0x43: cdrom_wp(regs, device); break; // IBM/MS extended write
767
768     default:   disk_13XX(regs, device); break;
769     }
770 }
771
772
773 /****************************************************************
774  * Entry points
775  ****************************************************************/
776
777 static u8
778 get_device(struct bregs *regs, u8 drive)
779 {
780     // basic check : device has to be defined
781     if (drive >= CONFIG_MAX_ATA_DEVICES) {
782         disk_ret(regs, DISK_RET_EPARAM);
783         return CONFIG_MAX_ATA_DEVICES;
784     }
785
786     // Get the ata channel
787     u8 device = GET_EBDA(ata.hdidmap[drive]);
788
789     // basic check : device has to be valid
790     if (device >= CONFIG_MAX_ATA_DEVICES) {
791         disk_ret(regs, DISK_RET_EPARAM);
792         return CONFIG_MAX_ATA_DEVICES;
793     }
794
795     return device;
796 }
797
798 static void
799 handle_legacy_disk(struct bregs *regs, u8 drive)
800 {
801     if (drive < 0x80) {
802         floppy_13(regs, drive);
803         return;
804     }
805
806     if (! CONFIG_ATA) {
807         // XXX - old code had other disk access method.
808         disk_ret(regs, DISK_RET_EPARAM);
809         return;
810     }
811
812     if (drive >= 0xe0) {
813         u8 device = get_device(regs, drive - 0xe0);
814         if (device >= CONFIG_MAX_ATA_DEVICES)
815             return;
816         cdrom_13(regs, device);
817         return;
818     }
819
820     u8 device = get_device(regs, drive - 0x80);
821     if (device >= CONFIG_MAX_ATA_DEVICES)
822         return;
823     disk_13(regs, device);
824 }
825
826 void VISIBLE
827 handle_40(struct bregs *regs)
828 {
829     debug_enter(regs);
830     handle_legacy_disk(regs, regs->dl);
831     debug_exit(regs);
832 }
833
834 // INT 13h Fixed Disk Services Entry Point
835 void VISIBLE
836 handle_13(struct bregs *regs)
837 {
838     //debug_enter(regs);
839     u8 drive = regs->dl;
840     // XXX
841 #if BX_ELTORITO_BOOT
842     if (regs->ah >= 0x4a || regs->ah <= 0x4d) {
843         int13_eltorito(regs);
844     } else if (cdemu_isactive() && cdrom_emulated_drive()) {
845         int13_cdemu(regs);
846     } else
847 #endif
848         handle_legacy_disk(regs, drive);
849     debug_exit(regs);
850 }
851
852 // record completion in BIOS task complete flag
853 void VISIBLE
854 handle_76(struct bregs *regs)
855 {
856     debug_isr(regs);
857     SET_BDA(floppy_harddisk_info, 0xff);
858     eoi_both_pics();
859 }