From c5c096482d18dbedad5143ee71834553e19fce79 Mon Sep 17 00:00:00 2001 From: caojinhuahw Date: Thu, 13 Nov 2025 21:44:24 +0800 Subject: [PATCH 1/5] ub: add some common function for later vfio-ub realize this is prepare for later vfio ub device realize Signed-off-by: caojinhuahw --- hw/ub/ub_common.c | 298 ++++++++++++++++++++++++++++++++++++++ include/hw/ub/ub_common.h | 8 +- 2 files changed, 305 insertions(+), 1 deletion(-) diff --git a/hw/ub/ub_common.c b/hw/ub/ub_common.c index 368d420463..f8ef12fb02 100644 --- a/hw/ub/ub_common.c +++ b/hw/ub/ub_common.c @@ -19,6 +19,7 @@ #include "qemu/log.h" #include "qapi/error.h" #include "hw/ub/ub_common.h" +#include "hw/ub/ubus_instance.h" #include "sysemu/dma.h" /* tmp for vfio-ub run with stub, remove later */ @@ -88,6 +89,280 @@ uint32_t fill_cq(BusControllerState *s, HiMsgCqe *cqe) return pi; } +#define UB_MEM_DUMP_MAX_STR_LEN 4096 +#define UB_MEM_DUMP_COLUMN 4 +#define UB_MEM_DUMP_WIDTH 36 +#define UB_MEM_DUMP_MAX_BYTES 2048 +#define UB_HEXDUMP_TITLE " ↓0x0 ↓0x4 ↓0x8 ↓0xC\n" +int ub_hexdump(void *data, int offset, int len, char *buff, int buff_size) +{ + size_t l = 0; + size_t tmp; + int dw = len / sizeof(uint32_t) + !!(len % sizeof(uint32_t)); + int dw_round_up = ROUND_UP(dw, UB_MEM_DUMP_COLUMN); + int i; + void *real_data = data + offset; + char str_addr[64] = {0}; + int width; + bool last_line_all_0 = 1; + int cnt_line_all_0 = 0; + size_t last_line_cnt_character = 0; + g_autofree char *line = line_generator(UB_MEM_DUMP_WIDTH); + g_autofree char *line_head = g_strdup_printf("┌%s┐", line); + g_autofree char *line_tail = g_strdup_printf("└%s┘", line); + g_autofree char *line_zero = g_strdup_printf("%-*s", + UB_MEM_DUMP_WIDTH + 3, "│"); + + if (!line || !line_head || !line_tail || !line_zero) { + qemu_log("failed to alloc mem %p %p %p %p\n", + line, line_head, line_tail, line_zero); + return -1; + } + + if (buff_size < strlen(line_head) + strlen(line_tail) + + strlen(UB_HEXDUMP_TITLE) + dw_round_up * 8) { + qemu_log("buff too small %d %d %ld\n", + buff_size, len, strlen(line_head) + strlen(line_tail) + + strlen(UB_HEXDUMP_TITLE) + dw_round_up * 8); + return -1; + } + snprintf(str_addr, sizeof(str_addr), "0x%x", offset + len); + width = (int)strlen(str_addr); + l += snprintf(buff + l, buff_size - l, "\n%*s%s", + width, " ", UB_HEXDUMP_TITLE); + l += snprintf(buff + l, buff_size - l, "%*s%s", + width, " ", line_head); + for (i = 0; i < dw_round_up; i++) { + if (i >= dw) { + l += snprintf(buff + l, buff_size - l, " %8s", " "); + } else { + if ((i % UB_MEM_DUMP_COLUMN) != 0) { + tmp = snprintf(buff + l, buff_size - l, " %.8x", + *((uint32_t *)real_data + i)); + l += tmp; + last_line_all_0 &= !(*((uint32_t *)real_data + i)); + last_line_cnt_character += tmp; + } else { + if (last_line_all_0 && last_line_cnt_character) { + cnt_line_all_0++; + if (cnt_line_all_0 == 2) { + l -= last_line_cnt_character; + l += snprintf(buff + l, buff_size - l, + "│\n%*s%s", width, "...", + line_zero); + } else if (cnt_line_all_0 > 2) { + l -= last_line_cnt_character; + } + } else { + cnt_line_all_0 = 0; + } + snprintf(str_addr, sizeof(str_addr), "0x%lx", + offset + i * sizeof(uint32_t)); + tmp = snprintf(buff + l, buff_size - l, "%s\n%*s│ %.8x", + i == 0 ? "" : "│", width, str_addr, + *((uint32_t *)real_data + i)); + l += tmp; + last_line_all_0 = !(*((uint32_t *)real_data + i)); + last_line_cnt_character = tmp; + } + } + } + l += snprintf(buff + l, buff_size - l, "│\n%*s%s\n", + width, " ", line_tail); + return 0; +} + +void ub_mem_dump(void *start, int size, const char *tag_fmt, ...) +{ + va_list ap; + char str[UB_MEM_DUMP_MAX_STR_LEN] = {0}; + size_t l; + + /* get mem tag info */ + va_start(ap, tag_fmt); + l = vsnprintf(str, sizeof(str), tag_fmt, ap); + va_end(ap); + + if (size > UB_MEM_DUMP_MAX_BYTES) { + qemu_log("%s execeed max len %d\n", + str, UB_MEM_DUMP_MAX_BYTES); + return; + } + + if (ub_hexdump(start, 0, size, str + l, sizeof(str) - l) < 0) { + qemu_log("failed to dump memory. %s\n", str); + return; + } + qemu_log("%s", str); +} + +/* get interrupt_id from sysfs, not found will return UINT32_MAX */ +#define MAX_BUF_LENGTH 1024 +uint32_t sysfs_get_dev_number_by_guid(UbGuid *guid) +{ + char guid_str[UB_DEV_GUID_STRING_LENGTH + 1] = {0}; + uint32_t id = UINT32_MAX; + const char *ub_sysfs_devices = "/sys/bus/ub/devices"; + struct dirent *entry; + DIR *dir = NULL; + + dir = opendir(ub_sysfs_devices); + if (!dir) { + qemu_log("failed to opendir %s\n", ub_sysfs_devices); + return UINT32_MAX; + } + ub_device_get_str_from_guid(guid, guid_str, UB_DEV_GUID_STRING_LENGTH + 1); + + while ((entry = readdir(dir)) != NULL) { + char file_path[MAX_BUF_LENGTH] = {0}; /* guid file path */ + char guid_buffer[MAX_BUF_LENGTH] = {0}; /* guid that read from file */ + FILE *file = NULL; + size_t bytes_read; + + /* skip the stumbling blocks */ + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + snprintf(file_path, sizeof(file_path), "%s/%s/guid", + ub_sysfs_devices, entry->d_name); + file = fopen(file_path, "r"); + if (file == NULL) { + qemu_log("failed to open %s\n", file_path); + closedir(dir); + return UINT32_MAX; + } + + bytes_read = fread(guid_buffer, 1, MAX_BUF_LENGTH - 1, file); + fclose(file); + guid_buffer[bytes_read] = '\0'; + /* discard annoying line breaks */ + if (bytes_read > 0 && guid_buffer[bytes_read - 1] == '\n') { + guid_buffer[bytes_read - 1] = '\0'; + } + + /* check if it's a long-awaited true love */ + if (strcmp(guid_buffer, guid_str) == 0) { + sscanf(entry->d_name, "%x", &id); + closedir(dir); + return id; + } + } + closedir(dir); + return id; +} + +uint32_t sysfs_get_ub_device_bus_instance_eid(char *sysfsdev) +{ + FILE *f = NULL; + char *path = NULL; + char bus_instance_eid_buf[MAX_BUF_LENGTH] = {0}; + uint32_t eid = UINT32_MAX; + + path = g_strdup_printf("%s/instance", sysfsdev); + f = fopen(path, "r"); + if (!f) { + qemu_log("failed to open file:%s\n", path); + g_free(path); + return eid; + } + + if (fgets(bus_instance_eid_buf, MAX_BUF_LENGTH, f) != NULL) { + sscanf(bus_instance_eid_buf, "%x", &eid); + qemu_log("sysfs(%s) get bus instance eid: 0x%x.\n", sysfsdev, eid); + } + + if (eid == UINT32_MAX) { + qemu_log("cannot get bus instance eid: %s.\n", sysfsdev); + } + + fclose(f); + g_free(path); + return eid; +} + +uint32_t sysfs_get_bus_instance_eid_by_guid(UbGuid *guid) +{ + FILE *file = NULL; + char guid_str[UB_DEV_GUID_STRING_LENGTH + 1] = {0}; + uint32_t eid = UINT32_MAX; + char line[MAX_BUF_LENGTH] = {0}; + bool found = false; + + ub_device_get_str_from_guid(guid, guid_str, UB_DEV_GUID_STRING_LENGTH + 1); + file = fopen("/sys/bus/ub/instance", "r"); + if (file == NULL) { + qemu_log("failed to open /sys/bus/ub/instance\n"); + return eid; + } + + while (fgets(line, sizeof(line), file) != NULL) { + if (strstr(line, guid_str) != NULL) { + found = true; + break; + } + } + fclose(file); + + if (found) { + /* 0 1 2 3 + * /sys/bus/ub/instance: guid:xxx type:xxx eid:xxx upi:xxx + */ + g_autofree char **eid_str = g_strsplit(line, " ", 4); + if (eid_str && eid_str[2]) { + sscanf(eid_str[2], "eid:%05x", &eid); + qemu_log("find ubus instance eid 0x%x by guid %s\n", eid, guid_str); + } + } + + if (eid == UINT32_MAX) { + qemu_log("can not find instance eid by guid %s.\n", guid_str); + } + + return eid; +} + +uint32_t sysfs_get_bus_instance_type_by_eid(uint32_t eid) +{ + FILE *file = NULL; + char *eid_str = NULL; + char line[MAX_BUF_LENGTH] = {0}; + int bus_instance_type = UBUS_INSTANCE_UNKNOW; + bool found = false; + + file = fopen("/sys/bus/ub/instance", "r"); + if (file == NULL) { + qemu_log("failed to open /sys/bus/ub/instance\n"); + return bus_instance_type; + } + + eid_str = g_strdup_printf("eid:%05x", eid); + while (fgets(line, sizeof(line), file) != NULL) { + if (strstr(line, eid_str) != NULL) { + found = true; + break; + } + } + g_free(eid_str); + fclose(file); + + if (found) { + /* 0 1 2 3 + * /sys/bus/ub/instance: guid:xxx type:xxx eid:xxx upi:xxx + */ + g_autofree char **type = g_strsplit(line, " ", 4); + if (type && type[1]) { + sscanf(type[1] + strlen("type:"), "%d", &bus_instance_type); + qemu_log("bus instance eid(0x%x) type is %d.\n", eid, bus_instance_type); + } + } + + if (bus_instance_type == UBUS_INSTANCE_UNKNOW) { + qemu_log("can not get bus instance type by eid: 0x%x\n", eid); + } + + return bus_instance_type; +} + bool ub_guid_is_none(UbGuid *guid) { if (guid->seq_num == 0 && @@ -98,3 +373,26 @@ bool ub_guid_is_none(UbGuid *guid) return false; } + +/* The caller is responsible for free memory. */ +char *line_generator(uint8_t len) +{ + char *line = NULL; + int i, j; + if (!len) { + qemu_log("invalid len %d", len); + return NULL; + } + + line = g_malloc0(len * DASH_SZ + 1); + if (!line) { + qemu_log("failed to alloc mem %d", len * DASH_SZ + 1); + return NULL; + } + for (i = 0, j = 0; i < len; i++) { + line[j++] = '\xE2'; + line[j++] = '\x80'; + line[j++] = '\x94'; + } + return line; +} diff --git a/include/hw/ub/ub_common.h b/include/hw/ub/ub_common.h index 440a5bacdf..0af352c2bb 100644 --- a/include/hw/ub/ub_common.h +++ b/include/hw/ub/ub_common.h @@ -415,7 +415,13 @@ typedef struct MsgPktHeader { /* TODO, check byte order */ uint32_t fill_rq(BusControllerState *s, void *rsp, uint32_t rsp_size); uint32_t fill_cq(BusControllerState *s, HiMsgCqe *cqe); - +/* get eid from sysfs, not found will return UINT32_MAX */ +uint32_t sysfs_get_dev_number_by_guid(UbGuid *guid); +uint32_t sysfs_get_ub_device_bus_instance_eid(char *sysfsdev); +uint32_t sysfs_get_bus_instance_type_by_eid(uint32_t eid); +uint32_t sysfs_get_bus_instance_eid_by_guid(UbGuid *guid); +void ub_mem_dump(void *start, int size, const char *tag_fmt, ...) __attribute__((format(printf, 3, 4))); +int ub_hexdump(void *data, int offset, int len, char *buff, int buff_size); bool ub_guid_is_none(UbGuid *guid); #endif -- Gitee From f953a105abfd3b7e9e1a695d9e51333c0cca68a3 Mon Sep 17 00:00:00 2001 From: caojinhuahw Date: Thu, 13 Nov 2025 22:19:12 +0800 Subject: [PATCH 2/5] ub: add function for later alloc idev ers addr ers addr is allocate by qemu when idev used by guest through vfio, this prepre for later idev ers addr allocate Signed-off-by: caojinhuahw --- hw/ub/ub_acpi.c | 192 ++++++++++++++++++++++++++++++++++++++++ include/hw/ub/ub_acpi.h | 2 + 2 files changed, 194 insertions(+) diff --git a/hw/ub/ub_acpi.c b/hw/ub/ub_acpi.c index f9e38a2da3..4f4c77b869 100644 --- a/hw/ub/ub_acpi.c +++ b/hw/ub/ub_acpi.c @@ -23,6 +23,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/ub/ub.h" +#include "hw/ub/ub_config.h" #include "hw/ub/ub_bus.h" #include "hw/ub/ub_ubc.h" #include "hw/ub/ub_acpi.h" @@ -42,6 +43,26 @@ #define DTS_SIG_UMMU "ummu" #define DTS_SIG_RSV_MEM "rsv_mem" +typedef struct UBIdevErsAddrSpaceNode { + uint64_t offset; + uint64_t allocated_offset; + uint64_t size; + + QTAILQ_ENTRY(UBIdevErsAddrSpaceNode) stailq_free; + QTAILQ_ENTRY(UBIdevErsAddrSpaceNode) stailq_used; +} UBIdevErsAddrSpaceNode; + +typedef struct UBIdevErsAddrSpaceManage { + bool init; + uint64_t size; + hwaddr base_addr; + + QTAILQ_HEAD(, UBIdevErsAddrSpaceNode) as_free_list; + QTAILQ_HEAD(, UBIdevErsAddrSpaceNode) as_used_list; +} UBIdevErsAddrSpaceManage; + +UBIdevErsAddrSpaceManage g_idevErsAddrSpaceManage; + static uint8_t gpa_bits; void ub_set_gpa_bits(uint8_t bits) { @@ -319,6 +340,177 @@ void ub_set_ubinfo_in_ubc_table(VirtMachineState *vms) cpu_physical_memory_unmap(ubios, size, true, size); } +static void ub_idev_ers_address_space_manage_init(void) +{ + VirtMachineState *vms = (VirtMachineState *)current_machine; + UBIdevErsAddrSpaceNode *free_node = NULL; + + g_idevErsAddrSpaceManage.base_addr = vms->memmap[VIRT_UB_IDEV_ERS].base; + g_idevErsAddrSpaceManage.size = vms->memmap[VIRT_UB_IDEV_ERS].size; + + QTAILQ_INIT(&g_idevErsAddrSpaceManage.as_free_list); + QTAILQ_INIT(&g_idevErsAddrSpaceManage.as_used_list); + + free_node = g_new0(UBIdevErsAddrSpaceNode, 1); + free_node->size = g_idevErsAddrSpaceManage.size; + free_node->offset = 0; + QTAILQ_INSERT_TAIL(&g_idevErsAddrSpaceManage.as_free_list, free_node, stailq_free); + qemu_log("ub idev ers address space manage init success, base_addr: 0x%lx size: 0x%lx\n", + g_idevErsAddrSpaceManage.base_addr, g_idevErsAddrSpaceManage.size); +} + +hwaddr ub_idev_ers_alloc_address_space(uint64_t size, uint32_t sys_pgs) +{ + UBIdevErsAddrSpaceNode *free_node = NULL; + UBIdevErsAddrSpaceNode *selected_free_node = NULL; + UBIdevErsAddrSpaceNode *used_node = NULL; + uint64_t need_node_size; + uint64_t free_node_base_addr; + uint64_t allocated_base_addr; + uint64_t allocated_diff; + + if (!g_idevErsAddrSpaceManage.init) { + g_idevErsAddrSpaceManage.init = true; + ub_idev_ers_address_space_manage_init(); + } + + /* according UB Spec, if sys_pgs 0, unit is 4Kbytes, then unit is 64Kbytes */ + if (!sys_pgs) { + size *= UB_CFG1_BASIC_SYSTEM_GRANULE_SIZE_4K; + } else { + size *= UB_CFG1_BASIC_SYSTEM_GRANULE_SIZE_64K; + } + + QTAILQ_FOREACH(free_node, &g_idevErsAddrSpaceManage.as_free_list, stailq_free) { + if (free_node->size < size) { + continue; + } + + free_node_base_addr = g_idevErsAddrSpaceManage.base_addr + free_node->offset; + /* allocated base addr need align to allocated size */ + allocated_base_addr = ALIGN_UP(free_node_base_addr, size); + allocated_diff = allocated_base_addr - free_node_base_addr; + need_node_size = allocated_diff + size; + if (free_node->size < need_node_size) { + continue; + } + + if (selected_free_node && selected_free_node->size < free_node->size) { + continue; + } + + selected_free_node = free_node; + if (!used_node) { + /* create used node */ + used_node = g_new0(UBIdevErsAddrSpaceNode, 1); + } + used_node->offset = selected_free_node->offset; + used_node->allocated_offset = used_node->offset + allocated_diff; + used_node->size = size + allocated_diff; + } + + if (!selected_free_node) { + g_free(used_node); + return UINT64_MAX; + } + + /* adjust free node */ + if (selected_free_node->size - size < UB_CFG1_BASIC_SYSTEM_GRANULE_SIZE_4K) { + used_node->size = selected_free_node->size; + QTAILQ_REMOVE(&g_idevErsAddrSpaceManage.as_free_list, selected_free_node, stailq_free); + g_free(selected_free_node); + } else { + selected_free_node->size -= used_node->size; + selected_free_node->offset += used_node->size; + } + + QTAILQ_INSERT_TAIL(&g_idevErsAddrSpaceManage.as_used_list, used_node, stailq_used); + + return allocated_base_addr; +} + +void ub_idev_ers_free_address_space(hwaddr offset) +{ + UBIdevErsAddrSpaceNode *used_node = NULL; + UBIdevErsAddrSpaceNode *free_node = NULL; + UBIdevErsAddrSpaceNode *next_free_node = NULL; + uint64_t as_offset = offset - g_idevErsAddrSpaceManage.base_addr; + + QTAILQ_FOREACH(used_node, &g_idevErsAddrSpaceManage.as_used_list, stailq_used) { + if (used_node->allocated_offset == as_offset) { + QTAILQ_REMOVE(&g_idevErsAddrSpaceManage.as_used_list, used_node, stailq_used); + break; + } + } + + if (!used_node) { + qemu_log("idev ers address space free failed, unable to find offset 0x%lx.\n", offset); + return; + } + + /* adjust free node list */ + /* case 1: as free list is empty */ + if (QTAILQ_EMPTY(&g_idevErsAddrSpaceManage.as_free_list)) { + QTAILQ_INSERT_HEAD(&g_idevErsAddrSpaceManage.as_free_list, used_node, stailq_free); + return; + } + + /* case 2: freed used_node->offset is minial */ + free_node = QTAILQ_FIRST(&g_idevErsAddrSpaceManage.as_free_list); + if (used_node->offset + used_node->size < free_node->offset) { + QTAILQ_INSERT_HEAD(&g_idevErsAddrSpaceManage.as_free_list, used_node, stailq_free); + return; + } else if (used_node->offset + used_node->size == free_node->offset) { /* merge to first free node */ + free_node->offset = used_node->offset; + free_node->size += used_node->size; + g_free(used_node); + return; + } + + /* case 3: foreach all free node, insert freed address space to free node in order */ + QTAILQ_FOREACH(free_node, &g_idevErsAddrSpaceManage.as_free_list, stailq_free) { + next_free_node = QTAILQ_NEXT(free_node, stailq_free); + if (!next_free_node) { + if (free_node->offset + free_node->size < used_node->offset) { + QTAILQ_INSERT_TAIL(&g_idevErsAddrSpaceManage.as_free_list, used_node, stailq_free); + } else if (free_node->offset + free_node->size == used_node->offset) { + free_node->size += used_node->size; + g_free(used_node); + } + return; + } + + if (used_node->offset >= next_free_node->offset + next_free_node->size) { + continue; + } + + if (free_node->offset + free_node->size == used_node->offset && + used_node->offset + used_node->size < next_free_node->offset) { + free_node->size += used_node->size; + g_free(used_node); + return; + } else if (free_node->offset + free_node->size < used_node->offset && + used_node->offset + used_node->size == next_free_node->offset) { + next_free_node->offset = used_node->offset; + next_free_node->size += used_node->size; + g_free(used_node); + return; + } else if (free_node->offset + free_node->size < used_node->offset && + used_node->offset + used_node->size < next_free_node->offset) { + QTAILQ_INSERT_AFTER(&g_idevErsAddrSpaceManage.as_free_list, free_node, used_node, stailq_free); + return; + } else { + next_free_node->offset = free_node->offset; + next_free_node->size += free_node->size; + next_free_node->size += used_node->size; + QTAILQ_REMOVE(&g_idevErsAddrSpaceManage.as_free_list, free_node, stailq_free); + g_free(used_node); + g_free(free_node); + return; + } + } +} + void build_ubrt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { /* 3 subtables: ubc, ummu, UB Reserved Memory */ diff --git a/include/hw/ub/ub_acpi.h b/include/hw/ub/ub_acpi.h index 3579256444..47f3950dfe 100644 --- a/include/hw/ub/ub_acpi.h +++ b/include/hw/ub/ub_acpi.h @@ -175,6 +175,8 @@ typedef struct AcpiUbrtTable { UBIOS_RSV_MEM_TABLE_SIZE(UBIOS_UMMU_TABLE_CNT)) void ub_init_ubios_info_table(VirtMachineState *vms, uint64_t total_size); +hwaddr ub_idev_ers_alloc_address_space(uint64_t size, uint32_t sys_pgs); +void ub_idev_ers_free_address_space(hwaddr offset); void ub_set_gpa_bits(uint8_t bits); void build_ubrt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms); void ub_set_ubinfo_in_ubc_table(VirtMachineState *vms); -- Gitee From 9d95b63b82a727fb5feeb2e8132ff9e829976c24 Mon Sep 17 00:00:00 2001 From: caojinhuahw Date: Fri, 14 Nov 2025 09:36:20 +0800 Subject: [PATCH 3/5] ub: add bus instance verify for vfio-ub prepare ub device used by guest through vfio should bind to bus instance, this prepare for bus instance check when use vfio ub device Signed-off-by: caojinhuahw --- hw/ub/ub.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/hw/ub/ub.c b/hw/ub/ub.c index 6d42abfe27..fbfedb6368 100644 --- a/hw/ub/ub.c +++ b/hw/ub/ub.c @@ -34,6 +34,7 @@ #include "hw/ub/ub_ubc.h" #include "migration/vmstate.h" #include "exec/address-spaces.h" +#include "hw/ub/ubus_instance.h" #include "monitor/monitor.h" #include "trace.h" @@ -474,6 +475,70 @@ static void do_ub_unregister_device(UBDevice *ub_dev) ub_port_info_free(ub_dev); } +static uint32_t ub_get_host_bus_instance_eid(UbGuid *guid) +{ + uint32_t bus_instance_eid; + char guid_str[UB_DEV_GUID_STRING_LENGTH + 1] = {0}; + int bus_instance_type; + + ub_device_get_str_from_guid(guid, guid_str, UB_DEV_GUID_STRING_LENGTH + 1); + bus_instance_eid = sysfs_get_bus_instance_eid_by_guid(guid); + if (bus_instance_eid == UINT32_MAX) { + qemu_log("sysfs failed to get bus instance eid by guid %s\n", guid_str); + return UINT32_MAX; + } + + bus_instance_type = sysfs_get_bus_instance_type_by_eid(bus_instance_eid); + if (!UBUS_INSTANCE_IS_DYNAMIC(bus_instance_type)) { + qemu_log("bus instance(guid: %s) not dynamic bus instance.\n", guid_str); + return UINT32_MAX; + } + + return bus_instance_eid; +} + +/* current this just for vfio ub dev host bus instance verify */ +static int ub_dev_bus_instance_verify(UBDevice *dev, Error **errp) +{ + BusControllerState *ubc = QLIST_FIRST(&ub_bus_controllers); + BusControllerDev *ub_bus_controller_dev = NULL; + UBDevice *ubc_dev = NULL; + uint32_t bus_instance_eid; + char guid_str[UB_DEV_GUID_STRING_LENGTH + 1] = {0}; + + if (!ubc) { + qemu_log("failed to get ub bus controller, bus instance verify later.\n"); + return 0; + } + + ub_bus_controller_dev = ubc->ubc_dev; + + if (!ub_bus_controller_dev) { + qemu_log("ub controller dev not realized, bus instance verify later.\n"); + return 0; + } + + ubc_dev = &ub_bus_controller_dev->parent; + + if (ubc_dev->bus_instance_eid == UINT32_MAX) { + bus_instance_eid = ub_get_host_bus_instance_eid(&ub_bus_controller_dev->bus_instance_guid); + if (bus_instance_eid == UINT32_MAX) { + error_setg(errp, "failed to get bus instance eid.\n"); + return -1; + } + ubc_dev->bus_instance_eid = bus_instance_eid; + } + + if (ubc_dev->bus_instance_eid != dev->bus_instance_eid) { + ub_device_get_str_from_guid(&dev->guid, guid_str, UB_DEV_GUID_STRING_LENGTH + 1); + error_setg(errp, "ub dev(guid: %s) bus instance eid verify failed: expect 0x%x, actual 0x%x\n", + guid_str, ubc_dev->bus_instance_eid, dev->bus_instance_eid); + return -1; + } + + return 0; +} + static void ub_qdev_realize(DeviceState *qdev, Error **errp) { UBDevice *ub_dev = (UBDevice *)qdev; @@ -490,6 +555,7 @@ static void ub_qdev_realize(DeviceState *qdev, Error **errp) return; } + ub_dev->bus_instance_verify = ub_dev_bus_instance_verify; if (uc->realize) { uc->realize(ub_dev, &local_err); if (local_err) { @@ -916,6 +982,24 @@ uint32_t ub_interrupt_id(UBDevice *udev) return cfg1_int_cap->interrupt_id; } +static int ub_bus_instance_verify(Error **errp) +{ + BusControllerState *ubc = QLIST_FIRST(&ub_bus_controllers); + UBDevice *dev = NULL; + + QLIST_FOREACH(dev, &ubc->bus->devices, node) { + if (dev->dev_type == UB_TYPE_IBUS_CONTROLLER || + dev->bus_instance_eid == UINT32_MAX) { + continue; + } + + if (ub_dev_bus_instance_verify(dev, errp)) { + return -1; + } + } + return 0; +} + /* * now all ub device add, finally setup for all ub device. * 1. check ub device bus instance type @@ -923,6 +1007,10 @@ uint32_t ub_interrupt_id(UBDevice *udev) * */ int ub_dev_finally_setup(VirtMachineState *vms, Error **errp) { + if (ub_bus_instance_verify(errp)) { + return -1; + } + /* * Initialize the port information of all UB devices according * to the input information after all UB devices are constructed. -- Gitee From 960234ba49dcbebfe1fd92279f6743e58d4d6178 Mon Sep 17 00:00:00 2001 From: caojinhuahw Date: Fri, 14 Nov 2025 09:55:31 +0800 Subject: [PATCH 4/5] ub: prepare some function for later vfio-ub realize add ers register and iommu operator help functions Signed-off-by: caojinhuahw --- hw/ub/ub.c | 129 +++++++++++++++++++++++++++++++++++++++++---- include/hw/ub/ub.h | 7 +++ 2 files changed, 126 insertions(+), 10 deletions(-) diff --git a/hw/ub/ub.c b/hw/ub/ub.c index fbfedb6368..45a2c84968 100644 --- a/hw/ub/ub.c +++ b/hw/ub/ub.c @@ -691,16 +691,6 @@ BusControllerState *container_of_ubbus(UBBus *bus) return NULL; } -AddressSpace *ub_device_iommu_address_space(UBDevice *dev) -{ - UBBus *bus = ub_get_bus(dev); - - if (bus->iommu_ops && bus->iommu_ops->get_address_space) { - return bus->iommu_ops->get_address_space(bus, bus->iommu_opaque, dev->eid); - } - return &address_space_memory; -} - UBDevice *ub_find_device_by_id(const char *id) { BusControllerState *ubc = NULL; @@ -974,6 +964,87 @@ static int ub_dev_init_port_info_by_cmd(Error **errp) return 0; } +bool ub_guid_initialized(UbGuid *guid) +{ + if (!guid->vendor && !guid->type && !guid->version && + !guid->device_id && !guid->rsv && !guid->seq_num) { + return false; + } else { + return true; + } +} + +AddressSpace *ub_device_iommu_address_space(UBDevice *dev) +{ + UBBus *bus = ub_get_bus(dev); + + if (bus->iommu_ops && bus->iommu_ops->get_address_space) { + return bus->iommu_ops->get_address_space(bus, bus->iommu_opaque, dev->eid); + } + return &address_space_memory; +} + +int ub_device_set_iommu_device(UBDevice *dev, HostIOMMUDevice *hoid, Error **errp) +{ + UBBus *bus = ub_get_bus(dev); + + if (bus->iommu_ops && bus->iommu_ops->set_iommu_device) { + return bus->iommu_ops->set_iommu_device(bus, bus->iommu_opaque, dev->eid, hoid, errp); + } + + return 0; +} + +void ub_device_unset_iommu_device(UBDevice *dev) +{ + UBBus *bus = ub_get_bus(dev); + + if (bus->iommu_ops && bus->iommu_ops->unset_iommu_device) { + bus->iommu_ops->unset_iommu_device(bus, bus->iommu_opaque, dev->eid); + } +} + +bool ub_device_check_ummu_is_nested(UBDevice *dev) +{ + UBBus *bus = ub_get_bus(dev); + + if (bus->iommu_ops && bus->iommu_ops->ummu_is_nested) { + return bus->iommu_ops->ummu_is_nested(bus->iommu_opaque); + } + + return false; +} + +void ub_register_ers(UBDevice *dev, uint8_t region_num, MemoryRegion *memory) +{ + UBIORegion *r; + UbCfg1Basic *cfg1_basic_wmask; + uint64_t size = memory_region_size(memory); + uint64_t emulated_offset; + uint64_t wmask; + + if (region_num >= UB_NUM_REGIONS) { + qemu_log("invalid region_num %u\n", region_num); + return; + } + if (!is_power_of_2(size)) { + qemu_log("region %u is_power_of_2 check failed! size 0x%"PRIx64"\n", + region_num, size); + return; + } + + r = &dev->io_regions[region_num]; + r->addr = UINT64_MAX; + r->size = size; + r->memory = memory; + r->address_space = ub_get_bus(dev)->address_space_mem; + wmask = ~(size - 1); + /* Mark that the ers is RW */ + emulated_offset = ub_cfg_offset_to_emulated_offset(UB_CFG1_BASIC_START, true); + cfg1_basic_wmask = (UbCfg1Basic *)(dev->wmask + emulated_offset); + ub_set_quad((uint8_t *)&cfg1_basic_wmask->ers_ubba[region_num], wmask); +} + uint32_t ub_interrupt_id(UBDevice *udev) { uint64_t offset = ub_cfg_offset_to_emulated_offset(UB_CFG1_CAP4_INT_TYPE2, true); @@ -1045,3 +1116,41 @@ uint32_t ub_dev_get_ueid(UBDevice *udev) uint64_t offset = ub_cfg_offset_to_emulated_offset(UB_CFG0_DEV_UEID_OFFSET, true); return *(uint32_t *)(udev->config + offset); } + +enum UbDeviceType ub_dev_get_type(UBDevice *udev) +{ + uint64_t offset; + UbCfg1Basic *cfg1; + int baseCode; + + if (udev == NULL) { + return UB_TYPE_UNINIT; + } + + offset = ub_cfg_offset_to_emulated_offset(UB_CFG1_BASIC_START, true); + cfg1 = (UbCfg1Basic *)(udev->config + offset); + baseCode = cfg1->class_code & UB_GUID_BASE_CODE_MASK; + + switch (udev->guid.type) { + case UB_GUID_TYPE_BUS_INSTANCE: + return UB_TYPE_BUS_INSTANCE; + case UB_GUID_TYPE_BUS_CONTROLLER: + if (baseCode == UB_GUID_BASE_INSTANCE) { + return UB_TYPE_UNINIT; + } else { + return UB_TYPE_DEVICE; + } + case UB_GUID_TYPE_IBUS_CONTROLLER: + if (baseCode == UB_GUID_BASE_INSTANCE) { + return UB_TYPE_IBUS_CONTROLLER; + } else { + return UB_TYPE_IDEVICE; + } + case UB_GUID_TYPE_SWITCH: + return UB_TYPE_SWITCH; + case UB_GUID_TYPE_ISWITCH: + return UB_TYPE_ISWITCH; + default: + return UB_TYPE_UNINIT; + } +} \ No newline at end of file diff --git a/include/hw/ub/ub.h b/include/hw/ub/ub.h index ca2a54d845..800be61451 100644 --- a/include/hw/ub/ub.h +++ b/include/hw/ub/ub.h @@ -53,6 +53,7 @@ typedef struct __attribute__ ((__packed__)) UbGuid { unsigned int device_id : 16; unsigned int vendor : 16; } UbGuid; +bool ub_guid_initialized(UbGuid *guid); #define UB_DEV_GUID_STRING_LENGTH 37 void ub_device_get_str_from_guid(UbGuid *guid, char *guid_str, uint32_t str_len); bool ub_device_get_guid_from_str(UbGuid *guid, char *guid_str); @@ -253,9 +254,15 @@ static inline uint64_t ub_config_size(void) return UB_DEV_CONFIG_SPACE_TOTAL_SIZE; } AddressSpace *ub_device_iommu_address_space(UBDevice *dev); +int ub_device_set_iommu_device(UBDevice *dev, HostIOMMUDevice *hoid, Error **errp); +void ub_device_unset_iommu_device(UBDevice *dev); +bool ub_device_check_ummu_is_nested(UBDevice *dev); UBDevice *ub_find_device_by_id(const char *id); +void ub_register_ers(UBDevice *dev, uint8_t region_num, + MemoryRegion *memory); uint32_t ub_interrupt_id(UBDevice *udev); void ub_setup_iommu(UBBus *bus, const UBIOMMUOps *ops, void *opaque); uint32_t ub_dev_get_token_id(UBDevice *udev); uint32_t ub_dev_get_ueid(UBDevice *udev); +enum UbDeviceType ub_dev_get_type(UBDevice *udev); #endif -- Gitee From 69c969333282e8c2962273ff6124a166d2d37f06 Mon Sep 17 00:00:00 2001 From: caojinhuahw Date: Fri, 14 Nov 2025 10:13:09 +0800 Subject: [PATCH 5/5] ub: init usi operator function add some usi operator function, this will be used later in vfio ub device realized. Signed-off-by: caojinhuahw --- hw/intc/arm_gicv3_its_kvm.c | 2 +- hw/ub/ub_usi.c | 513 +++++++++++++++++++++++++ include/exec/memattrs.h | 2 +- include/hw/intc/arm_gicv3_its_common.h | 2 +- include/hw/ub/ub.h | 19 + include/hw/ub/ub_usi.h | 19 + 6 files changed, 554 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index f7df602cff..5f79802804 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -41,7 +41,7 @@ struct KVMARMITSClass { }; -static int kvm_its_send_msi(GICv3ITSState *s, uint32_t value, uint16_t devid) +static int kvm_its_send_msi(GICv3ITSState *s, uint32_t value, uint32_t devid) { struct kvm_msi msi; diff --git a/hw/ub/ub_usi.c b/hw/ub/ub_usi.c index 8250d853eb..0e3dae9d6e 100644 --- a/hw/ub/ub_usi.c +++ b/hw/ub/ub_usi.c @@ -21,6 +21,451 @@ #include "qemu/log.h" #include "exec/address-spaces.h" +static void usi_init_vector_notifiers(UBDevice *udev, + USIVectorUseNotifier use_notifier, + USIVectorReleaseNotifier release_notifier, + USIVectorPollNotifier poll_notifier) +{ + udev->usi_vector_use_notifier = use_notifier; + udev->usi_vector_release_notifier = release_notifier; + udev->usi_vector_poll_notifier = poll_notifier; +} + +static int usi_set_notifier_for_vector(UBDevice *udev, uint16_t vector) +{ + USIMessage msg; + + if (usi_is_masked(udev, vector)) { + return 0; + } + + msg = usi_get_message(udev, vector); + return udev->usi_vector_use_notifier(udev, vector, msg); +} + +static void usi_unset_notifier_for_vector(UBDevice *udev, uint16_t vector) +{ + if (usi_is_masked(udev, vector)) { + return; + } + udev->usi_vector_release_notifier(udev, vector); +} + +void usi_unset_vector_notifiers(UBDevice *udev) +{ + int vector; + + for (vector = 0; vector < udev->usi_entries_nr; vector++) { + usi_unset_notifier_for_vector(udev, vector); + } + + udev->usi_vector_use_notifier = NULL; + udev->usi_vector_release_notifier = NULL; + udev->usi_vector_poll_notifier = NULL; +} + +int usi_set_vector_notifiers(UBDevice *udev, + USIVectorUseNotifier use_notifier, + USIVectorReleaseNotifier release_notifier, + USIVectorPollNotifier poll_notifier) +{ + int vector, ret; + + usi_init_vector_notifiers(udev, use_notifier, release_notifier, poll_notifier); + for (vector = 0; vector < udev->usi_entries_nr; vector++) { + ret = usi_set_notifier_for_vector(udev, vector); + if (ret < 0) { + goto undo; + } + } + + qemu_log("usi set notifier for vector success.\n"); + return 0; + +undo: + qemu_log("usi set notifier for vector failed.\n"); + while (--vector >= 0) { + usi_unset_notifier_for_vector(udev, vector); + } + udev->usi_vector_use_notifier = NULL; + udev->usi_vector_release_notifier = NULL; + return ret; +} + +int usi_enabled(UBDevice *udev) +{ + uint64_t emulated_offset = ub_cfg_offset_to_emulated_offset(UB_CFG1_CAP4_INT_TYPE2_ENABLE_OFFSET, true); + uint32_t *mask = (uint32_t *)(udev->config + emulated_offset); + + return (*mask) & UB_CFG1_CAP4_INT_TYPE2_ENABLEBIT; +} + +USIMessage usi_get_message(UBDevice *udev, uint16_t vector) +{ + USIMessage msg; + uint16_t addr_index; + uint8_t *vec_table_entry = NULL; + uint8_t *addr_table_entry = NULL; + uint8_t *valid_byte = NULL; + uint8_t valid_bit; + + vec_table_entry = udev->usi_vec_table + vector * USI_VEC_TABLE_ENTRY_SIZE; + msg.data = ub_get_long(vec_table_entry); + addr_index = ub_get_word(vec_table_entry + USI_VEC_TABLE_ADDR_INDEX_OFFSET); + if (addr_index >= udev->usi_addr_table_nr) { + qemu_log("address index exceed, the index is %u, total table num is %u\n", + addr_index, udev->usi_addr_table_nr); + addr_index = udev->usi_addr_table_nr - 1; + } + + addr_table_entry = udev->usi_addr_table + addr_index * USI_ADDR_TABLE_ENTRY_SIZE; + /* check addr table entry is valid */ + msg.address = ub_get_quad(addr_table_entry); + + valid_byte = addr_table_entry + USI_ADDR_TABLE_VALID_BIT_OFFSET; + valid_bit = ub_get_byte(valid_byte); + valid_bit = valid_bit & USI_ADDR_TABLE_VALID_BIT_MASK; + if (valid_bit == 0) { + qemu_log("invalid interrupt address table, the index is %u\n", addr_index); + } + + return msg; +} + +static uint8_t usi_pending_mask(uint16_t vector) +{ + return 1 << (vector % 8); +} + +static uint8_t *usi_pending_byte(UBDevice *udev, uint16_t vector) +{ + return udev->usi_pend_table + vector / 8; +} + +int usi_is_pending(UBDevice *udev, uint16_t vector) +{ + return *usi_pending_byte(udev, vector) & usi_pending_mask(vector); +} + +void usi_set_pending(UBDevice *udev, uint16_t vector) +{ + *usi_pending_byte(udev, vector) |= usi_pending_mask(vector); +} + +void usi_clr_pending(UBDevice *udev, uint16_t vector) +{ + *usi_pending_byte(udev, vector) &= ~usi_pending_mask(vector); +} + +static void usi_fire_vector_notifier(UBDevice *udev, uint16_t vector, bool is_masked) +{ + USIMessage msg; + + if (!udev->usi_vector_use_notifier) { + qemu_log("usi_vector_use_notifier not init, do nothing.\n"); + return; + } + + if (is_masked) { + qemu_log("udev(%s %s) vector(%u) masked.\n", + udev->name, udev->qdev.id, vector); + udev->usi_vector_release_notifier(udev, vector); + return; + } + + msg = usi_get_message(udev, vector); + udev->usi_vector_use_notifier(udev, vector, msg); +} + +static void usi_handle_mask_update(UBDevice *udev, uint16_t vector, bool was_masked) +{ + bool is_masked = usi_is_masked(udev, vector); + + if (is_masked == was_masked) { + qemu_log("vector(%u) is_masked and was_masked equal, val is %d, " + "update do nothing.\n", vector, is_masked); + return; + } + + if (usi_ue_is_masked(udev)) { + qemu_log("function entity is masked, vector(%u) mask update do nothing.\n", vector); + return; + } + + usi_fire_vector_notifier(udev, vector, is_masked); + + if (!is_masked && usi_is_pending(udev, vector)) { + usi_clr_pending(udev, vector); + usi_notify(udev, vector); + } +} + +static uint64_t usi_vec_table_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + UBDevice *udev = opaque; + uint64_t val = UINT64_MAX; + + switch (size) { + case BYTE_SIZE: + val = ub_get_byte(udev->usi_vec_table + addr); + break; + case WORD_SIZE: + val = ub_get_word(udev->usi_vec_table + addr); + break; + case DWORD_SIZE: + val = ub_get_long(udev->usi_vec_table + addr); + break; + default: + qemu_log("uxpect usi vec table read size %u.\n", size); + break; + } + + qemu_log("vec table read: addr(0x%lx), size(%u) value(0x%lx).\n", addr, size, val); + return val; +} + +static void usi_vec_table_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + UBDevice *udev = opaque; + uint16_t vector = addr / USI_VEC_TABLE_ENTRY_SIZE; + bool was_masked; + + was_masked = usi_is_masked(udev, vector); + switch (size) { + case BYTE_SIZE: + ub_set_byte(udev->usi_vec_table + addr, val); + break; + case WORD_SIZE: + ub_set_word(udev->usi_vec_table + addr, val); + break; + case DWORD_SIZE: + ub_set_long(udev->usi_vec_table + addr, val); + break; + default: + qemu_log("uxpect usi vec table write size %u.\n", size); + break; + } + + qemu_log("vec table update: addr(0x%lx), size(%u), val(0x%lx).\n", addr, size, val); + usi_handle_mask_update(udev, vector, was_masked); +} + +static const MemoryRegionOps usi_vec_table_mmio_ops = { + .read = usi_vec_table_mmio_read, + .write = usi_vec_table_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static uint64_t usi_addr_table_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + UBDevice *udev = opaque; + uint64_t val = UINT64_MAX; + + switch (size) { + case BYTE_SIZE: + val = ub_get_byte(udev->usi_addr_table + addr); + break; + case WORD_SIZE: + val = ub_get_word(udev->usi_addr_table + addr); + break; + case DWORD_SIZE: + val = ub_get_long(udev->usi_addr_table + addr); + break; + default: + qemu_log("uxpect usi addr table read size %u.\n", size); + break; + } + + qemu_log("addr table read: addr(0x%lx), size(%u) value(0x%lx).\n", addr, size, val); + return val; +} + +static void usi_addr_table_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + UBDevice *udev = opaque; + + switch (size) { + case BYTE_SIZE: + ub_set_byte(udev->usi_addr_table + addr, val); + break; + case WORD_SIZE: + ub_set_word(udev->usi_addr_table + addr, val); + break; + case DWORD_SIZE: + ub_set_long(udev->usi_addr_table + addr, val); + break; + default: + qemu_log("uxpect usi addr table write size %u.\n", size); + break; + } + + qemu_log("usi addr table update: addr(0x%lx), size(%u), val(0x%lx).\n", + addr, size, val); +} + +static const MemoryRegionOps usi_addr_table_mmio_ops = { + .read = usi_addr_table_mmio_read, + .write = usi_addr_table_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + } +}; + +static uint64_t usi_pend_table_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + UBDevice *udev = opaque; + uint64_t val = UINT64_MAX; + + switch (size) { + case BYTE_SIZE: + val = ub_get_byte(udev->usi_pend_table + addr); + break; + case WORD_SIZE: + val = ub_get_word(udev->usi_pend_table + addr); + break; + case DWORD_SIZE: + val = ub_get_long(udev->usi_pend_table + addr); + break; + default: + qemu_log("expect usi pend addr table read size %u.\n", size); + break; + } + + qemu_log("pend table read: addr(0x%lx), size(%u) value(0x%lx).\n", addr, size, val); + + return val; +} + +static void usi_pend_table_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + /* do nothing now */ +} + +static const MemoryRegionOps usi_pend_table_mmio_ops = { + .read = usi_pend_table_mmio_read, + .write = usi_pend_table_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + } +}; + +bool usi_is_masked(UBDevice *udev, uint16_t vector) +{ + uint32_t offset = (uint32_t)vector * USI_VEC_TABLE_ENTRY_SIZE + USI_VEC_TABLE_MASK_OFFSET; + + return udev->usi_vec_table[offset] & USI_VEC_TABLE_MASKBIT; +} + +static void usi_mask_all(UBDevice *udev, uint16_t entries) +{ + uint16_t vector; + uint32_t offset; + bool was_masked; + + for (vector = 0; vector < entries; vector++) { + offset = (uint32_t)vector * USI_VEC_TABLE_ENTRY_SIZE + USI_VEC_TABLE_MASK_OFFSET; + was_masked = usi_is_masked(udev, vector); + udev->usi_vec_table[offset] |= USI_VEC_TABLE_MASKBIT; + usi_handle_mask_update(udev, vector, was_masked); + } +} + +static void usi_set_disable(UBDevice *udev) +{ + uint64_t emulated_offset = ub_cfg_offset_to_emulated_offset(UB_CFG1_CAP4_INT_TYPE2_ENABLE_OFFSET, true); + uint32_t *val = (uint32_t *)(udev->config + emulated_offset); + memset(val, 0, sizeof(uint32_t)); + + emulated_offset = ub_cfg_offset_to_emulated_offset(UB_CFG1_CAP4_INT_TYPE2_MASK_OFFSET, true); + val = (uint32_t *)(udev->config + emulated_offset); + memset(val, 0, sizeof(uint32_t)); + (*val) |= UB_CFG1_CAP4_INT_TYPE2_MASKBIT; + qemu_log("ub device(%s %s) disable usi\n", udev->name, udev->qdev.id); +} + +static void usi_clear_all_vectors(UBDevice *dev) +{ + int vector; + + for (vector = 0; vector < dev->usi_entries_nr; ++vector) { + usi_clr_pending(dev, vector); + } +} + +void usi_reset(UBDevice *udev) +{ + uint32_t pend_table_size = DIV_ROUND_UP(udev->usi_entries_nr, USI_PEND_TABLE_ENTRY_BIT_NUM) * + USI_PEND_TABLE_ENTRY_SIZE; + usi_clear_all_vectors(udev); + memset(udev->usi_vec_table, 0, udev->usi_entries_nr * USI_VEC_TABLE_ENTRY_SIZE); + memset(udev->usi_addr_table, 0, udev->usi_addr_table_nr * USI_ADDR_TABLE_ENTRY_SIZE); + memset(udev->usi_pend_table, 0, pend_table_size); + + usi_mask_all(udev, udev->usi_entries_nr); + usi_set_disable(udev); +} + +void usi_init(UBDevice *udev, uint16_t vec_table_num, uint16_t addr_table_num, + uint64_t vec_table_start_addr, uint64_t addr_table_start_addr, + uint64_t pend_table_start_addr, MemoryRegion *fer0_mr) +{ + uint32_t vec_table_size, addr_table_size, pend_table_size; + + vec_table_size = (uint32_t)vec_table_num * USI_VEC_TABLE_ENTRY_SIZE; + addr_table_size = (uint32_t)addr_table_num * USI_ADDR_TABLE_ENTRY_SIZE; + pend_table_size = DIV_ROUND_UP(vec_table_num, USI_PEND_TABLE_ENTRY_BIT_NUM) * + USI_PEND_TABLE_ENTRY_SIZE; + + udev->usi_entries_nr = vec_table_num; + udev->usi_addr_table_nr = addr_table_num; + udev->usi_vec_table = g_malloc0(vec_table_size); + udev->usi_addr_table = g_malloc0(addr_table_size); + udev->usi_pend_table = g_malloc0(pend_table_size); + + usi_mask_all(udev, vec_table_num); + usi_set_disable(udev); + + memory_region_init_io(&udev->usi_vec_table_mmio, OBJECT(udev), &usi_vec_table_mmio_ops, + udev, "usi-vec-table", vec_table_size); + memory_region_add_subregion(fer0_mr, vec_table_start_addr, &udev->usi_vec_table_mmio); + memory_region_init_io(&udev->usi_addr_table_mmio, OBJECT(udev), &usi_addr_table_mmio_ops, + udev, "usi-addr-table", addr_table_size); + memory_region_add_subregion(fer0_mr, addr_table_start_addr, &udev->usi_addr_table_mmio); + memory_region_init_io(&udev->usi_pend_table_mmio, OBJECT(udev), &usi_pend_table_mmio_ops, + udev, "usi-pend-table", pend_table_size); + memory_region_add_subregion(fer0_mr, pend_table_start_addr, &udev->usi_pend_table_mmio); +} + +void usi_uninit(UBDevice *udev, MemoryRegion *fer0_mr) +{ + g_free(udev->usi_vec_table); + memory_region_del_subregion(fer0_mr, &udev->usi_vec_table_mmio); + g_free(udev->usi_addr_table); + memory_region_del_subregion(fer0_mr, &udev->usi_addr_table_mmio); + g_free(udev->usi_pend_table); + memory_region_del_subregion(fer0_mr, &udev->usi_pend_table_mmio); +} + void usi_send_message(USIMessage *msg, uint32_t interrupt_id, UBDevice *udev) { MemTxAttrs attrs = {}; @@ -36,3 +481,71 @@ void usi_send_message(USIMessage *msg, uint32_t interrupt_id, UBDevice *udev) qemu_log("usi notify success: interrupt_id %u eventid %u gicv3_its 0x%lx\n", interrupt_id, msg->data, msg->address); } + +void usi_notify(UBDevice *udev, uint16_t vector) +{ + USIMessage msg; + + /* check vector is valid later */ + + if (usi_is_masked(udev, vector) || usi_ue_is_masked(udev)) { + usi_set_pending(udev, vector); + return; + } + + msg = usi_get_message(udev, vector); + usi_send_message(&msg, ub_interrupt_id(udev), udev); +} + +int usi_ue_is_masked(UBDevice *udev) +{ + uint64_t emulated_offset = ub_cfg_offset_to_emulated_offset(UB_CFG1_CAP4_INT_TYPE2_MASK_OFFSET, true); + uint32_t *mask = (uint32_t *)(udev->config + emulated_offset); + + return (*mask) & UB_CFG1_CAP4_INT_TYPE2_MASKBIT; +} + +static void usi_ue_each_vector_update(UBDevice *udev, uint16_t vector, bool ue_is_masked) +{ + USIMessage msg; + + if (usi_is_masked(udev, vector)) { + qemu_log("vector(%u) is masked, do nothing.\n", vector); + return; + } + + if (!udev->usi_vector_use_notifier) { + qemu_log("usi_vector_use_notifier not init, do nothing.\n"); + return; + } + + if (ue_is_masked) { + udev->usi_vector_release_notifier(udev, vector); + return; + } + + msg = usi_get_message(udev, vector); + udev->usi_vector_use_notifier(udev, vector, msg); + + if (usi_is_pending(udev, vector)) { + qemu_log("start udev(%s) vector(%u) pending interrupt notify.\n", udev->name, vector); + usi_clr_pending(udev, vector); + usi_notify(udev, vector); + } +} + +void usi_handle_ue_mask_update(UBDevice *udev, bool was_masked) +{ + bool is_masked = usi_ue_is_masked(udev); + uint16_t vector; + + if (is_masked == was_masked) { + qemu_log("ue is_masked and was_masked equal, val is %d, " + "update do nothing.\n", is_masked); + return; + } + + for (vector = 0; vector < udev->usi_entries_nr; vector++) { + usi_ue_each_vector_update(udev, vector, is_masked); + } +} \ No newline at end of file diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index d04170aa27..6dded7df43 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -51,7 +51,7 @@ typedef struct MemTxAttrs { */ unsigned int memory:1; /* Requester ID (for MSI for example) */ - unsigned int requester_id:16; + unsigned int requester_id; /* Invert endianness for this page */ unsigned int byte_swap:1; /* diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h index 7dc712b38d..e072c36cca 100644 --- a/include/hw/intc/arm_gicv3_its_common.h +++ b/include/hw/intc/arm_gicv3_its_common.h @@ -117,7 +117,7 @@ struct GICv3ITSCommonClass { SysBusDeviceClass parent_class; /*< public >*/ - int (*send_msi)(GICv3ITSState *s, uint32_t data, uint16_t devid); + int (*send_msi)(GICv3ITSState *s, uint32_t data, uint32_t devid); void (*pre_save)(GICv3ITSState *s); void (*post_load)(GICv3ITSState *s); }; diff --git a/include/hw/ub/ub.h b/include/hw/ub/ub.h index 800be61451..9a5d4c6c33 100644 --- a/include/hw/ub/ub.h +++ b/include/hw/ub/ub.h @@ -142,6 +142,9 @@ typedef void UBConfigReadFunc(UBDevice *dev, uint64_t offset, uint32_t *val, uint32_t dw_mask); typedef void UBConfigWriteFunc(UBDevice *dev, uint64_t offset, uint32_t *val, uint32_t dw_mask); +typedef int (*USIVectorUseNotifier)(UBDevice *udev, uint16_t vector, USIMessage msg); +typedef void (*USIVectorReleaseNotifier)(UBDevice *udev, uint16_t vector); +typedef void (*USIVectorPollNotifier)(UBDevice *dev, uint16_t vector_start, uint16_t vector_end); struct UBDevice { DeviceState qdev; @@ -165,6 +168,22 @@ struct UBDevice { UBConfigWriteFunc *config_write; int (* bus_instance_verify)(UBDevice *dev, Error **errp); + /* usi entries */ + uint16_t usi_entries_nr; + uint16_t usi_addr_table_nr; + /* Space to store usi vec table & addr table & pending bit array */ + uint8_t *usi_vec_table; + uint8_t *usi_addr_table; + uint8_t *usi_pend_table; + /* MemoryRegion container for usi vec table & addr table & pending bit array */ + MemoryRegion usi_vec_table_mmio; + MemoryRegion usi_addr_table_mmio; + MemoryRegion usi_pend_table_mmio; + /* USI notifiers */ + USIVectorUseNotifier usi_vector_use_notifier; + USIVectorReleaseNotifier usi_vector_release_notifier; + USIVectorPollNotifier usi_vector_poll_notifier; + QLIST_ENTRY(UBDevice) node; }; diff --git a/include/hw/ub/ub_usi.h b/include/hw/ub/ub_usi.h index 96332e5850..5fcefe8f7f 100644 --- a/include/hw/ub/ub_usi.h +++ b/include/hw/ub/ub_usi.h @@ -24,6 +24,25 @@ struct USIMessage { uint32_t data; }; +void usi_init(UBDevice *udev, uint16_t vec_table_num, uint16_t addr_table_num, + uint64_t vec_table_start_addr, uint64_t addr_table_start_addr, + uint64_t pend_table_start_addr, MemoryRegion *fer0_mr); +void usi_uninit(UBDevice *udev, MemoryRegion *fer0_mr); +bool usi_is_masked(UBDevice *udev, uint16_t vector); +USIMessage usi_get_message(UBDevice *udev, uint16_t vector); +int usi_enabled(UBDevice *udev); +int usi_set_vector_notifiers(UBDevice *udev, + USIVectorUseNotifier use_notifier, + USIVectorReleaseNotifier release_notifier, + USIVectorPollNotifier poll_notifier); +void usi_unset_vector_notifiers(UBDevice *udev); +void usi_notify(UBDevice *udev, uint16_t vector); +int usi_is_pending(UBDevice *udev, uint16_t vector); +void usi_set_pending(UBDevice *udev, uint16_t vector); +void usi_clr_pending(UBDevice *udev, uint16_t vector); +int usi_ue_is_masked(UBDevice *udev); +void usi_handle_ue_mask_update(UBDevice *udev, bool was_masked); void usi_send_message(USIMessage *msg, uint32_t interrupt_id, UBDevice *udev); +void usi_reset(UBDevice *dev); #endif -- Gitee