virtio简介(四)—— 从零实现一个virtio设备【转】
转自:https://www.cnblogs.com/edver/p/15874178.html
简介:
前几节分析了virtio机制和现有的balloon设备实现,至此我们已经知道了virtio是什么、怎么使用的,本节我们就自己实现一个virtio纯虚设备。
功能:
- QEMU模拟的设备启动一个定时器,每5秒发送一次中断通知GUEST
- GUEST对应的驱动接收到中断后讲自身变量自增,然后通过vring发送给QEMU
- QEMU收到GUEST发送过来的消息后打印出接收到的数值
一: 设备创建
1. 添加virtio id,
用于guest内部的设备和驱动match,需要和linux内核中定义一致。
文件: include/standard-headers/linux/virtio_ids.h
#define VIRTIO_ID_TEST 21 /* virtio test */
2. 添加device id
vendor-id和device-id用于区分PCI设备,注意不要超过0x104f
文件: include/hw/pci/pci.h
#define PCI_DEVICE_ID_VIRTIO_TEST 0x1013
3. 添加virtio-test设备配置空间定义的头文件
定义于GUEST协商配置的feature和config结构体,需要与linux中定义一致,config在本示例中并未使用,结构拷贝自balloon
文件: include/standard-headers/linux/virtio_test.h
#ifndef _LINUX_VIRTIO_TEST_H
#define _LINUX_VIRTIO_TEST_H
#include "standard-headers/linux/types.h"
#include "standard-headers/linux/virtio_types.h"
#include "standard-headers/linux/virtio_ids.h"
#include "standard-headers/linux/virtio_config.h"
#define VIRTIO_TEST_F_CAN_PRINT 0
struct virtio_test_config {
/* Number of pages host wants Guest to give up. */
uint32_t num_pages;
/* Number of pages we've actually got in balloon. */
uint32_t actual;
/* Event host wants Guest to do */
uint32_t event;
};
struct virtio_test_stat {
__virtio16 tag;
__virtio64 val;
} QEMU_PACKED;
#endif
4. 添加virtio-test设备模拟代码
此代码包括了对vring的操作和简介中的功能主体实现,与驱动交互的代码逻辑都在这里。
文件:hw/virtio/virtio-test.c
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/iov.h"
#include "qemu/timer.h"
#include "qemu-common.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-test.h"
#include "sysemu/kvm.h"
#include "sysemu/hax.h"
#include "exec/address-spaces.h"
#include "qapi/error.h"
#include "qapi/qapi-events-misc.h"
#include "qapi/visitor.h"
#include "qemu/error-report.h"
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
#include "migration/migration.h"
static void virtio_test_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOTest *s = VIRTIO_TEST(vdev);
VirtQueueElement *elem;
MemoryRegionSection section;
for (;;) {
size_t offset = 0;
uint32_t pfn;
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
if (!elem) {
return;
}
while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) {
int p = virtio_ldl_p(vdev, &pfn);
offset += 4;
qemu_log("=========get virtio num:%d\n", p);
}
virtqueue_push(vq, elem, offset);
virtio_notify(vdev, vq);
g_free(elem);
}
}
static void virtio_test_get_config(VirtIODevice *vdev, uint8_t *config_data)
{
VirtIOTest *dev = VIRTIO_TEST(vdev);
struct virtio_test_config config;
config.actual = cpu_to_le32(dev->actual);
config.event = cpu_to_le32(dev->event);
memcpy(config_data, &config, sizeof(struct virtio_test_config));
}
static void virtio_test_set_config(VirtIODevice *vdev,
const uint8_t *config_data)
{
VirtIOTest *dev = VIRTIO_TEST(vdev);
struct virtio_test_config config;
memcpy(&config, config_data, sizeof(struct virtio_test_config));
dev->actual = le32_to_cpu(config.actual);
dev->event = le32_to_cpu(config.event);
}
static uint64_t virtio_test_get_features(VirtIODevice *vdev, uint64_t f,
Error **errp)
{
VirtIOTest *dev = VIRTIO_TEST(vdev);
f |= dev->host_features;
virtio_add_feature(&f, VIRTIO_TEST_F_CAN_PRINT);
return f;
}
static int virtio_test_post_load_device(void *opaque, int version_id)
{
VirtIOTest *s = VIRTIO_TEST(opaque);
return 0;
}
static const VMStateDescription vmstate_virtio_test_device = {
.name = "virtio-test-device",
.version_id = 1,
.minimum_version_id = 1,
.post_load = virtio_test_post_load_device,
.fields = (VMStateField[]) {
VMSTATE_UINT32(actual, VirtIOTest),
VMSTATE_END_OF_LIST()
},
};
static void test_stats_change_timer(VirtIOTest *s, int64_t secs)
{
timer_mod(s->stats_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + secs * 1000);
}
static void test_stats_poll_cb(void *opaque)
{
VirtIOTest *s = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
qemu_log("==============set config:%d\n", s->set_config++);
virtio_notify_config(vdev);
test_stats_change_timer(s, 1);
}
static void virtio_test_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOTest *s = VIRTIO_TEST(dev);
int ret;
virtio_init(vdev, "virtio-test", VIRTIO_ID_TEST,
sizeof(struct virtio_test_config));
s->ivq = virtio_add_queue(vdev, 128, virtio_test_handle_output);
/* create a new timer */
g_assert(s->stats_timer == NULL);
s->stats_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, test_stats_poll_cb, s);
test_stats_change_timer(s, 30);
}
static void virtio_test_device_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOTest *s = VIRTIO_TEST(dev);
virtio_cleanup(vdev);
}
static void virtio_test_device_reset(VirtIODevice *vdev)
{
VirtIOTest *s = VIRTIO_TEST(vdev);
}
static void virtio_test_set_status(VirtIODevice *vdev, uint8_t status)
{
VirtIOTest *s = VIRTIO_TEST(vdev);
return;
}
static void virtio_test_instance_init(Object *obj)
{
VirtIOTest *s = VIRTIO_TEST(obj);
return;
}
static const VMStateDescription vmstate_virtio_test = {
.name = "virtio-test",
.minimum_version_id = 1,
.version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_VIRTIO_DEVICE,
VMSTATE_END_OF_LIST()
},
};
static Property virtio_test_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
static void virtio_test_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
dc->props = virtio_test_properties;
dc->vmsd = &vmstate_virtio_test;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
vdc->realize = virtio_test_device_realize;
vdc->unrealize = virtio_test_device_unrealize;
vdc->reset = virtio_test_device_reset;
vdc->get_config = virtio_test_get_config;
vdc->set_config = virtio_test_set_config;
vdc->get_features = virtio_test_get_features;
vdc->set_status = virtio_test_set_status;
vdc->vmsd = &