diff options
-rw-r--r-- | arch/um/Kconfig | 1 | ||||
-rw-r--r-- | arch/um/drivers/virt-pci.c | 108 | ||||
-rw-r--r-- | arch/um/drivers/virtio_uml.c | 5 | ||||
-rw-r--r-- | arch/um/kernel/skas/clone.c | 3 | ||||
-rw-r--r-- | arch/x86/um/shared/sysdep/stub_32.h | 12 | ||||
-rw-r--r-- | arch/x86/um/shared/sysdep/stub_64.h | 12 | ||||
-rw-r--r-- | arch/x86/um/stub_segv.c | 3 | ||||
-rw-r--r-- | fs/hostfs/hostfs_kern.c | 1 | ||||
-rw-r--r-- | include/uapi/linux/virtio_pcidev.h | 5 | ||||
-rw-r--r-- | lib/logic_iomem.c | 16 |
10 files changed, 124 insertions, 42 deletions
diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 8de2646fbdb1..c18b45f75d41 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -24,6 +24,7 @@ config UML select SET_FS select TRACE_IRQFLAGS_SUPPORT select TTY # Needed for line.c + select HAVE_ARCH_VMAP_STACK config MMU bool diff --git a/arch/um/drivers/virt-pci.c b/arch/um/drivers/virt-pci.c index 0b802834f40a..c08066633023 100644 --- a/arch/um/drivers/virt-pci.c +++ b/arch/um/drivers/virt-pci.c @@ -56,6 +56,13 @@ static unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)]; #define UM_VIRT_PCI_MAXDELAY 40000 +struct um_pci_message_buffer { + struct virtio_pcidev_msg hdr; + u8 data[8]; +}; + +static struct um_pci_message_buffer __percpu *um_pci_msg_bufs; + static int um_pci_send_cmd(struct um_pci_device *dev, struct virtio_pcidev_msg *cmd, unsigned int cmd_size, @@ -68,11 +75,12 @@ static int um_pci_send_cmd(struct um_pci_device *dev, [1] = extra ? &extra_sg : &in_sg, [2] = extra ? &in_sg : NULL, }; + struct um_pci_message_buffer *buf; int delay_count = 0; int ret, len; bool posted; - if (WARN_ON(cmd_size < sizeof(*cmd))) + if (WARN_ON(cmd_size < sizeof(*cmd) || cmd_size > sizeof(*buf))) return -EINVAL; switch (cmd->op) { @@ -88,6 +96,9 @@ static int um_pci_send_cmd(struct um_pci_device *dev, break; } + buf = get_cpu_var(um_pci_msg_bufs); + memcpy(buf, cmd, cmd_size); + if (posted) { u8 *ncmd = kmalloc(cmd_size + extra_size, GFP_ATOMIC); @@ -102,7 +113,10 @@ static int um_pci_send_cmd(struct um_pci_device *dev, } else { /* try without allocating memory */ posted = false; + cmd = (void *)buf; } + } else { + cmd = (void *)buf; } sg_init_one(&out_sg, cmd, cmd_size); @@ -118,11 +132,12 @@ static int um_pci_send_cmd(struct um_pci_device *dev, posted ? cmd : HANDLE_NO_FREE(cmd), GFP_ATOMIC); if (ret) - return ret; + goto out; if (posted) { virtqueue_kick(dev->cmd_vq); - return 0; + ret = 0; + goto out; } /* kick and poll for getting a response on the queue */ @@ -148,6 +163,8 @@ static int um_pci_send_cmd(struct um_pci_device *dev, } clear_bit(UM_PCI_STAT_WAITING, &dev->status); +out: + put_cpu_var(um_pci_msg_bufs); return ret; } @@ -161,12 +178,17 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset, .size = size, .addr = offset, }; - /* maximum size - we may only use parts of it */ - u8 data[8]; + /* buf->data is maximum size - we may only use parts of it */ + struct um_pci_message_buffer *buf; + u8 *data; + unsigned long ret = ~0ULL; if (!dev) return ~0ULL; + buf = get_cpu_var(um_pci_msg_bufs); + data = buf->data; + memset(data, 0xff, sizeof(data)); switch (size) { @@ -179,27 +201,34 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset, break; default: WARN(1, "invalid config space read size %d\n", size); - return ~0ULL; + goto out; } - if (um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, - data, sizeof(data))) - return ~0ULL; + if (um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, data, 8)) + goto out; switch (size) { case 1: - return data[0]; + ret = data[0]; + break; case 2: - return le16_to_cpup((void *)data); + ret = le16_to_cpup((void *)data); + break; case 4: - return le32_to_cpup((void *)data); + ret = le32_to_cpup((void *)data); + break; #ifdef CONFIG_64BIT case 8: - return le64_to_cpup((void *)data); + ret = le64_to_cpup((void *)data); + break; #endif default: - return ~0ULL; + break; } + +out: + put_cpu_var(um_pci_msg_bufs); + return ret; } static void um_pci_cfgspace_write(void *priv, unsigned int offset, int size, @@ -272,8 +301,13 @@ static void um_pci_bar_copy_from(void *priv, void *buffer, static unsigned long um_pci_bar_read(void *priv, unsigned int offset, int size) { - /* maximum size - we may only use parts of it */ - u8 data[8]; + /* buf->data is maximum size - we may only use parts of it */ + struct um_pci_message_buffer *buf; + u8 *data; + unsigned long ret = ~0ULL; + + buf = get_cpu_var(um_pci_msg_bufs); + data = buf->data; switch (size) { case 1: @@ -285,25 +319,33 @@ static unsigned long um_pci_bar_read(void *priv, unsigned int offset, break; default: WARN(1, "invalid config space read size %d\n", size); - return ~0ULL; + goto out; } um_pci_bar_copy_from(priv, data, offset, size); switch (size) { case 1: - return data[0]; + ret = data[0]; + break; case 2: - return le16_to_cpup((void *)data); + ret = le16_to_cpup((void *)data); + break; case 4: - return le32_to_cpup((void *)data); + ret = le32_to_cpup((void *)data); + break; #ifdef CONFIG_64BIT case 8: - return le64_to_cpup((void *)data); + ret = le64_to_cpup((void *)data); + break; #endif default: - return ~0ULL; + break; } + +out: + put_cpu_var(um_pci_msg_bufs); + return ret; } static void um_pci_bar_copy_to(void *priv, unsigned int offset, @@ -810,7 +852,7 @@ void *pci_root_bus_fwnode(struct pci_bus *bus) return um_pci_fwnode; } -int um_pci_init(void) +static int um_pci_init(void) { int err, i; @@ -823,10 +865,16 @@ int um_pci_init(void) "No virtio device ID configured for PCI - no PCI support\n")) return 0; - bridge = pci_alloc_host_bridge(0); - if (!bridge) + um_pci_msg_bufs = alloc_percpu(struct um_pci_message_buffer); + if (!um_pci_msg_bufs) return -ENOMEM; + bridge = pci_alloc_host_bridge(0); + if (!bridge) { + err = -ENOMEM; + goto free; + } + um_pci_fwnode = irq_domain_alloc_named_fwnode("um-pci"); if (!um_pci_fwnode) { err = -ENOMEM; @@ -878,18 +926,22 @@ free: irq_domain_remove(um_pci_inner_domain); if (um_pci_fwnode) irq_domain_free_fwnode(um_pci_fwnode); - pci_free_resource_list(&bridge->windows); - pci_free_host_bridge(bridge); + if (bridge) { + pci_free_resource_list(&bridge->windows); + pci_free_host_bridge(bridge); + } + free_percpu(um_pci_msg_bufs); return err; } module_init(um_pci_init); -void um_pci_exit(void) +static void um_pci_exit(void) { unregister_virtio_driver(&um_pci_virtio_driver); irq_domain_remove(um_pci_msi_domain); irq_domain_remove(um_pci_inner_domain); pci_free_resource_list(&bridge->windows); pci_free_host_bridge(bridge); + free_percpu(um_pci_msg_bufs); } module_exit(um_pci_exit); diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c index 4412d6febade..d51e445df797 100644 --- a/arch/um/drivers/virtio_uml.c +++ b/arch/um/drivers/virtio_uml.c @@ -27,6 +27,7 @@ #include <linux/virtio_config.h> #include <linux/virtio_ring.h> #include <linux/time-internal.h> +#include <linux/virtio-uml.h> #include <shared/as-layout.h> #include <irq_kern.h> #include <init.h> @@ -1139,7 +1140,7 @@ static int virtio_uml_probe(struct platform_device *pdev) rc = os_connect_socket(pdata->socket_path); } while (rc == -EINTR); if (rc < 0) - return rc; + goto error_free; vu_dev->sock = rc; spin_lock_init(&vu_dev->sock_lock); @@ -1160,6 +1161,8 @@ static int virtio_uml_probe(struct platform_device *pdev) error_init: os_close_file(vu_dev->sock); +error_free: + kfree(vu_dev); return rc; } diff --git a/arch/um/kernel/skas/clone.c b/arch/um/kernel/skas/clone.c index 5afac0fef24e..ff5061f29167 100644 --- a/arch/um/kernel/skas/clone.c +++ b/arch/um/kernel/skas/clone.c @@ -24,8 +24,7 @@ void __attribute__ ((__section__ (".__syscall_stub"))) stub_clone_handler(void) { - int stack; - struct stub_data *data = (void *) ((unsigned long)&stack & ~(UM_KERN_PAGE_SIZE - 1)); + struct stub_data *data = get_stub_page(); long err; err = stub_syscall2(__NR_clone, CLONE_PARENT | CLONE_FILES | SIGCHLD, diff --git a/arch/x86/um/shared/sysdep/stub_32.h b/arch/x86/um/shared/sysdep/stub_32.h index b95db9daf0e8..4c6c2be0c899 100644 --- a/arch/x86/um/shared/sysdep/stub_32.h +++ b/arch/x86/um/shared/sysdep/stub_32.h @@ -101,4 +101,16 @@ static inline void remap_stack_and_trap(void) "memory"); } +static __always_inline void *get_stub_page(void) +{ + unsigned long ret; + + asm volatile ( + "movl %%esp,%0 ;" + "andl %1,%0" + : "=a" (ret) + : "g" (~(UM_KERN_PAGE_SIZE - 1))); + + return (void *)ret; +} #endif diff --git a/arch/x86/um/shared/sysdep/stub_64.h b/arch/x86/um/shared/sysdep/stub_64.h index 6e2626b77a2e..e9c4b2b38803 100644 --- a/arch/x86/um/shared/sysdep/stub_64.h +++ b/arch/x86/um/shared/sysdep/stub_64.h @@ -108,4 +108,16 @@ static inline void remap_stack_and_trap(void) __syscall_clobber, "r10", "r8", "r9"); } +static __always_inline void *get_stub_page(void) +{ + unsigned long ret; + + asm volatile ( + "movq %%rsp,%0 ;" + "andq %1,%0" + : "=a" (ret) + : "g" (~(UM_KERN_PAGE_SIZE - 1))); + + return (void *)ret; +} #endif diff --git a/arch/x86/um/stub_segv.c b/arch/x86/um/stub_segv.c index 21836eaf1725..f7eefba034f9 100644 --- a/arch/x86/um/stub_segv.c +++ b/arch/x86/um/stub_segv.c @@ -11,9 +11,8 @@ void __attribute__ ((__section__ (".__syscall_stub"))) stub_segv_handler(int sig, siginfo_t *info, void *p) { - int stack; + struct faultinfo *f = get_stub_page(); ucontext_t *uc = p; - struct faultinfo *f = (void *)(((unsigned long)&stack) & ~(UM_KERN_PAGE_SIZE - 1)); GET_FAULTINFO_FROM_MC(*f, &uc->uc_mcontext); trap_myself(); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 7d0c3dbb2898..d5c9d886cd9f 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -381,6 +381,7 @@ static int hostfs_fsync(struct file *file, loff_t start, loff_t end, static const struct file_operations hostfs_file_fops = { .llseek = generic_file_llseek, .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, diff --git a/include/uapi/linux/virtio_pcidev.h b/include/uapi/linux/virtio_pcidev.h index 89daa88bcfef..668b07ce515b 100644 --- a/include/uapi/linux/virtio_pcidev.h +++ b/include/uapi/linux/virtio_pcidev.h @@ -9,13 +9,14 @@ /** * enum virtio_pcidev_ops - virtual PCI device operations + * @VIRTIO_PCIDEV_OP_RESERVED: reserved to catch errors * @VIRTIO_PCIDEV_OP_CFG_READ: read config space, size is 1, 2, 4 or 8; * the @data field should be filled in by the device (in little endian). * @VIRTIO_PCIDEV_OP_CFG_WRITE: write config space, size is 1, 2, 4 or 8; * the @data field contains the data to write (in little endian). - * @VIRTIO_PCIDEV_OP_BAR_READ: read BAR mem/pio, size can be variable; + * @VIRTIO_PCIDEV_OP_MMIO_READ: read BAR mem/pio, size can be variable; * the @data field should be filled in by the device (in little endian). - * @VIRTIO_PCIDEV_OP_BAR_WRITE: write BAR mem/pio, size can be variable; + * @VIRTIO_PCIDEV_OP_MMIO_WRITE: write BAR mem/pio, size can be variable; * the @data field contains the data to write (in little endian). * @VIRTIO_PCIDEV_OP_MMIO_MEMSET: memset MMIO, size is variable but * the @data field only has one byte (unlike @VIRTIO_PCIDEV_OP_MMIO_WRITE) diff --git a/lib/logic_iomem.c b/lib/logic_iomem.c index b76b92dd0f1f..9bdfde0c0f86 100644 --- a/lib/logic_iomem.c +++ b/lib/logic_iomem.c @@ -6,6 +6,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/logic_iomem.h> +#include <asm/io.h> struct logic_iomem_region { const struct resource *res; @@ -78,7 +79,7 @@ static void __iomem *real_ioremap(phys_addr_t offset, size_t size) static void real_iounmap(void __iomem *addr) { WARN(1, "invalid iounmap for addr 0x%llx\n", - (unsigned long long)addr); + (unsigned long long __force)addr); } #endif /* CONFIG_LOGIC_IOMEM_FALLBACK */ @@ -172,14 +173,15 @@ EXPORT_SYMBOL(iounmap); static u##sz real_raw_read ## op(const volatile void __iomem *addr) \ { \ WARN(1, "Invalid read" #op " at address %llx\n", \ - (unsigned long long)addr); \ + (unsigned long long __force)addr); \ return (u ## sz)~0ULL; \ } \ \ -void real_raw_write ## op(u ## sz val, volatile void __iomem *addr) \ +static void real_raw_write ## op(u ## sz val, \ + volatile void __iomem *addr) \ { \ WARN(1, "Invalid writeq" #op " of 0x%llx at address %llx\n", \ - (unsigned long long)val, (unsigned long long)addr); \ + (unsigned long long)val, (unsigned long long __force)addr);\ } \ MAKE_FALLBACK(b, 8); @@ -192,14 +194,14 @@ MAKE_FALLBACK(q, 64); static void real_memset_io(volatile void __iomem *addr, int value, size_t size) { WARN(1, "Invalid memset_io at address 0x%llx\n", - (unsigned long long)addr); + (unsigned long long __force)addr); } static void real_memcpy_fromio(void *buffer, const volatile void __iomem *addr, size_t size) { WARN(1, "Invalid memcpy_fromio at address 0x%llx\n", - (unsigned long long)addr); + (unsigned long long __force)addr); memset(buffer, 0xff, size); } @@ -208,7 +210,7 @@ static void real_memcpy_toio(volatile void __iomem *addr, const void *buffer, size_t size) { WARN(1, "Invalid memcpy_toio at address 0x%llx\n", - (unsigned long long)addr); + (unsigned long long __force)addr); } #endif /* CONFIG_LOGIC_IOMEM_FALLBACK */ |