forked from mirrors/linux
Updates for UML for this cycle, notably:
- proper nofault accesses and read-only rodata - hostfs fix for host inode number reuse - fixes for host errno handling - various cleanups/small fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmfs6R0ACgkQ10qiO8sP aAC6VxAAoIqPoyeOqH5DRnHQZQuZOE2lelhKCKckH3xg8peXqHdBl5Mx3kE2j851 S+ufysSJkbZIYfPw/B59ZzpQg7d1JmrCoDCvleH/LdWhi6sosqe9F7Zwxk2p9STA swCifnACaIZc0iir70MrCAd/1/bBZq7PG8BWO1XFDrqNBLBGQxtSiMCmzBWsKjuj 33uocQMi5PvVikBRxfz2uo4PJuJumhGs3sWAoFlA61ogHP4JrwKjW/HvuHJGaHKV YgaObr/JPhDkbGn7bXdQpLT+Qz7FwBeZFt9AUHOk+IibcwQY126ArXD11zzAAPkT 3Q9H8eNV+MpieGtpA2+3Gwe//QsNjEOj3ACfV+S7veQ0Vxk+Bd/wMSDBKLF+z71g qpFqFeO0wS/XmwFI6RVN+GW6rZZ6mR3c7r/5mtAOa5+iJTnqDadyE/4oouQht2of IrS4LugnTB0KCgRZZDmtTOFT8lGOjey3e+AO42Qi+Z64oolI6zKUakTdBWvywmk4 V9w9OUmEZAy64a0luvavYfxx+6WoTHURyQ/L99Ysk6ns9BrUk7U+hpfsLZBWZiyT 3jfOlRGgt4N7iHaVqQwB6l6/Q/FtrdVK7SrTtsGzURhuCSy3SP0HZxWh9qhaL/2j Af8Qz5OAOEBYmTaN9lLYsHXp02NyM+4hlsR1DvEcNGuWQsyepIw= =MSjq -----END PGP SIGNATURE----- Merge tag 'uml-for-linux-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/uml/linux Pull UML updates from Johannes Berg: - proper nofault accesses and read-only rodata - hostfs fix for host inode number reuse - fixes for host errno handling - various cleanups/small fixes * tag 'uml-for-linux-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/uml/linux: um: Rewrite the sigio workaround based on epoll and tgkill um: Prohibit the VM_CLONE flag in run_helper_thread() um: Switch to the pthread-based helper in sigio workaround um: ubd: Switch to the pthread-based helper um: Add pthread-based helper support um: x86: clean up elf specific definitions um: Store full CSGSFS and SS register from mcontext um: virt-pci: Refactor virtio_pcidev into its own module um: work around sched_yield not yielding in time-travel mode um/locking: Remove semicolon from "lock" prefix um: Update min_low_pfn to match changes in uml_reserved um: use str_yes_no() to remove hardcoded "yes" and "no" um: hostfs: avoid issues on inode number reuse by host um: Allocate vdso page pointer statically um: remove copy_from_kernel_nofault_allowed um: mark rodata read-only and implement _nofault accesses um: Pass the correct Rust target and options with gcc
This commit is contained in:
commit
8a6b94032e
44 changed files with 1146 additions and 1136 deletions
|
|
@ -12,6 +12,7 @@ config UML
|
||||||
select ARCH_HAS_KCOV
|
select ARCH_HAS_KCOV
|
||||||
select ARCH_HAS_STRNCPY_FROM_USER
|
select ARCH_HAS_STRNCPY_FROM_USER
|
||||||
select ARCH_HAS_STRNLEN_USER
|
select ARCH_HAS_STRNLEN_USER
|
||||||
|
select ARCH_HAS_STRICT_KERNEL_RWX
|
||||||
select HAVE_ARCH_AUDITSYSCALL
|
select HAVE_ARCH_AUDITSYSCALL
|
||||||
select HAVE_ARCH_KASAN if X86_64
|
select HAVE_ARCH_KASAN if X86_64
|
||||||
select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN
|
select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN
|
||||||
|
|
|
||||||
|
|
@ -345,16 +345,20 @@ config UML_RTC
|
||||||
by providing a fake RTC clock that causes a wakeup at the right
|
by providing a fake RTC clock that causes a wakeup at the right
|
||||||
time.
|
time.
|
||||||
|
|
||||||
config UML_PCI_OVER_VIRTIO
|
config UML_PCI
|
||||||
bool "Enable PCI over VIRTIO device simulation"
|
bool
|
||||||
# in theory, just VIRTIO is enough, but that causes recursion
|
|
||||||
depends on VIRTIO_UML
|
|
||||||
select FORCE_PCI
|
select FORCE_PCI
|
||||||
select UML_IOMEM_EMULATION
|
select UML_IOMEM_EMULATION
|
||||||
select UML_DMA_EMULATION
|
select UML_DMA_EMULATION
|
||||||
select PCI_MSI
|
select PCI_MSI
|
||||||
select PCI_LOCKLESS_CONFIG
|
select PCI_LOCKLESS_CONFIG
|
||||||
|
|
||||||
|
config UML_PCI_OVER_VIRTIO
|
||||||
|
bool "Enable PCI over VIRTIO device simulation"
|
||||||
|
# in theory, just VIRTIO is enough, but that causes recursion
|
||||||
|
depends on VIRTIO_UML
|
||||||
|
select UML_PCI
|
||||||
|
|
||||||
config UML_PCI_OVER_VIRTIO_DEVICE_ID
|
config UML_PCI_OVER_VIRTIO_DEVICE_ID
|
||||||
int "set the virtio device ID for PCI emulation"
|
int "set the virtio device ID for PCI emulation"
|
||||||
default -1
|
default -1
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,8 @@ obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o
|
||||||
obj-$(CONFIG_UML_RANDOM) += random.o
|
obj-$(CONFIG_UML_RANDOM) += random.o
|
||||||
obj-$(CONFIG_VIRTIO_UML) += virtio_uml.o
|
obj-$(CONFIG_VIRTIO_UML) += virtio_uml.o
|
||||||
obj-$(CONFIG_UML_RTC) += rtc.o
|
obj-$(CONFIG_UML_RTC) += rtc.o
|
||||||
obj-$(CONFIG_UML_PCI_OVER_VIRTIO) += virt-pci.o
|
obj-$(CONFIG_UML_PCI) += virt-pci.o
|
||||||
|
obj-$(CONFIG_UML_PCI_OVER_VIRTIO) += virtio_pcidev.o
|
||||||
|
|
||||||
# pcap_user.o must be added explicitly.
|
# pcap_user.o must be added explicitly.
|
||||||
USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o vde_user.o vector_user.o
|
USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o vde_user.o vector_user.o
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ static int __init rng_init (void)
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto err_out_cleanup_hw;
|
goto err_out_cleanup_hw;
|
||||||
|
|
||||||
sigio_broken(random_fd);
|
sigio_broken();
|
||||||
hwrng.name = RNG_MODULE_NAME;
|
hwrng.name = RNG_MODULE_NAME;
|
||||||
hwrng.read = rng_dev_read;
|
hwrng.read = rng_dev_read;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ int uml_rtc_start(bool timetravel)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* apparently timerfd won't send SIGIO, use workaround */
|
/* apparently timerfd won't send SIGIO, use workaround */
|
||||||
sigio_broken(uml_rtc_irq_fds[0]);
|
sigio_broken();
|
||||||
err = add_sigio_fd(uml_rtc_irq_fds[0]);
|
err = add_sigio_fd(uml_rtc_irq_fds[0]);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
close(uml_rtc_irq_fds[0]);
|
close(uml_rtc_irq_fds[0]);
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,10 @@
|
||||||
#ifndef __UM_UBD_USER_H
|
#ifndef __UM_UBD_USER_H
|
||||||
#define __UM_UBD_USER_H
|
#define __UM_UBD_USER_H
|
||||||
|
|
||||||
extern int start_io_thread(unsigned long sp, int *fds_out);
|
#include <os.h>
|
||||||
extern int io_thread(void *arg);
|
|
||||||
|
int start_io_thread(struct os_helper_thread **td_out, int *fd_out);
|
||||||
|
void *io_thread(void *arg);
|
||||||
extern int kernel_fd;
|
extern int kernel_fd;
|
||||||
|
|
||||||
extern int ubd_read_poll(int timeout);
|
extern int ubd_read_poll(int timeout);
|
||||||
|
|
|
||||||
|
|
@ -474,12 +474,12 @@ static irqreturn_t ubd_intr(int irq, void *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only changed by ubd_init, which is an initcall. */
|
/* Only changed by ubd_init, which is an initcall. */
|
||||||
static int io_pid = -1;
|
static struct os_helper_thread *io_td;
|
||||||
|
|
||||||
static void kill_io_thread(void)
|
static void kill_io_thread(void)
|
||||||
{
|
{
|
||||||
if(io_pid != -1)
|
if (io_td)
|
||||||
os_kill_process(io_pid, 1);
|
os_kill_helper_thread(io_td);
|
||||||
}
|
}
|
||||||
|
|
||||||
__uml_exitcall(kill_io_thread);
|
__uml_exitcall(kill_io_thread);
|
||||||
|
|
@ -1104,8 +1104,8 @@ static int __init ubd_init(void)
|
||||||
|
|
||||||
late_initcall(ubd_init);
|
late_initcall(ubd_init);
|
||||||
|
|
||||||
static int __init ubd_driver_init(void){
|
static int __init ubd_driver_init(void)
|
||||||
unsigned long stack;
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
|
/* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
|
||||||
|
|
@ -1114,13 +1114,11 @@ static int __init ubd_driver_init(void){
|
||||||
/* Letting ubd=sync be like using ubd#s= instead of ubd#= is
|
/* Letting ubd=sync be like using ubd#s= instead of ubd#= is
|
||||||
* enough. So use anyway the io thread. */
|
* enough. So use anyway the io thread. */
|
||||||
}
|
}
|
||||||
stack = alloc_stack(0, 0);
|
err = start_io_thread(&io_td, &thread_fd);
|
||||||
io_pid = start_io_thread(stack + PAGE_SIZE, &thread_fd);
|
if (err < 0) {
|
||||||
if(io_pid < 0){
|
|
||||||
printk(KERN_ERR
|
printk(KERN_ERR
|
||||||
"ubd : Failed to start I/O thread (errno = %d) - "
|
"ubd : Failed to start I/O thread (errno = %d) - "
|
||||||
"falling back to synchronous I/O\n", -io_pid);
|
"falling back to synchronous I/O\n", -err);
|
||||||
io_pid = -1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
|
err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
|
||||||
|
|
@ -1496,12 +1494,11 @@ int kernel_fd = -1;
|
||||||
/* Only changed by the io thread. XXX: currently unused. */
|
/* Only changed by the io thread. XXX: currently unused. */
|
||||||
static int io_count;
|
static int io_count;
|
||||||
|
|
||||||
int io_thread(void *arg)
|
void *io_thread(void *arg)
|
||||||
{
|
{
|
||||||
int n, count, written, res;
|
int n, count, written, res;
|
||||||
|
|
||||||
os_set_pdeathsig();
|
os_fix_helper_thread_signals();
|
||||||
os_fix_helper_signals();
|
|
||||||
|
|
||||||
while(1){
|
while(1){
|
||||||
n = bulk_req_safe_read(
|
n = bulk_req_safe_read(
|
||||||
|
|
@ -1543,5 +1540,5 @@ int io_thread(void *arg)
|
||||||
} while (written < n);
|
} while (written < n);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@
|
||||||
|
|
||||||
static struct pollfd kernel_pollfd;
|
static struct pollfd kernel_pollfd;
|
||||||
|
|
||||||
int start_io_thread(unsigned long sp, int *fd_out)
|
int start_io_thread(struct os_helper_thread **td_out, int *fd_out)
|
||||||
{
|
{
|
||||||
int pid, fds[2], err;
|
int fds[2], err;
|
||||||
|
|
||||||
err = os_pipe(fds, 1, 1);
|
err = os_pipe(fds, 1, 1);
|
||||||
if(err < 0){
|
if(err < 0){
|
||||||
|
|
@ -47,14 +47,14 @@ int start_io_thread(unsigned long sp, int *fd_out)
|
||||||
goto out_close;
|
goto out_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid = clone(io_thread, (void *) sp, CLONE_FILES | CLONE_VM, NULL);
|
err = os_run_helper_thread(td_out, io_thread, NULL);
|
||||||
if(pid < 0){
|
if (err < 0) {
|
||||||
err = -errno;
|
printk("%s - failed to run helper thread, err = %d\n",
|
||||||
printk("start_io_thread - clone failed : errno = %d\n", errno);
|
__func__, -err);
|
||||||
goto out_close;
|
goto out_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
return(pid);
|
return 0;
|
||||||
|
|
||||||
out_close:
|
out_close:
|
||||||
os_close_file(fds[0]);
|
os_close_file(fds[0]);
|
||||||
|
|
|
||||||
|
|
@ -5,52 +5,19 @@
|
||||||
*/
|
*/
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/virtio.h>
|
|
||||||
#include <linux/virtio_config.h>
|
|
||||||
#include <linux/logic_iomem.h>
|
#include <linux/logic_iomem.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/virtio_pcidev.h>
|
|
||||||
#include <linux/virtio-uml.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/msi.h>
|
#include <linux/msi.h>
|
||||||
#include <linux/unaligned.h>
|
#include <linux/unaligned.h>
|
||||||
#include <irq_kern.h>
|
#include <irq_kern.h>
|
||||||
|
|
||||||
|
#include "virt-pci.h"
|
||||||
|
|
||||||
#define MAX_DEVICES 8
|
#define MAX_DEVICES 8
|
||||||
#define MAX_MSI_VECTORS 32
|
#define MAX_MSI_VECTORS 32
|
||||||
#define CFG_SPACE_SIZE 4096
|
#define CFG_SPACE_SIZE 4096
|
||||||
|
|
||||||
/* for MSI-X we have a 32-bit payload */
|
|
||||||
#define MAX_IRQ_MSG_SIZE (sizeof(struct virtio_pcidev_msg) + sizeof(u32))
|
|
||||||
#define NUM_IRQ_MSGS 10
|
|
||||||
|
|
||||||
struct um_pci_message_buffer {
|
|
||||||
struct virtio_pcidev_msg hdr;
|
|
||||||
u8 data[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct um_pci_device {
|
|
||||||
struct virtio_device *vdev;
|
|
||||||
|
|
||||||
/* for now just standard BARs */
|
|
||||||
u8 resptr[PCI_STD_NUM_BARS];
|
|
||||||
|
|
||||||
struct virtqueue *cmd_vq, *irq_vq;
|
|
||||||
|
|
||||||
#define UM_PCI_WRITE_BUFS 20
|
|
||||||
struct um_pci_message_buffer bufs[UM_PCI_WRITE_BUFS + 1];
|
|
||||||
void *extra_ptrs[UM_PCI_WRITE_BUFS + 1];
|
|
||||||
DECLARE_BITMAP(used_bufs, UM_PCI_WRITE_BUFS);
|
|
||||||
|
|
||||||
#define UM_PCI_STAT_WAITING 0
|
|
||||||
unsigned long status;
|
|
||||||
|
|
||||||
int irq;
|
|
||||||
|
|
||||||
bool platform;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct um_pci_device_reg {
|
struct um_pci_device_reg {
|
||||||
struct um_pci_device *dev;
|
struct um_pci_device *dev;
|
||||||
void __iomem *iomem;
|
void __iomem *iomem;
|
||||||
|
|
@ -65,179 +32,15 @@ static struct irq_domain *um_pci_inner_domain;
|
||||||
static struct irq_domain *um_pci_msi_domain;
|
static struct irq_domain *um_pci_msi_domain;
|
||||||
static unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)];
|
static unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)];
|
||||||
|
|
||||||
static unsigned int um_pci_max_delay_us = 40000;
|
|
||||||
module_param_named(max_delay_us, um_pci_max_delay_us, uint, 0644);
|
|
||||||
|
|
||||||
static int um_pci_get_buf(struct um_pci_device *dev, bool *posted)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < UM_PCI_WRITE_BUFS; i++) {
|
|
||||||
if (!test_and_set_bit(i, dev->used_bufs))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
*posted = false;
|
|
||||||
return UM_PCI_WRITE_BUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void um_pci_free_buf(struct um_pci_device *dev, void *buf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (buf == &dev->bufs[UM_PCI_WRITE_BUFS]) {
|
|
||||||
kfree(dev->extra_ptrs[UM_PCI_WRITE_BUFS]);
|
|
||||||
dev->extra_ptrs[UM_PCI_WRITE_BUFS] = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < UM_PCI_WRITE_BUFS; i++) {
|
|
||||||
if (buf == &dev->bufs[i]) {
|
|
||||||
kfree(dev->extra_ptrs[i]);
|
|
||||||
dev->extra_ptrs[i] = NULL;
|
|
||||||
WARN_ON(!test_and_clear_bit(i, dev->used_bufs));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ON(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int um_pci_send_cmd(struct um_pci_device *dev,
|
|
||||||
struct virtio_pcidev_msg *cmd,
|
|
||||||
unsigned int cmd_size,
|
|
||||||
const void *extra, unsigned int extra_size,
|
|
||||||
void *out, unsigned int out_size)
|
|
||||||
{
|
|
||||||
struct scatterlist out_sg, extra_sg, in_sg;
|
|
||||||
struct scatterlist *sgs_list[] = {
|
|
||||||
[0] = &out_sg,
|
|
||||||
[1] = extra ? &extra_sg : &in_sg,
|
|
||||||
[2] = extra ? &in_sg : NULL,
|
|
||||||
};
|
|
||||||
struct um_pci_message_buffer *buf;
|
|
||||||
int delay_count = 0;
|
|
||||||
bool bounce_out;
|
|
||||||
int ret, len;
|
|
||||||
int buf_idx;
|
|
||||||
bool posted;
|
|
||||||
|
|
||||||
if (WARN_ON(cmd_size < sizeof(*cmd) || cmd_size > sizeof(*buf)))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
switch (cmd->op) {
|
|
||||||
case VIRTIO_PCIDEV_OP_CFG_WRITE:
|
|
||||||
case VIRTIO_PCIDEV_OP_MMIO_WRITE:
|
|
||||||
case VIRTIO_PCIDEV_OP_MMIO_MEMSET:
|
|
||||||
/* in PCI, writes are posted, so don't wait */
|
|
||||||
posted = !out;
|
|
||||||
WARN_ON(!posted);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
posted = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bounce_out = !posted && cmd_size <= sizeof(*cmd) &&
|
|
||||||
out && out_size <= sizeof(buf->data);
|
|
||||||
|
|
||||||
buf_idx = um_pci_get_buf(dev, &posted);
|
|
||||||
buf = &dev->bufs[buf_idx];
|
|
||||||
memcpy(buf, cmd, cmd_size);
|
|
||||||
|
|
||||||
if (posted && extra && extra_size > sizeof(buf) - cmd_size) {
|
|
||||||
dev->extra_ptrs[buf_idx] = kmemdup(extra, extra_size,
|
|
||||||
GFP_ATOMIC);
|
|
||||||
|
|
||||||
if (!dev->extra_ptrs[buf_idx]) {
|
|
||||||
um_pci_free_buf(dev, buf);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
extra = dev->extra_ptrs[buf_idx];
|
|
||||||
} else if (extra && extra_size <= sizeof(buf) - cmd_size) {
|
|
||||||
memcpy((u8 *)buf + cmd_size, extra, extra_size);
|
|
||||||
cmd_size += extra_size;
|
|
||||||
extra_size = 0;
|
|
||||||
extra = NULL;
|
|
||||||
cmd = (void *)buf;
|
|
||||||
} else {
|
|
||||||
cmd = (void *)buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
sg_init_one(&out_sg, cmd, cmd_size);
|
|
||||||
if (extra)
|
|
||||||
sg_init_one(&extra_sg, extra, extra_size);
|
|
||||||
/* allow stack for small buffers */
|
|
||||||
if (bounce_out)
|
|
||||||
sg_init_one(&in_sg, buf->data, out_size);
|
|
||||||
else if (out)
|
|
||||||
sg_init_one(&in_sg, out, out_size);
|
|
||||||
|
|
||||||
/* add to internal virtio queue */
|
|
||||||
ret = virtqueue_add_sgs(dev->cmd_vq, sgs_list,
|
|
||||||
extra ? 2 : 1,
|
|
||||||
out ? 1 : 0,
|
|
||||||
cmd, GFP_ATOMIC);
|
|
||||||
if (ret) {
|
|
||||||
um_pci_free_buf(dev, buf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (posted) {
|
|
||||||
virtqueue_kick(dev->cmd_vq);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* kick and poll for getting a response on the queue */
|
|
||||||
set_bit(UM_PCI_STAT_WAITING, &dev->status);
|
|
||||||
virtqueue_kick(dev->cmd_vq);
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
void *completed = virtqueue_get_buf(dev->cmd_vq, &len);
|
|
||||||
|
|
||||||
if (completed == buf)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (completed)
|
|
||||||
um_pci_free_buf(dev, completed);
|
|
||||||
|
|
||||||
if (WARN_ONCE(virtqueue_is_broken(dev->cmd_vq) ||
|
|
||||||
++delay_count > um_pci_max_delay_us,
|
|
||||||
"um virt-pci delay: %d", delay_count)) {
|
|
||||||
ret = -EIO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
udelay(1);
|
|
||||||
}
|
|
||||||
clear_bit(UM_PCI_STAT_WAITING, &dev->status);
|
|
||||||
|
|
||||||
if (bounce_out)
|
|
||||||
memcpy(out, buf->data, out_size);
|
|
||||||
|
|
||||||
um_pci_free_buf(dev, buf);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
|
static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
|
||||||
int size)
|
int size)
|
||||||
{
|
{
|
||||||
struct um_pci_device_reg *reg = priv;
|
struct um_pci_device_reg *reg = priv;
|
||||||
struct um_pci_device *dev = reg->dev;
|
struct um_pci_device *dev = reg->dev;
|
||||||
struct virtio_pcidev_msg hdr = {
|
|
||||||
.op = VIRTIO_PCIDEV_OP_CFG_READ,
|
|
||||||
.size = size,
|
|
||||||
.addr = offset,
|
|
||||||
};
|
|
||||||
/* max 8, we might not use it all */
|
|
||||||
u8 data[8];
|
|
||||||
|
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return ULONG_MAX;
|
return ULONG_MAX;
|
||||||
|
|
||||||
memset(data, 0xff, sizeof(data));
|
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
|
|
@ -251,23 +54,7 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
|
||||||
return ULONG_MAX;
|
return ULONG_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, data, size))
|
return dev->ops->cfgspace_read(dev, offset, size);
|
||||||
return ULONG_MAX;
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 1:
|
|
||||||
return data[0];
|
|
||||||
case 2:
|
|
||||||
return le16_to_cpup((void *)data);
|
|
||||||
case 4:
|
|
||||||
return le32_to_cpup((void *)data);
|
|
||||||
#ifdef CONFIG_64BIT
|
|
||||||
case 8:
|
|
||||||
return le64_to_cpup((void *)data);
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
return ULONG_MAX;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|
@ -275,42 +62,24 @@ static void um_pci_cfgspace_write(void *priv, unsigned int offset, int size,
|
||||||
{
|
{
|
||||||
struct um_pci_device_reg *reg = priv;
|
struct um_pci_device_reg *reg = priv;
|
||||||
struct um_pci_device *dev = reg->dev;
|
struct um_pci_device *dev = reg->dev;
|
||||||
struct {
|
|
||||||
struct virtio_pcidev_msg hdr;
|
|
||||||
/* maximum size - we may only use parts of it */
|
|
||||||
u8 data[8];
|
|
||||||
} msg = {
|
|
||||||
.hdr = {
|
|
||||||
.op = VIRTIO_PCIDEV_OP_CFG_WRITE,
|
|
||||||
.size = size,
|
|
||||||
.addr = offset,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
msg.data[0] = (u8)val;
|
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
put_unaligned_le16(val, (void *)msg.data);
|
|
||||||
break;
|
|
||||||
case 4:
|
case 4:
|
||||||
put_unaligned_le32(val, (void *)msg.data);
|
|
||||||
break;
|
|
||||||
#ifdef CONFIG_64BIT
|
#ifdef CONFIG_64BIT
|
||||||
case 8:
|
case 8:
|
||||||
put_unaligned_le64(val, (void *)msg.data);
|
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
WARN(1, "invalid config space write size %d\n", size);
|
WARN(1, "invalid config space write size %d\n", size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WARN_ON(um_pci_send_cmd(dev, &msg.hdr, sizeof(msg), NULL, 0, NULL, 0));
|
dev->ops->cfgspace_write(dev, offset, size, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct logic_iomem_ops um_pci_device_cfgspace_ops = {
|
static const struct logic_iomem_ops um_pci_device_cfgspace_ops = {
|
||||||
|
|
@ -318,6 +87,56 @@ static const struct logic_iomem_ops um_pci_device_cfgspace_ops = {
|
||||||
.write = um_pci_cfgspace_write,
|
.write = um_pci_cfgspace_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static unsigned long um_pci_bar_read(void *priv, unsigned int offset,
|
||||||
|
int size)
|
||||||
|
{
|
||||||
|
u8 *resptr = priv;
|
||||||
|
struct um_pci_device *dev = container_of(resptr - *resptr,
|
||||||
|
struct um_pci_device,
|
||||||
|
resptr[0]);
|
||||||
|
u8 bar = *resptr;
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
case 8:
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WARN(1, "invalid bar read size %d\n", size);
|
||||||
|
return ULONG_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev->ops->bar_read(dev, bar, offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void um_pci_bar_write(void *priv, unsigned int offset, int size,
|
||||||
|
unsigned long val)
|
||||||
|
{
|
||||||
|
u8 *resptr = priv;
|
||||||
|
struct um_pci_device *dev = container_of(resptr - *resptr,
|
||||||
|
struct um_pci_device,
|
||||||
|
resptr[0]);
|
||||||
|
u8 bar = *resptr;
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
case 8:
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WARN(1, "invalid bar write size %d\n", size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->ops->bar_write(dev, bar, offset, size, val);
|
||||||
|
}
|
||||||
|
|
||||||
static void um_pci_bar_copy_from(void *priv, void *buffer,
|
static void um_pci_bar_copy_from(void *priv, void *buffer,
|
||||||
unsigned int offset, int size)
|
unsigned int offset, int size)
|
||||||
{
|
{
|
||||||
|
|
@ -325,53 +144,9 @@ static void um_pci_bar_copy_from(void *priv, void *buffer,
|
||||||
struct um_pci_device *dev = container_of(resptr - *resptr,
|
struct um_pci_device *dev = container_of(resptr - *resptr,
|
||||||
struct um_pci_device,
|
struct um_pci_device,
|
||||||
resptr[0]);
|
resptr[0]);
|
||||||
struct virtio_pcidev_msg hdr = {
|
u8 bar = *resptr;
|
||||||
.op = VIRTIO_PCIDEV_OP_MMIO_READ,
|
|
||||||
.bar = *resptr,
|
|
||||||
.size = size,
|
|
||||||
.addr = offset,
|
|
||||||
};
|
|
||||||
|
|
||||||
memset(buffer, 0xff, size);
|
dev->ops->bar_copy_from(dev, bar, buffer, offset, size);
|
||||||
|
|
||||||
um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long um_pci_bar_read(void *priv, unsigned int offset,
|
|
||||||
int size)
|
|
||||||
{
|
|
||||||
/* 8 is maximum size - we may only use parts of it */
|
|
||||||
u8 data[8];
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
#ifdef CONFIG_64BIT
|
|
||||||
case 8:
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WARN(1, "invalid config space read size %d\n", size);
|
|
||||||
return ULONG_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
um_pci_bar_copy_from(priv, data, offset, size);
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 1:
|
|
||||||
return data[0];
|
|
||||||
case 2:
|
|
||||||
return le16_to_cpup((void *)data);
|
|
||||||
case 4:
|
|
||||||
return le32_to_cpup((void *)data);
|
|
||||||
#ifdef CONFIG_64BIT
|
|
||||||
case 8:
|
|
||||||
return le64_to_cpup((void *)data);
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
return ULONG_MAX;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void um_pci_bar_copy_to(void *priv, unsigned int offset,
|
static void um_pci_bar_copy_to(void *priv, unsigned int offset,
|
||||||
|
|
@ -381,43 +156,9 @@ static void um_pci_bar_copy_to(void *priv, unsigned int offset,
|
||||||
struct um_pci_device *dev = container_of(resptr - *resptr,
|
struct um_pci_device *dev = container_of(resptr - *resptr,
|
||||||
struct um_pci_device,
|
struct um_pci_device,
|
||||||
resptr[0]);
|
resptr[0]);
|
||||||
struct virtio_pcidev_msg hdr = {
|
u8 bar = *resptr;
|
||||||
.op = VIRTIO_PCIDEV_OP_MMIO_WRITE,
|
|
||||||
.bar = *resptr,
|
|
||||||
.size = size,
|
|
||||||
.addr = offset,
|
|
||||||
};
|
|
||||||
|
|
||||||
um_pci_send_cmd(dev, &hdr, sizeof(hdr), buffer, size, NULL, 0);
|
dev->ops->bar_copy_to(dev, bar, offset, buffer, size);
|
||||||
}
|
|
||||||
|
|
||||||
static void um_pci_bar_write(void *priv, unsigned int offset, int size,
|
|
||||||
unsigned long val)
|
|
||||||
{
|
|
||||||
/* maximum size - we may only use parts of it */
|
|
||||||
u8 data[8];
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 1:
|
|
||||||
data[0] = (u8)val;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
put_unaligned_le16(val, (void *)data);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
put_unaligned_le32(val, (void *)data);
|
|
||||||
break;
|
|
||||||
#ifdef CONFIG_64BIT
|
|
||||||
case 8:
|
|
||||||
put_unaligned_le64(val, (void *)data);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
WARN(1, "invalid config space write size %d\n", size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
um_pci_bar_copy_to(priv, offset, data, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void um_pci_bar_set(void *priv, unsigned int offset, u8 value, int size)
|
static void um_pci_bar_set(void *priv, unsigned int offset, u8 value, int size)
|
||||||
|
|
@ -426,20 +167,9 @@ static void um_pci_bar_set(void *priv, unsigned int offset, u8 value, int size)
|
||||||
struct um_pci_device *dev = container_of(resptr - *resptr,
|
struct um_pci_device *dev = container_of(resptr - *resptr,
|
||||||
struct um_pci_device,
|
struct um_pci_device,
|
||||||
resptr[0]);
|
resptr[0]);
|
||||||
struct {
|
u8 bar = *resptr;
|
||||||
struct virtio_pcidev_msg hdr;
|
|
||||||
u8 data;
|
|
||||||
} msg = {
|
|
||||||
.hdr = {
|
|
||||||
.op = VIRTIO_PCIDEV_OP_CFG_WRITE,
|
|
||||||
.bar = *resptr,
|
|
||||||
.size = size,
|
|
||||||
.addr = offset,
|
|
||||||
},
|
|
||||||
.data = value,
|
|
||||||
};
|
|
||||||
|
|
||||||
um_pci_send_cmd(dev, &msg.hdr, sizeof(msg), NULL, 0, NULL, 0);
|
dev->ops->bar_set(dev, bar, offset, value, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct logic_iomem_ops um_pci_device_bar_ops = {
|
static const struct logic_iomem_ops um_pci_device_bar_ops = {
|
||||||
|
|
@ -486,76 +216,6 @@ static void um_pci_rescan(void)
|
||||||
pci_unlock_rescan_remove();
|
pci_unlock_rescan_remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void um_pci_irq_vq_addbuf(struct virtqueue *vq, void *buf, bool kick)
|
|
||||||
{
|
|
||||||
struct scatterlist sg[1];
|
|
||||||
|
|
||||||
sg_init_one(sg, buf, MAX_IRQ_MSG_SIZE);
|
|
||||||
if (virtqueue_add_inbuf(vq, sg, 1, buf, GFP_ATOMIC))
|
|
||||||
kfree(buf);
|
|
||||||
else if (kick)
|
|
||||||
virtqueue_kick(vq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void um_pci_handle_irq_message(struct virtqueue *vq,
|
|
||||||
struct virtio_pcidev_msg *msg)
|
|
||||||
{
|
|
||||||
struct virtio_device *vdev = vq->vdev;
|
|
||||||
struct um_pci_device *dev = vdev->priv;
|
|
||||||
|
|
||||||
if (!dev->irq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* we should properly chain interrupts, but on ARCH=um we don't care */
|
|
||||||
|
|
||||||
switch (msg->op) {
|
|
||||||
case VIRTIO_PCIDEV_OP_INT:
|
|
||||||
generic_handle_irq(dev->irq);
|
|
||||||
break;
|
|
||||||
case VIRTIO_PCIDEV_OP_MSI:
|
|
||||||
/* our MSI message is just the interrupt number */
|
|
||||||
if (msg->size == sizeof(u32))
|
|
||||||
generic_handle_irq(le32_to_cpup((void *)msg->data));
|
|
||||||
else
|
|
||||||
generic_handle_irq(le16_to_cpup((void *)msg->data));
|
|
||||||
break;
|
|
||||||
case VIRTIO_PCIDEV_OP_PME:
|
|
||||||
/* nothing to do - we already woke up due to the message */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dev_err(&vdev->dev, "unexpected virt-pci message %d\n", msg->op);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void um_pci_cmd_vq_cb(struct virtqueue *vq)
|
|
||||||
{
|
|
||||||
struct virtio_device *vdev = vq->vdev;
|
|
||||||
struct um_pci_device *dev = vdev->priv;
|
|
||||||
void *cmd;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if (test_bit(UM_PCI_STAT_WAITING, &dev->status))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while ((cmd = virtqueue_get_buf(vq, &len)))
|
|
||||||
um_pci_free_buf(dev, cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void um_pci_irq_vq_cb(struct virtqueue *vq)
|
|
||||||
{
|
|
||||||
struct virtio_pcidev_msg *msg;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
while ((msg = virtqueue_get_buf(vq, &len))) {
|
|
||||||
if (len >= sizeof(*msg))
|
|
||||||
um_pci_handle_irq_message(vq, msg);
|
|
||||||
|
|
||||||
/* recycle the message buffer */
|
|
||||||
um_pci_irq_vq_addbuf(vq, msg, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
/* Copied from arch/x86/kernel/devicetree.c */
|
/* Copied from arch/x86/kernel/devicetree.c */
|
||||||
struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
|
struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
|
||||||
|
|
@ -577,200 +237,6 @@ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int um_pci_init_vqs(struct um_pci_device *dev)
|
|
||||||
{
|
|
||||||
struct virtqueue_info vqs_info[] = {
|
|
||||||
{ "cmd", um_pci_cmd_vq_cb },
|
|
||||||
{ "irq", um_pci_irq_vq_cb },
|
|
||||||
};
|
|
||||||
struct virtqueue *vqs[2];
|
|
||||||
int err, i;
|
|
||||||
|
|
||||||
err = virtio_find_vqs(dev->vdev, 2, vqs, vqs_info, NULL);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
dev->cmd_vq = vqs[0];
|
|
||||||
dev->irq_vq = vqs[1];
|
|
||||||
|
|
||||||
virtio_device_ready(dev->vdev);
|
|
||||||
|
|
||||||
for (i = 0; i < NUM_IRQ_MSGS; i++) {
|
|
||||||
void *msg = kzalloc(MAX_IRQ_MSG_SIZE, GFP_KERNEL);
|
|
||||||
|
|
||||||
if (msg)
|
|
||||||
um_pci_irq_vq_addbuf(dev->irq_vq, msg, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtqueue_kick(dev->irq_vq);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __um_pci_virtio_platform_remove(struct virtio_device *vdev,
|
|
||||||
struct um_pci_device *dev)
|
|
||||||
{
|
|
||||||
virtio_reset_device(vdev);
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
|
|
||||||
mutex_lock(&um_pci_mtx);
|
|
||||||
um_pci_platform_device = NULL;
|
|
||||||
mutex_unlock(&um_pci_mtx);
|
|
||||||
|
|
||||||
kfree(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int um_pci_virtio_platform_probe(struct virtio_device *vdev,
|
|
||||||
struct um_pci_device *dev)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
dev->platform = true;
|
|
||||||
|
|
||||||
mutex_lock(&um_pci_mtx);
|
|
||||||
|
|
||||||
if (um_pci_platform_device) {
|
|
||||||
mutex_unlock(&um_pci_mtx);
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = um_pci_init_vqs(dev);
|
|
||||||
if (ret) {
|
|
||||||
mutex_unlock(&um_pci_mtx);
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
um_pci_platform_device = dev;
|
|
||||||
|
|
||||||
mutex_unlock(&um_pci_mtx);
|
|
||||||
|
|
||||||
ret = of_platform_default_populate(vdev->dev.of_node, NULL, &vdev->dev);
|
|
||||||
if (ret)
|
|
||||||
__um_pci_virtio_platform_remove(vdev, dev);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
out_free:
|
|
||||||
kfree(dev);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int um_pci_virtio_probe(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct um_pci_device *dev;
|
|
||||||
int i, free = -1;
|
|
||||||
int err = -ENOSPC;
|
|
||||||
|
|
||||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
||||||
if (!dev)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
dev->vdev = vdev;
|
|
||||||
vdev->priv = dev;
|
|
||||||
|
|
||||||
if (of_device_is_compatible(vdev->dev.of_node, "simple-bus"))
|
|
||||||
return um_pci_virtio_platform_probe(vdev, dev);
|
|
||||||
|
|
||||||
mutex_lock(&um_pci_mtx);
|
|
||||||
for (i = 0; i < MAX_DEVICES; i++) {
|
|
||||||
if (um_pci_devices[i].dev)
|
|
||||||
continue;
|
|
||||||
free = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (free < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
err = um_pci_init_vqs(dev);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
dev->irq = irq_alloc_desc(numa_node_id());
|
|
||||||
if (dev->irq < 0) {
|
|
||||||
err = dev->irq;
|
|
||||||
goto err_reset;
|
|
||||||
}
|
|
||||||
um_pci_devices[free].dev = dev;
|
|
||||||
vdev->priv = dev;
|
|
||||||
|
|
||||||
mutex_unlock(&um_pci_mtx);
|
|
||||||
|
|
||||||
device_set_wakeup_enable(&vdev->dev, true);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In order to do suspend-resume properly, don't allow VQs
|
|
||||||
* to be suspended.
|
|
||||||
*/
|
|
||||||
virtio_uml_set_no_vq_suspend(vdev, true);
|
|
||||||
|
|
||||||
um_pci_rescan();
|
|
||||||
return 0;
|
|
||||||
err_reset:
|
|
||||||
virtio_reset_device(vdev);
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
error:
|
|
||||||
mutex_unlock(&um_pci_mtx);
|
|
||||||
kfree(dev);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void um_pci_virtio_remove(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct um_pci_device *dev = vdev->priv;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (dev->platform) {
|
|
||||||
of_platform_depopulate(&vdev->dev);
|
|
||||||
__um_pci_virtio_platform_remove(vdev, dev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
device_set_wakeup_enable(&vdev->dev, false);
|
|
||||||
|
|
||||||
mutex_lock(&um_pci_mtx);
|
|
||||||
for (i = 0; i < MAX_DEVICES; i++) {
|
|
||||||
if (um_pci_devices[i].dev != dev)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
um_pci_devices[i].dev = NULL;
|
|
||||||
irq_free_desc(dev->irq);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mutex_unlock(&um_pci_mtx);
|
|
||||||
|
|
||||||
if (i < MAX_DEVICES) {
|
|
||||||
struct pci_dev *pci_dev;
|
|
||||||
|
|
||||||
pci_dev = pci_get_slot(bridge->bus, i);
|
|
||||||
if (pci_dev)
|
|
||||||
pci_stop_and_remove_bus_device_locked(pci_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop all virtqueues */
|
|
||||||
virtio_reset_device(vdev);
|
|
||||||
dev->cmd_vq = NULL;
|
|
||||||
dev->irq_vq = NULL;
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
|
|
||||||
kfree(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct virtio_device_id id_table[] = {
|
|
||||||
{ CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID, VIRTIO_DEV_ANY_ID },
|
|
||||||
{ 0 },
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
|
||||||
|
|
||||||
static struct virtio_driver um_pci_virtio_driver = {
|
|
||||||
.driver.name = "virtio-pci",
|
|
||||||
.id_table = id_table,
|
|
||||||
.probe = um_pci_virtio_probe,
|
|
||||||
.remove = um_pci_virtio_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct resource virt_cfgspace_resource = {
|
static struct resource virt_cfgspace_resource = {
|
||||||
.name = "PCI config space",
|
.name = "PCI config space",
|
||||||
.start = 0xf0000000 - MAX_DEVICES * CFG_SPACE_SIZE,
|
.start = 0xf0000000 - MAX_DEVICES * CFG_SPACE_SIZE,
|
||||||
|
|
@ -889,7 +355,7 @@ static void um_pci_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct irq_chip um_pci_msi_bottom_irq_chip = {
|
static struct irq_chip um_pci_msi_bottom_irq_chip = {
|
||||||
.name = "UM virtio MSI",
|
.name = "UM virtual MSI",
|
||||||
.irq_compose_msi_msg = um_pci_compose_msi_msg,
|
.irq_compose_msi_msg = um_pci_compose_msi_msg,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -939,7 +405,7 @@ static const struct irq_domain_ops um_pci_inner_domain_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct irq_chip um_pci_msi_irq_chip = {
|
static struct irq_chip um_pci_msi_irq_chip = {
|
||||||
.name = "UM virtio PCIe MSI",
|
.name = "UM virtual PCIe MSI",
|
||||||
.irq_mask = pci_msi_mask_irq,
|
.irq_mask = pci_msi_mask_irq,
|
||||||
.irq_unmask = pci_msi_unmask_irq,
|
.irq_unmask = pci_msi_unmask_irq,
|
||||||
};
|
};
|
||||||
|
|
@ -998,6 +464,78 @@ static struct resource virt_platform_resource = {
|
||||||
.flags = IORESOURCE_MEM,
|
.flags = IORESOURCE_MEM,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int um_pci_device_register(struct um_pci_device *dev)
|
||||||
|
{
|
||||||
|
int i, free = -1;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
mutex_lock(&um_pci_mtx);
|
||||||
|
for (i = 0; i < MAX_DEVICES; i++) {
|
||||||
|
if (um_pci_devices[i].dev)
|
||||||
|
continue;
|
||||||
|
free = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free < 0) {
|
||||||
|
err = -ENOSPC;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->irq = irq_alloc_desc(numa_node_id());
|
||||||
|
if (dev->irq < 0) {
|
||||||
|
err = dev->irq;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
um_pci_devices[free].dev = dev;
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&um_pci_mtx);
|
||||||
|
if (!err)
|
||||||
|
um_pci_rescan();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void um_pci_device_unregister(struct um_pci_device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mutex_lock(&um_pci_mtx);
|
||||||
|
for (i = 0; i < MAX_DEVICES; i++) {
|
||||||
|
if (um_pci_devices[i].dev != dev)
|
||||||
|
continue;
|
||||||
|
um_pci_devices[i].dev = NULL;
|
||||||
|
irq_free_desc(dev->irq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mutex_unlock(&um_pci_mtx);
|
||||||
|
|
||||||
|
if (i < MAX_DEVICES) {
|
||||||
|
struct pci_dev *pci_dev;
|
||||||
|
|
||||||
|
pci_dev = pci_get_slot(bridge->bus, i);
|
||||||
|
if (pci_dev)
|
||||||
|
pci_stop_and_remove_bus_device_locked(pci_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int um_pci_platform_device_register(struct um_pci_device *dev)
|
||||||
|
{
|
||||||
|
guard(mutex)(&um_pci_mtx);
|
||||||
|
if (um_pci_platform_device)
|
||||||
|
return -EBUSY;
|
||||||
|
um_pci_platform_device = dev;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void um_pci_platform_device_unregister(struct um_pci_device *dev)
|
||||||
|
{
|
||||||
|
guard(mutex)(&um_pci_mtx);
|
||||||
|
if (um_pci_platform_device == dev)
|
||||||
|
um_pci_platform_device = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int __init um_pci_init(void)
|
static int __init um_pci_init(void)
|
||||||
{
|
{
|
||||||
struct irq_domain_info inner_domain_info = {
|
struct irq_domain_info inner_domain_info = {
|
||||||
|
|
@ -1014,10 +552,6 @@ static int __init um_pci_init(void)
|
||||||
WARN_ON(logic_iomem_add_region(&virt_platform_resource,
|
WARN_ON(logic_iomem_add_region(&virt_platform_resource,
|
||||||
&um_pci_platform_ops));
|
&um_pci_platform_ops));
|
||||||
|
|
||||||
if (WARN(CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID < 0,
|
|
||||||
"No virtio device ID configured for PCI - no PCI support\n"))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
bridge = pci_alloc_host_bridge(0);
|
bridge = pci_alloc_host_bridge(0);
|
||||||
if (!bridge) {
|
if (!bridge) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
|
@ -1065,10 +599,8 @@ static int __init um_pci_init(void)
|
||||||
if (err)
|
if (err)
|
||||||
goto free;
|
goto free;
|
||||||
|
|
||||||
err = register_virtio_driver(&um_pci_virtio_driver);
|
|
||||||
if (err)
|
|
||||||
goto free;
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free:
|
free:
|
||||||
if (!IS_ERR_OR_NULL(um_pci_inner_domain))
|
if (!IS_ERR_OR_NULL(um_pci_inner_domain))
|
||||||
irq_domain_remove(um_pci_inner_domain);
|
irq_domain_remove(um_pci_inner_domain);
|
||||||
|
|
@ -1080,11 +612,10 @@ static int __init um_pci_init(void)
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
module_init(um_pci_init);
|
device_initcall(um_pci_init);
|
||||||
|
|
||||||
static void __exit um_pci_exit(void)
|
static void __exit um_pci_exit(void)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
|
||||||
41
arch/um/drivers/virt-pci.h
Normal file
41
arch/um/drivers/virt-pci.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef __UM_VIRT_PCI_H
|
||||||
|
#define __UM_VIRT_PCI_H
|
||||||
|
|
||||||
|
#include <linux/pci.h>
|
||||||
|
|
||||||
|
struct um_pci_device {
|
||||||
|
const struct um_pci_ops *ops;
|
||||||
|
|
||||||
|
/* for now just standard BARs */
|
||||||
|
u8 resptr[PCI_STD_NUM_BARS];
|
||||||
|
|
||||||
|
int irq;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct um_pci_ops {
|
||||||
|
unsigned long (*cfgspace_read)(struct um_pci_device *dev,
|
||||||
|
unsigned int offset, int size);
|
||||||
|
void (*cfgspace_write)(struct um_pci_device *dev, unsigned int offset,
|
||||||
|
int size, unsigned long val);
|
||||||
|
|
||||||
|
unsigned long (*bar_read)(struct um_pci_device *dev, int bar,
|
||||||
|
unsigned int offset, int size);
|
||||||
|
void (*bar_write)(struct um_pci_device *dev, int bar,
|
||||||
|
unsigned int offset, int size, unsigned long val);
|
||||||
|
|
||||||
|
void (*bar_copy_from)(struct um_pci_device *dev, int bar, void *buffer,
|
||||||
|
unsigned int offset, int size);
|
||||||
|
void (*bar_copy_to)(struct um_pci_device *dev, int bar,
|
||||||
|
unsigned int offset, const void *buffer, int size);
|
||||||
|
void (*bar_set)(struct um_pci_device *dev, int bar,
|
||||||
|
unsigned int offset, u8 value, int size);
|
||||||
|
};
|
||||||
|
|
||||||
|
int um_pci_device_register(struct um_pci_device *dev);
|
||||||
|
void um_pci_device_unregister(struct um_pci_device *dev);
|
||||||
|
|
||||||
|
int um_pci_platform_device_register(struct um_pci_device *dev);
|
||||||
|
void um_pci_platform_device_unregister(struct um_pci_device *dev);
|
||||||
|
|
||||||
|
#endif /* __UM_VIRT_PCI_H */
|
||||||
628
arch/um/drivers/virtio_pcidev.c
Normal file
628
arch/um/drivers/virtio_pcidev.c
Normal file
|
|
@ -0,0 +1,628 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Intel Corporation
|
||||||
|
* Author: Johannes Berg <johannes@sipsolutions.net>
|
||||||
|
*/
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/virtio.h>
|
||||||
|
#include <linux/virtio_config.h>
|
||||||
|
#include <linux/logic_iomem.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/virtio_pcidev.h>
|
||||||
|
#include <linux/virtio-uml.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/msi.h>
|
||||||
|
#include <linux/unaligned.h>
|
||||||
|
#include <irq_kern.h>
|
||||||
|
|
||||||
|
#include "virt-pci.h"
|
||||||
|
|
||||||
|
#define to_virtio_pcidev(_pdev) \
|
||||||
|
container_of(_pdev, struct virtio_pcidev_device, pdev)
|
||||||
|
|
||||||
|
/* for MSI-X we have a 32-bit payload */
|
||||||
|
#define MAX_IRQ_MSG_SIZE (sizeof(struct virtio_pcidev_msg) + sizeof(u32))
|
||||||
|
#define NUM_IRQ_MSGS 10
|
||||||
|
|
||||||
|
struct virtio_pcidev_message_buffer {
|
||||||
|
struct virtio_pcidev_msg hdr;
|
||||||
|
u8 data[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct virtio_pcidev_device {
|
||||||
|
struct um_pci_device pdev;
|
||||||
|
struct virtio_device *vdev;
|
||||||
|
|
||||||
|
struct virtqueue *cmd_vq, *irq_vq;
|
||||||
|
|
||||||
|
#define VIRTIO_PCIDEV_WRITE_BUFS 20
|
||||||
|
struct virtio_pcidev_message_buffer bufs[VIRTIO_PCIDEV_WRITE_BUFS + 1];
|
||||||
|
void *extra_ptrs[VIRTIO_PCIDEV_WRITE_BUFS + 1];
|
||||||
|
DECLARE_BITMAP(used_bufs, VIRTIO_PCIDEV_WRITE_BUFS);
|
||||||
|
|
||||||
|
#define UM_PCI_STAT_WAITING 0
|
||||||
|
unsigned long status;
|
||||||
|
|
||||||
|
bool platform;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int virtio_pcidev_max_delay_us = 40000;
|
||||||
|
module_param_named(max_delay_us, virtio_pcidev_max_delay_us, uint, 0644);
|
||||||
|
|
||||||
|
static int virtio_pcidev_get_buf(struct virtio_pcidev_device *dev, bool *posted)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < VIRTIO_PCIDEV_WRITE_BUFS; i++) {
|
||||||
|
if (!test_and_set_bit(i, dev->used_bufs))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
*posted = false;
|
||||||
|
return VIRTIO_PCIDEV_WRITE_BUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_free_buf(struct virtio_pcidev_device *dev, void *buf)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (buf == &dev->bufs[VIRTIO_PCIDEV_WRITE_BUFS]) {
|
||||||
|
kfree(dev->extra_ptrs[VIRTIO_PCIDEV_WRITE_BUFS]);
|
||||||
|
dev->extra_ptrs[VIRTIO_PCIDEV_WRITE_BUFS] = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < VIRTIO_PCIDEV_WRITE_BUFS; i++) {
|
||||||
|
if (buf == &dev->bufs[i]) {
|
||||||
|
kfree(dev->extra_ptrs[i]);
|
||||||
|
dev->extra_ptrs[i] = NULL;
|
||||||
|
WARN_ON(!test_and_clear_bit(i, dev->used_bufs));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_pcidev_send_cmd(struct virtio_pcidev_device *dev,
|
||||||
|
struct virtio_pcidev_msg *cmd,
|
||||||
|
unsigned int cmd_size,
|
||||||
|
const void *extra, unsigned int extra_size,
|
||||||
|
void *out, unsigned int out_size)
|
||||||
|
{
|
||||||
|
struct scatterlist out_sg, extra_sg, in_sg;
|
||||||
|
struct scatterlist *sgs_list[] = {
|
||||||
|
[0] = &out_sg,
|
||||||
|
[1] = extra ? &extra_sg : &in_sg,
|
||||||
|
[2] = extra ? &in_sg : NULL,
|
||||||
|
};
|
||||||
|
struct virtio_pcidev_message_buffer *buf;
|
||||||
|
int delay_count = 0;
|
||||||
|
bool bounce_out;
|
||||||
|
int ret, len;
|
||||||
|
int buf_idx;
|
||||||
|
bool posted;
|
||||||
|
|
||||||
|
if (WARN_ON(cmd_size < sizeof(*cmd) || cmd_size > sizeof(*buf)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (cmd->op) {
|
||||||
|
case VIRTIO_PCIDEV_OP_CFG_WRITE:
|
||||||
|
case VIRTIO_PCIDEV_OP_MMIO_WRITE:
|
||||||
|
case VIRTIO_PCIDEV_OP_MMIO_MEMSET:
|
||||||
|
/* in PCI, writes are posted, so don't wait */
|
||||||
|
posted = !out;
|
||||||
|
WARN_ON(!posted);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
posted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bounce_out = !posted && cmd_size <= sizeof(*cmd) &&
|
||||||
|
out && out_size <= sizeof(buf->data);
|
||||||
|
|
||||||
|
buf_idx = virtio_pcidev_get_buf(dev, &posted);
|
||||||
|
buf = &dev->bufs[buf_idx];
|
||||||
|
memcpy(buf, cmd, cmd_size);
|
||||||
|
|
||||||
|
if (posted && extra && extra_size > sizeof(buf) - cmd_size) {
|
||||||
|
dev->extra_ptrs[buf_idx] = kmemdup(extra, extra_size,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (!dev->extra_ptrs[buf_idx]) {
|
||||||
|
virtio_pcidev_free_buf(dev, buf);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
extra = dev->extra_ptrs[buf_idx];
|
||||||
|
} else if (extra && extra_size <= sizeof(buf) - cmd_size) {
|
||||||
|
memcpy((u8 *)buf + cmd_size, extra, extra_size);
|
||||||
|
cmd_size += extra_size;
|
||||||
|
extra_size = 0;
|
||||||
|
extra = NULL;
|
||||||
|
cmd = (void *)buf;
|
||||||
|
} else {
|
||||||
|
cmd = (void *)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
sg_init_one(&out_sg, cmd, cmd_size);
|
||||||
|
if (extra)
|
||||||
|
sg_init_one(&extra_sg, extra, extra_size);
|
||||||
|
/* allow stack for small buffers */
|
||||||
|
if (bounce_out)
|
||||||
|
sg_init_one(&in_sg, buf->data, out_size);
|
||||||
|
else if (out)
|
||||||
|
sg_init_one(&in_sg, out, out_size);
|
||||||
|
|
||||||
|
/* add to internal virtio queue */
|
||||||
|
ret = virtqueue_add_sgs(dev->cmd_vq, sgs_list,
|
||||||
|
extra ? 2 : 1,
|
||||||
|
out ? 1 : 0,
|
||||||
|
cmd, GFP_ATOMIC);
|
||||||
|
if (ret) {
|
||||||
|
virtio_pcidev_free_buf(dev, buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (posted) {
|
||||||
|
virtqueue_kick(dev->cmd_vq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* kick and poll for getting a response on the queue */
|
||||||
|
set_bit(UM_PCI_STAT_WAITING, &dev->status);
|
||||||
|
virtqueue_kick(dev->cmd_vq);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
void *completed = virtqueue_get_buf(dev->cmd_vq, &len);
|
||||||
|
|
||||||
|
if (completed == buf)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (completed)
|
||||||
|
virtio_pcidev_free_buf(dev, completed);
|
||||||
|
|
||||||
|
if (WARN_ONCE(virtqueue_is_broken(dev->cmd_vq) ||
|
||||||
|
++delay_count > virtio_pcidev_max_delay_us,
|
||||||
|
"um virt-pci delay: %d", delay_count)) {
|
||||||
|
ret = -EIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
udelay(1);
|
||||||
|
}
|
||||||
|
clear_bit(UM_PCI_STAT_WAITING, &dev->status);
|
||||||
|
|
||||||
|
if (bounce_out)
|
||||||
|
memcpy(out, buf->data, out_size);
|
||||||
|
|
||||||
|
virtio_pcidev_free_buf(dev, buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long virtio_pcidev_cfgspace_read(struct um_pci_device *pdev,
|
||||||
|
unsigned int offset, int size)
|
||||||
|
{
|
||||||
|
struct virtio_pcidev_device *dev = to_virtio_pcidev(pdev);
|
||||||
|
struct virtio_pcidev_msg hdr = {
|
||||||
|
.op = VIRTIO_PCIDEV_OP_CFG_READ,
|
||||||
|
.size = size,
|
||||||
|
.addr = offset,
|
||||||
|
};
|
||||||
|
/* max 8, we might not use it all */
|
||||||
|
u8 data[8];
|
||||||
|
|
||||||
|
memset(data, 0xff, sizeof(data));
|
||||||
|
|
||||||
|
/* size has been checked in um_pci_cfgspace_read() */
|
||||||
|
if (virtio_pcidev_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, data, size))
|
||||||
|
return ULONG_MAX;
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
return data[0];
|
||||||
|
case 2:
|
||||||
|
return le16_to_cpup((void *)data);
|
||||||
|
case 4:
|
||||||
|
return le32_to_cpup((void *)data);
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
case 8:
|
||||||
|
return le64_to_cpup((void *)data);
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return ULONG_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_cfgspace_write(struct um_pci_device *pdev,
|
||||||
|
unsigned int offset, int size,
|
||||||
|
unsigned long val)
|
||||||
|
{
|
||||||
|
struct virtio_pcidev_device *dev = to_virtio_pcidev(pdev);
|
||||||
|
struct {
|
||||||
|
struct virtio_pcidev_msg hdr;
|
||||||
|
/* maximum size - we may only use parts of it */
|
||||||
|
u8 data[8];
|
||||||
|
} msg = {
|
||||||
|
.hdr = {
|
||||||
|
.op = VIRTIO_PCIDEV_OP_CFG_WRITE,
|
||||||
|
.size = size,
|
||||||
|
.addr = offset,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* size has been checked in um_pci_cfgspace_write() */
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
msg.data[0] = (u8)val;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
put_unaligned_le16(val, (void *)msg.data);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
put_unaligned_le32(val, (void *)msg.data);
|
||||||
|
break;
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
case 8:
|
||||||
|
put_unaligned_le64(val, (void *)msg.data);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON(virtio_pcidev_send_cmd(dev, &msg.hdr, sizeof(msg), NULL, 0, NULL, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_bar_copy_from(struct um_pci_device *pdev,
|
||||||
|
int bar, void *buffer,
|
||||||
|
unsigned int offset, int size)
|
||||||
|
{
|
||||||
|
struct virtio_pcidev_device *dev = to_virtio_pcidev(pdev);
|
||||||
|
struct virtio_pcidev_msg hdr = {
|
||||||
|
.op = VIRTIO_PCIDEV_OP_MMIO_READ,
|
||||||
|
.bar = bar,
|
||||||
|
.size = size,
|
||||||
|
.addr = offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
memset(buffer, 0xff, size);
|
||||||
|
|
||||||
|
virtio_pcidev_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long virtio_pcidev_bar_read(struct um_pci_device *pdev, int bar,
|
||||||
|
unsigned int offset, int size)
|
||||||
|
{
|
||||||
|
/* 8 is maximum size - we may only use parts of it */
|
||||||
|
u8 data[8];
|
||||||
|
|
||||||
|
/* size has been checked in um_pci_bar_read() */
|
||||||
|
virtio_pcidev_bar_copy_from(pdev, bar, data, offset, size);
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
return data[0];
|
||||||
|
case 2:
|
||||||
|
return le16_to_cpup((void *)data);
|
||||||
|
case 4:
|
||||||
|
return le32_to_cpup((void *)data);
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
case 8:
|
||||||
|
return le64_to_cpup((void *)data);
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return ULONG_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_bar_copy_to(struct um_pci_device *pdev,
|
||||||
|
int bar, unsigned int offset,
|
||||||
|
const void *buffer, int size)
|
||||||
|
{
|
||||||
|
struct virtio_pcidev_device *dev = to_virtio_pcidev(pdev);
|
||||||
|
struct virtio_pcidev_msg hdr = {
|
||||||
|
.op = VIRTIO_PCIDEV_OP_MMIO_WRITE,
|
||||||
|
.bar = bar,
|
||||||
|
.size = size,
|
||||||
|
.addr = offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtio_pcidev_send_cmd(dev, &hdr, sizeof(hdr), buffer, size, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_bar_write(struct um_pci_device *pdev, int bar,
|
||||||
|
unsigned int offset, int size,
|
||||||
|
unsigned long val)
|
||||||
|
{
|
||||||
|
/* maximum size - we may only use parts of it */
|
||||||
|
u8 data[8];
|
||||||
|
|
||||||
|
/* size has been checked in um_pci_bar_write() */
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
data[0] = (u8)val;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
put_unaligned_le16(val, (void *)data);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
put_unaligned_le32(val, (void *)data);
|
||||||
|
break;
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
case 8:
|
||||||
|
put_unaligned_le64(val, (void *)data);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
virtio_pcidev_bar_copy_to(pdev, bar, offset, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_bar_set(struct um_pci_device *pdev, int bar,
|
||||||
|
unsigned int offset, u8 value, int size)
|
||||||
|
{
|
||||||
|
struct virtio_pcidev_device *dev = to_virtio_pcidev(pdev);
|
||||||
|
struct {
|
||||||
|
struct virtio_pcidev_msg hdr;
|
||||||
|
u8 data;
|
||||||
|
} msg = {
|
||||||
|
.hdr = {
|
||||||
|
.op = VIRTIO_PCIDEV_OP_CFG_WRITE,
|
||||||
|
.bar = bar,
|
||||||
|
.size = size,
|
||||||
|
.addr = offset,
|
||||||
|
},
|
||||||
|
.data = value,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtio_pcidev_send_cmd(dev, &msg.hdr, sizeof(msg), NULL, 0, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct um_pci_ops virtio_pcidev_um_pci_ops = {
|
||||||
|
.cfgspace_read = virtio_pcidev_cfgspace_read,
|
||||||
|
.cfgspace_write = virtio_pcidev_cfgspace_write,
|
||||||
|
.bar_read = virtio_pcidev_bar_read,
|
||||||
|
.bar_write = virtio_pcidev_bar_write,
|
||||||
|
.bar_copy_from = virtio_pcidev_bar_copy_from,
|
||||||
|
.bar_copy_to = virtio_pcidev_bar_copy_to,
|
||||||
|
.bar_set = virtio_pcidev_bar_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void virtio_pcidev_irq_vq_addbuf(struct virtqueue *vq, void *buf, bool kick)
|
||||||
|
{
|
||||||
|
struct scatterlist sg[1];
|
||||||
|
|
||||||
|
sg_init_one(sg, buf, MAX_IRQ_MSG_SIZE);
|
||||||
|
if (virtqueue_add_inbuf(vq, sg, 1, buf, GFP_ATOMIC))
|
||||||
|
kfree(buf);
|
||||||
|
else if (kick)
|
||||||
|
virtqueue_kick(vq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_handle_irq_message(struct virtqueue *vq,
|
||||||
|
struct virtio_pcidev_msg *msg)
|
||||||
|
{
|
||||||
|
struct virtio_device *vdev = vq->vdev;
|
||||||
|
struct virtio_pcidev_device *dev = vdev->priv;
|
||||||
|
|
||||||
|
if (!dev->pdev.irq)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* we should properly chain interrupts, but on ARCH=um we don't care */
|
||||||
|
|
||||||
|
switch (msg->op) {
|
||||||
|
case VIRTIO_PCIDEV_OP_INT:
|
||||||
|
generic_handle_irq(dev->pdev.irq);
|
||||||
|
break;
|
||||||
|
case VIRTIO_PCIDEV_OP_MSI:
|
||||||
|
/* our MSI message is just the interrupt number */
|
||||||
|
if (msg->size == sizeof(u32))
|
||||||
|
generic_handle_irq(le32_to_cpup((void *)msg->data));
|
||||||
|
else
|
||||||
|
generic_handle_irq(le16_to_cpup((void *)msg->data));
|
||||||
|
break;
|
||||||
|
case VIRTIO_PCIDEV_OP_PME:
|
||||||
|
/* nothing to do - we already woke up due to the message */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(&vdev->dev, "unexpected virt-pci message %d\n", msg->op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_cmd_vq_cb(struct virtqueue *vq)
|
||||||
|
{
|
||||||
|
struct virtio_device *vdev = vq->vdev;
|
||||||
|
struct virtio_pcidev_device *dev = vdev->priv;
|
||||||
|
void *cmd;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (test_bit(UM_PCI_STAT_WAITING, &dev->status))
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ((cmd = virtqueue_get_buf(vq, &len)))
|
||||||
|
virtio_pcidev_free_buf(dev, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_irq_vq_cb(struct virtqueue *vq)
|
||||||
|
{
|
||||||
|
struct virtio_pcidev_msg *msg;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
while ((msg = virtqueue_get_buf(vq, &len))) {
|
||||||
|
if (len >= sizeof(*msg))
|
||||||
|
virtio_pcidev_handle_irq_message(vq, msg);
|
||||||
|
|
||||||
|
/* recycle the message buffer */
|
||||||
|
virtio_pcidev_irq_vq_addbuf(vq, msg, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_pcidev_init_vqs(struct virtio_pcidev_device *dev)
|
||||||
|
{
|
||||||
|
struct virtqueue_info vqs_info[] = {
|
||||||
|
{ "cmd", virtio_pcidev_cmd_vq_cb },
|
||||||
|
{ "irq", virtio_pcidev_irq_vq_cb },
|
||||||
|
};
|
||||||
|
struct virtqueue *vqs[2];
|
||||||
|
int err, i;
|
||||||
|
|
||||||
|
err = virtio_find_vqs(dev->vdev, 2, vqs, vqs_info, NULL);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
dev->cmd_vq = vqs[0];
|
||||||
|
dev->irq_vq = vqs[1];
|
||||||
|
|
||||||
|
virtio_device_ready(dev->vdev);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_IRQ_MSGS; i++) {
|
||||||
|
void *msg = kzalloc(MAX_IRQ_MSG_SIZE, GFP_KERNEL);
|
||||||
|
|
||||||
|
if (msg)
|
||||||
|
virtio_pcidev_irq_vq_addbuf(dev->irq_vq, msg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtqueue_kick(dev->irq_vq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __virtio_pcidev_virtio_platform_remove(struct virtio_device *vdev,
|
||||||
|
struct virtio_pcidev_device *dev)
|
||||||
|
{
|
||||||
|
um_pci_platform_device_unregister(&dev->pdev);
|
||||||
|
|
||||||
|
virtio_reset_device(vdev);
|
||||||
|
vdev->config->del_vqs(vdev);
|
||||||
|
|
||||||
|
kfree(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_pcidev_virtio_platform_probe(struct virtio_device *vdev,
|
||||||
|
struct virtio_pcidev_device *dev)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
dev->platform = true;
|
||||||
|
|
||||||
|
err = virtio_pcidev_init_vqs(dev);
|
||||||
|
if (err)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
err = um_pci_platform_device_register(&dev->pdev);
|
||||||
|
if (err)
|
||||||
|
goto err_reset;
|
||||||
|
|
||||||
|
err = of_platform_default_populate(vdev->dev.of_node, NULL, &vdev->dev);
|
||||||
|
if (err)
|
||||||
|
goto err_unregister;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_unregister:
|
||||||
|
um_pci_platform_device_unregister(&dev->pdev);
|
||||||
|
err_reset:
|
||||||
|
virtio_reset_device(vdev);
|
||||||
|
vdev->config->del_vqs(vdev);
|
||||||
|
err_free:
|
||||||
|
kfree(dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_pcidev_virtio_probe(struct virtio_device *vdev)
|
||||||
|
{
|
||||||
|
struct virtio_pcidev_device *dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||||
|
if (!dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dev->vdev = vdev;
|
||||||
|
vdev->priv = dev;
|
||||||
|
|
||||||
|
dev->pdev.ops = &virtio_pcidev_um_pci_ops;
|
||||||
|
|
||||||
|
if (of_device_is_compatible(vdev->dev.of_node, "simple-bus"))
|
||||||
|
return virtio_pcidev_virtio_platform_probe(vdev, dev);
|
||||||
|
|
||||||
|
err = virtio_pcidev_init_vqs(dev);
|
||||||
|
if (err)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
err = um_pci_device_register(&dev->pdev);
|
||||||
|
if (err)
|
||||||
|
goto err_reset;
|
||||||
|
|
||||||
|
device_set_wakeup_enable(&vdev->dev, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In order to do suspend-resume properly, don't allow VQs
|
||||||
|
* to be suspended.
|
||||||
|
*/
|
||||||
|
virtio_uml_set_no_vq_suspend(vdev, true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_reset:
|
||||||
|
virtio_reset_device(vdev);
|
||||||
|
vdev->config->del_vqs(vdev);
|
||||||
|
err_free:
|
||||||
|
kfree(dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pcidev_virtio_remove(struct virtio_device *vdev)
|
||||||
|
{
|
||||||
|
struct virtio_pcidev_device *dev = vdev->priv;
|
||||||
|
|
||||||
|
if (dev->platform) {
|
||||||
|
of_platform_depopulate(&vdev->dev);
|
||||||
|
__virtio_pcidev_virtio_platform_remove(vdev, dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_set_wakeup_enable(&vdev->dev, false);
|
||||||
|
|
||||||
|
um_pci_device_unregister(&dev->pdev);
|
||||||
|
|
||||||
|
/* Stop all virtqueues */
|
||||||
|
virtio_reset_device(vdev);
|
||||||
|
dev->cmd_vq = NULL;
|
||||||
|
dev->irq_vq = NULL;
|
||||||
|
vdev->config->del_vqs(vdev);
|
||||||
|
|
||||||
|
kfree(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct virtio_device_id id_table[] = {
|
||||||
|
{ CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID, VIRTIO_DEV_ANY_ID },
|
||||||
|
{ 0 },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||||
|
|
||||||
|
static struct virtio_driver virtio_pcidev_virtio_driver = {
|
||||||
|
.driver.name = "virtio-pci",
|
||||||
|
.id_table = id_table,
|
||||||
|
.probe = virtio_pcidev_virtio_probe,
|
||||||
|
.remove = virtio_pcidev_virtio_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init virtio_pcidev_init(void)
|
||||||
|
{
|
||||||
|
if (WARN(CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID < 0,
|
||||||
|
"No virtio device ID configured for PCI - no PCI support\n"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return register_virtio_driver(&virtio_pcidev_virtio_driver);
|
||||||
|
}
|
||||||
|
late_initcall(virtio_pcidev_init);
|
||||||
|
|
||||||
|
static void __exit virtio_pcidev_exit(void)
|
||||||
|
{
|
||||||
|
unregister_virtio_driver(&virtio_pcidev_virtio_driver);
|
||||||
|
}
|
||||||
|
module_exit(virtio_pcidev_exit);
|
||||||
|
|
@ -13,6 +13,7 @@ generic-y += irq_work.h
|
||||||
generic-y += kdebug.h
|
generic-y += kdebug.h
|
||||||
generic-y += mcs_spinlock.h
|
generic-y += mcs_spinlock.h
|
||||||
generic-y += mmiowb.h
|
generic-y += mmiowb.h
|
||||||
|
generic-y += module.h
|
||||||
generic-y += module.lds.h
|
generic-y += module.lds.h
|
||||||
generic-y += param.h
|
generic-y += param.h
|
||||||
generic-y += parport.h
|
generic-y += parport.h
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ struct thread_struct {
|
||||||
} thread;
|
} thread;
|
||||||
} request;
|
} request;
|
||||||
|
|
||||||
|
void *segv_continue;
|
||||||
|
|
||||||
/* Contains variable sized FP registers */
|
/* Contains variable sized FP registers */
|
||||||
struct pt_regs regs;
|
struct pt_regs regs;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <asm/elf.h>
|
#include <asm/elf.h>
|
||||||
#include <linux/unaligned.h>
|
#include <linux/unaligned.h>
|
||||||
|
#include <sysdep/faultinfo.h>
|
||||||
|
|
||||||
#define __under_task_size(addr, size) \
|
#define __under_task_size(addr, size) \
|
||||||
(((unsigned long) (addr) < TASK_SIZE) && \
|
(((unsigned long) (addr) < TASK_SIZE) && \
|
||||||
|
|
@ -44,19 +45,28 @@ static inline int __access_ok(const void __user *ptr, unsigned long size)
|
||||||
__access_ok_vsyscall(addr, size));
|
__access_ok_vsyscall(addr, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* no pagefaults for kernel addresses in um */
|
|
||||||
#define __get_kernel_nofault(dst, src, type, err_label) \
|
#define __get_kernel_nofault(dst, src, type, err_label) \
|
||||||
do { \
|
do { \
|
||||||
*((type *)dst) = get_unaligned((type *)(src)); \
|
int __faulted; \
|
||||||
if (0) /* make sure the label looks used to the compiler */ \
|
\
|
||||||
|
___backtrack_faulted(__faulted); \
|
||||||
|
if (__faulted) { \
|
||||||
|
*((type *)dst) = (type) 0; \
|
||||||
goto err_label; \
|
goto err_label; \
|
||||||
|
} \
|
||||||
|
*((type *)dst) = get_unaligned((type *)(src)); \
|
||||||
|
current->thread.segv_continue = NULL; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define __put_kernel_nofault(dst, src, type, err_label) \
|
#define __put_kernel_nofault(dst, src, type, err_label) \
|
||||||
do { \
|
do { \
|
||||||
put_unaligned(*((type *)src), (type *)(dst)); \
|
int __faulted; \
|
||||||
if (0) /* make sure the label looks used to the compiler */ \
|
\
|
||||||
|
___backtrack_faulted(__faulted); \
|
||||||
|
if (__faulted) \
|
||||||
goto err_label; \
|
goto err_label; \
|
||||||
|
put_unaligned(*((type *)src), (type *)(dst)); \
|
||||||
|
current->thread.segv_continue = NULL; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ extern void time_travel_not_configured(void);
|
||||||
#define time_travel_del_event(...) time_travel_not_configured()
|
#define time_travel_del_event(...) time_travel_not_configured()
|
||||||
#endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */
|
#endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */
|
||||||
|
|
||||||
|
extern unsigned long tt_extra_sched_jiffies;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Without CONFIG_UML_TIME_TRAVEL_SUPPORT this is a linker error if used,
|
* Without CONFIG_UML_TIME_TRAVEL_SUPPORT this is a linker error if used,
|
||||||
* which is intentional since we really shouldn't link it in that case.
|
* which is intentional since we really shouldn't link it in that case.
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,6 @@ extern void arch_check_bugs(void);
|
||||||
extern int arch_fixup(unsigned long address, struct uml_pt_regs *regs);
|
extern int arch_fixup(unsigned long address, struct uml_pt_regs *regs);
|
||||||
extern void arch_examine_signal(int sig, struct uml_pt_regs *regs);
|
extern void arch_examine_signal(int sig, struct uml_pt_regs *regs);
|
||||||
|
|
||||||
|
void mc_set_rip(void *_mc, void *target);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ extern int linux_main(int argc, char **argv, char **envp);
|
||||||
extern void uml_finishsetup(void);
|
extern void uml_finishsetup(void);
|
||||||
|
|
||||||
struct siginfo;
|
struct siginfo;
|
||||||
extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *);
|
extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *, void *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ enum um_irq_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct siginfo;
|
struct siginfo;
|
||||||
extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
|
extern void sigio_handler(int sig, struct siginfo *unused_si,
|
||||||
|
struct uml_pt_regs *regs, void *mc);
|
||||||
void sigio_run_timetravel_handlers(void);
|
void sigio_run_timetravel_handlers(void);
|
||||||
extern void free_irq_by_fd(int fd);
|
extern void free_irq_by_fd(int fd);
|
||||||
extern void deactivate_fd(int fd, int irqnum);
|
extern void deactivate_fd(int fd, int irqnum);
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,12 @@ extern void free_stack(unsigned long stack, int order);
|
||||||
struct pt_regs;
|
struct pt_regs;
|
||||||
extern void do_signal(struct pt_regs *regs);
|
extern void do_signal(struct pt_regs *regs);
|
||||||
extern void interrupt_end(void);
|
extern void interrupt_end(void);
|
||||||
extern void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs);
|
extern void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs,
|
||||||
|
void *mc);
|
||||||
|
|
||||||
extern unsigned long segv(struct faultinfo fi, unsigned long ip,
|
extern unsigned long segv(struct faultinfo fi, unsigned long ip,
|
||||||
int is_user, struct uml_pt_regs *regs);
|
int is_user, struct uml_pt_regs *regs,
|
||||||
|
void *mc);
|
||||||
extern int handle_page_fault(unsigned long address, unsigned long ip,
|
extern int handle_page_fault(unsigned long address, unsigned long ip,
|
||||||
int is_write, int is_user, int *code_out);
|
int is_write, int is_user, int *code_out);
|
||||||
|
|
||||||
|
|
@ -59,8 +61,10 @@ extern unsigned long from_irq_stack(int nested);
|
||||||
|
|
||||||
extern int singlestepping(void);
|
extern int singlestepping(void);
|
||||||
|
|
||||||
extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
|
extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
|
||||||
extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
|
void *mc);
|
||||||
|
extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
|
||||||
|
void *mc);
|
||||||
extern void fatal_sigsegv(void) __attribute__ ((noreturn));
|
extern void fatal_sigsegv(void) __attribute__ ((noreturn));
|
||||||
|
|
||||||
void um_idle_sleep(void);
|
void um_idle_sleep(void);
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,6 @@ extern int os_protect_memory(void *addr, unsigned long len,
|
||||||
extern int os_unmap_memory(void *addr, int len);
|
extern int os_unmap_memory(void *addr, int len);
|
||||||
extern int os_drop_memory(void *addr, int length);
|
extern int os_drop_memory(void *addr, int length);
|
||||||
extern int can_drop_memory(void);
|
extern int can_drop_memory(void);
|
||||||
extern int os_mincore(void *addr, unsigned long len);
|
|
||||||
|
|
||||||
void os_set_pdeathsig(void);
|
void os_set_pdeathsig(void);
|
||||||
|
|
||||||
|
|
@ -225,6 +224,11 @@ extern int run_helper_thread(int (*proc)(void *), void *arg,
|
||||||
unsigned int flags, unsigned long *stack_out);
|
unsigned int flags, unsigned long *stack_out);
|
||||||
extern int helper_wait(int pid);
|
extern int helper_wait(int pid);
|
||||||
|
|
||||||
|
struct os_helper_thread;
|
||||||
|
int os_run_helper_thread(struct os_helper_thread **td_out,
|
||||||
|
void *(*routine)(void *), void *arg);
|
||||||
|
void os_kill_helper_thread(struct os_helper_thread *td);
|
||||||
|
void os_fix_helper_thread_signals(void);
|
||||||
|
|
||||||
/* umid.c */
|
/* umid.c */
|
||||||
extern int umid_file_name(char *name, char *buf, int len);
|
extern int umid_file_name(char *name, char *buf, int len);
|
||||||
|
|
@ -310,7 +314,7 @@ extern void um_irqs_resume(void);
|
||||||
extern int add_sigio_fd(int fd);
|
extern int add_sigio_fd(int fd);
|
||||||
extern int ignore_sigio_fd(int fd);
|
extern int ignore_sigio_fd(int fd);
|
||||||
extern void maybe_sigio_broken(int fd);
|
extern void maybe_sigio_broken(int fd);
|
||||||
extern void sigio_broken(int fd);
|
extern void sigio_broken(void);
|
||||||
/*
|
/*
|
||||||
* unlocked versions for IRQ controller code.
|
* unlocked versions for IRQ controller code.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
#ifndef __SIGIO_H__
|
#ifndef __SIGIO_H__
|
||||||
#define __SIGIO_H__
|
#define __SIGIO_H__
|
||||||
|
|
||||||
extern int write_sigio_irq(int fd);
|
|
||||||
extern void sigio_lock(void);
|
extern void sigio_lock(void);
|
||||||
extern void sigio_unlock(void);
|
extern void sigio_unlock(void);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ extra-y := vmlinux.lds
|
||||||
obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \
|
obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \
|
||||||
physmem.o process.o ptrace.o reboot.o sigio.o \
|
physmem.o process.o ptrace.o reboot.o sigio.o \
|
||||||
signal.o sysrq.o time.o tlb.o trap.o \
|
signal.o sysrq.o time.o tlb.o trap.o \
|
||||||
um_arch.o umid.o maccess.o kmsg_dump.o capflags.o skas/
|
um_arch.o umid.o kmsg_dump.o capflags.o skas/
|
||||||
obj-y += load_file.o
|
obj-y += load_file.o
|
||||||
|
|
||||||
obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
|
obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,8 @@ static void _sigio_handler(struct uml_pt_regs *regs,
|
||||||
free_irqs();
|
free_irqs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
|
void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
|
||||||
|
void *mc)
|
||||||
{
|
{
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
_sigio_handler(regs, irqs_suspended);
|
_sigio_handler(regs, irqs_suspended);
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 Richard Weinberger <richrd@nod.at>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <os.h>
|
|
||||||
|
|
||||||
bool copy_from_kernel_nofault_allowed(const void *src, size_t size)
|
|
||||||
{
|
|
||||||
void *psrc = (void *)rounddown((unsigned long)src, PAGE_SIZE);
|
|
||||||
|
|
||||||
if ((unsigned long)src < PAGE_SIZE || size <= 0)
|
|
||||||
return false;
|
|
||||||
if (os_mincore(psrc, size + src - psrc) <= 0)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/swap.h>
|
#include <linux/swap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <asm/sections.h>
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
#include <asm/pgalloc.h>
|
#include <asm/pgalloc.h>
|
||||||
#include <as-layout.h>
|
#include <as-layout.h>
|
||||||
|
|
@ -66,6 +68,7 @@ void __init arch_mm_preinit(void)
|
||||||
map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0);
|
map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0);
|
||||||
memblock_free((void *)brk_end, uml_reserved - brk_end);
|
memblock_free((void *)brk_end, uml_reserved - brk_end);
|
||||||
uml_reserved = brk_end;
|
uml_reserved = brk_end;
|
||||||
|
min_low_pfn = PFN_UP(__pa(uml_reserved));
|
||||||
max_pfn = max_low_pfn;
|
max_pfn = max_low_pfn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,3 +245,11 @@ static const pgprot_t protection_map[16] = {
|
||||||
[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED
|
[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED
|
||||||
};
|
};
|
||||||
DECLARE_VM_GET_PAGE_PROT
|
DECLARE_VM_GET_PAGE_PROT
|
||||||
|
|
||||||
|
void mark_rodata_ro(void)
|
||||||
|
{
|
||||||
|
unsigned long rodata_start = PFN_ALIGN(__start_rodata);
|
||||||
|
unsigned long rodata_end = PFN_ALIGN(__end_rodata);
|
||||||
|
|
||||||
|
os_protect_memory((void *)rodata_start, rodata_end - rodata_start, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,32 +8,6 @@
|
||||||
#include <os.h>
|
#include <os.h>
|
||||||
#include <sigio.h>
|
#include <sigio.h>
|
||||||
|
|
||||||
/* Protected by sigio_lock() called from write_sigio_workaround */
|
|
||||||
static int sigio_irq_fd = -1;
|
|
||||||
|
|
||||||
static irqreturn_t sigio_interrupt(int irq, void *data)
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
|
|
||||||
os_read_file(sigio_irq_fd, &c, sizeof(c));
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
int write_sigio_irq(int fd)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
|
|
||||||
0, "write sigio", NULL);
|
|
||||||
if (err < 0) {
|
|
||||||
printk(KERN_ERR "write_sigio_irq : um_request_irq failed, "
|
|
||||||
"err = %d\n", err);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sigio_irq_fd = fd;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* These are called from os-Linux/sigio.c to protect its pollfds arrays. */
|
/* These are called from os-Linux/sigio.c to protect its pollfds arrays. */
|
||||||
static DEFINE_MUTEX(sigio_mutex);
|
static DEFINE_MUTEX(sigio_mutex);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,17 @@ void handle_syscall(struct uml_pt_regs *r)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
syscall = UPT_SYSCALL_NR(r);
|
syscall = UPT_SYSCALL_NR(r);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If no time passes, then sched_yield may not actually yield, causing
|
||||||
|
* broken spinlock implementations in userspace (ASAN) to hang for long
|
||||||
|
* periods of time.
|
||||||
|
*/
|
||||||
|
if ((time_travel_mode == TT_MODE_INFCPU ||
|
||||||
|
time_travel_mode == TT_MODE_EXTERNAL) &&
|
||||||
|
syscall == __NR_sched_yield)
|
||||||
|
tt_extra_sched_jiffies += 1;
|
||||||
|
|
||||||
if (syscall >= 0 && syscall < __NR_syscalls) {
|
if (syscall >= 0 && syscall < __NR_syscalls) {
|
||||||
unsigned long ret = EXECUTE_SYSCALL(syscall, regs);
|
unsigned long ret = EXECUTE_SYSCALL(syscall, regs);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <kern_util.h>
|
#include <kern_util.h>
|
||||||
#include <os.h>
|
#include <os.h>
|
||||||
#include <skas.h>
|
#include <skas.h>
|
||||||
|
#include <arch.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note this is constrained to return 0, -EFAULT, -EACCES, -ENOMEM by
|
* Note this is constrained to return 0, -EFAULT, -EACCES, -ENOMEM by
|
||||||
|
|
@ -175,12 +176,14 @@ void fatal_sigsegv(void)
|
||||||
* @sig: the signal number
|
* @sig: the signal number
|
||||||
* @unused_si: the signal info struct; unused in this handler
|
* @unused_si: the signal info struct; unused in this handler
|
||||||
* @regs: the ptrace register information
|
* @regs: the ptrace register information
|
||||||
|
* @mc: the mcontext of the signal
|
||||||
*
|
*
|
||||||
* The handler first extracts the faultinfo from the UML ptrace regs struct.
|
* The handler first extracts the faultinfo from the UML ptrace regs struct.
|
||||||
* If the userfault did not happen in an UML userspace process, bad_segv is called.
|
* If the userfault did not happen in an UML userspace process, bad_segv is called.
|
||||||
* Otherwise the signal did happen in a cloned userspace process, handle it.
|
* Otherwise the signal did happen in a cloned userspace process, handle it.
|
||||||
*/
|
*/
|
||||||
void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
|
void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
|
||||||
|
void *mc)
|
||||||
{
|
{
|
||||||
struct faultinfo * fi = UPT_FAULTINFO(regs);
|
struct faultinfo * fi = UPT_FAULTINFO(regs);
|
||||||
|
|
||||||
|
|
@ -189,7 +192,7 @@ void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
|
||||||
bad_segv(*fi, UPT_IP(regs));
|
bad_segv(*fi, UPT_IP(regs));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs);
|
segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -199,7 +202,7 @@ void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
|
||||||
* give us bad data!
|
* give us bad data!
|
||||||
*/
|
*/
|
||||||
unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
|
unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
|
||||||
struct uml_pt_regs *regs)
|
struct uml_pt_regs *regs, void *mc)
|
||||||
{
|
{
|
||||||
int si_code;
|
int si_code;
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -223,6 +226,19 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
else if (current->mm == NULL) {
|
else if (current->mm == NULL) {
|
||||||
|
if (current->pagefault_disabled) {
|
||||||
|
if (!mc) {
|
||||||
|
show_regs(container_of(regs, struct pt_regs, regs));
|
||||||
|
panic("Segfault with pagefaults disabled but no mcontext");
|
||||||
|
}
|
||||||
|
if (!current->thread.segv_continue) {
|
||||||
|
show_regs(container_of(regs, struct pt_regs, regs));
|
||||||
|
panic("Segfault without recovery target");
|
||||||
|
}
|
||||||
|
mc_set_rip(mc, current->thread.segv_continue);
|
||||||
|
current->thread.segv_continue = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
show_regs(container_of(regs, struct pt_regs, regs));
|
show_regs(container_of(regs, struct pt_regs, regs));
|
||||||
panic("Segfault with no mm");
|
panic("Segfault with no mm");
|
||||||
}
|
}
|
||||||
|
|
@ -274,7 +290,8 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs)
|
void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs,
|
||||||
|
void *mc)
|
||||||
{
|
{
|
||||||
int code, err;
|
int code, err;
|
||||||
if (!UPT_IS_USER(regs)) {
|
if (!UPT_IS_USER(regs)) {
|
||||||
|
|
@ -302,7 +319,8 @@ void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
|
void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
|
||||||
|
void *mc)
|
||||||
{
|
{
|
||||||
do_IRQ(WINCH_IRQ, regs);
|
do_IRQ(WINCH_IRQ, regs);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/panic_notifier.h>
|
#include <linux/panic_notifier.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
#include <linux/string_choices.h>
|
||||||
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/sched/task.h>
|
#include <linux/sched/task.h>
|
||||||
|
|
@ -78,7 +79,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
||||||
seq_printf(m, "model name\t: UML\n");
|
seq_printf(m, "model name\t: UML\n");
|
||||||
seq_printf(m, "mode\t\t: skas\n");
|
seq_printf(m, "mode\t\t: skas\n");
|
||||||
seq_printf(m, "host\t\t: %s\n", host_info);
|
seq_printf(m, "host\t\t: %s\n", host_info);
|
||||||
seq_printf(m, "fpu\t\t: %s\n", cpu_has(&boot_cpu_data, X86_FEATURE_FPU) ? "yes" : "no");
|
seq_printf(m, "fpu\t\t: %s\n", str_yes_no(cpu_has(&boot_cpu_data, X86_FEATURE_FPU)));
|
||||||
seq_printf(m, "flags\t\t:");
|
seq_printf(m, "flags\t\t:");
|
||||||
for (i = 0; i < 32*NCAPINTS; i++)
|
for (i = 0; i < 32*NCAPINTS; i++)
|
||||||
if (cpu_has(&boot_cpu_data, i) && (x86_cap_flags[i] != NULL))
|
if (cpu_has(&boot_cpu_data, i) && (x86_cap_flags[i] != NULL))
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
@ -121,6 +122,10 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
|
||||||
unsigned long stack, sp;
|
unsigned long stack, sp;
|
||||||
int pid, status, err;
|
int pid, status, err;
|
||||||
|
|
||||||
|
/* To share memory space, use os_run_helper_thread() instead. */
|
||||||
|
if (flags & CLONE_VM)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
stack = alloc_stack(0, __uml_cant_sleep());
|
stack = alloc_stack(0, __uml_cant_sleep());
|
||||||
if (stack == 0)
|
if (stack == 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
@ -167,3 +172,65 @@ int helper_wait(int pid)
|
||||||
} else
|
} else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct os_helper_thread {
|
||||||
|
pthread_t handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
int os_run_helper_thread(struct os_helper_thread **td_out,
|
||||||
|
void *(*routine)(void *), void *arg)
|
||||||
|
{
|
||||||
|
struct os_helper_thread *td;
|
||||||
|
sigset_t sigset, oset;
|
||||||
|
int err, flags;
|
||||||
|
|
||||||
|
flags = __uml_cant_sleep() ? UM_GFP_ATOMIC : UM_GFP_KERNEL;
|
||||||
|
td = uml_kmalloc(sizeof(*td), flags);
|
||||||
|
if (!td)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sigfillset(&sigset);
|
||||||
|
if (sigprocmask(SIG_SETMASK, &sigset, &oset) < 0) {
|
||||||
|
err = -errno;
|
||||||
|
kfree(td);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pthread_create(&td->handle, NULL, routine, arg);
|
||||||
|
|
||||||
|
if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0)
|
||||||
|
panic("Failed to restore the signal mask: %d", errno);
|
||||||
|
|
||||||
|
if (err != 0)
|
||||||
|
kfree(td);
|
||||||
|
else
|
||||||
|
*td_out = td;
|
||||||
|
|
||||||
|
return -err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_kill_helper_thread(struct os_helper_thread *td)
|
||||||
|
{
|
||||||
|
pthread_cancel(td->handle);
|
||||||
|
pthread_join(td->handle, NULL);
|
||||||
|
kfree(td);
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_fix_helper_thread_signals(void)
|
||||||
|
{
|
||||||
|
sigset_t sigset;
|
||||||
|
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
|
||||||
|
sigaddset(&sigset, SIGWINCH);
|
||||||
|
sigaddset(&sigset, SIGPIPE);
|
||||||
|
sigaddset(&sigset, SIGPROF);
|
||||||
|
sigaddset(&sigset, SIGINT);
|
||||||
|
sigaddset(&sigset, SIGTERM);
|
||||||
|
sigaddset(&sigset, SIGCHLD);
|
||||||
|
sigaddset(&sigset, SIGALRM);
|
||||||
|
sigaddset(&sigset, SIGIO);
|
||||||
|
sigaddset(&sigset, SIGUSR1);
|
||||||
|
|
||||||
|
pthread_sigmask(SIG_SETMASK, &sigset, NULL);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,57 +142,6 @@ int __init can_drop_memory(void)
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int os_page_mincore(void *addr)
|
|
||||||
{
|
|
||||||
char vec[2];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
|
|
||||||
if (ret < 0) {
|
|
||||||
if (errno == ENOMEM || errno == EINVAL)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
return vec[0] & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int os_mincore(void *addr, unsigned long len)
|
|
||||||
{
|
|
||||||
char *vec;
|
|
||||||
int ret, i;
|
|
||||||
|
|
||||||
if (len <= UM_KERN_PAGE_SIZE)
|
|
||||||
return os_page_mincore(addr);
|
|
||||||
|
|
||||||
vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE);
|
|
||||||
if (!vec)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
|
|
||||||
if (ret < 0) {
|
|
||||||
if (errno == ENOMEM || errno == EINVAL)
|
|
||||||
ret = 0;
|
|
||||||
else
|
|
||||||
ret = -errno;
|
|
||||||
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) {
|
|
||||||
if (!(vec[i] & 1)) {
|
|
||||||
ret = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 1;
|
|
||||||
out:
|
|
||||||
free(vec);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_new_thread_signals(void)
|
void init_new_thread_signals(void)
|
||||||
{
|
{
|
||||||
set_handler(SIGSEGV);
|
set_handler(SIGSEGV);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
#include <kern_util.h>
|
#include <kern_util.h>
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
#include <os.h>
|
#include <os.h>
|
||||||
|
|
@ -21,184 +22,51 @@
|
||||||
* Protected by sigio_lock(), also used by sigio_cleanup, which is an
|
* Protected by sigio_lock(), also used by sigio_cleanup, which is an
|
||||||
* exitcall.
|
* exitcall.
|
||||||
*/
|
*/
|
||||||
static int write_sigio_pid = -1;
|
static struct os_helper_thread *write_sigio_td;
|
||||||
static unsigned long write_sigio_stack;
|
|
||||||
|
|
||||||
/*
|
static int epollfd = -1;
|
||||||
* These arrays are initialized before the sigio thread is started, and
|
|
||||||
* the descriptors closed after it is killed. So, it can't see them change.
|
|
||||||
* On the UML side, they are changed under the sigio_lock.
|
|
||||||
*/
|
|
||||||
#define SIGIO_FDS_INIT {-1, -1}
|
|
||||||
|
|
||||||
static int write_sigio_fds[2] = SIGIO_FDS_INIT;
|
#define MAX_EPOLL_EVENTS 64
|
||||||
static int sigio_private[2] = SIGIO_FDS_INIT;
|
|
||||||
|
|
||||||
struct pollfds {
|
static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
|
||||||
struct pollfd *poll;
|
|
||||||
int size;
|
|
||||||
int used;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
static void *write_sigio_thread(void *unused)
|
||||||
* Protected by sigio_lock(). Used by the sigio thread, but the UML thread
|
|
||||||
* synchronizes with it.
|
|
||||||
*/
|
|
||||||
static struct pollfds current_poll;
|
|
||||||
static struct pollfds next_poll;
|
|
||||||
static struct pollfds all_sigio_fds;
|
|
||||||
|
|
||||||
static int write_sigio_thread(void *unused)
|
|
||||||
{
|
{
|
||||||
struct pollfds *fds, tmp;
|
int pid = getpid();
|
||||||
struct pollfd *p;
|
int r;
|
||||||
int i, n, respond_fd;
|
|
||||||
char c;
|
os_fix_helper_thread_signals();
|
||||||
|
|
||||||
os_set_pdeathsig();
|
|
||||||
os_fix_helper_signals();
|
|
||||||
fds = ¤t_poll;
|
|
||||||
while (1) {
|
while (1) {
|
||||||
n = poll(fds->poll, fds->used, -1);
|
r = epoll_wait(epollfd, epoll_events, MAX_EPOLL_EVENTS, -1);
|
||||||
if (n < 0) {
|
if (r < 0) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
printk(UM_KERN_ERR "write_sigio_thread : poll returned "
|
printk(UM_KERN_ERR "%s: epoll_wait failed, errno = %d\n",
|
||||||
"%d, errno = %d\n", n, errno);
|
__func__, errno);
|
||||||
}
|
}
|
||||||
for (i = 0; i < fds->used; i++) {
|
|
||||||
p = &fds->poll[i];
|
|
||||||
if (p->revents == 0)
|
|
||||||
continue;
|
|
||||||
if (p->fd == sigio_private[1]) {
|
|
||||||
CATCH_EINTR(n = read(sigio_private[1], &c,
|
|
||||||
sizeof(c)));
|
|
||||||
if (n != sizeof(c))
|
|
||||||
printk(UM_KERN_ERR
|
|
||||||
"write_sigio_thread : "
|
|
||||||
"read on socket failed, "
|
|
||||||
"err = %d\n", errno);
|
|
||||||
tmp = current_poll;
|
|
||||||
current_poll = next_poll;
|
|
||||||
next_poll = tmp;
|
|
||||||
respond_fd = sigio_private[1];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
respond_fd = write_sigio_fds[1];
|
|
||||||
fds->used--;
|
|
||||||
memmove(&fds->poll[i], &fds->poll[i + 1],
|
|
||||||
(fds->used - i) * sizeof(*fds->poll));
|
|
||||||
}
|
|
||||||
|
|
||||||
CATCH_EINTR(n = write(respond_fd, &c, sizeof(c)));
|
CATCH_EINTR(r = tgkill(pid, pid, SIGIO));
|
||||||
if (n != sizeof(c))
|
if (r < 0)
|
||||||
printk(UM_KERN_ERR "write_sigio_thread : "
|
printk(UM_KERN_ERR "%s: tgkill failed, errno = %d\n",
|
||||||
"write on socket failed, err = %d\n",
|
__func__, errno);
|
||||||
errno);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
static int need_poll(struct pollfds *polls, int n)
|
|
||||||
{
|
|
||||||
struct pollfd *new;
|
|
||||||
|
|
||||||
if (n <= polls->size)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
new = uml_kmalloc(n * sizeof(struct pollfd), UM_GFP_ATOMIC);
|
|
||||||
if (new == NULL) {
|
|
||||||
printk(UM_KERN_ERR "need_poll : failed to allocate new "
|
|
||||||
"pollfds\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(new, polls->poll, polls->used * sizeof(struct pollfd));
|
|
||||||
kfree(polls->poll);
|
|
||||||
|
|
||||||
polls->poll = new;
|
|
||||||
polls->size = n;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Must be called with sigio_lock held, because it's needed by the marked
|
|
||||||
* critical section.
|
|
||||||
*/
|
|
||||||
static void update_thread(void)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
int n;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
flags = um_set_signals_trace(0);
|
|
||||||
CATCH_EINTR(n = write(sigio_private[0], &c, sizeof(c)));
|
|
||||||
if (n != sizeof(c)) {
|
|
||||||
printk(UM_KERN_ERR "update_thread : write failed, err = %d\n",
|
|
||||||
errno);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
CATCH_EINTR(n = read(sigio_private[0], &c, sizeof(c)));
|
|
||||||
if (n != sizeof(c)) {
|
|
||||||
printk(UM_KERN_ERR "update_thread : read failed, err = %d\n",
|
|
||||||
errno);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
um_set_signals_trace(flags);
|
|
||||||
return;
|
|
||||||
fail:
|
|
||||||
/* Critical section start */
|
|
||||||
if (write_sigio_pid != -1) {
|
|
||||||
os_kill_process(write_sigio_pid, 1);
|
|
||||||
free_stack(write_sigio_stack, 0);
|
|
||||||
}
|
|
||||||
write_sigio_pid = -1;
|
|
||||||
close(sigio_private[0]);
|
|
||||||
close(sigio_private[1]);
|
|
||||||
close(write_sigio_fds[0]);
|
|
||||||
close(write_sigio_fds[1]);
|
|
||||||
/* Critical section end */
|
|
||||||
um_set_signals_trace(flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int __add_sigio_fd(int fd)
|
int __add_sigio_fd(int fd)
|
||||||
{
|
{
|
||||||
struct pollfd *p;
|
struct epoll_event event = {
|
||||||
int err, i, n;
|
.data.fd = fd,
|
||||||
|
.events = EPOLLIN | EPOLLET,
|
||||||
|
};
|
||||||
|
int r;
|
||||||
|
|
||||||
for (i = 0; i < all_sigio_fds.used; i++) {
|
CATCH_EINTR(r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event));
|
||||||
if (all_sigio_fds.poll[i].fd == fd)
|
return r < 0 ? -errno : 0;
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == all_sigio_fds.used)
|
|
||||||
return -ENOSPC;
|
|
||||||
|
|
||||||
p = &all_sigio_fds.poll[i];
|
|
||||||
|
|
||||||
for (i = 0; i < current_poll.used; i++) {
|
|
||||||
if (current_poll.poll[i].fd == fd)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = current_poll.used;
|
|
||||||
err = need_poll(&next_poll, n + 1);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
memcpy(next_poll.poll, current_poll.poll,
|
|
||||||
current_poll.used * sizeof(struct pollfd));
|
|
||||||
next_poll.poll[n] = *p;
|
|
||||||
next_poll.used = n + 1;
|
|
||||||
update_thread();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int add_sigio_fd(int fd)
|
int add_sigio_fd(int fd)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -212,38 +80,11 @@ int add_sigio_fd(int fd)
|
||||||
|
|
||||||
int __ignore_sigio_fd(int fd)
|
int __ignore_sigio_fd(int fd)
|
||||||
{
|
{
|
||||||
struct pollfd *p;
|
struct epoll_event event;
|
||||||
int err, i, n = 0;
|
int r;
|
||||||
|
|
||||||
/*
|
CATCH_EINTR(r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event));
|
||||||
* This is called from exitcalls elsewhere in UML - if
|
return r < 0 ? -errno : 0;
|
||||||
* sigio_cleanup has already run, then update_thread will hang
|
|
||||||
* or fail because the thread is no longer running.
|
|
||||||
*/
|
|
||||||
if (write_sigio_pid == -1)
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
for (i = 0; i < current_poll.used; i++) {
|
|
||||||
if (current_poll.poll[i].fd == fd)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == current_poll.used)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
err = need_poll(&next_poll, current_poll.used - 1);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
for (i = 0; i < current_poll.used; i++) {
|
|
||||||
p = ¤t_poll.poll[i];
|
|
||||||
if (p->fd != fd)
|
|
||||||
next_poll.poll[n++] = *p;
|
|
||||||
}
|
|
||||||
next_poll.used = current_poll.used - 1;
|
|
||||||
|
|
||||||
update_thread();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ignore_sigio_fd(int fd)
|
int ignore_sigio_fd(int fd)
|
||||||
|
|
@ -257,127 +98,39 @@ int ignore_sigio_fd(int fd)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pollfd *setup_initial_poll(int fd)
|
|
||||||
{
|
|
||||||
struct pollfd *p;
|
|
||||||
|
|
||||||
p = uml_kmalloc(sizeof(struct pollfd), UM_GFP_KERNEL);
|
|
||||||
if (p == NULL) {
|
|
||||||
printk(UM_KERN_ERR "setup_initial_poll : failed to allocate "
|
|
||||||
"poll\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
*p = ((struct pollfd) { .fd = fd,
|
|
||||||
.events = POLLIN,
|
|
||||||
.revents = 0 });
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void write_sigio_workaround(void)
|
static void write_sigio_workaround(void)
|
||||||
{
|
|
||||||
struct pollfd *p;
|
|
||||||
int err;
|
|
||||||
int l_write_sigio_fds[2];
|
|
||||||
int l_sigio_private[2];
|
|
||||||
int l_write_sigio_pid;
|
|
||||||
|
|
||||||
/* We call this *tons* of times - and most ones we must just fail. */
|
|
||||||
sigio_lock();
|
|
||||||
l_write_sigio_pid = write_sigio_pid;
|
|
||||||
sigio_unlock();
|
|
||||||
|
|
||||||
if (l_write_sigio_pid != -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
err = os_pipe(l_write_sigio_fds, 1, 1);
|
|
||||||
if (err < 0) {
|
|
||||||
printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 1 failed, "
|
|
||||||
"err = %d\n", -err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
err = os_pipe(l_sigio_private, 1, 1);
|
|
||||||
if (err < 0) {
|
|
||||||
printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 2 failed, "
|
|
||||||
"err = %d\n", -err);
|
|
||||||
goto out_close1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = setup_initial_poll(l_sigio_private[1]);
|
|
||||||
if (!p)
|
|
||||||
goto out_close2;
|
|
||||||
|
|
||||||
sigio_lock();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Did we race? Don't try to optimize this, please, it's not so likely
|
|
||||||
* to happen, and no more than once at the boot.
|
|
||||||
*/
|
|
||||||
if (write_sigio_pid != -1)
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
current_poll = ((struct pollfds) { .poll = p,
|
|
||||||
.used = 1,
|
|
||||||
.size = 1 });
|
|
||||||
|
|
||||||
if (write_sigio_irq(l_write_sigio_fds[0]))
|
|
||||||
goto out_clear_poll;
|
|
||||||
|
|
||||||
memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds));
|
|
||||||
memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private));
|
|
||||||
|
|
||||||
write_sigio_pid = run_helper_thread(write_sigio_thread, NULL,
|
|
||||||
CLONE_FILES | CLONE_VM,
|
|
||||||
&write_sigio_stack);
|
|
||||||
|
|
||||||
if (write_sigio_pid < 0)
|
|
||||||
goto out_clear;
|
|
||||||
|
|
||||||
sigio_unlock();
|
|
||||||
return;
|
|
||||||
|
|
||||||
out_clear:
|
|
||||||
write_sigio_pid = -1;
|
|
||||||
write_sigio_fds[0] = -1;
|
|
||||||
write_sigio_fds[1] = -1;
|
|
||||||
sigio_private[0] = -1;
|
|
||||||
sigio_private[1] = -1;
|
|
||||||
out_clear_poll:
|
|
||||||
current_poll = ((struct pollfds) { .poll = NULL,
|
|
||||||
.size = 0,
|
|
||||||
.used = 0 });
|
|
||||||
out_free:
|
|
||||||
sigio_unlock();
|
|
||||||
kfree(p);
|
|
||||||
out_close2:
|
|
||||||
close(l_sigio_private[0]);
|
|
||||||
close(l_sigio_private[1]);
|
|
||||||
out_close1:
|
|
||||||
close(l_write_sigio_fds[0]);
|
|
||||||
close(l_write_sigio_fds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sigio_broken(int fd)
|
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
write_sigio_workaround();
|
|
||||||
|
|
||||||
sigio_lock();
|
sigio_lock();
|
||||||
err = need_poll(&all_sigio_fds, all_sigio_fds.used + 1);
|
if (write_sigio_td)
|
||||||
if (err) {
|
goto out;
|
||||||
printk(UM_KERN_ERR "maybe_sigio_broken - failed to add pollfd "
|
|
||||||
"for descriptor %d\n", fd);
|
epollfd = epoll_create(MAX_EPOLL_EVENTS);
|
||||||
|
if (epollfd < 0) {
|
||||||
|
printk(UM_KERN_ERR "%s: epoll_create failed, errno = %d\n",
|
||||||
|
__func__, errno);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os_run_helper_thread(&write_sigio_td, write_sigio_thread, NULL);
|
||||||
|
if (err < 0) {
|
||||||
|
printk(UM_KERN_ERR "%s: os_run_helper_thread failed, errno = %d\n",
|
||||||
|
__func__, -err);
|
||||||
|
close(epollfd);
|
||||||
|
epollfd = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
all_sigio_fds.poll[all_sigio_fds.used++] =
|
|
||||||
((struct pollfd) { .fd = fd,
|
|
||||||
.events = POLLIN,
|
|
||||||
.revents = 0 });
|
|
||||||
out:
|
out:
|
||||||
sigio_unlock();
|
sigio_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sigio_broken(void)
|
||||||
|
{
|
||||||
|
write_sigio_workaround();
|
||||||
|
}
|
||||||
|
|
||||||
/* Changed during early boot */
|
/* Changed during early boot */
|
||||||
static int pty_output_sigio;
|
static int pty_output_sigio;
|
||||||
|
|
||||||
|
|
@ -389,17 +142,16 @@ void maybe_sigio_broken(int fd)
|
||||||
if (pty_output_sigio)
|
if (pty_output_sigio)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sigio_broken(fd);
|
sigio_broken();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sigio_cleanup(void)
|
static void sigio_cleanup(void)
|
||||||
{
|
{
|
||||||
if (write_sigio_pid == -1)
|
if (!write_sigio_td)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
os_kill_process(write_sigio_pid, 1);
|
os_kill_helper_thread(write_sigio_td);
|
||||||
free_stack(write_sigio_stack, 0);
|
write_sigio_td = NULL;
|
||||||
write_sigio_pid = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__uml_exitcall(sigio_cleanup);
|
__uml_exitcall(sigio_cleanup);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
#include <timetravel.h>
|
#include <timetravel.h>
|
||||||
|
|
||||||
void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
|
void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *, void *mc) = {
|
||||||
[SIGTRAP] = relay_signal,
|
[SIGTRAP] = relay_signal,
|
||||||
[SIGFPE] = relay_signal,
|
[SIGFPE] = relay_signal,
|
||||||
[SIGILL] = relay_signal,
|
[SIGILL] = relay_signal,
|
||||||
|
|
@ -47,7 +47,7 @@ static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc)
|
||||||
if ((sig != SIGIO) && (sig != SIGWINCH))
|
if ((sig != SIGIO) && (sig != SIGWINCH))
|
||||||
unblock_signals_trace();
|
unblock_signals_trace();
|
||||||
|
|
||||||
(*sig_info[sig])(sig, si, &r);
|
(*sig_info[sig])(sig, si, &r, mc);
|
||||||
|
|
||||||
errno = save_errno;
|
errno = save_errno;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ static void get_skas_faultinfo(int pid, struct faultinfo *fi)
|
||||||
static void handle_segv(int pid, struct uml_pt_regs *regs)
|
static void handle_segv(int pid, struct uml_pt_regs *regs)
|
||||||
{
|
{
|
||||||
get_skas_faultinfo(pid, ®s->faultinfo);
|
get_skas_faultinfo(pid, ®s->faultinfo);
|
||||||
segv(regs->faultinfo, 0, 1, NULL);
|
segv(regs->faultinfo, 0, 1, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_trap(int pid, struct uml_pt_regs *regs)
|
static void handle_trap(int pid, struct uml_pt_regs *regs)
|
||||||
|
|
@ -525,7 +525,7 @@ void userspace(struct uml_pt_regs *regs)
|
||||||
get_skas_faultinfo(pid,
|
get_skas_faultinfo(pid,
|
||||||
®s->faultinfo);
|
®s->faultinfo);
|
||||||
(*sig_info[SIGSEGV])(SIGSEGV, (struct siginfo *)&si,
|
(*sig_info[SIGSEGV])(SIGSEGV, (struct siginfo *)&si,
|
||||||
regs);
|
regs, NULL);
|
||||||
}
|
}
|
||||||
else handle_segv(pid, regs);
|
else handle_segv(pid, regs);
|
||||||
break;
|
break;
|
||||||
|
|
@ -533,7 +533,7 @@ void userspace(struct uml_pt_regs *regs)
|
||||||
handle_trap(pid, regs);
|
handle_trap(pid, regs);
|
||||||
break;
|
break;
|
||||||
case SIGTRAP:
|
case SIGTRAP:
|
||||||
relay_signal(SIGTRAP, (struct siginfo *)&si, regs);
|
relay_signal(SIGTRAP, (struct siginfo *)&si, regs, NULL);
|
||||||
break;
|
break;
|
||||||
case SIGALRM:
|
case SIGALRM:
|
||||||
break;
|
break;
|
||||||
|
|
@ -543,7 +543,7 @@ void userspace(struct uml_pt_regs *regs)
|
||||||
case SIGFPE:
|
case SIGFPE:
|
||||||
case SIGWINCH:
|
case SIGWINCH:
|
||||||
block_signals_trace();
|
block_signals_trace();
|
||||||
(*sig_info[sig])(sig, (struct siginfo *)&si, regs);
|
(*sig_info[sig])(sig, (struct siginfo *)&si, regs, NULL);
|
||||||
unblock_signals_trace();
|
unblock_signals_trace();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,13 @@ core-y += arch/x86/crypto/
|
||||||
# GCC versions < 11. See:
|
# GCC versions < 11. See:
|
||||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99652
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99652
|
||||||
#
|
#
|
||||||
ifeq ($(CONFIG_CC_IS_CLANG),y)
|
ifeq ($(call gcc-min-version, 110000)$(CONFIG_CC_IS_CLANG),y)
|
||||||
KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx
|
KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx
|
||||||
KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json
|
|
||||||
KBUILD_RUSTFLAGS += -Ctarget-feature=-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2
|
KBUILD_RUSTFLAGS += -Ctarget-feature=-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json
|
||||||
|
|
||||||
ifeq ($(CONFIG_X86_32),y)
|
ifeq ($(CONFIG_X86_32),y)
|
||||||
START := 0x8048000
|
START := 0x8048000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_X86_32
|
#ifdef CONFIG_X86_32
|
||||||
|
|
||||||
#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)
|
#define mb() alternative("lock addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)
|
||||||
#define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2)
|
#define rmb() alternative("lock addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2)
|
||||||
#define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM)
|
#define wmb() alternative("lock addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM)
|
||||||
|
|
||||||
#else /* CONFIG_X86_32 */
|
#else /* CONFIG_X86_32 */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
#ifndef __UM_MODULE_H
|
|
||||||
#define __UM_MODULE_H
|
|
||||||
|
|
||||||
/* UML is simple */
|
|
||||||
struct mod_arch_specific
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_32
|
|
||||||
|
|
||||||
#define Elf_Shdr Elf32_Shdr
|
|
||||||
#define Elf_Sym Elf32_Sym
|
|
||||||
#define Elf_Ehdr Elf32_Ehdr
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define Elf_Shdr Elf64_Shdr
|
|
||||||
#define Elf_Sym Elf64_Sym
|
|
||||||
#define Elf_Ehdr Elf64_Ehdr
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <sysdep/ptrace.h>
|
#include <sysdep/ptrace.h>
|
||||||
#include <sysdep/mcontext.h>
|
#include <sysdep/mcontext.h>
|
||||||
|
#include <arch.h>
|
||||||
|
|
||||||
void get_regs_from_mc(struct uml_pt_regs *regs, mcontext_t *mc)
|
void get_regs_from_mc(struct uml_pt_regs *regs, mcontext_t *mc)
|
||||||
{
|
{
|
||||||
|
|
@ -27,7 +28,17 @@ void get_regs_from_mc(struct uml_pt_regs *regs, mcontext_t *mc)
|
||||||
COPY(RIP);
|
COPY(RIP);
|
||||||
COPY2(EFLAGS, EFL);
|
COPY2(EFLAGS, EFL);
|
||||||
COPY2(CS, CSGSFS);
|
COPY2(CS, CSGSFS);
|
||||||
regs->gp[CS / sizeof(unsigned long)] &= 0xffff;
|
regs->gp[SS / sizeof(unsigned long)] = mc->gregs[REG_CSGSFS] >> 48;
|
||||||
regs->gp[CS / sizeof(unsigned long)] |= 3;
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void mc_set_rip(void *_mc, void *target)
|
||||||
|
{
|
||||||
|
mcontext_t *mc = _mc;
|
||||||
|
|
||||||
|
#ifdef __i386__
|
||||||
|
mc->gregs[REG_EIP] = (unsigned long)target;
|
||||||
|
#else
|
||||||
|
mc->gregs[REG_RIP] = (unsigned long)target;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,4 +29,16 @@ struct faultinfo {
|
||||||
|
|
||||||
#define PTRACE_FULL_FAULTINFO 0
|
#define PTRACE_FULL_FAULTINFO 0
|
||||||
|
|
||||||
|
#define ___backtrack_faulted(_faulted) \
|
||||||
|
asm volatile ( \
|
||||||
|
"mov $0, %0\n" \
|
||||||
|
"movl $__get_kernel_nofault_faulted_%=,%1\n" \
|
||||||
|
"jmp _end_%=\n" \
|
||||||
|
"__get_kernel_nofault_faulted_%=:\n" \
|
||||||
|
"mov $1, %0;" \
|
||||||
|
"_end_%=:" \
|
||||||
|
: "=r" (_faulted), \
|
||||||
|
"=m" (current->thread.segv_continue) :: \
|
||||||
|
)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -29,4 +29,16 @@ struct faultinfo {
|
||||||
|
|
||||||
#define PTRACE_FULL_FAULTINFO 1
|
#define PTRACE_FULL_FAULTINFO 1
|
||||||
|
|
||||||
|
#define ___backtrack_faulted(_faulted) \
|
||||||
|
asm volatile ( \
|
||||||
|
"mov $0, %0\n" \
|
||||||
|
"movq $__get_kernel_nofault_faulted_%=,%1\n" \
|
||||||
|
"jmp _end_%=\n" \
|
||||||
|
"__get_kernel_nofault_faulted_%=:\n" \
|
||||||
|
"mov $1, %0;" \
|
||||||
|
"_end_%=:" \
|
||||||
|
: "=r" (_faulted), \
|
||||||
|
"=m" (current->thread.segv_continue) :: \
|
||||||
|
)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -12,33 +12,22 @@
|
||||||
|
|
||||||
static unsigned int __read_mostly vdso_enabled = 1;
|
static unsigned int __read_mostly vdso_enabled = 1;
|
||||||
unsigned long um_vdso_addr;
|
unsigned long um_vdso_addr;
|
||||||
|
static struct page *um_vdso;
|
||||||
|
|
||||||
extern unsigned long task_size;
|
extern unsigned long task_size;
|
||||||
extern char vdso_start[], vdso_end[];
|
extern char vdso_start[], vdso_end[];
|
||||||
|
|
||||||
static struct page **vdsop;
|
|
||||||
|
|
||||||
static int __init init_vdso(void)
|
static int __init init_vdso(void)
|
||||||
{
|
{
|
||||||
struct page *um_vdso;
|
|
||||||
|
|
||||||
BUG_ON(vdso_end - vdso_start > PAGE_SIZE);
|
BUG_ON(vdso_end - vdso_start > PAGE_SIZE);
|
||||||
|
|
||||||
um_vdso_addr = task_size - PAGE_SIZE;
|
um_vdso_addr = task_size - PAGE_SIZE;
|
||||||
|
|
||||||
vdsop = kmalloc(sizeof(struct page *), GFP_KERNEL);
|
|
||||||
if (!vdsop)
|
|
||||||
goto oom;
|
|
||||||
|
|
||||||
um_vdso = alloc_page(GFP_KERNEL);
|
um_vdso = alloc_page(GFP_KERNEL);
|
||||||
if (!um_vdso) {
|
if (!um_vdso)
|
||||||
kfree(vdsop);
|
|
||||||
|
|
||||||
goto oom;
|
goto oom;
|
||||||
}
|
|
||||||
|
|
||||||
copy_page(page_address(um_vdso), vdso_start);
|
copy_page(page_address(um_vdso), vdso_start);
|
||||||
*vdsop = um_vdso;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -56,6 +45,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
||||||
struct mm_struct *mm = current->mm;
|
struct mm_struct *mm = current->mm;
|
||||||
static struct vm_special_mapping vdso_mapping = {
|
static struct vm_special_mapping vdso_mapping = {
|
||||||
.name = "[vdso]",
|
.name = "[vdso]",
|
||||||
|
.pages = &um_vdso,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!vdso_enabled)
|
if (!vdso_enabled)
|
||||||
|
|
@ -64,7 +54,6 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
||||||
if (mmap_write_lock_killable(mm))
|
if (mmap_write_lock_killable(mm))
|
||||||
return -EINTR;
|
return -EINTR;
|
||||||
|
|
||||||
vdso_mapping.pages = vdsop;
|
|
||||||
vma = _install_special_mapping(mm, um_vdso_addr, PAGE_SIZE,
|
vma = _install_special_mapping(mm, um_vdso_addr, PAGE_SIZE,
|
||||||
VM_READ|VM_EXEC|
|
VM_READ|VM_EXEC|
|
||||||
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
|
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ struct hostfs_stat {
|
||||||
unsigned int uid;
|
unsigned int uid;
|
||||||
unsigned int gid;
|
unsigned int gid;
|
||||||
unsigned long long size;
|
unsigned long long size;
|
||||||
struct hostfs_timespec atime, mtime, ctime;
|
struct hostfs_timespec atime, mtime, ctime, btime;
|
||||||
unsigned int blksize;
|
unsigned int blksize;
|
||||||
unsigned long long blocks;
|
unsigned long long blocks;
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ struct hostfs_inode_info {
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
struct mutex open_mutex;
|
struct mutex open_mutex;
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
|
struct hostfs_timespec btime;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
|
static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
|
||||||
|
|
@ -547,6 +548,7 @@ static int hostfs_inode_set(struct inode *ino, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
HOSTFS_I(ino)->dev = dev;
|
HOSTFS_I(ino)->dev = dev;
|
||||||
|
HOSTFS_I(ino)->btime = st->btime;
|
||||||
ino->i_ino = st->ino;
|
ino->i_ino = st->ino;
|
||||||
ino->i_mode = st->mode;
|
ino->i_mode = st->mode;
|
||||||
return hostfs_inode_update(ino, st);
|
return hostfs_inode_update(ino, st);
|
||||||
|
|
@ -557,7 +559,10 @@ static int hostfs_inode_test(struct inode *inode, void *data)
|
||||||
const struct hostfs_stat *st = data;
|
const struct hostfs_stat *st = data;
|
||||||
dev_t dev = MKDEV(st->dev.maj, st->dev.min);
|
dev_t dev = MKDEV(st->dev.maj, st->dev.min);
|
||||||
|
|
||||||
return inode->i_ino == st->ino && HOSTFS_I(inode)->dev == dev;
|
return inode->i_ino == st->ino && HOSTFS_I(inode)->dev == dev &&
|
||||||
|
(inode->i_mode & S_IFMT) == (st->mode & S_IFMT) &&
|
||||||
|
HOSTFS_I(inode)->btime.tv_sec == st->btime.tv_sec &&
|
||||||
|
HOSTFS_I(inode)->btime.tv_nsec == st->btime.tv_nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct inode *hostfs_iget(struct super_block *sb, char *name)
|
static struct inode *hostfs_iget(struct super_block *sb, char *name)
|
||||||
|
|
|
||||||
|
|
@ -18,39 +18,48 @@
|
||||||
#include "hostfs.h"
|
#include "hostfs.h"
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
|
|
||||||
static void stat64_to_hostfs(const struct stat64 *buf, struct hostfs_stat *p)
|
static void statx_to_hostfs(const struct statx *buf, struct hostfs_stat *p)
|
||||||
{
|
{
|
||||||
p->ino = buf->st_ino;
|
p->ino = buf->stx_ino;
|
||||||
p->mode = buf->st_mode;
|
p->mode = buf->stx_mode;
|
||||||
p->nlink = buf->st_nlink;
|
p->nlink = buf->stx_nlink;
|
||||||
p->uid = buf->st_uid;
|
p->uid = buf->stx_uid;
|
||||||
p->gid = buf->st_gid;
|
p->gid = buf->stx_gid;
|
||||||
p->size = buf->st_size;
|
p->size = buf->stx_size;
|
||||||
p->atime.tv_sec = buf->st_atime;
|
p->atime.tv_sec = buf->stx_atime.tv_sec;
|
||||||
p->atime.tv_nsec = 0;
|
p->atime.tv_nsec = buf->stx_atime.tv_nsec;
|
||||||
p->ctime.tv_sec = buf->st_ctime;
|
p->ctime.tv_sec = buf->stx_ctime.tv_sec;
|
||||||
p->ctime.tv_nsec = 0;
|
p->ctime.tv_nsec = buf->stx_ctime.tv_nsec;
|
||||||
p->mtime.tv_sec = buf->st_mtime;
|
p->mtime.tv_sec = buf->stx_mtime.tv_sec;
|
||||||
p->mtime.tv_nsec = 0;
|
p->mtime.tv_nsec = buf->stx_mtime.tv_nsec;
|
||||||
p->blksize = buf->st_blksize;
|
if (buf->stx_mask & STATX_BTIME) {
|
||||||
p->blocks = buf->st_blocks;
|
p->btime.tv_sec = buf->stx_btime.tv_sec;
|
||||||
p->rdev.maj = os_major(buf->st_rdev);
|
p->btime.tv_nsec = buf->stx_btime.tv_nsec;
|
||||||
p->rdev.min = os_minor(buf->st_rdev);
|
} else {
|
||||||
p->dev.maj = os_major(buf->st_dev);
|
memset(&p->btime, 0, sizeof(p->btime));
|
||||||
p->dev.min = os_minor(buf->st_dev);
|
}
|
||||||
|
p->blksize = buf->stx_blksize;
|
||||||
|
p->blocks = buf->stx_blocks;
|
||||||
|
p->rdev.maj = buf->stx_rdev_major;
|
||||||
|
p->rdev.min = buf->stx_rdev_minor;
|
||||||
|
p->dev.maj = buf->stx_dev_major;
|
||||||
|
p->dev.min = buf->stx_dev_minor;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stat_file(const char *path, struct hostfs_stat *p, int fd)
|
int stat_file(const char *path, struct hostfs_stat *p, int fd)
|
||||||
{
|
{
|
||||||
struct stat64 buf;
|
struct statx buf;
|
||||||
|
int flags = AT_SYMLINK_NOFOLLOW;
|
||||||
|
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
if (fstat64(fd, &buf) < 0)
|
flags |= AT_EMPTY_PATH;
|
||||||
return -errno;
|
path = "";
|
||||||
} else if (lstat64(path, &buf) < 0) {
|
|
||||||
return -errno;
|
|
||||||
}
|
}
|
||||||
stat64_to_hostfs(&buf, p);
|
|
||||||
|
if ((statx(fd, path, flags, STATX_BASIC_STATS | STATX_BTIME, &buf)) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
statx_to_hostfs(&buf, p);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue