blob: 811bef5b581508f9b6f8c530ba03c3b7a77a1e1b [file] [log] [blame]
Kevin O'Connor36c93a52009-09-12 19:35:04 -04001// Support for booting from cdroms (the "El Torito" spec).
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -05002//
Kevin O'Connorc892b132009-08-11 21:59:37 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -05004// 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'Connor31d8c8a2008-03-04 19:56:41 -05007
Kevin O'Connord3140832012-05-13 22:46:12 -04008#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor135f3f62013-09-14 23:57:26 -04009#include "block.h" // struct drive_s
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "bregs.h" // struct bregs
Kevin O'Connor5d369d82013-09-02 20:48:46 -040011#include "hw/ata.h" // ATA_CMD_REQUEST_SENSE
12#include "hw/blockcmd.h" // CDB_CMD_REQUEST_SENSE
Kevin O'Connor9dea5902013-09-14 20:23:54 -040013#include "malloc.h" // free
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "output.h" // dprintf
Kevin O'Connor135f3f62013-09-14 23:57:26 -040015#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040016#include "string.h" // memset
Kevin O'Connor135f3f62013-09-14 23:57:26 -040017#include "util.h" // cdrom_prepboot
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050018
Kevin O'Connord3140832012-05-13 22:46:12 -040019// Locks for removable devices
Kevin O'Connore52ad392013-02-20 23:48:22 -050020u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW;
Kevin O'Connord3140832012-05-13 22:46:12 -040021
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050022
23/****************************************************************
Kevin O'Connor51cfbe72009-08-18 22:38:49 -040024 * CD emulation
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050025 ****************************************************************/
26
Kevin O'Connord3140832012-05-13 22:46:12 -040027struct cdemu_s CDEmu VARLOW;
Kevin O'Connor89a2f962013-02-18 23:36:03 -050028struct drive_s *cdemu_drive_gf VARFSEG;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040029
Kevin O'Connor36c93a52009-09-12 19:35:04 -040030static int
31cdemu_read(struct disk_op_s *op)
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050032{
Kevin O'Connor1902c942013-10-26 11:48:06 -040033 struct drive_s *drive_gf = GET_LOW(CDEmu.emulated_drive_gf);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040034 struct disk_op_s dop;
Kevin O'Connor1902c942013-10-26 11:48:06 -040035 dop.drive_gf = drive_gf;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040036 dop.command = op->command;
Kevin O'Connord3140832012-05-13 22:46:12 -040037 dop.lba = GET_LOW(CDEmu.ilba) + op->lba / 4;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050038
Kevin O'Connor36c93a52009-09-12 19:35:04 -040039 int count = op->count;
40 op->count = 0;
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +020041 u8 *cdbuf_fl = GET_GLOBAL(bounce_buf_fl);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -040042
Kevin O'Connor36c93a52009-09-12 19:35:04 -040043 if (op->lba & 3) {
44 // Partial read of first block.
45 dop.count = 1;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040046 dop.buf_fl = cdbuf_fl;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040047 int ret = process_op(&dop);
48 if (ret)
49 return ret;
50 u8 thiscount = 4 - (op->lba & 3);
51 if (thiscount > count)
52 thiscount = count;
53 count -= thiscount;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040054 memcpy_fl(op->buf_fl, cdbuf_fl + (op->lba & 3) * 512, thiscount * 512);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040055 op->buf_fl += thiscount * 512;
56 op->count += thiscount;
57 dop.lba++;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050058 }
Kevin O'Connor36c93a52009-09-12 19:35:04 -040059
60 if (count > 3) {
61 // Read n number of regular blocks.
62 dop.count = count / 4;
63 dop.buf_fl = op->buf_fl;
64 int ret = process_op(&dop);
65 op->count += dop.count * 4;
66 if (ret)
67 return ret;
68 u8 thiscount = count & ~3;
69 count &= 3;
70 op->buf_fl += thiscount * 512;
71 dop.lba += thiscount / 4;
72 }
73
74 if (count) {
75 // Partial read on last block.
76 dop.count = 1;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040077 dop.buf_fl = cdbuf_fl;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040078 int ret = process_op(&dop);
79 if (ret)
80 return ret;
81 u8 thiscount = count;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040082 memcpy_fl(op->buf_fl, cdbuf_fl, thiscount * 512);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040083 op->count += thiscount;
84 }
85
86 return DISK_RET_SUCCESS;
87}
88
89int
90process_cdemu_op(struct disk_op_s *op)
91{
92 if (!CONFIG_CDROM_EMU)
93 return 0;
94
95 switch (op->command) {
96 case CMD_READ:
97 return cdemu_read(op);
98 case CMD_WRITE:
99 case CMD_FORMAT:
100 return DISK_RET_EWRITEPROTECT;
101 case CMD_VERIFY:
102 case CMD_RESET:
103 case CMD_SEEK:
104 case CMD_ISREADY:
105 return DISK_RET_SUCCESS;
106 default:
107 op->count = 0;
108 return DISK_RET_EPARAM;
109 }
110}
111
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400112void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500113cdrom_prepboot(void)
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400114{
115 if (!CONFIG_CDROM_EMU)
116 return;
Kevin O'Connora0842f82010-12-29 11:05:46 -0500117 if (!CDCount)
Kevin O'Connord5d02b62010-06-06 16:18:03 -0400118 return;
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500119 if (create_bounce_buf() < 0)
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +0200120 return;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400121
Kevin O'Connor1902c942013-10-26 11:48:06 -0400122 struct drive_s *drive = malloc_fseg(sizeof(*drive));
123 if (!drive) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500124 warn_noalloc();
Kevin O'Connor1902c942013-10-26 11:48:06 -0400125 free(drive);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400126 return;
127 }
Kevin O'Connor1902c942013-10-26 11:48:06 -0400128 cdemu_drive_gf = drive;
129 memset(drive, 0, sizeof(*drive));
130 drive->type = DTYPE_CDEMU;
131 drive->blksize = DISK_SECTOR_SIZE;
132 drive->sectors = (u64)-1;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500133}
134
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500135#define SET_INT13ET(regs,var,val) \
136 SET_FARVAR((regs)->ds, ((struct eltorito_s*)((regs)->si+0))->var, (val))
137
138// ElTorito - Terminate disk emu
139void
140cdemu_134b(struct bregs *regs)
141{
142 // FIXME ElTorito Hardcoded
143 SET_INT13ET(regs, size, 0x13);
Kevin O'Connord3140832012-05-13 22:46:12 -0400144 SET_INT13ET(regs, media, GET_LOW(CDEmu.media));
145 SET_INT13ET(regs, emulated_drive, GET_LOW(CDEmu.emulated_extdrive));
146 struct drive_s *drive_gf = GET_LOW(CDEmu.emulated_drive_gf);
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500147 u8 cntl_id = 0;
148 if (drive_gf)
149 cntl_id = GET_GLOBALFLAT(drive_gf->cntl_id);
Kevin O'Connor669e6442009-08-11 22:36:30 -0400150 SET_INT13ET(regs, controller_index, cntl_id / 2);
151 SET_INT13ET(regs, device_spec, cntl_id % 2);
Kevin O'Connord3140832012-05-13 22:46:12 -0400152 SET_INT13ET(regs, ilba, GET_LOW(CDEmu.ilba));
153 SET_INT13ET(regs, buffer_segment, GET_LOW(CDEmu.buffer_segment));
154 SET_INT13ET(regs, load_segment, GET_LOW(CDEmu.load_segment));
155 SET_INT13ET(regs, sector_count, GET_LOW(CDEmu.sector_count));
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400156 SET_INT13ET(regs, cylinders, GET_LOW(CDEmu.lchs.cylinder));
157 SET_INT13ET(regs, sectors, GET_LOW(CDEmu.lchs.sector));
158 SET_INT13ET(regs, heads, GET_LOW(CDEmu.lchs.head));
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500159
160 // If we have to terminate emulation
161 if (regs->al == 0x00) {
162 // FIXME ElTorito Various. Should be handled accordingly to spec
Kevin O'Connord3140832012-05-13 22:46:12 -0400163 SET_LOW(CDEmu.active, 0x00); // bye bye
Kevin O'Connor7d700252010-02-15 11:56:07 -0500164
165 // XXX - update floppy/hd count.
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500166 }
167
168 disk_ret(regs, DISK_RET_SUCCESS);
169}
Kevin O'Connor180a9592008-03-04 22:50:53 -0500170
171
172/****************************************************************
173 * CD booting
174 ****************************************************************/
175
Kevin O'Connora05223c2008-06-28 12:15:57 -0400176int
Kevin O'Connor1902c942013-10-26 11:48:06 -0400177cdrom_boot(struct drive_s *drive)
Kevin O'Connor180a9592008-03-04 22:50:53 -0500178{
Kevin O'Connord3140832012-05-13 22:46:12 -0400179 ASSERT32FLAT();
Kevin O'Connor7d700252010-02-15 11:56:07 -0500180 struct disk_op_s dop;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400181 int cdid = getDriveId(EXTTYPE_CD, drive);
Kevin O'Connor7d700252010-02-15 11:56:07 -0500182 memset(&dop, 0, sizeof(dop));
Kevin O'Connor1902c942013-10-26 11:48:06 -0400183 dop.drive_gf = drive;
184 if (!dop.drive_gf || cdid < 0)
Kevin O'Connor7da1dcd2009-02-16 10:51:24 -0500185 return 1;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500186
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100187 int ret = scsi_is_ready(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500188 if (ret)
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100189 dprintf(1, "scsi_is_ready returned %d\n", ret);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500190
191 // Read the Boot Record Volume Descriptor
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400192 u8 buffer[CDROM_SECTOR_SIZE];
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500193 dop.lba = 0x11;
194 dop.count = 1;
Kevin O'Connord3140832012-05-13 22:46:12 -0400195 dop.buf_fl = buffer;
Kevin O'Connor76977b22010-02-17 01:01:32 -0500196 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500197 if (ret)
198 return 3;
199
200 // Validity checks
201 if (buffer[0])
202 return 4;
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400203 if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
Kevin O'Connor180a9592008-03-04 22:50:53 -0500204 return 5;
205
206 // ok, now we calculate the Boot catalog address
207 u32 lba = *(u32*)&buffer[0x47];
208
209 // And we read the Boot Catalog
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500210 dop.lba = lba;
Kevin O'Connor76977b22010-02-17 01:01:32 -0500211 dop.count = 1;
212 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500213 if (ret)
214 return 7;
215
216 // Validation entry
217 if (buffer[0x00] != 0x01)
218 return 8; // Header
219 if (buffer[0x01] != 0x00)
220 return 9; // Platform
221 if (buffer[0x1E] != 0x55)
222 return 10; // key 1
223 if (buffer[0x1F] != 0xAA)
224 return 10; // key 2
225
226 // Initial/Default Entry
227 if (buffer[0x20] != 0x88)
228 return 11; // Bootable
229
Kevin O'Connordfa16502008-03-22 20:13:08 -0400230 u8 media = buffer[0x21];
Kevin O'Connord3140832012-05-13 22:46:12 -0400231 CDEmu.media = media;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500232
Kevin O'Connor1902c942013-10-26 11:48:06 -0400233 CDEmu.emulated_drive_gf = dop.drive_gf;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500234
235 u16 boot_segment = *(u16*)&buffer[0x22];
236 if (!boot_segment)
237 boot_segment = 0x07C0;
Kevin O'Connord3140832012-05-13 22:46:12 -0400238 CDEmu.load_segment = boot_segment;
239 CDEmu.buffer_segment = 0x0000;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500240
241 u16 nbsectors = *(u16*)&buffer[0x26];
Kevin O'Connord3140832012-05-13 22:46:12 -0400242 CDEmu.sector_count = nbsectors;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500243
244 lba = *(u32*)&buffer[0x28];
Kevin O'Connord3140832012-05-13 22:46:12 -0400245 CDEmu.ilba = lba;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500246
247 // And we read the image in memory
Kevin O'Connora4d70aa2009-08-09 11:32:00 -0400248 dop.lba = lba;
249 dop.count = DIV_ROUND_UP(nbsectors, 4);
Kevin O'Connor35ae7262009-01-19 15:44:44 -0500250 dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
Kevin O'Connor76977b22010-02-17 01:01:32 -0500251 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500252 if (ret)
253 return 12;
254
Kevin O'Connordfa16502008-03-22 20:13:08 -0400255 if (media == 0) {
256 // No emulation requested - return success.
Kevin O'Connord3140832012-05-13 22:46:12 -0400257 CDEmu.emulated_extdrive = EXTSTART_CD + cdid;
Kevin O'Connordfa16502008-03-22 20:13:08 -0400258 return 0;
259 }
260
261 // Emulation of a floppy/harddisk requested
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500262 if (! CONFIG_CDROM_EMU || !cdemu_drive_gf)
Kevin O'Connordfa16502008-03-22 20:13:08 -0400263 return 13;
264
265 // Set emulated drive id and increase bios installed hardware
266 // number of devices
267 if (media < 4) {
268 // Floppy emulation
Kevin O'Connord3140832012-05-13 22:46:12 -0400269 CDEmu.emulated_extdrive = 0x00;
Kevin O'Connor7d700252010-02-15 11:56:07 -0500270 // XXX - get and set actual floppy count.
Kevin O'Connore51316d2012-06-10 09:09:22 -0400271 set_equipment_flags(0x41, 0x41);
Kevin O'Connor95827c42009-02-07 00:04:57 -0500272
273 switch (media) {
274 case 0x01: // 1.2M floppy
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400275 CDEmu.lchs.sector = 15;
276 CDEmu.lchs.cylinder = 80;
277 CDEmu.lchs.head = 2;
Kevin O'Connor95827c42009-02-07 00:04:57 -0500278 break;
279 case 0x02: // 1.44M floppy
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400280 CDEmu.lchs.sector = 18;
281 CDEmu.lchs.cylinder = 80;
282 CDEmu.lchs.head = 2;
Kevin O'Connor95827c42009-02-07 00:04:57 -0500283 break;
284 case 0x03: // 2.88M floppy
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400285 CDEmu.lchs.sector = 36;
286 CDEmu.lchs.cylinder = 80;
287 CDEmu.lchs.head = 2;
Kevin O'Connor95827c42009-02-07 00:04:57 -0500288 break;
289 }
Kevin O'Connordfa16502008-03-22 20:13:08 -0400290 } else {
291 // Harddrive emulation
Kevin O'Connord3140832012-05-13 22:46:12 -0400292 CDEmu.emulated_extdrive = 0x80;
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500293 SET_BDA(hdcount, GET_BDA(hdcount) + 1);
Kevin O'Connordfa16502008-03-22 20:13:08 -0400294
Kevin O'Connor95827c42009-02-07 00:04:57 -0500295 // Peak at partition table to get chs.
296 struct mbr_s *mbr = (void*)0;
297 u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl);
298 u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow);
299 u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads);
300
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400301 CDEmu.lchs.sector = sptcyl & 0x3f;
302 CDEmu.lchs.cylinder = ((sptcyl<<2)&0x300) + cyllow + 1;
303 CDEmu.lchs.head = heads + 1;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500304 }
305
Kevin O'Connordfa16502008-03-22 20:13:08 -0400306 // everything is ok, so from now on, the emulation is active
Kevin O'Connord3140832012-05-13 22:46:12 -0400307 CDEmu.active = 0x01;
Kevin O'Connora05223c2008-06-28 12:15:57 -0400308 dprintf(6, "cdemu media=%d\n", media);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500309
310 return 0;
311}