Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


imported SEABIOS source tree
[palacios.git] / bios / seabios / 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 #include "blockcmd.h" // CDB_CMD_REQUEST_SENSE
14
15
16 /****************************************************************
17  * CD emulation
18  ****************************************************************/
19
20 struct drive_s *cdemu_drive_gf VAR16VISIBLE;
21
22 static int
23 cdemu_read(struct disk_op_s *op)
24 {
25     u16 ebda_seg = get_ebda_seg();
26     struct drive_s *drive_g;
27     drive_g = GLOBALFLAT2GLOBAL(GET_EBDA2(ebda_seg, cdemu.emulated_drive_gf));
28     struct disk_op_s dop;
29     dop.drive_g = drive_g;
30     dop.command = op->command;
31     dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + op->lba / 4;
32
33     int count = op->count;
34     op->count = 0;
35     u8 *cdbuf_fl = GET_GLOBAL(bounce_buf_fl);
36
37     if (op->lba & 3) {
38         // Partial read of first block.
39         dop.count = 1;
40         dop.buf_fl = cdbuf_fl;
41         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;
48         memcpy_fl(op->buf_fl, cdbuf_fl + (op->lba & 3) * 512, thiscount * 512);
49         op->buf_fl += thiscount * 512;
50         op->count += thiscount;
51         dop.lba++;
52     }
53
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;
71         dop.buf_fl = cdbuf_fl;
72         int ret = process_op(&dop);
73         if (ret)
74             return ret;
75         u8 thiscount = count;
76         memcpy_fl(op->buf_fl, cdbuf_fl, thiscount * 512);
77         op->count += thiscount;
78     }
79
80     return DISK_RET_SUCCESS;
81 }
82
83 int
84 process_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
106 void
107 cdemu_setup(void)
108 {
109     if (!CONFIG_CDROM_EMU)
110         return;
111     if (!CDCount)
112         return;
113     if (bounce_buf_init() < 0)
114         return;
115
116     struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
117     if (!drive_g) {
118         warn_noalloc();
119         free(drive_g);
120         return;
121     }
122     cdemu_drive_gf = drive_g;
123     memset(drive_g, 0, sizeof(*drive_g));
124     drive_g->type = DTYPE_CDEMU;
125     drive_g->blksize = DISK_SECTOR_SIZE;
126     drive_g->sectors = (u64)-1;
127 }
128
129 struct 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
148 void
149 cdemu_134b(struct bregs *regs)
150 {
151     // FIXME ElTorito Hardcoded
152     u16 ebda_seg = get_ebda_seg();
153     SET_INT13ET(regs, size, 0x13);
154     SET_INT13ET(regs, media, GET_EBDA2(ebda_seg, cdemu.media));
155     SET_INT13ET(regs, emulated_drive
156                 , GET_EBDA2(ebda_seg, cdemu.emulated_extdrive));
157     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);
161     SET_INT13ET(regs, controller_index, cntl_id / 2);
162     SET_INT13ET(regs, device_spec, cntl_id % 2);
163     SET_INT13ET(regs, ilba, GET_EBDA2(ebda_seg, cdemu.ilba));
164     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));
167     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));
170
171     // If we have to terminate emulation
172     if (regs->al == 0x00) {
173         // FIXME ElTorito Various. Should be handled accordingly to spec
174         SET_EBDA2(ebda_seg, cdemu.active, 0x00); // bye bye
175
176         // XXX - update floppy/hd count.
177     }
178
179     disk_ret(regs, DISK_RET_SUCCESS);
180 }
181
182
183 /****************************************************************
184  * CD booting
185  ****************************************************************/
186
187 static int
188 atapi_is_ready(struct disk_op_s *op)
189 {
190     dprintf(6, "atapi_is_ready (drive=%p)\n", op->drive_g);
191
192     /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT is
193      * reported by the device.  If the device reports "IN PROGRESS",
194      * 30 seconds is added. */
195     struct cdbres_read_capacity info;
196     int in_progress = 0;
197     u64 end = calc_future_tsc(5000);
198     for (;;) {
199         if (check_tsc(end)) {
200             dprintf(1, "read capacity failed\n");
201             return -1;
202         }
203
204         int ret = cdb_read_capacity(op, &info);
205         if (!ret)
206             // Success
207             break;
208
209         struct cdbres_request_sense sense;
210         ret = cdb_get_sense(op, &sense);
211         if (ret)
212             // Error - retry.
213             continue;
214
215         // Sense succeeded.
216         if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */
217             dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
218             return -1;
219         }
220
221         if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) {
222             /* IN PROGRESS OF BECOMING READY */
223             printf("Waiting for device to detect medium... ");
224             /* Allow 30 seconds more */
225             end = calc_future_tsc(30000);
226             in_progress = 1;
227         }
228     }
229
230     u32 blksize = ntohl(info.blksize), sectors = ntohl(info.sectors);
231     if (blksize != GET_GLOBAL(op->drive_g->blksize)) {
232         printf("Unsupported sector size %u\n", blksize);
233         return -1;
234     }
235
236     dprintf(6, "sectors=%u\n", sectors);
237     printf("%dMB medium detected\n", sectors>>(20-11));
238     return 0;
239 }
240
241 int
242 cdrom_boot(struct drive_s *drive_g)
243 {
244     struct disk_op_s dop;
245     int cdid = getDriveId(EXTTYPE_CD, drive_g);
246     memset(&dop, 0, sizeof(dop));
247     dop.drive_g = drive_g;
248     if (!dop.drive_g || cdid < 0)
249         return 1;
250
251     int ret = atapi_is_ready(&dop);
252     if (ret)
253         dprintf(1, "atapi_is_ready returned %d\n", ret);
254
255     // Read the Boot Record Volume Descriptor
256     u8 buffer[2048];
257     dop.lba = 0x11;
258     dop.count = 1;
259     dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
260     ret = cdb_read(&dop);
261     if (ret)
262         return 3;
263
264     // Validity checks
265     if (buffer[0])
266         return 4;
267     if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
268         return 5;
269
270     // ok, now we calculate the Boot catalog address
271     u32 lba = *(u32*)&buffer[0x47];
272
273     // And we read the Boot Catalog
274     dop.lba = lba;
275     dop.count = 1;
276     ret = cdb_read(&dop);
277     if (ret)
278         return 7;
279
280     // Validation entry
281     if (buffer[0x00] != 0x01)
282         return 8;   // Header
283     if (buffer[0x01] != 0x00)
284         return 9;   // Platform
285     if (buffer[0x1E] != 0x55)
286         return 10;  // key 1
287     if (buffer[0x1F] != 0xAA)
288         return 10;  // key 2
289
290     // Initial/Default Entry
291     if (buffer[0x20] != 0x88)
292         return 11; // Bootable
293
294     u16 ebda_seg = get_ebda_seg();
295     u8 media = buffer[0x21];
296     SET_EBDA2(ebda_seg, cdemu.media, media);
297
298     SET_EBDA2(ebda_seg, cdemu.emulated_drive_gf, dop.drive_g);
299
300     u16 boot_segment = *(u16*)&buffer[0x22];
301     if (!boot_segment)
302         boot_segment = 0x07C0;
303     SET_EBDA2(ebda_seg, cdemu.load_segment, boot_segment);
304     SET_EBDA2(ebda_seg, cdemu.buffer_segment, 0x0000);
305
306     u16 nbsectors = *(u16*)&buffer[0x26];
307     SET_EBDA2(ebda_seg, cdemu.sector_count, nbsectors);
308
309     lba = *(u32*)&buffer[0x28];
310     SET_EBDA2(ebda_seg, cdemu.ilba, lba);
311
312     // And we read the image in memory
313     dop.lba = lba;
314     dop.count = DIV_ROUND_UP(nbsectors, 4);
315     dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
316     ret = cdb_read(&dop);
317     if (ret)
318         return 12;
319
320     if (media == 0) {
321         // No emulation requested - return success.
322         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, EXTSTART_CD + cdid);
323         return 0;
324     }
325
326     // Emulation of a floppy/harddisk requested
327     if (! CONFIG_CDROM_EMU || !cdemu_drive_gf)
328         return 13;
329
330     // Set emulated drive id and increase bios installed hardware
331     // number of devices
332     if (media < 4) {
333         // Floppy emulation
334         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x00);
335         // XXX - get and set actual floppy count.
336         SETBITS_BDA(equipment_list_flags, 0x41);
337
338         switch (media) {
339         case 0x01:  // 1.2M floppy
340             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 15);
341             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
342             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
343             break;
344         case 0x02:  // 1.44M floppy
345             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 18);
346             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
347             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
348             break;
349         case 0x03:  // 2.88M floppy
350             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 36);
351             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
352             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
353             break;
354         }
355     } else {
356         // Harddrive emulation
357         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x80);
358         SET_BDA(hdcount, GET_BDA(hdcount) + 1);
359
360         // Peak at partition table to get chs.
361         struct mbr_s *mbr = (void*)0;
362         u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl);
363         u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow);
364         u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads);
365
366         SET_EBDA2(ebda_seg, cdemu.lchs.spt, sptcyl & 0x3f);
367         SET_EBDA2(ebda_seg, cdemu.lchs.cylinders
368                   , ((sptcyl<<2)&0x300) + cyllow + 1);
369         SET_EBDA2(ebda_seg, cdemu.lchs.heads, heads + 1);
370     }
371
372     // everything is ok, so from now on, the emulation is active
373     SET_EBDA2(ebda_seg, cdemu.active, 0x01);
374     dprintf(6, "cdemu media=%d\n", media);
375
376     return 0;
377 }