VirtualBox

Changeset 82164 in vbox


Ignore:
Timestamp:
Nov 25, 2019 10:32:37 AM (5 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
134974
Message:

PC/BIOS/VirtIoScsi: Current state

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/PC/BIOS/virtio.c

    r81407 r82164  
    2525#include "scsi.h"
    2626
    27 #define DEBUG_VIRTIO 1
     27//#define DEBUG_VIRTIO 1
    2828#if DEBUG_VIRTIO
    2929# define DBG_VIRTIO(...)        BX_INFO(__VA_ARGS__)
     
    278278#define VIRTIO_SCSI_Q_REQUEST           0x02
    279279
     280#define VIRTIO_SCSI_STS_RESPONSE_OK     0x00
     281
    280282/* Machinery to save/restore high bits of EAX. 32-bit port I/O needs to use
    281283 * EAX, but saving/restoring EAX around each port access would be inefficient.
     
    338340}
    339341
     342static void virtio_reg_isr_prepare(virtio_t __far *virtio, uint32_t cbAcc)
     343{
     344    virtio_reg_set_bar_offset_length(virtio,
     345                                     virtio->aBarCfgs[VIRTIO_PCI_CAP_ISR_CFG - 1].u8Bar,
     346                                     virtio->aBarCfgs[VIRTIO_PCI_CAP_ISR_CFG - 1].u32Offset,
     347                                     cbAcc);
     348}
     349
    340350static uint8_t virtio_reg_common_read_u8(virtio_t __far *virtio, uint16_t offReg)
    341351{
     
    362372}
    363373
    364 static uint32_t virtio_reg_common_read_u32(virtio_t __far *virtio, uint16_t offReg)
    365 {
    366     virtio_reg_common_access_prepare(virtio, offReg, sizeof(uint32_t));
    367     return pci_read_config_dword(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t));
    368 }
    369 
    370374static void virtio_reg_common_write_u32(virtio_t __far *virtio, uint16_t offReg, uint32_t u32Val)
    371375{
     
    374378}
    375379
    376 static uint16_t virtio_reg_dev_cfg_read_u16(virtio_t __far *virtio, uint16_t offReg)
    377 {
    378     virtio_reg_dev_access_prepare(virtio, offReg, sizeof(uint16_t));
    379     return pci_read_config_word(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t));
    380 }
    381 
    382380static uint32_t virtio_reg_dev_cfg_read_u32(virtio_t __far *virtio, uint16_t offReg)
    383381{
     
    398396}
    399397
     398static uint8_t virtio_reg_isr_read_u8(virtio_t __far *virtio)
     399{
     400    virtio_reg_isr_prepare(virtio, sizeof(uint8_t));
     401    return pci_read_config_byte(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t));
     402}
     403
    400404/**
    401405 * Allocates 1K of conventional memory.
     
    429433}
    430434
    431 int virtio_scsi_cmd_data_in(virtio_t __far *virtio, uint8_t idTgt, uint8_t __far *aCDB,
     435int virtio_scsi_cmd_data_out(virtio_t __far *virtio, uint8_t idTgt, uint8_t __far *aCDB,
    432436                            uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
    433437{
     
    451455    virtio->Queue.aDescTbl[0].idxNext       = 1;
    452456
     457    virtio->Queue.aDescTbl[1].GCPhysBufLow  = virtio_addr_to_phys(buffer);
     458    virtio->Queue.aDescTbl[1].GCPhysBufHigh = 0;
     459    virtio->Queue.aDescTbl[1].cbBuf         = length;
     460    virtio->Queue.aDescTbl[1].fFlags        = VIRTIO_Q_DESC_F_NEXT;
     461    virtio->Queue.aDescTbl[1].idxNext       = 1;
     462
     463    virtio->Queue.aDescTbl[1].GCPhysBufLow  = virtio_addr_to_phys(&virtio->ScsiReqSts);
     464    virtio->Queue.aDescTbl[1].GCPhysBufHigh = 0;
     465    virtio->Queue.aDescTbl[1].cbBuf         = sizeof(virtio->ScsiReqSts);
     466    virtio->Queue.aDescTbl[1].fFlags        = VIRTIO_Q_DESC_F_WRITE; /* End of chain. */
     467    virtio->Queue.aDescTbl[1].idxNext       = 0;
     468
     469    /* Put it into the queue. */
     470    virtio->Queue.AvailRing.au16Ring[virtio->Queue.AvailRing.idxNextFree] = 0;
     471    virtio->Queue.AvailRing.idxNextFree++;
     472    virtio->Queue.AvailRing.idxNextFree %= VIRTIO_SCSI_RING_ELEM;
     473
     474    /* Notify the device about the new command. */
     475    DBG_VIRTIO("VirtIO: Submitting new request, Queue.offNotify=0x%x\n", virtio->Queue.offNotify);
     476    virtio_reg_notify_write_u16(virtio, virtio->Queue.offNotify, idxNext);
     477
     478    /* Wait for it to complete. */
     479    while (idxUsedOld == virtio->Queue.UsedRing.idxNextUsed);
     480
     481    DBG_VIRTIO("VirtIO: Request complete u8Response=%u\n", virtio->ScsiReqSts.u8Response);
     482
     483    /* Read ISR register to de-assert the interrupt, don't need to do anything with out. */
     484    virtio_reg_isr_read_u8(virtio);
     485
     486    if (virtio->ScsiReqSts.u8Response != VIRTIO_SCSI_STS_RESPONSE_OK)
     487        return 4;
     488
     489    return 0;
     490}
     491
     492int virtio_scsi_cmd_data_in(virtio_t __far *virtio, uint8_t idTgt, uint8_t __far *aCDB,
     493                            uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
     494{
     495    uint16_t idxUsedOld = virtio->Queue.UsedRing.idxNextUsed;
     496    uint64_t idxNext = virtio->Queue.AvailRing.idxNextFree;
     497
     498    _fmemset(&virtio->ScsiReqHdr, 0, sizeof(virtio->ScsiReqHdr));
     499    _fmemset(&virtio->ScsiReqSts, 0, sizeof(virtio->ScsiReqSts));
     500
     501    virtio->ScsiReqHdr.au8Lun[0] = 0x1;
     502    virtio->ScsiReqHdr.au8Lun[1] = idTgt;
     503    virtio->ScsiReqHdr.au8Lun[2] = 0;
     504    virtio->ScsiReqHdr.au8Lun[3] = 0;
     505    _fmemcpy(&virtio->ScsiReqHdr.abCdb[0], aCDB, cbCDB);
     506
     507    /* Fill in the descriptors. */
     508    virtio->Queue.aDescTbl[0].GCPhysBufLow  = virtio_addr_to_phys(&virtio->ScsiReqHdr);
     509    virtio->Queue.aDescTbl[0].GCPhysBufHigh = 0;
     510    virtio->Queue.aDescTbl[0].cbBuf         = sizeof(virtio->ScsiReqHdr);
     511    virtio->Queue.aDescTbl[0].fFlags        = VIRTIO_Q_DESC_F_NEXT;
     512    virtio->Queue.aDescTbl[0].idxNext       = 1;
     513
    453514    /* No data out buffer, the status comes right after this in the next descriptor. */
    454515    virtio->Queue.aDescTbl[1].GCPhysBufLow  = virtio_addr_to_phys(&virtio->ScsiReqSts);
     
    476537    while (idxUsedOld == virtio->Queue.UsedRing.idxNextUsed);
    477538
    478     DBG_VIRTIO("VirtIO: Request complete\n");
     539    DBG_VIRTIO("VirtIO: Request complete u8Response=%u\n", virtio->ScsiReqSts.u8Response);
     540
     541    /* Read ISR register to de-assert the interrupt, don't need to do anything with out. */
     542    virtio_reg_isr_read_u8(virtio);
     543
     544    if (virtio->ScsiReqSts.u8Response != VIRTIO_SCSI_STS_RESPONSE_OK)
     545        return 4;
    479546
    480547    return 0;
    481548}
     549
     550/**
     551 * Read sectors from an attached VirtIO SCSI device.
     552 *
     553 * @returns status code.
     554 * @param   bios_dsk    Pointer to disk request packet (in the
     555 *                      EBDA).
     556 */
     557int virtio_scsi_read_sectors(bio_dsk_t __far *bios_dsk)
     558{
     559    uint8_t             rc;
     560    cdb_rw16            cdb;
     561    uint32_t            count;
     562    uint8_t             target_id;
     563    uint8_t             device_id;
     564
     565    device_id = VBOX_GET_SCSI_DEVICE(bios_dsk->drqp.dev_id);
     566    if (device_id > BX_MAX_SCSI_DEVICES)
     567        BX_PANIC("%s: device_id out of range %d\n", __func__, device_id);
     568
     569    count    = bios_dsk->drqp.nsect;
     570
     571    high_bits_save(bios_dsk->virtio_seg :> 0);
     572    /* Prepare a CDB. */
     573    cdb.command = SCSI_READ_16;
     574    cdb.lba     = swap_64(bios_dsk->drqp.lba);
     575    cdb.pad1    = 0;
     576    cdb.nsect32 = swap_32(count);
     577    cdb.pad2    = 0;
     578
     579
     580    target_id = bios_dsk->scsidev[device_id].target_id;
     581
     582    DBG_VIRTIO("%s: reading %u sectors, device %d, target %d\n", __func__,
     583             count, device_id, bios_dsk->scsidev[device_id].target_id);
     584
     585    rc = virtio_scsi_cmd_data_in(bios_dsk->virtio_seg :> 0, target_id, (void __far *)&cdb, 16,
     586                                 bios_dsk->drqp.buffer, (count * 512L));
     587
     588    if (!rc)
     589    {
     590        bios_dsk->drqp.trsfsectors = count;
     591        bios_dsk->drqp.trsfbytes   = count * 512L;
     592    }
     593    DBG_VIRTIO("%s: transferred %u sectors\n", __func__, bios_dsk->drqp.nsect);
     594    high_bits_restore(bios_dsk->virtio_seg :> 0);
     595
     596    return rc;
     597}
     598
     599/**
     600 * Write sectors to an attached VirtIO SCSI device.
     601 *
     602 * @returns status code.
     603 * @param   bios_dsk    Pointer to disk request packet (in the
     604 *                      EBDA).
     605 */
     606int virtio_scsi_write_sectors(bio_dsk_t __far *bios_dsk)
     607{
     608    uint8_t             rc;
     609    cdb_rw16            cdb;
     610    uint32_t            count;
     611    uint8_t             target_id;
     612    uint8_t             device_id;
     613
     614    device_id = VBOX_GET_SCSI_DEVICE(bios_dsk->drqp.dev_id);
     615    if (device_id > BX_MAX_SCSI_DEVICES)
     616        BX_PANIC("%s: device_id out of range %d\n", __func__, device_id);
     617
     618    count    = bios_dsk->drqp.nsect;
     619
     620    high_bits_save(bios_dsk->virtio_seg :> 0);
     621
     622    /* Prepare a CDB. */
     623    cdb.command = SCSI_WRITE_16;
     624    cdb.lba     = swap_64(bios_dsk->drqp.lba);
     625    cdb.pad1    = 0;
     626    cdb.nsect32 = swap_32(count);
     627    cdb.pad2    = 0;
     628
     629    target_id = bios_dsk->scsidev[device_id].target_id;
     630
     631    DBG_VIRTIO("%s: writing %u sectors, device %d, target %d\n", __func__,
     632             count, device_id, bios_dsk->scsidev[device_id].target_id);
     633
     634    rc = virtio_scsi_cmd_data_out(bios_dsk->virtio_seg :> 0, target_id, (void __far *)&cdb, 16,
     635                                  bios_dsk->drqp.buffer, (count * 512L));
     636
     637    if (!rc)
     638    {
     639        bios_dsk->drqp.trsfsectors = count;
     640        bios_dsk->drqp.trsfbytes   = (count * 512L);
     641    }
     642    DBG_VIRTIO("%s: transferred %u sectors\n", __func__, bios_dsk->drqp.nsect);
     643    high_bits_restore(bios_dsk->virtio_seg :> 0);
     644
     645    return rc;
     646}
     647
     648#if 0
     649#define ATA_DATA_OUT     0x02
     650
     651/**
     652 * Perform a "packet style" read with supplied CDB.
     653 *
     654 * @returns status code.
     655 * @param   device_id   ID of the device to access.
     656 * @param   cmdlen      Length of the CDB.
     657 * @param   cmdbuf      The CDB buffer.
     658 * @param   before      How much to skip before reading into the provided data buffer.
     659 * @param   length      How much to transfer.
     660 * @param   inout       Read/Write direction indicator.
     661 * @param   buffer      Data buffer to store the data from the device in.
     662 */
     663uint16_t scsi_cmd_packet(uint16_t device_id, uint8_t cmdlen, char __far *cmdbuf,
     664                         uint16_t before, uint32_t length, uint8_t inout, char __far *buffer)
     665{
     666    bio_dsk_t __far *bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk;
     667    uint32_t        read_len;
     668    uint8_t         status, sizes;
     669    uint16_t        i;
     670    uint16_t        io_base;
     671    uint8_t         target_id;
     672
     673    /* Data out is currently not supported. */
     674    if (inout == ATA_DATA_OUT) {
     675        BX_INFO("%s: DATA_OUT not supported yet\n", __func__);
     676        return 1;
     677    }
     678
     679    /* Convert to SCSI specific device number. */
     680    device_id = VBOX_GET_SCSI_DEVICE(device_id);
     681
     682    DBG_VIRTIO("%s: reading %lu bytes, skip %u/%u, device %d, target %d\n", __func__,
     683               length, bios_dsk->drqp.skip_b, bios_dsk->drqp.skip_a,
     684               device_id, bios_dsk->scsidev[device_id].target_id);
     685    DBG_VIRTIO("%s: reading %u %u-byte sectors\n", __func__,
     686               bios_dsk->drqp.nsect, bios_dsk->drqp.sect_sz);
     687
     688    cmdlen -= 2; /* ATAPI uses 12-byte command packets for a READ 10. */
     689
     690    io_base   = bios_dsk->scsidev[device_id].io_base;
     691    target_id = bios_dsk->scsidev[device_id].target_id;
     692
     693    /* Wait until the adapter is ready. */
     694    do
     695        status = inb(io_base + VBSCSI_REGISTER_STATUS);
     696    while (status & VBSCSI_BUSY);
     697
     698    /* On the SCSI level, we have to transfer whole sectors. */
     699    /* NB: With proper residual length support, this should not be necessary; we should
     700     * be able to avoid transferring the 'after' part of the sector.
     701     */
     702    read_len = length + before + bios_dsk->drqp.skip_a;
     703
     704    sizes = (((read_len) >> 12) & 0xF0) | cmdlen;
     705    outb(io_base + VBSCSI_REGISTER_COMMAND, target_id);                 /* Write the target ID. */
     706    outb(io_base + VBSCSI_REGISTER_COMMAND, SCSI_TXDIR_FROM_DEVICE);    /* Write the transfer direction. */
     707    outb(io_base + VBSCSI_REGISTER_COMMAND, sizes);                     /* Write the CDB size. */
     708    outb(io_base + VBSCSI_REGISTER_COMMAND, read_len);                  /* Write the buffer size. */
     709    outb(io_base + VBSCSI_REGISTER_COMMAND, (read_len) >> 8);
     710    for (i = 0; i < cmdlen; i++)                                        /* Write the CDB. */
     711        outb(io_base + VBSCSI_REGISTER_COMMAND, cmdbuf[i]);
     712
     713    /* Now wait for the command to complete. */
     714    do
     715        status = inb(io_base + VBSCSI_REGISTER_STATUS);
     716    while (status & VBSCSI_BUSY);
     717
     718    /* If any error occurred, inform the caller and don't bother reading the data. */
     719    if (status & VBSCSI_ERROR) {
     720        outb(io_base + VBSCSI_REGISTER_RESET, 0);
     721
     722        status = inb(io_base + VBSCSI_REGISTER_DEVSTAT);
     723        DBG_SCSI("%s: read failed, device status %02X\n", __func__, status);
     724        return 3;
     725    }
     726
     727    /* Transfer the data read from the device. */
     728
     729    if (before)     /* If necessary, throw away data which needs to be skipped. */
     730        insb_discard(before, io_base + VBSCSI_REGISTER_DATA_IN);
     731
     732    bios_dsk->drqp.trsfbytes = length;
     733
     734    /* The requested length may be exactly 64K or more, which needs
     735     * a bit of care when we're using 16-bit 'rep ins'.
     736     */
     737    while (length > 32768) {
     738        DBG_SCSI("%s: reading 32K to %X:%X\n", __func__, FP_SEG(buffer), FP_OFF(buffer));
     739        rep_insb(buffer, 32768, io_base + VBSCSI_REGISTER_DATA_IN);
     740        length -= 32768;
     741        buffer = (FP_SEG(buffer) + (32768 >> 4)) :> FP_OFF(buffer);
     742    }
     743
     744    DBG_SCSI("%s: reading %ld bytes to %X:%X\n", __func__, length, FP_SEG(buffer), FP_OFF(buffer));
     745    rep_insb(buffer, length, io_base + VBSCSI_REGISTER_DATA_IN);
     746
     747    if (bios_dsk->drqp.skip_a)  /* If necessary, throw away more data. */
     748        insb_discard(bios_dsk->drqp.skip_a, io_base + VBSCSI_REGISTER_DATA_IN);
     749
     750    return 0;
     751}
     752#endif
    482753
    483754static int virtio_scsi_detect_devices(virtio_t __far *virtio)
     
    608879                hd_index = devcount_scsi + BX_MAX_ATA_DEVICES;
    609880
    610                 //bios_dsk->scsidev[devcount_scsi].io_base   = io_base;
    611881                bios_dsk->scsidev[devcount_scsi].target_id = i;
    612                 bios_dsk->devices[hd_index].type        = DSK_TYPE_SCSI;
     882                bios_dsk->devices[hd_index].type        = DSK_TYPE_VIRTIO_SCSI;
    613883                bios_dsk->devices[hd_index].device      = DSK_DEVICE_HD;
    614884                bios_dsk->devices[hd_index].removable   = 0;
     
    655925            }
    656926        }
     927#if 0
    657928        else if (   ((buffer[0] & 0xe0) == 0)
    658929                 && ((buffer[0] & 0x1f) == 0x05))
     
    668939            removable = buffer[1] & 0x80 ? 1 : 0;
    669940
    670             //bios_dsk->scsidev[devcount_scsi].io_base   = io_base;
    671941            bios_dsk->scsidev[devcount_scsi].target_id = i;
    672             bios_dsk->devices[hd_index].type        = DSK_TYPE_SCSI;
     942            bios_dsk->devices[hd_index].type        = DSK_TYPE_VIRTIO_SCSI;
    673943            bios_dsk->devices[hd_index].device      = DSK_DEVICE_CDROM;
    674944            bios_dsk->devices[hd_index].removable   = removable;
     
    684954            devcount_scsi++;
    685955        }
     956#endif
    686957        else
    687958            DBG_VIRTIO("%s: No supported device detected at %d\n", __func__, i);
     
    689960        bios_dsk->scsi_devcount = devcount_scsi;
    690961    }
     962
     963    return 0;
    691964}
    692965
     
    699972    uint16_t            ebda_seg;
    700973    uint16_t            virtio_seg;
    701     uint32_t            fFeatures;
    702974    uint8_t             u8DevStat;
    703975    bio_dsk_t __far     *bios_dsk;
     
    718990
    719991    bios_dsk->virtio_seg    = virtio_seg;
    720     bios_dsk->virtio_devcnt = 0;
    721992
    722993    virtio = virtio_seg :> 0;
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette