1 // 16bit code to access hard drives.
3 // Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002 MandrakeSoft S.A.
6 // This file may be distributed under the terms of the GNU GPLv3 license.
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_*
16 disk_ret(struct bregs *regs, u8 code)
19 SET_BDA(disk_last_status, code);
23 // disk controller reset
25 disk_1300(struct bregs *regs, u8 device)
32 disk_1301(struct bregs *regs, u8 device)
34 regs->ah = GET_BDA(disk_last_status);
35 disk_ret(regs, DISK_RET_SUCCESS);
39 check_params(struct bregs *regs, u8 device)
42 u16 cylinder = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
43 u16 sector = regs->cl & 0x3f;
46 if ((count > 128) || (count == 0) || (sector == 0)) {
47 BX_INFO("int13_harddisk: function %02x, parameter out of range!\n"
49 disk_ret(regs, DISK_RET_EPARAM);
53 u16 nlc = GET_EBDA(ata.devices[device].lchs.cylinders);
54 u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
55 u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
57 // sanity check on cyl heads, sec
58 if ( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
59 BX_INFO("int13_harddisk: function %02x, parameters out of"
60 " range %04x/%04x/%04x!\n"
61 , regs->ah, cylinder, head, sector);
62 disk_ret(regs, DISK_RET_EPARAM);
69 disk_1302(struct bregs *regs, u8 device)
71 int ret = check_params(regs, device);
75 u16 cylinder = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
76 u16 sector = regs->cl & 0x3f;
78 u16 nph = GET_EBDA(ata.devices[device].pchs.heads);
79 u16 npspt = GET_EBDA(ata.devices[device].pchs.spt);
81 u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
82 u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
85 // if needed, translate lchs to lba, and execute command
86 if ( (nph != nlh) || (npspt != nlspt)) {
87 lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
89 sector = 0; // this forces the command to be lba
92 u16 segment = regs->es;
93 u16 offset = regs->bx;
95 u8 status = ata_cmd_data_in(device, ATA_CMD_READ_SECTORS
96 , count, cylinder, head, sector
97 , lba, segment, offset);
99 // Set nb of sector transferred
100 regs->al = GET_EBDA(ata.trsfsectors);
103 BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
104 disk_ret(regs, DISK_RET_EBADTRACK);
106 disk_ret(regs, DISK_RET_SUCCESS);
110 disk_1303(struct bregs *regs, u8 device)
112 int ret = check_params(regs, device);
115 u16 count = regs->al;
116 u16 cylinder = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
117 u16 sector = regs->cl & 0x3f;
119 u16 nph = GET_EBDA(ata.devices[device].pchs.heads);
120 u16 npspt = GET_EBDA(ata.devices[device].pchs.spt);
122 u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
123 u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
126 // if needed, translate lchs to lba, and execute command
127 if ( (nph != nlh) || (npspt != nlspt)) {
128 lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
130 sector = 0; // this forces the command to be lba
133 u16 segment = regs->es;
134 u16 offset = regs->bx;
136 u8 status = ata_cmd_data_out(device, ATA_CMD_READ_SECTORS
137 , count, cylinder, head, sector
138 , lba, segment, offset);
140 // Set nb of sector transferred
141 regs->al = GET_EBDA(ata.trsfsectors);
144 BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
145 disk_ret(regs, DISK_RET_EBADTRACK);
147 disk_ret(regs, DISK_RET_SUCCESS);
151 disk_1304(struct bregs *regs, u8 device)
153 int ret = check_params(regs, device);
157 disk_ret(regs, DISK_RET_SUCCESS);
160 #define DISK_STUB(regs) do { \
161 struct bregs *__regs = (regs); \
162 debug_stub(__regs); \
163 disk_ret(__regs, DISK_RET_SUCCESS); \
168 disk_1305(struct bregs *regs, u8 device)
173 // read disk drive parameters
175 disk_1308(struct bregs *regs, u8 device)
177 // Get logical geometry from table
178 u16 nlc = GET_EBDA(ata.devices[device].lchs.cylinders);
179 u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
180 u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
181 u16 count = GET_EBDA(ata.hdcount);
183 nlc = nlc - 2; /* 0 based , last sector not used */
185 regs->ch = nlc & 0xff;
186 regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
188 regs->dl = count; /* FIXME returns 0, 1, or n hard drives */
190 // FIXME should set ES & DI
191 disk_ret(regs, DISK_RET_SUCCESS);
196 disk_1310(struct bregs *regs, u8 device)
198 // should look at 40:8E also???
200 // Read the status from controller
201 u8 status = inb(GET_EBDA(ata.channels[device/2].iobase1) + ATA_CB_STAT);
202 if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY )
203 disk_ret(regs, DISK_RET_SUCCESS);
205 disk_ret(regs, DISK_RET_ENOTREADY);
208 // read disk drive size
210 disk_1315(struct bregs *regs, u8 device)
212 // Get logical geometry from table
213 u16 nlc = GET_EBDA(ata.devices[device].lchs.cylinders);
214 u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
215 u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
217 // Compute sector count seen by int13
218 u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
219 regs->cx = lba >> 16;
220 regs->dx = lba & 0xffff;
223 regs->ah = 3; // hard disk accessible
227 disk_13XX(struct bregs *regs, u8 device)
229 BX_INFO("int13_harddisk: function %xh unsupported, returns fail\n", regs->ah);
230 disk_ret(regs, DISK_RET_EPARAM);
234 disk_13(struct bregs *regs, u8 drive)
237 disk_13XX(regs, drive);
243 // clear completion flag
244 SET_BDA(disk_interrupt_flag, 0);
246 // basic check : device has to be defined
247 if (drive < 0x80 || drive >= 0x80 + CONFIG_MAX_ATA_DEVICES) {
248 disk_13XX(regs, drive);
252 // Get the ata channel
253 u8 device = GET_EBDA(ata.hdidmap[drive-0x80]);
255 // basic check : device has to be valid
256 if (device >= CONFIG_MAX_ATA_DEVICES) {
257 disk_13XX(regs, drive);
262 case 0x00: disk_1300(regs, device); break;
263 case 0x01: disk_1301(regs, device); break;
264 case 0x02: disk_1302(regs, device); break;
265 case 0x03: disk_1303(regs, device); break;
266 case 0x04: disk_1304(regs, device); break;
267 case 0x05: disk_1305(regs, device); break;
268 case 0x08: disk_1308(regs, device); break;
269 case 0x10: disk_1310(regs, device); break;
270 case 0x15: disk_1315(regs, device); break;
271 // XXX - several others defined
272 default: disk_13XX(regs, device); break;
277 handle_legacy_disk(struct bregs *regs, u8 drive)
280 floppy_13(regs, drive);
285 int13_cdrom(regs); // xxx
290 disk_13(regs, drive);
294 handle_40(struct bregs *regs)
297 handle_legacy_disk(regs, regs->dl);
301 // INT 13h Fixed Disk Services Entry Point
303 handle_13(struct bregs *regs)
309 if (regs->ah >= 0x4a || regs->ah <= 0x4d) {
310 int13_eltorito(regs);
311 } else if (cdemu_isactive() && cdrom_emulated_drive()) {
315 handle_legacy_disk(regs, drive);
319 // record completion in BIOS task complete flag
321 handle_76(struct bregs *regs)
324 SET_BDA(floppy_harddisk_info, 0xff);