Annotation of qemu/roms/seabios/src/cdrom.c, revision 1.1

1.1     ! root        1: // Support for booting from cdroms (the "El Torito" spec).
        !             2: //
        !             3: // Copyright (C) 2008,2009  Kevin O'Connor <[email protected]>
        !             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: 
        !            14: 
        !            15: /****************************************************************
        !            16:  * CD emulation
        !            17:  ****************************************************************/
        !            18: 
        !            19: static int
        !            20: cdemu_read(struct disk_op_s *op)
        !            21: {
        !            22:     u16 ebda_seg = get_ebda_seg();
        !            23:     struct drive_s *drive_g = GET_EBDA2(ebda_seg, cdemu.emulated_drive);
        !            24:     struct disk_op_s dop;
        !            25:     dop.drive_g = drive_g;
        !            26:     dop.command = op->command;
        !            27:     dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + op->lba / 4;
        !            28: 
        !            29:     int count = op->count;
        !            30:     op->count = 0;
        !            31:     u8 *cdbuf_far = (void*)offsetof(struct extended_bios_data_area_s, cdemu_buf);
        !            32: 
        !            33:     if (op->lba & 3) {
        !            34:         // Partial read of first block.
        !            35:         dop.count = 1;
        !            36:         dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
        !            37:         int ret = process_op(&dop);
        !            38:         if (ret)
        !            39:             return ret;
        !            40:         u8 thiscount = 4 - (op->lba & 3);
        !            41:         if (thiscount > count)
        !            42:             thiscount = count;
        !            43:         count -= thiscount;
        !            44:         memcpy_far(FLATPTR_TO_SEG(op->buf_fl)
        !            45:                    , (void*)FLATPTR_TO_OFFSET(op->buf_fl)
        !            46:                    , ebda_seg, cdbuf_far + (op->lba & 3) * 512
        !            47:                    , thiscount * 512);
        !            48:         op->buf_fl += thiscount * 512;
        !            49:         op->count += thiscount;
        !            50:         dop.lba++;
        !            51:     }
        !            52: 
        !            53:     if (count > 3) {
        !            54:         // Read n number of regular blocks.
        !            55:         dop.count = count / 4;
        !            56:         dop.buf_fl = op->buf_fl;
        !            57:         int ret = process_op(&dop);
        !            58:         op->count += dop.count * 4;
        !            59:         if (ret)
        !            60:             return ret;
        !            61:         u8 thiscount = count & ~3;
        !            62:         count &= 3;
        !            63:         op->buf_fl += thiscount * 512;
        !            64:         dop.lba += thiscount / 4;
        !            65:     }
        !            66: 
        !            67:     if (count) {
        !            68:         // Partial read on last block.
        !            69:         dop.count = 1;
        !            70:         dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
        !            71:         int ret = process_op(&dop);
        !            72:         if (ret)
        !            73:             return ret;
        !            74:         u8 thiscount = count;
        !            75:         memcpy_far(FLATPTR_TO_SEG(op->buf_fl)
        !            76:                    , (void*)FLATPTR_TO_OFFSET(op->buf_fl)
        !            77:                    , ebda_seg, cdbuf_far, thiscount * 512);
        !            78:         op->count += thiscount;
        !            79:     }
        !            80: 
        !            81:     return DISK_RET_SUCCESS;
        !            82: }
        !            83: 
        !            84: int
        !            85: process_cdemu_op(struct disk_op_s *op)
        !            86: {
        !            87:     if (!CONFIG_CDROM_EMU)
        !            88:         return 0;
        !            89: 
        !            90:     switch (op->command) {
        !            91:     case CMD_READ:
        !            92:         return cdemu_read(op);
        !            93:     case CMD_WRITE:
        !            94:     case CMD_FORMAT:
        !            95:         return DISK_RET_EWRITEPROTECT;
        !            96:     case CMD_VERIFY:
        !            97:     case CMD_RESET:
        !            98:     case CMD_SEEK:
        !            99:     case CMD_ISREADY:
        !           100:         return DISK_RET_SUCCESS;
        !           101:     default:
        !           102:         op->count = 0;
        !           103:         return DISK_RET_EPARAM;
        !           104:     }
        !           105: }
        !           106: 
        !           107: struct drive_s *cdemu_drive VAR16VISIBLE;
        !           108: 
        !           109: void
        !           110: cdemu_setup()
        !           111: {
        !           112:     if (!CONFIG_CDROM_EMU)
        !           113:         return;
        !           114: 
        !           115:     struct drive_s *drive_g = allocDrive();
        !           116:     if (!drive_g) {
        !           117:         cdemu_drive = NULL;
        !           118:         return;
        !           119:     }
        !           120:     cdemu_drive = ADJUST_GLOBAL_PTR(drive_g);
        !           121:     drive_g->type = DTYPE_CDEMU;
        !           122:     drive_g->blksize = DISK_SECTOR_SIZE;
        !           123:     drive_g->sectors = (u64)-1;
        !           124: }
        !           125: 
        !           126: struct eltorito_s {
        !           127:     u8 size;
        !           128:     u8 media;
        !           129:     u8 emulated_drive;
        !           130:     u8 controller_index;
        !           131:     u32 ilba;
        !           132:     u16 device_spec;
        !           133:     u16 buffer_segment;
        !           134:     u16 load_segment;
        !           135:     u16 sector_count;
        !           136:     u8 cylinders;
        !           137:     u8 sectors;
        !           138:     u8 heads;
        !           139: };
        !           140: 
        !           141: #define SET_INT13ET(regs,var,val)                                      \
        !           142:     SET_FARVAR((regs)->ds, ((struct eltorito_s*)((regs)->si+0))->var, (val))
        !           143: 
        !           144: // ElTorito - Terminate disk emu
        !           145: void
        !           146: cdemu_134b(struct bregs *regs)
        !           147: {
        !           148:     // FIXME ElTorito Hardcoded
        !           149:     u16 ebda_seg = get_ebda_seg();
        !           150:     SET_INT13ET(regs, size, 0x13);
        !           151:     SET_INT13ET(regs, media, GET_EBDA2(ebda_seg, cdemu.media));
        !           152:     SET_INT13ET(regs, emulated_drive
        !           153:                 , GET_EBDA2(ebda_seg, cdemu.emulated_extdrive));
        !           154:     struct drive_s *drive_g = GET_EBDA2(ebda_seg, cdemu.emulated_drive);
        !           155:     u8 cntl_id = GET_GLOBAL(drive_g->cntl_id);
        !           156:     SET_INT13ET(regs, controller_index, cntl_id / 2);
        !           157:     SET_INT13ET(regs, device_spec, cntl_id % 2);
        !           158:     SET_INT13ET(regs, ilba, GET_EBDA2(ebda_seg, cdemu.ilba));
        !           159:     SET_INT13ET(regs, buffer_segment, GET_EBDA2(ebda_seg, cdemu.buffer_segment));
        !           160:     SET_INT13ET(regs, load_segment, GET_EBDA2(ebda_seg, cdemu.load_segment));
        !           161:     SET_INT13ET(regs, sector_count, GET_EBDA2(ebda_seg, cdemu.sector_count));
        !           162:     SET_INT13ET(regs, cylinders, GET_EBDA2(ebda_seg, cdemu.lchs.cylinders));
        !           163:     SET_INT13ET(regs, sectors, GET_EBDA2(ebda_seg, cdemu.lchs.spt));
        !           164:     SET_INT13ET(regs, heads, GET_EBDA2(ebda_seg, cdemu.lchs.heads));
        !           165: 
        !           166:     // If we have to terminate emulation
        !           167:     if (regs->al == 0x00) {
        !           168:         // FIXME ElTorito Various. Should be handled accordingly to spec
        !           169:         SET_EBDA2(ebda_seg, cdemu.active, 0x00); // bye bye
        !           170:     }
        !           171: 
        !           172:     disk_ret(regs, DISK_RET_SUCCESS);
        !           173: }
        !           174: 
        !           175: 
        !           176: /****************************************************************
        !           177:  * CD booting
        !           178:  ****************************************************************/
        !           179: 
        !           180: // Request SENSE
        !           181: static int
        !           182: atapi_get_sense(struct drive_s *drive_g, u8 *asc, u8 *ascq)
        !           183: {
        !           184:     u8 atacmd[12], buffer[18];
        !           185:     memset(atacmd, 0, sizeof(atacmd));
        !           186:     atacmd[0] = ATA_CMD_REQUEST_SENSE;
        !           187:     atacmd[4] = sizeof(buffer);
        !           188:     int ret = ata_cmd_packet(drive_g, atacmd, sizeof(atacmd), sizeof(buffer)
        !           189:                              , MAKE_FLATPTR(GET_SEG(SS), buffer));
        !           190:     if (ret)
        !           191:         return ret;
        !           192: 
        !           193:     *asc = buffer[12];
        !           194:     *ascq = buffer[13];
        !           195: 
        !           196:     return 0;
        !           197: }
        !           198: 
        !           199: // Request capacity
        !           200: static int
        !           201: atapi_read_capacity(struct drive_s *drive_g, u32 *blksize, u32 *sectors)
        !           202: {
        !           203:     u8 packet[12], buf[8];
        !           204:     memset(packet, 0, sizeof(packet));
        !           205:     packet[0] = 0x25; /* READ CAPACITY */
        !           206:     int ret = ata_cmd_packet(drive_g, packet, sizeof(packet), sizeof(buf)
        !           207:                              , MAKE_FLATPTR(GET_SEG(SS), buf));
        !           208:     if (ret)
        !           209:         return ret;
        !           210: 
        !           211:     *blksize = (((u32)buf[4] << 24) | ((u32)buf[5] << 16)
        !           212:                 | ((u32)buf[6] << 8) | ((u32)buf[7] << 0));
        !           213:     *sectors = (((u32)buf[0] << 24) | ((u32)buf[1] << 16)
        !           214:                 | ((u32)buf[2] << 8) | ((u32)buf[3] << 0));
        !           215: 
        !           216:     return 0;
        !           217: }
        !           218: 
        !           219: static int
        !           220: atapi_is_ready(struct drive_s *drive_g)
        !           221: {
        !           222:     dprintf(6, "atapi_is_ready (drive=%p)\n", drive_g);
        !           223: 
        !           224:     /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT is
        !           225:      * reported by the device.  If the device reports "IN PROGRESS",
        !           226:      * 30 seconds is added. */
        !           227:     u32 blksize, sectors;
        !           228:     int in_progress = 0;
        !           229:     u64 end = calc_future_tsc(5000);
        !           230:     for (;;) {
        !           231:         if (check_time(end)) {
        !           232:             dprintf(1, "read capacity failed\n");
        !           233:             return -1;
        !           234:         }
        !           235: 
        !           236:         int ret = atapi_read_capacity(drive_g, &blksize, &sectors);
        !           237:         if (!ret)
        !           238:             // Success
        !           239:             break;
        !           240: 
        !           241:         u8 asc, ascq;
        !           242:         ret = atapi_get_sense(drive_g, &asc, &ascq);
        !           243:         if (ret)
        !           244:             // Error - retry.
        !           245:             continue;
        !           246: 
        !           247:         // Sense succeeded.
        !           248:         if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
        !           249:             dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
        !           250:             return -1;
        !           251:         }
        !           252: 
        !           253:         if (asc == 0x04 && ascq == 0x01 && !in_progress) {
        !           254:             /* IN PROGRESS OF BECOMING READY */
        !           255:             printf("Waiting for device to detect medium... ");
        !           256:             /* Allow 30 seconds more */
        !           257:             end = calc_future_tsc(30000);
        !           258:             in_progress = 1;
        !           259:         }
        !           260:     }
        !           261: 
        !           262:     if (blksize != GET_GLOBAL(drive_g->blksize)) {
        !           263:         printf("Unsupported sector size %u\n", blksize);
        !           264:         return -1;
        !           265:     }
        !           266: 
        !           267:     dprintf(6, "sectors=%u\n", sectors);
        !           268:     printf("%dMB medium detected\n", sectors>>(20-11));
        !           269:     return 0;
        !           270: }
        !           271: 
        !           272: int
        !           273: cdrom_boot(int cdid)
        !           274: {
        !           275:     struct drive_s *drive_g = getDrive(EXTTYPE_CD, cdid);
        !           276:     if (!drive_g)
        !           277:         return 1;
        !           278: 
        !           279:     int ret = atapi_is_ready(drive_g);
        !           280:     if (ret)
        !           281:         dprintf(1, "atapi_is_ready returned %d\n", ret);
        !           282: 
        !           283:     // Read the Boot Record Volume Descriptor
        !           284:     u8 buffer[2048];
        !           285:     struct disk_op_s dop;
        !           286:     memset(&dop, 0, sizeof(dop));
        !           287:     dop.drive_g = drive_g;
        !           288:     dop.lba = 0x11;
        !           289:     dop.count = 1;
        !           290:     dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
        !           291:     ret = cdrom_read(&dop);
        !           292:     if (ret)
        !           293:         return 3;
        !           294: 
        !           295:     // Validity checks
        !           296:     if (buffer[0])
        !           297:         return 4;
        !           298:     if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
        !           299:         return 5;
        !           300: 
        !           301:     // ok, now we calculate the Boot catalog address
        !           302:     u32 lba = *(u32*)&buffer[0x47];
        !           303: 
        !           304:     // And we read the Boot Catalog
        !           305:     dop.lba = lba;
        !           306:     ret = cdrom_read(&dop);
        !           307:     if (ret)
        !           308:         return 7;
        !           309: 
        !           310:     // Validation entry
        !           311:     if (buffer[0x00] != 0x01)
        !           312:         return 8;   // Header
        !           313:     if (buffer[0x01] != 0x00)
        !           314:         return 9;   // Platform
        !           315:     if (buffer[0x1E] != 0x55)
        !           316:         return 10;  // key 1
        !           317:     if (buffer[0x1F] != 0xAA)
        !           318:         return 10;  // key 2
        !           319: 
        !           320:     // Initial/Default Entry
        !           321:     if (buffer[0x20] != 0x88)
        !           322:         return 11; // Bootable
        !           323: 
        !           324:     u16 ebda_seg = get_ebda_seg();
        !           325:     u8 media = buffer[0x21];
        !           326:     SET_EBDA2(ebda_seg, cdemu.media, media);
        !           327: 
        !           328:     SET_EBDA2(ebda_seg, cdemu.emulated_drive, ADJUST_GLOBAL_PTR(drive_g));
        !           329: 
        !           330:     u16 boot_segment = *(u16*)&buffer[0x22];
        !           331:     if (!boot_segment)
        !           332:         boot_segment = 0x07C0;
        !           333:     SET_EBDA2(ebda_seg, cdemu.load_segment, boot_segment);
        !           334:     SET_EBDA2(ebda_seg, cdemu.buffer_segment, 0x0000);
        !           335: 
        !           336:     u16 nbsectors = *(u16*)&buffer[0x26];
        !           337:     SET_EBDA2(ebda_seg, cdemu.sector_count, nbsectors);
        !           338: 
        !           339:     lba = *(u32*)&buffer[0x28];
        !           340:     SET_EBDA2(ebda_seg, cdemu.ilba, lba);
        !           341: 
        !           342:     // And we read the image in memory
        !           343:     dop.lba = lba;
        !           344:     dop.count = DIV_ROUND_UP(nbsectors, 4);
        !           345:     dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
        !           346:     ret = cdrom_read(&dop);
        !           347:     if (ret)
        !           348:         return 12;
        !           349: 
        !           350:     if (media == 0) {
        !           351:         // No emulation requested - return success.
        !           352:         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, EXTSTART_CD + cdid);
        !           353:         return 0;
        !           354:     }
        !           355: 
        !           356:     // Emulation of a floppy/harddisk requested
        !           357:     if (! CONFIG_CDROM_EMU || !cdemu_drive)
        !           358:         return 13;
        !           359: 
        !           360:     // Set emulated drive id and increase bios installed hardware
        !           361:     // number of devices
        !           362:     if (media < 4) {
        !           363:         // Floppy emulation
        !           364:         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x00);
        !           365:         SETBITS_BDA(equipment_list_flags, 0x41);
        !           366: 
        !           367:         switch (media) {
        !           368:         case 0x01:  // 1.2M floppy
        !           369:             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 15);
        !           370:             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
        !           371:             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
        !           372:             break;
        !           373:         case 0x02:  // 1.44M floppy
        !           374:             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 18);
        !           375:             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
        !           376:             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
        !           377:             break;
        !           378:         case 0x03:  // 2.88M floppy
        !           379:             SET_EBDA2(ebda_seg, cdemu.lchs.spt, 36);
        !           380:             SET_EBDA2(ebda_seg, cdemu.lchs.cylinders, 80);
        !           381:             SET_EBDA2(ebda_seg, cdemu.lchs.heads, 2);
        !           382:             break;
        !           383:         }
        !           384:     } else {
        !           385:         // Harddrive emulation
        !           386:         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, 0x80);
        !           387:         SET_BDA(hdcount, GET_BDA(hdcount) + 1);
        !           388: 
        !           389:         // Peak at partition table to get chs.
        !           390:         struct mbr_s *mbr = (void*)0;
        !           391:         u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl);
        !           392:         u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow);
        !           393:         u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads);
        !           394: 
        !           395:         SET_EBDA2(ebda_seg, cdemu.lchs.spt, sptcyl & 0x3f);
        !           396:         SET_EBDA2(ebda_seg, cdemu.lchs.cylinders
        !           397:                   , ((sptcyl<<2)&0x300) + cyllow + 1);
        !           398:         SET_EBDA2(ebda_seg, cdemu.lchs.heads, heads + 1);
        !           399:     }
        !           400: 
        !           401:     // everything is ok, so from now on, the emulation is active
        !           402:     SET_EBDA2(ebda_seg, cdemu.active, 0x01);
        !           403:     dprintf(6, "cdemu media=%d\n", media);
        !           404: 
        !           405:     return 0;
        !           406: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.