From patchwork Fri Sep 17 11:15:05 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony PERARD X-Patchwork-Id: 65067 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 8AFCCB70AE for ; Fri, 17 Sep 2010 21:32:04 +1000 (EST) Received: from localhost ([127.0.0.1]:35680 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OwZAZ-0007nA-E7 for incoming@patchwork.ozlabs.org; Fri, 17 Sep 2010 07:31:55 -0400 Received: from [140.186.70.92] (port=37202 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OwYvs-0006Tn-TA for qemu-devel@nongnu.org; Fri, 17 Sep 2010 07:16:49 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OwYvq-00084R-Dt for qemu-devel@nongnu.org; Fri, 17 Sep 2010 07:16:44 -0400 Received: from smtp02.citrix.com ([66.165.176.63]:45944) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OwYvq-00081M-3J for qemu-devel@nongnu.org; Fri, 17 Sep 2010 07:16:42 -0400 X-IronPort-AV: E=Sophos;i="4.56,381,1280721600"; d="scan'208";a="114266553" Received: from ftlpexchmx01.citrite.net ([10.9.154.126]) by FTLPIPO02.CITRIX.COM with ESMTP; 17 Sep 2010 07:16:41 -0400 Received: from smtp01.ad.xensource.com ([10.219.128.104]) by FTLPEXCHMX01.citrite.net with Microsoft SMTPSVC(6.0.3790.4675); Fri, 17 Sep 2010 07:16:30 -0400 Received: from localhost.localdomain (perard.cam.xci-test.com [10.80.248.106]) by smtp01.ad.xensource.com (8.13.1/8.13.1) with ESMTP id o8HBFpax017817; Fri, 17 Sep 2010 04:16:40 -0700 From: anthony.perard@citrix.com To: qemu-devel@nongnu.org Date: Fri, 17 Sep 2010 12:15:05 +0100 Message-Id: <1284722107-28550-11-git-send-email-anthony.perard@citrix.com> X-Mailer: git-send-email 1.6.5 In-Reply-To: <1284722107-28550-1-git-send-email-anthony.perard@citrix.com> References: <1284722107-28550-1-git-send-email-anthony.perard@citrix.com> X-OriginalArrivalTime: 17 Sep 2010 11:16:30.0537 (UTC) FILETIME=[C743AB90:01CB5659] X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. Cc: anthony.perard@citrix.com, Stefano Stabellini , xen-devel@lists.xensource.com, Stefano.Stabellini@eu.citrix.com Subject: [Qemu-devel] [PATCH RFC V3 10/12] xen: Initialize event channels and io rings X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Anthony PERARD Open and bind event channels; map ioreq and buffered ioreq rings. Signed-off-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/xen_common.h | 1 + xen-all.c | 381 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 382 insertions(+), 0 deletions(-) diff --git a/hw/xen_common.h b/hw/xen_common.h index dd54063..96cfad7 100644 --- a/hw/xen_common.h +++ b/hw/xen_common.h @@ -53,5 +53,6 @@ typedef xc_interface *qemu_xc_interface; #endif qemu_irq *i8259_xen_init(void); +void destroy_hvm_domain(void); #endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/xen-all.c b/xen-all.c index 4e0b061..13672f0 100644 --- a/xen-all.c +++ b/xen-all.c @@ -8,12 +8,38 @@ #include "config.h" +#include + #include "hw/pci.h" #include "hw/xen_common.h" #include "hw/xen_backend.h" #include "xen_mapcache.h" +#include + +//#define DEBUG_XEN + +#ifdef DEBUG_XEN +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +shared_iopage_t *shared_page = NULL; +#define BUFFER_IO_MAX_DELAY 100 +buffered_iopage_t *buffered_io_page = NULL; +QEMUTimer *buffered_io_timer; +/* the evtchn port for polling the notification, */ +evtchn_port_t *ioreq_local_port; +/* the evtchn fd for polling */ +int xce_handle = -1; +/* which vcpu we are serving */ +int send_vcpu = 0; +long time_offset = 0; + /* Xen specific function for piix pci */ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) @@ -111,19 +137,374 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size) } +/* VCPU Operations, MMIO, IO ring ... */ + +/* get the ioreq packets from share mem */ +static ioreq_t *cpu_get_ioreq_from_shared_memory(int vcpu) +{ + ioreq_t *req = &shared_page->vcpu_ioreq[vcpu]; + + if (req->state != STATE_IOREQ_READY) { + DPRINTF("I/O request not ready: " + "%x, ptr: %x, port: %"PRIx64", " + "data: %"PRIx64", count: %u, size: %u\n", + req->state, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + return NULL; + } + + xen_rmb(); /* see IOREQ_READY /then/ read contents of ioreq */ + + req->state = STATE_IOREQ_INPROCESS; + return req; +} + +/* use poll to get the port notification */ +/* ioreq_vec--out,the */ +/* retval--the number of ioreq packet */ +static ioreq_t *cpu_get_ioreq(void) +{ + int i; + evtchn_port_t port; + + port = xc_evtchn_pending(xce_handle); + if (port != -1) { + for ( i = 0; i < smp_cpus; i++ ) + if ( ioreq_local_port[i] == port ) + break; + + if ( i == smp_cpus ) { + hw_error("Fatal error while trying to get io event!\n"); + } + + /* unmask the wanted port again */ + xc_evtchn_unmask(xce_handle, port); + + /* get the io packet from shared memory */ + send_vcpu = i; + return cpu_get_ioreq_from_shared_memory(i); + } + + /* read error or read nothing */ + return NULL; +} + +static uint32_t do_inp(CPUState *env, pio_addr_t addr, unsigned long size) +{ + switch(size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + default: + hw_error("inp: bad size: %04"FMT_pioaddr" %lx", addr, size); + } +} + +static void do_outp(CPUState *env, pio_addr_t addr, + unsigned long size, uint32_t val) +{ + switch(size) { + case 1: + return cpu_outb(addr, val); + case 2: + return cpu_outw(addr, val); + case 4: + return cpu_outl(addr, val); + default: + hw_error("outp: bad size: %04"FMT_pioaddr" %lx", addr, size); + } +} + +static void cpu_ioreq_pio(CPUState *env, ioreq_t *req) +{ + int i, sign; + + sign = req->df ? -1 : 1; + + if (req->dir == IOREQ_READ) { + if (!req->data_is_ptr) { + req->data = do_inp(env, req->addr, req->size); + } else { + uint32_t tmp; + + for (i = 0; i < req->count; i++) { + tmp = do_inp(env, req->addr, req->size); + cpu_physical_memory_write(req->data + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + } + } + } else if (req->dir == IOREQ_WRITE) { + if (!req->data_is_ptr) { + do_outp(env, req->addr, req->size, req->data); + } else { + for (i = 0; i < req->count; i++) { + uint32_t tmp = 0; + + cpu_physical_memory_read(req->data + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + do_outp(env, req->addr, req->size, tmp); + } + } + } +} + +static void cpu_ioreq_move(CPUState *env, ioreq_t *req) +{ + int i, sign; + + sign = req->df ? -1 : 1; + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + cpu_physical_memory_read(req->addr + (sign * i * req->size), + (uint8_t*) &req->data, req->size); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + cpu_physical_memory_write(req->addr + (sign * i * req->size), + (uint8_t*) &req->data, req->size); + } + } + } else { + target_ulong tmp; + + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + cpu_physical_memory_read(req->addr + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + cpu_physical_memory_write(req->data + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + cpu_physical_memory_read(req->data + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + cpu_physical_memory_write(req->addr + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + } + } + } +} + +static void cpu_ioreq_timeoffset(CPUState *env, ioreq_t *req) +{ + /* char b[64]; */ + + time_offset += (unsigned long)req->data; + + //DPRINTF("Time offset set %ld, added offset %"PRId64"\n", + //time_offset, req->data); + /* snprintf(b, 64, "%ld", time_offset); */ + /* xenstore_vm_write(xen_domid, "rtc/timeoffset", b); */ +} + +static void handle_ioreq(CPUState *env, ioreq_t *req) +{ + if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) && + (req->size < sizeof(target_ulong))) + req->data &= ((target_ulong)1 << (8 * req->size)) - 1; + + switch (req->type) { + case IOREQ_TYPE_PIO: + cpu_ioreq_pio(env, req); + break; + case IOREQ_TYPE_COPY: + cpu_ioreq_move(env, req); + break; + case IOREQ_TYPE_TIMEOFFSET: + cpu_ioreq_timeoffset(env, req); + break; + case IOREQ_TYPE_INVALIDATE: + qemu_invalidate_map_cache(); + break; + default: + hw_error("Invalid ioreq type 0x%x\n", req->type); + } +} + +static void handle_buffered_iopage(CPUState *env) +{ + buf_ioreq_t *buf_req = NULL; + ioreq_t req; + int qw; + + if (!buffered_io_page) + return; + + while (buffered_io_page->read_pointer != + buffered_io_page->write_pointer) { + buf_req = &buffered_io_page->buf_ioreq[ + buffered_io_page->read_pointer % IOREQ_BUFFER_SLOT_NUM]; + req.size = 1UL << buf_req->size; + req.count = 1; + req.addr = buf_req->addr; + req.data = buf_req->data; + req.state = STATE_IOREQ_READY; + req.dir = buf_req->dir; + req.df = 1; + req.type = buf_req->type; + req.data_is_ptr = 0; + qw = (req.size == 8); + if (qw) { + buf_req = &buffered_io_page->buf_ioreq[ + (buffered_io_page->read_pointer+1) % IOREQ_BUFFER_SLOT_NUM]; + req.data |= ((uint64_t)buf_req->data) << 32; + } + + handle_ioreq(env, &req); + + xen_mb(); + buffered_io_page->read_pointer += qw ? 2 : 1; + } +} + +static void handle_buffered_io(void *opaque) +{ + CPUState *env = opaque; + + handle_buffered_iopage(env); + qemu_mod_timer(buffered_io_timer, BUFFER_IO_MAX_DELAY + + qemu_get_clock(rt_clock)); +} + +static void cpu_handle_ioreq(void *opaque) +{ + CPUState *env = opaque; + ioreq_t *req = cpu_get_ioreq(); + + handle_buffered_iopage(env); + if (req) { + handle_ioreq(env, req); + + if (req->state != STATE_IOREQ_INPROCESS) { + fprintf(stderr, "Badness in I/O request ... not in service?!: " + "%x, ptr: %x, port: %"PRIx64", " + "data: %"PRIx64", count: %u, size: %u\n", + req->state, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + destroy_hvm_domain(); + return; + } + + xen_wmb(); /* Update ioreq contents /then/ update state. */ + + /* + * We do this before we send the response so that the tools + * have the opportunity to pick up on the reset before the + * guest resumes and does a hlt with interrupts disabled which + * causes Xen to powerdown the domain. + */ + if (vm_running) { + if (qemu_shutdown_requested_get()) { + destroy_hvm_domain(); + } + if (qemu_reset_requested_get()) { + qemu_system_reset(); + } + } + + req->state = STATE_IORESP_READY; + xc_evtchn_notify(xce_handle, ioreq_local_port[send_vcpu]); + } +} + +static void xen_main_loop_prepare(void) +{ + CPUState *env = cpu_single_env; + + int evtchn_fd = xce_handle == -1 ? -1 : xc_evtchn_fd(xce_handle); + + buffered_io_timer = qemu_new_timer(rt_clock, handle_buffered_io, + cpu_single_env); + qemu_mod_timer(buffered_io_timer, qemu_get_clock(rt_clock)); + + if (evtchn_fd != -1) + qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env); +} + + /* Initialise Xen */ +static void xen_vm_change_state_handler(void *opaque, int running, int reason) +{ + if (running) + xen_main_loop_prepare(); +} + int xen_init(int smp_cpus) { + int i, rc; + unsigned long ioreq_pfn; + xen_xc = xc_interface_open(NULL, NULL, 0); if (xen_xc == NULL) { xen_be_printf(NULL, 0, "can't open xen interface\n"); return -1; } + xce_handle = xc_evtchn_open(); + if (xce_handle == -1) { + perror("open"); + return -errno; + } + + xc_get_hvm_param(xen_xc, xen_domid, HVM_PARAM_IOREQ_PFN, &ioreq_pfn); + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + shared_page = xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, ioreq_pfn); + if (shared_page == NULL) { + hw_error("map shared IO page returned error %d handle=%p", errno, xen_xc); + } + + xc_get_hvm_param(xen_xc, xen_domid, HVM_PARAM_BUFIOREQ_PFN, &ioreq_pfn); + DPRINTF("buffered io page at pfn %lx\n", ioreq_pfn); + buffered_io_page = xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, ioreq_pfn); + if (buffered_io_page == NULL) { + hw_error("map buffered IO page returned error %d", errno); + } + + ioreq_local_port = qemu_mallocz(smp_cpus * sizeof(evtchn_port_t)); + + /* FIXME: how about if we overflow the page here? */ + for (i = 0; i < smp_cpus; i++) { + rc = xc_evtchn_bind_interdomain(xce_handle, xen_domid, + shared_page->vcpu_ioreq[i].vp_eport); + if (rc == -1) { + fprintf(stderr, "bind interdomain ioctl error %d\n", errno); + return -1; + } + ioreq_local_port[i] = rc; + } + /* Init RAM management */ qemu_map_cache_init(); xen_ram_init(ram_size); + qemu_add_vm_change_state_handler(xen_vm_change_state_handler, NULL); + return 0; } + +void destroy_hvm_domain(void) +{ + xc_interface *xc_handle; + int sts; + + xc_handle = xc_interface_open(NULL, NULL, 0); + if (!xc_handle) + fprintf(stderr, "Cannot acquire xenctrl handle\n"); + else { + sts = xc_domain_shutdown(xc_handle, xen_domid, SHUTDOWN_poweroff); + if (sts != 0) + fprintf(stderr, "? xc_domain_shutdown failed to issue poweroff, " + "sts %d, errno %d\n", sts, errno); + else + fprintf(stderr, "Issued domain %d poweroff\n", xen_domid); + xc_interface_close(xc_handle); + } +}