blob: 01eb87a4773a545aaff18dc8ef5f903e49fb8e50 [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
Kevin O'Connord3140832012-05-13 22:46:12 -040011#include "biosvar.h" // GET_GLOBAL
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
Kevin O'Connord3140832012-05-13 22:46:12 -040015// Locks for removable devices
16u8 CDRom_locks[CONFIG_MAX_EXTDRIVE] VARLOW;
17
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050018
19/****************************************************************
Kevin O'Connor51cfbe72009-08-18 22:38:49 -040020 * CD emulation
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050021 ****************************************************************/
22
Kevin O'Connord3140832012-05-13 22:46:12 -040023struct cdemu_s CDEmu VARLOW;
Kevin O'Connor89a2f962013-02-18 23:36:03 -050024struct drive_s *cdemu_drive_gf VARFSEG;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040025
Kevin O'Connor36c93a52009-09-12 19:35:04 -040026static int
27cdemu_read(struct disk_op_s *op)
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050028{
Kevin O'Connor8f469b92010-02-28 01:28:11 -050029 struct drive_s *drive_g;
Kevin O'Connord3140832012-05-13 22:46:12 -040030 drive_g = GLOBALFLAT2GLOBAL(GET_LOW(CDEmu.emulated_drive_gf));
Kevin O'Connor36c93a52009-09-12 19:35:04 -040031 struct disk_op_s dop;
Kevin O'Connor77d227b2009-10-22 21:48:39 -040032 dop.drive_g = drive_g;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040033 dop.command = op->command;
Kevin O'Connord3140832012-05-13 22:46:12 -040034 dop.lba = GET_LOW(CDEmu.ilba) + op->lba / 4;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050035
Kevin O'Connor36c93a52009-09-12 19:35:04 -040036 int count = op->count;
37 op->count = 0;
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +020038 u8 *cdbuf_fl = GET_GLOBAL(bounce_buf_fl);
Kevin O'Connoraa2590c2008-03-22 23:13:24 -040039
Kevin O'Connor36c93a52009-09-12 19:35:04 -040040 if (op->lba & 3) {
41 // Partial read of first block.
42 dop.count = 1;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040043 dop.buf_fl = cdbuf_fl;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040044 int ret = process_op(&dop);
45 if (ret)
46 return ret;
47 u8 thiscount = 4 - (op->lba & 3);
48 if (thiscount > count)
49 thiscount = count;
50 count -= thiscount;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040051 memcpy_fl(op->buf_fl, cdbuf_fl + (op->lba & 3) * 512, thiscount * 512);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040052 op->buf_fl += thiscount * 512;
53 op->count += thiscount;
54 dop.lba++;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -050055 }
Kevin O'Connor36c93a52009-09-12 19:35:04 -040056
57 if (count > 3) {
58 // Read n number of regular blocks.
59 dop.count = count / 4;
60 dop.buf_fl = op->buf_fl;
61 int ret = process_op(&dop);
62 op->count += dop.count * 4;
63 if (ret)
64 return ret;
65 u8 thiscount = count & ~3;
66 count &= 3;
67 op->buf_fl += thiscount * 512;
68 dop.lba += thiscount / 4;
69 }
70
71 if (count) {
72 // Partial read on last block.
73 dop.count = 1;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040074 dop.buf_fl = cdbuf_fl;
Kevin O'Connor36c93a52009-09-12 19:35:04 -040075 int ret = process_op(&dop);
76 if (ret)
77 return ret;
78 u8 thiscount = count;
Kevin O'Connord5d02b62010-06-06 16:18:03 -040079 memcpy_fl(op->buf_fl, cdbuf_fl, thiscount * 512);
Kevin O'Connor36c93a52009-09-12 19:35:04 -040080 op->count += thiscount;
81 }
82
83 return DISK_RET_SUCCESS;
84}
85
86int
87process_cdemu_op(struct disk_op_s *op)
88{
89 if (!CONFIG_CDROM_EMU)
90 return 0;
91
92 switch (op->command) {
93 case CMD_READ:
94 return cdemu_read(op);
95 case CMD_WRITE:
96 case CMD_FORMAT:
97 return DISK_RET_EWRITEPROTECT;
98 case CMD_VERIFY:
99 case CMD_RESET:
100 case CMD_SEEK:
101 case CMD_ISREADY:
102 return DISK_RET_SUCCESS;
103 default:
104 op->count = 0;
105 return DISK_RET_EPARAM;
106 }
107}
108
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400109void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500110cdrom_prepboot(void)
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400111{
112 if (!CONFIG_CDROM_EMU)
113 return;
Kevin O'Connora0842f82010-12-29 11:05:46 -0500114 if (!CDCount)
Kevin O'Connord5d02b62010-06-06 16:18:03 -0400115 return;
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500116 if (create_bounce_buf() < 0)
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +0200117 return;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400118
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500119 struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +0200120 if (!drive_g) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500121 warn_noalloc();
Kevin O'Connord5d02b62010-06-06 16:18:03 -0400122 free(drive_g);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400123 return;
124 }
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500125 cdemu_drive_gf = drive_g;
Kevin O'Connord5d02b62010-06-06 16:18:03 -0400126 memset(drive_g, 0, sizeof(*drive_g));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400127 drive_g->type = DTYPE_CDEMU;
128 drive_g->blksize = DISK_SECTOR_SIZE;
129 drive_g->sectors = (u64)-1;
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500130}
131
132struct eltorito_s {
133 u8 size;
134 u8 media;
135 u8 emulated_drive;
136 u8 controller_index;
137 u32 ilba;
138 u16 device_spec;
139 u16 buffer_segment;
140 u16 load_segment;
141 u16 sector_count;
142 u8 cylinders;
143 u8 sectors;
144 u8 heads;
145};
146
147#define SET_INT13ET(regs,var,val) \
148 SET_FARVAR((regs)->ds, ((struct eltorito_s*)((regs)->si+0))->var, (val))
149
150// ElTorito - Terminate disk emu
151void
152cdemu_134b(struct bregs *regs)
153{
154 // FIXME ElTorito Hardcoded
155 SET_INT13ET(regs, size, 0x13);
Kevin O'Connord3140832012-05-13 22:46:12 -0400156 SET_INT13ET(regs, media, GET_LOW(CDEmu.media));
157 SET_INT13ET(regs, emulated_drive, GET_LOW(CDEmu.emulated_extdrive));
158 struct drive_s *drive_gf = GET_LOW(CDEmu.emulated_drive_gf);
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500159 u8 cntl_id = 0;
160 if (drive_gf)
161 cntl_id = GET_GLOBALFLAT(drive_gf->cntl_id);
Kevin O'Connor669e6442009-08-11 22:36:30 -0400162 SET_INT13ET(regs, controller_index, cntl_id / 2);
163 SET_INT13ET(regs, device_spec, cntl_id % 2);
Kevin O'Connord3140832012-05-13 22:46:12 -0400164 SET_INT13ET(regs, ilba, GET_LOW(CDEmu.ilba));
165 SET_INT13ET(regs, buffer_segment, GET_LOW(CDEmu.buffer_segment));
166 SET_INT13ET(regs, load_segment, GET_LOW(CDEmu.load_segment));
167 SET_INT13ET(regs, sector_count, GET_LOW(CDEmu.sector_count));
168 SET_INT13ET(regs, cylinders, GET_LOW(CDEmu.lchs.cylinders));
169 SET_INT13ET(regs, sectors, GET_LOW(CDEmu.lchs.spt));
170 SET_INT13ET(regs, heads, GET_LOW(CDEmu.lchs.heads));
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500171
172 // If we have to terminate emulation
173 if (regs->al == 0x00) {
174 // FIXME ElTorito Various. Should be handled accordingly to spec
Kevin O'Connord3140832012-05-13 22:46:12 -0400175 SET_LOW(CDEmu.active, 0x00); // bye bye
Kevin O'Connor7d700252010-02-15 11:56:07 -0500176
177 // XXX - update floppy/hd count.
Kevin O'Connor31d8c8a2008-03-04 19:56:41 -0500178 }
179
180 disk_ret(regs, DISK_RET_SUCCESS);
181}
Kevin O'Connor180a9592008-03-04 22:50:53 -0500182
183
184/****************************************************************
185 * CD booting
186 ****************************************************************/
187
Kevin O'Connora05223c2008-06-28 12:15:57 -0400188int
Gleb Natapov4c90a202010-12-07 13:50:54 +0200189cdrom_boot(struct drive_s *drive_g)
Kevin O'Connor180a9592008-03-04 22:50:53 -0500190{
Kevin O'Connord3140832012-05-13 22:46:12 -0400191 ASSERT32FLAT();
Kevin O'Connor7d700252010-02-15 11:56:07 -0500192 struct disk_op_s dop;
Gleb Natapov4c90a202010-12-07 13:50:54 +0200193 int cdid = getDriveId(EXTTYPE_CD, drive_g);
Kevin O'Connor7d700252010-02-15 11:56:07 -0500194 memset(&dop, 0, sizeof(dop));
Gleb Natapov4c90a202010-12-07 13:50:54 +0200195 dop.drive_g = drive_g;
196 if (!dop.drive_g || cdid < 0)
Kevin O'Connor7da1dcd2009-02-16 10:51:24 -0500197 return 1;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500198
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100199 int ret = scsi_is_ready(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500200 if (ret)
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100201 dprintf(1, "scsi_is_ready returned %d\n", ret);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500202
203 // Read the Boot Record Volume Descriptor
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400204 u8 buffer[CDROM_SECTOR_SIZE];
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500205 dop.lba = 0x11;
206 dop.count = 1;
Kevin O'Connord3140832012-05-13 22:46:12 -0400207 dop.buf_fl = buffer;
Kevin O'Connor76977b22010-02-17 01:01:32 -0500208 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500209 if (ret)
210 return 3;
211
212 // Validity checks
213 if (buffer[0])
214 return 4;
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400215 if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
Kevin O'Connor180a9592008-03-04 22:50:53 -0500216 return 5;
217
218 // ok, now we calculate the Boot catalog address
219 u32 lba = *(u32*)&buffer[0x47];
220
221 // And we read the Boot Catalog
Kevin O'Connor4524bf72008-12-31 00:31:03 -0500222 dop.lba = lba;
Kevin O'Connor76977b22010-02-17 01:01:32 -0500223 dop.count = 1;
224 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500225 if (ret)
226 return 7;
227
228 // Validation entry
229 if (buffer[0x00] != 0x01)
230 return 8; // Header
231 if (buffer[0x01] != 0x00)
232 return 9; // Platform
233 if (buffer[0x1E] != 0x55)
234 return 10; // key 1
235 if (buffer[0x1F] != 0xAA)
236 return 10; // key 2
237
238 // Initial/Default Entry
239 if (buffer[0x20] != 0x88)
240 return 11; // Bootable
241
Kevin O'Connordfa16502008-03-22 20:13:08 -0400242 u8 media = buffer[0x21];
Kevin O'Connord3140832012-05-13 22:46:12 -0400243 CDEmu.media = media;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500244
Kevin O'Connord3140832012-05-13 22:46:12 -0400245 CDEmu.emulated_drive_gf = dop.drive_g;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500246
247 u16 boot_segment = *(u16*)&buffer[0x22];
248 if (!boot_segment)
249 boot_segment = 0x07C0;
Kevin O'Connord3140832012-05-13 22:46:12 -0400250 CDEmu.load_segment = boot_segment;
251 CDEmu.buffer_segment = 0x0000;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500252
253 u16 nbsectors = *(u16*)&buffer[0x26];
Kevin O'Connord3140832012-05-13 22:46:12 -0400254 CDEmu.sector_count = nbsectors;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500255
256 lba = *(u32*)&buffer[0x28];
Kevin O'Connord3140832012-05-13 22:46:12 -0400257 CDEmu.ilba = lba;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500258
259 // And we read the image in memory
Kevin O'Connora4d70aa2009-08-09 11:32:00 -0400260 dop.lba = lba;
261 dop.count = DIV_ROUND_UP(nbsectors, 4);
Kevin O'Connor35ae7262009-01-19 15:44:44 -0500262 dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
Kevin O'Connor76977b22010-02-17 01:01:32 -0500263 ret = cdb_read(&dop);
Kevin O'Connor180a9592008-03-04 22:50:53 -0500264 if (ret)
265 return 12;
266
Kevin O'Connordfa16502008-03-22 20:13:08 -0400267 if (media == 0) {
268 // No emulation requested - return success.
Kevin O'Connord3140832012-05-13 22:46:12 -0400269 CDEmu.emulated_extdrive = EXTSTART_CD + cdid;
Kevin O'Connordfa16502008-03-22 20:13:08 -0400270 return 0;
271 }
272
273 // Emulation of a floppy/harddisk requested
Kevin O'Connor8f469b92010-02-28 01:28:11 -0500274 if (! CONFIG_CDROM_EMU || !cdemu_drive_gf)
Kevin O'Connordfa16502008-03-22 20:13:08 -0400275 return 13;
276
277 // Set emulated drive id and increase bios installed hardware
278 // number of devices
279 if (media < 4) {
280 // Floppy emulation
Kevin O'Connord3140832012-05-13 22:46:12 -0400281 CDEmu.emulated_extdrive = 0x00;
Kevin O'Connor7d700252010-02-15 11:56:07 -0500282 // XXX - get and set actual floppy count.
Kevin O'Connore51316d2012-06-10 09:09:22 -0400283 set_equipment_flags(0x41, 0x41);
Kevin O'Connor95827c42009-02-07 00:04:57 -0500284
285 switch (media) {
286 case 0x01: // 1.2M floppy
Kevin O'Connord3140832012-05-13 22:46:12 -0400287 CDEmu.lchs.spt = 15;
288 CDEmu.lchs.cylinders = 80;
289 CDEmu.lchs.heads = 2;
Kevin O'Connor95827c42009-02-07 00:04:57 -0500290 break;
291 case 0x02: // 1.44M floppy
Kevin O'Connord3140832012-05-13 22:46:12 -0400292 CDEmu.lchs.spt = 18;
293 CDEmu.lchs.cylinders = 80;
294 CDEmu.lchs.heads = 2;
Kevin O'Connor95827c42009-02-07 00:04:57 -0500295 break;
296 case 0x03: // 2.88M floppy
Kevin O'Connord3140832012-05-13 22:46:12 -0400297 CDEmu.lchs.spt = 36;
298 CDEmu.lchs.cylinders = 80;
299 CDEmu.lchs.heads = 2;
Kevin O'Connor95827c42009-02-07 00:04:57 -0500300 break;
301 }
Kevin O'Connordfa16502008-03-22 20:13:08 -0400302 } else {
303 // Harddrive emulation
Kevin O'Connord3140832012-05-13 22:46:12 -0400304 CDEmu.emulated_extdrive = 0x80;
Kevin O'Connor4a16ef62008-12-31 00:09:28 -0500305 SET_BDA(hdcount, GET_BDA(hdcount) + 1);
Kevin O'Connordfa16502008-03-22 20:13:08 -0400306
Kevin O'Connor95827c42009-02-07 00:04:57 -0500307 // Peak at partition table to get chs.
308 struct mbr_s *mbr = (void*)0;
309 u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl);
310 u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow);
311 u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads);
312
Kevin O'Connord3140832012-05-13 22:46:12 -0400313 CDEmu.lchs.spt = sptcyl & 0x3f;
314 CDEmu.lchs.cylinders = ((sptcyl<<2)&0x300) + cyllow + 1;
315 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'Connord3140832012-05-13 22:46:12 -0400319 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}