1843e1988Sjohnlev /* 2843e1988Sjohnlev * CDDL HEADER START 3843e1988Sjohnlev * 4843e1988Sjohnlev * The contents of this file are subject to the terms of the 5843e1988Sjohnlev * Common Development and Distribution License (the "License"). 6843e1988Sjohnlev * You may not use this file except in compliance with the License. 7843e1988Sjohnlev * 8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing. 10843e1988Sjohnlev * See the License for the specific language governing permissions 11843e1988Sjohnlev * and limitations under the License. 12843e1988Sjohnlev * 13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 18843e1988Sjohnlev * 19843e1988Sjohnlev * CDDL HEADER END 20843e1988Sjohnlev */ 21843e1988Sjohnlev 22843e1988Sjohnlev /* 23*ea8190a2Ssmaybe * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24843e1988Sjohnlev * Use is subject to license terms. 25843e1988Sjohnlev */ 26843e1988Sjohnlev 27843e1988Sjohnlev /* 28843e1988Sjohnlev * 29843e1988Sjohnlev * xenbus_comms.c 30843e1988Sjohnlev * 31843e1988Sjohnlev * Low level code to talks to Xen Store: ringbuffer and event channel. 32843e1988Sjohnlev * 33843e1988Sjohnlev * Copyright (C) 2005 Rusty Russell, IBM Corporation 34843e1988Sjohnlev * 35843e1988Sjohnlev * This file may be distributed separately from the Linux kernel, or 36843e1988Sjohnlev * incorporated into other software packages, subject to the following license: 37843e1988Sjohnlev * 38843e1988Sjohnlev * Permission is hereby granted, free of charge, to any person obtaining a copy 39843e1988Sjohnlev * of this source file (the "Software"), to deal in the Software without 40843e1988Sjohnlev * restriction, including without limitation the rights to use, copy, modify, 41843e1988Sjohnlev * merge, publish, distribute, sublicense, and/or sell copies of the Software, 42843e1988Sjohnlev * and to permit persons to whom the Software is furnished to do so, subject to 43843e1988Sjohnlev * the following conditions: 44843e1988Sjohnlev * 45843e1988Sjohnlev * The above copyright notice and this permission notice shall be included in 46843e1988Sjohnlev * all copies or substantial portions of the Software. 47843e1988Sjohnlev * 48843e1988Sjohnlev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49843e1988Sjohnlev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50843e1988Sjohnlev * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51843e1988Sjohnlev * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52843e1988Sjohnlev * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 53843e1988Sjohnlev * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 54843e1988Sjohnlev * IN THE SOFTWARE. 55843e1988Sjohnlev */ 56843e1988Sjohnlev 57843e1988Sjohnlev #pragma ident "%Z%%M% %I% %E% SMI" 58843e1988Sjohnlev 59843e1988Sjohnlev #include <sys/types.h> 60843e1988Sjohnlev #include <vm/hat.h> 61843e1988Sjohnlev #include <vm/as.h> 62843e1988Sjohnlev #include <sys/bootconf.h> 63843e1988Sjohnlev #include <vm/seg_kmem.h> 64551bc2a6Smrj #ifdef XPV_HVM_DRIVER 65551bc2a6Smrj #include <sys/pc_mmu.h> 66551bc2a6Smrj #include <sys/xpv_support.h> 67551bc2a6Smrj #include <sys/hypervisor.h> 68551bc2a6Smrj #else 69551bc2a6Smrj #include <vm/kboot_mmu.h> 70551bc2a6Smrj #include <sys/bootinfo.h> 71843e1988Sjohnlev #include <sys/hypervisor.h> 72843e1988Sjohnlev #include <sys/evtchn_impl.h> 73551bc2a6Smrj #endif 74843e1988Sjohnlev #include <sys/condvar.h> 75843e1988Sjohnlev #include <sys/mutex.h> 76843e1988Sjohnlev #include <sys/atomic.h> 77843e1988Sjohnlev #include <sys/mman.h> 78843e1988Sjohnlev #include <sys/errno.h> 79843e1988Sjohnlev #include <sys/cmn_err.h> 80843e1988Sjohnlev #include <sys/avintr.h> 81843e1988Sjohnlev #include <xen/sys/xenbus_comms.h> 82843e1988Sjohnlev #include <xen/public/io/xs_wire.h> 83843e1988Sjohnlev 84*ea8190a2Ssmaybe #ifndef XPV_HVM_DRIVER 85843e1988Sjohnlev static int xenbus_irq; 86*ea8190a2Ssmaybe #endif 87843e1988Sjohnlev static ddi_umem_cookie_t xb_cookie; /* cookie for xenbus comm page */ 88843e1988Sjohnlev extern caddr_t xb_addr; /* va of xenbus comm page */ 89843e1988Sjohnlev 90843e1988Sjohnlev static kcondvar_t xb_wait_cv; 91843e1988Sjohnlev static kmutex_t xb_wait_lock; 92843e1988Sjohnlev 93843e1988Sjohnlev #define xs_domain_interface(ra) ((struct xenstore_domain_interface *)(ra)) 94843e1988Sjohnlev 95843e1988Sjohnlev /*ARGSUSED*/ 96843e1988Sjohnlev static uint_t 97843e1988Sjohnlev xenbus_intr(void *unused) 98843e1988Sjohnlev { 99843e1988Sjohnlev mutex_enter(&xb_wait_lock); 100843e1988Sjohnlev cv_broadcast(&xb_wait_cv); 101843e1988Sjohnlev mutex_exit(&xb_wait_lock); 102843e1988Sjohnlev return (DDI_INTR_CLAIMED); 103843e1988Sjohnlev } 104843e1988Sjohnlev 105843e1988Sjohnlev static int 106843e1988Sjohnlev check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod) 107843e1988Sjohnlev { 108843e1988Sjohnlev return ((prod - cons) <= XENSTORE_RING_SIZE); 109843e1988Sjohnlev } 110843e1988Sjohnlev 111843e1988Sjohnlev static void * 112843e1988Sjohnlev get_output_chunk(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod, 113843e1988Sjohnlev char *buf, uint32_t *len) 114843e1988Sjohnlev { 115843e1988Sjohnlev *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod); 116843e1988Sjohnlev if ((XENSTORE_RING_SIZE - (prod - cons)) < *len) 117843e1988Sjohnlev *len = XENSTORE_RING_SIZE - (prod - cons); 118843e1988Sjohnlev return ((void *)(buf + MASK_XENSTORE_IDX(prod))); 119843e1988Sjohnlev } 120843e1988Sjohnlev 121843e1988Sjohnlev static const void * 122843e1988Sjohnlev get_input_chunk(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod, 123843e1988Sjohnlev const char *buf, uint32_t *len) 124843e1988Sjohnlev { 125843e1988Sjohnlev *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons); 126843e1988Sjohnlev if ((prod - cons) < *len) 127843e1988Sjohnlev *len = prod - cons; 128843e1988Sjohnlev return ((void *)(buf + MASK_XENSTORE_IDX(cons))); 129843e1988Sjohnlev } 130843e1988Sjohnlev 131843e1988Sjohnlev 132843e1988Sjohnlev int 133843e1988Sjohnlev xb_write(const void *data, unsigned len) 134843e1988Sjohnlev { 135843e1988Sjohnlev volatile struct xenstore_domain_interface *intf = 136843e1988Sjohnlev xs_domain_interface(xb_addr); 137843e1988Sjohnlev XENSTORE_RING_IDX cons, prod; 138843e1988Sjohnlev extern int do_polled_io; 139843e1988Sjohnlev 140843e1988Sjohnlev while (len != 0) { 141843e1988Sjohnlev void *dst; 142843e1988Sjohnlev unsigned int avail; 143843e1988Sjohnlev 144843e1988Sjohnlev mutex_enter(&xb_wait_lock); 145843e1988Sjohnlev while ((intf->req_prod - intf->req_cons) == 146843e1988Sjohnlev XENSTORE_RING_SIZE) { 147843e1988Sjohnlev if (interrupts_unleashed && !do_polled_io) { 148843e1988Sjohnlev if (cv_wait_sig(&xb_wait_cv, 149843e1988Sjohnlev &xb_wait_lock) == 0) { 150843e1988Sjohnlev mutex_exit(&xb_wait_lock); 151843e1988Sjohnlev return (EINTR); 152843e1988Sjohnlev } 153843e1988Sjohnlev } else { /* polled mode needed for early probes */ 154843e1988Sjohnlev (void) HYPERVISOR_yield(); 155843e1988Sjohnlev } 156843e1988Sjohnlev } 157843e1988Sjohnlev mutex_exit(&xb_wait_lock); 158843e1988Sjohnlev /* Read indexes, then verify. */ 159843e1988Sjohnlev cons = intf->req_cons; 160843e1988Sjohnlev prod = intf->req_prod; 161843e1988Sjohnlev membar_enter(); 162843e1988Sjohnlev if (!check_indexes(cons, prod)) 163843e1988Sjohnlev return (EIO); 164843e1988Sjohnlev 165843e1988Sjohnlev dst = get_output_chunk(cons, prod, (char *)intf->req, &avail); 166843e1988Sjohnlev if (avail == 0) 167843e1988Sjohnlev continue; 168843e1988Sjohnlev if (avail > len) 169843e1988Sjohnlev avail = len; 170843e1988Sjohnlev 171843e1988Sjohnlev (void) memcpy(dst, data, avail); 172843e1988Sjohnlev data = (void *)((uintptr_t)data + avail); 173843e1988Sjohnlev len -= avail; 174843e1988Sjohnlev 175843e1988Sjohnlev /* Other side must not see new header until data is there. */ 176843e1988Sjohnlev membar_producer(); 177843e1988Sjohnlev intf->req_prod += avail; 178843e1988Sjohnlev 179843e1988Sjohnlev /* This implies mb() before other side sees interrupt. */ 180843e1988Sjohnlev ec_notify_via_evtchn(xen_info->store_evtchn); 181843e1988Sjohnlev } 182843e1988Sjohnlev 183843e1988Sjohnlev return (0); 184843e1988Sjohnlev } 185843e1988Sjohnlev 186843e1988Sjohnlev int 187843e1988Sjohnlev xb_read(void *data, unsigned len) 188843e1988Sjohnlev { 189843e1988Sjohnlev volatile struct xenstore_domain_interface *intf = 190843e1988Sjohnlev xs_domain_interface(xb_addr); 191843e1988Sjohnlev XENSTORE_RING_IDX cons, prod; 192843e1988Sjohnlev extern int do_polled_io; 193843e1988Sjohnlev 194843e1988Sjohnlev while (len != 0) { 195843e1988Sjohnlev unsigned int avail; 196843e1988Sjohnlev const char *src; 197843e1988Sjohnlev 198843e1988Sjohnlev mutex_enter(&xb_wait_lock); 199843e1988Sjohnlev while (intf->rsp_cons == intf->rsp_prod) { 200843e1988Sjohnlev if (interrupts_unleashed && !do_polled_io) { 201843e1988Sjohnlev if (cv_wait_sig(&xb_wait_cv, 202843e1988Sjohnlev &xb_wait_lock) == 0) { 203843e1988Sjohnlev mutex_exit(&xb_wait_lock); 204843e1988Sjohnlev return (EINTR); 205843e1988Sjohnlev } 206843e1988Sjohnlev } else { /* polled mode needed for early probes */ 207843e1988Sjohnlev (void) HYPERVISOR_yield(); 208843e1988Sjohnlev } 209843e1988Sjohnlev } 210843e1988Sjohnlev mutex_exit(&xb_wait_lock); 211843e1988Sjohnlev /* Read indexes, then verify. */ 212843e1988Sjohnlev cons = intf->rsp_cons; 213843e1988Sjohnlev prod = intf->rsp_prod; 214843e1988Sjohnlev membar_enter(); 215843e1988Sjohnlev if (!check_indexes(cons, prod)) 216843e1988Sjohnlev return (EIO); 217843e1988Sjohnlev 218843e1988Sjohnlev src = get_input_chunk(cons, prod, (char *)intf->rsp, &avail); 219843e1988Sjohnlev if (avail == 0) 220843e1988Sjohnlev continue; 221843e1988Sjohnlev if (avail > len) 222843e1988Sjohnlev avail = len; 223843e1988Sjohnlev 224843e1988Sjohnlev /* We must read header before we read data. */ 225843e1988Sjohnlev membar_consumer(); 226843e1988Sjohnlev 227843e1988Sjohnlev (void) memcpy(data, src, avail); 228843e1988Sjohnlev data = (void *)((uintptr_t)data + avail); 229843e1988Sjohnlev len -= avail; 230843e1988Sjohnlev 231843e1988Sjohnlev /* Other side must not see free space until we've copied out */ 232843e1988Sjohnlev membar_enter(); 233843e1988Sjohnlev intf->rsp_cons += avail; 234843e1988Sjohnlev 235843e1988Sjohnlev /* Implies mb(): they will see new header. */ 236843e1988Sjohnlev ec_notify_via_evtchn(xen_info->store_evtchn); 237843e1988Sjohnlev } 238843e1988Sjohnlev 239843e1988Sjohnlev return (0); 240843e1988Sjohnlev } 241843e1988Sjohnlev 242843e1988Sjohnlev void 243843e1988Sjohnlev xb_suspend(void) 244843e1988Sjohnlev { 245*ea8190a2Ssmaybe #ifdef XPV_HVM_DRIVER 246*ea8190a2Ssmaybe ec_unbind_evtchn(xen_info->store_evtchn); 247*ea8190a2Ssmaybe #else 248843e1988Sjohnlev rem_avintr(NULL, IPL_XENBUS, (avfunc)xenbus_intr, xenbus_irq); 249*ea8190a2Ssmaybe #endif 250843e1988Sjohnlev } 251843e1988Sjohnlev 252843e1988Sjohnlev void 253843e1988Sjohnlev xb_setup_intr(void) 254843e1988Sjohnlev { 255551bc2a6Smrj #ifdef XPV_HVM_DRIVER 256551bc2a6Smrj ec_bind_evtchn_to_handler(xen_info->store_evtchn, IPL_XENBUS, 257551bc2a6Smrj xenbus_intr, NULL); 258551bc2a6Smrj #else 259843e1988Sjohnlev xenbus_irq = ec_bind_evtchn_to_irq(xen_info->store_evtchn); 260551bc2a6Smrj if (xenbus_irq < 0) { 261551bc2a6Smrj cmn_err(CE_WARN, "Couldn't bind xenbus event channel"); 262551bc2a6Smrj return; 263551bc2a6Smrj } 264843e1988Sjohnlev if (!add_avintr(NULL, IPL_XENBUS, (avfunc)xenbus_intr, "xenbus", 265843e1988Sjohnlev xenbus_irq, NULL, NULL, NULL, NULL)) 266843e1988Sjohnlev cmn_err(CE_WARN, "XENBUS add intr failed\n"); 267551bc2a6Smrj #endif 268843e1988Sjohnlev } 269843e1988Sjohnlev 270843e1988Sjohnlev /* 271843e1988Sjohnlev * Set up our xenstore page and event channel. Domain 0 needs to allocate a 272843e1988Sjohnlev * page and event channel; other domains use what we are told. 273843e1988Sjohnlev */ 274843e1988Sjohnlev void 275843e1988Sjohnlev xb_init(void) 276843e1988Sjohnlev { 277843e1988Sjohnlev int err; 278843e1988Sjohnlev 279843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) { 280843e1988Sjohnlev 281843e1988Sjohnlev if (xb_addr != NULL) 282843e1988Sjohnlev return; 283843e1988Sjohnlev 284843e1988Sjohnlev xb_addr = ddi_umem_alloc(PAGESIZE, DDI_UMEM_SLEEP, 285843e1988Sjohnlev &xb_cookie); 286843e1988Sjohnlev xen_info->store_mfn = pfn_to_mfn(hat_getpfnum(kas.a_hat, 287843e1988Sjohnlev xb_addr)); 288843e1988Sjohnlev 289843e1988Sjohnlev err = xen_alloc_unbound_evtchn(0, 290843e1988Sjohnlev (int *)&xen_info->store_evtchn); 291843e1988Sjohnlev ASSERT(err == 0); 292843e1988Sjohnlev } else { 293843e1988Sjohnlev /* 294843e1988Sjohnlev * This is harmless on first boot, but needed for resume and 295843e1988Sjohnlev * migrate. We use kbm_map_ma() as a shortcut instead of 296843e1988Sjohnlev * directly using HYPERVISOR_update_va_mapping(). 297843e1988Sjohnlev */ 298843e1988Sjohnlev ASSERT(xb_addr != NULL); 299843e1988Sjohnlev kbm_map_ma(mfn_to_ma(xen_info->store_mfn), 300843e1988Sjohnlev (uintptr_t)xb_addr, 0); 301843e1988Sjohnlev } 302843e1988Sjohnlev 303843e1988Sjohnlev ASSERT(xen_info->store_evtchn); 304843e1988Sjohnlev } 305843e1988Sjohnlev 306843e1988Sjohnlev void * 307843e1988Sjohnlev xb_xenstore_cookie(void) 308843e1988Sjohnlev { 309843e1988Sjohnlev ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 310843e1988Sjohnlev return (xb_cookie); 311843e1988Sjohnlev } 312