From 4e4b14641cd94c0c9fe64606b329cdbbf9c6a92b Mon Sep 17 00:00:00 2001 From: George Dunlap Date: Thu, 22 Sep 2016 12:30:26 +0100 Subject: [PATCH] x86/hvm: Handle both ioreq detach races and read-modify-write instructions Compile-tested only. Signed-off-by: George Dunlap --- xen/arch/x86/hvm/emulate.c | 68 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index 564c117..120ef86 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -214,40 +214,84 @@ static int hvmemul_do_io( break; case X86EMUL_UNHANDLEABLE: { + /* + * Xen isn't emulating the instruction internally, so see if + * there's an ioreq server that can handle it. Rules: + * + * - PIO and "normal" mmio run through + * hvm_select_ioreq_server() to choose the ioreq server by + * range. If no server is found, the access is ignored. + * + * - p2m_ioreq_server accesses are handled by the current + * ioreq_server for the domain, but there are some corner + * cases: + * + * - If the domain ioreq_server is NULL, assume this is a + * race between unlooking the ioreq server and + * p2m_type_change and re-try the instruction. + * + * - If the IOREQ_MEM_ACCESS_WRITE flag is not set, treat it + * like a normal PIO or MMIO that doesn't have an ioreq + * server (i.e., by ignoring it). + * + * - If the accesss is a read, this must be part of a + * read-modify-write instruction; emulate the read so that we have + * it + */ + struct hvm_ioreq_server *s = NULL; p2m_type_t p2mt = p2m_invalid; - + if ( is_mmio ) { unsigned long gmfn = paddr_to_pfn(addr); (void) get_gfn_query_unlocked(currd, gmfn, &p2mt); - if ( p2mt == p2m_ioreq_server && dir == IOREQ_WRITE ) + if ( p2mt == p2m_ioreq_server ) { unsigned int flags; s = p2m_get_ioreq_server(currd, &flags); + + /* + * If p2mt is ioreq_server but ioreq_server is NULL, + * we probably lost a race with p2m_type_change; just + * retry the access. + */ + if ( s == NULL ) + { + rc = X86EMUL_RETRY; + vio->io_req.state = STATE_IOREQ_NONE; + break; + } + + /* + * This is part of a read-modify-write instruction. + * Emulate the read part so we have the value cached. + */ + if ( dir == IOREQ_READ ) + { + rc = hvm_process_io_intercept(&mem_handler, &p); + vio->io_req.state = STATE_IOREQ_NONE; + break; + } + + if ( !(flags & XEN_HVMOP_IOREQ_MEM_ACCESS_WRITE) ) + { s = NULL; + } } } if ( !s && p2mt != p2m_ioreq_server ) s = hvm_select_ioreq_server(currd, &p); - /* If there is no suitable backing DM, just ignore accesses */ if ( !s ) { - /* - * For p2m_ioreq_server pages accessed with read-modify-write - * instructions, we provide a read handler to copy the data to - * the buffer. - */ - if ( p2mt == p2m_ioreq_server ) - rc = hvm_process_io_intercept(&mem_handler, &p); - else - rc = hvm_process_io_intercept(&null_handler, &p); + /* If there is no suitable backing DM, just ignore accesses */ + rc = hvm_process_io_intercept(&null_handler, &p); vio->io_req.state = STATE_IOREQ_NONE; } else -- 2.1.4