Commit d6c338a7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml

Pull UML updates from Richard Weinberger:

 - Support for VMAP_STACK

 - Support for splice_write in hostfs

 - Fixes for virt-pci

 - Fixes for virtio_uml

 - Various fixes

* tag 'for-linus-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml:
  um: fix stub location calculation
  um: virt-pci: fix uapi documentation
  um: enable VMAP_STACK
  um: virt-pci: don't do DMA from stack
  hostfs: support splice_write
  um: virtio_uml: fix memory leak on init failures
  um: virtio_uml: include linux/virtio-uml.h
  lib/logic_iomem: fix sparse warnings
  um: make PCI emulation driver init/exit static
parents 35776f10 adf9ae0d
...@@ -24,6 +24,7 @@ config UML ...@@ -24,6 +24,7 @@ config UML
select SET_FS select SET_FS
select TRACE_IRQFLAGS_SUPPORT select TRACE_IRQFLAGS_SUPPORT
select TTY # Needed for line.c select TTY # Needed for line.c
select HAVE_ARCH_VMAP_STACK
config MMU config MMU
bool bool
......
...@@ -56,6 +56,13 @@ static unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)]; ...@@ -56,6 +56,13 @@ static unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)];
#define UM_VIRT_PCI_MAXDELAY 40000 #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, static int um_pci_send_cmd(struct um_pci_device *dev,
struct virtio_pcidev_msg *cmd, struct virtio_pcidev_msg *cmd,
unsigned int cmd_size, unsigned int cmd_size,
...@@ -68,11 +75,12 @@ static int um_pci_send_cmd(struct um_pci_device *dev, ...@@ -68,11 +75,12 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
[1] = extra ? &extra_sg : &in_sg, [1] = extra ? &extra_sg : &in_sg,
[2] = extra ? &in_sg : NULL, [2] = extra ? &in_sg : NULL,
}; };
struct um_pci_message_buffer *buf;
int delay_count = 0; int delay_count = 0;
int ret, len; int ret, len;
bool posted; bool posted;
if (WARN_ON(cmd_size < sizeof(*cmd))) if (WARN_ON(cmd_size < sizeof(*cmd) || cmd_size > sizeof(*buf)))
return -EINVAL; return -EINVAL;
switch (cmd->op) { switch (cmd->op) {
...@@ -88,6 +96,9 @@ static int um_pci_send_cmd(struct um_pci_device *dev, ...@@ -88,6 +96,9 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
break; break;
} }
buf = get_cpu_var(um_pci_msg_bufs);
memcpy(buf, cmd, cmd_size);
if (posted) { if (posted) {
u8 *ncmd = kmalloc(cmd_size + extra_size, GFP_ATOMIC); u8 *ncmd = kmalloc(cmd_size + extra_size, GFP_ATOMIC);
...@@ -102,7 +113,10 @@ static int um_pci_send_cmd(struct um_pci_device *dev, ...@@ -102,7 +113,10 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
} else { } else {
/* try without allocating memory */ /* try without allocating memory */
posted = false; posted = false;
cmd = (void *)buf;
} }
} else {
cmd = (void *)buf;
} }
sg_init_one(&out_sg, cmd, cmd_size); sg_init_one(&out_sg, cmd, cmd_size);
...@@ -118,11 +132,12 @@ static int um_pci_send_cmd(struct um_pci_device *dev, ...@@ -118,11 +132,12 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
posted ? cmd : HANDLE_NO_FREE(cmd), posted ? cmd : HANDLE_NO_FREE(cmd),
GFP_ATOMIC); GFP_ATOMIC);
if (ret) if (ret)
return ret; goto out;
if (posted) { if (posted) {
virtqueue_kick(dev->cmd_vq); virtqueue_kick(dev->cmd_vq);
return 0; ret = 0;
goto out;
} }
/* kick and poll for getting a response on the queue */ /* 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, ...@@ -148,6 +163,8 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
} }
clear_bit(UM_PCI_STAT_WAITING, &dev->status); clear_bit(UM_PCI_STAT_WAITING, &dev->status);
out:
put_cpu_var(um_pci_msg_bufs);
return ret; return ret;
} }
...@@ -161,12 +178,17 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset, ...@@ -161,12 +178,17 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
.size = size, .size = size,
.addr = offset, .addr = offset,
}; };
/* maximum size - we may only use parts of it */ /* buf->data is maximum size - we may only use parts of it */
u8 data[8]; struct um_pci_message_buffer *buf;
u8 *data;
unsigned long ret = ~0ULL;
if (!dev) if (!dev)
return ~0ULL; return ~0ULL;
buf = get_cpu_var(um_pci_msg_bufs);
data = buf->data;
memset(data, 0xff, sizeof(data)); memset(data, 0xff, sizeof(data));
switch (size) { switch (size) {
...@@ -179,27 +201,34 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset, ...@@ -179,27 +201,34 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
break; break;
default: default:
WARN(1, "invalid config space read size %d\n", size); 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, if (um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, data, 8))
data, sizeof(data))) goto out;
return ~0ULL;
switch (size) { switch (size) {
case 1: case 1:
return data[0]; ret = data[0];
break;
case 2: case 2:
return le16_to_cpup((void *)data); ret = le16_to_cpup((void *)data);
break;
case 4: case 4:
return le32_to_cpup((void *)data); ret = le32_to_cpup((void *)data);
break;
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
case 8: case 8:
return le64_to_cpup((void *)data); ret = le64_to_cpup((void *)data);
break;
#endif #endif
default: 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, 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, ...@@ -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, static unsigned long um_pci_bar_read(void *priv, unsigned int offset,
int size) int size)
{ {
/* maximum size - we may only use parts of it */ /* buf->data is maximum size - we may only use parts of it */
u8 data[8]; 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) { switch (size) {
case 1: case 1:
...@@ -285,25 +319,33 @@ static unsigned long um_pci_bar_read(void *priv, unsigned int offset, ...@@ -285,25 +319,33 @@ static unsigned long um_pci_bar_read(void *priv, unsigned int offset,
break; break;
default: default:
WARN(1, "invalid config space read size %d\n", size); WARN(1, "invalid config space read size %d\n", size);
return ~0ULL; goto out;
} }
um_pci_bar_copy_from(priv, data, offset, size); um_pci_bar_copy_from(priv, data, offset, size);
switch (size) { switch (size) {
case 1: case 1:
return data[0]; ret = data[0];
break;
case 2: case 2:
return le16_to_cpup((void *)data); ret = le16_to_cpup((void *)data);
break;
case 4: case 4:
return le32_to_cpup((void *)data); ret = le32_to_cpup((void *)data);
break;
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
case 8: case 8:
return le64_to_cpup((void *)data); ret = le64_to_cpup((void *)data);
break;
#endif #endif
default: 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, 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) ...@@ -810,7 +852,7 @@ void *pci_root_bus_fwnode(struct pci_bus *bus)
return um_pci_fwnode; return um_pci_fwnode;
} }
int um_pci_init(void) static int um_pci_init(void)
{ {
int err, i; int err, i;
...@@ -823,10 +865,16 @@ int um_pci_init(void) ...@@ -823,10 +865,16 @@ int um_pci_init(void)
"No virtio device ID configured for PCI - no PCI support\n")) "No virtio device ID configured for PCI - no PCI support\n"))
return 0; return 0;
bridge = pci_alloc_host_bridge(0); um_pci_msg_bufs = alloc_percpu(struct um_pci_message_buffer);
if (!bridge) if (!um_pci_msg_bufs)
return -ENOMEM; return -ENOMEM;
bridge = pci_alloc_host_bridge(0);
if (!bridge) {
err = -ENOMEM;
goto free;
}
um_pci_fwnode = irq_domain_alloc_named_fwnode("um-pci"); um_pci_fwnode = irq_domain_alloc_named_fwnode("um-pci");
if (!um_pci_fwnode) { if (!um_pci_fwnode) {
err = -ENOMEM; err = -ENOMEM;
...@@ -878,18 +926,22 @@ int um_pci_init(void) ...@@ -878,18 +926,22 @@ int um_pci_init(void)
irq_domain_remove(um_pci_inner_domain); irq_domain_remove(um_pci_inner_domain);
if (um_pci_fwnode) if (um_pci_fwnode)
irq_domain_free_fwnode(um_pci_fwnode); irq_domain_free_fwnode(um_pci_fwnode);
pci_free_resource_list(&bridge->windows); if (bridge) {
pci_free_host_bridge(bridge); pci_free_resource_list(&bridge->windows);
pci_free_host_bridge(bridge);
}
free_percpu(um_pci_msg_bufs);
return err; return err;
} }
module_init(um_pci_init); module_init(um_pci_init);
void um_pci_exit(void) static void um_pci_exit(void)
{ {
unregister_virtio_driver(&um_pci_virtio_driver); unregister_virtio_driver(&um_pci_virtio_driver);
irq_domain_remove(um_pci_msi_domain); irq_domain_remove(um_pci_msi_domain);
irq_domain_remove(um_pci_inner_domain); irq_domain_remove(um_pci_inner_domain);
pci_free_resource_list(&bridge->windows); pci_free_resource_list(&bridge->windows);
pci_free_host_bridge(bridge); pci_free_host_bridge(bridge);
free_percpu(um_pci_msg_bufs);
} }
module_exit(um_pci_exit); module_exit(um_pci_exit);
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/virtio_config.h> #include <linux/virtio_config.h>
#include <linux/virtio_ring.h> #include <linux/virtio_ring.h>
#include <linux/time-internal.h> #include <linux/time-internal.h>
#include <linux/virtio-uml.h>
#include <shared/as-layout.h> #include <shared/as-layout.h>
#include <irq_kern.h> #include <irq_kern.h>
#include <init.h> #include <init.h>
...@@ -1139,7 +1140,7 @@ static int virtio_uml_probe(struct platform_device *pdev) ...@@ -1139,7 +1140,7 @@ static int virtio_uml_probe(struct platform_device *pdev)
rc = os_connect_socket(pdata->socket_path); rc = os_connect_socket(pdata->socket_path);
} while (rc == -EINTR); } while (rc == -EINTR);
if (rc < 0) if (rc < 0)
return rc; goto error_free;
vu_dev->sock = rc; vu_dev->sock = rc;
spin_lock_init(&vu_dev->sock_lock); spin_lock_init(&vu_dev->sock_lock);
...@@ -1160,6 +1161,8 @@ static int virtio_uml_probe(struct platform_device *pdev) ...@@ -1160,6 +1161,8 @@ static int virtio_uml_probe(struct platform_device *pdev)
error_init: error_init:
os_close_file(vu_dev->sock); os_close_file(vu_dev->sock);
error_free:
kfree(vu_dev);
return rc; return rc;
} }
......
...@@ -24,8 +24,7 @@ ...@@ -24,8 +24,7 @@
void __attribute__ ((__section__ (".__syscall_stub"))) void __attribute__ ((__section__ (".__syscall_stub")))
stub_clone_handler(void) stub_clone_handler(void)
{ {
int stack; struct stub_data *data = get_stub_page();
struct stub_data *data = (void *) ((unsigned long)&stack & ~(UM_KERN_PAGE_SIZE - 1));
long err; long err;
err = stub_syscall2(__NR_clone, CLONE_PARENT | CLONE_FILES | SIGCHLD, err = stub_syscall2(__NR_clone, CLONE_PARENT | CLONE_FILES | SIGCHLD,
......
...@@ -101,4 +101,16 @@ static inline void remap_stack_and_trap(void) ...@@ -101,4 +101,16 @@ static inline void remap_stack_and_trap(void)
"memory"); "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 #endif
...@@ -108,4 +108,16 @@ static inline void remap_stack_and_trap(void) ...@@ -108,4 +108,16 @@ static inline void remap_stack_and_trap(void)
__syscall_clobber, "r10", "r8", "r9"); __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 #endif
...@@ -11,9 +11,8 @@ ...@@ -11,9 +11,8 @@
void __attribute__ ((__section__ (".__syscall_stub"))) void __attribute__ ((__section__ (".__syscall_stub")))
stub_segv_handler(int sig, siginfo_t *info, void *p) stub_segv_handler(int sig, siginfo_t *info, void *p)
{ {
int stack; struct faultinfo *f = get_stub_page();
ucontext_t *uc = p; ucontext_t *uc = p;
struct faultinfo *f = (void *)(((unsigned long)&stack) & ~(UM_KERN_PAGE_SIZE - 1));
GET_FAULTINFO_FROM_MC(*f, &uc->uc_mcontext); GET_FAULTINFO_FROM_MC(*f, &uc->uc_mcontext);
trap_myself(); trap_myself();
......
...@@ -381,6 +381,7 @@ static int hostfs_fsync(struct file *file, loff_t start, loff_t end, ...@@ -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 = { static const struct file_operations hostfs_file_fops = {
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
.splice_read = generic_file_splice_read, .splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
.read_iter = generic_file_read_iter, .read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter, .write_iter = generic_file_write_iter,
.mmap = generic_file_mmap, .mmap = generic_file_mmap,
......
...@@ -9,13 +9,14 @@ ...@@ -9,13 +9,14 @@
/** /**
* enum virtio_pcidev_ops - virtual PCI device operations * 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; * @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). * 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; * @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). * 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). * 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). * the @data field contains the data to write (in little endian).
* @VIRTIO_PCIDEV_OP_MMIO_MEMSET: memset MMIO, size is variable but * @VIRTIO_PCIDEV_OP_MMIO_MEMSET: memset MMIO, size is variable but
* the @data field only has one byte (unlike @VIRTIO_PCIDEV_OP_MMIO_WRITE) * the @data field only has one byte (unlike @VIRTIO_PCIDEV_OP_MMIO_WRITE)
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/logic_iomem.h> #include <linux/logic_iomem.h>
#include <asm/io.h>
struct logic_iomem_region { struct logic_iomem_region {
const struct resource *res; const struct resource *res;
...@@ -78,7 +79,7 @@ static void __iomem *real_ioremap(phys_addr_t offset, size_t size) ...@@ -78,7 +79,7 @@ static void __iomem *real_ioremap(phys_addr_t offset, size_t size)
static void real_iounmap(void __iomem *addr) static void real_iounmap(void __iomem *addr)
{ {
WARN(1, "invalid iounmap for addr 0x%llx\n", WARN(1, "invalid iounmap for addr 0x%llx\n",
(unsigned long long)addr); (unsigned long long __force)addr);
} }
#endif /* CONFIG_LOGIC_IOMEM_FALLBACK */ #endif /* CONFIG_LOGIC_IOMEM_FALLBACK */
...@@ -172,14 +173,15 @@ EXPORT_SYMBOL(iounmap); ...@@ -172,14 +173,15 @@ EXPORT_SYMBOL(iounmap);
static u##sz real_raw_read ## op(const volatile void __iomem *addr) \ static u##sz real_raw_read ## op(const volatile void __iomem *addr) \
{ \ { \
WARN(1, "Invalid read" #op " at address %llx\n", \ WARN(1, "Invalid read" #op " at address %llx\n", \
(unsigned long long)addr); \ (unsigned long long __force)addr); \
return (u ## sz)~0ULL; \ 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", \ 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); MAKE_FALLBACK(b, 8);
...@@ -192,14 +194,14 @@ MAKE_FALLBACK(q, 64); ...@@ -192,14 +194,14 @@ MAKE_FALLBACK(q, 64);
static void real_memset_io(volatile void __iomem *addr, int value, size_t size) static void real_memset_io(volatile void __iomem *addr, int value, size_t size)
{ {
WARN(1, "Invalid memset_io at address 0x%llx\n", 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, static void real_memcpy_fromio(void *buffer, const volatile void __iomem *addr,
size_t size) size_t size)
{ {
WARN(1, "Invalid memcpy_fromio at address 0x%llx\n", WARN(1, "Invalid memcpy_fromio at address 0x%llx\n",
(unsigned long long)addr); (unsigned long long __force)addr);
memset(buffer, 0xff, size); memset(buffer, 0xff, size);
} }
...@@ -208,7 +210,7 @@ static void real_memcpy_toio(volatile void __iomem *addr, const void *buffer, ...@@ -208,7 +210,7 @@ static void real_memcpy_toio(volatile void __iomem *addr, const void *buffer,
size_t size) size_t size)
{ {
WARN(1, "Invalid memcpy_toio at address 0x%llx\n", WARN(1, "Invalid memcpy_toio at address 0x%llx\n",
(unsigned long long)addr); (unsigned long long __force)addr);
} }
#endif /* CONFIG_LOGIC_IOMEM_FALLBACK */ #endif /* CONFIG_LOGIC_IOMEM_FALLBACK */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment