Make disk access work.
[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 static inline void
16 disk_ret(struct bregs *regs, u8 code)
17 {
18     regs->ah = code;
19     SET_BDA(disk_last_status, code);
20     set_cf(regs, code);
21 }
22
23 // disk controller reset
24 static void
25 disk_1300(struct bregs *regs, u8 device)
26 {
27     ata_reset(device);
28 }
29
30 // read disk status
31 static void
32 disk_1301(struct bregs *regs, u8 device)
33 {
34     regs->ah = GET_BDA(disk_last_status);
35     disk_ret(regs, DISK_RET_SUCCESS);
36 }
37
38 static int
39 check_params(struct bregs *regs, u8 device)
40 {
41     u16 count       = regs->al;
42     u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
43     u16 sector      = regs->cl & 0x3f;
44     u16 head        = regs->dh;
45
46     if ((count > 128) || (count == 0) || (sector == 0)) {
47         BX_INFO("int13_harddisk: function %02x, parameter out of range!\n"
48                 , regs->ah);
49         disk_ret(regs, DISK_RET_EPARAM);
50         return -1;
51     }
52
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);
56
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);
63         return -1;
64     }
65     return 0;
66 }
67
68 static void
69 disk_1302(struct bregs *regs, u8 device)
70 {
71     int ret = check_params(regs, device);
72     if (ret)
73         return;
74     u16 count       = regs->al;
75     u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
76     u16 sector      = regs->cl & 0x3f;
77     u16 head        = regs->dh;
78     u16 nph   = GET_EBDA(ata.devices[device].pchs.heads);
79     u16 npspt = GET_EBDA(ata.devices[device].pchs.spt);
80
81     u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
82     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
83
84     u32 lba = 0;
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)
88                + (u32)sector - 1);
89         sector = 0; // this forces the command to be lba
90     }
91
92     u16 segment = regs->es;
93     u16 offset  = regs->bx;
94
95     u8 status = ata_cmd_data_in(device, ATA_CMD_READ_SECTORS
96                                 , count, cylinder, head, sector
97                                 , lba, segment, offset);
98
99     // Set nb of sector transferred
100     regs->al = GET_EBDA(ata.trsfsectors);
101
102     if (status != 0) {
103         BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
104         disk_ret(regs, DISK_RET_EBADTRACK);
105     }
106     disk_ret(regs, DISK_RET_SUCCESS);
107 }
108
109 static void
110 disk_1303(struct bregs *regs, u8 device)
111 {
112     int ret = check_params(regs, device);
113     if (ret)
114         return;
115     u16 count       = regs->al;
116     u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
117     u16 sector      = regs->cl & 0x3f;
118     u16 head        = regs->dh;
119     u16 nph   = GET_EBDA(ata.devices[device].pchs.heads);
120     u16 npspt = GET_EBDA(ata.devices[device].pchs.spt);
121
122     u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
123     u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
124
125     u32 lba = 0;
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)
129                + (u32)sector - 1);
130         sector = 0; // this forces the command to be lba
131     }
132
133     u16 segment = regs->es;
134     u16 offset  = regs->bx;
135
136     u8 status = ata_cmd_data_out(device, ATA_CMD_READ_SECTORS
137                                  , count, cylinder, head, sector
138                                  , lba, segment, offset);
139
140     // Set nb of sector transferred
141     regs->al = GET_EBDA(ata.trsfsectors);
142
143     if (status != 0) {
144         BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
145         disk_ret(regs, DISK_RET_EBADTRACK);
146     }
147     disk_ret(regs, DISK_RET_SUCCESS);
148 }
149
150 static void
151 disk_1304(struct bregs *regs, u8 device)
152 {
153     int ret = check_params(regs, device);
154     if (ret)
155         return;
156     // FIXME verify
157     disk_ret(regs, DISK_RET_SUCCESS);
158 }
159
160 #define DISK_STUB(regs) do {                    \
161         struct bregs *__regs = (regs);          \
162         debug_stub(__regs);                     \
163         disk_ret(__regs, DISK_RET_SUCCESS);     \
164     } while (0)
165
166 // format disk track
167 static void
168 disk_1305(struct bregs *regs, u8 device)
169 {
170     DISK_STUB(regs);
171 }
172
173 // read disk drive parameters
174 static void
175 disk_1308(struct bregs *regs, u8 device)
176 {
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);
182
183     nlc = nlc - 2; /* 0 based , last sector not used */
184     regs->al = 0;
185     regs->ch = nlc & 0xff;
186     regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
187     regs->dh = nlh - 1;
188     regs->dl = count; /* FIXME returns 0, 1, or n hard drives */
189
190     // FIXME should set ES & DI
191     disk_ret(regs, DISK_RET_SUCCESS);
192 }
193
194 // check drive ready
195 static void
196 disk_1310(struct bregs *regs, u8 device)
197 {
198     // should look at 40:8E also???
199
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);
204     else
205         disk_ret(regs, DISK_RET_ENOTREADY);
206 }
207
208 // read disk drive size
209 static void
210 disk_1315(struct bregs *regs, u8 device)
211 {
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);
216
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;
221
222     disk_ret(regs, 0);
223     regs->ah = 3; // hard disk accessible
224 }
225
226 static void
227 disk_13XX(struct bregs *regs, u8 device)
228 {
229     BX_INFO("int13_harddisk: function %xh unsupported, returns fail\n", regs->ah);
230     disk_ret(regs, DISK_RET_EPARAM);
231 }
232
233 static void
234 disk_13(struct bregs *regs, u8 drive)
235 {
236     if (! CONFIG_ATA) {
237         disk_13XX(regs, drive);
238         return;
239     }
240
241     //debug_stub(regs);
242
243     // clear completion flag
244     SET_BDA(disk_interrupt_flag, 0);
245
246     // basic check : device has to be defined
247     if (drive < 0x80 || drive >= 0x80 + CONFIG_MAX_ATA_DEVICES) {
248         disk_13XX(regs, drive);
249         return;
250     }
251
252     // Get the ata channel
253     u8 device = GET_EBDA(ata.hdidmap[drive-0x80]);
254
255     // basic check : device has to be valid
256     if (device >= CONFIG_MAX_ATA_DEVICES) {
257         disk_13XX(regs, drive);
258         return;
259     }
260
261     switch (regs->ah) {
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;
273     }
274 }
275
276 static void
277 handle_legacy_disk(struct bregs *regs, u8 drive)
278 {
279     if (drive < 0x80) {
280         floppy_13(regs, drive);
281         return;
282     }
283 #if BX_USE_ATADRV
284     if (drive >= 0xE0) {
285         int13_cdrom(regs); // xxx
286         return;
287     }
288 #endif
289
290     disk_13(regs, drive);
291 }
292
293 void VISIBLE
294 handle_40(struct bregs *regs)
295 {
296     debug_enter(regs);
297     handle_legacy_disk(regs, regs->dl);
298     debug_exit(regs);
299 }
300
301 // INT 13h Fixed Disk Services Entry Point
302 void VISIBLE
303 handle_13(struct bregs *regs)
304 {
305     //debug_enter(regs);
306     u8 drive = regs->dl;
307     // XXX
308 #if BX_ELTORITO_BOOT
309     if (regs->ah >= 0x4a || regs->ah <= 0x4d) {
310         int13_eltorito(regs);
311     } else if (cdemu_isactive() && cdrom_emulated_drive()) {
312         int13_cdemu(regs);
313     } else
314 #endif
315         handle_legacy_disk(regs, drive);
316     debug_exit(regs);
317 }
318
319 // record completion in BIOS task complete flag
320 void VISIBLE
321 handle_76(struct bregs *regs)
322 {
323     debug_enter(regs);
324     SET_BDA(floppy_harddisk_info, 0xff);
325     eoi_both_pics();
326 }