blob: 170ffc482375f4829b35e0e718afb396db0f8d5b [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
8#include "disk.h" // cdrom_13
9#include "util.h" // memset
Kevin O'Connor9521e262008-07-04 13:04:29 -040010#include "bregs.h" // struct bregs
11#include "biosvar.h" // GET_EBDA
Kevin O'Connorc892b132009-08-11 21:59:37 -040012#include "ata.h" // ATA_CMD_REQUEST_SENSE
Kevin O'Connor7d700252010-02-15 11:56:07 -050013#include "blockcmd.h" // CDB_CMD_REQUEST_SENSE
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050014
15
16/****************************************************************
Kevin O'Connor51cfbe72009-08-18 22:38:49 -040017 * CD emulation
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050018 ****************************************************************/
19
Kevin O'Connord5d02b62010-06-06 16:18:03 -040020struct drive_s *cdemu_drive_gf VAR16VISIBLE;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040021
Kevin O'Connor36c93a52009-09-12 19:35:04 -040022static int
23cdemu_read(struct disk_op_s *op)
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050024{
Kevin O'Connor4a16ef62008-12-31 00:09:28 -050025 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor8f469b92010-02-28 01:28:11 -050026 struct drive_s *drive_g;
27 drive_g = GLOBALFLAT2GLOBAL(GET_EBDA2(ebda_seg, cdemu.emulated_drive_gf));
Kevin O'Connor36c93a52009-09-12 19:35:04 -040028 struct disk_op_s dop;
Kevin O'Connor77d227b2009-10-22 21:48:39 -040029 dop.drive_g = drive_g;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040030 dop.command = op->command;
31 dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + op->lba / 4;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050032
Kevin O'Connor36c93a52009-09-12 19:35:04 -040033 int count = op->count;
34 op->count = 0;
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +020035 u8 *cdbuf_fl = GET_GLOBAL(bounce_buf_fl);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -040036
Kevin O'Connor36c93a52009-09-12 19:35:04 -040037 if (op->lba & 3) {
38 // Partial read of first block.
39 dop.count = 1;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040040 dop.buf_fl = cdbuf_fl;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040041 int ret = process_op(&dop);
42 if (ret)
43 return ret;
44 u8 thiscount = 4 - (op->lba & 3);
45 if (thiscount > count)
46 thiscount = count;
47 count -= thiscount;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040048 memcpy_fl(op->buf_fl, cdbuf_fl + (op->lba & 3) * 512, thiscount * 512);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040049 op->buf_fl += thiscount * 512;
50 op->count += thiscount;
51 dop.lba++;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050052 }
Kevin O'Connor36c93a52009-09-12 19:35:04 -040053
54 if (count > 3) {
55 // Read n number of regular blocks.
56 dop.count = count / 4;
57 dop.buf_fl = op->buf_fl;
58 int ret = process_op(&dop);
59 op->count += dop.count * 4;
60 if (ret)
61 return ret;
62 u8 thiscount = count & ~3;
63 count &= 3;
64 op->buf_fl += thiscount * 512;
65 dop.lba += thiscount / 4;
66 }
67
68 if (count) {
69 // Partial read on last block.
70 dop.count = 1;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040071 dop.buf_fl = cdbuf_fl;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040072 int ret = process_op(&dop);
73 if (ret)
74 return ret;
75 u8 thiscount = count;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040076 memcpy_fl(op->buf_fl, cdbuf_fl, thiscount * 512);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040077 op->count += thiscount;
78 }
79
80 return DISK_RET_SUCCESS;
81}
82
83int
84process_cdemu_op(struct disk_op_s *op)
85{
86 if (!CONFIG_CDROM_EMU)
87 return 0;
88
89 switch (op->command) {
90 case CMD_READ:
91 return cdemu_read(op);
92 case CMD_WRITE:
93 case CMD_FORMAT:
94 return DISK_RET_EWRITEPROTECT;
95 case CMD_VERIFY:
96 case CMD_RESET:
97 case CMD_SEEK:
98 case CMD_ISREADY:
99 return DISK_RET_SUCCESS;
100 default:
101 op->count = 0;
102 return DISK_RET_EPARAM;
103 }
104}
105
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400106void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500107cdemu_setup(void)
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400108{
109 if (!CONFIG_CDROM_EMU)
110 return;
Kevin O'Connora0842f82010-12-29 11:05:46 -0500111 if (!CDCount)
Kevin O'Connord5d02b62010-06-06 16:18:03 -0400112 return;
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +0200113 if (bounce_buf_init() < 0)
114 return;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400115
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500116 struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +0200117 if (!drive_g) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500118 warn_noalloc();
Kevin O'Connord5d02b62010-06-06 16:18:03 -0400119 free(drive_g);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400120 return;
121 }
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500122 cdemu_drive_gf = drive_g;
Kevin O'Connord5d02b62010-06-06 16:18:03 -0400123 memset(drive_g, 0, sizeof(*drive_g));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400124 drive_g->type = DTYPE_CDEMU;
125 drive_g->blksize = DISK_SECTOR_SIZE;
126 drive_g->sectors = (u64)-1;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500127}
128
129struct eltorito_s {
130 u8 size;
131 u8 media;
132 u8 emulated_drive;
133 u8 controller_index;
134 u32 ilba;
135 u16 device_spec;
136 u16 buffer_segment;
137 u16 load_segment;
138 u16 sector_count;
139 u8 cylinders;
140 u8 sectors;
141 u8 heads;
142};
143
144#define SET_INT13ET(regs,var,val) \
145 SET_FARVAR((regs)->ds, ((struct eltorito_s*)((regs)->si+0))->var, (val))
146
147// ElTorito - Terminate disk emu
148void
149cdemu_134b(struct bregs *regs)
150{
151 // FIXME ElTorito Hardcoded
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500152 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500153 SET_INT13ET(regs, size, 0x13);
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500154 SET_INT13ET(regs, media, GET_EBDA2(ebda_seg, cdemu.media));
Kevin O'Connor669e6442009-08-11 22:36:30 -0400155 SET_INT13ET(regs, emulated_drive
156 , GET_EBDA2(ebda_seg, cdemu.emulated_extdrive));
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500157 struct drive_s *drive_gf = GET_EBDA2(ebda_seg, cdemu.emulated_drive_gf);
158 u8 cntl_id = 0;
159 if (drive_gf)
160 cntl_id = GET_GLOBALFLAT(drive_gf->cntl_id);
Kevin O'Connor669e6442009-08-11 22:36:30 -0400161 SET_INT13ET(regs, controller_index, cntl_id / 2);
162 SET_INT13ET(regs, device_spec, cntl_id % 2);
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500163 SET_INT13ET(regs, ilba, GET_EBDA2(ebda_seg, cdemu.ilba));
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500164 SET_INT13ET(regs, buffer_segment, GET_EBDA2(ebda_seg, cdemu.buffer_segment));
165 SET_INT13ET(regs, load_segment, GET_EBDA2(ebda_seg, cdemu.load_segment));
166 SET_INT13ET(regs, sector_count, GET_EBDA2(ebda_seg, cdemu.sector_count));
Kevin O'Connorb68ac712009-08-09 17:25:19 -0400167 SET_INT13ET(regs, cylinders, GET_EBDA2(ebda_seg, cdemu.lchs.cylinders));
168 SET_INT13ET(regs, sectors, GET_EBDA2(ebda_seg, cdemu.lchs.spt));
169 SET_INT13ET(regs, heads, GET_EBDA2(ebda_seg, cdemu.lchs.heads));
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500170
171 // If we have to terminate emulation
172 if (regs->al == 0x00) {
173 // FIXME ElTorito Various. Should be handled accordingly to spec
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500174 SET_EBDA2(ebda_seg, cdemu.active, 0x00); // bye bye
Kevin O'Connor7d700252010-02-15 11:56:07 -0500175
176 // XXX - update floppy/hd count.
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500177 }
178
179 disk_ret(regs, DISK_RET_SUCCESS);
180}
Kevin O'Connor180a9592008-03-04 22:50:53 -0500181
182
183/****************************************************************
184 * CD booting
185 ****************************************************************/
186
Kevin O'Connora05223c2008-06-28 12:15:57 -0400187int
Gleb Natapov4c90a202010-12-07 13:50:54 +0200188cdrom_boot(struct drive_s *drive_g)
Kevin O'Connor180a9592008-03-04 22:50:53 -0500189{
Kevin O'Connor7d700252010-02-15 11:56:07 -0500190 struct disk_op_s dop;
Gleb Natapov4c90a202010-12-07 13:50:54 +0200191 int cdid = getDriveId(EXTTYPE_CD, drive_g);
Kevin O'Connor7d700252010-02-15 11:56:07 -0500192 memset(&dop, 0, sizeof(dop));
Gleb Natapov4c90a202010-12-07 13:50:54 +0200193 dop.drive_g = drive_g;
194 if (!dop.drive_g || cdid < 0)
Kevin O'Connor7da1dcd2009-02-16 10:51:24 -0500195 return 1;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500196
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100197 int ret = scsi_is_ready(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500198 if (ret)
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100199 dprintf(1, "scsi_is_ready returned %d\n", ret);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500200
201 // Read the Boot Record Volume Descriptor
202 u8 buffer[2048];
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500203 dop.lba = 0x11;
204 dop.count = 1;
Kevin O'Connor35ae7262009-01-19 15:44:44 -0500205 dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
Kevin O'Connor76977b22010-02-17 01:01:32 -0500206 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500207 if (ret)
208 return 3;
209
210 // Validity checks
211 if (buffer[0])
212 return 4;
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400213 if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
Kevin O'Connor180a9592008-03-04 22:50:53 -0500214 return 5;
215
216 // ok, now we calculate the Boot catalog address
217 u32 lba = *(u32*)&buffer[0x47];
218
219 // And we read the Boot Catalog
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500220 dop.lba = lba;
Kevin O'Connor76977b22010-02-17 01:01:32 -0500221 dop.count = 1;
222 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500223 if (ret)
224 return 7;
225
226 // Validation entry
227 if (buffer[0x00] != 0x01)
228 return 8; // Header
229 if (buffer[0x01] != 0x00)
230 return 9; // Platform
231 if (buffer[0x1E] != 0x55)
232 return 10; // key 1
233 if (buffer[0x1F] != 0xAA)
234 return 10; // key 2
235
236 // Initial/Default Entry
237 if (buffer[0x20] != 0x88)
238 return 11; // Bootable
239
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500240 u16 ebda_seg = get_ebda_seg();
Kevin O'Connordfa16502008-03-22 20:13:08 -0400241 u8 media = buffer[0x21];
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500242 SET_EBDA2(ebda_seg, cdemu.media, media);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500243
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500244 SET_EBDA2(ebda_seg, cdemu.emulated_drive_gf, dop.drive_g);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500245
246 u16 boot_segment = *(u16*)&buffer[0x22];
247 if (!boot_segment)
248 boot_segment = 0x07C0;
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500249 SET_EBDA2(ebda_seg, cdemu.load_segment, boot_segment);
250 SET_EBDA2(ebda_seg, cdemu.buffer_segment, 0x0000);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500251
252 u16 nbsectors = *(u16*)&buffer[0x26];
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500253 SET_EBDA2(ebda_seg, cdemu.sector_count, nbsectors);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500254
255 lba = *(u32*)&buffer[0x28];
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500256 SET_EBDA2(ebda_seg, cdemu.ilba, lba);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500257
258 // And we read the image in memory
Kevin O'Connora4d70aa2009-08-09 11:32:00 -0400259 dop.lba = lba;
260 dop.count = DIV_ROUND_UP(nbsectors, 4);
Kevin O'Connor35ae7262009-01-19 15:44:44 -0500261 dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
Kevin O'Connor76977b22010-02-17 01:01:32 -0500262 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500263 if (ret)
264 return 12;
265
Kevin O'Connordfa16502008-03-22 20:13:08 -0400266 if (media == 0) {
267 // No emulation requested - return success.
Kevin O'Connor51cfbe72009-08-18 22:38:49 -0400268 SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, EXTSTART_CD + cdid);
Kevin O'Connordfa16502008-03-22 20:13:08 -0400269 return 0;
270 }
271
272 // Emulation of a floppy/harddisk requested
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500273 if (! CONFIG_CDROM_EMU || !cdemu_drive_gf)
Kevin O'Connordfa16502008-03-22 20:13:08 -0400274 return 13;
275
276 // Set emulated drive id and increase bios installed hardware
277 // number of devices
278 if (media < 4) {
279 // Floppy emulation
Kevin O'Connor669e6442009-08-11 22:36:30 -0400280 SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x00);
Kevin O'Connor7d700252010-02-15 11:56:07 -0500281 // XXX - get and set actual floppy count.
Kevin O'Connordfa16502008-03-22 20:13:08 -0400282 SETBITS_BDA(equipment_list_flags, 0x41);
Kevin O'Connor95827c42009-02-07 00:04:57 -0500283
284 switch (media) {
285 case 0x01: // 1.2M floppy
Kevin O'Connorb68ac712009-08-09 17:25:19 -0400286 SET_EBDA2(ebda_seg, cdemu.lchs.spt, 15);
287 SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
288 SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
Kevin O'Connor95827c42009-02-07 00:04:57 -0500289 break;
290 case 0x02: // 1.44M floppy
Kevin O'Connorb68ac712009-08-09 17:25:19 -0400291 SET_EBDA2(ebda_seg, cdemu.lchs.spt, 18);
292 SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
293 SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
Kevin O'Connor95827c42009-02-07 00:04:57 -0500294 break;
295 case 0x03: // 2.88M floppy
Kevin O'Connorb68ac712009-08-09 17:25:19 -0400296 SET_EBDA2(ebda_seg, cdemu.lchs.spt, 36);
297 SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
298 SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
Kevin O'Connor95827c42009-02-07 00:04:57 -0500299 break;
300 }
Kevin O'Connordfa16502008-03-22 20:13:08 -0400301 } else {
302 // Harddrive emulation
Kevin O'Connor669e6442009-08-11 22:36:30 -0400303 SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x80);
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500304 SET_BDA(hdcount, GET_BDA(hdcount) + 1);
Kevin O'Connordfa16502008-03-22 20:13:08 -0400305
Kevin O'Connor95827c42009-02-07 00:04:57 -0500306 // Peak at partition table to get chs.
307 struct mbr_s *mbr = (void*)0;
308 u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl);
309 u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow);
310 u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads);
311
Kevin O'Connorb68ac712009-08-09 17:25:19 -0400312 SET_EBDA2(ebda_seg, cdemu.lchs.spt, sptcyl & 0x3f);
313 SET_EBDA2(ebda_seg, cdemu.lchs.cylinders
314 , ((sptcyl<<2)&0x300) + cyllow + 1);
315 SET_EBDA2(ebda_seg, cdemu.lchs.heads, heads + 1);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500316 }
317
Kevin O'Connordfa16502008-03-22 20:13:08 -0400318 // everything is ok, so from now on, the emulation is active
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500319 SET_EBDA2(ebda_seg, cdemu.active, 0x01);
Kevin O'Connora05223c2008-06-28 12:15:57 -0400320 dprintf(6, "cdemu media=%d\n", media);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500321
322 return 0;
323}