diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index b1b5aa851e4ec..7920973b7b279 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -5921,6 +5921,19 @@ West: labels: - "area: Rust" +"West project: zephyr-xenlib": + status: maintained + maintainers: + - firscity + collaborators: + - lorc + - luca-fancellu + - soburi + files: + - modules/zephyr-xenlib/ + labels: + - "area: Xen Platform" + "West project: zscilib": status: maintained maintainers: diff --git a/arch/arm64/core/xen/enlighten.c b/arch/arm64/core/xen/enlighten.c index 164947a09ffdc..afa76432bdb11 100644 --- a/arch/arm64/core/xen/enlighten.c +++ b/arch/arm64/core/xen/enlighten.c @@ -4,11 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include +#include + #include #include #include -#include -#include #include #include diff --git a/arch/arm64/core/xen/hypercall.S b/arch/arm64/core/xen/hypercall.S index 063538bae9029..a8e6c4737722f 100644 --- a/arch/arm64/core/xen/hypercall.S +++ b/arch/arm64/core/xen/hypercall.S @@ -3,10 +3,11 @@ * Copyright (c) 2021-2023 EPAM Systems */ +#include +#include + #include #include -#include -#include #define HYPERCALL(hypercall) \ GTEXT(HYPERVISOR_##hypercall); \ @@ -23,7 +24,10 @@ HYPERCALL(sched_op); HYPERCALL(event_channel_op); HYPERCALL(hvm_op); HYPERCALL(memory_op); +HYPERCALL(dm_op); +HYPERCALL(xen_version); #ifdef CONFIG_XEN_DOM0 HYPERCALL(domctl); +HYPERCALL(sysctl); #endif diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index ed668b9534230..be3dfd49e430a 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -92,6 +92,7 @@ add_subdirectory_ifdef(CONFIG_STEPPER stepper) add_subdirectory_ifdef(CONFIG_SYSCON syscon) add_subdirectory_ifdef(CONFIG_SYS_CLOCK_EXISTS timer) add_subdirectory_ifdef(CONFIG_TEE tee) +add_subdirectory_ifdef(CONFIG_VHOST vhost) add_subdirectory_ifdef(CONFIG_VIDEO video) add_subdirectory_ifdef(CONFIG_VIRTIO virtio) add_subdirectory_ifdef(CONFIG_VIRTUALIZATION virtualization) diff --git a/drivers/Kconfig b/drivers/Kconfig index dcb941ee1654a..c60f5b4512812 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -91,6 +91,7 @@ source "drivers/syscon/Kconfig" source "drivers/timer/Kconfig" source "drivers/usb/Kconfig" source "drivers/usb_c/Kconfig" +source "drivers/vhost/Kconfig" source "drivers/video/Kconfig" source "drivers/virtio/Kconfig" source "drivers/virtualization/Kconfig" diff --git a/drivers/serial/uart_hvc_xen.c b/drivers/serial/uart_hvc_xen.c index 3283a6ec1e55b..3aff94476f72d 100644 --- a/drivers/serial/uart_hvc_xen.c +++ b/drivers/serial/uart_hvc_xen.c @@ -4,14 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include +#include +#include + #include #include #include #include #include -#include -#include -#include #include #include diff --git a/drivers/serial/uart_hvc_xen_consoleio.c b/drivers/serial/uart_hvc_xen_consoleio.c index 97d5392d750e7..0f5bba9765ebb 100644 --- a/drivers/serial/uart_hvc_xen_consoleio.c +++ b/drivers/serial/uart_hvc_xen_consoleio.c @@ -9,8 +9,9 @@ * should be used (uart_hvc_xen.c), this console will not be available. */ +#include + #include -#include #include #include diff --git a/drivers/vhost/CMakeLists.txt b/drivers/vhost/CMakeLists.txt new file mode 100644 index 0000000000000..72e1a581e2025 --- /dev/null +++ b/drivers/vhost/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_VHOST vringh.c) +zephyr_library_sources_ifdef(CONFIG_VHOST_XEN_MMIO vhost_xen_mmio.c) diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig new file mode 100644 index 0000000000000..1c3acc98b2198 --- /dev/null +++ b/drivers/vhost/Kconfig @@ -0,0 +1,22 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +config VHOST + bool "support for VIRTIO" + help + Enable options for VIRTIO + +if VHOST + +config VHOST_XEN_MMIO + bool "support for MMIO-based VIRTIO backend on Xen hypervisor" + default y + depends on DT_HAS_XEN_VHOST_MMIO_ENABLED + help + Enable VIRTIO-MMIO backend on Xen hypervisor + +endif # VIRTIO + +module = VHOST +module-str = VHOST +source "subsys/logging/Kconfig.template.log_config" diff --git a/drivers/vhost/vhost_xen_mmio.c b/drivers/vhost/vhost_xen_mmio.c new file mode 100644 index 0000000000000..1f483b39a64c7 --- /dev/null +++ b/drivers/vhost/vhost_xen_mmio.c @@ -0,0 +1,1454 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(xen_vhost_mmio); + +#define DT_DRV_COMPAT xen_vhost_mmio + +#define PAGE_SHIFT (__builtin_ctz(CONFIG_MMU_PAGE_SIZE)) +#define XEN_GRANT_ADDR_OFF (1ULL << 63) + +#define VIRTIO_MMIO_MAGIC 0x74726976 +#define VIRTIO_MMIO_SUPPORTED_VERSION 2 + +/* The maximum retry period is 12.8 seconds */ +#define RETRY_DELAY_BASE_MS 50 +#define RETRY_BACKOFF_EXP_MAX 8 + +#define HEX_64BIT_DIGITS 16 + +#define LOG_LVL_Q(lvl, str, ...) \ + UTIL_CAT(LOG_, lvl)("%s[%u]: " str, __func__, queue_id, ##__VA_ARGS__) +#define LOG_ERR_Q(str, ...) LOG_LVL_Q(ERR, str, ##__VA_ARGS__) +#define LOG_WRN_Q(str, ...) LOG_LVL_Q(WRN, str, ##__VA_ARGS__) +#define LOG_INF_Q(str, ...) LOG_LVL_Q(INF, str, ##__VA_ARGS__) +#define LOG_DBG_Q(str, ...) LOG_LVL_Q(DBG, str, ##__VA_ARGS__) + +#define META_PAGES_INDEX(cfg) (cfg->queue_size_max) + +enum virtq_parts { + VIRTQ_DESC = 0, + VIRTQ_AVAIL, + VIRTQ_USED, + NUM_OF_VIRTQ_PARTS, +}; + +struct vhost_xen_mmio_config { + k_thread_stack_t *workq_stack; + size_t workq_stack_size; + int workq_priority; + + uint16_t num_queues; + uint16_t queue_size_max; + uint8_t device_id; + uint32_t vendor_id; + uintptr_t base; + size_t reg_size; + + uint64_t device_features; +}; + +struct mapped_pages { + uint64_t gpa; + uint8_t *buf; + size_t len; + struct gnttab_unmap_grant_ref *ops; + size_t map_count; + size_t pages; +}; + +struct mapped_pages_chunk { + size_t count; + struct mapped_pages *map; + bool releasing; +}; + +struct virtq_callback { + void (*cb)(const struct device *dev, uint16_t queue_id, void *user_data); + void *data; +}; + +struct virtq_context { + /** + * Store pages mapped to descriptors. + * Initializer allocate (queue_size_max + 1) statically, + * The last one is used to hold the desc, avail, and used + * of virtq itself. + */ + struct mapped_pages_chunk *pages_chunks; + struct virtq_callback queue_notify_cb; + atomic_t queue_size; + atomic_t queue_ready_notified; + uint64_t virtq_parts_gpa[NUM_OF_VIRTQ_PARTS]; + struct k_spinlock lock; +}; + +struct vhost_xen_mmio_data { + struct k_work_delayable init_work; + struct k_work_delayable isr_work; + struct k_work_delayable ready_work; + struct k_work_q workq; + const struct device *dev; + atomic_t initialized; + atomic_t retry; + + struct xs_watcher watcher; + evtchn_port_t xs_port; + evtchn_port_t ioserv_port; + struct shared_iopage *shared_iopage; + uint32_t vcpus; + + struct { + ioservid_t servid; + domid_t domid; + uint32_t deviceid; + uint32_t irq; + uintptr_t base; + } fe; + + struct { + uint64_t driver_features; + uint8_t device_features_sel; + uint8_t driver_features_sel; + atomic_t irq_status; + atomic_t status; + atomic_t queue_sel; + } be; + + atomic_t notify_queue_id; /**< Temporary variable to pass to workq */ + struct virtq_callback queue_ready_cb; + struct virtq_context *vq_ctx; +}; + +struct query_param { + const char *key; + const char *expected; +}; + +/** + * Get the nth string from a null-separated string buffer + */ +static const char *nth_str(const char *buf, size_t len, size_t n) +{ + int cnt = 0; + + if (n == 0) { + return buf; + } + + for (size_t i = 0; i < len; i++) { + if (buf[i] == '\0') { + cnt++; + } + + if (cnt == n && (i != (len - 1))) { + return &buf[i + 1]; + } + } + + return NULL; +} + +/** + * Query VIRTIO frontend's domid/deviceid from XenStore + */ +static int query_virtio_backend(const struct query_param *params, size_t param_num, domid_t *domid, + int *deviceid) +{ + char buf[65] = {0}; + const size_t len = ARRAY_SIZE(buf) - 1; + const char *ptr_i, *ptr_j; + int i, j; + + const ssize_t len_i = xs_directory("backend/virtio", buf, len); + + if (len_i < 0) { + return -EIO; + } + if (len_i == 6 && strncmp(buf, "ENOENT", len) == 0) { + return -ENOENT; + } + + for (i = 0, ptr_i = buf; ptr_i; ptr_i = nth_str(buf, len_i, i++)) { + char *endptr; + + *domid = strtol(ptr_i, &endptr, 10); + if (*endptr != '\0') { + continue; + } + + snprintf(buf, len, "backend/virtio/%d", *domid); + + const ssize_t len_j = xs_directory(buf, buf, ARRAY_SIZE(buf)); + + if (len_j < 0 || strncmp(buf, "ENOENT", ARRAY_SIZE(buf)) == 0) { + continue; + } + + for (j = 0, ptr_j = buf; ptr_j; ptr_j = nth_str(buf, len_j, j++)) { + *deviceid = strtol(ptr_j, &endptr, 10); + if (*endptr != '\0') { + continue; /* Skip invalid device ID */ + } + + bool match = true; + + for (size_t k = 0; k < param_num; k++) { + snprintf(buf, len, "backend/virtio/%d/%d/%s", *domid, *deviceid, + params[k].key); + const ssize_t len_k = xs_read(buf, buf, ARRAY_SIZE(buf)); + + if ((len_k < 0) || (strncmp(buf, "ENOENT", ARRAY_SIZE(buf)) == 0) || + (strncmp(params[k].expected, buf, ARRAY_SIZE(buf)) != 0)) { + match = false; + break; + } + } + + if (match) { + return 0; + } + } + } + + return -ENOENT; +} + +static uintptr_t query_irq(domid_t domid, int deviceid) +{ + char buf[65] = {0}; + size_t len = ARRAY_SIZE(buf) - 1; + char *endptr; + + snprintf(buf, len, "backend/virtio/%d/%d/irq", domid, deviceid); + + len = xs_read(buf, buf, ARRAY_SIZE(buf) - 1); + if ((len < 0) || (strncmp(buf, "ENOENT", ARRAY_SIZE(buf)) == 0)) { + return (uintptr_t)-1; + } + + uintptr_t irq_val = strtol(buf, &endptr, 10); + + if (*endptr != '\0') { + return (uintptr_t)-1; + } + + return irq_val; +} + +static int unmap_pages(struct mapped_pages *pages) +{ + int ret = 0; + + if (!pages || !pages->ops) { + return 0; + } + + LOG_DBG("%s: pages=%p unmap=%p count=%zu pages=%zu", __func__, pages, pages->ops, + pages->map_count, pages->pages); + + for (size_t i = 0; i < pages->map_count; i++) { + LOG_DBG("pages: i=%zu status=%d", i, pages->ops[i].status); + if (pages->ops[i].status == GNTST_okay) { + int rc = gnttab_unmap_refs(&pages->ops[i], 1); + + if (rc < 0) { + LOG_ERR("gnttab_unmap_refs failed: %d", rc); + ret = rc; + } + pages->ops[i].status = GNTST_general_error; + } + } + + pages->map_count = 0; + + return ret; +} + +static int free_pages_array(struct mapped_pages *pages, size_t len) +{ + int ret = 0; + + if (!pages) { + return 0; + } + + for (size_t i = 0; i < len; i++) { + int rc = unmap_pages(&pages[i]); + + if (rc < 0) { + LOG_ERR("%s: [%zu] unmap failed: %d", __func__, i, rc); + ret = rc; + } + + if (pages[i].ops) { + k_free(pages[i].ops); + } + + if (pages[i].buf) { + rc = gnttab_put_pages(pages[i].buf, pages[i].pages); + if (rc < 0) { + LOG_ERR("%s: [%zu] gnttab_put_pages failed: %d", __func__, i, rc); + ret = rc; + } + } + } + + return ret; +} + +static inline k_spinlock_key_t wait_for_chunk_ready(struct virtq_context *ctx, + struct mapped_pages_chunk *chunk, + k_spinlock_key_t key) +{ + while (chunk->releasing) { + k_spin_unlock(&ctx->lock, key); + key = k_spin_lock(&ctx->lock); + } + + return key; +} + +static void reset_queue(const struct device *dev, uint16_t queue_id) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + struct virtq_context *vq_ctx = &data->vq_ctx[queue_id]; + + k_spinlock_key_t key = k_spin_lock(&vq_ctx->lock); + + for (size_t i = 0; i <= config->queue_size_max; i++) { + struct mapped_pages_chunk *chunk = &vq_ctx->pages_chunks[i]; + + key = wait_for_chunk_ready(vq_ctx, chunk, key); + + if (chunk->map && chunk->count > 0) { + struct mapped_pages *map = chunk->map; + const size_t count = chunk->count; + + chunk->releasing = true; + chunk->map = NULL; + chunk->count = 0; + k_spin_unlock(&vq_ctx->lock, key); + + free_pages_array(map, count); + k_free(map); + + key = k_spin_lock(&vq_ctx->lock); + chunk->releasing = false; + } else { + chunk->count = 0; + } + } + + vq_ctx->queue_notify_cb.cb = NULL; + vq_ctx->queue_notify_cb.data = NULL; + + k_spin_unlock(&vq_ctx->lock, key); + + atomic_set(&vq_ctx->queue_size, 0); + atomic_set(&vq_ctx->queue_ready_notified, 0); +} + +static void setup_unmap_info(struct mapped_pages *pages, const struct vhost_buf *bufs, + size_t bufs_len, const struct gnttab_map_grant_ref *map_ops) +{ + size_t map_idx = 0; + + for (size_t i = 0; i < bufs_len; i++) { + const size_t num_pages = (bufs[i].len + XEN_PAGE_SIZE - 1) / XEN_PAGE_SIZE; + struct mapped_pages *page_info = &pages[i]; + + for (size_t j = 0; j < num_pages; j++) { + const struct gnttab_map_grant_ref *map = &map_ops[map_idx]; + struct gnttab_unmap_grant_ref *unmap = &page_info->ops[j]; + + unmap->host_addr = map->host_addr; + unmap->dev_bus_addr = map->dev_bus_addr; + unmap->handle = map->handle; + unmap->status = map->status; + map_idx++; + } + + page_info->map_count = num_pages; + LOG_DBG("%s: range[%zu] map_count=%zu num_pages=%zu " + "pages=%zu", + __func__, i, page_info->map_count, num_pages, page_info->pages); + } +} + +static int setup_iovec_mappings(struct mapped_pages *pages, domid_t domid, + const struct vhost_buf *bufs, size_t bufs_len) +{ + size_t total_map_ops = 0; + size_t map_idx = 0; + int ret = 0; + + for (size_t i = 0; i < bufs_len; i++) { + total_map_ops += (bufs[i].len + XEN_PAGE_SIZE - 1) / XEN_PAGE_SIZE; + } + + struct gnttab_map_grant_ref *map_ops = + k_malloc(sizeof(struct gnttab_map_grant_ref) * total_map_ops); + + if (!map_ops) { + LOG_ERR("k_malloc failed for %zu map operations", total_map_ops); + return -ENOMEM; + } + + for (size_t i = 0; i < bufs_len; i++) { + const size_t num_pages = (bufs[i].len + XEN_PAGE_SIZE - 1) / XEN_PAGE_SIZE; + struct mapped_pages *page_info = &pages[i]; + + for (size_t j = 0; j < num_pages; j++) { + const uint64_t page_gpa = bufs[i].gpa + (j * XEN_PAGE_SIZE); + + if (!(page_gpa & XEN_GRANT_ADDR_OFF)) { + LOG_ERR("addr missing grant marker: 0x%" PRIx64, page_gpa); + ret = -EINVAL; + goto free_ops; + } + + map_ops[map_idx].host_addr = + (uintptr_t)page_info->buf + (j * XEN_PAGE_SIZE); + map_ops[map_idx].flags = GNTMAP_host_map; + map_ops[map_idx].ref = (page_gpa & ~XEN_GRANT_ADDR_OFF) >> XEN_PAGE_SHIFT; + map_ops[map_idx].dom = domid; + + map_idx++; + } + } + + ret = gnttab_map_refs(map_ops, total_map_ops); + if (ret < 0) { + LOG_ERR("gnttab_map_refs failed: %d", ret); + goto free_ops; + } + + /* Check mapping results */ + map_idx = 0; + for (size_t i = 0; i < bufs_len; i++) { + const size_t num_pages = (bufs[i].len + XEN_PAGE_SIZE - 1) / XEN_PAGE_SIZE; + + for (size_t j = 0; j < num_pages; j++) { + const struct gnttab_map_grant_ref *op = &map_ops[map_idx]; + + if (op->status != GNTST_okay) { + LOG_ERR("Mapping failed for range %zu page %zu: status=%d", i, j, + op->status); + ret = -EIO; + goto unmap; + } + map_idx++; + } + } + + ret = 0; + +unmap: + setup_unmap_info(pages, bufs, bufs_len, map_ops); + + if (ret < 0) { + unmap_pages(pages); + } + +free_ops: + if (map_ops) { + k_free(map_ops); + } + + return ret; +} + +static int init_pages_chunks(const struct device *dev, uint16_t queue_id, uint16_t head, + const struct vhost_buf *bufs, size_t bufs_len, + struct vhost_iovec *r_iovecs, size_t r_iovecs_len, + struct vhost_iovec *w_iovecs, size_t w_iovecs_len, size_t *r_count_out, + size_t *w_count_out, size_t total_pages) +{ + struct vhost_xen_mmio_data *data = dev->data; + struct virtq_context *vq_ctx = &data->vq_ctx[queue_id]; + struct mapped_pages_chunk *chunk = &vq_ctx->pages_chunks[head]; + size_t r_iovecs_count = 0, w_iovecs_count = 0; + int ret; + + LOG_DBG_Q("%zu bufs, %zu total pages", bufs_len, total_pages); + + /* Reallocate page chunk structure */ + if (!chunk->map || chunk->count < bufs_len) { + if (chunk->map) { + free_pages_array(chunk->map, chunk->count); + k_free(chunk->map); + } + chunk->map = k_malloc(sizeof(struct mapped_pages) * bufs_len); + if (!chunk->map) { + return -ENOMEM; + } + memset(chunk->map, 0, sizeof(struct mapped_pages) * bufs_len); + chunk->count = bufs_len; + LOG_DBG_Q("Allocated chunk at %p, count=%zu", chunk->map, chunk->count); + } + + for (size_t i = 0; i < bufs_len; i++) { + const size_t num_pages = (bufs[i].len + XEN_PAGE_SIZE - 1) / XEN_PAGE_SIZE; + struct mapped_pages *page_info = &chunk->map[i]; + + /* Allocate or reuse buffer for this range */ + if (!page_info->buf || page_info->pages < num_pages) { + free_pages_array(page_info, 1); + memset(page_info, 0, sizeof(struct mapped_pages)); + + barrier_dmem_fence_full(); + + page_info->len = num_pages * XEN_PAGE_SIZE; + page_info->gpa = bufs[i].gpa; + page_info->buf = gnttab_get_pages(num_pages); + page_info->ops = + k_malloc(sizeof(struct gnttab_unmap_grant_ref) * num_pages); + page_info->pages = num_pages; + page_info->map_count = 0; + + LOG_DBG_Q("range[%zu] allocated pages=%zu num_pages=%zu buf=%p unmap=%p", i, + page_info->pages, num_pages, page_info->buf, page_info->ops); + + if (!page_info->buf || !page_info->ops) { + LOG_ERR_Q("Failed to allocate for range[%zu]: buf=%p unmap=%p", i, + page_info->buf, page_info->ops); + return -ENOMEM; + } + + for (size_t j = 0; j < num_pages; j++) { + page_info->ops[j].status = GNTST_general_error; + } + } + } + + ret = setup_iovec_mappings(chunk->map, data->fe.domid, bufs, bufs_len); + if (ret < 0) { + return ret; + } + + k_spinlock_key_t key = k_spin_lock(&vq_ctx->lock); + + key = wait_for_chunk_ready(vq_ctx, chunk, key); + + for (size_t i = 0; i < bufs_len; i++) { + const bool is_write = bufs[i].is_write; + const size_t iovecs_len = is_write ? w_iovecs_len : r_iovecs_len; + const size_t current_count = is_write ? w_iovecs_count : r_iovecs_count; + struct vhost_iovec *iovec = + is_write ? &w_iovecs[w_iovecs_count] : &r_iovecs[r_iovecs_count]; + + if (current_count >= iovecs_len) { + LOG_ERR_Q("no more %s iovecs: %zu >= %zu", is_write ? "write" : "read", + current_count, iovecs_len); + k_spin_unlock(&vq_ctx->lock, key); + ret = -E2BIG; + break; + } + + const size_t page_offset = bufs[i].gpa & (XEN_PAGE_SIZE - 1); + const void *base_buf = chunk->map[i].buf; + const void *va = (void *)(((uintptr_t)base_buf) + page_offset); + + iovec->iov_base = (void *)va; + iovec->iov_len = bufs[i].len; + + if (is_write) { + w_iovecs_count++; + } else { + r_iovecs_count++; + } + } + + if (ret != -E2BIG) { + k_spin_unlock(&vq_ctx->lock, key); + } + + if (ret < 0) { + LOG_ERR_Q("chunk[%u] initialization failed: %d", head, ret); + } else { + *r_count_out = r_iovecs_count; + *w_count_out = w_iovecs_count; + LOG_DBG_Q("chunk[%u] init succeed bufs=%zu", head, bufs_len); + } + + return ret; +} + +/** + * @brief Set up a VirtIO queue for operation + * + * Maps the required grant pages for VirtIO ring structures (descriptor, + * available, and used rings) based on the current queue size. Uses + * metachain (pages[queue_size_max]) to store the meta pages. + * + * @param dev VHost device instance + * @param queue_id ID of the queue to set up + * @return 0 on success, negative error code on failure + */ +static int setup_queue(const struct device *dev, uint16_t queue_id) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + struct virtq_context *vq_ctx = &data->vq_ctx[queue_id]; + const size_t queue_size = atomic_get(&data->vq_ctx[queue_id].queue_size); + const size_t num_pages[] = {DIV_ROUND_UP(16 * queue_size, XEN_PAGE_SIZE), + DIV_ROUND_UP(2 * queue_size + 6, XEN_PAGE_SIZE), + DIV_ROUND_UP(8 * queue_size + 6, XEN_PAGE_SIZE)}; + int ret = 0; + + struct vhost_buf meta_bufs[NUM_OF_VIRTQ_PARTS]; + struct vhost_iovec dummy_iovecs[NUM_OF_VIRTQ_PARTS]; + size_t dummy_read_count, dummy_write_count; + size_t total_pages = 0; + + for (size_t i = 0; i < NUM_OF_VIRTQ_PARTS; i++) { + meta_bufs[i].gpa = vq_ctx->virtq_parts_gpa[i]; + meta_bufs[i].len = num_pages[i] * XEN_PAGE_SIZE; + meta_bufs[i].is_write = true; + total_pages += num_pages[i]; + + LOG_DBG_Q("Meta range[%zu]: gpa=0x%" PRIx64 " len=%zu pages=%zu", i, + meta_bufs[i].gpa, meta_bufs[i].len, num_pages[i]); + } + + ret = init_pages_chunks(dev, queue_id, META_PAGES_INDEX(config), meta_bufs, + NUM_OF_VIRTQ_PARTS, dummy_iovecs, + 0, /* No read iovecs needed for meta pages */ + dummy_iovecs, NUM_OF_VIRTQ_PARTS, /* Write iovecs for meta pages */ + &dummy_read_count, &dummy_write_count, total_pages); + + if (ret < 0) { + LOG_ERR_Q("init_pages_chunks failed: %d", ret); + return ret; + } + + k_spinlock_key_t key = k_spin_lock(&vq_ctx->lock); + + for (size_t i = 0; i < config->queue_size_max; i++) { + struct mapped_pages_chunk *chunk = &vq_ctx->pages_chunks[i]; + + key = wait_for_chunk_ready(vq_ctx, chunk, key); + + chunk->map = NULL; + chunk->count = 0; + } + + k_spin_unlock(&vq_ctx->lock, key); + + return 0; +} + +static void reset_device(const struct device *dev) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + + data->be.driver_features = 0; + data->be.device_features_sel = 0; + data->be.driver_features_sel = 0; + atomic_set(&data->be.irq_status, 0); + atomic_set(&data->be.status, 0); + atomic_set(&data->be.queue_sel, 0); + + for (size_t i = 0; i < config->num_queues; i++) { + reset_queue(dev, i); + } +} + +static void ioreq_server_read_req(const struct device *dev, struct ioreq *r) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + const size_t addr_offset = r->addr - data->fe.base; + + LOG_DBG("count %u: size: %u vp_eport: %u state: %d df: %d type: %d", r->count, r->size, + r->vp_eport, r->state, r->df, r->type); + + switch (addr_offset) { + case VIRTIO_MMIO_MAGIC_VALUE: { + r->data = VIRTIO_MMIO_MAGIC; + } break; + case VIRTIO_MMIO_VERSION: { + r->data = VIRTIO_MMIO_SUPPORTED_VERSION; + } break; + case VIRTIO_MMIO_DEVICE_ID: { + r->data = config->device_id; + } break; + case VIRTIO_MMIO_VENDOR_ID: { + r->data = config->vendor_id; + } break; + case VIRTIO_MMIO_DEVICE_FEATURES: { + if (data->be.device_features_sel == 0) { + r->data = (config->device_features & UINT32_MAX); + } else if (data->be.device_features_sel == 1) { + r->data = (config->device_features >> 32); + } else { + r->data = 0; + } + } break; + case VIRTIO_MMIO_DRIVER_FEATURES: { + if (data->be.driver_features_sel == 0) { + r->data = (data->be.driver_features & UINT32_MAX); + } else if (data->be.driver_features_sel == 1) { + r->data = (data->be.driver_features >> 32); + } else { + r->data = 0; + } + } break; + case VIRTIO_MMIO_QUEUE_SIZE_MAX: { + r->data = config->queue_size_max; + } break; + case VIRTIO_MMIO_STATUS: { + r->data = atomic_get(&data->be.status); + } break; + case VIRTIO_MMIO_INTERRUPT_STATUS: { + r->data = atomic_clear(&data->be.irq_status); + } break; + case VIRTIO_MMIO_QUEUE_READY: { + r->data = vhost_queue_ready(dev, atomic_get(&data->be.queue_sel)); + } break; + default: { + r->data = -1; + } break; + } + + LOG_DBG("r/%zx %" PRIx64, addr_offset, r->data); +} + +static void ioreq_server_write_req(const struct device *dev, struct ioreq *r) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + const size_t addr_offset = r->addr - data->fe.base; + + LOG_DBG("w/%zx %" PRIx64, addr_offset, r->data); + + switch (addr_offset) { + case VIRTIO_MMIO_DEVICE_FEATURES_SEL: { + if (r->data == 0 || r->data == 1) { + data->be.device_features_sel = (uint8_t)r->data; + } + } break; + case VIRTIO_MMIO_DRIVER_FEATURES_SEL: { + if (r->data == 0 || r->data == 1) { + data->be.driver_features_sel = (uint8_t)r->data; + } + } break; + case VIRTIO_MMIO_DRIVER_FEATURES: { + if (data->be.driver_features_sel == 0) { + uint64_t *drvfeats = &data->be.driver_features; + + *drvfeats = (r->data | (*drvfeats & 0xFFFFFFFF00000000)); + } else { + uint64_t *drvfeats = &data->be.driver_features; + + *drvfeats = ((r->data << 32) | (*drvfeats & UINT32_MAX)); + } + } break; + case VIRTIO_MMIO_INTERRUPT_ACK: { + if (r->data) { + atomic_and(&data->be.irq_status, ~r->data); + } + } break; + case VIRTIO_MMIO_STATUS: { + if (r->data & BIT(DEVICE_STATUS_FEATURES_OK)) { + const bool ok = !(data->be.driver_features & ~config->device_features); + + if (ok) { + atomic_or(&data->be.status, BIT(DEVICE_STATUS_FEATURES_OK)); + } else { + LOG_ERR("%" PRIx64 " d driver_feats=%" PRIx64 + " device_feats=%" PRIx64, + r->data, data->be.driver_features, config->device_features); + atomic_or(&data->be.status, BIT(DEVICE_STATUS_FAILED)); + atomic_and(&data->be.status, ~BIT(DEVICE_STATUS_FEATURES_OK)); + } + } else if (r->data == 0) { + reset_device(dev); + } else { + atomic_or(&data->be.status, r->data); + } + } break; + case VIRTIO_MMIO_QUEUE_DESC_LOW: + case VIRTIO_MMIO_QUEUE_DESC_HIGH: + case VIRTIO_MMIO_QUEUE_AVAIL_LOW: + case VIRTIO_MMIO_QUEUE_AVAIL_HIGH: + case VIRTIO_MMIO_QUEUE_USED_LOW: + case VIRTIO_MMIO_QUEUE_USED_HIGH: { + const uint16_t queue_id = atomic_get(&data->be.queue_sel); + + if (queue_id < config->num_queues) { + const size_t part = (addr_offset - VIRTIO_MMIO_QUEUE_DESC_LOW) / 0x10; + const bool hi = !!((addr_offset - VIRTIO_MMIO_QUEUE_DESC_LOW) % 0x10); + uint64_t *p_gpa = &data->vq_ctx[queue_id].virtq_parts_gpa[part]; + + *p_gpa = hi ? ((r->data << 32) | (*p_gpa & UINT32_MAX)) + : (r->data | (*p_gpa & 0xFFFFFFFF00000000)); + } + } break; + case VIRTIO_MMIO_QUEUE_NOTIFY: { + if (r->data < config->num_queues) { + atomic_set(&data->notify_queue_id, r->data); + k_work_schedule_for_queue(&data->workq, &data->isr_work, K_NO_WAIT); + } + } break; + case VIRTIO_MMIO_QUEUE_SIZE: { + const uint16_t queue_sel = atomic_get(&data->be.queue_sel); + + if (queue_sel < config->num_queues) { + const bool is_pow2 = (POPCOUNT((int)r->data) == 1); + + if (r->data > 0 && r->data <= config->queue_size_max && is_pow2) { + atomic_set(&data->vq_ctx[queue_sel].queue_size, r->data); + } else { + LOG_ERR("queue_size should be 2^n and <%u: size=%" PRIu64, + config->queue_size_max, r->data); + atomic_or(&data->be.status, BIT(DEVICE_STATUS_FAILED)); + } + } + } break; + case VIRTIO_MMIO_QUEUE_READY: { + const uint16_t queue_sel = atomic_get(&data->be.queue_sel); + const uint16_t queue_id = queue_sel; + + if (r->data == 0) { + reset_queue(dev, queue_id); + } else if (r->data && (queue_sel < config->num_queues)) { + int err = setup_queue(dev, queue_id); + + if (err < 0) { + atomic_or(&data->be.status, BIT(DEVICE_STATUS_FAILED)); + LOG_ERR("queue%u setup failed: %d", queue_sel, err); + } else if (data->queue_ready_cb.cb) { + data->queue_ready_cb.cb(dev, queue_id, data->queue_ready_cb.data); + } + } + } break; + case VIRTIO_MMIO_QUEUE_SEL: { + atomic_set(&data->be.queue_sel, r->data); + } break; + default: + break; + } +} + +static void ioreq_server_cb(void *ptr) +{ + const struct device *dev = ptr; + struct vhost_xen_mmio_data *data = dev->data; + struct ioreq *r = &data->shared_iopage->vcpu_ioreq[0]; + + if (r->state == STATE_IOREQ_READY) { + if (r->dir == IOREQ_WRITE) { + ioreq_server_write_req(dev, r); + } else { + ioreq_server_read_req(dev, r); + } + + r->state = STATE_IORESP_READY; + + barrier_dmem_fence_full(); + notify_evtchn(data->ioserv_port); + } +} + +static void bind_interdomain_nop(void *priv) +{ +} + +static void xs_notify_handler(const char *path, const char *token, void *param) +{ + const struct device *dev = param; + struct vhost_xen_mmio_data *data = dev->data; + + if (!atomic_get(&data->initialized) && !k_work_delayable_is_pending(&data->init_work)) { + k_work_schedule_for_queue(&data->workq, &data->init_work, K_NO_WAIT); + } +} + +static void isr_workhandler(struct k_work *work) +{ + const struct k_work_delayable *delayable = k_work_delayable_from_work(work); + struct vhost_xen_mmio_data *data = + CONTAINER_OF(delayable, struct vhost_xen_mmio_data, isr_work); + const struct device *dev = data->dev; + const struct vhost_xen_mmio_config *config = dev->config; + + const uint16_t queue_id = atomic_get(&data->notify_queue_id); + const struct virtq_context *vq_ctx = + (queue_id < config->num_queues) ? &data->vq_ctx[queue_id] : NULL; + + if (vq_ctx && vq_ctx->queue_notify_cb.cb) { + vq_ctx->queue_notify_cb.cb(dev, queue_id, vq_ctx->queue_notify_cb.data); + } +} + +static void ready_workhandler(struct k_work *work) +{ + const struct k_work_delayable *delayable = k_work_delayable_from_work(work); + struct vhost_xen_mmio_data *data = + CONTAINER_OF(delayable, struct vhost_xen_mmio_data, ready_work); + const struct device *dev = data->dev; + const struct vhost_xen_mmio_config *config = dev->config; + + for (size_t i = 0; i < config->num_queues; i++) { + bool queue_ready_notified = atomic_get(&data->vq_ctx[i].queue_ready_notified); + + if (vhost_queue_ready(dev, i) && data->queue_ready_cb.cb && !queue_ready_notified) { + data->queue_ready_cb.cb(dev, i, data->queue_ready_cb.data); + atomic_set(&data->vq_ctx[i].queue_ready_notified, 1); + } + } +} + +static void init_workhandler(struct k_work *work) +{ + struct k_work_delayable *delayable = k_work_delayable_from_work(work); + struct vhost_xen_mmio_data *data = + CONTAINER_OF(delayable, struct vhost_xen_mmio_data, init_work); + const struct device *dev = data->dev; + const struct vhost_xen_mmio_config *config = dev->config; + char baseaddr[HEX_64BIT_DIGITS + 3]; /* add rooms for "0x" and '\0' */ + uint32_t n_frms = 1; + xen_pfn_t gfn = 0; + mm_reg_t va; + int ret; + + if (atomic_get(&data->initialized)) { + return; + } + + /* + * Using the settings obtained from xenstore can only be checked for + * matching the base value. + * This means that if multiple FEs try to connect to a BE using the same base address, + * they cannot be matched correctly. + */ + + snprintf(baseaddr, sizeof(baseaddr), "0x%lx", config->base); + + struct query_param params[] = {{ + .key = "base", + .expected = baseaddr, + }}; + + ret = query_virtio_backend(params, ARRAY_SIZE(params), &data->fe.domid, &data->fe.deviceid); + if (ret < 0) { + LOG_INF("%s: failed %d", __func__, ret); + goto retry; + } + + data->fe.base = config->base; + data->fe.irq = query_irq(data->fe.domid, data->fe.deviceid); + if (data->fe.irq == -1) { + ret = -EINVAL; + goto retry; + } + + LOG_DBG("%u %u %lu", data->fe.domid, data->fe.irq, data->fe.base); + + ret = dmop_nr_vcpus(data->fe.domid); + if (ret < 0) { + LOG_ERR("dmop_nr_vcpus err=%d", ret); + goto retry; + } + data->vcpus = ret; + + ret = dmop_create_ioreq_server(data->fe.domid, HVM_IOREQSRV_BUFIOREQ_OFF, &data->fe.servid); + if (ret < 0) { + LOG_ERR("dmop_create_ioreq_server err=%d", ret); + goto retry; + } + + ret = dmop_map_io_range_to_ioreq_server(data->fe.domid, data->fe.servid, 1, data->fe.base, + data->fe.base + config->reg_size - 1); + if (ret < 0) { + LOG_ERR("dmop_map_io_range_to_ioreq_server err=%d", ret); + goto retry; + } + + ret = xendom_acquire_resource(data->fe.domid, XENMEM_resource_ioreq_server, data->fe.servid, + XENMEM_resource_ioreq_server_frame_ioreq(0), &n_frms, &gfn); + if (ret < 0) { + LOG_ERR("xendom_acquire_resource err=%d", ret); + goto retry; + } + + device_map(&va, (gfn << XEN_PAGE_SHIFT), (n_frms << XEN_PAGE_SHIFT), K_MEM_CACHE_NONE); + data->shared_iopage = (void *)va; + + ret = dmop_set_ioreq_server_state(data->fe.domid, data->fe.servid, 1); + if (ret) { + LOG_ERR("dmop_set_ioreq_server_state err=%d", ret); + goto retry; + } + + LOG_DBG("bind_interdomain dom=%d remote_port=%d", data->fe.domid, + data->shared_iopage->vcpu_ioreq[0].vp_eport); + + /* Assume that all interrupts are accepted by cpu0. */ + ret = bind_interdomain_event_channel(data->fe.domid, + data->shared_iopage->vcpu_ioreq[0].vp_eport, + bind_interdomain_nop, NULL); + if (ret < 0) { + LOG_ERR("EVTCHNOP_bind_interdomain[0] err=%d", ret); + goto retry; + } + data->ioserv_port = ret; + + bind_event_channel(data->ioserv_port, ioreq_server_cb, (void *)dev); + unmask_event_channel(data->ioserv_port); + + LOG_INF("%s: backend ready base=%zx fe.domid=%d irq=%d vcpus=%d shared_iopage=%p " + "ioserv_port=%d", + dev->name, data->fe.base, data->fe.domid, data->fe.irq, data->vcpus, + data->shared_iopage, data->ioserv_port); + + atomic_set(&data->initialized, 1); + + ret = 0; + +retry: + if (ret < 0) { + const uint32_t retry_count = MIN(RETRY_BACKOFF_EXP_MAX, atomic_inc(&data->retry)); + + reset_device(dev); + k_work_schedule_for_queue(&data->workq, &data->init_work, + K_MSEC(RETRY_DELAY_BASE_MS * (1 << retry_count))); + } + + LOG_INF("exit work_inithandler: %d", ret); +} + +static bool vhost_xen_mmio_virtq_is_ready(const struct device *dev, uint16_t queue_id) +{ + const struct vhost_xen_mmio_config *config = dev->config; + const struct vhost_xen_mmio_data *data = dev->data; + const struct virtq_context *vq_ctx = &data->vq_ctx[queue_id]; + const size_t queue_size = atomic_get(&vq_ctx->queue_size); + + if (queue_id >= config->num_queues) { + LOG_ERR_Q("Invalid queue ID"); + return false; + } + + if (queue_size == 0) { + return false; + } + + struct mapped_pages_chunk *meta = &vq_ctx->pages_chunks[META_PAGES_INDEX(config)]; + + if (!meta->map || meta->count != NUM_OF_VIRTQ_PARTS) { + return false; + } + + for (size_t i = 0; i < meta->count; i++) { + if (meta->map[i].buf == NULL) { + return false; + } + + for (size_t j = 0; j < meta->map[i].map_count; j++) { + if (meta->map[i].ops[j].status != GNTST_okay) { + return false; + } + } + } + + return vq_ctx->virtq_parts_gpa[VIRTQ_DESC] != 0 && + vq_ctx->virtq_parts_gpa[VIRTQ_AVAIL] != 0 && + vq_ctx->virtq_parts_gpa[VIRTQ_USED] != 0; +} + +static int vhost_xen_mmio_get_virtq(const struct device *dev, uint16_t queue_id, void **parts, + size_t *queue_size) +{ + const struct vhost_xen_mmio_config *config = dev->config; + const struct vhost_xen_mmio_data *data = dev->data; + const struct virtq_context *vq_ctx = &data->vq_ctx[queue_id]; + + if (queue_id >= config->num_queues) { + LOG_ERR_Q("Invalid queue ID"); + return -EINVAL; + } + + if (!vhost_xen_mmio_virtq_is_ready(dev, queue_id)) { + LOG_ERR_Q("not ready"); + return -ENODEV; + } + + struct mapped_pages_chunk *meta = &vq_ctx->pages_chunks[META_PAGES_INDEX(config)]; + + if (!meta->map || meta->count != NUM_OF_VIRTQ_PARTS) { + return -EINVAL; + } + + for (size_t i = 0; i < meta->count; i++) { + parts[i] = meta->map[i].buf + (vq_ctx->virtq_parts_gpa[i] & (XEN_PAGE_SIZE - 1)); + } + + if (!parts[VIRTQ_DESC] || !parts[VIRTQ_AVAIL] || !parts[VIRTQ_USED]) { + LOG_ERR_Q("failed to get ring base addresses"); + return -EINVAL; + } + + *queue_size = atomic_get(&vq_ctx->queue_size); + + LOG_DBG_Q("rings desc=%p, avail=%p, used=%p, size=%zu", parts[VIRTQ_DESC], + parts[VIRTQ_AVAIL], parts[VIRTQ_USED], *queue_size); + + return 0; +} + +static int vhost_xen_mmio_get_driver_features(const struct device *dev, uint64_t *drv_feats) +{ + const struct vhost_xen_mmio_data *data = dev->data; + + *drv_feats = data->be.driver_features; + + return 0; +} + +static int vhost_xen_mmio_notify_virtq(const struct device *dev, uint16_t queue_id) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + + if (queue_id >= config->num_queues) { + LOG_ERR_Q("Invalid queue ID"); + return -EINVAL; + } + + atomic_or(&data->be.irq_status, VIRTIO_QUEUE_INTERRUPT); + + dmop_set_irq_level(data->fe.domid, data->fe.irq, 1); + dmop_set_irq_level(data->fe.domid, data->fe.irq, 0); + + return 0; +} + +static int vhost_xen_mmio_set_device_status(const struct device *dev, uint32_t status) +{ + struct vhost_xen_mmio_data *data = dev->data; + + if (!data) { + return -EINVAL; + } + + atomic_or(&data->be.status, status); + atomic_or(&data->be.irq_status, VIRTIO_DEVICE_CONFIGURATION_INTERRUPT); + + dmop_set_irq_level(data->fe.domid, data->fe.irq, 1); + dmop_set_irq_level(data->fe.domid, data->fe.irq, 0); + + return 0; +} + +static int vhost_xen_mmio_release_iovec(const struct device *dev, uint16_t queue_id, uint16_t head) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + struct virtq_context *vq_ctx = &data->vq_ctx[queue_id]; + const size_t queue_size = atomic_get(&vq_ctx->queue_size); + int ret = 0; + + k_spinlock_key_t key = k_spin_lock(&vq_ctx->lock); + + if (queue_id >= config->num_queues) { + LOG_ERR_Q("Invalid queue ID"); + k_spin_unlock(&vq_ctx->lock, key); + return -EINVAL; + } + + if (head >= queue_size) { + LOG_ERR_Q("Invalid head: head=%u >= queue_size=%zu", head, queue_size); + k_spin_unlock(&vq_ctx->lock, key); + return -EINVAL; + } + + struct mapped_pages_chunk *chunk = &vq_ctx->pages_chunks[head]; + + if (!chunk->map || chunk->count == 0) { + LOG_ERR_Q("Head not in use: head=%u", head); + + k_spin_unlock(&vq_ctx->lock, key); + return -EINVAL; + } + + key = wait_for_chunk_ready(vq_ctx, chunk, key); + + chunk->releasing = true; + + k_spin_unlock(&vq_ctx->lock, key); + + for (size_t i = 0; i < chunk->count; i++) { + int rc = unmap_pages(&chunk->map[i]); + + if (rc < 0) { + LOG_ERR_Q("gnttab_unmap_refs failed for page %zu: %d", i, rc); + ret = rc; + } + } + + key = k_spin_lock(&vq_ctx->lock); + + for (size_t i = 0; i < chunk->count; i++) { + chunk->map[i].map_count = 0; + } + + chunk->releasing = false; + k_spin_unlock(&vq_ctx->lock, key); + + return ret; +} + +static int vhost_xen_mmio_prepare_iovec(const struct device *dev, uint16_t queue_id, uint16_t head, + const struct vhost_buf *bufs, size_t bufs_count, + struct vhost_iovec *r_iovecs, size_t r_iovecs_count, + struct vhost_iovec *w_iovecs, size_t w_iovecs_count, + size_t *read_count, size_t *write_count) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + struct virtq_context *vq_ctx = &data->vq_ctx[queue_id]; + const size_t queue_size = atomic_get(&vq_ctx->queue_size); + size_t total_pages = 0; + int ret = 0; + + if (queue_id >= config->num_queues) { + LOG_ERR_Q("Invalid queue ID"); + ret = -EINVAL; + goto end; + } + + if (head >= queue_size) { + LOG_ERR_Q("Invalid head: head=%u >= queue_size=%zu", head, queue_size); + ret = -EINVAL; + goto end; + } + + for (size_t i = 0; i < bufs_count; i++) { + const uint64_t start_page = bufs[i].gpa >> XEN_PAGE_SHIFT; + const uint64_t end_page = (bufs[i].gpa + bufs[i].len - 1) >> XEN_PAGE_SHIFT; + + if (!(bufs[i].gpa & XEN_GRANT_ADDR_OFF)) { + LOG_ERR_Q("addr missing grant marker: 0x%" PRIx64, bufs[i].gpa); + ret = -EINVAL; + goto end; + } + + total_pages += end_page - start_page + 1; + } + + if (total_pages == 0) { + *read_count = 0; + *write_count = 0; + + return 0; + } + + LOG_DBG_Q("bufs_count=%zu total_pages=%zu max_read=%zu max_write=%zu", bufs_count, + total_pages, r_iovecs_count, w_iovecs_count); + + struct mapped_pages_chunk *chunk = &vq_ctx->pages_chunks[head]; + + if (chunk->map && chunk->count > 0 && chunk->map[0].map_count > 0) { + LOG_WRN("Found unreleased head: %d", head); + + ret = vhost_xen_mmio_release_iovec(dev, queue_id, head); + if (ret < 0) { + LOG_ERR_Q("vhost_xen_mmio_release_iovec: failed %d", ret); + goto end; + } + } + + ret = init_pages_chunks(dev, queue_id, head, bufs, bufs_count, r_iovecs, r_iovecs_count, + w_iovecs, w_iovecs_count, read_count, write_count, total_pages); + +end: + if (ret < 0) { + *read_count = 0; + *write_count = 0; + } + + return ret; +} + +static int vhost_xen_mmio_register_virtq_ready_cb(const struct device *dev, + void (*callback)(const struct device *dev, + uint16_t queue_id, + void *user_data), + void *user_data) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + + data->queue_ready_cb.cb = callback; + data->queue_ready_cb.data = user_data; + + for (size_t i = 0; i < config->num_queues; i++) { + atomic_set(&data->vq_ctx[i].queue_ready_notified, 0); + } + + k_work_schedule_for_queue(&data->workq, &data->ready_work, K_NO_WAIT); + + return 0; +} + +static int vhost_xen_mmio_register_virtq_notify_cb(const struct device *dev, uint16_t queue_id, + void (*callback)(const struct device *dev, + uint16_t queue_id, + void *user_data), + void *user_data) +{ + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + struct virtq_context *vq_ctx = &data->vq_ctx[queue_id]; + + if (queue_id >= config->num_queues) { + LOG_ERR_Q("Invalid queue ID"); + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&vq_ctx->lock); + + vq_ctx->queue_notify_cb.cb = callback; + vq_ctx->queue_notify_cb.data = user_data; + + k_spin_unlock(&vq_ctx->lock, key); + + return 0; +} + +static const struct vhost_controller_api vhost_driver_xen_mmio_api = { + .virtq_is_ready = vhost_xen_mmio_virtq_is_ready, + .get_virtq = vhost_xen_mmio_get_virtq, + .get_driver_features = vhost_xen_mmio_get_driver_features, + .set_device_status = vhost_xen_mmio_set_device_status, + .notify_virtq = vhost_xen_mmio_notify_virtq, + .prepare_iovec = vhost_xen_mmio_prepare_iovec, + .release_iovec = vhost_xen_mmio_release_iovec, + .register_virtq_ready_cb = vhost_xen_mmio_register_virtq_ready_cb, + .register_virtq_notify_cb = vhost_xen_mmio_register_virtq_notify_cb, +}; + +static int vhost_xen_mmio_init(const struct device *dev) +{ + const struct k_work_queue_config qcfg = {.name = "vhost-mmio-wq"}; + const struct vhost_xen_mmio_config *config = dev->config; + struct vhost_xen_mmio_data *data = dev->data; + char buf[65] = {0}; + int ret; + + data->dev = dev; + + atomic_set(&data->initialized, 0); + atomic_set(&data->retry, 0); + atomic_set(&data->be.status, 0); + atomic_set(&data->be.queue_sel, 0); + atomic_set(&data->be.irq_status, 0); + atomic_set(&data->notify_queue_id, 0); + xen_events_init(); + + ret = xs_init(); + + if (ret < 0) { + LOG_INF("xs_init_xenstore failed: %d", ret); + return ret; + } + + k_work_init_delayable(&data->init_work, init_workhandler); + k_work_init_delayable(&data->isr_work, isr_workhandler); + k_work_init_delayable(&data->ready_work, ready_workhandler); + k_work_queue_init(&data->workq); + k_work_queue_start(&data->workq, config->workq_stack, config->workq_stack_size, + config->workq_priority, &qcfg); + + xs_watcher_init(&data->watcher, xs_notify_handler, (void *)dev); + xs_watcher_register(&data->watcher); + + ret = xs_watch("backend/virtio", dev->name, buf, ARRAY_SIZE(buf) - 1); + + if (ret < 0) { + LOG_INF("xs_watch failed: %d", ret); + return ret; + } + + return 0; +} + +#define VQCTX_INIT(n, idx) \ + { \ + .pages_chunks = vhost_xen_mmio_pages_chunks_##idx[n], \ + } + +#define Q_NUM(idx) DT_INST_PROP_OR(idx, num_queues, 1) +#define Q_SZ_MAX(idx) DT_INST_PROP_OR(idx, queue_size_max, 1) + +#define VHOST_XEN_MMIO_INST(idx) \ + static K_THREAD_STACK_DEFINE(workq_stack_##idx, DT_INST_PROP_OR(idx, stack_size, 4096)); \ + static const struct vhost_xen_mmio_config vhost_xen_mmio_config_##idx = { \ + .queue_size_max = DT_INST_PROP_OR(idx, queue_size_max, 1), \ + .num_queues = DT_INST_PROP_OR(idx, num_queues, 1), \ + .device_id = DT_INST_PROP(idx, device_id), \ + .vendor_id = DT_INST_PROP_OR(idx, vendor_id, 0), \ + .base = DT_INST_PROP(idx, base), \ + .reg_size = XEN_PAGE_SIZE, \ + .workq_stack = (k_thread_stack_t *)&workq_stack_##idx, \ + .workq_stack_size = K_THREAD_STACK_SIZEOF(workq_stack_##idx), \ + .workq_priority = DT_INST_PROP_OR(idx, priority, 0), \ + .device_features = BIT(VIRTIO_F_VERSION_1) | BIT(VIRTIO_F_ACCESS_PLATFORM), \ + }; \ + struct mapped_pages_chunk vhost_xen_mmio_pages_chunks_##idx[Q_NUM(idx)] \ + [Q_SZ_MAX(idx) + 1]; \ + static struct virtq_context vhost_xen_mmio_vq_ctx_##idx[Q_NUM(idx)] = { \ + LISTIFY(Q_NUM(idx), VQCTX_INIT, (,), idx), \ + }; \ + static struct vhost_xen_mmio_data vhost_xen_mmio_data_##idx = { \ + .vq_ctx = vhost_xen_mmio_vq_ctx_##idx, \ + .fe.base = -1, \ + .ioserv_port = -1, \ + .fe.servid = -1, \ + }; \ + DEVICE_DT_INST_DEFINE(idx, vhost_xen_mmio_init, NULL, &vhost_xen_mmio_data_##idx, \ + &vhost_xen_mmio_config_##idx, POST_KERNEL, 100, \ + &vhost_driver_xen_mmio_api); + +DT_INST_FOREACH_STATUS_OKAY(VHOST_XEN_MMIO_INST) diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c new file mode 100644 index 0000000000000..54c58c5c74251 --- /dev/null +++ b/drivers/vhost/vringh.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(vhost_vringh); + +int vringh_init(struct vringh *vrh, uint64_t features, uint16_t num, bool weak_barriers, + struct virtq_desc *desc, struct virtq_avail *avail, struct virtq_used *used) +{ + if (!vrh || !desc || !avail || !used) { + return -EINVAL; + } + + memset(vrh, 0, sizeof(*vrh)); + + vrh->event_indices = false; /* not supported */ + vrh->weak_barriers = weak_barriers; + vrh->last_avail_idx = 0; + vrh->last_used_idx = 0; + vrh->completed = 0; + + vrh->vring.num = num; + vrh->vring.desc = desc; + vrh->vring.avail = avail; + vrh->vring.used = used; + + return 0; +} + +static void vringh_kick_callback(const struct device *dev, uint16_t queue_id, void *ptr) +{ + struct vringh *vrh = ptr; + + if (vrh->kick) { + vrh->kick(vrh); + } +} + +int vringh_init_device(struct vringh *vrh, const struct device *dev, uint16_t queue_id, + void (*kick_callback)(struct vringh *vrh)) +{ + uint64_t drv_feats; + void *parts[3]; + size_t q_num; + int ret; + + if (!vrh || !dev) { + return -EINVAL; + } + + ret = vhost_get_virtq(dev, queue_id, parts, &q_num); + if (ret < 0) { + LOG_ERR("vhost_get_virtq failed: %d", ret); + return ret; + } + + ret = vhost_get_driver_features(dev, &drv_feats); + if (ret < 0) { + LOG_ERR("vhost_get_driver_features failed: %d", ret); + return ret; + } + + ret = vringh_init(vrh, drv_feats, q_num, false, parts[0], parts[1], parts[2]); + if (ret < 0) { + LOG_ERR("vringh_init failed: %d", ret); + return ret; + } + + vrh->dev = dev; + vrh->queue_id = queue_id; + vrh->kick = kick_callback; + + ret = vhost_register_virtq_notify_cb(dev, queue_id, vringh_kick_callback, (void *)vrh); + if (ret < 0) { + LOG_ERR("vhost_set_notify_callback failed: %d", ret); + return ret; + } + + return 0; +} + +int vringh_getdesc(struct vringh *vrh, struct vringh_iov *riov, struct vringh_iov *wiov, + uint16_t *head_out) +{ + if (!vrh || !riov || !wiov || !head_out) { + return -EINVAL; + } + + barrier_dmem_fence_full(); + + k_spinlock_key_t key = k_spin_lock(&vrh->lock); + struct vring *vr = &vrh->vring; + const uint16_t avail_idx = sys_le16_to_cpu(vrh->vring.avail->idx); + const uint16_t slot = vrh->last_avail_idx % vr->num; + const uint16_t head = sys_le16_to_cpu(vr->avail->ring[slot]); + struct vhost_buf desc_ranges[vr->num]; + size_t filled_read = 0; + size_t filled_write = 0; + uint16_t idx = head; + size_t count = 0; + uint16_t flags; + int ret; + + if (vrh->last_avail_idx == avail_idx) { + k_spin_unlock(&vrh->lock, key); + return 0; + } + + if (head >= vrh->vring.num) { + k_spin_unlock(&vrh->lock, key); + LOG_ERR("Invalid descriptor head: %u >= %u", head, vrh->vring.num); + return -EINVAL; + } + + if (!vrh->event_indices) { + flags = sys_le16_to_cpu(vr->used->flags); + flags |= VIRTQ_USED_F_NO_NOTIFY; + vr->used->flags = sys_cpu_to_le16(flags); + } + + barrier_dmem_fence_full(); + + k_spin_unlock(&vrh->lock, key); + + vringh_iov_reset(riov); + vringh_iov_reset(wiov); + + do { + const struct virtq_desc *d = &vr->desc[idx]; + const uint64_t gpa = sys_le64_to_cpu(d->addr); + const uint32_t len = sys_le32_to_cpu(d->len); + const uint16_t next = sys_le16_to_cpu(d->next); + + flags = sys_le16_to_cpu(d->flags); + + if (count >= vr->num) { + LOG_ERR("Descriptor chain too long: %zu", count); + ret = -ENOMEM; + goto failed; + } + + /* Validate next descriptor index */ + if ((flags & VIRTQ_DESC_F_NEXT) && next >= vr->num) { + LOG_ERR("Invalid next descriptor: %u >= %u", next, vr->num); + ret = -EINVAL; + goto failed; + } + + if (len == 0) { + LOG_WRN("Zero-length descriptor at index %u", idx); + idx = next; + continue; + } + + /* Store descriptor information for Phase 2 */ + desc_ranges[count].gpa = gpa; + desc_ranges[count].len = len; + desc_ranges[count].is_write = !!(flags & VIRTQ_DESC_F_WRITE); + + count++; + idx = next; + } while (flags & VIRTQ_DESC_F_NEXT); + + ret = vhost_prepare_iovec(vrh->dev, vrh->queue_id, head, desc_ranges, count, riov->iov, + riov->max_num, wiov->iov, wiov->max_num, &filled_read, + &filled_write); + if (ret < 0) { + LOG_ERR("vhost_prepare_iovec failed: %d", ret); + goto failed; + } + + riov->used = filled_read; + wiov->used = filled_write; + + /* Success - update state and return */ + *head_out = head; + + key = k_spin_lock(&vrh->lock); + vrh->last_avail_idx++; + k_spin_unlock(&vrh->lock, key); + + return 1; + +failed: + if (count > 0) { + int rc = vhost_release_iovec(vrh->dev, vrh->queue_id, head); + + if (rc < 0) { + LOG_ERR("vhost_release_iovec failed: %d", rc); + vhost_set_device_status(vrh->dev, DEVICE_STATUS_FAILED); + } + } + + vringh_iov_reset(riov); + vringh_iov_reset(wiov); + + return ret; +} + +int vringh_complete(struct vringh *vrh, uint16_t head, uint32_t total_len) +{ + struct vring *vr = &vrh->vring; + int rc = 0; + + if (!vrh) { + return -EINVAL; + } + + rc = vhost_release_iovec(vrh->dev, vrh->queue_id, head); + if (rc < 0) { + LOG_ERR("vhost_release_iovec failed: %d", rc); + vhost_set_device_status(vrh->dev, DEVICE_STATUS_FAILED); + return rc; + } + + k_spinlock_key_t key = k_spin_lock(&vrh->lock); + + const uint16_t used_idx = sys_le16_to_cpu(vr->used->idx); + struct virtq_used_elem *ue = &vr->used->ring[used_idx % vr->num]; + + LOG_DBG("used_idx %u ue={%u, %u}", used_idx, head, total_len); + + ue->id = sys_cpu_to_le32((uint32_t)head); + ue->len = sys_cpu_to_le32(total_len); + + barrier_dmem_fence_full(); + vr->used->idx = sys_cpu_to_le16(used_idx + 1); + vrh->last_used_idx++; + + uint16_t flags = sys_le16_to_cpu(vr->used->flags); + + flags &= ~VIRTQ_USED_F_NO_NOTIFY; + vr->used->flags = sys_cpu_to_le16(flags); + barrier_dmem_fence_full(); + + k_spin_unlock(&vrh->lock, key); + + return rc; +} + +int vringh_abandon(struct vringh *vrh, uint32_t num) +{ + struct vring *vr = &vrh->vring; + int rc = 0; + + if (num == 0) { + return 0; + } + + if (!vrh) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&vrh->lock); + + if (num <= vrh->last_avail_idx) { + vrh->last_avail_idx -= num; + LOG_DBG("Abandoned %u descs, new last_avail_idx: %u", num, vrh->last_avail_idx); + } else { + LOG_ERR("Cannot abandon %u descs, avail=%u", num, vrh->last_avail_idx); + rc = -ERANGE; + } + + uint16_t flags = sys_le16_to_cpu(vr->used->flags); + + flags &= ~VIRTQ_USED_F_NO_NOTIFY; + vr->used->flags = sys_cpu_to_le16(flags); + + barrier_dmem_fence_full(); + + k_spin_unlock(&vrh->lock, key); + + return rc; +} + +void vringh_iov_reset(struct vringh_iov *iov) +{ + if (!iov || !iov->iov) { + return; + } + + if (iov->consumed > 0 && iov->i < iov->used) { + iov->iov[iov->i].iov_len += iov->consumed; + iov->iov[iov->i].iov_base = (char *)iov->iov[iov->i].iov_base - iov->consumed; + } + + iov->consumed = 0; + iov->i = 0; + iov->used = 0; +} + +int vringh_need_notify(struct vringh *vrh) +{ + if (!vrh) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&vrh->lock); + const uint16_t flags = sys_le16_to_cpu(vrh->vring.avail->flags); + + k_spin_unlock(&vrh->lock, key); + + return !(flags & VIRTQ_AVAIL_F_NO_INTERRUPT); +} + +void vringh_notify(struct vringh *vrh) +{ + k_spinlock_key_t key = k_spin_lock(&vrh->lock); + const uint16_t flags = sys_le16_to_cpu(vrh->vring.avail->flags); + + k_spin_unlock(&vrh->lock, key); + + if (flags & VIRTQ_AVAIL_F_NO_INTERRUPT) { + return; + } + + vhost_notify_virtq(vrh->dev, vrh->queue_id); +} diff --git a/drivers/virtio/virtio_common.c b/drivers/virtio/virtio_common.c index 44012370239d0..4d4376b4849bb 100644 --- a/drivers/virtio/virtio_common.c +++ b/drivers/virtio/virtio_common.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "virtio_common.h" diff --git a/drivers/virtio/virtio_common.h b/drivers/virtio/virtio_common.h index 3324b70b8aad8..a2b9cbfa41124 100644 --- a/drivers/virtio/virtio_common.h +++ b/drivers/virtio/virtio_common.h @@ -7,67 +7,6 @@ #ifndef ZEPHYR_VIRTIO_VIRTIO_COMMON_H_ #define ZEPHYR_VIRTIO_VIRTIO_COMMON_H_ -#define DEVICE_STATUS_ACKNOWLEDGE 0 -#define DEVICE_STATUS_DRIVER 1 -#define DEVICE_STATUS_DRIVER_OK 2 -#define DEVICE_STATUS_FEATURES_OK 3 -#define DEVICE_STATUS_NEEDS_RESET 6 -#define DEVICE_STATUS_FAILED 7 - -#define VIRTIO_F_VERSION_1 32 - -/* Ranges of feature bits for specific device types (see spec 2.2)*/ -#define DEV_TYPE_FEAT_RANGE_0_BEGIN 0 -#define DEV_TYPE_FEAT_RANGE_0_END 23 -#define DEV_TYPE_FEAT_RANGE_1_BEGIN 50 -#define DEV_TYPE_FEAT_RANGE_1_END 127 - -/* - * While defined separately in 4.1.4.5 for PCI and in 4.2.2 for MMIO - * the same bits are responsible for the same interrupts, so defines - * with them can be unified - */ -#define VIRTIO_QUEUE_INTERRUPT 1 -#define VIRTIO_DEVICE_CONFIGURATION_INTERRUPT 2 - -/* - * VIRTIO-MMIO register definitions. - * - * Based on Virtual I/O Device (VIRTIO) Version 1.3 specification: - * https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.pdf - */ - -#define VIRTIO_MMIO_MAGIC_VALUE 0x000 -#define VIRTIO_MMIO_VERSION 0x004 -#define VIRTIO_MMIO_DEVICE_ID 0x008 -#define VIRTIO_MMIO_VENDOR_ID 0x00c -#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 -#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 -#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 -#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 -#define VIRTIO_MMIO_QUEUE_SEL 0x030 -#define VIRTIO_MMIO_QUEUE_SIZE_MAX 0x034 -#define VIRTIO_MMIO_QUEUE_SIZE 0x038 -#define VIRTIO_MMIO_QUEUE_READY 0x044 -#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 -#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 -#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 -#define VIRTIO_MMIO_STATUS 0x070 -#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 -#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 -#define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090 -#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094 -#define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0 -#define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4 -#define VIRTIO_MMIO_SHM_SEL 0x0ac -#define VIRTIO_MMIO_SHM_LEN_LOW 0x0b0 -#define VIRTIO_MMIO_SHM_LEN_HIGH 0x0b4 -#define VIRTIO_MMIO_SHM_BASE_LOW 0x0b8 -#define VIRTIO_MMIO_SHM_BASE_HIGH 0x0bc -#define VIRTIO_MMIO_QUEUE_RESET 0x0c0 -#define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc -#define VIRTIO_MMIO_CONFIG 0x100 - /** * Common virtio isr * diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index f9ad7070ec60b..1872682372bea 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "virtio_common.h" diff --git a/drivers/xen/CMakeLists.txt b/drivers/xen/CMakeLists.txt index 412be7d318d3d..ca56ffe8cc351 100644 --- a/drivers/xen/CMakeLists.txt +++ b/drivers/xen/CMakeLists.txt @@ -2,8 +2,10 @@ # Copyright (c) 2021-2023 EPAM Systems zephyr_sources(hvm.c) +zephyr_sources_ifdef(CONFIG_XEN_DMOP dmop.c) zephyr_sources(events.c) zephyr_sources_ifdef(CONFIG_XEN_GRANT_TABLE gnttab.c) zephyr_sources(memory.c) +zephyr_sources_ifdef(CONFIG_XEN_SCHED sched.c) add_subdirectory_ifdef(CONFIG_XEN_DOM0 dom0) diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 99ad113887b12..5b7a476a08194 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -5,6 +5,12 @@ if XEN menu "Xen drivers" +config XEN_DMOP + bool "Xen dmop hypercall wrappers" + default y + help + Enable wrappers for Xen dm_op hypercalls + config XEN_GRANT_TABLE bool "Xen grant table driver" depends on HEAP_MEM_POOL_SIZE > 0 @@ -29,6 +35,12 @@ config NR_GRANT_FRAMES grant table driver in runtime. This value should be <= max_grant_frames configured for domain in Xen hypervisor. +config XEN_SCHED + bool "Xen scheduler helpers" + default y + help + Enable Xen scheduler helper functions + endmenu endif # XEN diff --git a/drivers/xen/dmop.c b/drivers/xen/dmop.c new file mode 100644 index 0000000000000..92ebf99ae6e5e --- /dev/null +++ b/drivers/xen/dmop.c @@ -0,0 +1,157 @@ +/* + * Copyright 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +int dmop_create_ioreq_server(domid_t domid, uint8_t handle_bufioreq, ioservid_t *id) +{ + struct xen_dm_op_buf bufs[1] = {0}; + struct xen_dm_op dm_op = {0}; + int err; + + dm_op.op = XEN_DMOP_create_ioreq_server; + dm_op.u.create_ioreq_server.handle_bufioreq = handle_bufioreq; + + set_xen_guest_handle(bufs[0].h, &dm_op); + bufs[0].size = sizeof(struct xen_dm_op); + + err = HYPERVISOR_dm_op(domid, ARRAY_SIZE(bufs), bufs); + if (err) { + return err; + } + + *id = dm_op.u.create_ioreq_server.id; + + return 0; +} + +int dmop_destroy_ioreq_server(domid_t domid, ioservid_t id) +{ + struct xen_dm_op_buf bufs[1] = {0}; + struct xen_dm_op dm_op = {0}; + int err; + + dm_op.op = XEN_DMOP_destroy_ioreq_server; + dm_op.u.destroy_ioreq_server.id = id; + + set_xen_guest_handle(bufs[0].h, &dm_op); + bufs[0].size = sizeof(struct xen_dm_op); + + err = HYPERVISOR_dm_op(domid, ARRAY_SIZE(bufs), bufs); + if (err) { + return err; + } + + return 0; +} + +int dmop_map_io_range_to_ioreq_server(domid_t domid, ioservid_t id, uint32_t type, uint64_t start, + uint64_t end) +{ + struct xen_dm_op_buf bufs[1] = {0}; + struct xen_dm_op dm_op = {0}; + int err; + + dm_op.op = XEN_DMOP_map_io_range_to_ioreq_server; + dm_op.u.map_io_range_to_ioreq_server.id = id; + dm_op.u.map_io_range_to_ioreq_server.type = type; + dm_op.u.map_io_range_to_ioreq_server.start = start; + dm_op.u.map_io_range_to_ioreq_server.end = end; + + set_xen_guest_handle(bufs[0].h, &dm_op); + bufs[0].size = sizeof(struct xen_dm_op); + + err = HYPERVISOR_dm_op(domid, ARRAY_SIZE(bufs), bufs); + if (err < 0) { + return err; + } + + return 0; +} + +int dmop_unmap_io_range_from_ioreq_server(domid_t domid, ioservid_t id, uint32_t type, + uint64_t start, uint64_t end) +{ + struct xen_dm_op_buf bufs[1] = {0}; + struct xen_dm_op dm_op = {0}; + int err; + + dm_op.op = XEN_DMOP_unmap_io_range_from_ioreq_server; + dm_op.u.unmap_io_range_from_ioreq_server.id = id; + dm_op.u.unmap_io_range_from_ioreq_server.type = type; + dm_op.u.unmap_io_range_from_ioreq_server.start = start; + dm_op.u.unmap_io_range_from_ioreq_server.end = end; + + set_xen_guest_handle(bufs[0].h, &dm_op); + bufs[0].size = sizeof(struct xen_dm_op); + + err = HYPERVISOR_dm_op(domid, ARRAY_SIZE(bufs), bufs); + if (err < 0) { + return err; + } + + return 0; +} + +int dmop_set_ioreq_server_state(domid_t domid, ioservid_t id, uint8_t enabled) +{ + struct xen_dm_op_buf bufs[1] = {0}; + struct xen_dm_op dm_op = {0}; + int err; + + dm_op.op = XEN_DMOP_set_ioreq_server_state; + dm_op.u.set_ioreq_server_state.id = id; + dm_op.u.set_ioreq_server_state.enabled = enabled; + + set_xen_guest_handle(bufs[0].h, &dm_op); + bufs[0].size = sizeof(struct xen_dm_op); + + err = HYPERVISOR_dm_op(domid, 1, bufs); + if (err) { + return err; + } + + return 0; +} + +int dmop_nr_vcpus(domid_t domid) +{ + struct xen_dm_op_buf bufs[1] = {0}; + struct xen_dm_op dm_op = {0}; + int err; + + dm_op.op = XEN_DMOP_nr_vcpus; + + set_xen_guest_handle(bufs[0].h, &dm_op); + bufs[0].size = sizeof(struct xen_dm_op); + + err = HYPERVISOR_dm_op(domid, 1, bufs); + if (err < 0) { + return err; + } + + return dm_op.u.nr_vcpus.vcpus; +} + +int dmop_set_irq_level(domid_t domid, uint32_t irq, uint8_t level) +{ + struct xen_dm_op_buf bufs[1] = {0}; + struct xen_dm_op dm_op = {0}; + int err; + + dm_op.op = XEN_DMOP_set_irq_level; + dm_op.u.set_irq_level.irq = irq; + dm_op.u.set_irq_level.level = level; + + set_xen_guest_handle(bufs[0].h, &dm_op); + bufs[0].size = sizeof(struct xen_dm_op); + + err = HYPERVISOR_dm_op(domid, 1, bufs); + + return err; +} diff --git a/drivers/xen/dom0/CMakeLists.txt b/drivers/xen/dom0/CMakeLists.txt index c722beb611939..30e6bb2a7789d 100644 --- a/drivers/xen/dom0/CMakeLists.txt +++ b/drivers/xen/dom0/CMakeLists.txt @@ -2,3 +2,5 @@ # Copyright (c) 2023 EPAM Systems zephyr_sources(domctl.c) +zephyr_sources(sysctl.c) +zephyr_sources(version.c) diff --git a/drivers/xen/dom0/domctl.c b/drivers/xen/dom0/domctl.c index 5d5178584ad80..f4a91c915b9ec 100644 --- a/drivers/xen/dom0/domctl.c +++ b/drivers/xen/dom0/domctl.c @@ -4,11 +4,12 @@ * */ +#include +#include + #include #include #include -#include -#include #include #include @@ -123,12 +124,12 @@ int xen_domctl_get_paging_mempool_size(int domid, uint64_t *size_mb) return 0; } -int xen_domctl_set_paging_mempool_size(int domid, uint64_t size_mb) +int xen_domctl_set_paging_mempool_size(int domid, uint64_t size) { xen_domctl_t domctl = { .cmd = XEN_DOMCTL_set_paging_mempool_size, .domain = domid, - .u.paging_mempool.size = size_mb, + .u.paging_mempool.size = size, }; return do_domctl(&domctl); @@ -273,15 +274,23 @@ int xen_domctl_max_vcpus(int domid, int max_vcpus) return do_domctl(&domctl); } -int xen_domctl_createdomain(int domid, struct xen_domctl_createdomain *config) +int xen_domctl_createdomain(int *domid, struct xen_domctl_createdomain *config) { - xen_domctl_t domctl = { - .cmd = XEN_DOMCTL_createdomain, - .domain = domid, - .u.createdomain = *config, - }; + int ret; + xen_domctl_t domctl; - return do_domctl(&domctl); + if (!domid || !config) { + return -EINVAL; + } + + domctl.cmd = XEN_DOMCTL_createdomain, + domctl.domain = *domid, + domctl.u.createdomain = *config, + + ret = do_domctl(&domctl); + *domid = domctl.domain; + + return ret; } int xen_domctl_destroydomain(int domid) @@ -304,3 +313,19 @@ int xen_domctl_cacheflush(int domid, struct xen_domctl_cacheflush *cacheflush) return do_domctl(&domctl); } + +int xen_domctl_getvcpu(int domid, uint32_t vcpu, struct xen_domctl_getvcpuinfo *info) +{ + int ret; + xen_domctl_t domctl = { + .cmd = XEN_DOMCTL_getvcpuinfo, + .domain = domid, + .u.getvcpuinfo.vcpu = vcpu, + }; + + ret = do_domctl(&domctl); + if (ret >= 0) { + *info = domctl.u.getvcpuinfo; + } + return ret; +} diff --git a/drivers/xen/dom0/sysctl.c b/drivers/xen/dom0/sysctl.c new file mode 100644 index 0000000000000..4ea37143e8202 --- /dev/null +++ b/drivers/xen/dom0/sysctl.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +static int do_sysctl(xen_sysctl_t *sysctl) +{ + sysctl->interface_version = XEN_SYSCTL_INTERFACE_VERSION; + return HYPERVISOR_sysctl(sysctl); +} + +int xen_sysctl_physinfo(struct xen_sysctl_physinfo *info) +{ + xen_sysctl_t sysctl = { + .cmd = XEN_SYSCTL_physinfo, + }; + int ret; + + ret = do_sysctl(&sysctl); + if (ret < 0) { + return ret; + } + *info = sysctl.u.physinfo; + return ret; +} + +int xen_sysctl_getdomaininfo(struct xen_domctl_getdomaininfo *domaininfo, + uint16_t first, uint16_t num) +{ + xen_sysctl_t sysctl = { + .cmd = XEN_SYSCTL_getdomaininfolist, + .u.getdomaininfolist.first_domain = first, + .u.getdomaininfolist.max_domains = num, + .u.getdomaininfolist.buffer.p = domaininfo, + }; + int ret; + + ret = do_sysctl(&sysctl); + if (ret < 0) { + return ret; + } + return sysctl.u.getdomaininfolist.num_domains; +} diff --git a/drivers/xen/dom0/version.c b/drivers/xen/dom0/version.c new file mode 100644 index 0000000000000..165795b4fd958 --- /dev/null +++ b/drivers/xen/dom0/version.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include + +int xen_version(void) +{ + return HYPERVISOR_xen_version(XENVER_version, NULL); +} + +int xen_version_extraversion(char *extra, int len) +{ + if (!extra || len < XEN_EXTRAVERSION_LEN) { + return -EINVAL; + } + memset(extra, 0, len); + return HYPERVISOR_xen_version(XENVER_extraversion, extra); +} diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 782882132a7e5..c527708e77d3d 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -5,9 +5,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include +#include + #include -#include -#include #include #include @@ -111,7 +112,7 @@ int evtchn_set_priority(evtchn_port_t port, uint32_t priority) return HYPERVISOR_event_channel_op(EVTCHNOP_set_priority, &set); } -void notify_evtchn(evtchn_port_t port) +int notify_evtchn(evtchn_port_t port) { struct evtchn_send send; @@ -121,7 +122,7 @@ void notify_evtchn(evtchn_port_t port) send.port = port; - HYPERVISOR_event_channel_op(EVTCHNOP_send, &send); + return HYPERVISOR_event_channel_op(EVTCHNOP_send, &send); } int bind_event_channel(evtchn_port_t port, evtchn_cb_t cb, void *data) diff --git a/drivers/xen/gnttab.c b/drivers/xen/gnttab.c index ccf1b888e17a6..b6c82ad107c11 100644 --- a/drivers/xen/gnttab.c +++ b/drivers/xen/gnttab.c @@ -17,12 +17,15 @@ * **************************************************************************** */ + +#include +#include +#include + #include #include #include -#include -#include -#include +#include #include #include @@ -193,61 +196,92 @@ static void gop_eagain_retry(int cmd, struct gnttab_map_grant_ref *gref) } } -void *gnttab_get_page(void) +void *gnttab_get_pages(unsigned int npages) { int ret; void *page_addr; - struct xen_remove_from_physmap rfpm; + unsigned int removed; + xen_pfn_t gfn; - page_addr = k_aligned_alloc(XEN_PAGE_SIZE, XEN_PAGE_SIZE); - if (!page_addr) { - LOG_WRN("Failed to allocate memory for gnttab page!\n"); + if (npages == 0) { return NULL; } - rfpm.domid = DOMID_SELF; - rfpm.gpfn = xen_virt_to_gfn(page_addr); + page_addr = k_aligned_alloc(XEN_PAGE_SIZE, XEN_PAGE_SIZE * npages); + if (!page_addr) { + LOG_WRN("Failed to allocate memory for gnttab %u pages!", npages); + return NULL; + } /* * GNTTABOP_map_grant_ref will simply replace the entry in the P2M * and not release any RAM that may have been associated with * page_addr, so we release this memory before mapping. */ - ret = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rfpm); + for (removed = 0; removed < npages; removed++) { + gfn = xen_virt_to_gfn((char *)page_addr + (removed * XEN_PAGE_SIZE)); + + ret = xendom_remove_from_physmap(DOMID_SELF, gfn); + if (ret) { + break; + } + } + if (ret) { - LOG_WRN("Failed to remove gnttab page from physmap, ret = %d\n", ret); + LOG_WRN("xendom_remove_from_physmap failed: ret=%d, removed=%u, gfn=%llx", + ret, removed, (uint64_t)gfn); + + if (removed > 0) { + ret = gnttab_put_pages(page_addr, removed); + if (ret) { + LOG_WRN("gnttab_put_pages failed ret=%d addr=%p", ret, page_addr); + } + } + return NULL; } return page_addr; } -void gnttab_put_page(void *page_addr) +int gnttab_put_pages(void *page_addr, unsigned int npages) { - int ret, nr_extents = 1; + int ret; + size_t i; struct xen_memory_reservation reservation; - xen_pfn_t page = xen_virt_to_gfn(page_addr); + xen_pfn_t *pages; + + if (npages == 0) { + return -EINVAL; + } + + pages = k_malloc(sizeof(xen_pfn_t) * npages); + if (pages == NULL) { + LOG_WRN("Failed to allocate memory: npages=%u", npages); + return -ENOMEM; + } + + for (i = 0; i < npages; i++) { + pages[i] = xen_virt_to_gfn((char *)page_addr + (i * XEN_PAGE_SIZE)); + } - /* - * After unmapping there will be a 4Kb holes in address space - * at 'page_addr' positions. To keep it contiguous and be able - * to return such addresses to memory allocator we need to - * populate memory on unmapped positions here. - */ memset(&reservation, 0, sizeof(reservation)); reservation.domid = DOMID_SELF; reservation.extent_order = 0; - reservation.nr_extents = nr_extents; - set_xen_guest_handle(reservation.extent_start, &page); + reservation.nr_extents = npages; + set_xen_guest_handle(reservation.extent_start, pages); ret = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); - if (ret != nr_extents) { - LOG_WRN("failed to populate physmap on gfn = 0x%llx, ret = %d\n", - page, ret); - return; + if (ret != npages) { + LOG_WRN("failed to populate physmap, ret = %d (npages=%u)", ret, npages); + k_free(pages); + return -EIO; } + k_free(pages); k_free(page_addr); + + return 0; } int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, unsigned int count) diff --git a/drivers/xen/hvm.c b/drivers/xen/hvm.c index 5244d34d43352..065896bb41c4c 100644 --- a/drivers/xen/hvm.c +++ b/drivers/xen/hvm.c @@ -4,10 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include +#include + #include #include -#include -#include #include diff --git a/drivers/xen/memory.c b/drivers/xen/memory.c index 6472ad74af253..29f0df899031d 100644 --- a/drivers/xen/memory.c +++ b/drivers/xen/memory.c @@ -3,10 +3,11 @@ * Copyright (c) 2023 EPAM Systems */ +#include +#include + #include #include -#include -#include #include @@ -66,3 +67,25 @@ int xendom_populate_physmap(int domid, unsigned int extent_order, return HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); } + +int xendom_acquire_resource(domid_t domid, uint16_t type, uint32_t id, uint64_t frame, + uint32_t *nr_frames, xen_pfn_t *frame_list) +{ + struct xen_mem_acquire_resource acquire_res = { + .domid = domid, + .type = type, + .id = id, + .pad = 0, + .frame = frame, + .nr_frames = *nr_frames, + }; + int ret; + + set_xen_guest_handle(acquire_res.frame_list, frame_list); + + ret = HYPERVISOR_memory_op(XENMEM_acquire_resource, &acquire_res); + + *nr_frames = acquire_res.nr_frames; + + return ret; +} diff --git a/drivers/xen/sched.c b/drivers/xen/sched.c new file mode 100644 index 0000000000000..873f8d69fa1c9 --- /dev/null +++ b/drivers/xen/sched.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int sched_poll(evtchn_port_t *ports, unsigned int nr_ports, uint64_t timeout) +{ + struct sched_poll poll = { + .ports.p = ports, + .nr_ports = 1, + .timeout = timeout, + }; + + return HYPERVISOR_sched_op(SCHEDOP_poll, &poll); +} diff --git a/dts/bindings/vhost/xen,virtio-mmio.yaml b/dts/bindings/vhost/xen,virtio-mmio.yaml new file mode 100644 index 0000000000000..89f5d86eb9daf --- /dev/null +++ b/dts/bindings/vhost/xen,virtio-mmio.yaml @@ -0,0 +1,39 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +description: Xen Platform Control Registers + +compatible: "xen,vhost-mmio" + +include: base.yaml + +properties: + base: + type: int + required: true + + num-queues: + type: int + required: true + + queue-size-max: + type: int + required: true + + device-id: + type: int + required: true + + vendor-id: + type: int + default: 0 + + stack-size: + type: int + description: > + Stack size (in bytes) for the instance-specific work_q thread. + + priority: + type: int + description: > + Priority for the instance-specific work_q thread. diff --git a/include/zephyr/arch/arm64/hypercall.h b/include/zephyr/arch/arm64/hypercall.h index 023ea10803d06..9cbce03d2dd9c 100644 --- a/include/zephyr/arch/arm64/hypercall.h +++ b/include/zephyr/arch/arm64/hypercall.h @@ -6,6 +6,8 @@ #ifndef ZEPHYR_INCLUDE_ARCH_ARM64_HYPERCALL_H_ #define ZEPHYR_INCLUDE_ARCH_ARM64_HYPERCALL_H_ +#include + /* defined in hypercall.S by HYPERCALL(hypercall) */ int HYPERVISOR_console_io(int op, int cnt, char *str); int HYPERVISOR_sched_op(int op, void *param); @@ -13,9 +15,12 @@ int HYPERVISOR_event_channel_op(int op, void *param); int HYPERVISOR_hvm_op(int op, void *param); int HYPERVISOR_memory_op(int op, void *param); int HYPERVISOR_grant_table_op(int op, void *uop, unsigned int count); +int HYPERVISOR_dm_op(domid_t domid, unsigned int nr_bufs, struct xen_dm_op_buf *bufs); +int HYPERVISOR_xen_version(int op, void *param); #ifdef CONFIG_XEN_DOM0 int HYPERVISOR_domctl(void *param); +int HYPERVISOR_sysctl(void *param); #endif #endif /* ZEPHYR_INCLUDE_ARCH_ARM64_HYPERCALL_H_ */ diff --git a/include/zephyr/drivers/vhost.h b/include/zephyr/drivers/vhost.h new file mode 100644 index 0000000000000..56c9a7aad781f --- /dev/null +++ b/include/zephyr/drivers/vhost.h @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_VHOST_H_ +#define ZEPHYR_DRIVERS_VHOST_H_ + +/** + * @brief VHost API + * + * The VHost provides functions for VIRTIO device backends in + * a hypervisor environment. + * VHost backends handle guest VIRTIO requests and respond to them. + * + * @defgroup vhost_apis VHost Controller APIs + * @ingroup io_interfaces + * @{ + */ + +#include +#include + +/** + * Represents a memory buffer segment for VHost operations. + */ +struct vhost_iovec { + void *iov_base; + size_t iov_len; +}; + +/** + * Represents a guest physical address and length pair for VHost operations. + */ +struct vhost_buf { + uint64_t gpa; + size_t len; + bool is_write; +}; + +/** + * VHost controller API structure + */ +__subsystem struct vhost_controller_api { + int (*prepare_iovec)(const struct device *dev, uint16_t queue_id, uint16_t head, + const struct vhost_buf *bufs, size_t bufs_count, + struct vhost_iovec *read_iovec, size_t max_read_iovecs, + struct vhost_iovec *write_iovec, size_t max_write_iovecs, + size_t *read_count, size_t *write_count); + int (*release_iovec)(const struct device *dev, uint16_t queue_id, uint16_t head); + int (*get_virtq)(const struct device *dev, uint16_t queue_id, void **parts, + size_t *queue_size); + int (*get_driver_features)(const struct device *dev, uint64_t *drv_feats); + bool (*virtq_is_ready)(const struct device *dev, uint16_t queue_id); + int (*register_virtq_ready_cb)(const struct device *dev, + void (*callback)(const struct device *dev, uint16_t queue_id, + void *data), + void *data); + int (*register_virtq_notify_cb)(const struct device *dev, uint16_t queue_id, + void (*callback)(const struct device *dev, + uint16_t queue_id, void *data), + void *data); + int (*notify_virtq)(const struct device *dev, uint16_t queue_id); + int (*set_device_status)(const struct device *dev, uint32_t status); +}; + +/** + * @brief Prepare iovecs for virtq process + * + * Maps guest physical addresses to host virtual addresses for the given + * GPAs and fills the provided read and write iovec arrays. + * + * @param dev VHost device + * @param queue_id Queue identifier + * @param slot_id Slot identifier + * @param bufs Array of GPA/length pairs + * @param bufs_count Number of bufs in the array + * @param read_iovec Array to fill with read iovecs + * @param max_read_iovecs Maximum number of read iovecs that can be stored + * @param write_iovec Array to fill with write iovecs + * @param max_write_iovecs Maximum number of write iovecs that can be stored + * @param read_count Number of read iovecs prepared + * @param write_count Number of write iovecs prepared + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -ENOMEM Insufficient memory + * @retval -E2BIG Buffer too large (in other word, iovecs are too small) + */ +static inline int vhost_prepare_iovec(const struct device *dev, uint16_t queue_id, uint16_t slot_id, + const struct vhost_buf *bufs, size_t bufs_count, + struct vhost_iovec *read_iovec, size_t max_read_iovecs, + struct vhost_iovec *write_iovec, size_t max_write_iovecs, + size_t *read_count, size_t *write_count) +{ + const struct vhost_controller_api *api = dev->api; + + return api->prepare_iovec(dev, queue_id, slot_id, bufs, bufs_count, read_iovec, + max_read_iovecs, write_iovec, max_write_iovecs, read_count, + write_count); +} + +/** + * @brief Release all iovecs + * + * Release iovecs that prepared by host_prepare_iovec. + * + * @param dev VHost controller device + * @param queue_id Queue ID + * @param slot_id Slot ID. + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + */ +static inline int vhost_release_iovec(const struct device *dev, uint16_t queue_id, uint16_t slot_id) +{ + const struct vhost_controller_api *api = dev->api; + + return api->release_iovec(dev, queue_id, slot_id); +} + +/** + * @brief Get VirtQueue components + * + * @param dev VHost controller device + * @param queue_id Queue ID + * @param parts Array for descriptor, available, used ring pointers + * @param queue_size Queue size output + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -ENODEV Queue not ready + */ +static inline int vhost_get_virtq(const struct device *dev, uint16_t queue_id, void **parts, + size_t *queue_size) +{ + const struct vhost_controller_api *api = dev->api; + + return api->get_virtq(dev, queue_id, parts, queue_size); +} + +/** + * @brief Get negotiated VirtIO feature bits + * + * @param dev VHost controller device + * @param drv_feats Output for feature mask + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + */ +static inline int vhost_get_driver_features(const struct device *dev, uint64_t *drv_feats) +{ + const struct vhost_controller_api *api = dev->api; + + return api->get_driver_features(dev, drv_feats); +} + +/** + * @brief Check if queue is ready for processing + * + * @param dev VHost controller device + * @param queue_id Queue ID (0-based) + * + * @retval true Queue is ready + * @retval false Queue not ready or invalid + */ +static inline bool vhost_queue_ready(const struct device *dev, uint16_t queue_id) +{ + const struct vhost_controller_api *api = dev->api; + + return api->virtq_is_ready(dev, queue_id); +} + +/** + * @brief Register device-wide queue ready callback + * + * This callback will unregister on device reset. + * + * @param dev VHost controller device + * @param callback Function to call when any queue becomes ready + * @param user_data User data for callback + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + */ +static inline int vhost_register_virtq_ready_cb(const struct device *dev, + void (*callback)(const struct device *dev, + uint16_t queue_id, void *data), + void *user_data) +{ + const struct vhost_controller_api *api = dev->api; + + return api->register_virtq_ready_cb(dev, callback, user_data); +} + +/** + * @brief Register per-queue guest notification callback + * + * This callback will unregister on queue reset. + * + * @param dev VHost controller device + * @param queue_id Queue ID (0-based) + * @param callback Function to call on guest notifications + * @param user_data User data for callback + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -ENODEV Queue not found + */ +static inline int vhost_register_virtq_notify_cb(const struct device *dev, uint16_t queue_id, + void (*callback)(const struct device *dev, + uint16_t queue_id, void *data), + void *user_data) +{ + const struct vhost_controller_api *api = dev->api; + + return api->register_virtq_notify_cb(dev, queue_id, callback, user_data); +} + +/** + * @brief Send interrupt notification to guest + * + * @param dev VHost controller device + * @param queue_id Queue ID (0-based) + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -ENODEV Queue not ready + * @retval -EIO Interrupt delivery failed + */ +static inline int vhost_notify_virtq(const struct device *dev, uint16_t queue_id) +{ + const struct vhost_controller_api *api = dev->api; + + return api->notify_virtq(dev, queue_id); +} + +/** + * @brief Set device status and notify guest + * + * @param dev VHost controller device + * @param status VirtIO device status bits to set + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -EIO Notification failed + */ +static inline int vhost_set_device_status(const struct device *dev, uint32_t status) +{ + const struct vhost_controller_api *api = dev->api; + + return api->set_device_status(dev, status); +} + +/** + * @} + */ + +#endif /* ZEPHYR_DRIVERS_VHOST_H_ */ diff --git a/include/zephyr/drivers/vhost/vringh.h b/include/zephyr/drivers/vhost/vringh.h new file mode 100644 index 0000000000000..b20ce4e42be6e --- /dev/null +++ b/include/zephyr/drivers/vhost/vringh.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_VHOST_VRINGH_H_ +#define ZEPHYR_DRIVERS_VHOST_VRINGH_H_ + +/** + * @file + * @brief VIRTIO Ring Handler API + * + * VIRTIO ring handler (vringh) provides host-side access to guest VIRTIO rings. + * Based on Linux kernel's vringh implementation. + * + * @defgroup vringh_apis VIRTIO Ring Handler APIs + * @ingroup vhost_apis + * @{ + */ + +#include +#include +#include + +struct virtq_desc; +struct virtq_avail; +struct virtq_used; + +/** + * @brief VirtQueue ring structure + * + * Contains pointers to VIRTIO ring components: descriptor table, + * available ring, and used ring. + */ +struct vring { + uint16_t num; /**< Number of descriptors in ring (power of 2) */ + struct virtq_desc *desc; /**< Descriptor table pointer (guest memory) */ + struct virtq_avail *avail; /**< Available ring pointer (guest memory) */ + struct virtq_used *used; /**< Used ring pointer (guest memory) */ +}; + +/** + * @brief VIRTIO ring host-side handler + * + * Host-side interface for processing guest VIRTIO rings. + * Based on Linux kernel vringh implementation with split virtqueue support. + */ +struct vringh { + bool event_indices; /**< Guest supports VIRTIO_F_EVENT_IDX */ + bool weak_barriers; /**< Use weak memory barriers */ + uint16_t last_avail_idx; /**< Last available index processed */ + uint16_t last_used_idx; /**< Last used index written */ + uint32_t completed; /**< Descriptors completed since last notification */ + struct vring vring; /**< VirtQueue ring components */ + const struct device *dev; /**< Associated VHost backend device */ + size_t queue_id; /**< Queue ID within VHost device */ + struct k_spinlock lock; /**< Spinlock for exclusive execution */ + + /** + * @brief Virtqueue notification callback + * Called to signal the VirtIO driver about completed buffers. + */ + void (*notify)(struct vringh *vr); + + /** + * @brief Queue kick callback + * Called when the VirtIO driver notifies (kicks) the queue. + */ + void (*kick)(struct vringh *vr); +}; + +/** + * @brief VirtQueue I/O vector structure + * + * Manages iovec array for processing VirtQueue descriptor chains. + * Tracks current position and handles partial buffer consumption. + */ +struct vringh_iov { + struct vhost_iovec *iov; /**< Array of I/O vectors */ + size_t i; /**< Current iovec index */ + size_t consumed; /**< Bytes consumed from current iovec */ + unsigned int max_num; /**< Maximum number of iovecs */ + unsigned int used; /**< Number of iovecs currently used */ +}; + +/** + * @brief Initialize VirtQueue ring handler with raw pointers + * + * @param vh VirtQueue ring handler to initialize + * @param features VIRTIO feature bits + * @param num Number of descriptors (must be power of 2) + * @param weak_barriers Use weak memory barriers + * @param desc Descriptor table pointer + * @param avail Available ring pointer + * @param used Used ring pointer + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -ENOMEM Insufficient memory + */ +int vringh_init(struct vringh *vh, uint64_t features, uint16_t num, bool weak_barriers, + struct virtq_desc *desc, struct virtq_avail *avail, struct virtq_used *used); + +/** + * @brief Initialize VirtQueue ring handler with VHost device + * + * @param vrh VirtQueue ring handler to initialize + * @param dev VHost backend device + * @param queue_id Queue ID to handle + * @param kick_callback Queue kick callback invoked when the VirtIO driver notifies the queue + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -ENODEV Device or queue not found + * @retval -ENOMEM Insufficient memory + * @retval -EBUSY Queue already in use + * @retval -ENOTCONN Device not connected + * + * @code{.c} + * static void kick_handler(struct vringh *vrh) + * { + * uint16_t head; + * + * while (vringh_getdesc(vrh, &riov, &wiov, &head) > 0) { + * process_buffers(&riov, &wiov); + * vringh_complete(vrh, head, total_bytes_written(&wiov)); + * if (vringh_need_notify(vrh) > 0) { + * vringh_notify(vrh); + * } + * } + * } + * + * int ret = vringh_init_device(&vrh, vhost_dev, 0, kick_handler); + * @endcode + */ +int vringh_init_device(struct vringh *vrh, const struct device *dev, uint16_t queue_id, + void (*kick_callback)(struct vringh *vrh)); + +/** + * @brief Retrieve next available descriptor from VirtQueue + * + * Maps descriptor chain into host-accessible iovecs. + * Separates readable and writable buffers per VIRTIO specification. + * + * @param vrh VirtQueue ring handler + * @param riov IOV for readable buffers + * @param wiov IOV for writable buffers + * @param head_out Descriptor head index for completion + * + * @retval 1 Success - descriptor retrieved + * @retval 0 No descriptors available + * @retval -errno Invalid parameters + */ +int vringh_getdesc(struct vringh *vrh, struct vringh_iov *riov, struct vringh_iov *wiov, + uint16_t *head_out); + +/** + * @brief Complete processing of VirtQueue descriptor + * + * Marks descriptor as completed and adds entry to used ring. + * Based on Linux vringh complete operation. + * + * @param vrh VirtQueue ring handler + * @param head Descriptor head index from vringh_getdesc() + * @param len Total bytes written to writable buffers + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -EFAULT Cannot access used ring + * @retval -ENOSPC Used ring full + * @warning Do not call multiple times for the same descriptor + * + * @code{.c} + * // After processing a descriptor chain + * uint32_t bytes_written = generate_response(&wiov); + * int ret = vringh_complete(&vrh, head, bytes_written); + * if (ret == 0) { + * // Check if guest notification needed + * if (vringh_need_notify(&vrh) > 0) { + * vringh_notify(&vrh); + * } + * } + * @endcode + */ +int vringh_complete(struct vringh *vrh, uint16_t head, uint32_t len); + +/** + * @brief Abandon processing of descriptors without completion + * + * Returns descriptors to available state for re-processing. + * Based on Linux vringh abandon operation. + * + * @param vrh VirtQueue ring handler + * @param num Number of descriptors to abandon + * + * @retval 0 Success + * @retval -EINVAL Invalid parameters + * @retval -ERANGE Cannot abandon more than retrieved + * @retval -EFAULT Error accessing ring + */ +int vringh_abandon(struct vringh *vrh, uint32_t num); + +__maybe_unused static inline void vringh_iov_init(struct vringh_iov *iov, struct vhost_iovec *kvec, + unsigned int num) +{ + iov->used = iov->i = 0; + iov->consumed = 0; + iov->max_num = num; + iov->iov = kvec; +} + +/** + * @brief Reset IOV structure for reuse + * + * @param iov IOV structure to reset + */ +void vringh_iov_reset(struct vringh_iov *iov); + +/** + * @brief Check if guest notification is required + * + * Determines whether guest should be notified based on VIRTIO + * notification suppression mechanism. + * Based on Linux vringh need_notify implementation. + * + * @param vrh VirtQueue ring handler + * + * @retval 1 Notification required + * @retval 0 Notification suppressed + * @retval -EINVAL Invalid parameters + * @retval -EFAULT Cannot access guest memory + */ +int vringh_need_notify(struct vringh *vrh); + +/** + * @brief Send notification to guest about completed buffers + * + * Invokes registered notification callback to inform guest. + * Based on Linux vringh notify implementation. + * + * @param vrh VirtQueue ring handler + */ +void vringh_notify(struct vringh *vrh); + +/** + * @} + */ + +#endif /* ZEPHYR_DRIVERS_VHOST_VRINGH_H_ */ diff --git a/include/zephyr/drivers/virtio/virtio_config.h b/include/zephyr/drivers/virtio/virtio_config.h new file mode 100644 index 0000000000000..097be99227d29 --- /dev/null +++ b/include/zephyr/drivers/virtio/virtio_config.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_VIRTIO_VIRTIO_CONFIG_H_ +#define ZEPHYR_DRIVERS_VIRTIO_VIRTIO_CONFIG_H_ + +#define DEVICE_STATUS_ACKNOWLEDGE 0 +#define DEVICE_STATUS_DRIVER 1 +#define DEVICE_STATUS_DRIVER_OK 2 +#define DEVICE_STATUS_FEATURES_OK 3 +#define DEVICE_STATUS_NEEDS_RESET 6 +#define DEVICE_STATUS_FAILED 7 + +#define VIRTIO_F_ANY_LAYOUT 27 +#define VIRTIO_F_VERSION_1 32 +#define VIRTIO_F_ACCESS_PLATFORM 33 + +#define VIRTIO_RING_F_INDIRECT_DESC 28 +#define VIRTIO_RING_F_EVENT_IDX 29 + +#define VIRTQ_AVAIL_F_NO_INTERRUPT 1 + +#define VIRTQ_USED_F_NO_NOTIFY 1 + +/* Ranges of feature bits for specific device types (see spec 2.2)*/ +#define DEV_TYPE_FEAT_RANGE_0_BEGIN 0 +#define DEV_TYPE_FEAT_RANGE_0_END 23 +#define DEV_TYPE_FEAT_RANGE_1_BEGIN 50 +#define DEV_TYPE_FEAT_RANGE_1_END 127 + +/* + * While defined separately in 4.1.4.5 for PCI and in 4.2.2 for MMIO + * the same bits are responsible for the same interrupts, so defines + * with them can be unified + */ +#define VIRTIO_QUEUE_INTERRUPT 1 +#define VIRTIO_DEVICE_CONFIGURATION_INTERRUPT 2 + +/* + * VIRTIO-MMIO register definitions. + * + * Based on Virtual I/O Device (VIRTIO) Version 1.3 specification: + * https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.pdf + */ + +#define VIRTIO_MMIO_MAGIC_VALUE 0x000 +#define VIRTIO_MMIO_VERSION 0x004 +#define VIRTIO_MMIO_DEVICE_ID 0x008 +#define VIRTIO_MMIO_VENDOR_ID 0x00c +#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 +#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 +#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 +#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 +#define VIRTIO_MMIO_QUEUE_SEL 0x030 +#define VIRTIO_MMIO_QUEUE_SIZE_MAX 0x034 +#define VIRTIO_MMIO_QUEUE_SIZE 0x038 +#define VIRTIO_MMIO_QUEUE_READY 0x044 +#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define VIRTIO_MMIO_STATUS 0x070 +#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 +#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 +#define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090 +#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094 +#define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0 +#define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4 +#define VIRTIO_MMIO_SHM_SEL 0x0ac +#define VIRTIO_MMIO_SHM_LEN_LOW 0x0b0 +#define VIRTIO_MMIO_SHM_LEN_HIGH 0x0b4 +#define VIRTIO_MMIO_SHM_BASE_LOW 0x0b8 +#define VIRTIO_MMIO_SHM_BASE_HIGH 0x0bc +#define VIRTIO_MMIO_QUEUE_RESET 0x0c0 +#define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc +#define VIRTIO_MMIO_CONFIG 0x100 + +#endif /* ZEPHYR_DRIVERS_VIRTIO_VIRTIO_CONFIG_H_ */ diff --git a/include/zephyr/xen/dmop.h b/include/zephyr/xen/dmop.h new file mode 100644 index 0000000000000..dc1f5d5f5539a --- /dev/null +++ b/include/zephyr/xen/dmop.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_XEN_DMOP_H_ +#define ZEPHYR_XEN_DMOP_H_ + +#include + +/** + * @brief Create an I/O request server in the given Xen domain. + * + * This function issues the XEN_DMOP_create_ioreq_server hypercall to create + * a server that handles I/O requests on behalf of the guest domain. + * + * @param domid Xen domain identifier where the server is created. + * @param handle_bufioreq Flag indicating whether buffered I/O requests should be handled. + * Set to non-zero to enable buffered handling. + * @param id Output pointer to receive the newly created server ID. + * + * @return 0 on success, or a negative errno code on failure. + */ +int dmop_create_ioreq_server(domid_t domid, uint8_t handle_bufioreq, ioservid_t *id); + +/** + * @brief Destroy a previously created I/O request server. + * + * Issues the XEN_DMOP_destroy_ioreq_server hypercall to tear down the + * specified I/O request server. + * + * @param domid Xen domain identifier where the server exists. + * @param id I/O request server ID returned by dmop_create_ioreq_server(). + * + * @return 0 on success, or a negative errno code on failure. + */ +int dmop_destroy_ioreq_server(domid_t domid, ioservid_t id); + +/** + * @brief Map a specified I/O address range to an existing I/O request server. + * + * This function issues the XEN_DMOP_map_io_range_to_ioreq_server hypercall to grant + * access to the given I/O address range for the specified server. + * + * @param domid Xen domain identifier where the mapping is applied. + * @param id I/O request server ID returned by dmop_create_ioreq_server(). + * @param type Type identifier for the I/O range (e.g., MMIO, port I/O). + * @param start Start physical address of the I/O range. + * @param end End physical address (inclusive) of the I/O range. + * + * @return 0 on success, or a negative errno code on failure. + */ +int dmop_map_io_range_to_ioreq_server(domid_t domid, ioservid_t id, uint32_t type, uint64_t start, + uint64_t end); + +/** + * @brief Unmap an I/O address range from an I/O request server. + * + * Issues the XEN_DMOP_unmap_io_range_from_ioreq_server hypercall to revoke + * access to a previously mapped I/O address range. + * + * @param domid Xen domain identifier where the unmapping is applied. + * @param id I/O request server ID returned by dmop_create_ioreq_server(). + * @param type Type identifier for the I/O range (e.g., MMIO, port I/O). + * @param start Start physical address of the I/O range. + * @param end End physical address (inclusive) of the I/O range. + * + * @return 0 on success, or a negative errno code on failure. + */ +int dmop_unmap_io_range_from_ioreq_server(domid_t domid, ioservid_t id, uint32_t type, + uint64_t start, uint64_t end); + +/** + * @brief Enable or disable an existing I/O request server. + * + * This function issues the XEN_DMOP_set_ioreq_server_state hypercall to change + * the operational state of the specified I/O request server. + * + * @param domid Xen domain identifier. + * @param id I/O request server ID to modify. + * @param enabled Non-zero to enable the server, zero to disable it. + * + * @return 0 on success, or a negative errno code on failure. + */ +int dmop_set_ioreq_server_state(domid_t domid, ioservid_t id, uint8_t enabled); + +/** + * @brief Query the number of virtual CPUs in a Xen domain. + * + * This function issues the XEN_DMOP_nr_vcpus hypercall to retrieve + * the current vCPU count for the specified domain. + * + * @param domid Xen domain identifier to query. + * + * @return The number of vCPUs on success, or a negative errno code on failure. + */ +int dmop_nr_vcpus(domid_t domid); + +/** + * @brief Set the interrupt level for a specific IRQ in a Xen domain. + * + * This function issues the XEN_DMOP_set_irq_level hypercall to adjust + * the signal level (assert or deassert) for the given IRQ line. + * + * @param domid Xen domain identifier. + * @param irq IRQ number whose level is to be set. + * @param level Non-zero to assert (raise) the IRQ, zero to deassert (lower) it. + * + * @return 0 on success, or a negative errno code on failure. + */ +int dmop_set_irq_level(domid_t domid, uint32_t irq, uint8_t level); + +#endif /* ZEPHYR_XEN_DMOP_H_ */ diff --git a/include/zephyr/xen/dom0/domctl.h b/include/zephyr/xen/dom0/domctl.h index 1ff8334968851..700e7753fd0a0 100644 --- a/include/zephyr/xen/dom0/domctl.h +++ b/include/zephyr/xen/dom0/domctl.h @@ -3,36 +3,231 @@ * Copyright (c) 2023 EPAM Systems * */ + +/** + * @file + * + * @brief Xen Domain Control Interface + */ + #ifndef __XEN_DOM0_DOMCTL_H__ #define __XEN_DOM0_DOMCTL_H__ +#include +#include + #include -#include -#include #include +/** + * @brief Perform a scheduler operation on a specified domain. + * + * @param domid The ID of the domain on which the scheduler operation is to be performed. + * @param[in,out] sched_op A pointer to a `struct xen_domctl_scheduler_op` object that defines + * the specific scheduler operation to be performed. + * @return Returns 0 on success, or a negative error code on failure. + */ int xen_domctl_scheduler_op(int domid, struct xen_domctl_scheduler_op *sched_op); + +/** + * @brief Pauses a domain in the Xen hypervisor. + * + * @param domid The ID of the domain to be paused. + * @return Returns 0 on success, or a negative error code on failure. + */ int xen_domctl_pausedomain(int domid); + +/** + * @brief Unpauses a domain in the Xen hypervisor. + * + * @param domid The domain ID of the domain to be unpaused. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_unpausedomain(int domid); + +/** + * @brief Resumes a domain. + * + * This function resumes the execution of a domain in the shutdown state. + * + * @param domid The ID of the domain to be resumed. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_resumedomain(int domid); + +/** + * @brief Retrieves the virtual CPU context for a specific domain and virtual CPU. + * This function resumes the execution of a domain in the shutdown state. + * + * @param domid The ID of the domain. + * @param vcpu The ID of the virtual CPU. + * @param[out] ctxt Pointer to the `vcpu_guest_context_t` structure where the + * virtual CPU context will be stored. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_getvcpucontext(int domid, int vcpu, vcpu_guest_context_t *ctxt); + +/** + * @brief Sets the virtual CPU context for a specified domain and virtual CPU. + * + * @param domid The ID of the domain. + * @param vcpu The ID of the virtual CPU. + * @param ctxt Pointer to the virtual CPU guest context structure. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_setvcpucontext(int domid, int vcpu, vcpu_guest_context_t *ctxt); + +/** + * @brief Retrieves information about a Xen domain. + * + * @param domid The ID of the Xen domain to retrieve information for. + * @param[out] dom_info Pointer to the structure where the retrieved + * domain information will be stored. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_getdomaininfo(int domid, xen_domctl_getdomaininfo_t *dom_info); + int xen_domctl_get_paging_mempool_size(int domid, uint64_t *size_mb); -int xen_domctl_set_paging_mempool_size(int domid, uint64_t size_mb); + +/** + * @brief Sets the paging mempool size for a specified domain. + * + * @param domid The ID of the domain. + * @param size The size of the paging mempool in bytes. + * @return 0 on success, or a negative error code on failure. + */ +int xen_domctl_set_paging_mempool_size(int domid, uint64_t size); + +/** + * @brief Sets the maximum memory for a specified domain. + * + * @param domid The domain ID of the domain to set the maximum memory for. + * @param max_memkb The maximum memory (in kilobytes) to set for the domain. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_max_mem(int domid, uint64_t max_memkb); + +/** + * @brief Sets the address size for a specified domain. + * + * @param domid The ID of the domain. + * @param addr_size The address size to be set. + * @return 0 on success, negative error code on failure. + */ int xen_domctl_set_address_size(int domid, int addr_size); + +/** + * @brief Set IOMEM permission for a domain. + * + * @param domid The ID of the domain for which IOMEM permission is being set. + * @param first_mfn The starting machine frame number of the memory range. + * @param nr_mfns The number of MFNs in the memory range. + * @param allow_access Flag indicating whether to allow or deny access to the + * specified memory range. A non-zero value allows access, + * while a zero value denies access. + * + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_iomem_permission(int domid, uint64_t first_mfn, uint64_t nr_mfns, uint8_t allow_access); + +/** + * @brief Maps a range of machine memory to a range of guest memory. + * + * @param domid The domain ID of the target domain. + * @param first_gfn The first guest frame number to map. + * @param first_mfn The first machine frame number to map. + * @param nr_mfns The number of machine frames to map. + * @param add_mapping Flag indicating whether to add or remove the mapping. + * + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_memory_mapping(int domid, uint64_t first_gfn, uint64_t first_mfn, uint64_t nr_mfns, uint32_t add_mapping); + +/** + * @brief Assign a device to a guest. Sets up IOMMU structures. + * + * @param domid The ID of the domain to which the device is to be assigned. + * @param dtdev_path The path of the device tree device to be assigned. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_assign_dt_device(int domid, char *dtdev_path); + +/** + * @brief Binds a physical IRQ to a specified domain. + * + * Only supports SPI IRQs for now. + * + * @param domid The ID of the domain to bind the IRQ to. + * @param machine_irq The machine IRQ number to bind. + * @param irq_type The type of IRQ to bind (PT_IRQ_TYPE_SPI). + * @param bus The PCI bus number of the device generating the IRQ. (optional) + * @param device The PCI device number generating the IRQ. (optional) + * @param intx The PCI INTx line number of the device generating the IRQ. (optional) + * @param isa_irq The ISA IRQ number to bind. (optional) + * @param spi The shared peripheral interrupt (SPI) number to bind. (optional) + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_bind_pt_irq(int domid, uint32_t machine_irq, uint8_t irq_type, uint8_t bus, uint8_t device, uint8_t intx, uint8_t isa_irq, uint16_t spi); + +/** + * @brief Set the maximum number of vCPUs for a domain. + * + * The parameter passed to XEN_DOMCTL_max_vcpus must match the value passed to + * XEN_DOMCTL_createdomain. This hypercall is in the process of being removed + * (once the failure paths in domain_create() have been improved), but is + * still required in the short term to allocate the vcpus themselves. + * + * @param domid The ID of the domain. + * @param max_vcpus Maximum number of vCPUs to set. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_max_vcpus(int domid, int max_vcpus); -int xen_domctl_createdomain(int domid, struct xen_domctl_createdomain *config); + +/** + * @brief Creates a new domain with the specified domain ID and configuration. + * + * NB. domid is an IN/OUT parameter for this operation. + * If it is specified as an invalid value (0 or >= DOMID_FIRST_RESERVED), + * an id is auto-allocated and returned. + + * @param[in,out] domid Pointer to domain ID of the new domain. + * @param config Pointer to a structure containing the configuration for the new domain. + * @return 0 on success, or a negative error code on failure. + */ +int xen_domctl_createdomain(int *domid, struct xen_domctl_createdomain *config); + +/** + * @brief Clean and invalidate caches associated with given region of + * guest memory. + * + * @param domid The ID of the domain for which the cache needs to be flushed. + * @param cacheflush A pointer to the `xen_domctl_cacheflush` structure that + * contains the cache flush parameters. + * @return Returns an integer value indicating the success or failure of the + * cache flush operation. + */ int xen_domctl_cacheflush(int domid, struct xen_domctl_cacheflush *cacheflush); + +/** + * @brief Destroys a Xen domain. + * + * @param domid The ID of the domain to be destroyed. + * @return 0 on success, or a negative error code on failure. + */ int xen_domctl_destroydomain(int domid); +/** + * @brief Retrieves information about a specific virtual CPU (vCPU) in a Xen domain. + * + * @param domid The ID of the domain. + * @param vcpu The index of the vCPU. + * @param[out] info Pointer to a structure to store the vCPU information. + * @return 0 on success, or a negative error code on failure. + */ +int xen_domctl_getvcpu(int domid, uint32_t vcpu, struct xen_domctl_getvcpuinfo *info); + #endif /* __XEN_DOM0_DOMCTL_H__ */ diff --git a/include/zephyr/xen/dom0/sysctl.h b/include/zephyr/xen/dom0/sysctl.h new file mode 100644 index 0000000000000..34792d3d406e8 --- /dev/null +++ b/include/zephyr/xen/dom0/sysctl.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief Xen System Control Interface + */ + +#ifndef __XEN_DOM0_SYSCTL_H__ +#define __XEN_DOM0_SYSCTL_H__ + +#include +#include + +#include + +/** + * @brief Retrieves information about the host system. + * + * @param[out] info A pointer to a `struct xen_sysctl_physinfo` structure where the + * retrieved information will be stored. + * @return 0 on success, or a negative error code on failure. + */ +int xen_sysctl_physinfo(struct xen_sysctl_physinfo *info); + +/** + * @brief Retrieves information about Xen domains. + * + * @param[out] domaininfo A pointer to the `xen_domctl_getdomaininfo` structure + * to store the retrieved domain information. + * @param first The first domain ID to retrieve information for. + * @param num The maximum number of domains to retrieve information for. + * @return 0 on success, or a negative error code on failure. + */ +int xen_sysctl_getdomaininfo(struct xen_domctl_getdomaininfo *domaininfo, + uint16_t first, uint16_t num); + +#endif /* __XEN_DOM0_SYSCTL_H__ */ diff --git a/include/zephyr/xen/dom0/version.h b/include/zephyr/xen/dom0/version.h new file mode 100644 index 0000000000000..30fc633160bfe --- /dev/null +++ b/include/zephyr/xen/dom0/version.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __XEN_DOM0_VERSION_H__ +#define __XEN_DOM0_VERSION_H__ +#include +#include +#include + +int xen_version(void); +int xen_version_extraversion(char *extra, int len); + +#endif /* __XEN_DOM0_VERSION_H__ */ diff --git a/include/zephyr/xen/events.h b/include/zephyr/xen/events.h index 4517eb2af3eb4..5804b15b16a7a 100644 --- a/include/zephyr/xen/events.h +++ b/include/zephyr/xen/events.h @@ -7,7 +7,7 @@ #ifndef __XEN_EVENTS_H__ #define __XEN_EVENTS_H__ -#include +#include #include @@ -26,7 +26,7 @@ typedef struct event_channel_handle evtchn_handle_t; int evtchn_status(evtchn_status_t *status); int evtchn_close(evtchn_port_t port); int evtchn_set_priority(evtchn_port_t port, uint32_t priority); -void notify_evtchn(evtchn_port_t port); +int notify_evtchn(evtchn_port_t port); /* * Allocate event-channel between caller and remote domain diff --git a/include/zephyr/xen/generic.h b/include/zephyr/xen/generic.h index 756bf80d88430..65b4098c32ea2 100644 --- a/include/zephyr/xen/generic.h +++ b/include/zephyr/xen/generic.h @@ -6,7 +6,7 @@ #ifndef __XEN_GENERIC_H__ #define __XEN_GENERIC_H__ -#include +#include #define XEN_PAGE_SIZE 4096 #define XEN_PAGE_SHIFT 12 diff --git a/include/zephyr/xen/gnttab.h b/include/zephyr/xen/gnttab.h index 4cfc65e580e92..ef54297148570 100644 --- a/include/zephyr/xen/gnttab.h +++ b/include/zephyr/xen/gnttab.h @@ -6,7 +6,7 @@ #ifndef __XEN_GNTTAB_H__ #define __XEN_GNTTAB_H__ -#include +#include /* * Assigns gref and permits access to 4K page for specific domain. @@ -41,22 +41,25 @@ int gnttab_end_access(grant_ref_t gref); int32_t gnttab_alloc_and_grant(void **map, bool readonly); /* - * Provides interface to acquire free page, that can be used for - * mapping of foreign frames. Should be freed by gnttab_put_page() - * after usage. + * Provides interface to acquire one or more pages that can be used for + * mapping of foreign frames. Should be freed by gnttab_put_pages() + * after use. + * + * @param npages - number of pages to allocate. * * @return - pointer to page start address, that can be used as host_addr * in struct gnttab_map_grant_ref, NULL on error. */ -void *gnttab_get_page(void); +void *gnttab_get_pages(unsigned int npages); /* - * Releases provided page, that was used for mapping foreign grant frame, + * Releases pages that were used for mapping foreign grant frames, * should be called after unmapping. * - * @param page_addr - pointer to start address of used page. + * @param page_addr - pointer to start address of allocated buffer. + * @param npages - number of pages allocated for the buffer. */ -void gnttab_put_page(void *page_addr); +int gnttab_put_pages(void *page_addr, unsigned int npages); /* * Maps foreign grant ref to Zephyr address space. @@ -66,15 +69,15 @@ void gnttab_put_page(void *page_addr); * @return - zero on success or negative errno on failure * also per-page status will be set in map_ops[i].status (GNTST_*) * - * To map foreign frame you need 4K-aligned 4K memory page, which will be - * used as host_addr for grant mapping - it should be acquired by gnttab_get_page() + * To map foreign frames you need 4K-aligned memory pages, which will be + * used as host_addr for grant mapping - it should be acquired by gnttab_get_pages() * function. */ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, unsigned int count); /* - * Unmap foreign grant refs. The gnttab_put_page() should be used after this for - * each page, that was successfully unmapped. + * Unmap foreign grant refs. The gnttab_put_pages() should be used after this for + * pages that were successfully unmapped. * * @param unmap_ops - array of prepared gnttab_unmap_grant_ref's for unmapping * @param count - number of grefs in unmap_ops array diff --git a/include/zephyr/xen/hvm.h b/include/zephyr/xen/hvm.h index 1848705975a7a..d033b220a2440 100644 --- a/include/zephyr/xen/hvm.h +++ b/include/zephyr/xen/hvm.h @@ -6,8 +6,8 @@ #ifndef __XEN_HVM_H__ #define __XEN_HVM_H__ -#include -#include +#include +#include #include diff --git a/include/zephyr/xen/memory.h b/include/zephyr/xen/memory.h index 636f09988566b..88be68ffde5db 100644 --- a/include/zephyr/xen/memory.h +++ b/include/zephyr/xen/memory.h @@ -3,9 +3,13 @@ * Copyright (c) 2023 EPAM Systems */ +#ifndef ZEPHYR_XEN_MEMORY_H_ +#define ZEPHYR_XEN_MEMORY_H_ + +#include +#include + #include -#include -#include /** * Add mapping for specified page frame in Xen domain physmap. @@ -64,3 +68,25 @@ int xendom_remove_from_physmap(int domid, xen_pfn_t gpfn); int xendom_populate_physmap(int domid, unsigned int extent_order, unsigned int nr_extents, unsigned int mem_flags, xen_pfn_t *extent_start); + +/** + * @brief Acquire a resource mapping for the Xen domain. + * + * Issues the XENMEM_acquire_resource hypercall to map a resource buffer + * (e.g., I/O request server, grant table, VM trace buffer) into the + * specified domain's physmap, or query its total size. + * + * @param domid Target domain identifier. Use DOMID_SELF for the calling domain. + * @param type Resource type identifier (e.g., XENMEM_resource_ioreq_server). + * @param id Resource-specific identifier (e.g., server ID or table ID). + * @param frame Starting frame number for mapping, or ignored if *nr_frames == 0. + * @param nr_frames [in,out] On input, number of frames to map; on return, + * number of frames actually mapped (or total frames if queried). + * @param frame_list Guest frame list buffer: input GFNs for HVM guests, + * output MFNs for PV guests. + * @return Zero on success, or a negative errno code on failure. + */ +int xendom_acquire_resource(domid_t domid, uint16_t type, uint32_t id, uint64_t frame, + uint32_t *nr_frames, xen_pfn_t *frame_list); + +#endif /* ZEPHYR_XEN_MEMORY_H_ */ diff --git a/include/zephyr/xen/public/arch-arm.h b/include/zephyr/xen/public/arch-arm.h deleted file mode 100644 index c3a8afe9a92e8..0000000000000 --- a/include/zephyr/xen/public/arch-arm.h +++ /dev/null @@ -1,476 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/****************************************************************************** - * arch-arm.h - * - * Guest OS interface to ARM Xen. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright 2011 (C) Citrix Systems - */ - -#ifndef __XEN_PUBLIC_ARCH_ARM_H__ -#define __XEN_PUBLIC_ARCH_ARM_H__ - -#include - -/* - * `incontents 50 arm_abi Hypercall Calling Convention - * - * A hypercall is issued using the ARM HVC instruction. - * - * A hypercall can take up to 5 arguments. These are passed in - * registers, the first argument in x0/r0 (for arm64/arm32 guests - * respectively irrespective of whether the underlying hypervisor is - * 32- or 64-bit), the second argument in x1/r1, the third in x2/r2, - * the forth in x3/r3 and the fifth in x4/r4. - * - * The hypercall number is passed in r12 (arm) or x16 (arm64). In both - * cases the relevant ARM procedure calling convention specifies this - * is an inter-procedure-call scratch register (e.g. for use in linker - * stubs). This use does not conflict with use during a hypercall. - * - * The HVC ISS must contain a Xen specific TAG: XEN_HYPERCALL_TAG. - * - * The return value is in x0/r0. - * - * The hypercall will clobber x16/r12 and the argument registers used - * by that hypercall (except r0 which is the return value) i.e. in - * addition to x16/r12 a 2 argument hypercall will clobber x1/r1 and a - * 4 argument hypercall will clobber x1/r1, x2/r2 and x3/r3. - * - * Parameter structs passed to hypercalls are laid out according to - * the Procedure Call Standard for the ARM Architecture (AAPCS, AKA - * EABI) and Procedure Call Standard for the ARM 64-bit Architecture - * (AAPCS64). Where there is a conflict the 64-bit standard should be - * used regardless of guest type. Structures which are passed as - * hypercall arguments are always little endian. - * - * All memory which is shared with other entities in the system - * (including the hypervisor and other guests) must reside in memory - * which is mapped as Normal Inner Write-Back Outer Write-Back Inner-Shareable. - * This applies to: - * - hypercall arguments passed via a pointer to guest memory. - * - memory shared via the grant table mechanism (including PV I/O - * rings etc). - * - memory shared with the hypervisor (struct shared_info, struct - * vcpu_info, the grant table, etc). - * - * Any cache allocation hints are acceptable. - */ - -/* - * `incontents 55 arm_hcall Supported Hypercalls - * - * Xen on ARM makes extensive use of hardware facilities and therefore - * only a subset of the potential hypercalls are required. - * - * Since ARM uses second stage paging any machine/physical addresses - * passed to hypercalls are Guest Physical Addresses (Intermediate - * Physical Addresses) unless otherwise noted. - * - * The following hypercalls (and sub operations) are supported on the - * ARM platform. Other hypercalls should be considered - * unavailable/unsupported. - * - * HYPERVISOR_memory_op - * All generic sub-operations - * - * HYPERVISOR_domctl - * All generic sub-operations, with the exception of: - * * XEN_DOMCTL_irq_permission (not yet implemented) - * - * HYPERVISOR_sched_op - * All generic sub-operations, with the exception of: - * * SCHEDOP_block -- prefer wfi hardware instruction - * - * HYPERVISOR_console_io - * All generic sub-operations - * - * HYPERVISOR_xen_version - * All generic sub-operations - * - * HYPERVISOR_event_channel_op - * All generic sub-operations - * - * HYPERVISOR_physdev_op - * No sub-operations are currently supported - * - * HYPERVISOR_sysctl - * All generic sub-operations, with the exception of: - * * XEN_SYSCTL_page_offline_op - * * XEN_SYSCTL_get_pmstat - * * XEN_SYSCTL_pm_op - * - * HYPERVISOR_hvm_op - * Exactly these sub-operations are supported: - * * HVMOP_set_param - * * HVMOP_get_param - * - * HYPERVISOR_grant_table_op - * All generic sub-operations - * - * HYPERVISOR_vcpu_op - * Exactly these sub-operations are supported: - * * VCPUOP_register_vcpu_info - * * VCPUOP_register_runstate_memory_area - * - * - * Other notes on the ARM ABI: - * - * - struct start_info is not exported to ARM guests. - * - * - struct shared_info is mapped by ARM guests using the - * HYPERVISOR_memory_op sub-op XENMEM_add_to_physmap, passing - * XENMAPSPACE_shared_info as space parameter. - * - * - All the per-cpu struct vcpu_info are mapped by ARM guests using the - * HYPERVISOR_vcpu_op sub-op VCPUOP_register_vcpu_info, including cpu0 - * struct vcpu_info. - * - * - The grant table is mapped using the HYPERVISOR_memory_op sub-op - * XENMEM_add_to_physmap, passing XENMAPSPACE_grant_table as space - * parameter. The memory range specified under the Xen compatible - * hypervisor node on device tree can be used as target gpfn for the - * mapping. - * - * - Xenstore is initialized by using the two hvm_params - * HVM_PARAM_STORE_PFN and HVM_PARAM_STORE_EVTCHN. They can be read - * with the HYPERVISOR_hvm_op sub-op HVMOP_get_param. - * - * - The paravirtualized console is initialized by using the two - * hvm_params HVM_PARAM_CONSOLE_PFN and HVM_PARAM_CONSOLE_EVTCHN. They - * can be read with the HYPERVISOR_hvm_op sub-op HVMOP_get_param. - * - * - Event channel notifications are delivered using the percpu GIC - * interrupt specified under the Xen compatible hypervisor node on - * device tree. - * - * - The device tree Xen compatible node is fully described under Linux - * at Documentation/devicetree/bindings/arm/xen.txt. - */ - -#define XEN_HYPERCALL_TAG 0XEA1 - -#define int64_aligned_t int64_t __aligned(8) -#define uint64_aligned_t uint64_t __aligned(8) - -#ifndef __ASSEMBLY__ -#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ - typedef union { type *p; unsigned long q; } \ - __guest_handle_ ## name; \ - typedef union { type *p; uint64_aligned_t q; } \ - __guest_handle_64_ ## name - -/* - * XEN_GUEST_HANDLE represents a guest pointer, when passed as a field - * in a struct in memory. On ARM is always 8 bytes sizes and 8 bytes - * aligned. - * XEN_GUEST_HANDLE_PARAM represents a guest pointer, when passed as an - * hypercall argument. It is 4 bytes on aarch32 and 8 bytes on aarch64. - */ -#define __DEFINE_XEN_GUEST_HANDLE(name, type) \ - ___DEFINE_XEN_GUEST_HANDLE(name, type); \ - ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type) -#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name) -#define __XEN_GUEST_HANDLE(name) __guest_handle_64_ ## name -#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name) -#define XEN_GUEST_HANDLE_PARAM(name) __guest_handle_ ## name -#define set_xen_guest_handle_raw(hnd, val) \ - do { \ - __typeof__(&(hnd)) _sxghr_tmp = &(hnd); \ - _sxghr_tmp->q = 0; \ - _sxghr_tmp->p = val; \ - } while (0) -#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val) - -typedef uint64_t xen_pfn_t; -#define PRI_xen_pfn PRIx64 -#define PRIu_xen_pfn PRIu64 - -/* - * Maximum number of virtual CPUs in legacy multi-processor guests. - * Only one. All other VCPUS must use VCPUOP_register_vcpu_info. - */ -#define XEN_LEGACY_MAX_VCPUS 1 - -typedef uint64_t xen_ulong_t; -#define PRI_xen_ulong PRIx64 - -#ifdef CONFIG_XEN_DOM0 -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -/* Anonymous union includes both 32- and 64-bit names (e.g., r0/x0). */ -# define __DECL_REG(n64, n32) union { \ - uint64_t n64; \ - uint32_t n32; \ -} -#else -/* Non-gcc sources must always use the proper 64-bit name (e.g., x0). */ -#define __DECL_REG(n64, n32) uint64_t n64 -#endif - -struct vcpu_guest_core_regs { - /* Aarch64 Aarch32 */ - __DECL_REG(x0, r0_usr); - __DECL_REG(x1, r1_usr); - __DECL_REG(x2, r2_usr); - __DECL_REG(x3, r3_usr); - __DECL_REG(x4, r4_usr); - __DECL_REG(x5, r5_usr); - __DECL_REG(x6, r6_usr); - __DECL_REG(x7, r7_usr); - __DECL_REG(x8, r8_usr); - __DECL_REG(x9, r9_usr); - __DECL_REG(x10, r10_usr); - __DECL_REG(x11, r11_usr); - __DECL_REG(x12, r12_usr); - - __DECL_REG(x13, sp_usr); - __DECL_REG(x14, lr_usr); - - __DECL_REG(x15, __unused_sp_hyp); - - __DECL_REG(x16, lr_irq); - __DECL_REG(x17, sp_irq); - - __DECL_REG(x18, lr_svc); - __DECL_REG(x19, sp_svc); - - __DECL_REG(x20, lr_abt); - __DECL_REG(x21, sp_abt); - - __DECL_REG(x22, lr_und); - __DECL_REG(x23, sp_und); - - __DECL_REG(x24, r8_fiq); - __DECL_REG(x25, r9_fiq); - __DECL_REG(x26, r10_fiq); - __DECL_REG(x27, r11_fiq); - __DECL_REG(x28, r12_fiq); - - __DECL_REG(x29, sp_fiq); - __DECL_REG(x30, lr_fiq); - - /* Return address and mode */ - __DECL_REG(pc64, pc32); /* ELR_EL2 */ - uint32_t cpsr; /* SPSR_EL2 */ - - union { - uint32_t spsr_el1; /* AArch64 */ - uint32_t spsr_svc; /* AArch32 */ - }; - - /* AArch32 guests only */ - uint32_t spsr_fiq, spsr_irq, spsr_und, spsr_abt; - - /* AArch64 guests only */ - uint64_t sp_el0; - uint64_t sp_el1, elr_el1; -}; -typedef struct vcpu_guest_core_regs vcpu_guest_core_regs_t; -DEFINE_XEN_GUEST_HANDLE(vcpu_guest_core_regs_t); - -#undef __DECL_REG - -struct vcpu_guest_context { -#define _VGCF_online 0 -#define VGCF_online (1 << _VGCF_online) - uint32_t flags; /* VGCF_* */ - - struct vcpu_guest_core_regs user_regs; /* Core CPU registers */ - - uint64_t sctlr; - uint64_t ttbcr, ttbr0, ttbr1; -}; -typedef struct vcpu_guest_context vcpu_guest_context_t; -DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); - -/* - * struct xen_arch_domainconfig's ABI is covered by - * XEN_DOMCTL_INTERFACE_VERSION. - */ -#define XEN_DOMCTL_CONFIG_GIC_NATIVE 0 -#define XEN_DOMCTL_CONFIG_GIC_V2 1 -#define XEN_DOMCTL_CONFIG_GIC_V3 2 - -#define XEN_DOMCTL_CONFIG_TEE_NONE 0 -#define XEN_DOMCTL_CONFIG_TEE_OPTEE 1 - -struct xen_arch_domainconfig { - /* IN/OUT */ - uint8_t gic_version; - /* IN */ - uint16_t tee_type; - /* IN */ - uint32_t nr_spis; - /* - * OUT - * Based on the property clock-frequency in the DT timer node. - * The property may be present when the bootloader/firmware doesn't - * set correctly CNTFRQ which hold the timer frequency. - * - * As it's not possible to trap this register, we have to replicate - * the value in the guest DT. - * - * = 0 => property not present - * > 0 => Value of the property - * - */ - uint32_t clock_frequency; -}; -#endif /* CONFIG_XEN_DOM0 */ - -struct arch_vcpu_info { -}; -typedef struct arch_vcpu_info arch_vcpu_info_t; - -struct arch_shared_info { -}; -typedef struct arch_shared_info arch_shared_info_t; -typedef uint64_t xen_callback_t; - -#endif /* __ASSEMBLY__ */ - -#ifdef CONFIG_XEN_DOM0 - -/* PSR bits (CPSR, SPSR) */ -#define PSR_THUMB (1 << 5) /* Thumb Mode enable */ -#define PSR_FIQ_MASK (1 << 6) /* Fast Interrupt mask */ -#define PSR_IRQ_MASK (1 << 7) /* Interrupt mask */ -#define PSR_ABT_MASK (1 << 8) /* Asynchronous Abort mask */ -#define PSR_BIG_ENDIAN (1 << 9) /* arm32: Big Endian Mode */ -#define PSR_DBG_MASK (1 << 9) /* arm64: Debug Exception mask */ -#define PSR_IT_MASK (0x0600fc00) /* Thumb If-Then Mask */ -#define PSR_JAZELLE (1<<24) /* Jazelle Mode */ - -/* 32 bit modes */ -#define PSR_MODE_USR 0x10 -#define PSR_MODE_FIQ 0x11 -#define PSR_MODE_IRQ 0x12 -#define PSR_MODE_SVC 0x13 -#define PSR_MODE_MON 0x16 -#define PSR_MODE_ABT 0x17 -#define PSR_MODE_HYP 0x1a -#define PSR_MODE_UND 0x1b -#define PSR_MODE_SYS 0x1f - -/* 64 bit modes */ -#define PSR_MODE_BIT 0x10 /* Set iff AArch32 */ -#define PSR_MODE_EL3h 0x0d -#define PSR_MODE_EL3t 0x0c -#define PSR_MODE_EL2h 0x09 -#define PSR_MODE_EL2t 0x08 -#define PSR_MODE_EL1h 0x05 -#define PSR_MODE_EL1t 0x04 -#define PSR_MODE_EL0t 0x00 - -#define PSR_GUEST32_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_SVC) -#define PSR_GUEST64_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_EL1h) - -#define SCTLR_GUEST_INIT xen_mk_ullong(0x00c50078) - -/* - * Virtual machine platform (memory layout, interrupts) - * - * These are defined for consistency between the tools and the - * hypervisor. Guests must not rely on these hardcoded values but - * should instead use the FDT. - */ - -/* Physical Address Space */ - -/* - * vGIC mappings: Only one set of mapping is used by the guest. - * Therefore they can overlap. - */ - -/* vGIC v2 mappings */ -#define GUEST_GICD_BASE xen_mk_ullong(0x03001000) -#define GUEST_GICD_SIZE xen_mk_ullong(0x00001000) -#define GUEST_GICC_BASE xen_mk_ullong(0x03002000) -#define GUEST_GICC_SIZE xen_mk_ullong(0x00002000) - -/* vGIC v3 mappings */ -#define GUEST_GICV3_GICD_BASE xen_mk_ullong(0x03001000) -#define GUEST_GICV3_GICD_SIZE xen_mk_ullong(0x00010000) - -#define GUEST_GICV3_RDIST_REGIONS 1 - -#define GUEST_GICV3_GICR0_BASE xen_mk_ullong(0x03020000) /* vCPU0..127 */ -#define GUEST_GICV3_GICR0_SIZE xen_mk_ullong(0x01000000) - -/* ACPI tables physical address */ -#define GUEST_ACPI_BASE xen_mk_ullong(0x20000000) -#define GUEST_ACPI_SIZE xen_mk_ullong(0x02000000) - -/* PL011 mappings */ -#define GUEST_PL011_BASE xen_mk_ullong(0x22000000) -#define GUEST_PL011_SIZE xen_mk_ullong(0x00001000) - -/* - * 16MB == 4096 pages reserved for guest to use as a region to map its - * grant table in. - */ -#define GUEST_GNTTAB_BASE xen_mk_ullong(0x38000000) -#define GUEST_GNTTAB_SIZE xen_mk_ullong(0x01000000) - -#define GUEST_MAGIC_BASE xen_mk_ullong(0x39000000) -#define GUEST_MAGIC_SIZE xen_mk_ullong(0x01000000) - -#define GUEST_RAM_BANKS 2 - -#define GUEST_RAM0_BASE xen_mk_ullong(0x40000000) /* 3GB of low RAM @ 1GB */ -#define GUEST_RAM0_SIZE xen_mk_ullong(0xc0000000) - -#define GUEST_RAM1_BASE xen_mk_ullong(0x0200000000) /* 1016GB of RAM @ 8GB */ -#define GUEST_RAM1_SIZE xen_mk_ullong(0xfe00000000) - -#define GUEST_RAM_BASE GUEST_RAM0_BASE /* Lowest RAM address */ -/* Largest amount of actual RAM, not including holes */ -#define GUEST_RAM_MAX (GUEST_RAM0_SIZE + GUEST_RAM1_SIZE) -/* Suitable for e.g. const uint64_t ramfoo[] = GUEST_RAM_BANK_FOOS; */ -#define GUEST_RAM_BANK_BASES { GUEST_RAM0_BASE, GUEST_RAM1_BASE } -#define GUEST_RAM_BANK_SIZES { GUEST_RAM0_SIZE, GUEST_RAM1_SIZE } - -/* Current supported guest VCPUs */ -#define GUEST_MAX_VCPUS 128 - -/* Interrupts */ -#define GUEST_TIMER_VIRT_PPI 27 -#define GUEST_TIMER_PHYS_S_PPI 29 -#define GUEST_TIMER_PHYS_NS_PPI 30 -#define GUEST_EVTCHN_PPI 31 - -#define GUEST_VPL011_SPI 32 - -/* PSCI functions */ -#define PSCI_cpu_suspend 0 -#define PSCI_cpu_off 1 -#define PSCI_cpu_on 2 -#define PSCI_migrate 3 - -#endif /* CONFIG_XEN_DOM0 */ - -#ifndef __ASSEMBLY__ -/* Stub definition of PMU structure */ -typedef struct xen_pmu_arch { uint8_t dummy; } xen_pmu_arch_t; -#endif /* __ASSEMBLY__ */ - -#endif /* __XEN_PUBLIC_ARCH_ARM_H__ */ diff --git a/include/zephyr/xen/public/domctl.h b/include/zephyr/xen/public/domctl.h deleted file mode 100644 index 9d856501a0983..0000000000000 --- a/include/zephyr/xen/public/domctl.h +++ /dev/null @@ -1,517 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/****************************************************************************** - * domctl.h - * - * Domain management operations. For use by node control stack. - * - * Copyright (c) 2002-2003, B Dragovic - * Copyright (c) 2002-2006, K Fraser - */ - -#ifndef __XEN_PUBLIC_DOMCTL_H__ -#define __XEN_PUBLIC_DOMCTL_H__ - -#ifndef CONFIG_XEN_DOM0 -#error "domctl operations are intended for use by node control tools only" -#endif - -#include "xen.h" -#include "event_channel.h" -#include "grant_table.h" -#include "memory.h" - -#define XEN_DOMCTL_INTERFACE_VERSION 0x00000015 - -/* - * NB. xen_domctl.domain is an IN/OUT parameter for this operation. - * If it is specified as an invalid value (0 or >= DOMID_FIRST_RESERVED), - * an id is auto-allocated and returned. - */ -/* XEN_DOMCTL_createdomain */ -struct xen_domctl_createdomain { - /* IN parameters */ - uint32_t ssidref; - xen_domain_handle_t handle; -/* Is this an HVM guest (as opposed to a PV guest)? */ -#define _XEN_DOMCTL_CDF_hvm 0 -#define XEN_DOMCTL_CDF_hvm (1U << _XEN_DOMCTL_CDF_hvm) -/* Use hardware-assisted paging if available? */ -#define _XEN_DOMCTL_CDF_hap 1 -#define XEN_DOMCTL_CDF_hap (1U << _XEN_DOMCTL_CDF_hap) -/* Should domain memory integrity be verified by tboot during Sx? */ -#define _XEN_DOMCTL_CDF_s3_integrity 2 -#define XEN_DOMCTL_CDF_s3_integrity (1U << _XEN_DOMCTL_CDF_s3_integrity) -/* Disable out-of-sync shadow page tables? */ -#define _XEN_DOMCTL_CDF_oos_off 3 -#define XEN_DOMCTL_CDF_oos_off (1U << _XEN_DOMCTL_CDF_oos_off) -/* Is this a xenstore domain? */ -#define _XEN_DOMCTL_CDF_xs_domain 4 -#define XEN_DOMCTL_CDF_xs_domain (1U << _XEN_DOMCTL_CDF_xs_domain) -/* Should this domain be permitted to use the IOMMU? */ -#define _XEN_DOMCTL_CDF_iommu 5 -#define XEN_DOMCTL_CDF_iommu (1U << _XEN_DOMCTL_CDF_iommu) -#define _XEN_DOMCTL_CDF_nested_virt 6 -#define XEN_DOMCTL_CDF_nested_virt (1U << _XEN_DOMCTL_CDF_nested_virt) -/* Should we expose the vPMU to the guest? */ -#define XEN_DOMCTL_CDF_vpmu (1U << 7) - -/* Max XEN_DOMCTL_CDF_* constant. Used for ABI checking. */ -#define XEN_DOMCTL_CDF_MAX XEN_DOMCTL_CDF_vpmu - - uint32_t flags; - -#define _XEN_DOMCTL_IOMMU_no_sharept 0 -#define XEN_DOMCTL_IOMMU_no_sharep (1U << _XEN_DOMCTL_IOMMU_no_sharept) - -/* Max XEN_DOMCTL_IOMMU_* constant. Used for ABI checking. */ -#define XEN_DOMCTL_IOMMU_MAX XEN_DOMCTL_IOMMU_no_sharept - - uint32_t iommu_opts; - - /* - * Various domain limits, which impact the quantity of resources - * (global mapping space, xenheap, etc) a guest may consume. For - * max_grant_frames and max_maptrack_frames, < 0 means "use the - * default maximum value in the hypervisor". - */ - uint32_t max_vcpus; - uint32_t max_evtchn_port; - int32_t max_grant_frames; - int32_t max_maptrack_frames; - -/* Grant version, use low 4 bits. */ -#define XEN_DOMCTL_GRANT_version_mask 0xf -#define XEN_DOMCTL_GRANT_version(v) ((v) & XEN_DOMCTL_GRANT_version_mask) - - uint32_t grant_opts; - - /* Per-vCPU buffer size in bytes. 0 to disable. */ - uint32_t vmtrace_size; - - /* CPU pool to use; specify 0 or a specific existing pool */ - uint32_t cpupool_id; - - struct xen_arch_domainconfig arch; -}; - -/* XEN_DOMCTL_getdomaininfo */ -struct xen_domctl_getdomaininfo { - /* OUT variables. */ - domid_t domain; /* Also echoed in domctl.domain */ - uint16_t pad1; -/* Domain is scheduled to die. */ -#define _XEN_DOMINF_dying 0 -#define XEN_DOMINF_dying (1U << _XEN_DOMINF_dying) -/* Domain is an HVM guest (as opposed to a PV guest). */ -#define _XEN_DOMINF_hvm_guest 1 -#define XEN_DOMINF_hvm_guest (1U << _XEN_DOMINF_hvm_guest) -/* The guest OS has shut down. */ -#define _XEN_DOMINF_shutdown 2 -#define XEN_DOMINF_shutdown (1U << _XEN_DOMINF_shutdown) -/* Currently paused by control software. */ -#define _XEN_DOMINF_paused 3 -#define XEN_DOMINF_paused (1U << _XEN_DOMINF_paused) -/* Currently blocked pending an event. */ -#define _XEN_DOMINF_blocked 4 -#define XEN_DOMINF_blocked (1U << _XEN_DOMINF_blocked) -/* Domain is currently running. */ -#define _XEN_DOMINF_running 5 -#define XEN_DOMINF_running (1U << _XEN_DOMINF_running) -/* Being debugged. */ -#define _XEN_DOMINF_debugged 6 -#define XEN_DOMINF_debugged (1U << _XEN_DOMINF_debugged) -/* domain is a xenstore domain */ -#define _XEN_DOMINF_xs_domain 7 -#define XEN_DOMINF_xs_domain (1U << _XEN_DOMINF_xs_domain) -/* domain has hardware assisted paging */ -#define _XEN_DOMINF_hap 8 -#define XEN_DOMINF_hap (1U << _XEN_DOMINF_hap) -/* XEN_DOMINF_shutdown guest-supplied code. */ -#define XEN_DOMINF_shutdownmask 255 -#define XEN_DOMINF_shutdownshift 16 - uint32_t flags; /* XEN_DOMINF_* */ - uint64_aligned_t tot_pages; - uint64_aligned_t max_pages; - uint64_aligned_t outstanding_pages; - uint64_aligned_t shr_pages; - uint64_aligned_t paged_pages; - uint64_aligned_t shared_info_frame; /* GMFN of shared_info struct */ - uint64_aligned_t cpu_time; - uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ -#define XEN_INVALID_MAX_VCPU_ID (~0U) /* Domain has no vcpus? */ - uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ - uint32_t ssidref; - xen_domain_handle_t handle; - uint32_t cpupool; - uint8_t gpaddr_bits; /* Guest physical address space size. */ - uint8_t pad2[7]; - struct xen_arch_domainconfig arch_config; -}; -typedef struct xen_domctl_getdomaininfo xen_domctl_getdomaininfo_t; -DEFINE_XEN_GUEST_HANDLE(xen_domctl_getdomaininfo_t); - -/* - * Control shadow pagetables operation - */ -/* XEN_DOMCTL_shadow_op */ - -/* Memory allocation accessors. */ -#define XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION 30 -#define XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION 31 - -struct xen_domctl_shadow_op_stats { - uint32_t fault_count; - uint32_t dirty_count; -}; - -struct xen_domctl_shadow_op { - /* IN variables. */ - uint32_t op; /* XEN_DOMCTL_SHADOW_OP_* */ - - /* OP_ENABLE: XEN_DOMCTL_SHADOW_ENABLE_* */ - /* OP_PEAK / OP_CLEAN: XEN_DOMCTL_SHADOW_LOGDIRTY_* */ - uint32_t mode; - - /* OP_GET_ALLOCATION / OP_SET_ALLOCATION */ - uint32_t mb; /* Shadow memory allocation in MB */ - - /* OP_PEEK / OP_CLEAN */ - XEN_GUEST_HANDLE_64(uint8_t) dirty_bitmap; - uint64_aligned_t pages; /* Size of buffer. Updated with actual size. */ - struct xen_domctl_shadow_op_stats stats; -}; - -/* XEN_DOMCTL_max_mem */ -struct xen_domctl_max_mem { - /* IN variables. */ - uint64_aligned_t max_memkb; -}; - -/* XEN_DOMCTL_setvcpucontext */ -/* XEN_DOMCTL_getvcpucontext */ -struct xen_domctl_vcpucontext { - uint32_t vcpu; /* IN */ - - XEN_GUEST_HANDLE_64(vcpu_guest_context_t) ctxt; /* IN/OUT */ -}; - -/* - * XEN_DOMCTL_max_vcpus: - * - * The parameter passed to XEN_DOMCTL_max_vcpus must match the value passed to - * XEN_DOMCTL_createdomain. This hypercall is in the process of being removed - * (once the failure paths in domain_create() have been improved), but is - * still required in the short term to allocate the vcpus themselves. - */ -struct xen_domctl_max_vcpus { - uint32_t max; /* maximum number of vcpus */ -}; - -/* XEN_DOMCTL_scheduler_op */ -/* Scheduler types. */ -/* #define XEN_SCHEDULER_SEDF 4 (Removed) */ -#define XEN_SCHEDULER_CREDIT 5 -#define XEN_SCHEDULER_CREDIT2 6 -#define XEN_SCHEDULER_ARINC653 7 -#define XEN_SCHEDULER_RTDS 8 -#define XEN_SCHEDULER_NULL 9 - -struct xen_domctl_sched_credit { - uint16_t weight; - uint16_t cap; -}; - -struct xen_domctl_sched_credit2 { - uint16_t weight; - uint16_t cap; -}; - -struct xen_domctl_sched_rtds { - uint32_t period; - uint32_t budget; -/* Can this vCPU execute beyond its reserved amount of time? */ -#define _XEN_DOMCTL_SCHEDRT_extra 0 -#define XEN_DOMCTL_SCHEDRT_extra (1U<<_XEN_DOMCTL_SCHEDRT_extra) - uint32_t flags; -}; - -typedef struct xen_domctl_schedparam_vcpu { - union { - struct xen_domctl_sched_credit credit; - struct xen_domctl_sched_credit2 credit2; - struct xen_domctl_sched_rtds rtds; - } u; - uint32_t vcpuid; -} xen_domctl_schedparam_vcpu_t; -DEFINE_XEN_GUEST_HANDLE(xen_domctl_schedparam_vcpu_t); - -/* - * Set or get info? - * For schedulers supporting per-vcpu settings (e.g., RTDS): - * XEN_DOMCTL_SCHEDOP_putinfo sets params for all vcpus; - * XEN_DOMCTL_SCHEDOP_getinfo gets default params; - * XEN_DOMCTL_SCHEDOP_put(get)vcpuinfo sets (gets) params of vcpus; - * - * For schedulers not supporting per-vcpu settings: - * XEN_DOMCTL_SCHEDOP_putinfo sets params for all vcpus; - * XEN_DOMCTL_SCHEDOP_getinfo gets domain-wise params; - * XEN_DOMCTL_SCHEDOP_put(get)vcpuinfo returns error; - */ -#define XEN_DOMCTL_SCHEDOP_putinfo 0 -#define XEN_DOMCTL_SCHEDOP_getinfo 1 -#define XEN_DOMCTL_SCHEDOP_putvcpuinfo 2 -#define XEN_DOMCTL_SCHEDOP_getvcpuinfo 3 -struct xen_domctl_scheduler_op { - uint32_t sched_id; /* XEN_SCHEDULER_* */ - uint32_t cmd; /* XEN_DOMCTL_SCHEDOP_* */ - /* IN/OUT */ - union { - struct xen_domctl_sched_credit credit; - struct xen_domctl_sched_credit2 credit2; - struct xen_domctl_sched_rtds rtds; - struct { - XEN_GUEST_HANDLE_64(xen_domctl_schedparam_vcpu_t) vcpus; - /* - * IN: Number of elements in vcpus array. - * OUT: Number of processed elements of vcpus array. - */ - uint32_t nr_vcpus; - uint32_t padding; - } v; - } u; -}; - -/* XEN_DOMCTL_iomem_permission */ -struct xen_domctl_iomem_permission { - uint64_aligned_t first_mfn;/* first page (physical page number) in range */ - uint64_aligned_t nr_mfns; /* number of pages in range (>0) */ - uint8_t allow_access; /* allow (!0) or deny (0) access to range? */ -}; - -/* XEN_DOMCTL_set_address_size */ -/* XEN_DOMCTL_get_address_size */ -struct xen_domctl_address_size { - uint32_t size; -}; - -/* Assign a device to a guest. Sets up IOMMU structures. */ -/* XEN_DOMCTL_assign_device */ -/* - * XEN_DOMCTL_test_assign_device: Pass DOMID_INVALID to find out whether the - * given device is assigned to any DomU at all. Pass a specific domain ID to - * find out whether the given device can be assigned to that domain. - */ -/* - * XEN_DOMCTL_deassign_device: The behavior of this DOMCTL differs - * between the different type of device: - * - PCI device (XEN_DOMCTL_DEV_PCI) will be reassigned to DOM0 - * - DT device (XEN_DOMCTL_DEV_DT) will left unassigned. DOM0 - * will have to call XEN_DOMCTL_assign_device in order to use the - * device. - */ -#define XEN_DOMCTL_DEV_PCI 0 -#define XEN_DOMCTL_DEV_DT 1 -struct xen_domctl_assign_device { - /* IN */ - uint32_t dev; /* XEN_DOMCTL_DEV_* */ - uint32_t flags; -#define XEN_DOMCTL_DEV_RDM_RELAXED 1 /* assign only */ - union { - struct { - uint32_t machine_sbdf; /* machine PCI ID of assigned device */ - } pci; - struct { - uint32_t size; /* Length of the path */ - - XEN_GUEST_HANDLE_64(char) path; /* path to the device tree node */ - } dt; - } u; -}; - -/* Pass-through interrupts: bind real irq -> hvm devfn. */ -/* XEN_DOMCTL_bind_pt_irq */ -/* XEN_DOMCTL_unbind_pt_irq */ -enum pt_irq_type { - PT_IRQ_TYPE_PCI, - PT_IRQ_TYPE_ISA, - PT_IRQ_TYPE_MSI, - PT_IRQ_TYPE_MSI_TRANSLATE, - PT_IRQ_TYPE_SPI, /* ARM: valid range 32-1019 */ -}; -struct xen_domctl_bind_pt_irq { - uint32_t machine_irq; - uint32_t irq_type; /* enum pt_irq_type */ - - union { - struct { - uint8_t isa_irq; - } isa; - struct { - uint8_t bus; - uint8_t device; - uint8_t intx; - } pci; - struct { - uint8_t gvec; - uint32_t gflags; -#define XEN_DOMCTL_VMSI_X86_DEST_ID_MASK 0x0000ff -#define XEN_DOMCTL_VMSI_X86_RH_MASK 0x000100 -#define XEN_DOMCTL_VMSI_X86_DM_MASK 0x000200 -#define XEN_DOMCTL_VMSI_X86_DELIV_MASK 0x007000 -#define XEN_DOMCTL_VMSI_X86_TRIG_MASK 0x008000 -#define XEN_DOMCTL_VMSI_X86_UNMASKED 0x010000 - - uint64_aligned_t gtable; - } msi; - struct { - uint16_t spi; - } spi; - } u; -}; - - -/* Bind machine I/O address range -> HVM address range. */ -/* XEN_DOMCTL_memory_mapping */ -/* Returns - * - zero success, everything done - * - -E2BIG passed in nr_mfns value too large for the implementation - * - positive partial success for the first page frames (with - * less than nr_mfns), requiring re-invocation by the - * caller after updating inputs - * - negative error; other than -E2BIG - */ -#define DPCI_ADD_MAPPING 1 -#define DPCI_REMOVE_MAPPING 0 -struct xen_domctl_memory_mapping { - uint64_aligned_t first_gfn; /* first page (hvm guest phys page) in range */ - uint64_aligned_t first_mfn; /* first page (machine page) in range */ - uint64_aligned_t nr_mfns; /* number of pages in range (>0) */ - uint32_t add_mapping; /* add or remove mapping */ - uint32_t padding; /* padding for 64-bit aligned structure */ -}; - -/* - * ARM: Clean and invalidate caches associated with given region of - * guest memory. - */ -struct xen_domctl_cacheflush { - /* IN: page range to flush. */ - xen_pfn_t start_pfn, nr_pfns; -}; - -/* - * XEN_DOMCTL_get_paging_mempool_size / XEN_DOMCTL_set_paging_mempool_size. - * - * Get or set the paging memory pool size. The size is in bytes. - * - * This is a dedicated pool of memory for Xen to use while managing the guest, - * typically containing pagetables. As such, there is an implementation - * specific minimum granularity. - * - * The set operation can fail mid-way through the request (e.g. Xen running - * out of memory, no free memory to reclaim from the pool, etc.). - */ -struct xen_domctl_paging_mempool { - uint64_aligned_t size; /* Size in bytes. */ -}; - -struct xen_domctl { - uint32_t cmd; -#define XEN_DOMCTL_createdomain 1 -#define XEN_DOMCTL_destroydomain 2 -#define XEN_DOMCTL_pausedomain 3 -#define XEN_DOMCTL_unpausedomain 4 -#define XEN_DOMCTL_getdomaininfo 5 -#define XEN_DOMCTL_setvcpuaffinity 9 -#define XEN_DOMCTL_shadow_op 10 -#define XEN_DOMCTL_max_mem 11 -#define XEN_DOMCTL_setvcpucontext 12 -#define XEN_DOMCTL_getvcpucontext 13 -#define XEN_DOMCTL_getvcpuinfo 14 -#define XEN_DOMCTL_max_vcpus 15 -#define XEN_DOMCTL_scheduler_op 16 -#define XEN_DOMCTL_setdomainhandle 17 -#define XEN_DOMCTL_setdebugging 18 -#define XEN_DOMCTL_irq_permission 19 -#define XEN_DOMCTL_iomem_permission 20 -#define XEN_DOMCTL_ioport_permission 21 -#define XEN_DOMCTL_hypercall_init 22 -#define XEN_DOMCTL_settimeoffset 24 -#define XEN_DOMCTL_getvcpuaffinity 25 -#define XEN_DOMCTL_real_mode_area 26 /* Obsolete PPC only */ -#define XEN_DOMCTL_resumedomain 27 -#define XEN_DOMCTL_sendtrigger 28 -#define XEN_DOMCTL_subscribe 29 -#define XEN_DOMCTL_gethvmcontext 33 -#define XEN_DOMCTL_sethvmcontext 34 -#define XEN_DOMCTL_set_address_size 35 -#define XEN_DOMCTL_get_address_size 36 -#define XEN_DOMCTL_assign_device 37 -#define XEN_DOMCTL_bind_pt_irq 38 -#define XEN_DOMCTL_memory_mapping 39 -#define XEN_DOMCTL_ioport_mapping 40 -#define XEN_DOMCTL_set_ext_vcpucontext 42 -#define XEN_DOMCTL_get_ext_vcpucontext 43 -#define XEN_DOMCTL_set_opt_feature 44 /* Obsolete IA64 only */ -#define XEN_DOMCTL_test_assign_device 45 -#define XEN_DOMCTL_set_target 46 -#define XEN_DOMCTL_deassign_device 47 -#define XEN_DOMCTL_unbind_pt_irq 48 -#define XEN_DOMCTL_get_device_group 50 -#define XEN_DOMCTL_debug_op 54 -#define XEN_DOMCTL_gethvmcontext_partial 55 -#define XEN_DOMCTL_vm_event_op 56 -#define XEN_DOMCTL_mem_sharing_op 57 -#define XEN_DOMCTL_gettscinfo 59 -#define XEN_DOMCTL_settscinfo 60 -#define XEN_DOMCTL_getpageframeinfo3 61 -#define XEN_DOMCTL_setvcpuextstate 62 -#define XEN_DOMCTL_getvcpuextstate 63 -#define XEN_DOMCTL_set_access_required 64 -#define XEN_DOMCTL_audit_p2m 65 -#define XEN_DOMCTL_set_virq_handler 66 -#define XEN_DOMCTL_set_broken_page_p2m 67 -#define XEN_DOMCTL_setnodeaffinity 68 -#define XEN_DOMCTL_getnodeaffinity 69 -#define XEN_DOMCTL_cacheflush 71 -#define XEN_DOMCTL_get_vcpu_msrs 72 -#define XEN_DOMCTL_set_vcpu_msrs 73 -#define XEN_DOMCTL_setvnumainfo 74 -#define XEN_DOMCTL_psr_cmt_op 75 -#define XEN_DOMCTL_monitor_op 77 -#define XEN_DOMCTL_psr_alloc 78 -#define XEN_DOMCTL_soft_reset 79 -#define XEN_DOMCTL_vuart_op 81 -#define XEN_DOMCTL_get_cpu_policy 82 -#define XEN_DOMCTL_set_cpu_policy 83 -#define XEN_DOMCTL_vmtrace_op 84 -#define XEN_DOMCTL_get_paging_mempool_size 85 -#define XEN_DOMCTL_set_paging_mempool_size 86 -#define XEN_DOMCTL_gdbsx_guestmemio 1000 -#define XEN_DOMCTL_gdbsx_pausevcpu 1001 -#define XEN_DOMCTL_gdbsx_unpausevcpu 1002 -#define XEN_DOMCTL_gdbsx_domstatus 1003 - uint32_t interface_version; /* XEN_DOMCTL_INTERFACE_VERSION */ - domid_t domain; - uint16_t _pad[3]; - union { - struct xen_domctl_createdomain createdomain; - struct xen_domctl_getdomaininfo getdomaininfo; - struct xen_domctl_max_mem max_mem; - struct xen_domctl_vcpucontext vcpucontext; - struct xen_domctl_max_vcpus max_vcpus; - struct xen_domctl_scheduler_op scheduler_op; - struct xen_domctl_iomem_permission iomem_permission; - struct xen_domctl_address_size address_size; - struct xen_domctl_assign_device assign_device; - struct xen_domctl_bind_pt_irq bind_pt_irq; - struct xen_domctl_memory_mapping memory_mapping; - struct xen_domctl_cacheflush cacheflush; - struct xen_domctl_paging_mempool paging_mempool; - uint8_t pad[128]; - } u; -}; -typedef struct xen_domctl xen_domctl_t; -DEFINE_XEN_GUEST_HANDLE(xen_domctl_t); - -#endif /* __XEN_PUBLIC_DOMCTL_H__ */ diff --git a/include/zephyr/xen/public/event_channel.h b/include/zephyr/xen/public/event_channel.h deleted file mode 100644 index 7ce1ae0d38f96..0000000000000 --- a/include/zephyr/xen/public/event_channel.h +++ /dev/null @@ -1,216 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/****************************************************************************** - * event_channel.h - * - * Event channels between domains. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright (c) 2003-2004, K A Fraser. - */ - -#ifndef __XEN_PUBLIC_EVENT_CHANNEL_H__ -#define __XEN_PUBLIC_EVENT_CHANNEL_H__ - -#include "xen.h" - -/* - * `incontents 150 evtchn Event Channels - * - * Event channels are the basic primitive provided by Xen for event - * notifications. An event is the Xen equivalent of a hardware - * interrupt. They essentially store one bit of information, the event - * of interest is signalled by transitioning this bit from 0 to 1. - * - * Notifications are received by a guest via an upcall from Xen, - * indicating when an event arrives (setting the bit). Further - * notifications are masked until the bit is cleared again (therefore, - * guests must check the value of the bit after re-enabling event - * delivery to ensure no missed notifications). - * - * Event notifications can be masked by setting a flag; this is - * equivalent to disabling interrupts and can be used to ensure - * atomicity of certain operations in the guest kernel. - * - * Event channels are represented by the evtchn_* fields in - * struct shared_info and struct vcpu_info. - */ - -#define EVTCHNOP_bind_interdomain 0 -#define EVTCHNOP_bind_virq 1 -#define EVTCHNOP_bind_pirq 2 -#define EVTCHNOP_close 3 -#define EVTCHNOP_send 4 -#define EVTCHNOP_status 5 -#define EVTCHNOP_alloc_unbound 6 -#define EVTCHNOP_bind_ipi 7 -#define EVTCHNOP_bind_vcpu 8 -#define EVTCHNOP_unmask 9 -#define EVTCHNOP_reset 10 -#define EVTCHNOP_init_control 11 -#define EVTCHNOP_expand_array 12 -#define EVTCHNOP_set_priority 13 -#ifdef __XEN__ -#define EVTCHNOP_reset_cont 14 -#endif - -typedef uint32_t evtchn_port_t; -DEFINE_XEN_GUEST_HANDLE(evtchn_port_t); - -/* - * EVTCHNOP_alloc_unbound: Allocate a port in domain and mark as - * accepting interdomain bindings from domain . A fresh port - * is allocated in and returned as . - * NOTES: - * 1. If the caller is unprivileged then must be DOMID_SELF. - * 2. may be DOMID_SELF, allowing loopback connections. - */ -struct evtchn_alloc_unbound { - /* IN parameters */ - domid_t dom, remote_dom; - /* OUT parameters */ - evtchn_port_t port; -}; -typedef struct evtchn_alloc_unbound evtchn_alloc_unbound_t; - -/* - * EVTCHNOP_bind_interdomain: Construct an interdomain event channel between - * the calling domain and . must identify - * a port that is unbound and marked as accepting bindings from the calling - * domain. A fresh port is allocated in the calling domain and returned as - * . - * - * In case the peer domain has already tried to set our event channel - * pending, before it was bound, EVTCHNOP_bind_interdomain always sets - * the local event channel pending. - * - * The usual pattern of use, in the guest's upcall (or subsequent - * handler) is as follows: (Re-enable the event channel for subsequent - * signalling and then) check for the existence of whatever condition - * is being waited for by other means, and take whatever action is - * needed (if any). - * - * NOTES: - * 1. may be DOMID_SELF, allowing loopback connections. - */ -struct evtchn_bind_interdomain { - /* IN parameters. */ - domid_t remote_dom; - evtchn_port_t remote_port; - /* OUT parameters. */ - evtchn_port_t local_port; -}; -typedef struct evtchn_bind_interdomain evtchn_bind_interdomain_t; - -/* - * EVTCHNOP_close: Close a local event channel . If the channel is - * interdomain then the remote end is placed in the unbound state - * (EVTCHNSTAT_unbound), awaiting a new connection. - */ -struct evtchn_close { - /* IN parameters. */ - evtchn_port_t port; -}; -typedef struct evtchn_close evtchn_close_t; - -/* - * EVTCHNOP_send: Send an event to the remote end of the channel whose local - * endpoint is . - */ -struct evtchn_send { - /* IN parameters. */ - evtchn_port_t port; -}; -typedef struct evtchn_send evtchn_send_t; - -/* - * EVTCHNOP_status: Get the current status of the communication channel which - * has an endpoint at . - * NOTES: - * 1. may be specified as DOMID_SELF. - * 2. Only a sufficiently-privileged domain may obtain the status of an event - * channel for which is not DOMID_SELF. - */ -struct evtchn_status { - /* IN parameters */ - domid_t dom; - evtchn_port_t port; - /* OUT parameters */ -#define EVTCHNSTAT_closed 0 /* Channel is not in use. */ -#define EVTCHNSTAT_unbound 1 /* Channel is waiting interdom connection.*/ -#define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */ -#define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */ -#define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */ -#define EVTCHNSTAT_ipi 5 /* Channel is bound to a virtual IPI line */ - uint32_t status; - uint32_t vcpu; /* VCPU to which this channel is bound. */ - union { - struct { - domid_t dom; - } unbound; /* EVTCHNSTAT_unbound */ - struct { - domid_t dom; - evtchn_port_t port; - } interdomain; /* EVTCHNSTAT_interdomain */ - uint32_t pirq; /* EVTCHNSTAT_pirq */ - uint32_t virq; /* EVTCHNSTAT_virq */ - } u; -}; -typedef struct evtchn_status evtchn_status_t; - -/* - * EVTCHNOP_unmask: Unmask the specified local event-channel port and deliver - * a notification to the appropriate VCPU if an event is pending. - */ -struct evtchn_unmask { - /* IN parameters. */ - evtchn_port_t port; -}; -typedef struct evtchn_unmask evtchn_unmask_t; - -/* - * EVTCHNOP_reset: Close all event channels associated with specified domain. - * NOTES: - * 1. may be specified as DOMID_SELF. - * 2. Only a sufficiently-privileged domain may specify other than DOMID_SELF. - * 3. Destroys all control blocks and event array, resets event channel - * operations to 2-level ABI if called with == DOMID_SELF and FIFO - * ABI was used. Guests should not bind events during EVTCHNOP_reset call - * as these events are likely to be lost. - */ -struct evtchn_reset { - /* IN parameters. */ - domid_t dom; -}; -typedef struct evtchn_reset evtchn_reset_t; - -/* - * EVTCHNOP_set_priority: set the priority for an event channel. - */ -struct evtchn_set_priority { - /* IN parameters. */ - evtchn_port_t port; - uint32_t priority; -}; -typedef struct evtchn_set_priority evtchn_set_priority_t; - -#define EVTCHN_2L_NR_CHANNELS (sizeof(xen_ulong_t) * sizeof(xen_ulong_t) * 64) - -#endif /* __XEN_PUBLIC_EVENT_CHANNEL_H__ */ diff --git a/include/zephyr/xen/public/grant_table.h b/include/zephyr/xen/public/grant_table.h deleted file mode 100644 index 81803a1daa3f6..0000000000000 --- a/include/zephyr/xen/public/grant_table.h +++ /dev/null @@ -1,415 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/****************************************************************************** - * grant_table.h - * - * Interface for granting foreign access to page frames, and receiving - * page-ownership transfers. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright (c) 2004, K A Fraser - */ - -#ifndef __XEN_PUBLIC_GRANT_TABLE_H__ -#define __XEN_PUBLIC_GRANT_TABLE_H__ - -#include "xen.h" - -/* - * `incontents 150 gnttab Grant Tables - * - * Xen's grant tables provide a generic mechanism to memory sharing - * between domains. This shared memory interface underpins the split - * device drivers for block and network IO. - * - * Each domain has its own grant table. This is a data structure that - * is shared with Xen; it allows the domain to tell Xen what kind of - * permissions other domains have on its pages. Entries in the grant - * table are identified by grant references. A grant reference is an - * integer, which indexes into the grant table. It acts as a - * capability which the grantee can use to perform operations on the - * granter's memory. - * - * This capability-based system allows shared-memory communications - * between unprivileged domains. A grant reference also encapsulates - * the details of a shared page, removing the need for a domain to - * know the real machine address of a page it is sharing. This makes - * it possible to share memory correctly with domains running in - * fully virtualised memory. - */ - -/*********************************** - * GRANT TABLE REPRESENTATION - */ - -/* Some rough guidelines on accessing and updating grant-table entries - * in a concurrency-safe manner. For more information, Linux contains a - * reference implementation for guest OSes (drivers/xen/grant_table.c, see - * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=drivers/xen/grant-table.c;hb=HEAD - * - * NB. WMB is a no-op on current-generation x86 processors. However, a - * compiler barrier will still be required. - * - * Introducing a valid entry into the grant table: - * 1. Write ent->domid. - * 2. Write ent->frame: - * GTF_permit_access: Frame to which access is permitted. - * GTF_accept_transfer: Pseudo-phys frame slot being filled by new - * frame, or zero if none. - * 3. Write memory barrier (WMB). - * 4. Write ent->flags, inc. valid type. - * - * Invalidating an unused GTF_permit_access entry: - * 1. flags = ent->flags. - * 2. Observe that !(flags & (GTF_reading|GTF_writing)). - * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). - * NB. No need for WMB as reuse of entry is control-dependent on success of - * step 3, and all architectures guarantee ordering of ctrl-dep writes. - * - * Invalidating an in-use GTF_permit_access entry: - * This cannot be done directly. Request assistance from the domain controller - * which can set a timeout on the use of a grant entry and take necessary - * action. (NB. This is not yet implemented!). - * - * Invalidating an unused GTF_accept_transfer entry: - * 1. flags = ent->flags. - * 2. Observe that !(flags & GTF_transfer_committed). [*] - * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). - * NB. No need for WMB as reuse of entry is control-dependent on success of - * step 3, and all architectures guarantee ordering of ctrl-dep writes. - * [*] If GTF_transfer_committed is set then the grant entry is 'committed'. - * The guest must /not/ modify the grant entry until the address of the - * transferred frame is written. It is safe for the guest to spin waiting - * for this to occur (detect by observing GTF_transfer_completed in - * ent->flags). - * - * Invalidating a committed GTF_accept_transfer entry: - * 1. Wait for (ent->flags & GTF_transfer_completed). - * - * Changing a GTF_permit_access from writable to read-only: - * Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing. - * - * Changing a GTF_permit_access from read-only to writable: - * Use SMP-safe bit-setting instruction. - */ - -/* - * Reference to a grant entry in a specified domain's grant table. - */ -typedef uint32_t grant_ref_t; - -/* - * A grant table comprises a packed array of grant entries in one or more - * page frames shared between Xen and a guest. - * [XEN]: This field is written by Xen and read by the sharing guest. - * [GST]: This field is written by the guest and read by Xen. - */ - -/* - * Version 1 of the grant table entry structure is maintained purely - * for backwards compatibility. New guests should use version 2. - */ -#if CONFIG_XEN_INTERFACE_VERSION < 0x0003020a -#define grant_entry_v1 grant_entry -#define grant_entry_v1_t grant_entry_t -#endif -struct grant_entry_v1 { - /* GTF_xxx: various type and flag information. [XEN,GST] */ - uint16_t flags; - /* The domain being granted foreign privileges. [GST] */ - domid_t domid; - /* - * GTF_permit_access: GFN that @domid is allowed to map and access. [GST] - * GTF_accept_transfer: GFN that @domid is allowed to transfer into. [GST] - * GTF_transfer_completed: MFN whose ownership transferred by @domid - * (non-translated guests only). [XEN] - */ - uint32_t frame; -}; -typedef struct grant_entry_v1 grant_entry_v1_t; - -/* The first few grant table entries will be preserved across grant table - * version changes and may be pre-populated at domain creation by tools. - */ -#define GNTTAB_NR_RESERVED_ENTRIES 8 -#define GNTTAB_RESERVED_CONSOLE 0 -#define GNTTAB_RESERVED_XENSTORE 1 - -/* - * Type of grant entry. - * GTF_invalid: This grant entry grants no privileges. - * GTF_permit_access: Allow @domid to map/access @frame. - * GTF_accept_transfer: Allow @domid to transfer ownership of one page frame - * to this guest. Xen writes the page number to @frame. - * GTF_transitive: Allow @domid to transitively access a subrange of - * @trans_grant in @trans_domid. No mappings are allowed. - */ -#define GTF_invalid (0U << 0) -#define GTF_permit_access (1U << 0) -#define GTF_accept_transfer (2U << 0) -#define GTF_transitive (3U << 0) -#define GTF_type_mask (3U << 0) - -/* - * Subflags for GTF_permit_access and GTF_transitive. - * GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST] - * GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN] - * GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN] - * Further subflags for GTF_permit_access only. - * GTF_PAT, GTF_PWT, GTF_PCD: (x86) cache attribute flags to be used for - * mappings of the grant [GST] - * GTF_sub_page: Grant access to only a subrange of the page. @domid - * will only be allowed to copy from the grant, and not - * map it. [GST] - */ -#define _GTF_readonly (2) -#define GTF_readonly (1U << _GTF_readonly) -#define _GTF_reading (3) -#define GTF_reading (1U << _GTF_reading) -#define _GTF_writing (4) -#define GTF_writing (1U << _GTF_writing) -#define _GTF_PWT (5) -#define GTF_PWT (1U << _GTF_PWT) -#define _GTF_PCD (6) -#define GTF_PCD (1U << _GTF_PCD) -#define _GTF_PAT (7) -#define GTF_PAT (1U << _GTF_PAT) -#define _GTF_sub_page (8) -#define GTF_sub_page (1U << _GTF_sub_page) - -/* - * Subflags for GTF_accept_transfer: - * GTF_transfer_committed: Xen sets this flag to indicate that it is committed - * to transferring ownership of a page frame. When a guest sees this flag - * it must /not/ modify the grant entry until GTF_transfer_completed is - * set by Xen. - * GTF_transfer_completed: It is safe for the guest to spin-wait on this flag - * after reading GTF_transfer_committed. Xen will always write the frame - * address, followed by ORing this flag, in a timely manner. - */ -#define _GTF_transfer_committed (2) -#define GTF_transfer_committed (1U << _GTF_transfer_committed) -#define _GTF_transfer_completed (3) -#define GTF_transfer_completed (1U << _GTF_transfer_completed) - -/*********************************** - * GRANT TABLE QUERIES AND USES - */ - -/* ` enum neg_errnoval - * ` HYPERVISOR_grant_table_op(enum grant_table_op cmd, - * ` void *args, - * ` unsigned int count) - * ` - * - * @args points to an array of a per-command data structure. The array - * has @count members - */ - -/* ` enum grant_table_op { // GNTTABOP_* => struct gnttab_* */ -#define GNTTABOP_map_grant_ref 0 -#define GNTTABOP_unmap_grant_ref 1 -#define GNTTABOP_setup_table 2 -#define GNTTABOP_dump_table 3 -#define GNTTABOP_transfer 4 -#define GNTTABOP_copy 5 -#define GNTTABOP_query_size 6 -#define GNTTABOP_unmap_and_replace 7 -#if CONFIG_XEN_INTERFACE_VERSION >= 0x0003020a -#define GNTTABOP_set_version 8 -#define GNTTABOP_get_status_frames 9 -#define GNTTABOP_get_version 10 -#define GNTTABOP_swap_grant_ref 11 -#define GNTTABOP_cache_flush 12 -#endif /* CONFIG_XEN_INTERFACE_VERSION */ -/* ` } */ - -/* - * Handle to track a mapping created via a grant reference. - */ -typedef uint32_t grant_handle_t; - -/* - * GNTTABOP_map_grant_ref: Map the grant entry (,) for access - * by devices and/or host CPUs. If successful, is a tracking number - * that must be presented later to destroy the mapping(s). On error, - * is a negative status code. - * NOTES: - * 1. If GNTMAP_device_map is specified then is the address - * via which I/O devices may access the granted frame. - * 2. If GNTMAP_host_map is specified then a mapping will be added at - * either a host virtual address in the current address space, or at - * a PTE at the specified machine address. The type of mapping to - * perform is selected through the GNTMAP_contains_pte flag, and the - * address is specified in . - * 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a - * host mapping is destroyed by other means then it is *NOT* guaranteed - * to be accounted to the correct grant reference! - */ -struct gnttab_map_grant_ref { - /* IN parameters. */ - uint64_t host_addr; - uint32_t flags; /* GNTMAP_* */ - grant_ref_t ref; - domid_t dom; - /* OUT parameters. */ - int16_t status; /* => enum grant_status */ - grant_handle_t handle; - uint64_t dev_bus_addr; -}; -typedef struct gnttab_map_grant_ref gnttab_map_grant_ref_t; -DEFINE_XEN_GUEST_HANDLE(gnttab_map_grant_ref_t); - -/* - * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings - * tracked by . If or is zero, that - * field is ignored. If non-zero, they must refer to a device/host mapping - * that is tracked by - * NOTES: - * 1. The call may fail in an undefined manner if either mapping is not - * tracked by . - * 3. After executing a batch of unmaps, it is guaranteed that no stale - * mappings will remain in the device or host TLBs. - */ -struct gnttab_unmap_grant_ref { - /* IN parameters. */ - uint64_t host_addr; - uint64_t dev_bus_addr; - grant_handle_t handle; - /* OUT parameters. */ - int16_t status; /* => enum grant_status */ -}; -typedef struct gnttab_unmap_grant_ref gnttab_unmap_grant_ref_t; -DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_grant_ref_t); - -/* - * GNTTABOP_setup_table: Set up a grant table for comprising at least - * pages. The frame addresses are written to the . - * Only addresses are written, even if the table is larger. - * NOTES: - * 1. may be specified as DOMID_SELF. - * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. - * 3. Xen may not support more than a single grant-table page per domain. - */ -struct gnttab_setup_table { - /* IN parameters. */ - domid_t dom; - uint32_t nr_frames; - - /* OUT parameters. */ - int16_t status; /* => enum grant_status */ -#if CONFIG_XEN_INTERFACE_VERSION < 0x00040300 - XEN_GUEST_HANDLE(ulong) frame_list; -#else - XEN_GUEST_HANDLE(xen_pfn_t) frame_list; -#endif -}; -typedef struct gnttab_setup_table gnttab_setup_table_t; -DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_t); - -/* - * GNTTABOP_query_size: Query the current and maximum sizes of the shared - * grant table. - * NOTES: - * 1. may be specified as DOMID_SELF. - * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. - */ -struct gnttab_query_size { - /* IN parameters. */ - domid_t dom; - /* OUT parameters. */ - uint32_t nr_frames; - uint32_t max_nr_frames; - int16_t status; /* => enum grant_status */ -}; -typedef struct gnttab_query_size gnttab_query_size_t; -DEFINE_XEN_GUEST_HANDLE(gnttab_query_size_t); - -/* - * Bitfield values for gnttab_map_grant_ref.flags. - */ - /* Map the grant entry for access by I/O devices. */ -#define _GNTMAP_device_map (0) -#define GNTMAP_device_map (1<<_GNTMAP_device_map) - /* Map the grant entry for access by host CPUs. */ -#define _GNTMAP_host_map (1) -#define GNTMAP_host_map (1<<_GNTMAP_host_map) - /* Accesses to the granted frame will be restricted to read-only access. */ -#define _GNTMAP_readonly (2) -#define GNTMAP_readonly (1<<_GNTMAP_readonly) - /* - * GNTMAP_host_map subflag: - * 0 => The host mapping is usable only by the guest OS. - * 1 => The host mapping is usable by guest OS + current application. - */ -#define _GNTMAP_application_map (3) -#define GNTMAP_application_map (1<<_GNTMAP_application_map) - - /* - * GNTMAP_contains_pte subflag: - * 0 => This map request contains a host virtual address. - * 1 => This map request contains the machine address of the PTE to update. - */ -#define _GNTMAP_contains_pte (4) -#define GNTMAP_contains_pte (1<<_GNTMAP_contains_pte) - -/* - * Bits to be placed in guest kernel available PTE bits (architecture - * dependent; only supported when XENFEAT_gnttab_map_avail_bits is set). - */ -#define _GNTMAP_guest_avail0 (16) -#define GNTMAP_guest_avail_mask ((uint32_t)~0 << _GNTMAP_guest_avail0) - -/* - * Values for error status returns. All errors are -ve. - */ -/* ` enum grant_status { */ -#define GNTST_okay (0) /* Normal return */ -#define GNTST_general_error (-1) /* General undefined error */ -#define GNTST_bad_domain (-2) /* Unrecognsed domain id */ -#define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref */ -#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle */ -#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map */ -#define GNTST_bad_dev_addr (-6) /* Inappropriate device address to unmap */ -#define GNTST_no_device_space (-7) /* Out of space in I/O MMU */ -#define GNTST_permission_denied (-8) /* Not enough privilege for operation */ -#define GNTST_bad_page (-9) /* Specified page was invalid for op */ -#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary */ -#define GNTST_address_too_big (-11) /* transfer page address too large */ -#define GNTST_eagain (-12) /* Operation not done; try again */ -/* ` } */ - -#define GNTTABOP_error_msgs { \ - "okay", \ - "undefined error", \ - "unrecognised domain id", \ - "invalid grant reference", \ - "invalid mapping handle", \ - "invalid virtual address", \ - "invalid device address", \ - "no spare translation slot in the I/O MMU", \ - "permission denied", \ - "bad page", \ - "copy arguments cross page boundary", \ - "page address size too large", \ - "operation not done; try again" \ -} - -#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */ diff --git a/include/zephyr/xen/public/hvm/hvm_op.h b/include/zephyr/xen/public/hvm/hvm_op.h deleted file mode 100644 index 89a63dc1291ef..0000000000000 --- a/include/zephyr/xen/public/hvm/hvm_op.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/* - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright (c) 2007, Keir Fraser - */ - -#ifndef __XEN_PUBLIC_HVM_HVM_OP_H__ -#define __XEN_PUBLIC_HVM_HVM_OP_H__ - -#include "../xen.h" - -/* Get/set subcommands: extra argument == pointer to xen_hvm_param struct. */ -#define HVMOP_set_param 0 -#define HVMOP_get_param 1 -struct xen_hvm_param { - domid_t domid; /* IN */ - uint16_t pad; - uint32_t index; /* IN */ - uint64_t value; /* IN/OUT */ -}; -typedef struct xen_hvm_param xen_hvm_param_t; -DEFINE_XEN_GUEST_HANDLE(xen_hvm_param_t); - -#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */ diff --git a/include/zephyr/xen/public/hvm/params.h b/include/zephyr/xen/public/hvm/params.h deleted file mode 100644 index 1222b2ffdefda..0000000000000 --- a/include/zephyr/xen/public/hvm/params.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/* - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright (c) 2007, Keir Fraser - */ - -#ifndef __XEN_PUBLIC_HVM_PARAMS_H__ -#define __XEN_PUBLIC_HVM_PARAMS_H__ - -#include "hvm_op.h" - -/* - * These are not used by Xen. They are here for convenience of HVM-guest - * xenbus implementations. - */ -#define HVM_PARAM_STORE_PFN 1 -#define HVM_PARAM_STORE_EVTCHN 2 - -/* Console debug shared memory ring and event channel */ -#define HVM_PARAM_CONSOLE_PFN 17 -#define HVM_PARAM_CONSOLE_EVTCHN 18 - -#endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */ diff --git a/include/zephyr/xen/public/io/console.h b/include/zephyr/xen/public/io/console.h deleted file mode 100644 index 5178bb086e86f..0000000000000 --- a/include/zephyr/xen/public/io/console.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/****************************************************************************** - * console.h - * - * Console I/O interface for Xen guest OSes. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright (c) 2005, Keir Fraser - */ - -#ifndef __XEN_PUBLIC_IO_CONSOLE_H__ -#define __XEN_PUBLIC_IO_CONSOLE_H__ - -typedef uint32_t XENCONS_RING_IDX; - -#define MASK_XENCONS_IDX(idx, ring) ((idx) & (sizeof(ring)-1)) - -struct xencons_interface { - char in[1024]; - char out[2048]; - XENCONS_RING_IDX in_cons, in_prod; - XENCONS_RING_IDX out_cons, out_prod; -}; - -#ifdef XEN_WANT_FLEX_CONSOLE_RING -#include "ring.h" -DEFINE_XEN_FLEX_RING(xencons); -#endif - -#endif /* __XEN_PUBLIC_IO_CONSOLE_H__ */ diff --git a/include/zephyr/xen/public/memory.h b/include/zephyr/xen/public/memory.h deleted file mode 100644 index 2baf69ef2391a..0000000000000 --- a/include/zephyr/xen/public/memory.h +++ /dev/null @@ -1,164 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/****************************************************************************** - * memory.h - * - * Memory reservation and information. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright (c) 2005, Keir Fraser - */ - -#ifndef __XEN_PUBLIC_MEMORY_H__ -#define __XEN_PUBLIC_MEMORY_H__ - -#include "xen.h" - -#define XENMEM_populate_physmap 6 - -struct xen_memory_reservation { - - /* - * XENMEM_increase_reservation: - * OUT: MFN (*not* GMFN) bases of extents that were allocated - * XENMEM_decrease_reservation: - * IN: GMFN bases of extents to free - * XENMEM_populate_physmap: - * IN: GPFN bases of extents to populate with memory - * OUT: GMFN bases of extents that were allocated - * (NB. This command also updates the mach_to_phys translation table) - * XENMEM_claim_pages: - * IN: must be zero - */ - XEN_GUEST_HANDLE(xen_pfn_t) extent_start; - - /* Number of extents, and size/alignment of each (2^extent_order pages). */ - xen_ulong_t nr_extents; - unsigned int extent_order; - -#if CONFIG_XEN_INTERFACE_VERSION >= 0x00030209 - /* XENMEMF flags. */ - unsigned int mem_flags; -#else - unsigned int address_bits; -#endif - - /* - * Domain whose reservation is being changed. - * Unprivileged domains can specify only DOMID_SELF. - */ - domid_t domid; -}; -typedef struct xen_memory_reservation xen_memory_reservation_t; -DEFINE_XEN_GUEST_HANDLE(xen_memory_reservation_t); - -/* A batched version of add_to_physmap. */ -#define XENMEM_add_to_physmap_batch 23 -struct xen_add_to_physmap_batch { - /* IN */ - /* Which domain to change the mapping for. */ - domid_t domid; - uint16_t space; /* => enum phys_map_space */ - - /* Number of pages to go through */ - uint16_t size; - -#if CONFIG_XEN_INTERFACE_VERSION < 0x00040700 - domid_t foreign_domid; /* IFF gmfn_foreign. Should be 0 for other spaces. */ -#else - union xen_add_to_physmap_batch_extra { - domid_t foreign_domid; /* gmfn_foreign */ - uint16_t res0; /* All the other spaces. Should be 0 */ - } u; -#endif - - /* Indexes into space being mapped. */ - XEN_GUEST_HANDLE(xen_ulong_t) idxs; - - /* GPFN in domid where the source mapping page should appear. */ - XEN_GUEST_HANDLE(xen_pfn_t) gpfns; - - /* OUT */ - /* Per index error code. */ - XEN_GUEST_HANDLE(int) errs; -}; -typedef struct xen_add_to_physmap_batch xen_add_to_physmap_batch_t; -DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_batch_t); - - -#define XENMAPSPACE_shared_info 0 /* shared info page */ -#define XENMAPSPACE_grant_table 1 /* grant table page */ -#define XENMAPSPACE_gmfn 2 /* GMFN */ - -/* GMFN range, XENMEM_add_to_physmap only.*/ -#define XENMAPSPACE_gmfn_range 3 - -/* GMFN from another dom, XENMEM_add_to_physmap_batch only. */ -#define XENMAPSPACE_gmfn_foreign 4 - -/* - * Device mmio region ARM only; the region is mapped in Stage-2 using the - * Normal Memory Inner/Outer Write-Back Cacheable memory attribute. - */ -#define XENMAPSPACE_dev_mmio 5 - -/* - * Sets the GPFN at which a particular page appears in the specified guest's - * physical address space (translated guests only). - * arg == addr of xen_add_to_physmap_t. - */ -#define XENMEM_add_to_physmap 7 -struct xen_add_to_physmap { - /* Which domain to change the mapping for. */ - domid_t domid; - - /* Number of pages to go through for gmfn_range */ - uint16_t size; - - unsigned int space; /* => enum phys_map_space */ - -#define XENMAPIDX_grant_table_status 0x80000000 - - /* Index into space being mapped. */ - xen_ulong_t idx; - - /* GPFN in domid where the source mapping page should appear. */ - xen_pfn_t gpfn; -}; -typedef struct xen_add_to_physmap xen_add_to_physmap_t; -DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_t); - -/* - * Unmaps the page appearing at a particular GPFN from the specified guest's - * physical address space (translated guests only). - * arg == addr of xen_remove_from_physmap_t. - */ -#define XENMEM_remove_from_physmap 15 -struct xen_remove_from_physmap { - /* Which domain to change the mapping for. */ - domid_t domid; - - /* GPFN of the current mapping of the page. */ - xen_pfn_t gpfn; -}; -typedef struct xen_remove_from_physmap xen_remove_from_physmap_t; -DEFINE_XEN_GUEST_HANDLE(xen_remove_from_physmap_t); - -#endif /* __XEN_PUBLIC_MEMORY_H__ */ diff --git a/include/zephyr/xen/public/sched.h b/include/zephyr/xen/public/sched.h deleted file mode 100644 index e9a29a172df18..0000000000000 --- a/include/zephyr/xen/public/sched.h +++ /dev/null @@ -1,201 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/****************************************************************************** - * sched.h - * - * Scheduler state interactions - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright (c) 2005, Keir Fraser - */ - -#ifndef __XEN_PUBLIC_SCHED_H__ -#define __XEN_PUBLIC_SCHED_H__ - -#include "event_channel.h" - -/* - * `incontents 150 sched Guest Scheduler Operations - * - * The SCHEDOP interface provides mechanisms for a guest to interact - * with the scheduler, including yield, blocking and shutting itself - * down. - */ - -/* - * The prototype for this hypercall is: - * ` long HYPERVISOR_sched_op(enum sched_op cmd, void *arg, ...) - * - * @cmd == SCHEDOP_??? (scheduler operation). - * @arg == Operation-specific extra argument(s), as described below. - * ... == Additional Operation-specific extra arguments, described below. - * - * Versions of Xen prior to 3.0.2 provided only the following legacy version - * of this hypercall, supporting only the commands yield, block and shutdown: - * long sched_op(int cmd, unsigned long arg) - * @cmd == SCHEDOP_??? (scheduler operation). - * @arg == 0 (SCHEDOP_yield and SCHEDOP_block) - * == SHUTDOWN_* code (SCHEDOP_shutdown) - * - * This legacy version is available to new guests as: - * ` long HYPERVISOR_sched_op_compat(enum sched_op cmd, unsigned long arg) - */ - -/* - * Voluntarily yield the CPU. - * @arg == NULL. - */ -#define SCHEDOP_yield 0 - -/* - * Block execution of this VCPU until an event is received for processing. - * If called with event upcalls masked, this operation will atomically - * reenable event delivery and check for pending events before blocking the - * VCPU. This avoids a "wakeup waiting" race. - * @arg == NULL. - */ -#define SCHEDOP_block 1 - -/* - * Halt execution of this domain (all VCPUs) and notify the system controller. - * @arg == pointer to sched_shutdown_t structure. - * - * If the sched_shutdown_t reason is SHUTDOWN_suspend then - * x86 PV guests must also set RDX (EDX for 32-bit guests) to the MFN - * of the guest's start info page. RDX/EDX is the third hypercall - * argument. - * - * In addition, which reason is SHUTDOWN_suspend this hypercall - * returns 1 if suspend was cancelled or the domain was merely - * checkpointed, and 0 if it is resuming in a new domain. - */ -#define SCHEDOP_shutdown 2 - -/* - * Poll a set of event-channel ports. Return when one or more are pending. An - * optional timeout may be specified. - * @arg == pointer to sched_poll_t structure. - */ -#define SCHEDOP_poll 3 - -/* - * Declare a shutdown for another domain. The main use of this function is - * in interpreting shutdown requests and reasons for fully-virtualized - * domains. A para-virtualized domain may use SCHEDOP_shutdown directly. - * @arg == pointer to sched_remote_shutdown_t structure. - */ -#define SCHEDOP_remote_shutdown 4 - -/* - * Latch a shutdown code, so that when the domain later shuts down it - * reports this code to the control tools. - * @arg == sched_shutdown_t, as for SCHEDOP_shutdown. - */ -#define SCHEDOP_shutdown_code 5 - -/* - * Setup, poke and destroy a domain watchdog timer. - * @arg == pointer to sched_watchdog_t structure. - * With id == 0, setup a domain watchdog timer to cause domain shutdown - * after timeout, returns watchdog id. - * With id != 0 and timeout == 0, destroy domain watchdog timer. - * With id != 0 and timeout != 0, poke watchdog timer and set new timeout. - */ -#define SCHEDOP_watchdog 6 - -/* - * Override the current vcpu affinity by pinning it to one physical cpu or - * undo this override restoring the previous affinity. - * @arg == pointer to sched_pin_override_t structure. - * - * A negative pcpu value will undo a previous pin override and restore the - * previous cpu affinity. - * This call is allowed for the hardware domain only and requires the cpu - * to be part of the domain's cpupool. - */ -#define SCHEDOP_pin_override 7 - -struct sched_shutdown { - unsigned int reason; /* SHUTDOWN_* => enum sched_shutdown_reason */ -}; -typedef struct sched_shutdown sched_shutdown_t; -DEFINE_XEN_GUEST_HANDLE(sched_shutdown_t); - -struct sched_poll { - XEN_GUEST_HANDLE(evtchn_port_t) ports; - unsigned int nr_ports; - uint64_t timeout; -}; -typedef struct sched_poll sched_poll_t; -DEFINE_XEN_GUEST_HANDLE(sched_poll_t); - -struct sched_remote_shutdown { - domid_t domain_id; /* Remote domain ID */ - unsigned int reason; /* SHUTDOWN_* => enum sched_shutdown_reason */ -}; -typedef struct sched_remote_shutdown sched_remote_shutdown_t; -DEFINE_XEN_GUEST_HANDLE(sched_remote_shutdown_t); - -struct sched_watchdog { - uint32_t id; /* watchdog ID */ - uint32_t timeout; /* timeout */ -}; -typedef struct sched_watchdog sched_watchdog_t; -DEFINE_XEN_GUEST_HANDLE(sched_watchdog_t); - -struct sched_pin_override { - int32_t pcpu; -}; -typedef struct sched_pin_override sched_pin_override_t; -DEFINE_XEN_GUEST_HANDLE(sched_pin_override_t); - -/* - * Reason codes for SCHEDOP_shutdown. These may be interpreted by control - * software to determine the appropriate action. For the most part, Xen does - * not care about the shutdown code. - */ -/* Domain exited normally. Clean up and kill. */ -#define SHUTDOWN_poweroff 0 - -/* Clean up, kill, and then restart. */ -#define SHUTDOWN_reboot 1 - -/* Clean up, save suspend info, kill. */ -#define SHUTDOWN_suspend 2 - -/* Tell controller we've crashed. */ -#define SHUTDOWN_crash 3 - -/* Restart because watchdog time expired. */ -#define SHUTDOWN_watchdog 4 - -/* - * Domain asked to perform 'soft reset' for it. The expected behavior is to - * reset internal Xen state for the domain returning it to the point where it - * was created but leaving the domain's memory contents and vCPU contexts - * intact. This will allow the domain to start over and set up all Xen specific - * interfaces again. - */ -#define SHUTDOWN_soft_reset 5 - -/* Maximum valid shutdown reason. */ -#define SHUTDOWN_MAX 5 - -#endif /* __XEN_PUBLIC_SCHED_H__ */ diff --git a/include/zephyr/xen/public/xen.h b/include/zephyr/xen/public/xen.h deleted file mode 100644 index 3018a92d7dc33..0000000000000 --- a/include/zephyr/xen/public/xen.h +++ /dev/null @@ -1,397 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -/****************************************************************************** - * xen.h - * - * Guest OS interface to Xen. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Copyright (c) 2004, K A Fraser - */ - -#ifndef __XEN_PUBLIC_XEN_H__ -#define __XEN_PUBLIC_XEN_H__ - -#if defined(CONFIG_ARM64) -#include "arch-arm.h" -#else -#error "Unsupported architecture" -#endif - -#ifndef __ASSEMBLY__ -/* Guest handles for primitive C types. */ -DEFINE_XEN_GUEST_HANDLE(char); -__DEFINE_XEN_GUEST_HANDLE(uchar, unsigned char); -DEFINE_XEN_GUEST_HANDLE(int); -__DEFINE_XEN_GUEST_HANDLE(uint, unsigned int); -#if CONFIG_XEN_INTERFACE_VERSION < 0x00040300 -DEFINE_XEN_GUEST_HANDLE(long); -__DEFINE_XEN_GUEST_HANDLE(ulong, unsigned long); -#endif -DEFINE_XEN_GUEST_HANDLE(void); - -DEFINE_XEN_GUEST_HANDLE(uint8_t); -DEFINE_XEN_GUEST_HANDLE(uint64_t); -DEFINE_XEN_GUEST_HANDLE(xen_pfn_t); -DEFINE_XEN_GUEST_HANDLE(xen_ulong_t); - -/* Define a variable length array (depends on compiler). */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#define XEN_FLEX_ARRAY_DIM -#elif defined(__GNUC__) -#define XEN_FLEX_ARRAY_DIM 0 -#else -#define XEN_FLEX_ARRAY_DIM 1 /* variable size */ -#endif - -/* Turn a plain number into a C unsigned (long (long)) constant. */ -#define __xen_mk_uint(x) x ## U -#define __xen_mk_ulong(x) x ## UL -#ifndef __xen_mk_ullong -#define __xen_mk_ullong(x) x ## ULL -#endif -#define xen_mk_uint(x) __xen_mk_uint(x) -#define xen_mk_ulong(x) __xen_mk_ulong(x) -#define xen_mk_ullong(x) __xen_mk_ullong(x) - -#else - -/* In assembly code we cannot use C numeric constant suffixes. */ -#define xen_mk_uint(x) x -#define xen_mk_ulong(x) x -#define xen_mk_ullong(x) x - -#endif - -/* - * HYPERCALLS - */ - -/* `incontents 100 hcalls List of hypercalls - * ` enum hypercall_num { // __HYPERVISOR_* => HYPERVISOR_*() - */ - -#define __HYPERVISOR_set_trap_table 0 -#define __HYPERVISOR_mmu_update 1 -#define __HYPERVISOR_set_gdt 2 -#define __HYPERVISOR_stack_switch 3 -#define __HYPERVISOR_set_callbacks 4 -#define __HYPERVISOR_fpu_taskswitch 5 - -/* compat since 0x00030101 */ -#define __HYPERVISOR_sched_op_compat 6 -#define __HYPERVISOR_platform_op 7 -#define __HYPERVISOR_set_debugreg 8 -#define __HYPERVISOR_get_debugreg 9 -#define __HYPERVISOR_update_descriptor 10 -#define __HYPERVISOR_memory_op 12 -#define __HYPERVISOR_multicall 13 -#define __HYPERVISOR_update_va_mapping 14 -#define __HYPERVISOR_set_timer_op 15 - -/* compat since 0x00030202 */ -#define __HYPERVISOR_event_channel_op_compat 16 -#define __HYPERVISOR_xen_version 17 -#define __HYPERVISOR_console_io 18 - -/* compat since 0x00030202 */ -#define __HYPERVISOR_physdev_op_compat 19 -#define __HYPERVISOR_grant_table_op 20 -#define __HYPERVISOR_vm_assist 21 -#define __HYPERVISOR_update_va_mapping_otherdomain 22 - -/* x86 only */ -#define __HYPERVISOR_iret 23 -#define __HYPERVISOR_vcpu_op 24 - -/* x86/64 only */ -#define __HYPERVISOR_set_segment_base 25 -#define __HYPERVISOR_mmuext_op 26 -#define __HYPERVISOR_xsm_op 27 -#define __HYPERVISOR_nmi_op 28 -#define __HYPERVISOR_sched_op 29 -#define __HYPERVISOR_callback_op 30 -#define __HYPERVISOR_xenoprof_op 31 -#define __HYPERVISOR_event_channel_op 32 -#define __HYPERVISOR_physdev_op 33 -#define __HYPERVISOR_hvm_op 34 -#define __HYPERVISOR_sysctl 35 -#define __HYPERVISOR_domctl 36 -#define __HYPERVISOR_kexec_op 37 -#define __HYPERVISOR_tmem_op 38 -#define __HYPERVISOR_argo_op 39 -#define __HYPERVISOR_xenpmu_op 40 -#define __HYPERVISOR_dm_op 41 -#define __HYPERVISOR_hypfs_op 42 - -/* - * ` int - * ` HYPERVISOR_console_io(unsigned int cmd, - * ` unsigned int count, - * ` char buffer[]); - * - * @cmd: Command (see below) - * @count: Size of the buffer to read/write - * @buffer: Pointer in the guest memory - * - * List of commands: - * - * * CONSOLEIO_write: Write the buffer to Xen console. - * For the hardware domain, all the characters in the buffer will - * be written. Characters will be printed directly to the console. - * For all the other domains, only the printable characters will be - * written. Characters may be buffered until a newline (i.e '\n') is - * found. - * @return 0 on success, otherwise return an error code. - * * CONSOLEIO_read: Attempts to read up to @count characters from Xen - * console. The maximum buffer size (i.e. @count) supported is 2GB. - * @return the number of characters read on success, otherwise return - * an error code. - */ -#define CONSOLEIO_write 0 -#define CONSOLEIO_read 1 - -/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */ -#define DOMID_FIRST_RESERVED xen_mk_uint(0x7FF0) - -/* DOMID_SELF is used in certain contexts to refer to oneself. */ -#define DOMID_SELF xen_mk_uint(0x7FF0) - -/* - * DOMID_IO is used to restrict page-table updates to mapping I/O memory. - * Although no Foreign Domain need be specified to map I/O pages, DOMID_IO - * is useful to ensure that no mappings to the OS's own heap are accidentally - * installed. (e.g., in Linux this could cause havoc as reference counts - * aren't adjusted on the I/O-mapping code path). - * This only makes sense as HYPERVISOR_mmu_update()'s and - * HYPERVISOR_update_va_mapping_otherdomain()'s "foreigndom" argument. For - * HYPERVISOR_mmu_update() context it can be specified by any calling domain, - * otherwise it's only permitted if the caller is privileged. - */ -#define DOMID_IO xen_mk_uint(0x7FF1) - -/* - * DOMID_XEN is used to allow privileged domains to map restricted parts of - * Xen's heap space (e.g., the machine_to_phys table). - * This only makes sense as - * - HYPERVISOR_mmu_update()'s, HYPERVISOR_mmuext_op()'s, or - * HYPERVISOR_update_va_mapping_otherdomain()'s "foreigndom" argument, - * - with XENMAPSPACE_gmfn_foreign, - * and is only permitted if the caller is privileged. - */ -#define DOMID_XEN xen_mk_uint(0x7FF2) - -/* - * DOMID_COW is used as the owner of sharable pages. - */ -#define DOMID_COW xen_mk_uint(0x7FF3) - -/* DOMID_INVALID is used to identify pages with unknown owner. */ -#define DOMID_INVALID xen_mk_uint(0x7FF4) - -/* Idle domain. */ -#define DOMID_IDLE xen_mk_uint(0x7FFF) - -/* Mask for valid domain id values */ -#define DOMID_MASK xen_mk_uint(0x7FFF) - -#ifndef __ASSEMBLY__ - -typedef uint16_t domid_t; - -#if CONFIG_XEN_INTERFACE_VERSION < 0x00040400 -/* - * Event channel endpoints per domain (when using the 2-level ABI): - * 1024 if a long is 32 bits; 4096 if a long is 64 bits. - */ -#define NR_EVENT_CHANNELS EVTCHN_2L_NR_CHANNELS -#endif - -struct vcpu_time_info { - /* - * Updates to the following values are preceded and followed by an - * increment of 'version'. The guest can therefore detect updates by - * looking for changes to 'version'. If the least-significant bit of - * the version number is set then an update is in progress and the - * guest must wait to read a consistent set of values. - * The correct way to interact with the version number is similar to - * Linux's seqlock: see the implementations of - * read_seqbegin/read_seqretry. - */ - uint32_t version; - uint32_t pad0; - uint64_t tsc_timestamp; /* TSC at last update of time vals. */ - uint64_t system_time; /* Time, in nanosecs, since boot.*/ - /* - * Current system time: - * system_time + - * ((((tsc - tsc_timestamp) << tsc_shift) * tsc_to_system_mul) >> 32) - * CPU frequency (Hz): - * ((10^9 << 32) / tsc_to_system_mul) >> tsc_shift - */ - uint32_t tsc_to_system_mul; - int8_t tsc_shift; -#if CONFIG_XEN_INTERFACE_VERSION > 0x040600 - uint8_t flags; - uint8_t pad1[2]; -#else - int8_t pad1[3]; -#endif -}; /* 32 bytes */ -typedef struct vcpu_time_info vcpu_time_info_t; - -#define XEN_PVCLOCK_TSC_STABLE_BIT (1 << 0) -#define XEN_PVCLOCK_GUEST_STOPPED (1 << 1) - -struct vcpu_info { - /* - * 'evtchn_upcall_pending' is written non-zero by Xen to indicate - * a pending notification for a particular VCPU. It is then cleared - * by the guest OS /before/ checking for pending work, thus avoiding - * a set-and-check race. Note that the mask is only accessed by Xen - * on the CPU that is currently hosting the VCPU. This means that the - * pending and mask flags can be updated by the guest without special - * synchronisation (i.e., no need for the x86 LOCK prefix). - * This may seem suboptimal because if the pending flag is set by - * a different CPU then an IPI may be scheduled even when the mask - * is set. However, note: - * 1. The task of 'interrupt holdoff' is covered by the per-event- - * channel mask bits. A 'noisy' event that is continually being - * triggered can be masked at source at this very precise - * granularity. - * 2. The main purpose of the per-VCPU mask is therefore to restrict - * reentrant execution: whether for concurrency control, or to - * prevent unbounded stack usage. Whatever the purpose, we expect - * that the mask will be asserted only for short periods at a time, - * and so the likelihood of a 'spurious' IPI is suitably small. - * The mask is read before making an event upcall to the guest: a - * non-zero mask therefore guarantees that the VCPU will not receive - * an upcall activation. The mask is cleared when the VCPU requests - * to block: this avoids wakeup-waiting races. - */ - uint8_t evtchn_upcall_pending; -#ifdef XEN_HAVE_PV_UPCALL_MASK - uint8_t evtchn_upcall_mask; -#else /* XEN_HAVE_PV_UPCALL_MASK */ - uint8_t pad0; -#endif /* XEN_HAVE_PV_UPCALL_MASK */ - xen_ulong_t evtchn_pending_sel; - struct arch_vcpu_info arch; - vcpu_time_info_t time; -}; /* 64 bytes (x86) */ -#ifndef __XEN__ -typedef struct vcpu_info vcpu_info_t; -#endif - -/* - * `incontents 200 startofday_shared Start-of-day shared data structure - * Xen/kernel shared data -- pointer provided in start_info. - * - * This structure is defined to be both smaller than a page, and the - * only data on the shared page, but may vary in actual size even within - * compatible Xen versions; guests should not rely on the size - * of this structure remaining constant. - */ -struct shared_info { - struct vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS]; - - /* - * A domain can create "event channels" on which it can send and receive - * asynchronous event notifications. There are three classes of event that - * are delivered by this mechanism: - * 1. Bi-directional inter- and intra-domain connections. Domains must - * arrange out-of-band to set up a connection (usually by allocating - * an unbound 'listener' port and advertising that via a storage service - * such as xenstore). - * 2. Physical interrupts. A domain with suitable hardware-access - * privileges can bind an event-channel port to a physical interrupt - * source. - * 3. Virtual interrupts ('events'). A domain can bind an event-channel - * port to a virtual interrupt source, such as the virtual-timer - * device or the emergency console. - * - * Event channels are addressed by a "port index". Each channel is - * associated with two bits of information: - * 1. PENDING -- notifies the domain that there is a pending notification - * to be processed. This bit is cleared by the guest. - * 2. MASK -- if this bit is clear then a 0->1 transition of PENDING - * will cause an asynchronous upcall to be scheduled. This bit is only - * updated by the guest. It is read-only within Xen. If a channel - * becomes pending while the channel is masked then the 'edge' is lost - * (i.e., when the channel is unmasked, the guest must manually handle - * pending notifications as no upcall will be scheduled by Xen). - * - * To expedite scanning of pending notifications, any 0->1 pending - * transition on an unmasked channel causes a corresponding bit in a - * per-vcpu selector word to be set. Each bit in the selector covers a - * 'C long' in the PENDING bitfield array. - */ - xen_ulong_t evtchn_pending[sizeof(xen_ulong_t) * 8]; - xen_ulong_t evtchn_mask[sizeof(xen_ulong_t) * 8]; - - /* - * Wallclock time: updated by control software or RTC emulation. - * Guests should base their gettimeofday() syscall on this - * wallclock-base value. - * The values of wc_sec and wc_nsec are offsets from the Unix epoch - * adjusted by the domain's 'time offset' (in seconds) as set either - * by XEN_DOMCTL_settimeoffset, or adjusted via a guest write to the - * emulated RTC. - */ - uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */ - uint32_t wc_sec; - uint32_t wc_nsec; -#if !defined(__i386__) - uint32_t wc_sec_hi; -# define xen_wc_sec_hi wc_sec_hi -#elif !defined(__XEN__) && !defined(__XEN_TOOLS__) -# define xen_wc_sec_hi arch.wc_sec_hi -#endif - - struct arch_shared_info arch; - -}; -#ifndef __XEN__ -typedef struct shared_info shared_info_t; -#endif - -typedef uint8_t xen_domain_handle_t[16]; - -#ifndef int64_aligned_t -#define int64_aligned_t int64_t -#endif -#ifndef uint64_aligned_t -#define uint64_aligned_t uint64_t -#endif -#ifndef XEN_GUEST_HANDLE_64 -#define XEN_GUEST_HANDLE_64(name) XEN_GUEST_HANDLE(name) -#endif - -#ifndef __ASSEMBLY__ -struct xenctl_bitmap { - XEN_GUEST_HANDLE_64(uint8_t) bitmap; - uint32_t nr_bits; -}; -typedef struct xenctl_bitmap xenctl_bitmap_t; -#endif - -#endif /* !__ASSEMBLY__ */ - -#endif /* __XEN_PUBLIC_XEN_H__ */ diff --git a/include/zephyr/xen/sched.h b/include/zephyr/xen/sched.h new file mode 100644 index 0000000000000..14a6caa64bea1 --- /dev/null +++ b/include/zephyr/xen/sched.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_XEN_SCHED_H_ +#define ZEPHYR_XEN_SCHED_H_ + +#include + +/** + * @brief Poll one or more Xen event channels for activity. + * + * Issues the SCHEDOP_poll hypercall to wait for events on the specified ports. + * + * @param ports Array of event channel ports to poll. + * @param nr_ports Number of ports in the array. + * @param timeout Timeout in microseconds to wait for an event. + * @return 0 if an event occurred, -EAGAIN on timeout, or negative errno on error. + */ +int sched_poll(evtchn_port_t *ports, unsigned int nr_ports, uint64_t timeout); + +#endif /* ZEPHYR_XEN_SCHED_H_ */ diff --git a/modules/zephyr-xenlib/CMakeLists.txt b/modules/zephyr-xenlib/CMakeLists.txt new file mode 100644 index 0000000000000..df75c346a2d42 --- /dev/null +++ b/modules/zephyr-xenlib/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_XEN) + set(XEN_DIR ${ZEPHYR_CURRENT_MODULE_DIR}) + + zephyr_include_directories(${XEN_DIR}/include) +endif() diff --git a/modules/zephyr-xenlib/Kconfig b/modules/zephyr-xenlib/Kconfig new file mode 100644 index 0000000000000..1c5a52e66fe20 --- /dev/null +++ b/modules/zephyr-xenlib/Kconfig @@ -0,0 +1,5 @@ +# Copyright (c) 2025 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +config ZEPHYR_XENLIB_MODULE + bool diff --git a/samples/drivers/virtualization/vhost/CMakeLists.txt b/samples/drivers/virtualization/vhost/CMakeLists.txt new file mode 100644 index 0000000000000..ceb1058eff75a --- /dev/null +++ b/samples/drivers/virtualization/vhost/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(vhost) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/drivers/virtualization/vhost/README.rst b/samples/drivers/virtualization/vhost/README.rst new file mode 100644 index 0000000000000..dfaf59575c782 --- /dev/null +++ b/samples/drivers/virtualization/vhost/README.rst @@ -0,0 +1,52 @@ +.. zephyr:code-sample:: vhost + :name: Vhost sample application + +Overview +******** + +This sample demonstrates the use of the vhost driver subsystem for implementing +VIRTIO backends in Zephyr. The application shows how to: + +* Initialize and configure vhost devices +* Handle VIRTIO queue operations +* Process guest requests using the vringh utility +* Implement a basic VIRTIO backend for Xen virtualization + +The sample sets up a vhost device that can communicate with VIRTIO frontend +drivers in guest virtual machines, specifically designed for Xen MMIO +virtualization environments. + +Requirements +************ + +This sample requires: + +* A Xen hypervisor environment +* Xen domain management tools +* A board that supports Xen virtualization (e.g., xenvm) + +Building and Running +******************** + +This application can be built and executed on Xen as follows: + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/virtualization/vhost + :host-os: unix + :board: xenvm + :goals: run + :compact: + +The application will initialize the vhost subsystem and wait for VIRTIO +frontend connections from guest domains. When a guest connects and sends +requests, the sample will process them and provide responses. + +Expected Output +*************** + +When running successfully, you should see output similar to:: + + *** Booting Zephyr OS build zephyr-v3.x.x *** + [00:00:00.000,000] vhost: VHost device ready + [00:00:00.000,000] vhost: queue_ready_handler(dev=0x..., qid=0, data=0x...) + [00:00:00.000,000] vhost: vringh_kick_handler: queue_id=0 diff --git a/samples/drivers/virtualization/vhost/boards/xenvm.overlay b/samples/drivers/virtualization/vhost/boards/xenvm.overlay new file mode 100644 index 0000000000000..31e6bbf596795 --- /dev/null +++ b/samples/drivers/virtualization/vhost/boards/xenvm.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + vhost: xen-vhost-mmio { + compatible = "xen,vhost-mmio"; + device-id = <4>; + vendor-id = <0x7a707972>; /* "zphy" */ + num-queues = <2>; + queue-size-max= <16>; + base = <0x2000000>; + }; +}; diff --git a/samples/drivers/virtualization/vhost/boards/xenvm_xenvm_gicv3.overlay b/samples/drivers/virtualization/vhost/boards/xenvm_xenvm_gicv3.overlay new file mode 100644 index 0000000000000..363550cc41acd --- /dev/null +++ b/samples/drivers/virtualization/vhost/boards/xenvm_xenvm_gicv3.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "xenvm.overlay" diff --git a/samples/drivers/virtualization/vhost/prj.conf b/samples/drivers/virtualization/vhost/prj.conf new file mode 100644 index 0000000000000..71a1adb90e2af --- /dev/null +++ b/samples/drivers/virtualization/vhost/prj.conf @@ -0,0 +1,6 @@ +CONFIG_VHOST=y +CONFIG_LOG=y +CONFIG_HEAP_MEM_POOL_SIZE=8388608 +CONFIG_LOG_MODE_IMMEDIATE=y +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_VHOST_LOG_LEVEL_DBG=y diff --git a/samples/drivers/virtualization/vhost/sample.yaml b/samples/drivers/virtualization/vhost/sample.yaml new file mode 100644 index 0000000000000..e55f344cecb99 --- /dev/null +++ b/samples/drivers/virtualization/vhost/sample.yaml @@ -0,0 +1,20 @@ +sample: + description: VHost driver sample for VIRTIO backend implementation + name: vhost +common: + integration_platforms: + - xenvm + platform_allow: xenvm + harness: console + harness_config: + type: multi_line + regex: + - "VHost device ready" + - "VHost sample application started" + timeout: 60 + depends_on: vhost +tests: + sample.drivers.virtualization.vhost: + tags: drivers virtualization vhost xen + min_ram: 32 + extra_args: CONFIG_LOG_MODE_IMMEDIATE=y diff --git a/samples/drivers/virtualization/vhost/src/main.c b/samples/drivers/virtualization/vhost/src/main.c new file mode 100644 index 0000000000000..7895d5c38f83d --- /dev/null +++ b/samples/drivers/virtualization/vhost/src/main.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +LOG_MODULE_REGISTER(vhost); + +struct vhost_iovec riovec[16]; +struct vhost_iovec wiovec[16]; + +struct vringh_iov riov = { + .iov = riovec, + .max_num = 16, +}; + +struct vringh_iov wiov = { + .iov = wiovec, + .max_num = 16, +}; + +struct vringh vrh_inst; + +static void vringh_kick_handler(struct vringh *vrh) +{ + LOG_DBG("%s: queue_id=%lu", __func__, vrh->queue_id); + uint16_t head; + + while (true) { + int ret = vringh_getdesc(vrh, &riov, &wiov, &head); + + if (ret < 0) { + LOG_ERR("vringh_getdesc failed: %d", ret); + return; + } + + if (ret == 0) { + return; + } + + /* Process writable iovecs */ + for (uint32_t s = 0; s < wiov.used; s++) { + uint8_t *dst = wiov.iov[s].iov_base; + uint32_t len = wiov.iov[s].iov_len; + + LOG_DBG("%s: addr=%p len=%u", __func__, dst, len); + + for (uint32_t i = 0; i < len; i++) { + sys_write8(i, (mem_addr_t)&dst[i]); + } + } + + barrier_dmem_fence_full(); + + uint32_t total_len = 0; + + for (uint32_t i = 0; i < wiov.used; i++) { + total_len += wiov.iov[i].iov_len; + } + + vringh_complete(vrh, head, total_len); + + if (vringh_need_notify(vrh) > 0) { + vringh_notify(vrh); + } + + /* Reset iovecs for next iteration */ + vringh_iov_reset(&riov); + vringh_iov_reset(&wiov); + } +} + +void queue_ready_handler(const struct device *dev, uint16_t qid, void *data) +{ + LOG_DBG("%s(dev=%p, qid=%u, data=%p)", __func__, dev, qid, data); + + /* Initialize iovecs before descriptor processing */ + vringh_iov_init(&riov, riov.iov, riov.max_num); + vringh_iov_init(&wiov, wiov.iov, wiov.max_num); + + int err = vringh_init_device(&vrh_inst, dev, qid, vringh_kick_handler); + + if (err) { + LOG_ERR("vringh_init_device failed: %d", err); + return; + } +} + +int main(void) +{ + const struct device *device = DEVICE_DT_GET(DT_NODELABEL(vhost)); + + if (!device_is_ready(device)) { + LOG_ERR("VHost device not ready"); + return -ENODEV; + } + + LOG_INF("VHost device ready"); + vhost_register_virtq_ready_cb(device, queue_ready_handler, (void *)device); + + LOG_INF("VHost sample application started, waiting for guest connections..."); + k_sleep(K_FOREVER); + + return 0; +} diff --git a/west.yml b/west.yml index bcef02412e50f..54bad2e662971 100644 --- a/west.yml +++ b/west.yml @@ -23,6 +23,8 @@ manifest: url-base: https://github.com/zephyrproject-rtos - name: babblesim url-base: https://github.com/BabbleSim + - name: soburi + url-base: https://github.com/soburi group-filter: [-babblesim, -optional] @@ -374,6 +376,10 @@ manifest: - name: zcbor revision: 9b07780aca6fb21f82a241ba386ad9b379809337 path: modules/lib/zcbor + - name: zephyr-xenlib + revision: xen-virtio-backend + path: modules/lib/zephyr-xenlib + remote: soburi # zephyr-keep-sorted-stop self: