名字不能取太长 2023-06-01 01:30 采纳率: 75.6%
浏览 87
已结题

我编写的Virtio驱动在初始化存在问题

以下代码是我编写的virtio驱动的一部分,用来初始化virtio mmio blk device,由于我的操作系统运行在QEMU上,因此我的驱动是针对于legacy device的,在编写时参考了vitio规范v1.2和xv6,但是在调试时发现我无法将队列数量QUEUE_SIZE写入QueueNUM中,也不能将PAGE_SIZE写入到QueueAlian中,依照规范我并没有成功定位到问题并且已经多次询问过ChatGPT和bingAI等ai,都没能成功找到问题,想请问我的问题出在哪里?(QEMU版本7.0.0),另外请问我的代码是否还有其他问题?

代码:

static int virtio_blk_init_legacy(virtio_regs_legacy *regs, uint32_t intid)
{

    /*4.Read device feature bits, and write the subset of feature bits understood by the OS and driver to the
    device. During this step the driver MAY read (but MUST NOT write) the device-specific configuration
    fields to check that it can support the device before accepting it.*/
    uint32_t features = readword(&regs->GuestFeatures);
    features &= ~(1 << VIRTIO_BLK_F_RO);
    features &= ~(1 << VIRTIO_BLK_F_SCSI);
    features &= ~(1 << VIRTIO_BLK_F_CONFIG_WCE);
    features &= ~(1 << VIRTIO_BLK_F_MQ);
    features &= ~(1 << VIRTIO_F_ANY_LAYOUT);
    features &= ~(1 << VIRTIO_RING_F_EVENT_IDX);
    features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
    writeword(features, &regs->GuestFeatures);
    refresh_cache();
    
    writeword(readword(&regs->Status)|VIRTIO_STATUS_FEATURES_OK , &regs->Status);
    refresh_cache();
    /*
    7. Perform device-specific setup, including discovery of virtqueues for the device, optional per-bus setup,
    reading and possibly writing the device’s virtio configuration space, and population of virtqueues.
    */

    //configure the queue:
    //a. Select the queue writing its index (first queue is 0) to QueueSel(init queue)
    writeword(0, &regs->QueueSel);
    refresh_cache();
    //b. Check if the queue is not already in use: read QueuePFN, expecting a returned value of zero (0x0).
    if(readword(&regs->QueuePFN))
    {
        printk("the queue is already in use\n");
    }
    refresh_cache();
    /*
    c. Read maximum queue size (number of elements) from QueueNumMax. If the returned value is zero
    (0x0) the queue is not available.
    */
    uint32_t size = readword(&regs->QueueNumMax);
    refresh_cache();
    if(size == 0)
    {
        printk("device queue not available!\n");
        return -1;
    }
    else if (size < QUEUE_SIZE)
    {
        printk("device queue too short!\n");
        return -1;
    }

    /*if((readword(&regs->HostFeatures) & VIRTIO_F_VERSION_1) == 0)
    {
        printk("Device does not support virtio 1.0 %x\n", regs->HostFeatures);
        return -1;
    }*/
    /*
    d. Allocate and zero the queue pages in contiguous virtual memory,
    aligning the Used Ring to an optimal boundary (usually page size). 
    The driver should choose a queue size smaller than or equal to
    */
    g_vring.avail = alloc_pgtable();
    g_vring.desc = alloc_pgtable();
    g_vring.used = PAGE_ALIGN_UP(alloc_pgtable());
    if(g_vring.avail == 1 || g_vring.desc == 1 || g_vring.used == 1)
    {
        printk("not enough for g_vring!\n");
        return -1;
    }
    g_vring.num = QUEUE_SIZE;
    
    //e. Notify the device about the queue size by writing the size to QueueNum.
    writeword(QUEUE_SIZE, &regs->QueueNum);    //无法写入
    refresh_cache();
    printk("regs->QueueNum = 0x%x",(unsigned long)(&regs->QueueNum)-(unsigned long)(regs));
    //f. Notify the device about the used alignment by writing its value in bytes to QueueAlign.
    writeword(PAGE_SIZE, &regs->QueueAlign);      //无法写入
    refresh_cache();
    //g. Write the physical number of the first page of the queue to the QueuePFN register
    writeword(g_vring.desc, &regs->QueuePFN);
    refresh_cache();

    //8. Set the DRIVER_OK status bit. At this point the device is “live”.
    writeword((readword(&regs->Status) | VIRTIO_STATUS_DRIVER_OK), &regs->Status);
    refresh_cache();
    printk("virtio device init finish!\n");
    g_regs = regs;
    return 0;
}

//initial device
static int virtio_dev_init(uint64_t virt, uint64_t intid)
{
    virtio_regs_legacy *regs = (virtio_regs*)virt;
    if (readword(&regs->MagicValue) != VIRTIO_MAGIC) {
        printk("error: virtio at 0x%x had wrong magic value 0x%x,expected 0x%x\n",virt, regs->MagicValue, VIRTIO_MAGIC);
        return -1;
    }
    refresh_cache();
    if (readword(&regs->Version) != VIRTIO_VERSION_LEGACY) {
        printk("error: virtio at 0x%x had wrong version 0x%x, expected 0x%x\n",virt, regs->Version, VIRTIO_VERSION_LEGACY);
        return -1;
    }

    refresh_cache();
    if (readword(&regs->DeviceID) == 0) {
        printk("this is a invalid device\n");
        return -1;
    }

    refresh_cache();
    if(readword(&regs->VendorID) != VIRTIO_VENDORID)
    {
        printk("vendorid wrong!\n");
        return -1;
    }
    refresh_cache();

    //1.reset device
    writeword(0, &regs->Status);
    refresh_cache();
    //2.Set the ACKNOWLEDGE status bit: the guest OS has noticed the device.
    writeword(readword(&regs->Status) | VIRTIO_STATUS_ACKNOWLEDGE, &regs->Status);
    refresh_cache();
    //3.Set the DRIVER status bit: the guest OS knows how to drive the device
    writeword(readword(&regs->Status) | VIRTIO_STATUS_DRIVER, &regs->Status);
    refresh_cache();

    switch (readword(&regs->DeviceID))
    {
    case VIRTIO_DEV_BLK:
        virtio_blk_init_legacy(regs, intid);
        break;
    default:
        printk("not support this device! DeviceID = %d",readword(&regs->DeviceID));
        break;
    }
}

头文件:


#define VIRTIO_REGS_SIZE                0x00001000
#define VIRTIO_MAGIC                       0x74726976UL
#define VIRTIO_VENDORID                    0x554d4551UL
#define VIRTIO_VERSION_LEGACY             0x1
#define VIRTIO_VERSION                     0x2
#define VIRTIO_DEV_NET                     0x1
#define VIRTIO_DEV_BLK                     0x2

//virtio_regs status
#define VIRTIO_STATUS_ACKNOWLEDGE        (1)
#define VIRTIO_STATUS_DRIVER             (2)
#define VIRTIO_STATUS_FAILED             (128)
#define VIRTIO_STATUS_FEATURES_OK        (8)
#define VIRTIO_STATUS_DRIVER_OK          (4)
#define VIRTIO_STATUS_DEVICE_NEEDS_RESET (64)


// device feature bits
#define VIRTIO_BLK_F_RO              5    /* Disk is read-only */
#define VIRTIO_BLK_F_SCSI            7    /* Supports scsi command passthru */
#define VIRTIO_BLK_F_CONFIG_WCE     11    /* Writeback mode available in config */
#define VIRTIO_BLK_F_MQ             12    /* support more than one vq */
#define VIRTIO_F_ANY_LAYOUT         27
#define VIRTIO_RING_F_INDIRECT_DESC 28
#define VIRTIO_RING_F_EVENT_IDX     29
#define VIRTIO_BLK_F_BARRIER         (0)    //Device supports request barriers.
#define VIRTIO_BLK_F_SCSI             (7) //Device supports scsi packet commands.


//request type
#define VIRTIO_BLK_T_IN                     0
#define VIRTIO_BLK_T_OUT                     1
#define VIRTIO_BLK_T_FLUSH                     4
#define VIRTIO_BLK_T_GET_ID                 8
#define VIRTIO_BLK_T_GET_LIFETIME             10
#define VIRTIO_BLK_T_DISCARD                 11
#define VIRTIO_BLK_T_WRITE_ZEROES             13
#define VIRTIO_BLK_T_SECURE_ERASE             14

//vring
#define QUEUE_SIZE                1024
//desc flags
/* This marks a buffer as continuing via the next field. */
#define VIRTQ_DESC_F_NEXT         1
/* This marks a buffer as device write-only (otherwise device read-only). */
#define VIRTQ_DESC_F_WRITE         2
/* This means the buffer contains a list of buffer descriptors. */
#define VIRTQ_DESC_F_INDIRECT     4
#define VIRTQ_USED_F_NO_NOTIFY     1
#define VIRTQ_USED_F_INDIRECT     0x04

#define BIT(x) (1UL << (x))
#define VIRTIO_F_VERSION_1        ((uint64_t) BIT(32))

//operation type
#define VIRTIO_BLK_T_IN     0        //read
#define VIRTIO_BLK_T_OUT     1        //write


typedef unsigned long long     __virtio64;
typedef unsigned int         __virtio32;
typedef unsigned short        __virtio16;

typedef signed char        int8_t;
typedef short              int16_t;
typedef int                int32_t;
typedef long long          int64_t;
typedef unsigned char      uint8_t;
typedef unsigned short     uint16_t;
typedef unsigned int       uint32_t;
typedef unsigned long long uint64_t;

//legacy device
typedef volatile struct {
    __virtio32 MagicValue;
    __virtio32 Version;
    __virtio32 DeviceID;
    __virtio32 VendorID;
    __virtio32 HostFeatures;
    __virtio32 HostFeaturesSel;
    __virtio32 _reserved0[2];
    __virtio32 GuestFeatures;
    __virtio32 GuestFeaturesSel;
    __virtio32 GuestPageSize;
    __virtio32 _reserved1[1];
    __virtio32 QueueSel;
    __virtio32 QueueNumMax;
    __virtio32 QueueNum;
    __virtio32 QueueAlign;
    __virtio32 QueuePFN;
    __virtio32 _reserved2[3];
    __virtio32 QueueNotify;
    __virtio32 _reserved3[3];
    __virtio32 InterruptStatus;
    __virtio32 InterruptACK;
    __virtio32 _reserved4[2];
    __virtio32 Status;
    __virtio32 _reserved5[35];
    __virtio32 Config[0];
}virtio_regs_legacy;

virtio_regs_legacy *g_regs;

//from qemu-7.0.0
/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */
struct vring_desc {
    /* Address (guest-physical) */
    __virtio64 addr;
    /* Length */
    __virtio32 len;
    /* The flags as indicated above */
    __virtio16 flags;
    /* We chain unused descriptors via this, too */
    __virtio16 next;
};

struct vring_avail {
    __virtio16 flags;
    __virtio16 idx;
    __virtio16 ring[QUEUE_SIZE];    //index of desc
};

struct vring_used_elem {
    /* Index of start of used descriptor chain */
    __virtio32 id;
    /* Total length of the descriptor chain which was used (written to) */
    __virtio32 len;
};

struct vring_used {
    __virtio16 flags;
    __virtio16 idx;
    struct vring_used_elem ring[QUEUE_SIZE];
};

struct vring {
    unsigned int num;
    struct vring_desc *desc;
    struct vring_avail *avail;
    struct vring_used *used;
};

struct virtio_blk_req {
    // header
    __virtio32 type; // VIRTIO_BLK_T_IN for read
    __virtio32 reverse; 
    __virtio64 sector; // sector number
    uint8_t data[512];
    uint8_t status;
    // descriptor table
    struct vring_desc desc[3];
    // avail ring
    struct vring_avail avail;
    // used ring
    struct vring_used used;    
    } req;

extern struct vring g_vring;
void virtio_init();
int virtio_blk_rw(uint32_t type, uint32_t sector, uint8_t *data);
int virtio_rw(uint64_t sector, uint8_t *buffer, int write);
#endif

其他定义:

#define __arch_putword(v,a)        (*(volatile unsigned int *)(a) = (v))
#define writeword(v,c)    ({ unsigned int  __v = v; refresh_cache(); __arch_putword(__v,c);})
#define refresh_cache()        __asm__ __volatile__ ("" : : : "memory")
#define readword(c)    ({ unsigned int  __v = __arch_getword(c); refresh_cache(); __v; })
#define __arch_getqword(a)            (*(volatile unsigned long *)(a))

感谢您!

  • 写回答

6条回答 默认 最新

  • 小码哥Lvan 贵州字节社官方账户 2023-06-01 08:05
    关注
    获得8.45元问题酬金

    你这代码总感觉差点啥,但是又说不出来 没环境不好测试。你可以考虑下环境的问题

    评论

报告相同问题?

问题事件

  • 系统已结题 6月9日
  • 赞助了问题酬金15元 6月1日
  • 修改了问题 6月1日
  • 修改了问题 6月1日
  • 展开全部

悬赏问题

  • ¥30 模拟电路 logisim
  • ¥15 PVE8.2.7无法成功使用a5000的vGPU,什么原因
  • ¥15 is not in the mmseg::model registry。报错,模型注册表找不到自定义模块。
  • ¥15 安装quartus II18.1时弹出此error,怎么解决?
  • ¥15 keil官网下载psn序列号在哪
  • ¥15 想用adb命令做一个通话软件,播放录音
  • ¥30 Pytorch深度学习服务器跑不通问题解决?
  • ¥15 部分客户订单定位有误的问题
  • ¥15 如何在maya程序中利用python编写领子和褶裥的模型的方法
  • ¥15 Bug traq 数据包 大概什么价