mirrored from git://xenbits.xen.org/xen.git
-
Notifications
You must be signed in to change notification settings - Fork 325
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
arm/ioreq: Introduce arch specific bits for IOREQ/DM features
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
Showing
9 changed files
with
409 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: | ||
*/ |
Oops, something went wrong.