Set the PCI base address to 0xf0000000.
[seabios.git] / src / cdrom.c
1 // Support for booting from cdroms (the "El Torito" spec).
2 //
3 // Copyright (C) 2008,2009  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 LGPLv3 license.
7
8 #include "disk.h" // cdrom_13
9 #include "util.h" // memset
10 #include "bregs.h" // struct bregs
11 #include "biosvar.h" // GET_EBDA
12 #include "ata.h" // ATA_CMD_REQUEST_SENSE
13
14
15 /****************************************************************
16  * CD emulation
17  ****************************************************************/
18
19 static int
20 cdemu_read(struct disk_op_s *op)
21 {
22     u16 ebda_seg = get_ebda_seg();
23     u8 driveid = GET_EBDA2(ebda_seg, cdemu.emulated_driveid);
24     struct disk_op_s dop;
25     dop.driveid = driveid;
26     dop.command = op->command;
27     dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + op->lba / 4;
28
29     int count = op->count;
30     op->count = 0;
31     u8 *cdbuf_far = (void*)offsetof(struct extended_bios_data_area_s, cdemu_buf);
32
33     if (op->lba & 3) {
34         // Partial read of first block.
35         dop.count = 1;
36         dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
37         int ret = process_op(&dop);
38         if (ret)
39             return ret;
40         u8 thiscount = 4 - (op->lba & 3);
41         if (thiscount > count)
42             thiscount = count;
43         count -= thiscount;
44         memcpy_far(FLATPTR_TO_SEG(op->buf_fl)
45                    , (void*)FLATPTR_TO_OFFSET(op->buf_fl)
46                    , ebda_seg, cdbuf_far + (op->lba & 3) * 512
47                    , thiscount * 512);
48         op->buf_fl += thiscount * 512;
49         op->count += thiscount;
50         dop.lba++;
51     }
52
53     if (count > 3) {
54         // Read n number of regular blocks.
55         dop.count = count / 4;
56         dop.buf_fl = op->buf_fl;
57         int ret = process_op(&dop);
58         op->count += dop.count * 4;
59         if (ret)
60             return ret;
61         u8 thiscount = count & ~3;
62         count &= 3;
63         op->buf_fl += thiscount * 512;
64         dop.lba += thiscount / 4;
65     }
66
67     if (count) {
68         // Partial read on last block.
69         dop.count = 1;
70         dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
71         int ret = process_op(&dop);
72         if (ret)
73             return ret;
74         u8 thiscount = count;
75         memcpy_far(FLATPTR_TO_SEG(op->buf_fl)
76                    , (void*)FLATPTR_TO_OFFSET(op->buf_fl)
77                    , ebda_seg, cdbuf_far, thiscount * 512);
78         op->count += thiscount;
79     }
80
81     return DISK_RET_SUCCESS;
82 }
83
84 int
85 process_cdemu_op(struct disk_op_s *op)
86 {
87     if (!CONFIG_CDROM_EMU)
88         return 0;
89
90     switch (op->command) {
91     case CMD_READ:
92         return cdemu_read(op);
93     case CMD_WRITE:
94     case CMD_FORMAT:
95         return DISK_RET_EWRITEPROTECT;
96     case CMD_VERIFY:
97     case CMD_RESET:
98     case CMD_SEEK:
99     case CMD_ISREADY:
100         return DISK_RET_SUCCESS;
101     default:
102         op->count = 0;
103         return DISK_RET_EPARAM;
104     }
105 }
106
107 int cdemu_driveid VAR16VISIBLE;
108
109 void
110 cdemu_setup()
111 {
112     if (!CONFIG_CDROM_EMU)
113         return;
114
115     int driveid = Drives.drivecount;
116     if (driveid >= ARRAY_SIZE(Drives.drives)) {
117         cdemu_driveid = -1;
118         return;
119     }
120     Drives.drivecount++;
121     cdemu_driveid = driveid;
122     memset(&Drives.drives[driveid], 0, sizeof(Drives.drives[0]));
123     Drives.drives[driveid].type = DTYPE_CDEMU;
124     Drives.drives[driveid].blksize = DISK_SECTOR_SIZE;
125     Drives.drives[driveid].sectors = (u64)-1;
126 }
127
128 struct eltorito_s {
129     u8 size;
130     u8 media;
131     u8 emulated_drive;
132     u8 controller_index;
133     u32 ilba;
134     u16 device_spec;
135     u16 buffer_segment;
136     u16 load_segment;
137     u16 sector_count;
138     u8 cylinders;
139     u8 sectors;
140     u8 heads;
141 };
142
143 #define SET_INT13ET(regs,var,val)                                      \
144     SET_FARVAR((regs)->ds, ((struct eltorito_s*)((regs)->si+0))->var, (val))
145
146 // ElTorito - Terminate disk emu
147 void
148 cdemu_134b(struct bregs *regs)
149 {
150     // FIXME ElTorito Hardcoded
151     u16 ebda_seg = get_ebda_seg();
152     SET_INT13ET(regs, size, 0x13);
153     SET_INT13ET(regs, media, GET_EBDA2(ebda_seg, cdemu.media));
154     SET_INT13ET(regs, emulated_drive
155                 , GET_EBDA2(ebda_seg, cdemu.emulated_extdrive));
156     u8 driveid = GET_EBDA2(ebda_seg, cdemu.emulated_driveid);
157     u8 cntl_id = GET_GLOBAL(Drives.drives[driveid].cntl_id);
158     SET_INT13ET(regs, controller_index, cntl_id / 2);
159     SET_INT13ET(regs, device_spec, cntl_id % 2);
160     SET_INT13ET(regs, ilba, GET_EBDA2(ebda_seg, cdemu.ilba));
161     SET_INT13ET(regs, buffer_segment, GET_EBDA2(ebda_seg, cdemu.buffer_segment));
162     SET_INT13ET(regs, load_segment, GET_EBDA2(ebda_seg, cdemu.load_segment));
163     SET_INT13ET(regs, sector_count, GET_EBDA2(ebda_seg, cdemu.sector_count));
164     SET_INT13ET(regs, cylinders, GET_EBDA2(ebda_seg, cdemu.lchs.cylinders));
165     SET_INT13ET(regs, sectors, GET_EBDA2(ebda_seg, cdemu.lchs.spt));
166     SET_INT13ET(regs, heads, GET_EBDA2(ebda_seg, cdemu.lchs.heads));
167
168     // If we have to terminate emulation
169     if (regs->al == 0x00) {
170         // FIXME ElTorito Various. Should be handled accordingly to spec
171         SET_EBDA2(ebda_seg, cdemu.active, 0x00); // bye bye
172     }
173
174     disk_ret(regs, DISK_RET_SUCCESS);
175 }
176
177
178 /****************************************************************
179  * CD booting
180  ****************************************************************/
181
182 // Request SENSE
183 static int
184 atapi_get_sense(int driveid, u8 *asc, u8 *ascq)
185 {
186     u8 atacmd[12], buffer[18];
187     memset(atacmd, 0, sizeof(atacmd));
188     atacmd[0] = ATA_CMD_REQUEST_SENSE;
189     atacmd[4] = sizeof(buffer);
190     int ret = ata_cmd_packet(driveid, atacmd, sizeof(atacmd), sizeof(buffer)
191                              , MAKE_FLATPTR(GET_SEG(SS), buffer));
192     if (ret)
193         return ret;
194
195     *asc = buffer[12];
196     *ascq = buffer[13];
197
198     return 0;
199 }
200
201 // Request capacity
202 static int
203 atapi_read_capacity(int driveid, u32 *blksize, u32 *sectors)
204 {
205     u8 packet[12], buf[8];
206     memset(packet, 0, sizeof(packet));
207     packet[0] = 0x25; /* READ CAPACITY */
208     int ret = ata_cmd_packet(driveid, packet, sizeof(packet), sizeof(buf)
209                              , MAKE_FLATPTR(GET_SEG(SS), buf));
210     if (ret)
211         return ret;
212
213     *blksize = (((u32)buf[4] << 24) | ((u32)buf[5] << 16)
214                 | ((u32)buf[6] << 8) | ((u32)buf[7] << 0));
215     *sectors = (((u32)buf[0] << 24) | ((u32)buf[1] << 16)
216                 | ((u32)buf[2] << 8) | ((u32)buf[3] << 0));
217
218     return 0;
219 }
220
221 static int
222 atapi_is_ready(u16 driveid)
223 {
224     dprintf(6, "atapi_is_ready (driveid=%d)\n", driveid);
225
226     /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT is
227      * reported by the device.  If the device reports "IN PROGRESS",
228      * 30 seconds is added. */
229     u32 blksize, sectors;
230     int in_progress = 0;
231     u64 end = calc_future_tsc(5000);
232     for (;;) {
233         if (rdtscll() > end) {
234             dprintf(1, "read capacity failed\n");
235             return -1;
236         }
237
238         int ret = atapi_read_capacity(driveid, &blksize, &sectors);
239         if (!ret)
240             // Success
241             break;
242
243         u8 asc, ascq;
244         ret = atapi_get_sense(driveid, &asc, &ascq);
245         if (ret)
246             // Error - retry.
247             continue;
248
249         // Sense succeeded.
250         if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
251             dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
252             return -1;
253         }
254
255         if (asc == 0x04 && ascq == 0x01 && !in_progress) {
256             /* IN PROGRESS OF BECOMING READY */
257             printf("Waiting for device to detect medium... ");
258             /* Allow 30 seconds more */
259             end = calc_future_tsc(30000);
260             in_progress = 1;
261         }
262     }
263
264     if (blksize != GET_GLOBAL(Drives.drives[driveid].blksize)) {
265         printf("Unsupported sector size %u\n", blksize);
266         return -1;
267     }
268
269     dprintf(6, "sectors=%u\n", sectors);
270     printf("%dMB medium detected\n", sectors>>(20-11));
271     return 0;
272 }
273
274 int
275 cdrom_boot(int cdid)
276 {
277     // Verify device is a cdrom.
278     if (cdid >= Drives.cdcount)
279         return 1;
280     int driveid = GET_GLOBAL(Drives.idmap[EXTTYPE_CD][cdid]);
281
282     int ret = atapi_is_ready(driveid);
283     if (ret)
284         dprintf(1, "atapi_is_ready returned %d\n", ret);
285
286     // Read the Boot Record Volume Descriptor
287     u8 buffer[2048];
288     struct disk_op_s dop;
289     memset(&dop, 0, sizeof(dop));
290     dop.driveid = driveid;
291     dop.lba = 0x11;
292     dop.count = 1;
293     dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
294     ret = cdrom_read(&dop);
295     if (ret)
296         return 3;
297
298     // Validity checks
299     if (buffer[0])
300         return 4;
301     if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
302         return 5;
303
304     // ok, now we calculate the Boot catalog address
305     u32 lba = *(u32*)&buffer[0x47];
306
307     // And we read the Boot Catalog
308     dop.lba = lba;
309     ret = cdrom_read(&dop);
310     if (ret)
311         return 7;
312
313     // Validation entry
314     if (buffer[0x00] != 0x01)
315         return 8;   // Header
316     if (buffer[0x01] != 0x00)
317         return 9;   // Platform
318     if (buffer[0x1E] != 0x55)
319         return 10;  // key 1
320     if (buffer[0x1F] != 0xAA)
321         return 10;  // key 2
322
323     // Initial/Default Entry
324     if (buffer[0x20] != 0x88)
325         return 11; // Bootable
326
327     u16 ebda_seg = get_ebda_seg();
328     u8 media = buffer[0x21];
329     SET_EBDA2(ebda_seg, cdemu.media, media);
330
331     SET_EBDA2(ebda_seg, cdemu.emulated_driveid, driveid);
332
333     u16 boot_segment = *(u16*)&buffer[0x22];
334     if (!boot_segment)
335         boot_segment = 0x07C0;
336     SET_EBDA2(ebda_seg, cdemu.load_segment, boot_segment);
337     SET_EBDA2(ebda_seg, cdemu.buffer_segment, 0x0000);
338
339     u16 nbsectors = *(u16*)&buffer[0x26];
340     SET_EBDA2(ebda_seg, cdemu.sector_count, nbsectors);
341
342     lba = *(u32*)&buffer[0x28];
343     SET_EBDA2(ebda_seg, cdemu.ilba, lba);
344
345     // And we read the image in memory
346     dop.lba = lba;
347     dop.count = DIV_ROUND_UP(nbsectors, 4);
348     dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
349     ret = cdrom_read(&dop);
350     if (ret)
351         return 12;
352
353     if (media == 0) {
354         // No emulation requested - return success.
355         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, EXTSTART_CD + cdid);
356         return 0;
357     }
358
359     // Emulation of a floppy/harddisk requested
360     if (! CONFIG_CDROM_EMU || cdemu_driveid < 0)
361         return 13;
362
363     // Set emulated drive id and increase bios installed hardware
364     // number of devices
365     if (media < 4) {
366         // Floppy emulation
367         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x00);
368         SETBITS_BDA(equipment_list_flags, 0x41);
369
370         switch (media) {
371         case 0x01:  // 1.2M floppy
372             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 15);
373             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
374             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
375             break;
376         case 0x02:  // 1.44M floppy
377             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 18);
378             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
379             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
380             break;
381         case 0x03:  // 2.88M floppy
382             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 36);
383             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
384             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
385             break;
386         }
387     } else {
388         // Harddrive emulation
389         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x80);
390         SET_BDA(hdcount, GET_BDA(hdcount) + 1);
391
392         // Peak at partition table to get chs.
393         struct mbr_s *mbr = (void*)0;
394         u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl);
395         u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow);
396         u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads);
397
398         SET_EBDA2(ebda_seg, cdemu.lchs.spt, sptcyl & 0x3f);
399         SET_EBDA2(ebda_seg, cdemu.lchs.cylinders
400                   , ((sptcyl<<2)&0x300) + cyllow + 1);
401         SET_EBDA2(ebda_seg, cdemu.lchs.heads, heads + 1);
402     }
403
404     // everything is ok, so from now on, the emulation is active
405     SET_EBDA2(ebda_seg, cdemu.active, 0x01);
406     dprintf(6, "cdemu media=%d\n", media);
407
408     return 0;
409 }