blob: f63a833f24e8a74103c54487eb0e28778229e5d7 [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 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
Kevin O'Connor9521e262008-07-04 13:04:29 -04009#include "biosvar.h" // SET_BDA
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050010#include "config.h" // CONFIG_*
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050011#include "util.h" // debug_enter
Kevin O'Connorf54c1502008-06-14 15:56:16 -040012#include "pic.h" // eoi_pic2
Kevin O'Connor9521e262008-07-04 13:04:29 -040013#include "bregs.h" // struct bregs
Kevin O'Connor53236cc2008-08-31 11:16:33 -040014#include "pci.h" // pci_bdf_to_bus
Kevin O'Connor4524bf72008-12-31 00:31:03 -050015#include "atabits.h" // ATA_CB_STAT
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050016
Kevin O'Connorb74102d2008-03-03 21:57:30 -050017
18/****************************************************************
19 * Helper functions
20 ****************************************************************/
21
Kevin O'Connor567e4e32008-04-05 11:37:51 -040022void
Kevin O'Connor05600342009-01-02 13:10:58 -050023__disk_ret(struct bregs *regs, u32 linecode, const char *fname)
Kevin O'Connor567e4e32008-04-05 11:37:51 -040024{
Kevin O'Connor05600342009-01-02 13:10:58 -050025 u8 code = linecode;
Kevin O'Connor567e4e32008-04-05 11:37:51 -040026 SET_BDA(disk_last_status, code);
27 if (code)
Kevin O'Connor05600342009-01-02 13:10:58 -050028 __set_code_fail(regs, linecode, fname);
Kevin O'Connor567e4e32008-04-05 11:37:51 -040029 else
30 set_code_success(regs);
31}
32
33static void
Kevin O'Connor05600342009-01-02 13:10:58 -050034__disk_stub(struct bregs *regs, int lineno, const char *fname)
Kevin O'Connor567e4e32008-04-05 11:37:51 -040035{
Kevin O'Connor05600342009-01-02 13:10:58 -050036 __debug_stub(regs, lineno, fname);
37 __disk_ret(regs, DISK_RET_SUCCESS | (lineno << 8), fname);
Kevin O'Connor567e4e32008-04-05 11:37:51 -040038}
39
Kevin O'Connor05600342009-01-02 13:10:58 -050040#define DISK_STUB(regs) \
41 __disk_stub((regs), __LINE__, __func__)
Kevin O'Connore43df9e2008-03-01 22:16:32 -050042
Kevin O'Connor7f343092009-01-01 18:31:11 -050043static int
44__send_disk_op(struct disk_op_s *op_p, u16 op_s)
Kevin O'Connor4524bf72008-12-31 00:31:03 -050045{
Kevin O'Connor7f343092009-01-01 18:31:11 -050046 struct disk_op_s dop;
47 memcpy_far(MAKE_FARPTR(GET_SEG(SS), &dop)
48 , MAKE_FARPTR(op_s, op_p)
49 , sizeof(dop));
50
Kevin O'Connor4524bf72008-12-31 00:31:03 -050051 dprintf(DEBUG_HDL_13, "disk_op d=%d lba=%d buf=%p count=%d cmd=%d\n"
Kevin O'Connor7f343092009-01-01 18:31:11 -050052 , dop.driveid, (u32)dop.lba, dop.far_buffer
53 , dop.count, dop.command);
Kevin O'Connor4524bf72008-12-31 00:31:03 -050054
55 irq_enable();
56
57 int status;
Kevin O'Connor7f343092009-01-01 18:31:11 -050058 if (dop.command == CMD_CDEMU_READ)
59 status = cdrom_read_512(&dop);
60 else if (dop.command == CMD_CDROM_READ)
61 status = cdrom_read(&dop);
Kevin O'Connor4524bf72008-12-31 00:31:03 -050062 else
Kevin O'Connor7f343092009-01-01 18:31:11 -050063 status = ata_cmd_data(&dop);
Kevin O'Connor4524bf72008-12-31 00:31:03 -050064
65 irq_disable();
66
67 return status;
68}
69
Kevin O'Connor7f343092009-01-01 18:31:11 -050070static int
71send_disk_op(struct disk_op_s *op)
72{
73 return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
74}
75
Kevin O'Connore43df9e2008-03-01 22:16:32 -050076static void
77basic_access(struct bregs *regs, u8 device, u16 command)
78{
Kevin O'Connor4524bf72008-12-31 00:31:03 -050079 struct disk_op_s dop;
80 dop.lba = 0;
81 dop.driveid = device;
Kevin O'Connor609da232008-12-28 23:18:57 -050082 u8 type = GET_GLOBAL(ATA.devices[device].type);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -040083 u16 nlc, nlh, nlspt;
84 if (type == ATA_TYPE_ATA) {
Kevin O'Connor609da232008-12-28 23:18:57 -050085 nlc = GET_GLOBAL(ATA.devices[device].lchs.cylinders);
86 nlh = GET_GLOBAL(ATA.devices[device].lchs.heads);
87 nlspt = GET_GLOBAL(ATA.devices[device].lchs.spt);
Kevin O'Connor4524bf72008-12-31 00:31:03 -050088 dop.command = command;
Kevin O'Connoraa2590c2008-03-22 23:13:24 -040089 } else {
90 // Must be cd emulation.
Kevin O'Connor4a16ef62008-12-31 00:09:28 -050091 u16 ebda_seg = get_ebda_seg();
92 nlc = GET_EBDA2(ebda_seg, cdemu.cylinders);
93 nlh = GET_EBDA2(ebda_seg, cdemu.heads);
94 nlspt = GET_EBDA2(ebda_seg, cdemu.spt);
Kevin O'Connor4524bf72008-12-31 00:31:03 -050095 dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) * 4;
96 dop.command = CMD_CDEMU_READ;
Kevin O'Connoraa2590c2008-03-22 23:13:24 -040097 }
Kevin O'Connore43df9e2008-03-01 22:16:32 -050098
Kevin O'Connor4524bf72008-12-31 00:31:03 -050099 dop.count = regs->al;
Kevin O'Connor0cdac0e2008-03-29 12:45:53 -0400100 u16 cylinder = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
101 u16 sector = regs->cl & 0x3f;
102 u16 head = regs->dh;
103
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500104 if (dop.count > 128 || dop.count == 0 || sector == 0) {
Kevin O'Connorac8df8c2008-05-24 23:46:33 -0400105 dprintf(1, "int13_harddisk: function %02x, parameter out of range!\n"
Kevin O'Connor0cdac0e2008-03-29 12:45:53 -0400106 , regs->ah);
107 disk_ret(regs, DISK_RET_EPARAM);
108 return;
109 }
110
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500111 // sanity check on cyl heads, sec
Kevin O'Connor1fcf1442008-03-11 19:42:41 -0400112 if (cylinder >= nlc || head >= nlh || sector > nlspt) {
Kevin O'Connorac8df8c2008-05-24 23:46:33 -0400113 dprintf(1, "int13_harddisk: function %02x, parameters out of"
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500114 " range %04x/%04x/%04x!\n"
115 , regs->ah, cylinder, head, sector);
116 disk_ret(regs, DISK_RET_EPARAM);
117 return;
118 }
119
Kevin O'Connor3a049632008-03-11 11:48:04 -0400120 if (!command) {
121 // If verify or seek
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500122 disk_ret(regs, DISK_RET_SUCCESS);
123 return;
124 }
125
Kevin O'Connor049d5a22008-03-13 19:09:49 -0400126 // translate lchs to lba
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500127 dop.lba += (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
128 + (u32)sector - 1);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -0400129
130 u16 segment = regs->es;
131 u16 offset = regs->bx;
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500132 dop.far_buffer = MAKE_FARPTR(segment, offset);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -0400133
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500134 int status = send_disk_op(&dop);
Kevin O'Connor74799df2008-03-12 20:49:07 -0400135
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500136 // Set nb of sector transferred
Kevin O'Connor609da232008-12-28 23:18:57 -0500137 regs->al = GET_EBDA(sector_count);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500138
139 if (status != 0) {
Kevin O'Connorfe42eb22008-06-21 11:38:21 -0400140 dprintf(1, "int13_harddisk: function %02x, error %d!\n"
Kevin O'Connorfad2da82008-03-22 20:37:44 -0400141 , regs->ah, status);
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500142 disk_ret(regs, DISK_RET_EBADTRACK);
Kevin O'Connor6aa673d2009-01-02 12:36:46 -0500143 return;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500144 }
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500145 disk_ret(regs, DISK_RET_SUCCESS);
146}
147
Kevin O'Connored128492008-03-11 11:14:59 -0400148static void
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500149extended_access(struct bregs *regs, u8 device, u16 command)
150{
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500151 struct disk_op_s dop;
Kevin O'Connor1bb3b5c2008-05-14 00:43:13 -0400152 // Get lba and check.
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500153 dop.lba = GET_INT13EXT(regs, lba);
154 dop.command = command;
155 dop.driveid = device;
Kevin O'Connor609da232008-12-28 23:18:57 -0500156 u8 type = GET_GLOBAL(ATA.devices[device].type);
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500157 if (type == ATA_TYPE_ATA) {
158 if (dop.lba >= GET_GLOBAL(ATA.devices[device].sectors)) {
159 dprintf(1, "int13_harddisk: function %02x. LBA out of range\n"
160 , regs->ah);
161 disk_ret(regs, DISK_RET_EPARAM);
162 return;
163 }
164 } else {
165 dop.command = CMD_CDROM_READ;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500166 }
167
Kevin O'Connor3a049632008-03-11 11:48:04 -0400168 if (!command) {
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500169 // If verify or seek
170 disk_ret(regs, DISK_RET_SUCCESS);
171 return;
172 }
173
Kevin O'Connoraa2590c2008-03-22 23:13:24 -0400174 u16 segment = GET_INT13EXT(regs, segment);
175 u16 offset = GET_INT13EXT(regs, offset);
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500176 dop.far_buffer = MAKE_FARPTR(segment, offset);
177 dop.count = GET_INT13EXT(regs, count);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -0400178
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500179 int status = send_disk_op(&dop);
Kevin O'Connor74799df2008-03-12 20:49:07 -0400180
Kevin O'Connor609da232008-12-28 23:18:57 -0500181 SET_INT13EXT(regs, count, GET_EBDA(sector_count));
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500182
183 if (status != 0) {
Kevin O'Connorfe42eb22008-06-21 11:38:21 -0400184 dprintf(1, "int13_harddisk: function %02x, error %d!\n"
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500185 , regs->ah, status);
186 disk_ret(regs, DISK_RET_EBADTRACK);
187 return;
188 }
189 disk_ret(regs, DISK_RET_SUCCESS);
190}
191
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500192
193/****************************************************************
194 * Hard Drive functions
195 ****************************************************************/
196
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500197// disk controller reset
198static void
199disk_1300(struct bregs *regs, u8 device)
200{
201 ata_reset(device);
202}
203
204// read disk status
205static void
206disk_1301(struct bregs *regs, u8 device)
207{
Kevin O'Connorfad2da82008-03-22 20:37:44 -0400208 u8 v = GET_BDA(disk_last_status);
209 regs->ah = v;
210 set_cf(regs, v);
211 // XXX - clear disk_last_status?
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500212}
213
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500214// read disk sectors
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500215static void
216disk_1302(struct bregs *regs, u8 device)
217{
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500218 basic_access(regs, device, ATA_CMD_READ_SECTORS);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500219}
220
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500221// write disk sectors
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500222static void
223disk_1303(struct bregs *regs, u8 device)
224{
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500225 basic_access(regs, device, ATA_CMD_WRITE_SECTORS);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500226}
227
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500228// verify disk sectors
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500229static void
230disk_1304(struct bregs *regs, u8 device)
231{
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500232 basic_access(regs, device, 0);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500233 // FIXME verify
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500234}
235
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500236// format disk track
237static void
238disk_1305(struct bregs *regs, u8 device)
239{
240 DISK_STUB(regs);
241}
242
243// read disk drive parameters
244static void
245disk_1308(struct bregs *regs, u8 device)
246{
247 // Get logical geometry from table
Kevin O'Connor609da232008-12-28 23:18:57 -0500248 u16 nlc = GET_GLOBAL(ATA.devices[device].lchs.cylinders);
249 u16 nlh = GET_GLOBAL(ATA.devices[device].lchs.heads);
250 u16 nlspt = GET_GLOBAL(ATA.devices[device].lchs.spt);
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500251 u16 count = GET_BDA(hdcount);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500252
253 nlc = nlc - 2; /* 0 based , last sector not used */
254 regs->al = 0;
255 regs->ch = nlc & 0xff;
256 regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
257 regs->dh = nlh - 1;
258 regs->dl = count; /* FIXME returns 0, 1, or n hard drives */
259
260 // FIXME should set ES & DI
261 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500262}
263
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500264// initialize drive parameters
265static void
266disk_1309(struct bregs *regs, u8 device)
267{
268 DISK_STUB(regs);
269}
270
271// seek to specified cylinder
272static void
273disk_130c(struct bregs *regs, u8 device)
274{
275 DISK_STUB(regs);
276}
277
278// alternate disk reset
279static void
280disk_130d(struct bregs *regs, u8 device)
281{
282 DISK_STUB(regs);
283}
284
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500285// check drive ready
286static void
287disk_1310(struct bregs *regs, u8 device)
288{
289 // should look at 40:8E also???
290
291 // Read the status from controller
Kevin O'Connor609da232008-12-28 23:18:57 -0500292 u8 status = inb(GET_GLOBAL(ATA.channels[device/2].iobase1) + ATA_CB_STAT);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500293 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY )
294 disk_ret(regs, DISK_RET_SUCCESS);
295 else
296 disk_ret(regs, DISK_RET_ENOTREADY);
297}
298
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500299// recalibrate
300static void
301disk_1311(struct bregs *regs, u8 device)
302{
303 DISK_STUB(regs);
304}
305
306// controller internal diagnostic
307static void
308disk_1314(struct bregs *regs, u8 device)
309{
310 DISK_STUB(regs);
311}
312
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500313// read disk drive size
314static void
315disk_1315(struct bregs *regs, u8 device)
316{
317 // Get logical geometry from table
Kevin O'Connor609da232008-12-28 23:18:57 -0500318 u16 nlc = GET_GLOBAL(ATA.devices[device].lchs.cylinders);
319 u16 nlh = GET_GLOBAL(ATA.devices[device].lchs.heads);
320 u16 nlspt = GET_GLOBAL(ATA.devices[device].lchs.spt);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500321
322 // Compute sector count seen by int13
323 u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
324 regs->cx = lba >> 16;
325 regs->dx = lba & 0xffff;
326
Kevin O'Connordcc7a4f2008-03-08 23:25:16 -0500327 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500328 regs->ah = 3; // hard disk accessible
329}
330
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500331// IBM/MS installation check
332static void
333disk_1341(struct bregs *regs, u8 device)
334{
335 regs->bx = 0xaa55; // install check
336 regs->cx = 0x0007; // ext disk access and edd, removable supported
337 disk_ret(regs, DISK_RET_SUCCESS);
338 regs->ah = 0x30; // EDD 3.0
339}
340
341// IBM/MS extended read
342static void
343disk_1342(struct bregs *regs, u8 device)
344{
345 extended_access(regs, device, ATA_CMD_READ_SECTORS);
346}
347
348// IBM/MS extended write
349static void
350disk_1343(struct bregs *regs, u8 device)
351{
352 extended_access(regs, device, ATA_CMD_WRITE_SECTORS);
353}
354
355// IBM/MS verify
356static void
357disk_1344(struct bregs *regs, u8 device)
358{
359 extended_access(regs, device, 0);
360}
361
362// IBM/MS lock/unlock drive
363static void
364disk_1345(struct bregs *regs, u8 device)
365{
366 // Always success for HD
367 disk_ret(regs, DISK_RET_SUCCESS);
368}
369
370// IBM/MS eject media
371static void
372disk_1346(struct bregs *regs, u8 device)
373{
374 // Volume Not Removable
375 disk_ret(regs, DISK_RET_ENOTREMOVABLE);
376}
377
378// IBM/MS extended seek
379static void
380disk_1347(struct bregs *regs, u8 device)
381{
382 extended_access(regs, device, 0);
383}
384
385// IBM/MS get drive parameters
386static void
387disk_1348(struct bregs *regs, u8 device)
388{
389 u16 size = GET_INT13DPT(regs, size);
390
391 // Buffer is too small
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400392 if (size < 26) {
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500393 disk_ret(regs, DISK_RET_EPARAM);
394 return;
395 }
396
397 // EDD 1.x
398
Kevin O'Connor609da232008-12-28 23:18:57 -0500399 u8 type = GET_GLOBAL(ATA.devices[device].type);
400 u16 npc = GET_GLOBAL(ATA.devices[device].pchs.cylinders);
401 u16 nph = GET_GLOBAL(ATA.devices[device].pchs.heads);
402 u16 npspt = GET_GLOBAL(ATA.devices[device].pchs.spt);
403 u64 lba = GET_GLOBAL(ATA.devices[device].sectors);
404 u16 blksize = GET_GLOBAL(ATA.devices[device].blksize);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500405
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400406 dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n"
407 , size, type, npc, nph, npspt, (u32)lba, blksize);
408
409 SET_INT13DPT(regs, size, 26);
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500410 if (type == ATA_TYPE_ATA) {
Kevin O'Connor1bb3b5c2008-05-14 00:43:13 -0400411 if (lba > (u64)npspt*nph*0x3fff) {
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500412 SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
413 SET_INT13DPT(regs, cylinders, 0x3fff);
414 } else {
415 SET_INT13DPT(regs, infos, 0x02); // geometry is valid
416 SET_INT13DPT(regs, cylinders, (u32)npc);
417 }
418 SET_INT13DPT(regs, heads, (u32)nph);
419 SET_INT13DPT(regs, spt, (u32)npspt);
Kevin O'Connor1bb3b5c2008-05-14 00:43:13 -0400420 SET_INT13DPT(regs, sector_count, lba);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500421 } else {
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500422 // ATAPI
423 // 0x74 = removable, media change, lockable, max values
424 SET_INT13DPT(regs, infos, 0x74);
425 SET_INT13DPT(regs, cylinders, 0xffffffff);
426 SET_INT13DPT(regs, heads, 0xffffffff);
427 SET_INT13DPT(regs, spt, 0xffffffff);
Kevin O'Connor1bb3b5c2008-05-14 00:43:13 -0400428 SET_INT13DPT(regs, sector_count, (u64)-1);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500429 }
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500430 SET_INT13DPT(regs, blksize, blksize);
431
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400432 if (size < 30) {
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500433 disk_ret(regs, DISK_RET_SUCCESS);
434 return;
435 }
436
437 // EDD 2.x
438
Kevin O'Connor08815372008-12-29 21:16:31 -0500439 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400440 SET_INT13DPT(regs, size, 30);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500441
Kevin O'Connor08815372008-12-29 21:16:31 -0500442 SET_INT13DPT(regs, dpte_segment, ebda_seg);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500443 SET_INT13DPT(regs, dpte_offset
Kevin O'Connor609da232008-12-28 23:18:57 -0500444 , offsetof(struct extended_bios_data_area_s, dpte));
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500445
446 // Fill in dpte
447 u8 channel = device / 2;
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400448 u8 slave = device % 2;
Kevin O'Connor609da232008-12-28 23:18:57 -0500449 u16 iobase1 = GET_GLOBAL(ATA.channels[channel].iobase1);
450 u16 iobase2 = GET_GLOBAL(ATA.channels[channel].iobase2);
451 u8 irq = GET_GLOBAL(ATA.channels[channel].irq);
452 u8 mode = GET_GLOBAL(ATA.devices[device].mode);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500453
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400454 u16 options = 0;
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500455 if (type == ATA_TYPE_ATA) {
Kevin O'Connor609da232008-12-28 23:18:57 -0500456 u8 translation = GET_GLOBAL(ATA.devices[device].translation);
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400457 if (translation != ATA_TRANSLATION_NONE) {
458 options |= 1<<3; // CHS translation
459 if (translation == ATA_TRANSLATION_LBA)
460 options |= 1<<9;
461 if (translation == ATA_TRANSLATION_RECHS)
462 options |= 3<<9;
463 }
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500464 } else {
465 // ATAPI
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400466 options |= 1<<5; // removable device
467 options |= 1<<6; // atapi device
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500468 }
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400469 options |= 1<<4; // lba translation
470 if (mode == ATA_MODE_PIO32)
471 options |= 1<<7;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500472
Kevin O'Connor08815372008-12-29 21:16:31 -0500473 SET_EBDA2(ebda_seg, dpte.iobase1, iobase1);
474 SET_EBDA2(ebda_seg, dpte.iobase2, iobase2 + ATA_CB_DC);
475 SET_EBDA2(ebda_seg, dpte.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)
476 | ATA_CB_DH_LBA));
477 SET_EBDA2(ebda_seg, dpte.unused, 0xcb);
478 SET_EBDA2(ebda_seg, dpte.irq, irq);
479 SET_EBDA2(ebda_seg, dpte.blkcount, 1);
480 SET_EBDA2(ebda_seg, dpte.dma, 0);
481 SET_EBDA2(ebda_seg, dpte.pio, 0);
482 SET_EBDA2(ebda_seg, dpte.options, options);
483 SET_EBDA2(ebda_seg, dpte.reserved, 0);
484 SET_EBDA2(ebda_seg, dpte.revision, 0x11);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500485
Kevin O'Connor08815372008-12-29 21:16:31 -0500486 u8 *p = MAKE_FARPTR(ebda_seg
Kevin O'Connor609da232008-12-28 23:18:57 -0500487 , offsetof(struct extended_bios_data_area_s, dpte));
Kevin O'Connor08815372008-12-29 21:16:31 -0500488 SET_EBDA2(ebda_seg, dpte.checksum, -checksum(p, 15));
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500489
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400490 if (size < 66) {
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500491 disk_ret(regs, DISK_RET_SUCCESS);
492 return;
493 }
494
495 // EDD 3.x
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500496 SET_INT13DPT(regs, key, 0xbedd);
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400497 SET_INT13DPT(regs, dpi_length, 36);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500498 SET_INT13DPT(regs, reserved1, 0);
499 SET_INT13DPT(regs, reserved2, 0);
500
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400501 SET_INT13DPT(regs, host_bus[0], 'P');
502 SET_INT13DPT(regs, host_bus[1], 'C');
503 SET_INT13DPT(regs, host_bus[2], 'I');
504 SET_INT13DPT(regs, host_bus[3], 0);
505
Kevin O'Connor609da232008-12-28 23:18:57 -0500506 u32 bdf = GET_GLOBAL(ATA.channels[channel].pci_bdf);
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400507 u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8)
508 | (pci_bdf_to_fn(bdf) << 16));
509 SET_INT13DPT(regs, iface_path, path);
510
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500511 SET_INT13DPT(regs, iface_type[0], 'A');
512 SET_INT13DPT(regs, iface_type[1], 'T');
513 SET_INT13DPT(regs, iface_type[2], 'A');
514 SET_INT13DPT(regs, iface_type[3], 0);
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400515 SET_INT13DPT(regs, iface_type[4], 0);
516 SET_INT13DPT(regs, iface_type[5], 0);
517 SET_INT13DPT(regs, iface_type[6], 0);
518 SET_INT13DPT(regs, iface_type[7], 0);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500519
Kevin O'Connor970a0322008-10-26 12:01:21 -0400520 SET_INT13DPT(regs, device_path, slave);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500521
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400522 SET_INT13DPT(regs, checksum, -checksum(MAKE_FARPTR(regs->ds, 30), 35));
523
524 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500525}
526
527// IBM/MS extended media change
528static void
529disk_1349(struct bregs *regs, u8 device)
530{
531 // Always success for HD
532 disk_ret(regs, DISK_RET_SUCCESS);
533}
534
535static void
536disk_134e01(struct bregs *regs, u8 device)
537{
538 disk_ret(regs, DISK_RET_SUCCESS);
539}
540
541static void
542disk_134e03(struct bregs *regs, u8 device)
543{
544 disk_ret(regs, DISK_RET_SUCCESS);
545}
546
547static void
548disk_134e04(struct bregs *regs, u8 device)
549{
550 disk_ret(regs, DISK_RET_SUCCESS);
551}
552
553static void
554disk_134e06(struct bregs *regs, u8 device)
555{
556 disk_ret(regs, DISK_RET_SUCCESS);
557}
558
559static void
560disk_134eXX(struct bregs *regs, u8 device)
561{
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500562 disk_ret(regs, DISK_RET_EPARAM);
563}
564
565// IBM/MS set hardware configuration
566static void
567disk_134e(struct bregs *regs, u8 device)
568{
569 switch (regs->al) {
570 case 0x01: disk_134e01(regs, device); break;
571 case 0x03: disk_134e03(regs, device); break;
572 case 0x04: disk_134e04(regs, device); break;
573 case 0x06: disk_134e06(regs, device); break;
574 default: disk_134eXX(regs, device); break;
575 }
576}
577
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500578void
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500579disk_13XX(struct bregs *regs, u8 device)
580{
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500581 disk_ret(regs, DISK_RET_EPARAM);
582}
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500583
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500584void
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500585disk_13(struct bregs *regs, u8 device)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500586{
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500587 //debug_stub(regs);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500588
589 // clear completion flag
590 SET_BDA(disk_interrupt_flag, 0);
591
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500592 switch (regs->ah) {
593 case 0x00: disk_1300(regs, device); break;
594 case 0x01: disk_1301(regs, device); break;
595 case 0x02: disk_1302(regs, device); break;
596 case 0x03: disk_1303(regs, device); break;
597 case 0x04: disk_1304(regs, device); break;
598 case 0x05: disk_1305(regs, device); break;
599 case 0x08: disk_1308(regs, device); break;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500600 case 0x09: disk_1309(regs, device); break;
601 case 0x0c: disk_130c(regs, device); break;
602 case 0x0d: disk_130d(regs, device); break;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500603 case 0x10: disk_1310(regs, device); break;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500604 case 0x11: disk_1311(regs, device); break;
605 case 0x14: disk_1314(regs, device); break;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500606 case 0x15: disk_1315(regs, device); break;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500607 case 0x41: disk_1341(regs, device); break;
608 case 0x42: disk_1342(regs, device); break;
609 case 0x43: disk_1343(regs, device); break;
610 case 0x44: disk_1344(regs, device); break;
611 case 0x45: disk_1345(regs, device); break;
612 case 0x46: disk_1346(regs, device); break;
613 case 0x47: disk_1347(regs, device); break;
614 case 0x48: disk_1348(regs, device); break;
615 case 0x49: disk_1349(regs, device); break;
616 case 0x4e: disk_134e(regs, device); break;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500617 default: disk_13XX(regs, device); break;
618 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500619}
620
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500621
622/****************************************************************
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500623 * Entry points
624 ****************************************************************/
625
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500626static u8
Kevin O'Connor180a9592008-03-04 22:50:53 -0500627get_device(struct bregs *regs, u8 iscd, u8 drive)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500628{
629 // basic check : device has to be defined
630 if (drive >= CONFIG_MAX_ATA_DEVICES) {
631 disk_ret(regs, DISK_RET_EPARAM);
632 return CONFIG_MAX_ATA_DEVICES;
633 }
634
635 // Get the ata channel
Kevin O'Connor609da232008-12-28 23:18:57 -0500636 u8 device = GET_GLOBAL(ATA.idmap[iscd][drive]);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500637
638 // basic check : device has to be valid
639 if (device >= CONFIG_MAX_ATA_DEVICES) {
640 disk_ret(regs, DISK_RET_EPARAM);
641 return CONFIG_MAX_ATA_DEVICES;
642 }
643
644 return device;
645}
646
647static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500648handle_legacy_disk(struct bregs *regs, u8 drive)
649{
650 if (drive < 0x80) {
651 floppy_13(regs, drive);
652 return;
653 }
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500654
655 if (! CONFIG_ATA) {
656 // XXX - old code had other disk access method.
657 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500658 return;
659 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500660
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500661 if (drive >= 0xe0) {
Kevin O'Connor180a9592008-03-04 22:50:53 -0500662 u8 device = get_device(regs, 1, drive - 0xe0);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500663 if (device >= CONFIG_MAX_ATA_DEVICES)
664 return;
665 cdrom_13(regs, device);
666 return;
667 }
668
Kevin O'Connor180a9592008-03-04 22:50:53 -0500669 u8 device = get_device(regs, 0, drive - 0x80);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500670 if (device >= CONFIG_MAX_ATA_DEVICES)
671 return;
672 disk_13(regs, device);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500673}
674
Kevin O'Connor19786762008-03-05 21:09:59 -0500675void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500676handle_40(struct bregs *regs)
677{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400678 debug_enter(regs, DEBUG_HDL_40);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500679 handle_legacy_disk(regs, regs->dl);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500680}
681
682// INT 13h Fixed Disk Services Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500683void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500684handle_13(struct bregs *regs)
685{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400686 debug_enter(regs, DEBUG_HDL_13);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500687 u8 drive = regs->dl;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500688
Kevin O'Connordfa16502008-03-22 20:13:08 -0400689 if (CONFIG_CDROM_EMU) {
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500690 if (regs->ah == 0x4b) {
691 cdemu_134b(regs);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400692 return;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500693 }
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500694 u16 ebda_seg = get_ebda_seg();
695 if (GET_EBDA2(ebda_seg, cdemu.active)) {
696 if (drive == GET_EBDA2(ebda_seg, cdemu.emulated_drive)) {
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500697 cdemu_13(regs);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400698 return;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500699 }
Kevin O'Connor180a9592008-03-04 22:50:53 -0500700 if (drive < 0xe0)
701 drive--;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500702 }
703 }
704 handle_legacy_disk(regs, drive);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500705}
706
707// record completion in BIOS task complete flag
Kevin O'Connor19786762008-03-05 21:09:59 -0500708void VISIBLE16
Kevin O'Connored128492008-03-11 11:14:59 -0400709handle_76()
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500710{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400711 debug_isr(DEBUG_ISR_76);
Kevin O'Connored128492008-03-11 11:14:59 -0400712 SET_BDA(disk_interrupt_flag, 0xff);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400713 eoi_pic2();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500714}