blob: 1011b39291ed168c6301c0f703dc541b449c5c50 [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//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "biosvar.h" // SET_BDA
Kevin O'Connor9521e262008-07-04 13:04:29 -04009#include "bregs.h" // struct bregs
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "config.h" // CONFIG_*
Kevin O'Connor5d369d82013-09-02 20:48:46 -040011#include "hw/ata.h" // ATA_CB_DC
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040012#include "hw/pci.h" // pci_bdf_to_bus
13#include "hw/pic.h" // pic_eoi2
14#include "output.h" // debug_enter
Kevin O'Connor3df600b2013-09-14 19:28:55 -040015#include "stacks.h" // call16_int
Kevin O'Connor135f3f62013-09-14 23:57:26 -040016#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040017#include "string.h" // memset
Kevin O'Connor135f3f62013-09-14 23:57:26 -040018#include "util.h" // CDRom_locks
Kevin O'Connor3491e8b2008-02-29 00:22:27 -050019
Kevin O'Connorb74102d2008-03-03 21:57:30 -050020
21/****************************************************************
22 * Helper functions
23 ****************************************************************/
24
Kevin O'Connor567e4e32008-04-05 11:37:51 -040025static void
Kevin O'Connor05600342009-01-02 13:10:58 -050026__disk_stub(struct bregs *regs, int lineno, const char *fname)
Kevin O'Connor567e4e32008-04-05 11:37:51 -040027{
Kevin O'Connordfefeb52009-12-13 13:04:17 -050028 __warn_unimplemented(regs, lineno, fname);
Kevin O'Connor05600342009-01-02 13:10:58 -050029 __disk_ret(regs, DISK_RET_SUCCESS | (lineno << 8), fname);
Kevin O'Connor567e4e32008-04-05 11:37:51 -040030}
31
Kevin O'Connor05600342009-01-02 13:10:58 -050032#define DISK_STUB(regs) \
33 __disk_stub((regs), __LINE__, __func__)
Kevin O'Connore43df9e2008-03-01 22:16:32 -050034
Kevin O'Connor5c99b6c2009-12-30 12:36:22 -050035// Get the cylinders/heads/sectors for the given drive.
Kevin O'Connor48365582012-05-27 10:45:32 -040036static struct chs_s
Kevin O'Connor1902c942013-10-26 11:48:06 -040037getLCHS(struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -050038{
Kevin O'Connor48365582012-05-27 10:45:32 -040039 struct chs_s res = { };
Kevin O'Connor1902c942013-10-26 11:48:06 -040040 if (CONFIG_CDROM_EMU && drive_gf == GET_GLOBAL(cdemu_drive_gf)) {
Kevin O'Connord3140832012-05-13 22:46:12 -040041 // Emulated drive - get info from CDEmu. (It's not possible to
Kevin O'Connor36c93a52009-09-12 19:35:04 -040042 // populate the geometry directly in the driveid because the
43 // geometry is only known after the bios segment is made
44 // read-only).
Kevin O'Connor8ab9a342013-09-28 23:34:49 -040045 res.cylinder = GET_LOW(CDEmu.lchs.cylinder);
46 res.head = GET_LOW(CDEmu.lchs.head);
47 res.sector = GET_LOW(CDEmu.lchs.sector);
Kevin O'Connor48365582012-05-27 10:45:32 -040048 return res;
Kevin O'Connor0cdac0e2008-03-29 12:45:53 -040049 }
Kevin O'Connor1902c942013-10-26 11:48:06 -040050 res.cylinder = GET_GLOBALFLAT(drive_gf->lchs.cylinder);
51 res.head = GET_GLOBALFLAT(drive_gf->lchs.head);
52 res.sector = GET_GLOBALFLAT(drive_gf->lchs.sector);
Kevin O'Connor48365582012-05-27 10:45:32 -040053 return res;
Kevin O'Connor1625a752009-08-09 13:08:21 -040054}
55
56// Perform read/write/verify using old-style chs accesses
Kevin O'Connorecf9b7d2012-03-25 10:21:27 -040057static void noinline
Kevin O'Connor1902c942013-10-26 11:48:06 -040058basic_access(struct bregs *regs, struct drive_s *drive_gf, u16 command)
Kevin O'Connor1625a752009-08-09 13:08:21 -040059{
60 struct disk_op_s dop;
Kevin O'Connor1902c942013-10-26 11:48:06 -040061 dop.drive_gf = drive_gf;
Kevin O'Connor1625a752009-08-09 13:08:21 -040062 dop.command = command;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040063
64 u8 count = regs->al;
65 u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300);
66 u16 sector = regs->cl & 0x3f;
67 u16 head = regs->dh;
68
69 if (count > 128 || count == 0 || sector == 0) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050070 warn_invalid(regs);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040071 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connor1625a752009-08-09 13:08:21 -040072 return;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040073 }
74 dop.count = count;
75
Kevin O'Connor1902c942013-10-26 11:48:06 -040076 struct chs_s chs = getLCHS(drive_gf);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -040077 u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040078
79 // sanity check on cyl heads, sec
Kevin O'Connor8ab9a342013-09-28 23:34:49 -040080 if (cylinder >= nlc || head >= nlh || sector > nls) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050081 warn_invalid(regs);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040082 disk_ret(regs, DISK_RET_EPARAM);
83 return;
84 }
85
86 // translate lchs to lba
Kevin O'Connor8ab9a342013-09-28 23:34:49 -040087 dop.lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nls)
Kevin O'Connor36c93a52009-09-12 19:35:04 -040088 + (u32)sector - 1);
89
Kevin O'Connorb68ac712009-08-09 17:25:19 -040090 dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -040091
Kevin O'Connor4524bf72008-12-31 00:31:03 -050092 int status = send_disk_op(&dop);
Kevin O'Connor74799df2008-03-12 20:49:07 -040093
Kevin O'Connor1625a752009-08-09 13:08:21 -040094 regs->al = dop.count;
Kevin O'Connore43df9e2008-03-01 22:16:32 -050095
Kevin O'Connor126eac62009-08-16 13:32:24 -040096 disk_ret(regs, status);
Kevin O'Connor941d3e42008-03-04 19:45:04 -050097}
98
Kevin O'Connor1625a752009-08-09 13:08:21 -040099// Perform read/write/verify using new-style "int13ext" accesses.
Kevin O'Connorecf9b7d2012-03-25 10:21:27 -0400100static void noinline
Kevin O'Connor1902c942013-10-26 11:48:06 -0400101extended_access(struct bregs *regs, struct drive_s *drive_gf, u16 command)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500102{
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500103 struct disk_op_s dop;
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400104 struct int13ext_s *param_far = (struct int13ext_s*)(regs->si+0);
Kevin O'Connor1bb3b5c2008-05-14 00:43:13 -0400105 // Get lba and check.
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400106 dop.lba = GET_FARVAR(regs->ds, param_far->lba);
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500107 dop.command = command;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400108 dop.drive_gf = drive_gf;
109 if (dop.lba >= GET_GLOBALFLAT(drive_gf->sectors)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500110 warn_invalid(regs);
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400111 disk_ret(regs, DISK_RET_EPARAM);
112 return;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500113 }
114
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400115 dop.buf_fl = SEGOFF_TO_FLATPTR(GET_FARVAR(regs->ds, param_far->data));
116 dop.count = GET_FARVAR(regs->ds, param_far->count);
Kevin O'Connorb50488b2012-02-11 09:38:44 -0500117 if (! dop.count) {
118 // Nothing to do.
119 disk_ret(regs, DISK_RET_SUCCESS);
120 return;
121 }
Kevin O'Connoraa2590c2008-03-22 23:13:24 -0400122
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500123 int status = send_disk_op(&dop);
Kevin O'Connor74799df2008-03-12 20:49:07 -0400124
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400125 SET_FARVAR(regs->ds, param_far->count, dop.count);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500126
Kevin O'Connor126eac62009-08-16 13:32:24 -0400127 disk_ret(regs, status);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500128}
129
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500130
131/****************************************************************
132 * Hard Drive functions
133 ****************************************************************/
134
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500135// disk controller reset
136static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400137disk_1300(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500138{
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400139 struct disk_op_s dop;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400140 dop.drive_gf = drive_gf;
Kevin O'Connor3f6c2782009-08-09 19:17:11 -0400141 dop.command = CMD_RESET;
Kevin O'Connor126eac62009-08-16 13:32:24 -0400142 int status = send_disk_op(&dop);
143 disk_ret(regs, status);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500144}
145
146// read disk status
147static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400148disk_1301(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500149{
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400150 u8 v;
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400151 if (regs->dl < EXTSTART_HD)
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400152 // Floppy
153 v = GET_BDA(floppy_last_status);
154 else
155 v = GET_BDA(disk_last_status);
Kevin O'Connorfad2da82008-03-22 20:37:44 -0400156 regs->ah = v;
157 set_cf(regs, v);
158 // XXX - clear disk_last_status?
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500159}
160
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500161// read disk sectors
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500162static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400163disk_1302(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500164{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400165 basic_access(regs, drive_gf, CMD_READ);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500166}
167
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500168// write disk sectors
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500169static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400170disk_1303(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500171{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400172 basic_access(regs, drive_gf, CMD_WRITE);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500173}
174
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500175// verify disk sectors
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500176static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400177disk_1304(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500178{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400179 basic_access(regs, drive_gf, CMD_VERIFY);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500180}
181
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500182// format disk track
Kevin O'Connorecf9b7d2012-03-25 10:21:27 -0400183static void noinline
Kevin O'Connor1902c942013-10-26 11:48:06 -0400184disk_1305(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500185{
Kevin O'Connor4d9d4002009-11-25 19:35:01 -0500186 debug_stub(regs);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400187
Kevin O'Connor1902c942013-10-26 11:48:06 -0400188 struct chs_s chs = getLCHS(drive_gf);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400189 u16 nlh=chs.head, nls=chs.sector;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400190
191 u8 num_sectors = regs->al;
192 u8 head = regs->dh;
193
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400194 if (head >= nlh || num_sectors == 0 || num_sectors > nls) {
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400195 disk_ret(regs, DISK_RET_EPARAM);
196 return;
197 }
198
199 struct disk_op_s dop;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400200 dop.drive_gf = drive_gf;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400201 dop.command = CMD_FORMAT;
202 dop.lba = head;
203 dop.count = num_sectors;
204 dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
205 int status = send_disk_op(&dop);
206 disk_ret(regs, status);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500207}
208
209// read disk drive parameters
Kevin O'Connorecf9b7d2012-03-25 10:21:27 -0400210static void noinline
Kevin O'Connor1902c942013-10-26 11:48:06 -0400211disk_1308(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500212{
213 // Get logical geometry from table
Kevin O'Connor1902c942013-10-26 11:48:06 -0400214 struct chs_s chs = getLCHS(drive_gf);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400215 u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400216 nlc--;
217 nlh--;
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400218 u8 count;
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400219 if (regs->dl < EXTSTART_HD) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400220 // Floppy
Kevin O'Connora0842f82010-12-29 11:05:46 -0500221 count = GET_GLOBAL(FloppyCount);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500222
Kevin O'Connor1902c942013-10-26 11:48:06 -0400223 if (CONFIG_CDROM_EMU && drive_gf == GET_GLOBAL(cdemu_drive_gf))
Kevin O'Connord3140832012-05-13 22:46:12 -0400224 regs->bx = GET_LOW(CDEmu.media) * 2;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400225 else
Kevin O'Connor1902c942013-10-26 11:48:06 -0400226 regs->bx = GET_GLOBALFLAT(drive_gf->floppy_type);
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400227
228 // set es & di to point to 11 byte diskette param table in ROM
229 regs->es = SEG_BIOS;
230 regs->di = (u32)&diskette_param_table2;
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400231 } else if (regs->dl < EXTSTART_CD) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400232 // Hard drive
233 count = GET_BDA(hdcount);
234 nlc--; // last sector reserved
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400235 } else {
236 // Not supported on CDROM
237 disk_ret(regs, DISK_RET_EPARAM);
238 return;
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400239 }
240
Kevin O'Connord3140832012-05-13 22:46:12 -0400241 if (CONFIG_CDROM_EMU && GET_LOW(CDEmu.active)) {
242 u8 emudrive = GET_LOW(CDEmu.emulated_extdrive);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400243 if (((emudrive ^ regs->dl) & 0x80) == 0)
244 // Note extra drive due to emulation.
245 count++;
246 if (regs->dl < EXTSTART_HD && count > 2)
247 // Max of two floppy drives.
248 count = 2;
249 }
250
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500251 regs->al = 0;
252 regs->ch = nlc & 0xff;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400253 regs->cl = ((nlc >> 2) & 0xc0) | (nls & 0x3f);
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400254 regs->dh = nlh;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500255
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500256 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400257 regs->dl = count;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500258}
259
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500260// initialize drive parameters
261static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400262disk_1309(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500263{
264 DISK_STUB(regs);
265}
266
267// seek to specified cylinder
268static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400269disk_130c(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500270{
271 DISK_STUB(regs);
272}
273
274// alternate disk reset
275static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400276disk_130d(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500277{
278 DISK_STUB(regs);
279}
280
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500281// check drive ready
282static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400283disk_1310(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500284{
285 // should look at 40:8E also???
286
Kevin O'Connor42337662009-08-10 00:06:37 -0400287 struct disk_op_s dop;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400288 dop.drive_gf = drive_gf;
Kevin O'Connor42337662009-08-10 00:06:37 -0400289 dop.command = CMD_ISREADY;
290 int status = send_disk_op(&dop);
Kevin O'Connor126eac62009-08-16 13:32:24 -0400291 disk_ret(regs, status);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500292}
293
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500294// recalibrate
295static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400296disk_1311(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500297{
298 DISK_STUB(regs);
299}
300
301// controller internal diagnostic
302static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400303disk_1314(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500304{
305 DISK_STUB(regs);
306}
307
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500308// read disk drive size
Kevin O'Connorecf9b7d2012-03-25 10:21:27 -0400309static void noinline
Kevin O'Connor1902c942013-10-26 11:48:06 -0400310disk_1315(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500311{
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400312 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400313 if (regs->dl < EXTSTART_HD || regs->dl >= EXTSTART_CD) {
314 // Floppy or cdrom
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400315 regs->ah = 1;
316 return;
317 }
318 // Hard drive
319
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500320 // Get logical geometry from table
Kevin O'Connor1902c942013-10-26 11:48:06 -0400321 struct chs_s chs = getLCHS(drive_gf);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400322 u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500323
324 // Compute sector count seen by int13
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400325 u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nls;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500326 regs->cx = lba >> 16;
327 regs->dx = lba & 0xffff;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500328 regs->ah = 3; // hard disk accessible
329}
330
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400331static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400332disk_1316(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400333{
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400334 if (regs->dl >= EXTSTART_HD) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400335 // Hard drive
336 disk_ret(regs, DISK_RET_EPARAM);
337 return;
338 }
339 disk_ret(regs, DISK_RET_ECHANGED);
340}
341
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500342// IBM/MS installation check
343static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400344disk_1341(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500345{
346 regs->bx = 0xaa55; // install check
347 regs->cx = 0x0007; // ext disk access and edd, removable supported
348 disk_ret(regs, DISK_RET_SUCCESS);
349 regs->ah = 0x30; // EDD 3.0
350}
351
352// IBM/MS extended read
353static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400354disk_1342(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500355{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400356 extended_access(regs, drive_gf, CMD_READ);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500357}
358
359// IBM/MS extended write
360static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400361disk_1343(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500362{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400363 extended_access(regs, drive_gf, CMD_WRITE);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500364}
365
366// IBM/MS verify
367static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400368disk_1344(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500369{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400370 extended_access(regs, drive_gf, CMD_VERIFY);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500371}
372
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400373// lock
374static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400375disk_134500(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400376{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400377 int cdid = regs->dl - EXTSTART_CD;
Kevin O'Connord3140832012-05-13 22:46:12 -0400378 u8 locks = GET_LOW(CDRom_locks[cdid]);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400379 if (locks == 0xff) {
380 regs->al = 1;
381 disk_ret(regs, DISK_RET_ETOOMANYLOCKS);
382 return;
383 }
Kevin O'Connord3140832012-05-13 22:46:12 -0400384 SET_LOW(CDRom_locks[cdid], locks + 1);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400385 regs->al = 1;
386 disk_ret(regs, DISK_RET_SUCCESS);
387}
388
389// unlock
390static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400391disk_134501(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400392{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400393 int cdid = regs->dl - EXTSTART_CD;
Kevin O'Connord3140832012-05-13 22:46:12 -0400394 u8 locks = GET_LOW(CDRom_locks[cdid]);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400395 if (locks == 0x00) {
396 regs->al = 0;
397 disk_ret(regs, DISK_RET_ENOTLOCKED);
398 return;
399 }
400 locks--;
Kevin O'Connord3140832012-05-13 22:46:12 -0400401 SET_LOW(CDRom_locks[cdid], locks);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400402 regs->al = (locks ? 1 : 0);
403 disk_ret(regs, DISK_RET_SUCCESS);
404}
405
406// status
407static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400408disk_134502(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400409{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400410 int cdid = regs->dl - EXTSTART_CD;
Kevin O'Connord3140832012-05-13 22:46:12 -0400411 u8 locks = GET_LOW(CDRom_locks[cdid]);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400412 regs->al = (locks ? 1 : 0);
413 disk_ret(regs, DISK_RET_SUCCESS);
414}
415
416static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400417disk_1345XX(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400418{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500419 disk_ret_unimplemented(regs, DISK_RET_EPARAM);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400420}
421
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500422// IBM/MS lock/unlock drive
423static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400424disk_1345(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500425{
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400426 if (regs->dl < EXTSTART_CD) {
427 // Always success for HD
428 disk_ret(regs, DISK_RET_SUCCESS);
429 return;
430 }
431
432 switch (regs->al) {
Kevin O'Connor1902c942013-10-26 11:48:06 -0400433 case 0x00: disk_134500(regs, drive_gf); break;
434 case 0x01: disk_134501(regs, drive_gf); break;
435 case 0x02: disk_134502(regs, drive_gf); break;
436 default: disk_1345XX(regs, drive_gf); break;
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400437 }
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500438}
439
440// IBM/MS eject media
Kevin O'Connorecf9b7d2012-03-25 10:21:27 -0400441static void noinline
Kevin O'Connor1902c942013-10-26 11:48:06 -0400442disk_1346(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500443{
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400444 if (regs->dl < EXTSTART_CD) {
445 // Volume Not Removable
446 disk_ret(regs, DISK_RET_ENOTREMOVABLE);
447 return;
448 }
449
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400450 int cdid = regs->dl - EXTSTART_CD;
Kevin O'Connord3140832012-05-13 22:46:12 -0400451 u8 locks = GET_LOW(CDRom_locks[cdid]);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400452 if (locks != 0) {
453 disk_ret(regs, DISK_RET_ELOCKED);
454 return;
455 }
456
457 // FIXME should handle 0x31 no media in device
458 // FIXME should handle 0xb5 valid request failed
459
460 // Call removable media eject
461 struct bregs br;
462 memset(&br, 0, sizeof(br));
463 br.ah = 0x52;
Kevin O'Connor74152702010-03-20 17:53:40 -0400464 br.dl = regs->dl;
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400465 call16_int(0x15, &br);
466
467 if (br.ah || br.flags & F_CF) {
468 disk_ret(regs, DISK_RET_ELOCKED);
469 return;
470 }
471 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500472}
473
474// IBM/MS extended seek
475static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400476disk_1347(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500477{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400478 extended_access(regs, drive_gf, CMD_SEEK);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500479}
480
481// IBM/MS get drive parameters
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400482static void noinline
Kevin O'Connor1902c942013-10-26 11:48:06 -0400483disk_1348(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500484{
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400485 u16 seg = regs->ds;
486 struct int13dpt_s *param_far = (struct int13dpt_s*)(regs->si+0);
487 u16 size = GET_FARVAR(seg, param_far->size);
Gleb Natapovf5154e22011-01-10 10:50:27 +0200488 u16 t13 = size == 74;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500489
490 // Buffer is too small
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400491 if (size < 26) {
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500492 disk_ret(regs, DISK_RET_EPARAM);
493 return;
494 }
495
496 // EDD 1.x
497
Kevin O'Connor1902c942013-10-26 11:48:06 -0400498 u8 type = GET_GLOBALFLAT(drive_gf->type);
499 u16 npc = GET_GLOBALFLAT(drive_gf->pchs.cylinder);
500 u16 nph = GET_GLOBALFLAT(drive_gf->pchs.head);
501 u16 nps = GET_GLOBALFLAT(drive_gf->pchs.sector);
502 u64 lba = GET_GLOBALFLAT(drive_gf->sectors);
503 u16 blksize = GET_GLOBALFLAT(drive_gf->blksize);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500504
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400505 dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n"
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400506 , size, type, npc, nph, nps, (u32)lba, blksize);
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400507
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400508 SET_FARVAR(seg, param_far->size, 26);
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400509 if (type == DTYPE_ATA_ATAPI) {
Kevin O'Connorb1144362009-08-11 20:43:38 -0400510 // 0x74 = removable, media change, lockable, max values
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400511 SET_FARVAR(seg, param_far->infos, 0x74);
512 SET_FARVAR(seg, param_far->cylinders, 0xffffffff);
513 SET_FARVAR(seg, param_far->heads, 0xffffffff);
514 SET_FARVAR(seg, param_far->spt, 0xffffffff);
515 SET_FARVAR(seg, param_far->sector_count, (u64)-1);
Kevin O'Connorb1144362009-08-11 20:43:38 -0400516 } else {
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400517 if (lba > (u64)nps*nph*0x3fff) {
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400518 SET_FARVAR(seg, param_far->infos, 0x00); // geometry is invalid
519 SET_FARVAR(seg, param_far->cylinders, 0x3fff);
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500520 } else {
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400521 SET_FARVAR(seg, param_far->infos, 0x02); // geometry is valid
522 SET_FARVAR(seg, param_far->cylinders, (u32)npc);
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500523 }
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400524 SET_FARVAR(seg, param_far->heads, (u32)nph);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400525 SET_FARVAR(seg, param_far->spt, (u32)nps);
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400526 SET_FARVAR(seg, param_far->sector_count, lba);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500527 }
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400528 SET_FARVAR(seg, param_far->blksize, blksize);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500529
Gleb Natapovf77e1792010-12-23 11:29:35 +0200530 if (size < 30 ||
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400531 (type != DTYPE_ATA && type != DTYPE_ATA_ATAPI &&
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100532 type != DTYPE_VIRTIO_BLK && type != DTYPE_VIRTIO_SCSI)) {
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500533 disk_ret(regs, DISK_RET_SUCCESS);
534 return;
535 }
536
537 // EDD 2.x
538
Gleb Natapovf77e1792010-12-23 11:29:35 +0200539 int bdf;
Gleb Natapovf5154e22011-01-10 10:50:27 +0200540 u16 iobase1 = 0;
541 u64 device_path = 0;
542 u8 channel = 0;
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400543 SET_FARVAR(seg, param_far->size, 30);
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400544 if (type == DTYPE_ATA || type == DTYPE_ATA_ATAPI) {
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400545 SET_FARVAR(seg, param_far->dpte, SEGOFF(SEG_LOW, (u32)&DefaultDPTE));
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500546
Gleb Natapovf77e1792010-12-23 11:29:35 +0200547 // Fill in dpte
Kevin O'Connor1902c942013-10-26 11:48:06 -0400548 struct atadrive_s *adrive_gf = container_of(
549 drive_gf, struct atadrive_s, drive);
550 struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
551 u8 slave = GET_GLOBALFLAT(adrive_gf->slave);
Gleb Natapovf77e1792010-12-23 11:29:35 +0200552 u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
553 u8 irq = GET_GLOBALFLAT(chan_gf->irq);
554 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
555 bdf = GET_GLOBALFLAT(chan_gf->pci_bdf);
556 device_path = slave;
Gleb Natapovf5154e22011-01-10 10:50:27 +0200557 channel = GET_GLOBALFLAT(chan_gf->chanid);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500558
Gleb Natapovf77e1792010-12-23 11:29:35 +0200559 u16 options = 0;
560 if (type == DTYPE_ATA) {
Kevin O'Connor1902c942013-10-26 11:48:06 -0400561 u8 translation = GET_GLOBALFLAT(drive_gf->translation);
Gleb Natapovf77e1792010-12-23 11:29:35 +0200562 if (translation != TRANSLATION_NONE) {
563 options |= 1<<3; // CHS translation
564 if (translation == TRANSLATION_LBA)
565 options |= 1<<9;
566 if (translation == TRANSLATION_RECHS)
567 options |= 3<<9;
568 }
569 } else {
570 // ATAPI
571 options |= 1<<5; // removable device
572 options |= 1<<6; // atapi device
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400573 }
Gleb Natapovf77e1792010-12-23 11:29:35 +0200574 options |= 1<<4; // lba translation
575 if (CONFIG_ATA_PIO32)
576 options |= 1<<7;
577
Kevin O'Connord3140832012-05-13 22:46:12 -0400578 SET_LOW(DefaultDPTE.iobase1, iobase1);
579 SET_LOW(DefaultDPTE.iobase2, iobase2 + ATA_CB_DC);
580 SET_LOW(DefaultDPTE.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)
581 | ATA_CB_DH_LBA));
582 SET_LOW(DefaultDPTE.unused, 0xcb);
583 SET_LOW(DefaultDPTE.irq, irq);
584 SET_LOW(DefaultDPTE.blkcount, 1);
585 SET_LOW(DefaultDPTE.dma, 0);
586 SET_LOW(DefaultDPTE.pio, 0);
587 SET_LOW(DefaultDPTE.options, options);
588 SET_LOW(DefaultDPTE.reserved, 0);
589 SET_LOW(DefaultDPTE.revision, 0x11);
Gleb Natapovf77e1792010-12-23 11:29:35 +0200590
Kevin O'Connord3140832012-05-13 22:46:12 -0400591 u8 sum = checksum_far(SEG_LOW, &DefaultDPTE, 15);
592 SET_LOW(DefaultDPTE.checksum, -sum);
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500593 } else {
Bruce Rogers9474a352012-10-25 13:48:16 -0600594 SET_FARVAR(seg, param_far->dpte.segoff, 0xffffffff);
Kevin O'Connor1902c942013-10-26 11:48:06 -0400595 bdf = GET_GLOBALFLAT(drive_gf->cntl_id);
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500596 }
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500597
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400598 if (size < 66) {
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500599 disk_ret(regs, DISK_RET_SUCCESS);
600 return;
601 }
602
603 // EDD 3.x
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400604 SET_FARVAR(seg, param_far->key, 0xbedd);
605 SET_FARVAR(seg, param_far->dpi_length, t13 ? 44 : 36);
606 SET_FARVAR(seg, param_far->reserved1, 0);
607 SET_FARVAR(seg, param_far->reserved2, 0);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500608
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500609 if (bdf != -1) {
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400610 SET_FARVAR(seg, param_far->host_bus[0], 'P');
611 SET_FARVAR(seg, param_far->host_bus[1], 'C');
612 SET_FARVAR(seg, param_far->host_bus[2], 'I');
613 SET_FARVAR(seg, param_far->host_bus[3], ' ');
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400614
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500615 u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8)
616 | (pci_bdf_to_fn(bdf) << 16));
Gleb Natapovf5154e22011-01-10 10:50:27 +0200617 if (t13)
618 path |= channel << 24;
619
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400620 SET_FARVAR(seg, param_far->iface_path, path);
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500621 } else {
622 // ISA
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400623 SET_FARVAR(seg, param_far->host_bus[0], 'I');
624 SET_FARVAR(seg, param_far->host_bus[1], 'S');
625 SET_FARVAR(seg, param_far->host_bus[2], 'A');
626 SET_FARVAR(seg, param_far->host_bus[3], ' ');
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500627
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400628 SET_FARVAR(seg, param_far->iface_path, iobase1);
Kevin O'Connor4ccb2312009-12-05 11:25:09 -0500629 }
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400630
Paolo Bonzini0e7fb5f2011-11-16 13:02:55 +0100631 if (type != DTYPE_VIRTIO_BLK) {
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400632 SET_FARVAR(seg, param_far->iface_type[0], 'A');
633 SET_FARVAR(seg, param_far->iface_type[1], 'T');
634 SET_FARVAR(seg, param_far->iface_type[2], 'A');
635 SET_FARVAR(seg, param_far->iface_type[3], ' ');
Gleb Natapovf77e1792010-12-23 11:29:35 +0200636 } else {
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400637 SET_FARVAR(seg, param_far->iface_type[0], 'S');
638 SET_FARVAR(seg, param_far->iface_type[1], 'C');
639 SET_FARVAR(seg, param_far->iface_type[2], 'S');
640 SET_FARVAR(seg, param_far->iface_type[3], 'I');
Gleb Natapovf77e1792010-12-23 11:29:35 +0200641 }
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400642 SET_FARVAR(seg, param_far->iface_type[4], ' ');
643 SET_FARVAR(seg, param_far->iface_type[5], ' ');
644 SET_FARVAR(seg, param_far->iface_type[6], ' ');
645 SET_FARVAR(seg, param_far->iface_type[7], ' ');
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500646
Gleb Natapovf5154e22011-01-10 10:50:27 +0200647 if (t13) {
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400648 SET_FARVAR(seg, param_far->t13.device_path[0], device_path);
649 SET_FARVAR(seg, param_far->t13.device_path[1], 0);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500650
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400651 SET_FARVAR(seg, param_far->t13.checksum
652 , -checksum_far(seg, (void*)param_far+30, 43));
Gleb Natapovf5154e22011-01-10 10:50:27 +0200653 } else {
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400654 SET_FARVAR(seg, param_far->phoenix.device_path, device_path);
Gleb Natapovf5154e22011-01-10 10:50:27 +0200655
Kevin O'Connorcd8dcbe2012-05-24 22:52:39 -0400656 SET_FARVAR(seg, param_far->phoenix.checksum
657 , -checksum_far(seg, (void*)param_far+30, 35));
Gleb Natapovf5154e22011-01-10 10:50:27 +0200658 }
Kevin O'Connor53236cc2008-08-31 11:16:33 -0400659
660 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500661}
662
663// IBM/MS extended media change
664static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400665disk_1349(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500666{
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400667 if (regs->dl < EXTSTART_CD) {
668 // Always success for HD
669 disk_ret(regs, DISK_RET_SUCCESS);
670 return;
671 }
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500672 set_invalid(regs);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400673 // always send changed ??
674 regs->ah = DISK_RET_ECHANGED;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500675}
676
677static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400678disk_134e01(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500679{
680 disk_ret(regs, DISK_RET_SUCCESS);
681}
682
683static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400684disk_134e03(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500685{
686 disk_ret(regs, DISK_RET_SUCCESS);
687}
688
689static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400690disk_134e04(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500691{
692 disk_ret(regs, DISK_RET_SUCCESS);
693}
694
695static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400696disk_134e06(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500697{
698 disk_ret(regs, DISK_RET_SUCCESS);
699}
700
701static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400702disk_134eXX(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500703{
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500704 disk_ret(regs, DISK_RET_EPARAM);
705}
706
707// IBM/MS set hardware configuration
708static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400709disk_134e(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500710{
711 switch (regs->al) {
Kevin O'Connor1902c942013-10-26 11:48:06 -0400712 case 0x01: disk_134e01(regs, drive_gf); break;
713 case 0x03: disk_134e03(regs, drive_gf); break;
714 case 0x04: disk_134e04(regs, drive_gf); break;
715 case 0x06: disk_134e06(regs, drive_gf); break;
716 default: disk_134eXX(regs, drive_gf); break;
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500717 }
718}
719
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400720static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400721disk_13XX(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500722{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500723 disk_ret_unimplemented(regs, DISK_RET_EPARAM);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500724}
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500725
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400726static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400727disk_13(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500728{
Kevin O'Connor15aee2e2008-03-01 13:34:04 -0500729 //debug_stub(regs);
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500730
731 // clear completion flag
732 SET_BDA(disk_interrupt_flag, 0);
733
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500734 switch (regs->ah) {
Kevin O'Connor1902c942013-10-26 11:48:06 -0400735 case 0x00: disk_1300(regs, drive_gf); break;
736 case 0x01: disk_1301(regs, drive_gf); break;
737 case 0x02: disk_1302(regs, drive_gf); break;
738 case 0x03: disk_1303(regs, drive_gf); break;
739 case 0x04: disk_1304(regs, drive_gf); break;
740 case 0x05: disk_1305(regs, drive_gf); break;
741 case 0x08: disk_1308(regs, drive_gf); break;
742 case 0x09: disk_1309(regs, drive_gf); break;
743 case 0x0c: disk_130c(regs, drive_gf); break;
744 case 0x0d: disk_130d(regs, drive_gf); break;
745 case 0x10: disk_1310(regs, drive_gf); break;
746 case 0x11: disk_1311(regs, drive_gf); break;
747 case 0x14: disk_1314(regs, drive_gf); break;
748 case 0x15: disk_1315(regs, drive_gf); break;
749 case 0x16: disk_1316(regs, drive_gf); break;
750 case 0x41: disk_1341(regs, drive_gf); break;
751 case 0x42: disk_1342(regs, drive_gf); break;
752 case 0x43: disk_1343(regs, drive_gf); break;
753 case 0x44: disk_1344(regs, drive_gf); break;
754 case 0x45: disk_1345(regs, drive_gf); break;
755 case 0x46: disk_1346(regs, drive_gf); break;
756 case 0x47: disk_1347(regs, drive_gf); break;
757 case 0x48: disk_1348(regs, drive_gf); break;
758 case 0x49: disk_1349(regs, drive_gf); break;
759 case 0x4e: disk_134e(regs, drive_gf); break;
760 default: disk_13XX(regs, drive_gf); break;
Kevin O'Connor3491e8b2008-02-29 00:22:27 -0500761 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500762}
763
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400764static void
Kevin O'Connor1902c942013-10-26 11:48:06 -0400765floppy_13(struct bregs *regs, struct drive_s *drive_gf)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400766{
767 // Only limited commands are supported on floppies.
768 switch (regs->ah) {
769 case 0x00:
770 case 0x01:
771 case 0x02:
772 case 0x03:
773 case 0x04:
774 case 0x05:
775 case 0x08:
776 case 0x15:
777 case 0x16:
Kevin O'Connor1902c942013-10-26 11:48:06 -0400778 disk_13(regs, drive_gf);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400779 break;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400780 default: disk_13XX(regs, drive_gf); break;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400781 }
782}
783
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500784
785/****************************************************************
Kevin O'Connorb74102d2008-03-03 21:57:30 -0500786 * Entry points
787 ****************************************************************/
788
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500789static void
Kevin O'Connor707298a2009-08-11 22:27:51 -0400790handle_legacy_disk(struct bregs *regs, u8 extdrive)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500791{
Kevin O'Connorc892b132009-08-11 21:59:37 -0400792 if (! CONFIG_DRIVES) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400793 // XXX - support handle_1301 anyway?
Kevin O'Connore43df9e2008-03-01 22:16:32 -0500794 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500795 return;
796 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500797
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400798 if (extdrive < EXTSTART_HD) {
Kevin O'Connor1902c942013-10-26 11:48:06 -0400799 struct drive_s *drive_gf = getDrive(EXTTYPE_FLOPPY, extdrive);
800 if (!drive_gf)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400801 goto fail;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400802 floppy_13(regs, drive_gf);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400803 return;
804 }
805
Kevin O'Connor1902c942013-10-26 11:48:06 -0400806 struct drive_s *drive_gf;
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400807 if (extdrive >= EXTSTART_CD)
Kevin O'Connor1902c942013-10-26 11:48:06 -0400808 drive_gf = getDrive(EXTTYPE_CD, extdrive - EXTSTART_CD);
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400809 else
Kevin O'Connor1902c942013-10-26 11:48:06 -0400810 drive_gf = getDrive(EXTTYPE_HD, extdrive - EXTSTART_HD);
811 if (!drive_gf)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400812 goto fail;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400813 disk_13(regs, drive_gf);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400814 return;
815
816fail:
817 // XXX - support 1301/1308/1315 anyway?
818 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500819}
820
Kevin O'Connor19786762008-03-05 21:09:59 -0500821void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500822handle_40(struct bregs *regs)
823{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400824 debug_enter(regs, DEBUG_HDL_40);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500825 handle_legacy_disk(regs, regs->dl);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500826}
827
828// INT 13h Fixed Disk Services Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500829void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500830handle_13(struct bregs *regs)
831{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400832 debug_enter(regs, DEBUG_HDL_13);
Kevin O'Connor707298a2009-08-11 22:27:51 -0400833 u8 extdrive = regs->dl;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500834
Kevin O'Connordfa16502008-03-22 20:13:08 -0400835 if (CONFIG_CDROM_EMU) {
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500836 if (regs->ah == 0x4b) {
837 cdemu_134b(regs);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400838 return;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500839 }
Kevin O'Connord3140832012-05-13 22:46:12 -0400840 if (GET_LOW(CDEmu.active)) {
841 u8 emudrive = GET_LOW(CDEmu.emulated_extdrive);
Kevin O'Connor707298a2009-08-11 22:27:51 -0400842 if (extdrive == emudrive) {
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400843 // Access to an emulated drive.
Kevin O'Connor1902c942013-10-26 11:48:06 -0400844 struct drive_s *cdemu_gf = GET_GLOBAL(cdemu_drive_gf);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400845 if (regs->ah > 0x16) {
846 // Only old-style commands supported.
Kevin O'Connor1902c942013-10-26 11:48:06 -0400847 disk_13XX(regs, cdemu_gf);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400848 return;
849 }
Kevin O'Connor1902c942013-10-26 11:48:06 -0400850 disk_13(regs, cdemu_gf);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400851 return;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500852 }
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400853 if (extdrive < EXTSTART_CD && ((emudrive ^ extdrive) & 0x80) == 0)
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400854 // Adjust id to make room for emulated drive.
Kevin O'Connor707298a2009-08-11 22:27:51 -0400855 extdrive--;
Kevin O'Connor941d3e42008-03-04 19:45:04 -0500856 }
857 }
Kevin O'Connor707298a2009-08-11 22:27:51 -0400858 handle_legacy_disk(regs, extdrive);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500859}
860
861// record completion in BIOS task complete flag
Kevin O'Connor19786762008-03-05 21:09:59 -0500862void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400863handle_76(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500864{
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400865 debug_isr(DEBUG_ISR_76);
Kevin O'Connored128492008-03-11 11:14:59 -0400866 SET_BDA(disk_interrupt_flag, 0xff);
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400867 pic_eoi2();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500868}
Kevin O'Connor30853762009-01-17 18:49:20 -0500869
870// Old Fixed Disk Parameter Table (newer tables are in the ebda).
871struct fdpt_s OldFDPT VAR16FIXED(0xe401);