VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/eltorito.c

Last change on this file was 100713, checked in by vboxsync, 9 months ago

BIOS: Needed to adjust int13_cdrom() signature after orgs.asm changes, duh.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.0 KB
Line 
1/* $Id: eltorito.c 100713 2023-07-27 09:30:15Z vboxsync $ */
2/** @file
3 * PC BIOS - ???
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 * --------------------------------------------------------------------
27 *
28 * This code is based on:
29 *
30 * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
31 *
32 * Copyright (C) 2002 MandrakeSoft S.A.
33 *
34 * MandrakeSoft S.A.
35 * 43, rue d'Aboukir
36 * 75002 Paris - France
37 * http://www.linux-mandrake.com/
38 * http://www.mandrakesoft.com/
39 *
40 * This library is free software; you can redistribute it and/or
41 * modify it under the terms of the GNU Lesser General Public
42 * License as published by the Free Software Foundation; either
43 * version 2 of the License, or (at your option) any later version.
44 *
45 * This library is distributed in the hope that it will be useful,
46 * but WITHOUT ANY WARRANTY; without even the implied warranty of
47 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
48 * Lesser General Public License for more details.
49 *
50 * You should have received a copy of the GNU Lesser General Public
51 * License along with this library; if not, write to the Free Software
52 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
53 *
54 */
55
56/*
57 * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
58 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
59 * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
60 * a choice of LGPL license versions is made available with the language indicating
61 * that LGPLv2 or any later version may be used, or where a choice of which version
62 * of the LGPL is applied is otherwise unspecified.
63 */
64
65
66#include <stdint.h>
67#include <string.h>
68#include "inlines.h"
69#include "biosint.h"
70#include "ebda.h"
71#include "ata.h"
72
73#if DEBUG_ELTORITO
74# define BX_DEBUG_INT13_ET(...) BX_DEBUG(__VA_ARGS__)
75#else
76# define BX_DEBUG_INT13_ET(...)
77#endif
78
79#if DEBUG_INT13_CD
80# define BX_DEBUG_INT13_CD(...) BX_DEBUG(__VA_ARGS__)
81#else
82# define BX_DEBUG_INT13_CD(...)
83#endif
84
85#if DEBUG_CD_BOOT
86# define BX_DEBUG_ELTORITO(...) BX_DEBUG(__VA_ARGS__)
87#else
88# define BX_DEBUG_ELTORITO(...)
89#endif
90
91#define MIN(a, b) ((a) < (b) ? (a) : (b))
92
93/// @todo put in a header
94#define AX r.gr.u.r16.ax
95#define BX r.gr.u.r16.bx
96#define CX r.gr.u.r16.cx
97#define DX r.gr.u.r16.dx
98#define SI r.gr.u.r16.si
99#define DI r.gr.u.r16.di
100#define BP r.gr.u.r16.bp
101#define ELDX r.gr.u.r16.sp
102#define DS r.ds
103#define ES r.es
104#define FLAGS r.ra.flags.u.r16.flags
105
106#pragma pack(1)
107
108/* READ_10/WRITE_10 CDB padded to 12 bytes for ATAPI. */
109typedef struct {
110 uint16_t command; /* Command. */
111 uint32_t lba; /* LBA, MSB first! */
112 uint8_t pad1; /* Unused. */
113 uint16_t nsect; /* Sector count, MSB first! */
114 uint8_t pad2[3]; /* Unused. */
115} cdb_atapi;
116
117#pragma pack()
118
119ct_assert(sizeof(cdb_atapi) == 12);
120
121/* Generic ATAPI/SCSI CD-ROM access routine signature. */
122typedef uint16_t (* cd_pkt_func)(uint16_t device_id, uint8_t cmdlen, char __far *cmdbuf,
123 uint32_t length, uint8_t inout, char __far *buffer);
124
125/* Pointers to HW specific CD-ROM access routines. */
126cd_pkt_func pktacc[DSKTYP_CNT] = {
127 [DSK_TYPE_ATAPI] = { ata_cmd_packet },
128#ifdef VBOX_WITH_AHCI
129 [DSK_TYPE_AHCI] = { ahci_cmd_packet },
130#endif
131#ifdef VBOX_WITH_SCSI
132 [DSK_TYPE_SCSI] = { scsi_cmd_packet },
133#endif
134};
135
136#if defined(VBOX_WITH_AHCI) || defined(VBOX_WITH_SCSI)
137uint16_t dummy_soft_reset(uint16_t device_id)
138{
139 return 0;
140}
141#endif
142
143/* Generic reset routine signature. */
144typedef uint16_t (* cd_rst_func)(uint16_t device_id);
145
146/* Pointers to HW specific CD-ROM reset routines. */
147cd_rst_func softrst[DSKTYP_CNT] = {
148 [DSK_TYPE_ATAPI] = { ata_soft_reset },
149#ifdef VBOX_WITH_AHCI
150 [DSK_TYPE_AHCI] = { dummy_soft_reset },
151#endif
152#ifdef VBOX_WITH_SCSI
153 [DSK_TYPE_SCSI] = { dummy_soft_reset },
154#endif
155};
156
157
158// ---------------------------------------------------------------------------
159// Start of El-Torito boot functions
160// ---------------------------------------------------------------------------
161
162// !! TODO !! convert EBDA accesses to far pointers
163
164extern int diskette_param_table;
165
166/**
167 * Allocates 2K of conventional memory.
168 */
169static uint16_t cdemu_bounce_buf_alloc(void)
170{
171 uint16_t base_mem_kb;
172 uint16_t bounce_seg;
173
174 base_mem_kb = read_word(0x00, 0x0413);
175 if (base_mem_kb == 0)
176 return 0;
177
178 base_mem_kb -= 2;
179 bounce_seg = (((uint32_t)base_mem_kb * 1024) >> 4); /* Calculate start segment. */
180
181 write_word(0x00, 0x0413, base_mem_kb);
182
183 return bounce_seg;
184}
185
186void BIOSCALL cdemu_init(void)
187{
188 /// @todo a macro or a function for getting the EBDA segment
189 uint16_t ebda_seg = read_word(0x0040,0x000E);
190 cdemu_t __far *cdemu = ebda_seg :> &EbdaData->cdemu;
191
192 // the only important data is this one for now
193 cdemu->active = 0x00;
194}
195
196uint8_t BIOSCALL cdemu_isactive(void)
197{
198 /// @todo a macro or a function for getting the EBDA segment
199 uint16_t ebda_seg = read_word(0x0040,0x000E);
200
201 return read_byte(ebda_seg,(uint16_t)&EbdaData->cdemu.active);
202}
203
204uint8_t BIOSCALL cdemu_emulated_drive(void)
205{
206 /// @todo a macro or a function for getting the EBDA segment
207 uint16_t ebda_seg = read_word(0x0040,0x000E);
208
209 return read_byte(ebda_seg,(uint16_t)&EbdaData->cdemu.emulated_drive);
210}
211
212// ---------------------------------------------------------------------------
213// Start of int13 for eltorito functions
214// ---------------------------------------------------------------------------
215
216void BIOSCALL int13_eltorito(disk_regs_t r)
217{
218 /// @todo a macro or a function for getting the EBDA segment
219 uint16_t ebda_seg=read_word(0x0040,0x000E);
220 cdemu_t __far *cdemu;
221
222 cdemu = ebda_seg :> &EbdaData->cdemu;
223
224
225 BX_DEBUG_INT13_ET("%s: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", __func__, AX, BX, CX, DX, ES);
226 // BX_DEBUG_INT13_ET("%s: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n", __func__, get_SS(), DS, ES, DI, SI);
227
228 switch (GET_AH()) {
229
230 // FIXME ElTorito Various. Not implemented in many real BIOSes.
231 case 0x4a: // ElTorito - Initiate disk emu
232 case 0x4c: // ElTorito - Initiate disk emu and boot
233 case 0x4d: // ElTorito - Return Boot catalog
234 BX_INFO("%s: call with AX=%04x not implemented.\n", __func__, AX);
235 goto int13_fail;
236 break;
237
238 case 0x4b: // ElTorito - Terminate disk emu
239 // FIXME ElTorito Hardcoded
240 /// @todo maybe our cdemu struct should match El Torito to allow memcpy()?
241 write_byte(DS,SI+0x00,0x13);
242 write_byte(DS,SI+0x01,cdemu->media);
243 write_byte(DS,SI+0x02,cdemu->emulated_drive);
244 write_byte(DS,SI+0x03,cdemu->controller_index);
245 write_dword(DS,SI+0x04,cdemu->ilba);
246 write_word(DS,SI+0x08,cdemu->device_spec);
247 write_word(DS,SI+0x0a,cdemu->buffer_segment);
248 write_word(DS,SI+0x0c,cdemu->load_segment);
249 write_word(DS,SI+0x0e,cdemu->sector_count);
250 write_byte(DS,SI+0x10,cdemu->vdevice.cylinders);
251 write_byte(DS,SI+0x11,cdemu->vdevice.spt);
252 write_byte(DS,SI+0x12,cdemu->vdevice.heads);
253
254 // If we have to terminate emulation
255 if(GET_AL() == 0x00) {
256 // FIXME ElTorito Various. Should be handled accordingly to spec
257 cdemu->active = 0; // bye bye
258 }
259
260 goto int13_success;
261 break;
262
263 default:
264 BX_INFO("%s: unsupported AH=%02x\n", __func__, GET_AH());
265 goto int13_fail;
266 break;
267 }
268
269int13_fail:
270 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
271 SET_DISK_RET_STATUS(GET_AH());
272 SET_CF(); // error occurred
273 return;
274
275int13_success:
276 SET_AH(0x00); // no error
277 SET_DISK_RET_STATUS(0x00);
278 CLEAR_CF(); // no error
279 return;
280}
281
282// ---------------------------------------------------------------------------
283// End of int13 for eltorito functions
284// ---------------------------------------------------------------------------
285
286/* Utility routine to check if a device is a CD-ROM. */
287/// @todo this function is kinda useless as the ATAPI type check is obsolete.
288static uint16_t device_is_cdrom(uint8_t device)
289{
290 bio_dsk_t __far *bios_dsk;
291
292 bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk;
293
294 if (device >= BX_MAX_STORAGE_DEVICES)
295 return 0;
296
297// if (bios_dsk->devices[device].type != DSK_TYPE_ATAPI)
298// return 0;
299
300 if (bios_dsk->devices[device].device != DSK_DEVICE_CDROM)
301 return 0;
302
303 return 1;
304}
305
306static uint16_t cdrom_read(uint8_t device, uint32_t lba, uint16_t nbsectors, void __far *buf)
307{
308 uint16_t ebda_seg=read_word(0x0040,0x000E);
309 cdb_atapi atapicmd;
310 bio_dsk_t __far *bios_dsk = ebda_seg :> &EbdaData->bdisk;
311
312 atapicmd.command = 0x28; // READ 10 command
313 atapicmd.lba = swap_32(lba);
314 atapicmd.nsect = swap_16(nbsectors);
315
316 bios_dsk->drqp.nsect = nbsectors;
317 bios_dsk->drqp.sect_sz = 2048L;
318
319 return pktacc[bios_dsk->devices[device].type](device, 12, (char __far *)&atapicmd, nbsectors*2048L, ATA_DATA_IN, buf);
320}
321
322static uint16_t cdemu_read(uint8_t device, uint32_t lba, uint16_t nbsectors, void __far *buf)
323{
324 uint16_t ebda_seg=read_word(0x0040,0x000E);
325 uint16_t error;
326 cdemu_t __far *cdemu = ebda_seg :> &EbdaData->cdemu;
327 uint32_t ilba = cdemu->ilba;
328 uint32_t slba;
329 uint16_t before;
330 uint8_t __far *dst = (uint8_t __far *)buf;
331
332 BX_DEBUG_ELTORITO("cdemu_read: lba=%lu nbsectors=%u\n", lba, nbsectors);
333
334 // start lba on cd
335 slba = (uint32_t)lba / 4;
336 before = (uint32_t)lba % 4;
337
338 // Unaligned start will go to a bounce buffer first.
339 if (before)
340 {
341 uint16_t xfer_sect = MIN(nbsectors, 4 - before);
342
343 error = cdrom_read(device, ilba + slba, 1, cdemu->ptr_unaligned);
344 if (error != 0)
345 return error;
346
347 _fmemcpy(dst, cdemu->ptr_unaligned + before * 512L, xfer_sect * 512L);
348 dst += xfer_sect * 512L;
349 nbsectors -= xfer_sect;
350 slba++;
351 }
352
353 // Now for the aligned part.
354 if (nbsectors / 4)
355 {
356 uint16_t xfer_sect = nbsectors / 4;
357
358 error = cdrom_read(device, ilba + slba, xfer_sect, dst);
359 if (error != 0)
360 return error;
361 dst += xfer_sect * 2048L;
362 nbsectors -= xfer_sect * 4;
363 slba += xfer_sect;
364 }
365
366 // Now for the unaligned end.
367 if (nbsectors)
368 {
369 error = cdrom_read(device, ilba + slba, 1, cdemu->ptr_unaligned);
370 if (error != 0)
371 return error;
372
373 _fmemcpy(dst, cdemu->ptr_unaligned, nbsectors * 512);
374 }
375
376 return error;
377}
378
379// ---------------------------------------------------------------------------
380// End of ATA/ATAPI generic functions
381// ---------------------------------------------------------------------------
382static const char isotag[]="CD001";
383static const char eltorito[]="EL TORITO SPECIFICATION";
384//
385// Returns ah: emulated drive, al: error code
386//
387uint16_t cdrom_boot(void)
388{
389 /// @todo a macro or a function for getting the EBDA segment
390 uint16_t ebda_seg=read_word(0x0040,0x000E);
391 uint8_t buffer[2048];
392 uint32_t lba;
393 uint16_t boot_segment, nbsectors, i, error;
394 uint8_t device;
395 uint8_t read_try;
396 cdemu_t __far *cdemu;
397 bio_dsk_t __far *bios_dsk;
398
399 cdemu = ebda_seg :> &EbdaData->cdemu;
400 bios_dsk = ebda_seg :> &EbdaData->bdisk;
401
402 /* Find the first CD-ROM. */
403 for (device = 0; device < BX_MAX_STORAGE_DEVICES; ++device) {
404 if (device_is_cdrom(device))
405 break;
406 }
407
408 /* Fail if not found. */
409 if (device >= BX_MAX_STORAGE_DEVICES)
410 return 2;
411
412 /* Read the Boot Record Volume Descriptor (BRVD). */
413 for (read_try = 0; read_try <= 4; ++read_try)
414 {
415 error = cdrom_read(device, 0x11, 1, &buffer);
416 if (!error)
417 break;
418 }
419 if (error)
420 return 3;
421
422 /* Check for a valid BRVD. */
423 if (buffer[0] != 0)
424 return 4;
425 /// @todo what's wrong with memcmp()?
426 for (i = 0; i < 5; ++i) {
427 if (buffer[1+i] != isotag[i])
428 return 5;
429 }
430 for (i = 0; i < 23; ++i)
431 if (buffer[7+i] != eltorito[i])
432 return 6;
433
434 // ok, now we calculate the Boot catalog address
435 lba = *((uint32_t *)&buffer[0x47]);
436 BX_DEBUG_ELTORITO("BRVD at LBA %lx\n", lba);
437
438 /* Now we read the Boot Catalog. */
439 error = cdrom_read(device, lba, 1, buffer);
440 if (error != 0)
441 return 7;
442
443 /// @todo Define a struct for the Boot Catalog, the hardcoded offsets are so dumb...
444
445 /* Check if the Boot Catalog looks valid. */
446 if (buffer[0x00] != 0x01)
447 return 8; // Header
448 if (buffer[0x01] != 0x00)
449 return 9; // Platform
450 if (buffer[0x1E] != 0x55)
451 return 10; // key 1
452 if (buffer[0x1F] != 0xAA)
453 return 10; // key 2
454
455 // Initial/Default Entry
456 if (buffer[0x20] != 0x88)
457 return 11; // Bootable
458
459 cdemu->media = buffer[0x21];
460 if (buffer[0x21] == 0) {
461 // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
462 // Win2000 cd boot needs to know it booted from cd
463 cdemu->emulated_drive = 0xE0;
464 }
465 else if (buffer[0x21] < 4)
466 cdemu->emulated_drive = 0x00;
467 else
468 cdemu->emulated_drive = 0x80;
469
470 cdemu->controller_index = device / 2;
471 cdemu->device_spec = device % 2;
472
473 boot_segment = *((uint16_t *)&buffer[0x22]);
474 if (boot_segment == 0)
475 boot_segment = 0x07C0;
476
477 cdemu->load_segment = boot_segment;
478 cdemu->buffer_segment = 0x0000;
479
480 nbsectors = ((uint16_t *)buffer)[0x26 / 2];
481 cdemu->sector_count = nbsectors;
482
483 /* Sanity check the sector count. In incorrectly mastered CDs, it might
484 * be zero. If it's more than 512K, reject it as well.
485 */
486 if (nbsectors == 0 || nbsectors > 1024)
487 return 12;
488
489 lba = *((uint32_t *)&buffer[0x28]);
490 cdemu->ilba = lba;
491
492 BX_DEBUG_ELTORITO("Emulate drive %02x, type %02x, LBA %lu\n",
493 cdemu->emulated_drive, cdemu->media, cdemu->ilba);
494
495 /* Now that we know El Torito emulation is in use, allocate buffer. */
496 cdemu->ptr_unaligned = cdemu_bounce_buf_alloc() :> 0;
497 if (cdemu->ptr_unaligned == NULL)
498 return 13;
499
500 /* Read the disk image's boot sector into memory. */
501 error = cdemu_read(device, 0, nbsectors, MK_FP(boot_segment,0));
502 if (error != 0)
503 return 14;
504
505 BX_DEBUG_ELTORITO("Emulate drive %02x, type %02x, LBA %lu\n",
506 cdemu->emulated_drive, cdemu->media, cdemu->ilba);
507 /* Set up emulated drive geometry based on the media type. */
508 switch (cdemu->media) {
509 case 0x01: /* 1.2M floppy */
510 cdemu->vdevice.spt = 15;
511 cdemu->vdevice.cylinders = 80;
512 cdemu->vdevice.heads = 2;
513 break;
514 case 0x02: /* 1.44M floppy */
515 cdemu->vdevice.spt = 18;
516 cdemu->vdevice.cylinders = 80;
517 cdemu->vdevice.heads = 2;
518 break;
519 case 0x03: /* 2.88M floppy */
520 cdemu->vdevice.spt = 36;
521 cdemu->vdevice.cylinders = 80;
522 cdemu->vdevice.heads = 2;
523 break;
524 case 0x04: /* Hard disk */
525 cdemu->vdevice.spt = read_byte(boot_segment,446+6)&0x3f;
526 cdemu->vdevice.cylinders = ((read_byte(boot_segment,446+6)&~0x3f)<<2) + read_byte(boot_segment,446+7) + 1;
527 cdemu->vdevice.heads = read_byte(boot_segment,446+5) + 1;
528 break;
529 }
530 BX_DEBUG_ELTORITO("VCHS=%u/%u/%u\n", cdemu->vdevice.cylinders,
531 cdemu->vdevice.heads, cdemu->vdevice.spt);
532
533 if (cdemu->media != 0) {
534 /* Increase BIOS installed number of drives (floppy or fixed). */
535 if (cdemu->emulated_drive == 0x00)
536 write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
537 else
538 write_byte(ebda_seg,(uint16_t)&EbdaData->bdisk.hdcount, read_byte(ebda_seg, (uint16_t)&EbdaData->bdisk.hdcount) + 1);
539 }
540
541 // everything is ok, so from now on, the emulation is active
542 if (cdemu->media != 0)
543 cdemu->active = 0x01;
544
545 // return the boot drive + no error
546 return (cdemu->emulated_drive*0x100)+0;
547}
548
549// ---------------------------------------------------------------------------
550// End of El-Torito boot functions
551// ---------------------------------------------------------------------------
552
553// ---------------------------------------------------------------------------
554// Start of int13 when emulating a device from the cd
555// ---------------------------------------------------------------------------
556
557void BIOSCALL int13_cdemu(disk_regs_t r)
558{
559 /// @todo a macro or a function for getting the EBDA segment
560 uint16_t ebda_seg=read_word(0x0040,0x000E);
561 uint8_t device, status;
562 uint16_t vheads, vspt, vcylinders;
563 uint16_t head, sector, cylinder, nbsectors;
564 uint32_t vlba;
565 uint16_t segment, offset;
566 cdemu_t __far *cdemu;
567 bio_dsk_t __far *bios_dsk;
568 int13ext_t __far *i13x;
569 uint32_t lba;
570 uint16_t count;
571
572 cdemu = ebda_seg :> &EbdaData->cdemu;
573 bios_dsk = ebda_seg :> &EbdaData->bdisk;
574
575 BX_DEBUG_INT13_ET("%s: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", __func__, AX, BX, CX, DX, ES);
576
577 /* at this point, we are emulating a floppy/harddisk */
578
579 // Recompute the device number
580 device = cdemu->controller_index * 2;
581 device += cdemu->device_spec;
582
583 SET_DISK_RET_STATUS(0x00);
584
585 /* basic checks : emulation should be active, dl should equal the emulated drive */
586 if (!cdemu->active || (cdemu->emulated_drive != GET_DL())) {
587 BX_INFO("%s: function %02x, emulation not active for DL= %02x\n", __func__, GET_AH(), GET_DL());
588 goto int13_fail;
589 }
590
591 switch (GET_AH()) {
592
593 case 0x00: /* disk controller reset */
594 if (pktacc[bios_dsk->devices[device].type])
595 {
596 status = softrst[bios_dsk->devices[device].type](device);
597 }
598 goto int13_success;
599 break;
600 // all those functions return SUCCESS
601 case 0x09: /* initialize drive parameters */
602 case 0x0c: /* seek to specified cylinder */
603 case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
604 case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
605 case 0x11: /* recalibrate */
606 case 0x14: /* controller internal diagnostic */
607 case 0x16: /* detect disk change */
608 goto int13_success;
609 break;
610
611 // all those functions return disk write-protected
612 case 0x03: /* write disk sectors */
613 case 0x05: /* format disk track */
614 SET_AH(0x03);
615 goto int13_fail_noah;
616 break;
617
618 case 0x01: /* read disk status */
619 status=read_byte(0x0040, 0x0074);
620 SET_AH(status);
621 SET_DISK_RET_STATUS(0);
622
623 /* set CF if error status read */
624 if (status)
625 goto int13_fail_nostatus;
626 else
627 goto int13_success_noah;
628 break;
629
630 case 0x02: // read disk sectors
631 case 0x04: // verify disk sectors
632 vspt = cdemu->vdevice.spt;
633 vcylinders = cdemu->vdevice.cylinders;
634 vheads = cdemu->vdevice.heads;
635
636 sector = GET_CL() & 0x003f;
637 cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
638 head = GET_DH();
639 nbsectors = GET_AL();
640 segment = ES;
641 offset = BX;
642
643 BX_DEBUG_INT13_ET("%s: read to %04x:%04x @ VCHS %u/%u/%u (%u sectors)\n", __func__,
644 ES, BX, cylinder, head, sector, nbsectors);
645
646 // no sector to read ?
647 if(nbsectors==0)
648 goto int13_success;
649
650 // sanity checks sco openserver needs this!
651 if ((sector > vspt)
652 || (cylinder >= vcylinders)
653 || (head >= vheads)) {
654 goto int13_fail;
655 }
656
657 // After validating the input, verify does nothing
658 if (GET_AH() == 0x04)
659 goto int13_success;
660
661 segment = ES+(BX / 16);
662 offset = BX % 16;
663
664 // calculate the virtual lba inside the image
665 vlba=((((uint32_t)cylinder*(uint32_t)vheads)+(uint32_t)head)*(uint32_t)vspt)+((uint32_t)(sector-1));
666
667 // In advance so we don't lose the count
668 SET_AL(nbsectors);
669
670 status = cdemu_read(device, vlba, nbsectors, MK_FP(segment,offset));
671 if (status != 0) {
672 BX_INFO("%s: function %02x, error %02x !\n", __func__, GET_AH(), status);
673 SET_AH(0x02);
674 SET_AL(0);
675 goto int13_fail_noah;
676 }
677
678 goto int13_success;
679 break;
680
681 case 0x08: /* read disk drive parameters */
682 vspt = cdemu->vdevice.spt;
683 vcylinders = cdemu->vdevice.cylinders - 1;
684 vheads = cdemu->vdevice.heads - 1;
685
686 SET_AL( 0x00 );
687 SET_BL( 0x00 );
688 SET_CH( vcylinders & 0xff );
689 SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
690 SET_DH( vheads );
691 SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
692 // FIXME ElTorito Harddisk. should send the HD count
693
694 switch (cdemu->media) {
695 case 0x01: SET_BL( 0x02 ); break; /* 1.2 MB */
696 case 0x02: SET_BL( 0x04 ); break; /* 1.44 MB */
697 case 0x03: SET_BL( 0x05 ); break; /* 2.88 MB */
698 }
699
700 /* Only set the DPT pointer for emulated floppies. */
701 if (cdemu->media < 4) {
702 DI = (uint16_t)&diskette_param_table; /// @todo should this depend on emulated medium?
703 ES = 0xF000; /// @todo how to make this relocatable?
704 }
705 goto int13_success;
706 break;
707
708 case 0x15: /* read disk drive size */
709 // FIXME ElTorito Harddisk. What geometry to send ?
710 SET_AH(0x03);
711 goto int13_success_noah;
712 break;
713
714 case 0x41: // IBM/MS installation check
715 BX = 0xaa55; // install check
716 SET_AH(0x30); // EDD 2.1
717 CX = 0x0007; // ext disk access, removable and edd
718 goto int13_success_noah;
719 break;
720
721 case 0x42: // IBM/MS extended read
722 case 0x44: // IBM/MS verify sectors
723 case 0x47: // IBM/MS extended seek
724
725 /* Load the I13X struct pointer. */
726 i13x = MK_FP(DS, SI);
727
728 count = i13x->count;
729 segment = i13x->segment;
730 offset = i13x->offset;
731
732 // Can't use 64 bits lba
733 lba = i13x->lba2;
734 if (lba != 0L) {
735 BX_PANIC("%s: function %02x. Can't use 64bits lba\n", __func__, GET_AH());
736 goto int13_fail;
737 }
738
739 // Get 32 bits lba
740 lba = i13x->lba1;
741
742 // If verify or seek
743 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
744 goto int13_success;
745
746 BX_DEBUG_INT13_ET("%s: read %u sectors @ LBA %lu to %04X:%04X\n",
747 __func__, count, lba, segment, offset);
748
749 status = cdemu_read(device, lba, count, MK_FP(segment,offset));
750 count = (uint16_t)(bios_dsk->drqp.trsfbytes >> 9);
751 i13x->count = count;
752
753 if (status != 0) {
754 BX_INFO("%s: function %02x, status %02x !\n", __func__, GET_AH(), status);
755 SET_AH(0x0c);
756 goto int13_fail_noah;
757 }
758
759 goto int13_success;
760 break;
761
762 case 0x48: // IBM/MS get drive parameters
763 if (edd_fill_dpt(DS :> (dpt_t *)SI, bios_dsk, device))
764 goto int13_fail;
765 else
766 goto int13_success;
767 break;
768
769 // all those functions return unimplemented
770 case 0x0a: /* read disk sectors with ECC */
771 case 0x0b: /* write disk sectors with ECC */
772 case 0x18: /* set media type for format */
773 case 0x43: // IBM/MS extended write
774 case 0x45: // IBM/MS lock/unlock drive
775 case 0x46: // IBM/MS eject media
776 case 0x49: // IBM/MS extended media change
777 case 0x4e: // ? - set hardware configuration
778 case 0x50: // ? - send packet command
779 default:
780 BX_INFO("%s: function AH=%02x unsupported, returns fail\n", __func__, GET_AH());
781 goto int13_fail;
782 break;
783 }
784
785int13_fail:
786 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
787int13_fail_noah:
788 SET_DISK_RET_STATUS(GET_AH());
789int13_fail_nostatus:
790 SET_CF(); // error occurred
791 return;
792
793int13_success:
794 SET_AH(0x00); // no error
795int13_success_noah:
796 SET_DISK_RET_STATUS(0x00);
797 CLEAR_CF(); // no error
798 return;
799}
800
801// ---------------------------------------------------------------------------
802// Start of int13 for cdrom
803// ---------------------------------------------------------------------------
804
805void BIOSCALL int13_cdrom(disk_regs_t r)
806{
807 uint16_t ebda_seg = read_word(0x0040,0x000E);
808 uint8_t device, status, locks;
809 uint32_t lba;
810 uint16_t count, segment, offset;
811 bio_dsk_t __far *bios_dsk;
812 int13ext_t __far *i13x;
813
814 bios_dsk = ebda_seg :> &EbdaData->bdisk;
815
816 BX_DEBUG_INT13_CD("%s: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", __func__, AX, BX, CX, DX, ES);
817
818 SET_DISK_RET_STATUS(0x00);
819
820 /* basic check : device should be 0xE0+ */
821 if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0 + BX_MAX_STORAGE_DEVICES) ) {
822 BX_DEBUG("%s: function %02x, ELDL out of range %02x\n", __func__, GET_AH(), GET_ELDL());
823 goto int13_fail;
824 }
825
826 // Get the ata channel
827 device = bios_dsk->cdidmap[GET_ELDL()-0xE0];
828
829 /* basic check : device has to be valid */
830 if (device >= BX_MAX_STORAGE_DEVICES) {
831 BX_DEBUG("%s: function %02x, unmapped device for ELDL=%02x\n", __func__, GET_AH(), GET_ELDL());
832 goto int13_fail;
833 }
834
835 switch (GET_AH()) {
836
837 // all those functions return SUCCESS
838 case 0x00: /* disk controller reset */
839 case 0x09: /* initialize drive parameters */
840 case 0x0c: /* seek to specified cylinder */
841 case 0x0d: /* alternate disk reset */
842 case 0x10: /* check drive ready */
843 case 0x11: /* recalibrate */
844 case 0x14: /* controller internal diagnostic */
845 case 0x16: /* detect disk change */
846 goto int13_success;
847 break;
848
849 // all those functions return disk write-protected
850 case 0x03: /* write disk sectors */
851 case 0x05: /* format disk track */
852 case 0x43: // IBM/MS extended write
853 SET_AH(0x03);
854 goto int13_fail_noah;
855 break;
856
857 case 0x01: /* read disk status */
858 status = read_byte(0x0040, 0x0074);
859 SET_AH(status);
860 SET_DISK_RET_STATUS(0);
861
862 /* set CF if error status read */
863 if (status)
864 goto int13_fail_nostatus;
865 else
866 goto int13_success_noah;
867 break;
868
869 case 0x15: /* read disk drive size */
870 SET_AH(0x02);
871 goto int13_fail_noah;
872 break;
873
874 case 0x41: // IBM/MS installation check
875 BX = 0xaa55; // install check
876 SET_AH(0x30); // EDD 2.1
877 CX = 0x0007; // ext disk access, removable and edd
878 goto int13_success_noah;
879 break;
880
881 case 0x42: // IBM/MS extended read
882 case 0x44: // IBM/MS verify sectors
883 case 0x47: // IBM/MS extended seek
884
885 /* Load the I13X struct pointer. */
886 i13x = MK_FP(DS, SI);
887
888 count = i13x->count;
889 segment = i13x->segment;
890 offset = i13x->offset;
891
892 // Can't use 64 bits lba
893 lba = i13x->lba2;
894 if (lba != 0L) {
895 BX_PANIC("%s: function %02x. Can't use 64bits lba\n", __func__, GET_AH());
896 goto int13_fail;
897 }
898
899 // Get 32 bits lba
900 lba = i13x->lba1;
901
902 // If verify or seek
903 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
904 goto int13_success;
905
906 BX_DEBUG_INT13_CD("%s: read %u sectors @ LBA %lu to %04X:%04X\n",
907 __func__, count, lba, segment, offset);
908
909 status = cdrom_read(device, lba, count, MK_FP(segment,offset));
910 count = (uint16_t)(bios_dsk->drqp.trsfbytes >> 11);
911 i13x->count = count;
912
913 if (status != 0) {
914 BX_INFO("%s: function %02x, status %02x !\n", __func__, GET_AH(), status);
915 SET_AH(0x0c);
916 goto int13_fail_noah;
917 }
918
919 goto int13_success;
920 break;
921
922 case 0x45: // IBM/MS lock/unlock drive
923 if (GET_AL() > 2)
924 goto int13_fail;
925
926 locks = bios_dsk->devices[device].lock;
927
928 switch (GET_AL()) {
929 case 0 : // lock
930 if (locks == 0xff) {
931 SET_AH(0xb4);
932 SET_AL(1);
933 goto int13_fail_noah;
934 }
935 bios_dsk->devices[device].lock = ++locks;
936 SET_AL(1);
937 break;
938 case 1 : // unlock
939 if (locks == 0x00) {
940 SET_AH(0xb0);
941 SET_AL(0);
942 goto int13_fail_noah;
943 }
944 bios_dsk->devices[device].lock = --locks;
945 SET_AL(locks==0?0:1);
946 break;
947 case 2 : // status
948 SET_AL(locks==0?0:1);
949 break;
950 }
951 goto int13_success;
952 break;
953
954 case 0x46: // IBM/MS eject media
955 locks = bios_dsk->devices[device].lock;
956
957 if (locks != 0) {
958 SET_AH(0xb1); // media locked
959 goto int13_fail_noah;
960 }
961 // FIXME should handle 0x31 no media in device
962 // FIXME should handle 0xb5 valid request failed
963
964#if 0 /// @todo implement!
965 // Call removable media eject
966 ASM_START
967 push bp
968 mov bp, sp
969
970 mov ah, #0x52
971 int #0x15
972 mov _int13_cdrom.status + 2[bp], ah
973 jnc int13_cdrom_rme_end
974 mov _int13_cdrom.status, #1
975int13_cdrom_rme_end:
976 pop bp
977 ASM_END
978#endif
979
980 if (status != 0) {
981 SET_AH(0xb1); // media locked
982 goto int13_fail_noah;
983 }
984
985 goto int13_success;
986 break;
987
988 case 0x48: // IBM/MS get drive parameters
989 if (edd_fill_dpt(DS :> (dpt_t *)SI, bios_dsk, device))
990 goto int13_fail;
991 else
992 goto int13_success;
993 break;
994
995 case 0x49: // IBM/MS extended media change
996 // always send changed ??
997 SET_AH(06);
998 goto int13_fail_nostatus;
999 break;
1000
1001 case 0x4e: // // IBM/MS set hardware configuration
1002 // DMA, prefetch, PIO maximum not supported
1003 switch (GET_AL()) {
1004 case 0x01:
1005 case 0x03:
1006 case 0x04:
1007 case 0x06:
1008 goto int13_success;
1009 break;
1010 default :
1011 goto int13_fail;
1012 }
1013 break;
1014
1015 // all those functions return unimplemented
1016 case 0x02: /* read sectors */
1017 case 0x04: /* verify sectors */
1018 case 0x08: /* read disk drive parameters */
1019 case 0x0a: /* read disk sectors with ECC */
1020 case 0x0b: /* write disk sectors with ECC */
1021 case 0x18: /* set media type for format */
1022 case 0x50: // ? - send packet command
1023 default:
1024 BX_INFO("%s: unsupported AH=%02x\n", __func__, GET_AH());
1025 goto int13_fail;
1026 break;
1027 }
1028
1029int13_fail:
1030 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
1031int13_fail_noah:
1032 SET_DISK_RET_STATUS(GET_AH());
1033int13_fail_nostatus:
1034 SET_CF(); // error occurred
1035 return;
1036
1037int13_success:
1038 SET_AH(0x00); // no error
1039int13_success_noah:
1040 SET_DISK_RET_STATUS(0x00);
1041 CLEAR_CF(); // no error
1042 return;
1043}
1044
1045// ---------------------------------------------------------------------------
1046// End of int13 for cdrom
1047// ---------------------------------------------------------------------------
1048
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use