Skip to content

Commit

Permalink
arm/ioreq: Introduce arch specific bits for IOREQ/DM features
Browse files Browse the repository at this point in the history
This patch adds basic IOREQ/DM support on Arm. The subsequent
patches will improve functionality and add remaining bits.

The IOREQ/DM features are supposed to be built with IOREQ_SERVER
option enabled, which is disabled by default on Arm for now.

Please note, the "PIO handling" TODO is expected to left unaddressed
for the current series. It is not an big issue for now while Xen
doesn't have support for vPCI on Arm. On Arm64 they are only used
for PCI IO Bar and we would probably want to expose them to emulator
as PIO access to make a DM completely arch-agnostic. So "PIO handling"
should be implemented when we add support for vPCI.

Signed-off-by: Julien Grall <julien.grall@arm.com>
Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
[On Arm only]
Tested-by: Wei Chen <Wei.Chen@arm.com>
  • Loading branch information
Julien Grall authored and Julien Grall committed Jan 29, 2021
1 parent 94aeaaa commit cb9953d
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 1 deletion.
2 changes: 2 additions & 0 deletions xen/arch/arm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ obj-y += cpuerrata.o
obj-y += cpufeature.o
obj-y += decode.o
obj-y += device.o
obj-$(CONFIG_IOREQ_SERVER) += dm.o
obj-y += domain.o
obj-y += domain_build.init.o
obj-y += domctl.o
Expand All @@ -27,6 +28,7 @@ obj-y += guest_atomics.o
obj-y += guest_walk.o
obj-y += hvm.o
obj-y += io.o
obj-$(CONFIG_IOREQ_SERVER) += ioreq.o
obj-y += irq.o
obj-y += kernel.init.o
obj-$(CONFIG_LIVEPATCH) += livepatch.o
Expand Down
97 changes: 97 additions & 0 deletions xen/arch/arm/dm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2019 Arm ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; If not, see <http://www.gnu.org/licenses/>.
*/

#include <xen/dm.h>
#include <xen/guest_access.h>
#include <xen/hypercall.h>
#include <xen/ioreq.h>
#include <xen/nospec.h>

int dm_op(const struct dmop_args *op_args)
{
struct domain *d;
struct xen_dm_op op;
bool const_op = true;
long rc;
size_t offset;

static const uint8_t op_size[] = {
[XEN_DMOP_create_ioreq_server] = sizeof(struct xen_dm_op_create_ioreq_server),
[XEN_DMOP_get_ioreq_server_info] = sizeof(struct xen_dm_op_get_ioreq_server_info),
[XEN_DMOP_map_io_range_to_ioreq_server] = sizeof(struct xen_dm_op_ioreq_server_range),
[XEN_DMOP_unmap_io_range_from_ioreq_server] = sizeof(struct xen_dm_op_ioreq_server_range),
[XEN_DMOP_set_ioreq_server_state] = sizeof(struct xen_dm_op_set_ioreq_server_state),
[XEN_DMOP_destroy_ioreq_server] = sizeof(struct xen_dm_op_destroy_ioreq_server),
};

rc = rcu_lock_remote_domain_by_id(op_args->domid, &d);
if ( rc )
return rc;

rc = xsm_dm_op(XSM_DM_PRIV, d);
if ( rc )
goto out;

offset = offsetof(struct xen_dm_op, u);

rc = -EFAULT;
if ( op_args->buf[0].size < offset )
goto out;

if ( copy_from_guest_offset((void *)&op, op_args->buf[0].h, 0, offset) )
goto out;

if ( op.op >= ARRAY_SIZE(op_size) )
{
rc = -EOPNOTSUPP;
goto out;
}

op.op = array_index_nospec(op.op, ARRAY_SIZE(op_size));

if ( op_args->buf[0].size < offset + op_size[op.op] )
goto out;

if ( copy_from_guest_offset((void *)&op.u, op_args->buf[0].h, offset,
op_size[op.op]) )
goto out;

rc = -EINVAL;
if ( op.pad )
goto out;

rc = ioreq_server_dm_op(&op, d, &const_op);

if ( (!rc || rc == -ERESTART) &&
!const_op && copy_to_guest_offset(op_args->buf[0].h, offset,
(void *)&op.u, op_size[op.op]) )
rc = -EFAULT;

out:
rcu_unlock_domain(d);

return rc;
}

