以下代码是我编写的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(®s->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, ®s->GuestFeatures);
refresh_cache();
writeword(readword(®s->Status)|VIRTIO_STATUS_FEATURES_OK , ®s->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, ®s->QueueSel);
refresh_cache();
//b. Check if the queue is not already in use: read QueuePFN, expecting a returned value of zero (0x0).
if(readword(®s->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(®s->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(®s->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, ®s->QueueNum); //无法写入
refresh_cache();
printk("regs->QueueNum = 0x%x",(unsigned long)(®s->QueueNum)-(unsigned long)(regs));
//f. Notify the device about the used alignment by writing its value in bytes to QueueAlign.
writeword(PAGE_SIZE, ®s->QueueAlign); //无法写入
refresh_cache();
//g. Write the physical number of the first page of the queue to the QueuePFN register
writeword(g_vring.desc, ®s->QueuePFN);
refresh_cache();
//8. Set the DRIVER_OK status bit. At this point the device is “live”.
writeword((readword(®s->Status) | VIRTIO_STATUS_DRIVER_OK), ®s->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(®s->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(®s->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(®s->DeviceID) == 0) {
printk("this is a invalid device\n");
return -1;
}
refresh_cache();
if(readword(®s->VendorID) != VIRTIO_VENDORID)
{
printk("vendorid wrong!\n");
return -1;
}
refresh_cache();
//1.reset device
writeword(0, ®s->Status);
refresh_cache();
//2.Set the ACKNOWLEDGE status bit: the guest OS has noticed the device.
writeword(readword(®s->Status) | VIRTIO_STATUS_ACKNOWLEDGE, ®s->Status);
refresh_cache();
//3.Set the DRIVER status bit: the guest OS knows how to drive the device
writeword(readword(®s->Status) | VIRTIO_STATUS_DRIVER, ®s->Status);
refresh_cache();
switch (readword(®s->DeviceID))
{
case VIRTIO_DEV_BLK:
virtio_blk_init_legacy(regs, intid);
break;
default:
printk("not support this device! DeviceID = %d",readword(®s->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))
感谢您!