From 6daf1171b50a081983a92acec8dd9f72d1781e41 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sat, 26 Mar 2022 00:24:02 -0700 Subject: [PATCH] Improve ioctl implementation to support arbitrary number of regions --- modules/mqnic/mqnic_dev.c | 160 +++++++++++++++++++------- modules/mqnic/mqnic_ioctl.h | 42 ++++++- utils/mqnic-dump.c | 6 + utils/mqnic.c | 222 ++++++++++++++++++++++++++++++------ utils/mqnic.h | 8 ++ 5 files changed, 357 insertions(+), 81 deletions(-) diff --git a/modules/mqnic/mqnic_dev.c b/modules/mqnic/mqnic_dev.c index 4ff6aa1b4..72bbf1d0c 100644 --- a/modules/mqnic/mqnic_dev.c +++ b/modules/mqnic/mqnic_dev.c @@ -54,39 +54,52 @@ static int mqnic_release(struct inode *inode, struct file *file) return 0; } -static int mqnic_map_registers(struct mqnic_dev *mqnic, struct vm_area_struct *vma) -{ - size_t map_size = vma->vm_end - vma->vm_start; - int ret; - - if (map_size > mqnic->hw_regs_size) { - dev_err(mqnic->dev, "%s: Tried to map registers region with wrong size %lu (expected <= %llu)", - __func__, vma->vm_end - vma->vm_start, mqnic->hw_regs_size); - return -EINVAL; - } - - ret = remap_pfn_range(vma, vma->vm_start, mqnic->hw_regs_phys >> PAGE_SHIFT, - map_size, pgprot_noncached(vma->vm_page_prot)); - - if (ret) - dev_err(mqnic->dev, "%s: remap_pfn_range failed for registers region", __func__); - else - dev_dbg(mqnic->dev, "%s: Mapped registers region at phys: 0x%pap, virt: 0x%p", - __func__, &mqnic->hw_regs_phys, (void *)vma->vm_start); - - return ret; -} - static int mqnic_mmap(struct file *file, struct vm_area_struct *vma) { struct miscdevice *miscdev = file->private_data; struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev); + int index; + u64 pgoff, req_len, req_start; - if (vma->vm_pgoff == 0) - return mqnic_map_registers(mqnic, vma); + index = vma->vm_pgoff >> (40 - PAGE_SHIFT); + req_len = vma->vm_end - vma->vm_start; + pgoff = vma->vm_pgoff & ((1U << (40 - PAGE_SHIFT)) - 1); + req_start = pgoff << PAGE_SHIFT; + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + + if ((vma->vm_flags & VM_SHARED) == 0) + return -EINVAL; + + switch (index) { + case 0: + if (req_start + req_len > mqnic->hw_regs_size) + return -EINVAL; + + return io_remap_pfn_range(vma, vma->vm_start, + (mqnic->hw_regs_phys >> PAGE_SHIFT) + pgoff, + req_len, pgprot_noncached(vma->vm_page_prot)); + case 1: + if (req_start + req_len > mqnic->app_hw_regs_size) + return -EINVAL; + + return io_remap_pfn_range(vma, vma->vm_start, + (mqnic->app_hw_regs_phys >> PAGE_SHIFT) + pgoff, + req_len, pgprot_noncached(vma->vm_page_prot)); + case 2: + if (req_start + req_len > mqnic->ram_hw_regs_size) + return -EINVAL; + + return io_remap_pfn_range(vma, vma->vm_start, + (mqnic->ram_hw_regs_phys >> PAGE_SHIFT) + pgoff, + req_len, pgprot_noncached(vma->vm_page_prot)); + default: + dev_err(mqnic->dev, "%s: Tried to map an unknown region at page offset 0x%lx", + __func__, vma->vm_pgoff); + return -EINVAL; + } - dev_err(mqnic->dev, "%s: Tried to map an unknown region at page offset %lu", - __func__, vma->vm_pgoff); return -EINVAL; } @@ -94,29 +107,90 @@ static long mqnic_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct miscdevice *miscdev = file->private_data; struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev); + size_t minsz; - if (_IOC_TYPE(cmd) != MQNIC_IOCTL_TYPE) - return -ENOTTY; + if (cmd == MQNIC_IOCTL_GET_API_VERSION) { + // Get API version + return MQNIC_IOCTL_API_VERSION; + } else if (cmd == MQNIC_IOCTL_GET_DEVICE_INFO) { + // Get device information + struct mqnic_ioctl_device_info info; - switch (cmd) { - case MQNIC_IOCTL_INFO: - { - struct mqnic_ioctl_info ctl; + minsz = offsetofend(struct mqnic_ioctl_device_info, num_irqs); - ctl.fw_id = mqnic->fw_id; - ctl.fw_ver = mqnic->fw_ver; - ctl.board_id = mqnic->board_id; - ctl.board_ver = mqnic->board_ver; - ctl.regs_size = mqnic->hw_regs_size; + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; - if (copy_to_user((void __user *)arg, &ctl, sizeof(ctl)) != 0) - return -EFAULT; + if (info.argsz < minsz) + return -EINVAL; - return 0; + info.flags = 0; + info.fw_id = mqnic->fw_id; + info.fw_ver = mqnic->fw_ver; + info.board_id = mqnic->board_id; + info.board_ver = mqnic->board_ver; + info.build_date = mqnic->build_date; + info.git_hash = mqnic->git_hash; + info.rel_info = mqnic->rel_info; + info.num_regions = 3; + info.num_irqs = 0; + + return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; + + } else if (cmd == MQNIC_IOCTL_GET_REGION_INFO) { + // Get region information + struct mqnic_ioctl_region_info info; + + minsz = offsetofend(struct mqnic_ioctl_region_info, name); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = 0; + info.type = MQNIC_REGION_TYPE_UNIMPLEMENTED; + info.next = 0; + info.child = 0; + info.size = 0; + info.offset = ((u64)info.index) << 40; + info.name[0] = 0; + + switch (info.index) { + case 0: + info.type = MQNIC_REGION_TYPE_NIC_CTRL; + info.next = 1; + info.child = 0; + info.size = mqnic->hw_regs_size; + info.offset = ((u64)info.index) << 40; + strlcpy(info.name, "ctrl", sizeof(info.name)); + break; + case 1: + info.type = MQNIC_REGION_TYPE_APP_CTRL; + info.next = 2; + info.child = 0; + info.size = mqnic->app_hw_regs_size; + info.offset = ((u64)info.index) << 40; + strlcpy(info.name, "app", sizeof(info.name)); + break; + case 2: + info.type = MQNIC_REGION_TYPE_RAM; + info.next = 3; + info.child = 0; + info.size = mqnic->ram_hw_regs_size; + info.offset = ((u64)info.index) << 40; + strlcpy(info.name, "ram", sizeof(info.name)); + break; + default: + return -EINVAL; } - default: - return -ENOTTY; + + return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; + } + + return -EINVAL; } const struct file_operations mqnic_fops = { diff --git a/modules/mqnic/mqnic_ioctl.h b/modules/mqnic/mqnic_ioctl.h index 9956555ce..3c31e203e 100644 --- a/modules/mqnic/mqnic_ioctl.h +++ b/modules/mqnic/mqnic_ioctl.h @@ -38,16 +38,52 @@ #include +#define MQNIC_IOCTL_API_VERSION 0 + #define MQNIC_IOCTL_TYPE 0x88 +#define MQNIC_IOCTL_BASE 0xC0 -#define MQNIC_IOCTL_INFO _IOR(MQNIC_IOCTL_TYPE, 0xf0, struct mqnic_ioctl_info) +enum { + MQNIC_REGION_TYPE_UNIMPLEMENTED = 0x00000000, + MQNIC_REGION_TYPE_CTRL = 0x00001000, + MQNIC_REGION_TYPE_NIC_CTRL = 0x00001001, + MQNIC_REGION_TYPE_APP_CTRL = 0x00001002, + MQNIC_REGION_TYPE_RAM = 0x00002000 +}; -struct mqnic_ioctl_info { +// get API version +#define MQNIC_IOCTL_GET_API_VERSION _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 0) + +// get device information +struct mqnic_ioctl_device_info { + __u32 argsz; + __u32 flags; __u32 fw_id; __u32 fw_ver; __u32 board_id; __u32 board_ver; - size_t regs_size; + __u32 build_date; + __u32 git_hash; + __u32 rel_info; + __u32 num_regions; + __u32 num_irqs; }; +#define MQNIC_IOCTL_GET_DEVICE_INFO _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 1) + +// get region information +struct mqnic_ioctl_region_info { + __u32 argsz; + __u32 flags; + __u32 index; + __u32 type; + __u32 next; + __u32 child; + __u64 size; + __u64 offset; + __u8 name[32]; +}; + +#define MQNIC_IOCTL_GET_REGION_INFO _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 2) + #endif /* MQNIC_IOCTL_H */ diff --git a/utils/mqnic-dump.c b/utils/mqnic-dump.c index b16a87ad0..ad1668ce7 100644 --- a/utils/mqnic-dump.c +++ b/utils/mqnic-dump.c @@ -106,6 +106,12 @@ int main(int argc, char *argv[]) printf("PCIe ID: %s\n", ptr+1); } + printf("Control region size: %lu\n", dev->regs_size); + if (dev->app_regs_size) + printf("Application region size: %lu\n", dev->app_regs_size); + if (dev->ram_size) + printf("RAM region size: %lu\n", dev->ram_size); + printf("Device-level register blocks:\n"); for (struct reg_block *rb = dev->rb_list; rb->type && rb->version; rb++) printf(" type 0x%08x (v %d.%d.%d.%d)\n", rb->type, rb->version >> 24, diff --git a/utils/mqnic.c b/utils/mqnic.c index 46d8e6c28..aab221ea5 100644 --- a/utils/mqnic.c +++ b/utils/mqnic.c @@ -75,26 +75,6 @@ static int mqnic_try_open(struct mqnic *dev, const char *fmt, ...) goto fail_open; } - if (fstat(dev->fd, &st)) - { - perror("fstat failed"); - goto fail_fstat; - } - - dev->regs_size = st.st_size; - - if (dev->regs_size == 0) - { - struct mqnic_ioctl_info info; - if (ioctl(dev->fd, MQNIC_IOCTL_INFO, &info) != 0) - { - perror("MQNIC_IOCTL_INFO ioctl failed"); - goto fail_ioctl; - } - - dev->regs_size = info.regs_size; - } - // determine sysfs path of PCIe device // first, try to find via miscdevice ptr = strrchr(dev->device_path, '/'); @@ -123,27 +103,173 @@ static int mqnic_try_open(struct mqnic *dev, const char *fmt, ...) dev->pci_device_path[0] = 0; } - // map registers - dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, 0); - if (dev->regs == MAP_FAILED) + if (fstat(dev->fd, &st)) { - perror("mmap regs failed"); - goto fail_mmap_regs; + perror("fstat failed"); + goto fail_fstat; } - if (dev->pci_device_path[0] && mqnic_reg_read32(dev->regs, 4) == 0xffffffff) + dev->regs_size = st.st_size; + + if (dev->regs_size == 0) { - // if we were given a PCIe resource, then we may need to enable the device - snprintf(path, sizeof(path), "%s/enable", dev->pci_device_path); + // miscdevice + off_t regs_offset = 0; + off_t app_regs_offset = 0; + off_t ram_offset = 0; - if (access(path, W_OK) == 0) + if (ioctl(dev->fd, MQNIC_IOCTL_GET_API_VERSION, 0) != MQNIC_IOCTL_API_VERSION) { - FILE *fp = fopen(path, "w"); + fprintf(stderr, "Error: unknown API version\n"); + goto fail_ioctl; + } - if (fp) + struct mqnic_ioctl_device_info device_info; + device_info.argsz = sizeof(device_info); + device_info.flags = 0; + + if (ioctl(dev->fd, MQNIC_IOCTL_GET_DEVICE_INFO, &device_info) != 0) + { + perror("MQNIC_IOCTL_GET_DEVICE_INFO ioctl failed"); + goto fail_ioctl; + } + + struct mqnic_ioctl_region_info region_info; + region_info.argsz = sizeof(region_info); + region_info.flags = 0; + region_info.index = 0; + + for (region_info.index = 0; region_info.index < device_info.num_regions; region_info.index++) + { + if (ioctl(dev->fd, MQNIC_IOCTL_GET_REGION_INFO, ®ion_info) != 0) { - fputc('1', fp); - fclose(fp); + perror("MQNIC_IOCTL_GET_REGION_INFO ioctl failed"); + goto fail_ioctl; + } + + switch (region_info.type) { + case MQNIC_REGION_TYPE_NIC_CTRL: + regs_offset = region_info.offset; + dev->regs_size = region_info.size; + break; + case MQNIC_REGION_TYPE_APP_CTRL: + app_regs_offset = region_info.offset; + dev->app_regs_size = region_info.size; + break; + case MQNIC_REGION_TYPE_RAM: + ram_offset = region_info.offset; + dev->ram_size = region_info.size; + break; + default: + break; + } + } + + // map registers + dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, regs_offset); + if (dev->regs == MAP_FAILED) + { + perror("mmap regs failed"); + goto fail_mmap_regs; + } + + // map application section registers + if (dev->app_regs_size) + { + dev->app_regs = (volatile uint8_t *)mmap(NULL, dev->app_regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, app_regs_offset); + if (dev->app_regs == MAP_FAILED) + { + perror("mmap app regs failed"); + goto fail_mmap_regs; + } + } + + // map RAM + if (dev->ram_size) + { + dev->ram = (volatile uint8_t *)mmap(NULL, dev->ram_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, ram_offset); + if (dev->ram == MAP_FAILED) + { + perror("mmap RAM failed"); + goto fail_mmap_regs; + } + } + } + else + { + // PCIe resource + + // map registers + dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, 0); + if (dev->regs == MAP_FAILED) + { + perror("mmap regs failed"); + goto fail_mmap_regs; + } + + if (dev->pci_device_path[0]) + { + // map application section registers + snprintf(path, sizeof(path), "%s/resource2", dev->pci_device_path); + + dev->app_fd = open(path, O_RDWR); + + if (dev->app_fd >= 0) + { + if (fstat(dev->app_fd, &st)) + { + perror("fstat failed"); + goto fail_fstat; + } + + dev->app_regs_size = st.st_size; + + dev->app_regs = (volatile uint8_t *)mmap(NULL, dev->app_regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->app_fd, 0); + if (dev->app_regs == MAP_FAILED) + { + perror("mmap app regs failed"); + goto fail_mmap_regs; + } + } + + // map RAM + snprintf(path, sizeof(path), "%s/resource4", dev->pci_device_path); + + dev->ram_fd = open(path, O_RDWR); + + if (dev->ram_fd >= 0) + { + if (fstat(dev->ram_fd, &st)) + { + perror("fstat failed"); + goto fail_fstat; + } + + dev->ram_size = st.st_size; + + dev->ram = (volatile uint8_t *)mmap(NULL, dev->ram_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->ram_fd, 0); + if (dev->ram == MAP_FAILED) + { + perror("mmap RAM failed"); + goto fail_mmap_regs; + } + } + + if (dev->pci_device_path[0] && mqnic_reg_read32(dev->regs, 4) == 0xffffffff) + { + // if we were given a PCIe resource, then we may need to enable the device + snprintf(path, sizeof(path), "%s/enable", dev->pci_device_path); + + if (access(path, W_OK) == 0) + { + FILE *fp = fopen(path, "w"); + + if (fp) + { + fputc('1', fp); + fclose(fp); + } + } } } } @@ -177,11 +303,24 @@ fail_enum: if (dev->rb_list) free_reg_block_list(dev->rb_list); fail_reset: - munmap((void *)dev->regs, dev->regs_size); fail_mmap_regs: + if (dev->ram) + munmap((void *)dev->ram, dev->ram_size); + dev->ram = NULL; + if (dev->app_regs) + munmap((void *)dev->app_regs, dev->app_regs_size); + dev->app_regs = NULL; + if (dev->regs) + munmap((void *)dev->regs, dev->regs_size); + dev->regs = NULL; fail_ioctl: fail_fstat: close(dev->fd); + dev->fd = -1; + close(dev->app_fd); + dev->app_fd = -1; + close(dev->ram_fd); + dev->ram_fd = -1; fail_open: return -1; } @@ -227,6 +366,10 @@ struct mqnic *mqnic_open(const char *dev_name) goto fail_alloc; } + dev->fd = -1; + dev->app_fd = -1; + dev->ram_fd = -1; + // absolute path if (mqnic_try_open(dev, "%s", dev_name) == 0) goto open; @@ -322,8 +465,17 @@ void mqnic_close(struct mqnic *dev) if (dev->rb_list) free_reg_block_list(dev->rb_list); - munmap((void *)dev->regs, dev->regs_size); + if (dev->ram) + munmap((void *)dev->ram, dev->ram_size); + if (dev->app_regs) + munmap((void *)dev->app_regs, dev->app_regs_size); + if (dev->regs) + munmap((void *)dev->regs, dev->regs_size); + close(dev->fd); + close(dev->app_fd); + close(dev->ram_fd); + free(dev); } diff --git a/utils/mqnic.h b/utils/mqnic.h index 73c3cb760..0f76a12ee 100644 --- a/utils/mqnic.h +++ b/utils/mqnic.h @@ -125,10 +125,18 @@ struct mqnic_if { struct mqnic { int fd; + int app_fd; + int ram_fd; size_t regs_size; volatile uint8_t *regs; + size_t app_regs_size; + volatile uint8_t *app_regs; + + size_t ram_size; + volatile uint8_t *ram; + struct reg_block *rb_list; struct reg_block *fw_id_rb; struct reg_block *if_rb;