/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/
9 changes: 9 additions & 0 deletions xen/arch/arm/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <xen/guest_access.h>
#include <xen/hypercall.h>
#include <xen/init.h>
#include <xen/ioreq.h>
#include <xen/lib.h>
#include <xen/livepatch.h>
#include <xen/sched.h>
Expand Down Expand Up @@ -696,6 +697,10 @@ int arch_domain_create(struct domain *d,

ASSERT(config != NULL);

#ifdef CONFIG_IOREQ_SERVER
ioreq_domain_init(d);
#endif

/* p2m_init relies on some value initialized by the IOMMU subsystem */
if ( (rc = iommu_domain_init(d, config->iommu_opts)) != 0 )
goto fail;
Expand Down Expand Up @@ -1009,6 +1014,10 @@ int domain_relinquish_resources(struct domain *d)
*/
domain_vpl011_deinit(d);

#ifdef CONFIG_IOREQ_SERVER
ioreq_server_destroy_all(d);
#endif

PROGRESS(tee):
ret = tee_relinquish_resources(d);
if (ret )
Expand Down
12 changes: 11 additions & 1 deletion xen/arch/arm/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
* GNU General Public License for more details.
*/

#include <xen/ioreq.h>
#include <xen/lib.h>
#include <xen/spinlock.h>
#include <xen/sched.h>
#include <xen/sort.h>
#include <asm/cpuerrata.h>
#include <asm/current.h>
#include <asm/ioreq.h>
#include <asm/mmio.h>

#include "decode.h"
Expand Down Expand Up @@ -123,7 +125,15 @@ enum io_state try_handle_mmio(struct cpu_user_regs *regs,

handler = find_mmio_handler(v->domain, info.gpa);
if ( !handler )
return IO_UNHANDLED;
{
int rc;

rc = try_fwd_ioserv(regs, v, &info);
if ( rc == IO_HANDLED )
return handle_ioserv(regs, v);

return rc;
}

/* All the instructions used on emulated MMIO region should be valid */
if ( !dabt.valid )
Expand Down
211 changes: 211 additions & 0 deletions xen/arch/arm/ioreq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
* arm/ioreq.c: hardware virtual machine I/O emulation
*
* Copyright (c) 2019 Arm ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; If not, see <http://www.gnu.org/licenses/>.
*/

#include <xen/domain.h>
#include <xen/ioreq.h>

#include <asm/traps.h>

#include <public/hvm/ioreq.h>

enum io_state handle_ioserv(struct cpu_user_regs *regs, struct vcpu *v)
{
const union hsr hsr = { .bits = regs->hsr };
const struct hsr_dabt dabt = hsr.dabt;
/* Code is similar to handle_read */
uint8_t size = (1 << dabt.size) * 8;
register_t r = v->io.req.data;

/* We are done with the IO */
v->io.req.state = STATE_IOREQ_NONE;

if ( dabt.write )
return IO_HANDLED;

/*
* Sign extend if required.
* Note that we expect the read handler to have zeroed the bits
* outside the requested access size.
*/
if ( dabt.sign && (r & (1UL << (size - 1))) )
{
/*
* We are relying on register_t using the same as
* an unsigned long in order to keep the 32-bit assembly
* code smaller.
*/
BUILD_BUG_ON(sizeof(register_t) != sizeof(unsigned long));
r |= (~0UL) << size;
}

set_user_reg(regs, dabt.reg, r);

return IO_HANDLED;
}

enum io_state try_fwd_ioserv(struct cpu_user_regs *regs,
struct vcpu *v, mmio_info_t *info)
{
struct vcpu_io *vio = &v->io;
ioreq_t p = {
.type = IOREQ_TYPE_COPY,
.addr = info->gpa,
.size = 1 << info->dabt.size,
.count = 1,
.dir = !info->dabt.write,
/*
* On x86, df is used by 'rep' instruction to tell the direction
* to iterate (forward or backward).
* On Arm, all the accesses to MMIO region will do a single
* memory access. So for now, we can safely always set to 0.
*/
.df = 0,
.data = get_user_reg(regs, info->dabt.reg),
.state = STATE_IOREQ_READY,
};
struct ioreq_server *s = NULL;
enum io_state rc;

if ( vio->req.state != STATE_IOREQ_NONE )
{
gdprintk(XENLOG_ERR, "wrong state %u\n", vio->req.state);
return IO_ABORT;
}

s = ioreq_server_select(v->domain, &p);
if ( !s )
return IO_UNHANDLED;

if ( !info->dabt.valid )
return IO_ABORT;

vio->req = p;

rc = ioreq_send(s, &p, 0);
if ( rc != IO_RETRY || v->domain->is_shutting_down )
vio->req.state = STATE_IOREQ_NONE;
else if ( !ioreq_needs_completion(&vio->req) )
rc = IO_HANDLED;
else
vio->completion = VIO_mmio_completion;

return rc;
}

bool arch_ioreq_complete_mmio(void)
{
struct vcpu *v = current;
struct cpu_user_regs *regs = guest_cpu_user_regs();
const union hsr hsr = { .bits = regs->hsr };

if ( v->io.req.state != STATE_IORESP_READY )
{
ASSERT_UNREACHABLE();
return false;
}

if ( handle_ioserv(regs, v) == IO_HANDLED )
{
advance_pc(regs, hsr);
return true;
}

return false;
}

bool arch_vcpu_ioreq_completion(enum vio_completion completion)
{
ASSERT_UNREACHABLE();
return true;
}

/*
* The "legacy" mechanism of mapping magic pages for the IOREQ servers
* is x86 specific, so the following hooks don't need to be implemented on Arm:
* - arch_ioreq_server_map_pages
* - arch_ioreq_server_unmap_pages
* - arch_ioreq_server_enable
* - arch_ioreq_server_disable
*/
int arch_ioreq_server_map_pages(struct ioreq_server *s)
{
return -EOPNOTSUPP;
}

void arch_ioreq_server_unmap_pages(struct ioreq_server *s)
{
}

void arch_ioreq_server_enable(struct ioreq_server *s)
{
}

void arch_ioreq_server_disable(struct ioreq_server *s)
{
}

void arch_ioreq_server_destroy(struct ioreq_server *s)
{
}

int arch_ioreq_server_map_mem_type(struct domain *d,
struct ioreq_server *s,
uint32_t flags)
{
return -EOPNOTSUPP;
}

void arch_ioreq_server_map_mem_type_completed(struct domain *d,
struct ioreq_server *s,
uint32_t flags)
{
}

bool arch_ioreq_server_destroy_all(struct domain *d)
{
return true;
}

bool arch_ioreq_server_get_type_addr(const struct domain *d,
const ioreq_t *p,
uint8_t *type,
uint64_t *addr)
{
if ( p->type != IOREQ_TYPE_COPY && p->type != IOREQ_TYPE_PIO )
return false;

*type = (p->type == IOREQ_TYPE_PIO) ?
XEN_DMOP_IO_RANGE_PORT : XEN_DMOP_IO_RANGE_MEMORY;
*addr = p->addr;

return true;
}

void arch_ioreq_domain_init(struct domain *d)
{
}

/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

0 comments on commit cb9953d

Please sign in to comment.