1 // Support for booting from cdroms (the "El Torito" spec).
3 // Copyright (C) 2008,2009 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 LGPLv3 license.
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
15 /****************************************************************
17 ****************************************************************/
20 cdemu_read(struct disk_op_s *op)
22 u16 ebda_seg = get_ebda_seg();
23 u8 driveid = GET_EBDA2(ebda_seg, cdemu.emulated_driveid);
25 dop.driveid = driveid;
26 dop.command = op->command;
27 dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + op->lba / 4;
29 int count = op->count;
31 u8 *cdbuf_far = (void*)offsetof(struct extended_bios_data_area_s, cdemu_buf);
34 // Partial read of first block.
36 dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
37 int ret = process_op(&dop);
40 u8 thiscount = 4 - (op->lba & 3);
41 if (thiscount > count)
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
48 op->buf_fl += thiscount * 512;
49 op->count += thiscount;
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;
61 u8 thiscount = count & ~3;
63 op->buf_fl += thiscount * 512;
64 dop.lba += thiscount / 4;
68 // Partial read on last block.
70 dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
71 int ret = process_op(&dop);
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;
81 return DISK_RET_SUCCESS;
85 process_cdemu_op(struct disk_op_s *op)
87 if (!CONFIG_CDROM_EMU)
90 switch (op->command) {
92 return cdemu_read(op);
95 return DISK_RET_EWRITEPROTECT;
100 return DISK_RET_SUCCESS;
103 return DISK_RET_EPARAM;
107 int cdemu_driveid VAR16VISIBLE;
112 if (!CONFIG_CDROM_EMU)
115 int driveid = Drives.drivecount;
116 if (driveid >= ARRAY_SIZE(Drives.drives)) {
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;
143 #define SET_INT13ET(regs,var,val) \
144 SET_FARVAR((regs)->ds, ((struct eltorito_s*)((regs)->si+0))->var, (val))
146 // ElTorito - Terminate disk emu
148 cdemu_134b(struct bregs *regs)
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));
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
174 disk_ret(regs, DISK_RET_SUCCESS);
178 /****************************************************************
180 ****************************************************************/
184 atapi_get_sense(int driveid, u8 *asc, u8 *ascq)
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));
203 atapi_read_capacity(int driveid, u32 *blksize, u32 *sectors)
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));
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));
222 atapi_is_ready(u16 driveid)
224 dprintf(6, "atapi_is_ready (driveid=%d)\n", driveid);
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;
231 u64 end = calc_future_tsc(5000);
233 if (rdtscll() > end) {
234 dprintf(1, "read capacity failed\n");
238 int ret = atapi_read_capacity(driveid, &blksize, §ors);
244 ret = atapi_get_sense(driveid, &asc, &ascq);
250 if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
251 dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
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);
264 if (blksize != GET_GLOBAL(Drives.drives[driveid].blksize)) {
265 printf("Unsupported sector size %u\n", blksize);
269 dprintf(6, "sectors=%u\n", sectors);
270 printf("%dMB medium detected\n", sectors>>(20-11));
277 // Verify device is a cdrom.
278 if (cdid >= Drives.cdcount)
280 int driveid = GET_GLOBAL(Drives.idmap[EXTTYPE_CD][cdid]);
282 int ret = atapi_is_ready(driveid);
284 dprintf(1, "atapi_is_ready returned %d\n", ret);
286 // Read the Boot Record Volume Descriptor
288 struct disk_op_s dop;
289 memset(&dop, 0, sizeof(dop));
290 dop.driveid = driveid;
293 dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
294 ret = cdrom_read(&dop);
301 if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
304 // ok, now we calculate the Boot catalog address
305 u32 lba = *(u32*)&buffer[0x47];
307 // And we read the Boot Catalog
309 ret = cdrom_read(&dop);
314 if (buffer[0x00] != 0x01)
316 if (buffer[0x01] != 0x00)
317 return 9; // Platform
318 if (buffer[0x1E] != 0x55)
320 if (buffer[0x1F] != 0xAA)
323 // Initial/Default Entry
324 if (buffer[0x20] != 0x88)
325 return 11; // Bootable
327 u16 ebda_seg = get_ebda_seg();
328 u8 media = buffer[0x21];
329 SET_EBDA2(ebda_seg, cdemu.media, media);
331 SET_EBDA2(ebda_seg, cdemu.emulated_driveid, driveid);
333 u16 boot_segment = *(u16*)&buffer[0x22];
335 boot_segment = 0x07C0;
336 SET_EBDA2(ebda_seg, cdemu.load_segment, boot_segment);
337 SET_EBDA2(ebda_seg, cdemu.buffer_segment, 0x0000);
339 u16 nbsectors = *(u16*)&buffer[0x26];
340 SET_EBDA2(ebda_seg, cdemu.sector_count, nbsectors);
342 lba = *(u32*)&buffer[0x28];
343 SET_EBDA2(ebda_seg, cdemu.ilba, lba);
345 // And we read the image in memory
347 dop.count = DIV_ROUND_UP(nbsectors, 4);
348 dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
349 ret = cdrom_read(&dop);
354 // No emulation requested - return success.
355 SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, EXTSTART_CD + cdid);
359 // Emulation of a floppy/harddisk requested
360 if (! CONFIG_CDROM_EMU || cdemu_driveid < 0)
363 // Set emulated drive id and increase bios installed hardware
367 SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x00);
368 SETBITS_BDA(equipment_list_flags, 0x41);
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);
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);
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);
388 // Harddrive emulation
389 SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x80);
390 SET_BDA(hdcount, GET_BDA(hdcount) + 1);
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);
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);
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);