diff --git a/tools/virtio/devices/blk/virtio_blk.c b/tools/virtio/devices/blk/virtio_blk.c index 76b8d55e..9c04b695 100644 --- a/tools/virtio/devices/blk/virtio_blk.c +++ b/tools/virtio/devices/blk/virtio_blk.c @@ -13,6 +13,7 @@ #include "virtio.h" #include #include +#include #include #include #include @@ -128,7 +129,7 @@ static void *blkproc_thread(void *arg) { } // create blk dev. -BlkDev *init_blk_dev(VirtIODevice *vdev) { +static BlkDev *init_blk_dev(VirtIODevice *vdev) { BlkDev *dev = malloc(sizeof(BlkDev)); vdev->dev = dev; dev->config.capacity = -1; @@ -145,27 +146,29 @@ BlkDev *init_blk_dev(VirtIODevice *vdev) { return dev; } -int virtio_blk_init(VirtIODevice *vdev, const char *img_path) { - int img_fd = open(img_path, O_RDWR); +static int virtio_blk_init(VirtIODevice *vdev, const char *img_path) { BlkDev *dev = vdev->dev; - struct stat st; - uint64_t blk_size; - if (img_fd == -1) { + if (!dev) { + log_error("virtio_blk_init: vdev->dev is nullptr"); + return -1; + } + + dev->img_fd = open(img_path, O_RDWR); + if (dev->img_fd == -1) { log_error("cannot open %s, Error code is %d", img_path, errno); - close(img_fd); return -1; } - if (fstat(img_fd, &st) == -1) { + + struct stat st; + if (fstat(dev->img_fd, &st) == -1) { log_error("cannot stat %s, Error code is %d", img_path, errno); - close(img_fd); return -1; } - blk_size = st.st_size / 512; // 512 bytes per block + uint64_t blk_size = st.st_size / SECTOR_BSIZE; dev->config.capacity = blk_size; dev->config.size_max = blk_size; - dev->img_fd = img_fd; - vdev->virtio_close = virtio_blk_close; - log_info("debug: virtio_blk_init: %s, size is %lld", img_path, + + log_info("virtio_blk_init: %s, size is %" PRIu64, img_path, dev->config.capacity); return 0; } @@ -227,7 +230,7 @@ static struct blkp_req *virtq_blk_handle_one_request(VirtQueue *vq) { return NULL; } -int virtio_blk_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { +static int virtio_blk_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { log_debug("virtio blk notify handler enter"); BlkDev *blkDev = (BlkDev *)vdev->dev; struct blkp_req *breq; @@ -252,17 +255,74 @@ int virtio_blk_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { return 0; } -void virtio_blk_close(VirtIODevice *vdev) { +static void virtio_blk_reset(VirtIODevice *vdev) { (void)vdev; } + +/* + * Shut down the blk device: signal close, wait for the worker to exit, + * then release all resources. + */ +static void virtio_blk_close(VirtIODevice *vdev) { + if (!vdev) + return; + BlkDev *dev = vdev->dev; - pthread_mutex_lock(&dev->mtx); - dev->close = 1; - pthread_cond_signal(&dev->cond); - pthread_mutex_unlock(&dev->mtx); - pthread_join(dev->tid, NULL); - pthread_mutex_destroy(&dev->mtx); - pthread_cond_destroy(&dev->cond); - close(dev->img_fd); - free(dev); + if (dev) { + pthread_mutex_lock(&dev->mtx); + dev->close = 1; + pthread_cond_signal(&dev->cond); + pthread_mutex_unlock(&dev->mtx); + pthread_join(dev->tid, NULL); + pthread_mutex_destroy(&dev->mtx); + pthread_cond_destroy(&dev->cond); + if (dev->img_fd >= 0) + close(dev->img_fd); + free(dev); + vdev->dev = NULL; + } free(vdev->vqs); + vdev->vqs = NULL; free(vdev); -} \ No newline at end of file +} + +static int virtio_blk_do_init(VirtIODevice *vdev, const void *params) { + const struct virtio_blk_init_params *p = params; + if (!p) + return -EINVAL; + if (!init_blk_dev(vdev)) + return -ENOMEM; + if (virtio_blk_init(vdev, p->img_path) != 0) + return -EIO; + return 0; +} + +const struct virtio_device_ops virtio_blk_ops = { + .type = VirtioTBlock, + .features = BLK_SUPPORTED_FEATURES, + .num_queues = 1, + .queue_max_size = VIRTQUEUE_BLK_MAX_SIZE, + .init = virtio_blk_do_init, + .close = virtio_blk_close, + .reset = virtio_blk_reset, + .notify_handlers = {virtio_blk_notify_handler}, +}; + +static int virtio_blk_parse_params(const cJSON *json, void **out) { + struct virtio_blk_init_params *p = calloc(1, sizeof(*p)); + if (!p) + return -ENOMEM; + cJSON *img = cJSON_GetObjectItem(json, "img"); + if (!cJSON_IsString(img) || !img->valuestring[0]) { + free(p); + return -EINVAL; + } + p->img_path = img->valuestring; + *out = p; + return 0; +} + +static void virtio_blk_free_params(void *params) { free(params); } + +const struct virtio_config_ops virtio_blk_config_ops = { + .parse = virtio_blk_parse_params, + .free = virtio_blk_free_params, +}; diff --git a/tools/virtio/devices/console/virtio_console.c b/tools/virtio/devices/console/virtio_console.c index 240ddbfb..a6390d22 100644 --- a/tools/virtio/devices/console/virtio_console.c +++ b/tools/virtio/devices/console/virtio_console.c @@ -23,7 +23,7 @@ static uint8_t trashbuf[1024]; -ConsoleDev *init_console_dev() { +static ConsoleDev *init_console_dev() { ConsoleDev *dev = (ConsoleDev *)malloc(sizeof(ConsoleDev)); dev->config.cols = 80; dev->config.rows = 25; @@ -87,7 +87,7 @@ static void virtio_console_event_handler(int fd, int epoll_type, void *param) { return; } -int virtio_console_init(VirtIODevice *vdev) { +static int virtio_console_init(VirtIODevice *vdev) { ConsoleDev *dev = (ConsoleDev *)vdev->dev; int master_fd, slave_fd; char *slave_name; @@ -115,8 +115,6 @@ int virtio_console_init(VirtIODevice *vdev) { slave_fd = open(slave_name, O_RDWR); if (slave_fd < 0) { log_error("Failed to open slave pty, errno is %d", errno); - close(master_fd); - dev->master_fd = -1; return -1; } @@ -129,13 +127,7 @@ int virtio_console_init(VirtIODevice *vdev) { dev->slave_keepalive_fd = slave_fd; if (set_nonblocking(dev->master_fd) < 0) { - close(dev->master_fd); - if (dev->slave_keepalive_fd >= 0) { - close(dev->slave_keepalive_fd); - dev->slave_keepalive_fd = -1; - } - dev->master_fd = -1; - log_error("Failed to set nonblocking mode, fd closed!"); + log_error("Failed to set nonblocking mode"); return -1; } @@ -144,20 +136,14 @@ int virtio_console_init(VirtIODevice *vdev) { if (dev->event == NULL) { log_error("Can't register console event"); - close(master_fd); - if (dev->slave_keepalive_fd >= 0) { - close(dev->slave_keepalive_fd); - dev->slave_keepalive_fd = -1; - } - dev->master_fd = -1; return -1; } - vdev->virtio_close = virtio_console_close; return 0; } -int virtio_console_rxq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { +static int virtio_console_rxq_notify_handler(VirtIODevice *vdev, + VirtQueue *vq) { log_debug("%s", __func__); ConsoleDev *dev = (ConsoleDev *)vdev->dev; if (dev->rx_ready <= 0) { @@ -191,7 +177,8 @@ static void virtq_tx_handle_one_request(ConsoleDev *dev, VirtQueue *vq) { free(iov); } -int virtio_console_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { +static int virtio_console_txq_notify_handler(VirtIODevice *vdev, + VirtQueue *vq) { log_debug("%s", __func__); while (!virtqueue_is_empty(vq)) { virtqueue_disable_notify(vq); @@ -204,14 +191,60 @@ int virtio_console_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { return 0; } -void virtio_console_close(VirtIODevice *vdev) { +static void virtio_console_reset(VirtIODevice *vdev) { (void)vdev; } + +static void virtio_console_close(VirtIODevice *vdev) { + if (!vdev) + return; + ConsoleDev *dev = vdev->dev; - close(dev->master_fd); - if (dev->slave_keepalive_fd >= 0) { - close(dev->slave_keepalive_fd); + if (dev) { + if (dev->master_fd >= 0) + close(dev->master_fd); + if (dev->slave_keepalive_fd >= 0) + close(dev->slave_keepalive_fd); + remove_event(dev->event); + free(dev->event); + free(dev); + vdev->dev = NULL; } - free(dev->event); - free(dev); free(vdev->vqs); + vdev->vqs = NULL; free(vdev); -} \ No newline at end of file +} + +static int virtio_console_do_init(VirtIODevice *vdev, const void *params) { + (void)params; + vdev->dev = init_console_dev(); + if (!vdev->dev) + return -ENOMEM; + return virtio_console_init(vdev); +} + +const struct virtio_device_ops virtio_console_ops = { + .type = VirtioTConsole, + .features = CONSOLE_SUPPORTED_FEATURES, + .num_queues = CONSOLE_MAX_QUEUES, + .queue_max_size = VIRTQUEUE_CONSOLE_MAX_SIZE, + .init = virtio_console_do_init, + .close = virtio_console_close, + .reset = virtio_console_reset, + .notify_handlers = + { + [CONSOLE_QUEUE_RX] = virtio_console_rxq_notify_handler, + [CONSOLE_QUEUE_TX] = virtio_console_txq_notify_handler, + }, +}; + +static int virtio_console_parse_params(const cJSON *json, void **out) { + (void)json; + *out = NULL; + return 0; +} + +static void virtio_console_free_params(void *params) { (void)params; } + +const struct virtio_config_ops virtio_console_config_ops = { + .parse = virtio_console_parse_params, + .free = virtio_console_free_params, +}; \ No newline at end of file diff --git a/tools/virtio/devices/gpu/virtio_gpu_base.c b/tools/virtio/devices/gpu/virtio_gpu_base.c index d727ccd6..a3ea1dc5 100644 --- a/tools/virtio/devices/gpu/virtio_gpu_base.c +++ b/tools/virtio/devices/gpu/virtio_gpu_base.c @@ -8,11 +8,13 @@  * Authors:  *        */ +#include "json_parse.h" #include "log.h" #include "sys/queue.h" #include "unistd.h" #include "virtio.h" #include "virtio_gpu.h" +#include #include #include #include @@ -22,7 +24,7 @@ #include #include -GPUDev *init_gpu_dev(GPURequestedState *requested_state) { +static GPUDev *init_gpu_dev(const GPURequestedState *requested_state) { log_info("initializing GPUDev"); if (requested_state == NULL) { @@ -90,15 +92,12 @@ GPUDev *init_gpu_dev(GPURequestedState *requested_state) { return gdev; } -int virtio_gpu_init(VirtIODevice *vdev) { +static int virtio_gpu_init(VirtIODevice *vdev) { log_info("entering %s", __func__); // TODO: Display device initialization GPUDev *gdev = vdev->dev; - // Set the close function for virtio gpu - vdev->virtio_close = virtio_gpu_close; - int drm_fd = 0; // Open card0 @@ -180,62 +179,74 @@ int virtio_gpu_init(VirtIODevice *vdev) { pthread_create(&gdev->gpu_thread, NULL, virtio_gpu_handler, vdev); pthread_cond_init(&gdev->gpu_cond, NULL); pthread_mutex_init(&gdev->queue_mutex, NULL); + gdev->async_started = true; return 0; } -void virtio_gpu_close(VirtIODevice *vdev) { +static void virtio_gpu_close(VirtIODevice *vdev) { + if (!vdev) + return; + log_info("virtio_gpu close"); - // Reclaim memory related to scanouts - GPUDev *gdev = (GPUDev *)vdev->dev; - for (int i = 0; i < gdev->scanouts_num; ++i) { - free(gdev->scanouts[i].current_cursor); + GPUDev *gdev = vdev->dev; + if (gdev) { + // Reclaim memory related to scanouts + for (int i = 0; i < gdev->scanouts_num; ++i) { + free(gdev->scanouts[i].current_cursor); - virtio_gpu_remove_drm_framebuffer(&gdev->scanouts[i]); + virtio_gpu_remove_drm_framebuffer(&gdev->scanouts[i]); - drmModeFreeCrtc(gdev->scanouts[i].crtc); - drmModeFreeEncoder(gdev->scanouts[i].encoder); - drmModeFreeConnector(gdev->scanouts[i].connector); + drmModeFreeCrtc(gdev->scanouts[i].crtc); + drmModeFreeEncoder(gdev->scanouts[i].encoder); + drmModeFreeConnector(gdev->scanouts[i].connector); - // Release card0_fd - if (gdev->scanouts[i].card0_fd != -1) { - close(gdev->scanouts[i].card0_fd); + if (gdev->scanouts[i].card0_fd != -1) { + close(gdev->scanouts[i].card0_fd); + } } - } - // Reclaim memory related to resources - while (!TAILQ_EMPTY(&gdev->resource_list)) { - GPUSimpleResource *temp = TAILQ_FIRST(&gdev->resource_list); - TAILQ_REMOVE(&gdev->resource_list, temp, next); - free(temp); - } + // Reclaim memory related to resources + while (!TAILQ_EMPTY(&gdev->resource_list)) { + GPUSimpleResource *temp = TAILQ_FIRST(&gdev->resource_list); + TAILQ_REMOVE(&gdev->resource_list, temp, next); + free(temp); + } - // Reclaim memory related to command queue - while (!TAILQ_EMPTY(&gdev->command_queue)) { - GPUCommand *temp = TAILQ_FIRST(&gdev->command_queue); - TAILQ_REMOVE(&gdev->command_queue, temp, next); - free(temp); - } + // Reclaim memory related to command queue + while (!TAILQ_EMPTY(&gdev->command_queue)) { + GPUCommand *temp = TAILQ_FIRST(&gdev->command_queue); + TAILQ_REMOVE(&gdev->command_queue, temp, next); + free(temp); + } - // Reclaim async part - gdev->close = true; - pthread_cond_signal(&gdev->gpu_cond); - pthread_join(gdev->gpu_thread, NULL); - pthread_cond_destroy(&gdev->gpu_cond); - pthread_mutex_destroy(&gdev->queue_mutex); + // Reclaim async part + gdev->close = true; + // gpu_cond/gpu_thread only exist once virtio_gpu_init finished; + // on a partial init (e.g. drm open failure) skip them entirely. + if (gdev->async_started) { + pthread_cond_signal(&gdev->gpu_cond); + pthread_join(gdev->gpu_thread, NULL); + pthread_cond_destroy(&gdev->gpu_cond); + pthread_mutex_destroy(&gdev->queue_mutex); + } - free(gdev); - gdev = NULL; + free(gdev); + vdev->dev = NULL; + } - // vq is managed by the driver frontend, free it directly here free(vdev->vqs); + vdev->vqs = NULL; free(vdev); } -void virtio_gpu_reset(GPUDev *gdev) { - // TODO: - for (int i = 0; i < HVISOR_VIRTIO_GPU_MAX_SCANOUTS; ++i) { +static void virtio_gpu_reset(VirtIODevice *vdev) { + if (!vdev || !vdev->dev) + return; + + GPUDev *gdev = vdev->dev; + for (int i = 0; i < gdev->scanouts_num; ++i) { gdev->scanouts[i].resource_id = 0; gdev->scanouts[i].width = 0; gdev->scanouts[i].height = 0; @@ -244,7 +255,35 @@ void virtio_gpu_reset(GPUDev *gdev) { } } -int virtio_gpu_ctrl_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { +static int virtio_gpu_do_init(VirtIODevice *vdev, const void *params) { + vdev->dev = init_gpu_dev(params); + if (!vdev->dev) + return -ENOMEM; + return virtio_gpu_init(vdev); +} + +static int virtio_gpu_parse_params(const cJSON *json, void **out) { + GPURequestedState *s = calloc(1, sizeof(*s)); + if (!s) + return -ENOMEM; + + if (parse_json_u32(cJSON_GetObjectItem(json, "width"), &s->width) != 0 || + parse_json_u32(cJSON_GetObjectItem(json, "height"), &s->height) != 0) { + free(s); + return -EINVAL; + } + *out = s; + return 0; +} + +static void virtio_gpu_free_params(void *params) { free(params); } + +const struct virtio_config_ops virtio_gpu_config_ops = { + .parse = virtio_gpu_parse_params, + .free = virtio_gpu_free_params, +}; + +static int virtio_gpu_ctrl_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { log_debug("entering %s", __func__); GPUDev *gdev = vdev->dev; @@ -272,7 +311,7 @@ int virtio_gpu_ctrl_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { return 0; } -int virtio_gpu_cursor_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { +static int virtio_gpu_cursor_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { log_debug("entering %s", __func__); virtqueue_disable_notify(vq); @@ -291,6 +330,21 @@ int virtio_gpu_cursor_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { return 0; } +const struct virtio_device_ops virtio_gpu_ops = { + .type = VirtioTGPU, + .features = GPU_SUPPORTED_FEATURES, + .num_queues = GPU_MAX_QUEUES, + .queue_max_size = VIRTQUEUE_GPU_MAX_SIZE, + .init = virtio_gpu_do_init, + .close = virtio_gpu_close, + .reset = virtio_gpu_reset, + .notify_handlers = + { + [GPU_CONTROL_QUEUE] = virtio_gpu_ctrl_notify_handler, + [GPU_CURSOR_QUEUE] = virtio_gpu_cursor_notify_handler, + }, +}; + int virtio_gpu_handle_single_request(VirtIODevice *vdev, VirtQueue *vq, uint32_t from) { // virtio-gpu dev diff --git a/tools/virtio/devices/net/virtio_net.c b/tools/virtio/devices/net/virtio_net.c index 4bccd250..db4d7be1 100644 --- a/tools/virtio/devices/net/virtio_net.c +++ b/tools/virtio/devices/net/virtio_net.c @@ -10,6 +10,7 @@  */ #include "virtio_net.h" #include "event_monitor.h" +#include "json_parse.h" #include "log.h" #include "virtio.h" @@ -23,14 +24,9 @@ #include #include -NetDev *init_net_dev(uint8_t mac[]) { +static NetDev *init_net_dev(const uint8_t mac[]) { NetDev *dev = malloc(sizeof(NetDev)); - dev->config.mac[0] = mac[0]; - dev->config.mac[1] = mac[1]; - dev->config.mac[2] = mac[2]; - dev->config.mac[3] = mac[3]; - dev->config.mac[4] = mac[4]; - dev->config.mac[5] = mac[5]; + memcpy(dev->config.mac, mac, sizeof(dev->config.mac)); dev->config.status = VIRTIO_NET_S_LINK_UP; dev->tapfd = -1; dev->rx_ready = 0; @@ -41,7 +37,7 @@ NetDev *init_net_dev(uint8_t mac[]) { } // open tap device -static int open_tap(char *devname) { +static int open_tap(const char *devname) { log_info("virtio net tap open"); int tunfd; struct ifreq ifr; @@ -66,7 +62,7 @@ static int open_tap(char *devname) { } /// When driver notifies rxq, it means the rx process can now begin -int virtio_net_rxq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { +static int virtio_net_rxq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { log_debug("virtio_net_rxq_notify_handler"); NetDev *net = vdev->dev; if (net->rx_ready <= 0) { @@ -88,7 +84,7 @@ size_t get_nethdr_size(VirtIODevice *vdev) { } /// Called when tap device received packets -void virtio_net_event_handler(int fd, int epoll_type, void *param) { +static void virtio_net_event_handler(int fd, int epoll_type, void *param) { log_debug("virtio_net_event_handler"); VirtIODevice *vdev = param; NetDev *net = vdev->dev; @@ -234,7 +230,7 @@ static void virtq_tx_handle_one_request(VirtIODevice *vdev, VirtQueue *vq, (*out_count)++; } -int virtio_net_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { +static int virtio_net_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { log_debug("virtio_net_txq_notify_handler"); virtqueue_disable_notify(vq); uint16_t batch_indices[VIRTQUEUE_NET_MAX_SIZE]; @@ -264,7 +260,7 @@ int virtio_net_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { return 0; } -void net_on_status(VirtIODevice *vdev, uint32_t status) { +static void net_on_status(VirtIODevice *vdev, uint32_t status) { NetDev *net = vdev->dev; // FEATURES_OK indicates guest has finished writing DRIVER_FEATURES. @@ -280,7 +276,7 @@ void net_on_status(VirtIODevice *vdev, uint32_t status) { } } -int virtio_net_init(VirtIODevice *vdev, char *devname) { +static int virtio_net_init(VirtIODevice *vdev, const char *devname) { log_info("virtio net init"); NetDev *net = vdev->dev; // open tap device @@ -292,41 +288,109 @@ int virtio_net_init(VirtIODevice *vdev, char *devname) { // set tap device O_NONBLOCK. If io operation like readv blocks, then return // errno EWOULDBLOCK if (set_nonblocking(net->tapfd) < 0) { - close(net->tapfd); - net->tapfd = -1; + log_error("failed to set tap nonblocking"); + return -1; } // register an epoll read event for tap device net->event = add_event(net->tapfd, EPOLLIN, virtio_net_event_handler, vdev); if (net->event == NULL) { log_error("Can't register net event"); - close(net->tapfd); - net->tapfd = -1; return -1; } net->in_iov = malloc(sizeof(struct iovec) * NET_IOV_MAX); net->out_iov = malloc(sizeof(struct iovec) * NET_IOV_MAX); if (!net->in_iov || !net->out_iov) { log_error("failed to allocate iov buffers"); - free(net->in_iov); - free(net->out_iov); - net->in_iov = NULL; - net->out_iov = NULL; - close(net->tapfd); - net->tapfd = -1; return -1; } - vdev->status_changed = net_on_status; - vdev->virtio_close = virtio_net_close; return 0; } -void virtio_net_close(VirtIODevice *vdev) { +static void virtio_net_reset(VirtIODevice *vdev) { + if (!vdev || !vdev->dev) + return; + NetDev *dev = vdev->dev; + dev->rx_ready = false; +} + +static void virtio_net_close(VirtIODevice *vdev) { + if (!vdev) + return; + NetDev *dev = vdev->dev; - close(dev->tapfd); - free(dev->event); - free(dev->in_iov); - free(dev->out_iov); - free(dev); + if (dev) { + if (dev->tapfd >= 0) + close(dev->tapfd); + remove_event(dev->event); + free(dev->event); + free(dev->in_iov); + free(dev->out_iov); + free(dev); + vdev->dev = NULL; + } free(vdev->vqs); + vdev->vqs = NULL; free(vdev); } + +static int virtio_net_do_init(VirtIODevice *vdev, const void *params) { + const struct virtio_net_init_params *p = params; + if (!p) + return -EINVAL; + vdev->dev = init_net_dev(p->mac); + if (!vdev->dev) + return -ENOMEM; + return virtio_net_init(vdev, p->tap); +} + +const struct virtio_device_ops virtio_net_ops = { + .type = VirtioTNet, + .features = NET_SUPPORTED_FEATURES, + .num_queues = NET_MAX_QUEUES, + .queue_max_size = VIRTQUEUE_NET_MAX_SIZE, + .init = virtio_net_do_init, + .close = virtio_net_close, + .reset = virtio_net_reset, + .status_changed = net_on_status, + .notify_handlers = + { + [NET_QUEUE_RX] = virtio_net_rxq_notify_handler, + [NET_QUEUE_TX] = virtio_net_txq_notify_handler, + }, +}; + +static int virtio_net_parse_params(const cJSON *json, void **out) { + struct virtio_net_init_params *p = calloc(1, sizeof(*p)); + if (!p) + return -ENOMEM; + + cJSON *tap = cJSON_GetObjectItem(json, "tap"); + if (!cJSON_IsString(tap) || !tap->valuestring[0]) { + free(p); + return -EINVAL; + } + p->tap = tap->valuestring; + + cJSON *mac_json = cJSON_GetObjectItem(json, "mac"); + if (cJSON_GetArraySize(mac_json) != 6) { + free(p); + return -EINVAL; + } + for (int i = 0; i < 6; i++) { + if (parse_json_u8(cJSON_GetArrayItem(mac_json, i), &p->mac[i]) != 0) { + log_error("failed to parse mac byte %d", i); + free(p); + return -EINVAL; + } + } + + *out = p; + return 0; +} + +static void virtio_net_free_params(void *params) { free(params); } + +const struct virtio_config_ops virtio_net_config_ops = { + .parse = virtio_net_parse_params, + .free = virtio_net_free_params, +}; diff --git a/tools/virtio/devices/scmi/virtio_scmi.c b/tools/virtio/devices/scmi/virtio_scmi.c index 8a2f0e31..3a9a6dec 100644 --- a/tools/virtio/devices/scmi/virtio_scmi.c +++ b/tools/virtio/devices/scmi/virtio_scmi.c @@ -19,7 +19,7 @@ #include #include -static int parse_id_array(cJSON *json_array, uint32_t **ids_out, +static int parse_id_array(const cJSON *json_array, uint32_t **ids_out, uint32_t *count_out) { if (!json_array || !cJSON_IsArray(json_array)) { *ids_out = NULL; @@ -65,19 +65,28 @@ void scmi_dev_free(SCMIDev *dev) { free(dev); } -int scmi_dev_parse_clock_ids(SCMIDev *dev, void *json_array) { - return parse_id_array((cJSON *)json_array, &dev->clock_ids, - &dev->clock_count); +int scmi_dev_parse_clock_ids(struct virtio_scmi_init_params *p, + const cJSON *json_array) { + return parse_id_array(json_array, &p->clock_ids, &p->clock_count); } -int scmi_dev_parse_reset_ids(SCMIDev *dev, void *json_array) { - return parse_id_array((cJSON *)json_array, &dev->reset_ids, - &dev->reset_count); +int scmi_dev_parse_reset_ids(struct virtio_scmi_init_params *p, + const cJSON *json_array) { + return parse_id_array(json_array, &p->reset_ids, &p->reset_count); } -int scmi_dev_parse_power_ids(SCMIDev *dev, void *json_array) { - return parse_id_array((cJSON *)json_array, &dev->power_ids, - &dev->power_count); +int scmi_dev_parse_power_ids(struct virtio_scmi_init_params *p, + const cJSON *json_array) { + return parse_id_array(json_array, &p->power_ids, &p->power_count); +} + +void scmi_dev_free_params(struct virtio_scmi_init_params *p) { + if (!p) + return; + free(p->clock_ids); + free(p->reset_ids); + free(p->power_ids); + free(p); } static int virtq_tx_handle_one_request(void *dev, VirtQueue *vq) { @@ -149,7 +158,7 @@ static int virtq_tx_handle_one_request(void *dev, VirtQueue *vq) { return 0; } -int virtio_scmi_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { +static int virtio_scmi_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { while (!virtqueue_is_empty(vq)) { virtqueue_disable_notify(vq); while (!virtqueue_is_empty(vq)) { @@ -168,9 +177,118 @@ int virtio_scmi_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq) { return 0; } -void virtio_scmi_close(VirtIODevice *vdev) { +static void virtio_scmi_reset(VirtIODevice *vdev) { (void)vdev; } + +static void virtio_scmi_close(VirtIODevice *vdev) { + if (!vdev) + return; + SCMIDev *dev = vdev->dev; - scmi_dev_free(dev); + if (dev) { + scmi_dev_free(dev); + vdev->dev = NULL; + } free(vdev->vqs); + vdev->vqs = NULL; free(vdev); } + +/* + * Deep-copy a count-sized uint32 id array; count == 0 keeps *dst NULL. + * Failure cleanup is deferred to ops->close (scmi_dev_free tolerates + * NULL id arrays), so a non-zero return just needs to propagate. + */ +static int scmi_copy_id_array(uint32_t **dst, const uint32_t *src, + uint32_t count) { + if (count == 0) + return 0; + *dst = calloc(count, sizeof(uint32_t)); + if (!*dst) + return -ENOMEM; + memcpy(*dst, src, count * sizeof(uint32_t)); + return 0; +} + +static int virtio_scmi_do_init(VirtIODevice *vdev, const void *params) { + const struct virtio_scmi_init_params *p = params; + SCMIDev *dev; + + if (p) { + dev = calloc(1, sizeof(SCMIDev)); + if (!dev) + return -ENOMEM; + vdev->dev = dev; + + // Deep-copy id arrays so that SCMIDev and the caller each own their + // copies — no ownership transfer, no double-free risk. + if (scmi_copy_id_array(&dev->clock_ids, p->clock_ids, p->clock_count) || + scmi_copy_id_array(&dev->reset_ids, p->reset_ids, p->reset_count) || + scmi_copy_id_array(&dev->power_ids, p->power_ids, p->power_count)) + return -ENOMEM; + dev->clock_count = p->clock_count; + dev->reset_count = p->reset_count; + dev->power_count = p->power_count; + + scmi_dev_register_protocol(dev, SCMI_PROTO_ID_BASE, + virtio_scmi_base_handle_req); + if (dev->clock_count > 0) + scmi_dev_register_protocol(dev, SCMI_PROTO_ID_CLOCK, + virtio_scmi_clock_handle_req); + if (dev->power_count > 0) + scmi_dev_register_protocol(dev, SCMI_PROTO_ID_POWER, + virtio_scmi_power_handle_req); + if (dev->reset_count > 0) + scmi_dev_register_protocol(dev, SCMI_PROTO_ID_RESET, + virtio_scmi_reset_handle_req); + } else { + dev = scmi_dev_create(); + if (!dev) + return -ENOMEM; + vdev->dev = dev; + } + + return 0; +} + +const struct virtio_device_ops virtio_scmi_ops = { + .type = VirtioTSCMI, + .features = SCMI_SUPPORTED_FEATURES, + .num_queues = SCMI_MAX_QUEUES, + .queue_max_size = VIRTQUEUE_SCMI_MAX_SIZE, + .init = virtio_scmi_do_init, + .close = virtio_scmi_close, + .reset = virtio_scmi_reset, + .notify_handlers = + { + [SCMI_QUEUE_TX] = virtio_scmi_txq_notify_handler, + }, +}; + +static int virtio_scmi_parse_params(const cJSON *json, void **out) { + struct virtio_scmi_init_params *p = calloc(1, sizeof(*p)); + if (!p) + return -ENOMEM; + + cJSON *clock_ids = cJSON_GetObjectItem(json, "clock_ids"); + cJSON *reset_ids = cJSON_GetObjectItem(json, "reset_ids"); + cJSON *power_ids = cJSON_GetObjectItem(json, "power_ids"); + + if (scmi_dev_parse_clock_ids(p, clock_ids) < 0 || + scmi_dev_parse_reset_ids(p, reset_ids) < 0 || + scmi_dev_parse_power_ids(p, power_ids) < 0) { + scmi_dev_free_params(p); + return -EINVAL; + } + + *out = p; + return 0; +} + +static void virtio_scmi_free_params(void *params) { + scmi_dev_free_params(params); +} + +const struct virtio_config_ops virtio_scmi_config_ops = { + .parse = virtio_scmi_parse_params, + .free = virtio_scmi_free_params, +}; diff --git a/tools/virtio/event_monitor.c b/tools/virtio/event_monitor.c index 7c7c0b48..095b652a 100644 --- a/tools/virtio/event_monitor.c +++ b/tools/virtio/event_monitor.c @@ -118,10 +118,28 @@ int initialize_event_monitor() { } } +void remove_event(struct hvisor_event *hevent) { + int i; + + if (!hevent) + return; + + for (i = 0; i < events_num; i++) { + if (events[i] == hevent) { + epoll_ctl(epoll_fd, EPOLL_CTL_DEL, hevent->fd, NULL); + events[i] = NULL; + return; + } + } +} + void destroy_event_monitor() { int i; - for (i = 0; i < events_num; i++) - epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i]->fd, NULL); + for (i = 0; i < events_num; i++) { + if (events[i] != NULL) + epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i]->fd, NULL); + events[i] = NULL; + } close(epoll_fd); // When the main thread exits, the epoll thread will also exit. Therefore, // we do not directly terminate the epoll thread here. diff --git a/tools/virtio/include/event_monitor.h b/tools/virtio/include/event_monitor.h index 55b27396..742028bd 100644 --- a/tools/virtio/include/event_monitor.h +++ b/tools/virtio/include/event_monitor.h @@ -23,4 +23,5 @@ int initialize_event_monitor(void); void destroy_event_monitor(); struct hvisor_event *add_event(int fd, int epoll_type, void (*handler)(int, int, void *), void *param); +void remove_event(struct hvisor_event *hevent); #endif // HVISOR_EVENT_H diff --git a/tools/virtio/include/virtio.h b/tools/virtio/include/virtio.h index 5dd92615..a6e88dfa 100644 --- a/tools/virtio/include/virtio.h +++ b/tools/virtio/include/virtio.h @@ -131,6 +131,23 @@ struct VirtIODevice { bool interrupt_line_asserted; }; +struct virtio_device_ops { + VirtioDeviceType type; + uint64_t features; + uint32_t num_queues; + uint32_t queue_max_size; + int (*init)(VirtIODevice *vdev, const void *params); + void (*close)(VirtIODevice *vdev); + void (*reset)(VirtIODevice *vdev); + void (*status_changed)(VirtIODevice *vdev, uint32_t status); +#define VIRTIO_MAX_VQUEUES 4 + int (*notify_handlers[VIRTIO_MAX_VQUEUES])(VirtIODevice *, VirtQueue *); +}; + +struct virtio_config_ops { + int (*parse)(const cJSON *json, void **params_out); + void (*free)(void *params); +}; // used event idx for driver telling device when to notify driver. #define VQ_USED_EVENT(vq) ((vq)->avail_ring->ring[(vq)->num]) // avail event idx for device telling driver when to notify device. @@ -156,9 +173,7 @@ void rw_barrier(void); VirtIODevice *create_virtio_device(VirtioDeviceType dev_type, uint32_t zone_id, uint64_t base_addr, uint64_t len, - uint32_t irq_id, void *arg0, void *arg1); - -void init_virtio_queue(VirtIODevice *vdev, VirtioDeviceType type); + uint32_t irq_id, const void *params); void init_mmio_regs(VirtMmioRegs *regs, VirtioDeviceType type); @@ -241,7 +256,7 @@ void handle_virtio_requests(); int virtio_init(); -int create_virtio_device_from_json(cJSON *device_json, int zone_id); +int create_virtio_device_from_json(const cJSON *device_json, int zone_id); int virtio_start_from_json(char *json_path); diff --git a/tools/virtio/include/virtio_blk.h b/tools/virtio/include/virtio_blk.h index 17a3a722..a89f5e3b 100644 --- a/tools/virtio/include/virtio_blk.h +++ b/tools/virtio/include/virtio_blk.h @@ -52,9 +52,11 @@ typedef struct virtio_blk_dev { int close; } BlkDev; -BlkDev *init_blk_dev(VirtIODevice *vdev); -int virtio_blk_init(VirtIODevice *vdev, const char *img_path); -int virtio_blk_notify_handler(VirtIODevice *vdev, VirtQueue *vq); -void virtio_blk_close(VirtIODevice *vdev); +struct virtio_blk_init_params { + const char *img_path; +}; + +extern const struct virtio_device_ops virtio_blk_ops; +extern const struct virtio_config_ops virtio_blk_config_ops; #endif /* _HVISOR_VIRTIO_BLK_H */ diff --git a/tools/virtio/include/virtio_console.h b/tools/virtio/include/virtio_console.h index bd08ebd1..7a6252ae 100644 --- a/tools/virtio/include/virtio_console.h +++ b/tools/virtio/include/virtio_console.h @@ -30,9 +30,7 @@ typedef struct virtio_console_dev { struct hvisor_event *event; } ConsoleDev; -ConsoleDev *init_console_dev(); -int virtio_console_init(VirtIODevice *vdev); -int virtio_console_rxq_notify_handler(VirtIODevice *vdev, VirtQueue *vq); -int virtio_console_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq); -void virtio_console_close(VirtIODevice *vdev); +extern const struct virtio_device_ops virtio_console_ops; +extern const struct virtio_config_ops virtio_console_config_ops; + #endif \ No newline at end of file diff --git a/tools/virtio/include/virtio_gpu.h b/tools/virtio/include/virtio_gpu.h index f3de4c14..32a4f2a7 100644 --- a/tools/virtio/include/virtio_gpu.h +++ b/tools/virtio/include/virtio_gpu.h @@ -193,6 +193,7 @@ typedef struct virtio_gpu_dev { pthread_cond_t gpu_cond; pthread_mutex_t queue_mutex; bool close; + bool async_started; // True once the async worker thread exists } GPUDev; typedef struct virtio_gpu_control_cmd { @@ -211,23 +212,13 @@ typedef struct virtio_gpu_control_cmd { /********************************************************************* virtio_gpu_base.c */ -// Initialize GPUDev structure -GPUDev *init_gpu_dev(GPURequestedState *requested_states); -// Initialize virtio-gpu device -int virtio_gpu_init(VirtIODevice *vdev); - -// Close virtio-gpu device -void virtio_gpu_close(VirtIODevice *vdev); - -// Reset virtio-gpu device -void virtio_gpu_reset(); +extern const struct virtio_device_ops virtio_gpu_ops; +extern const struct virtio_config_ops virtio_gpu_config_ops; // Handler function when controlq has requests to process -int virtio_gpu_ctrl_notify_handler(VirtIODevice *vdev, VirtQueue *vq); // Handler function when cursorq has requests to process -int virtio_gpu_cursor_notify_handler(VirtIODevice *vdev, VirtQueue *vq); // Process a single request int virtio_gpu_handle_single_request(VirtIODevice *vdev, VirtQueue *vq, diff --git a/tools/virtio/include/virtio_net.h b/tools/virtio/include/virtio_net.h index 0f457a09..5b71c856 100644 --- a/tools/virtio/include/virtio_net.h +++ b/tools/virtio/include/virtio_net.h @@ -23,6 +23,11 @@ #define VIRTQUEUE_NET_MAX_SIZE 256 +struct virtio_net_init_params { + uint8_t mac[6]; + const char *tap; +}; + // Max iov entries for a single descriptor chain. Each descriptor in the // chain contributes at most one iov entry, and a chain can never exceed // the total queue size (a single descriptor's next field cannot wrap past @@ -48,13 +53,7 @@ typedef struct virtio_net_dev { struct iovec *out_iov; } NetDev; -NetDev *init_net_dev(uint8_t mac[]); - -int virtio_net_rxq_notify_handler(VirtIODevice *vdev, VirtQueue *vq); -int virtio_net_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq); +extern const struct virtio_device_ops virtio_net_ops; +extern const struct virtio_config_ops virtio_net_config_ops; -void virtio_net_event_handler(int fd, int epoll_type, void *param); -int virtio_net_init(VirtIODevice *vdev, char *devname); -void virtio_net_close(VirtIODevice *vdev); -void net_on_status(VirtIODevice *vdev, uint32_t status); #endif //_HVISOR_VIRTIO_NET_H diff --git a/tools/virtio/include/virtio_scmi.h b/tools/virtio/include/virtio_scmi.h index 0c63926c..8ef73653 100644 --- a/tools/virtio/include/virtio_scmi.h +++ b/tools/virtio/include/virtio_scmi.h @@ -235,15 +235,28 @@ int scmi_handle_message(SCMIDev *dev, uint8_t protocol_id, uint8_t msg_id, uint16_t token, const struct iovec *req_iov, struct scmi_resp_ctx *ctx); +struct virtio_scmi_init_params { + uint32_t *clock_ids; + uint32_t clock_count; + uint32_t *reset_ids; + uint32_t reset_count; + uint32_t *power_ids; + uint32_t power_count; +}; + SCMIDev *scmi_dev_create(void); void scmi_dev_free(SCMIDev *dev); -int virtio_scmi_txq_notify_handler(VirtIODevice *vdev, VirtQueue *vq); -void virtio_scmi_close(VirtIODevice *vdev); -/* JSON array parsing: fills dev->clock_ids / dev->reset_ids / dev->power_ids */ -int scmi_dev_parse_clock_ids(SCMIDev *dev, void *json_array); -int scmi_dev_parse_reset_ids(SCMIDev *dev, void *json_array); -int scmi_dev_parse_power_ids(SCMIDev *dev, void *json_array); +extern const struct virtio_device_ops virtio_scmi_ops; + +/* JSON array parsing: fills p->clock_ids / p->reset_ids / p->power_ids */ +int scmi_dev_parse_clock_ids(struct virtio_scmi_init_params *p, + const cJSON *json_array); +int scmi_dev_parse_reset_ids(struct virtio_scmi_init_params *p, + const cJSON *json_array); +int scmi_dev_parse_power_ids(struct virtio_scmi_init_params *p, + const cJSON *json_array); +void scmi_dev_free_params(struct virtio_scmi_init_params *p); /* /dev/hvisor fd, opened once in virtio_start() */ extern int ko_fd; @@ -258,4 +271,6 @@ struct hvisor_scmi_ioctl_hdr { int hvisor_scmi_ioctl_cmd(int ioctl_cmd, void *args, size_t args_size, uint32_t subcmd, const char *proto_name); +extern const struct virtio_config_ops virtio_scmi_config_ops; + #endif diff --git a/tools/virtio/virtio.c b/tools/virtio/virtio.c index 0b00d61f..2d3cc4e0 100644 --- a/tools/virtio/virtio.c +++ b/tools/virtio/virtio.c @@ -184,22 +184,75 @@ inline void rw_barrier(void) { #endif } +// --------------------------------------------------------------------------- +// Device ops table — one pointer per device type, defined in each device's .c +static const struct virtio_device_ops *const device_ops_table[] = { + [VirtioTBlock] = &virtio_blk_ops, [VirtioTNet] = &virtio_net_ops, + [VirtioTConsole] = &virtio_console_ops, [VirtioTSCMI] = &virtio_scmi_ops, +#ifdef ENABLE_VIRTIO_GPU + [VirtioTGPU] = &virtio_gpu_ops, +#endif +}; + +static const struct virtio_device_ops *lookup_ops(VirtioDeviceType type) { + int n = (int)(sizeof(device_ops_table) / sizeof(device_ops_table[0])); + if (type <= VirtioTNone || (int)type >= n) + return NULL; + return device_ops_table[type]; +} + +static const struct virtio_config_ops *const config_ops_table[] = { + [VirtioTBlock] = &virtio_blk_config_ops, + [VirtioTNet] = &virtio_net_config_ops, + [VirtioTConsole] = &virtio_console_config_ops, + [VirtioTSCMI] = &virtio_scmi_config_ops, +#ifdef ENABLE_VIRTIO_GPU + [VirtioTGPU] = &virtio_gpu_config_ops, +#endif +}; + +static const struct virtio_config_ops * +lookup_config_ops(VirtioDeviceType type) { + int n = (int)(sizeof(config_ops_table) / sizeof(config_ops_table[0])); + if (type <= VirtioTNone || (int)type >= n) + return NULL; + return config_ops_table[type]; +} + +static int init_virtio_queue(VirtIODevice *vdev, + const struct virtio_device_ops *ops); + +// --------------------------------------------------------------------------- +// Device creation — fully table-driven. +// --------------------------------------------------------------------------- + // create a virtio device. VirtIODevice *create_virtio_device(VirtioDeviceType dev_type, uint32_t zone_id, uint64_t base_addr, uint64_t len, - uint32_t irq_id, void *arg0, void *arg1) { + uint32_t irq_id, const void *params) { + const struct virtio_device_ops *ops = lookup_ops(dev_type); + if (!ops) { + log_error("unsupported virtio device type %d", dev_type); + return NULL; + } + log_info( "create virtio device type %s, zone id %d, base addr %lx, len %lx, " "irq id %d", virtio_device_type_to_string(dev_type), zone_id, base_addr, len, irq_id); - VirtIODevice *vdev = NULL; - int is_err; - vdev = calloc(1, sizeof(VirtIODevice)); - if (vdev == NULL) { + + if (vdevs_num >= MAX_DEVS) { + log_error("virtio device num exceed max limit"); + return NULL; + } + + VirtIODevice *vdev = calloc(1, sizeof(VirtIODevice)); + if (!vdev) { log_error("failed to allocate virtio device"); return NULL; } + init_mmio_regs(&vdev->regs, dev_type); vdev->base_addr = base_addr; vdev->len = len; @@ -208,158 +261,54 @@ VirtIODevice *create_virtio_device(VirtioDeviceType dev_type, uint32_t zone_id, vdev->type = dev_type; pthread_mutex_init(&vdev->interrupt_lock, NULL); vdev->interrupt_line_asserted = false; - - switch (dev_type) { - case VirtioTBlock: - vdev->regs.dev_feature = BLK_SUPPORTED_FEATURES; - init_blk_dev(vdev); - init_virtio_queue(vdev, dev_type); - log_info("debug: init_blk_dev and init_virtio_queue finished\n"); - is_err = virtio_blk_init(vdev, (const char *)arg0); - break; - - case VirtioTNet: - vdev->regs.dev_feature = NET_SUPPORTED_FEATURES; - vdev->dev = init_net_dev(arg0); - init_virtio_queue(vdev, dev_type); - is_err = virtio_net_init(vdev, (char *)arg1); - break; - - case VirtioTConsole: - vdev->regs.dev_feature = CONSOLE_SUPPORTED_FEATURES; - vdev->dev = init_console_dev(); - init_virtio_queue(vdev, dev_type); - is_err = virtio_console_init(vdev); - break; - - case VirtioTSCMI: - vdev->regs.dev_feature = SCMI_SUPPORTED_FEATURES; - vdev->dev = arg0 ? arg0 : scmi_dev_create(); - vdev->virtio_close = virtio_scmi_close; - init_virtio_queue(vdev, dev_type); - is_err = 0; - break; - - case VirtioTGPU: -#ifdef ENABLE_VIRTIO_GPU - vdev->regs.dev_feature = GPU_SUPPORTED_FEATURES; - vdev->dev = init_gpu_dev((GPURequestedState *)arg0); - free(arg0); - init_virtio_queue(vdev, dev_type); - is_err = virtio_gpu_init(vdev); -#else - log_error("virtio gpu is not enabled"); - goto err; -#endif - break; - - default: - log_error("unsupported virtio device type"); - goto err; - } - - if (is_err) - + vdev->regs.dev_feature = ops->features; + vdev->virtio_close = ops->close; + vdev->status_changed = ops->status_changed; + + // Allocate virtqueues before device init: net/console register their + // fds with the already-running event-monitor epoll inside ops->init, + // and the event handlers dereference vdev->vqs. The pre-ops-table + // code also initialized queues first — keep that ordering. + if (init_virtio_queue(vdev, ops) != 0) goto err; - // If reaches max number of virtual devices - if (vdevs_num == MAX_DEVS) { - log_error("virtio device num exceed max limit"); + if (ops->init(vdev, params) != 0) goto err; - } - - if (vdev->dev == NULL) { - log_error("failed to init dev"); - goto err; - } log_info("create %s success", virtio_device_type_to_string(dev_type)); vdevs[vdevs_num++] = vdev; - return vdev; err: - free(vdev); + ops->close(vdev); return NULL; } -void init_virtio_queue(VirtIODevice *vdev, VirtioDeviceType type) { - VirtQueue *vqs = NULL; - +static int init_virtio_queue(VirtIODevice *vdev, + const struct virtio_device_ops *ops) { log_info("Initializing virtio queue for zone:%d, device type:%s", - vdev->zone_id, virtio_device_type_to_string(type)); - - switch (type) { - case VirtioTBlock: - vdev->vqs_len = 1; - vqs = malloc(sizeof(VirtQueue)); - virtqueue_reset(vqs, 0); - vqs->queue_num_max = VIRTQUEUE_BLK_MAX_SIZE; - vqs->notify_handler = virtio_blk_notify_handler; - vqs->dev = vdev; - vdev->vqs = vqs; - break; - - case VirtioTNet: - vdev->vqs_len = NET_MAX_QUEUES; - vqs = malloc(sizeof(VirtQueue) * NET_MAX_QUEUES); - for (int i = 0; i < NET_MAX_QUEUES; ++i) { - virtqueue_reset(vqs, i); - vqs[i].queue_num_max = VIRTQUEUE_NET_MAX_SIZE; - vqs[i].dev = vdev; - } - vqs[NET_QUEUE_RX].notify_handler = virtio_net_rxq_notify_handler; - vqs[NET_QUEUE_TX].notify_handler = virtio_net_txq_notify_handler; - vdev->vqs = vqs; - break; + vdev->zone_id, virtio_device_type_to_string(ops->type)); - case VirtioTConsole: - vdev->vqs_len = CONSOLE_MAX_QUEUES; - vqs = malloc(sizeof(VirtQueue) * CONSOLE_MAX_QUEUES); - for (int i = 0; i < CONSOLE_MAX_QUEUES; ++i) { - virtqueue_reset(vqs, i); - vqs[i].queue_num_max = VIRTQUEUE_CONSOLE_MAX_SIZE; - vqs[i].dev = vdev; - } - vqs[CONSOLE_QUEUE_RX].notify_handler = - virtio_console_rxq_notify_handler; - vqs[CONSOLE_QUEUE_TX].notify_handler = - virtio_console_txq_notify_handler; - vdev->vqs = vqs; - break; - - case VirtioTGPU: -#ifdef ENABLE_VIRTIO_GPU - vdev->vqs_len = GPU_MAX_QUEUES; - vqs = malloc(sizeof(VirtQueue) * GPU_MAX_QUEUES); - for (int i = 0; i < GPU_MAX_QUEUES; ++i) { - virtqueue_reset(vqs, i); - vqs[i].queue_num_max = VIRTQUEUE_GPU_MAX_SIZE; - vqs[i].dev = vdev; - } - vqs[GPU_CONTROL_QUEUE].notify_handler = virtio_gpu_ctrl_notify_handler; - vqs[GPU_CURSOR_QUEUE].notify_handler = virtio_gpu_cursor_notify_handler; - vdev->vqs = vqs; -#else - log_error("virtio gpu is not enabled"); -#endif - break; + if (ops->num_queues == 0 || ops->num_queues > VIRTIO_MAX_VQUEUES) { + log_error("invalid queue count %u for %s", ops->num_queues, + virtio_device_type_to_string(ops->type)); + return -EINVAL; + } - case VirtioTSCMI: - vdev->vqs_len = SCMI_MAX_QUEUES; - vqs = malloc(sizeof(VirtQueue) * SCMI_MAX_QUEUES); - for (int i = 0; i < SCMI_MAX_QUEUES; ++i) { - virtqueue_reset(vqs, i); - vqs[i].queue_num_max = VIRTQUEUE_SCMI_MAX_SIZE; - vqs[i].dev = vdev; - } - vqs[SCMI_QUEUE_TX].notify_handler = virtio_scmi_txq_notify_handler; - vdev->vqs = vqs; - break; + vdev->vqs_len = ops->num_queues; + VirtQueue *vqs = calloc(ops->num_queues, sizeof(VirtQueue)); + if (!vqs) + return -ENOMEM; - default: - break; + for (uint32_t i = 0; i < ops->num_queues; i++) { + virtqueue_reset(&vqs[i], i); + vqs[i].queue_num_max = ops->queue_max_size; + vqs[i].dev = vdev; + if (ops->notify_handlers[i]) + vqs[i].notify_handler = ops->notify_handlers[i]; } + vdev->vqs = vqs; + return 0; } void init_mmio_regs(VirtMmioRegs *regs, VirtioDeviceType type) { @@ -388,6 +337,9 @@ void virtio_dev_reset(VirtIODevice *vdev) { for (uint32_t i = 0; i < vdev->vqs_len; i++) { virtqueue_reset(&vdev->vqs[i], i); } + const struct virtio_device_ops *ops = lookup_ops(vdev->type); + if (ops && ops->reset) + ops->reset(vdev); vdev->activated = false; } @@ -998,10 +950,15 @@ void virtio_mmio_write(VirtIODevice *vdev, uint64_t offset, uint64_t value, log_debug("****** zone %d %s queue notify begin ******", vdev->zone_id, virtio_device_type_to_string(vdev->type)); - if (value < vdev->vqs_len) { + if (value < vdev->vqs_len && vqs[value].notify_handler) { log_debug("queue notify ready, handler addr is %#x", vqs[value].notify_handler); vqs[value].notify_handler(vdev, &vqs[value]); + } else { + log_warn("zone %d %s: ignoring queue notify, value %" PRIu64 + ", vqs_len %u", + vdev->zone_id, virtio_device_type_to_string(vdev->type), + value, vdev->vqs_len); } log_debug("****** zone %d %s queue notify end ******", vdev->zone_id, @@ -1541,46 +1498,37 @@ int virtio_init() { return -1; } -int create_virtio_device_from_json(cJSON *device_json, int zone_id) { - VirtioDeviceType dev_type = VirtioTNone; - uint64_t base_addr = 0, len = 0; - uint32_t irq_id = 0; - +int create_virtio_device_from_json(const cJSON *device_json, int zone_id) { char *status = SAFE_CJSON_GET_OBJECT_ITEM(device_json, "status")->valuestring; if (strcmp(status, "disable") == 0) return 0; - // Get device type char *type = SAFE_CJSON_GET_OBJECT_ITEM(device_json, "type")->valuestring; - void *arg0 = NULL, *arg1 = NULL; - // Mapping table for device types static const struct { const char *name; VirtioDeviceType type; } device_type_map[] = { {"blk", VirtioTBlock}, {"net", VirtioTNet}, {"console", VirtioTConsole}, {"gpu", VirtioTGPU}, - {"scmi", VirtioTSCMI}, {NULL, VirtioTNone} // Sentinel + {"scmi", VirtioTSCMI}, {NULL, VirtioTNone}, }; - // Find device type in mapping table - dev_type = VirtioTNone; + VirtioDeviceType dev_type = VirtioTNone; for (int i = 0; device_type_map[i].name != NULL; i++) { if (strcmp(type, device_type_map[i].name) == 0) { dev_type = device_type_map[i].type; break; } } - if (dev_type == VirtioTNone) { log_error("unknown device type %s", type); return -1; } - // Get base_addr, len, irq_id (mmio region base address and length, device - // interrupt number) + uint64_t base_addr = 0, len = 0; + uint32_t irq_id = 0; if (parse_json_u64(SAFE_CJSON_GET_OBJECT_ITEM(device_json, "addr"), &base_addr) != 0 || parse_json_u64(SAFE_CJSON_GET_OBJECT_ITEM(device_json, "len"), &len) != @@ -1591,113 +1539,27 @@ int create_virtio_device_from_json(cJSON *device_json, int zone_id) { return -1; } - // Handle other fields according to the device type - if (dev_type == VirtioTBlock) { - // virtio-blk - char *img = SAFE_CJSON_GET_OBJECT_ITEM(device_json, "img")->valuestring; - arg0 = img, arg1 = NULL; - log_info("debug: img is %s", img); - } else if (dev_type == VirtioTNet) { - // virtio-net - char *tap = SAFE_CJSON_GET_OBJECT_ITEM(device_json, "tap")->valuestring; - cJSON *mac_json = SAFE_CJSON_GET_OBJECT_ITEM(device_json, "mac"); - uint8_t mac[6]; - for (int i = 0; i < 6; i++) { - if (parse_json_u8(SAFE_CJSON_GET_ARRAY_ITEM(mac_json, i), - &mac[i]) != 0) { - log_error("failed to parse mac address"); - return -1; - } - } - arg0 = mac, arg1 = tap; - } else if (dev_type == VirtioTConsole) { - // virtio-console - arg0 = arg1 = NULL; - } else if (dev_type == VirtioTGPU) { -// virtio-gpu -#ifdef ENABLE_VIRTIO_GPU - // TODO: Add display device settings - GPURequestedState *requested_state = NULL; - requested_state = - (GPURequestedState *)malloc(sizeof(GPURequestedState)); - memset(requested_state, 0, sizeof(GPURequestedState)); - if (parse_json_u32(SAFE_CJSON_GET_OBJECT_ITEM(device_json, "width"), - &requested_state->width) != 0 || - parse_json_u32(SAFE_CJSON_GET_OBJECT_ITEM(device_json, "height"), - &requested_state->height) != 0) { - log_error("failed to parse gpu width or height"); - free(requested_state); - return -1; - } - arg0 = requested_state; - arg1 = NULL; -#else - log_error( - "virtio-gpu is not enabled, please add VIRTIO_GPU=y in make cmd"); - return -1; -#endif - } else if (dev_type == VirtioTSCMI) { - // virtio-scmi - SCMIDev *scmi_dev = scmi_dev_create(); - if (!scmi_dev) { - log_error("Failed to create SCMI device"); - return -1; - } - - cJSON *clock_ids = SAFE_CJSON_GET_OBJECT_ITEM(device_json, "clock_ids"); - cJSON *reset_ids = SAFE_CJSON_GET_OBJECT_ITEM(device_json, "reset_ids"); - cJSON *power_ids = SAFE_CJSON_GET_OBJECT_ITEM(device_json, "power_ids"); - - if (scmi_dev_parse_clock_ids(scmi_dev, clock_ids) < 0 || - scmi_dev_parse_reset_ids(scmi_dev, reset_ids) < 0 || - scmi_dev_parse_power_ids(scmi_dev, power_ids) < 0) { - scmi_dev_free(scmi_dev); - return -1; - } - - /* Register protocols per-device: BASE always, others only if present */ - scmi_dev_register_protocol(scmi_dev, SCMI_PROTO_ID_BASE, - virtio_scmi_base_handle_req); - if (scmi_dev->clock_count > 0) - scmi_dev_register_protocol(scmi_dev, SCMI_PROTO_ID_CLOCK, - virtio_scmi_clock_handle_req); - if (scmi_dev->power_count > 0) - scmi_dev_register_protocol(scmi_dev, SCMI_PROTO_ID_POWER, - virtio_scmi_power_handle_req); - if (scmi_dev->reset_count > 0) - scmi_dev_register_protocol(scmi_dev, SCMI_PROTO_ID_RESET, - virtio_scmi_reset_handle_req); - - arg0 = scmi_dev; - log_info("SCMI device created: clocks=%u resets=%u powers=%u", - scmi_dev->clock_count, scmi_dev->reset_count, - scmi_dev->power_count); - } - - // Check for missing fields if (base_addr == 0 || len == 0 || irq_id == 0) { log_error("missing arguments"); - if (dev_type == VirtioTSCMI) - scmi_dev_free(arg0); -#ifdef ENABLE_VIRTIO_GPU - if (dev_type == VirtioTGPU) - free(arg0); -#endif return -1; } - // Create virtio_device - if (!create_virtio_device(dev_type, zone_id, base_addr, len, irq_id, arg0, - arg1)) { - if (dev_type == VirtioTSCMI) - scmi_dev_free(arg0); -#ifdef ENABLE_VIRTIO_GPU - if (dev_type == VirtioTGPU) - free(arg0); -#endif + const struct virtio_config_ops *cfg_ops = lookup_config_ops(dev_type); + void *params = NULL; + if (cfg_ops && cfg_ops->parse && + cfg_ops->parse(device_json, ¶ms) != 0) { return -1; } + VirtIODevice *vdev = + create_virtio_device(dev_type, zone_id, base_addr, len, irq_id, params); + + if (cfg_ops && cfg_ops->free) + cfg_ops->free(params); + + if (!vdev) + return -1; + return 0; }