12309Srsmaeda /* 22309Srsmaeda * CDDL HEADER START 32309Srsmaeda * 42309Srsmaeda * The contents of this file are subject to the terms of the 52309Srsmaeda * Common Development and Distribution License (the "License"). 62309Srsmaeda * You may not use this file except in compliance with the License. 72309Srsmaeda * 82309Srsmaeda * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92309Srsmaeda * or http://www.opensolaris.org/os/licensing. 102309Srsmaeda * See the License for the specific language governing permissions 112309Srsmaeda * and limitations under the License. 122309Srsmaeda * 132309Srsmaeda * When distributing Covered Code, include this CDDL HEADER in each 142309Srsmaeda * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152309Srsmaeda * If applicable, add the following below this CDDL HEADER, with the 162309Srsmaeda * fields enclosed by brackets "[]" replaced with your own identifying 172309Srsmaeda * information: Portions Copyright [yyyy] [name of copyright owner] 182309Srsmaeda * 192309Srsmaeda * CDDL HEADER END 202309Srsmaeda */ 212309Srsmaeda 222309Srsmaeda /* 236441Sjm22469 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 242309Srsmaeda * Use is subject to license terms. 252309Srsmaeda */ 262309Srsmaeda 272309Srsmaeda /* 282309Srsmaeda * DR control module for LDoms 292309Srsmaeda */ 302309Srsmaeda 312309Srsmaeda #include <sys/sysmacros.h> 322309Srsmaeda #include <sys/modctl.h> 332309Srsmaeda #include <sys/conf.h> 342309Srsmaeda #include <sys/ddi.h> 352309Srsmaeda #include <sys/sunddi.h> 362309Srsmaeda #include <sys/ddi_impldefs.h> 372309Srsmaeda #include <sys/stat.h> 382309Srsmaeda #include <sys/door.h> 392309Srsmaeda #include <sys/open.h> 402309Srsmaeda #include <sys/note.h> 412309Srsmaeda #include <sys/ldoms.h> 422309Srsmaeda #include <sys/dr_util.h> 432309Srsmaeda #include <sys/drctl.h> 442309Srsmaeda #include <sys/drctl_impl.h> 452309Srsmaeda 462309Srsmaeda 472309Srsmaeda static int drctl_attach(dev_info_t *, ddi_attach_cmd_t); 482309Srsmaeda static int drctl_detach(dev_info_t *, ddi_detach_cmd_t); 492309Srsmaeda static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 502309Srsmaeda 512309Srsmaeda static int drctl_open(dev_t *, int, int, cred_t *); 522309Srsmaeda static int drctl_close(dev_t, int, int, cred_t *); 532309Srsmaeda static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 542309Srsmaeda 55*7899SJames.Marks@Sun.COM static void *pack_message(int, int, int, void *, size_t *, size_t *); 56*7899SJames.Marks@Sun.COM static int send_message(void *, size_t, drctl_resp_t **, size_t *); 572309Srsmaeda 582309Srsmaeda 592309Srsmaeda /* 602309Srsmaeda * Configuration data structures 612309Srsmaeda */ 622309Srsmaeda static struct cb_ops drctl_cb_ops = { 632309Srsmaeda drctl_open, /* open */ 642309Srsmaeda drctl_close, /* close */ 652309Srsmaeda nodev, /* strategy */ 662309Srsmaeda nodev, /* print */ 672309Srsmaeda nodev, /* dump */ 682309Srsmaeda nodev, /* read */ 692309Srsmaeda nodev, /* write */ 702309Srsmaeda drctl_ioctl, /* ioctl */ 712309Srsmaeda nodev, /* devmap */ 722309Srsmaeda nodev, /* mmap */ 732309Srsmaeda nodev, /* segmap */ 742309Srsmaeda nochpoll, /* poll */ 752309Srsmaeda ddi_prop_op, /* prop_op */ 762309Srsmaeda NULL, /* streamtab */ 772309Srsmaeda D_MP | D_NEW, /* driver compatibility flag */ 782309Srsmaeda CB_REV, /* cb_ops revision */ 792309Srsmaeda nodev, /* async read */ 802309Srsmaeda nodev /* async write */ 812309Srsmaeda }; 822309Srsmaeda 832309Srsmaeda 842309Srsmaeda static struct dev_ops drctl_ops = { 852309Srsmaeda DEVO_REV, /* devo_rev */ 862309Srsmaeda 0, /* refcnt */ 872309Srsmaeda drctl_getinfo, /* info */ 882309Srsmaeda nulldev, /* identify */ 892309Srsmaeda nulldev, /* probe */ 902309Srsmaeda drctl_attach, /* attach */ 912309Srsmaeda drctl_detach, /* detach */ 922309Srsmaeda nodev, /* reset */ 932309Srsmaeda &drctl_cb_ops, /* driver operations */ 942309Srsmaeda NULL, /* bus operations */ 952309Srsmaeda NULL, /* power */ 967656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 972309Srsmaeda }; 982309Srsmaeda 992309Srsmaeda static struct modldrv modldrv = { 1002309Srsmaeda &mod_driverops, /* type of module - driver */ 1017656SSherry.Moore@Sun.COM "DR Control pseudo driver", 1022309Srsmaeda &drctl_ops 1032309Srsmaeda }; 1042309Srsmaeda 1052309Srsmaeda static struct modlinkage modlinkage = { 1062309Srsmaeda MODREV_1, 1072309Srsmaeda &modldrv, 1082309Srsmaeda NULL 1092309Srsmaeda }; 1102309Srsmaeda 1112309Srsmaeda 1122309Srsmaeda /* 1132309Srsmaeda * Locking strategy 1142309Srsmaeda * 1152309Srsmaeda * One of the reasons for this module's existence is to serialize 1162309Srsmaeda * DR requests which might be coming from different sources. Only 1172309Srsmaeda * one operation is allowed to be in progress at any given time. 1182309Srsmaeda * 1192309Srsmaeda * A single lock word (the 'drc_busy' element below) is NULL 1202309Srsmaeda * when there is no operation in progress. When a client of this 1212309Srsmaeda * module initiates an operation it grabs the mutex 'drc_lock' in 1222309Srsmaeda * order to examine the lock word ('drc_busy'). If no other 1232309Srsmaeda * operation is in progress, the lock word will be NULL. If so, 1242309Srsmaeda * a cookie which uniquely identifies the requestor is stored in 1252309Srsmaeda * the lock word, and the mutex is released. Attempts by other 1262309Srsmaeda * clients to initiate an operation will fail. 1272309Srsmaeda * 1282309Srsmaeda * When the lock-holding client's operation is completed, the 1292309Srsmaeda * client will call a "finalize" function in this module, providing 1302309Srsmaeda * the cookie passed with the original request. Since the cookie 1312309Srsmaeda * matches, the operation will succeed and the lock word will be 1322309Srsmaeda * cleared. At this point, an new operation may be initiated. 1332309Srsmaeda */ 1342309Srsmaeda 1352309Srsmaeda /* 1362309Srsmaeda * Driver private data 1372309Srsmaeda */ 1382309Srsmaeda static struct drctl_unit { 1392309Srsmaeda kmutex_t drc_lock; /* global driver lock */ 1402309Srsmaeda dev_info_t *drc_dip; /* dev_info pointer */ 1412309Srsmaeda kcondvar_t drc_busy_cv; /* block for !busy */ 1422309Srsmaeda drctl_cookie_t drc_busy; /* NULL if free else a unique */ 1432309Srsmaeda /* identifier for caller */ 1442309Srsmaeda int drc_cmd; /* the cmd underway (or -1) */ 1452309Srsmaeda int drc_flags; /* saved flag from above cmd */ 1462309Srsmaeda int drc_inst; /* our single instance */ 1472309Srsmaeda uint_t drc_state; /* driver state */ 1482309Srsmaeda } drctl_state; 1492309Srsmaeda 1502309Srsmaeda static struct drctl_unit *drctlp = &drctl_state; 1512309Srsmaeda 1522309Srsmaeda int 1532309Srsmaeda _init(void) 1542309Srsmaeda { 1552410Slm66018 int rv; 1562410Slm66018 1572309Srsmaeda drctlp->drc_inst = -1; 1582309Srsmaeda mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL); 1592410Slm66018 1602410Slm66018 if ((rv = mod_install(&modlinkage)) != 0) 1612410Slm66018 mutex_destroy(&drctlp->drc_lock); 1622410Slm66018 1632410Slm66018 return (rv); 1642309Srsmaeda } 1652309Srsmaeda 1662309Srsmaeda 1672309Srsmaeda int 1682309Srsmaeda _fini(void) 1692309Srsmaeda { 1702410Slm66018 int rv; 1712410Slm66018 1722410Slm66018 if ((rv = mod_remove(&modlinkage)) != 0) 1732410Slm66018 return (rv); 1742410Slm66018 1752309Srsmaeda mutex_destroy(&drctlp->drc_lock); 1762410Slm66018 return (0); 1772309Srsmaeda } 1782309Srsmaeda 1792309Srsmaeda 1802309Srsmaeda int 1812309Srsmaeda _info(struct modinfo *modinfop) 1822309Srsmaeda { 1832309Srsmaeda return (mod_info(&modlinkage, modinfop)); 1842309Srsmaeda } 1852309Srsmaeda 1862309Srsmaeda 1872309Srsmaeda /* 1882309Srsmaeda * Do the attach work 1892309Srsmaeda */ 1902309Srsmaeda static int 1912309Srsmaeda drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1922309Srsmaeda { 1932309Srsmaeda _NOTE(ARGUNUSED(cmd)) 1942309Srsmaeda 1952309Srsmaeda char *str = "drctl_do_attach"; 1962309Srsmaeda int retval = DDI_SUCCESS; 1972309Srsmaeda 1982309Srsmaeda if (drctlp->drc_inst != -1) { 1992309Srsmaeda cmn_err(CE_WARN, "%s: an instance is already attached!", str); 2002309Srsmaeda return (DDI_FAILURE); 2012309Srsmaeda } 2022309Srsmaeda drctlp->drc_inst = ddi_get_instance(dip); 2032309Srsmaeda 2042309Srsmaeda retval = ddi_create_minor_node(dip, "drctl", S_IFCHR, 2052309Srsmaeda drctlp->drc_inst, DDI_PSEUDO, 0); 2062309Srsmaeda if (retval != DDI_SUCCESS) { 2072309Srsmaeda cmn_err(CE_WARN, "%s: can't create minor node", str); 2082309Srsmaeda drctlp->drc_inst = -1; 2092309Srsmaeda return (retval); 2102309Srsmaeda } 2112309Srsmaeda 2122309Srsmaeda drctlp->drc_dip = dip; 2132309Srsmaeda ddi_report_dev(dip); 2142309Srsmaeda 2152309Srsmaeda return (retval); 2162309Srsmaeda } 2172309Srsmaeda 2182309Srsmaeda 2192309Srsmaeda static int 2202309Srsmaeda drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2212309Srsmaeda { 2222309Srsmaeda switch (cmd) { 2232309Srsmaeda case DDI_ATTACH: 2242309Srsmaeda return (drctl_do_attach(dip, cmd)); 2252309Srsmaeda 2262309Srsmaeda default: 2272309Srsmaeda return (DDI_FAILURE); 2282309Srsmaeda } 2292309Srsmaeda } 2302309Srsmaeda 2312309Srsmaeda 2322309Srsmaeda /* ARGSUSED */ 2332309Srsmaeda static int 2342309Srsmaeda drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 2352309Srsmaeda { 2362309Srsmaeda switch (cmd) { 2372309Srsmaeda case DDI_DETACH: 2382309Srsmaeda drctlp->drc_inst = -1; 2392309Srsmaeda ddi_remove_minor_node(dip, "drctl"); 2402309Srsmaeda return (DDI_SUCCESS); 2412309Srsmaeda 2422309Srsmaeda default: 2432309Srsmaeda return (DDI_FAILURE); 2442309Srsmaeda } 2452309Srsmaeda } 2462309Srsmaeda 2472309Srsmaeda static int 2482309Srsmaeda drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 2492309Srsmaeda { 2502309Srsmaeda _NOTE(ARGUNUSED(dip, cmd, arg, resultp)) 2512309Srsmaeda 2522309Srsmaeda return (0); 2532309Srsmaeda } 2542309Srsmaeda 2552309Srsmaeda static int 2562309Srsmaeda drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 2572309Srsmaeda { 2582309Srsmaeda _NOTE(ARGUNUSED(devp, flag, cred_p)) 2592309Srsmaeda 2602309Srsmaeda if (otyp != OTYP_CHR) 2612309Srsmaeda return (EINVAL); 2622309Srsmaeda 2632309Srsmaeda return (0); 2642309Srsmaeda } 2652309Srsmaeda 2662309Srsmaeda static int 2672309Srsmaeda drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 2682309Srsmaeda { 2692309Srsmaeda _NOTE(ARGUNUSED(dev, flag, otyp, cred_p)) 2702309Srsmaeda 2712309Srsmaeda return (0); 2722309Srsmaeda } 2732309Srsmaeda 2742309Srsmaeda /* 275*7899SJames.Marks@Sun.COM * Create a reponse structure which includes an array of drctl_rsrc_t 276*7899SJames.Marks@Sun.COM * structures in which each status element is set to the 'status' 277*7899SJames.Marks@Sun.COM * arg. There is no error text, so set the 'offset' elements to 0. 2782309Srsmaeda */ 279*7899SJames.Marks@Sun.COM static drctl_resp_t * 2802309Srsmaeda drctl_generate_resp(drctl_rsrc_t *res, 2812309Srsmaeda int count, size_t *rsize, drctl_status_t status) 2822309Srsmaeda { 283*7899SJames.Marks@Sun.COM int i; 2842309Srsmaeda size_t size; 285*7899SJames.Marks@Sun.COM drctl_rsrc_t *rsrc; 286*7899SJames.Marks@Sun.COM drctl_resp_t *resp; 2872309Srsmaeda 288*7899SJames.Marks@Sun.COM size = offsetof(drctl_resp_t, resp_resources) + (count * sizeof (*res)); 289*7899SJames.Marks@Sun.COM resp = kmem_alloc(size, KM_SLEEP); 290*7899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: alloc addr %p size %ld\n", 291*7899SJames.Marks@Sun.COM __func__, (void *)resp, size); 2922309Srsmaeda 293*7899SJames.Marks@Sun.COM resp->resp_type = DRCTL_RESP_OK; 294*7899SJames.Marks@Sun.COM rsrc = resp->resp_resources; 2952309Srsmaeda 296*7899SJames.Marks@Sun.COM bcopy(res, rsrc, count * sizeof (*res)); 297*7899SJames.Marks@Sun.COM 298*7899SJames.Marks@Sun.COM for (i = 0; i < count; i++) { 299*7899SJames.Marks@Sun.COM rsrc[i].status = status; 300*7899SJames.Marks@Sun.COM rsrc[i].offset = 0; 3012309Srsmaeda } 3022309Srsmaeda 3032309Srsmaeda *rsize = size; 304*7899SJames.Marks@Sun.COM 305*7899SJames.Marks@Sun.COM return (resp); 306*7899SJames.Marks@Sun.COM } 307*7899SJames.Marks@Sun.COM 308*7899SJames.Marks@Sun.COM /* 309*7899SJames.Marks@Sun.COM * Generate an error response message. 310*7899SJames.Marks@Sun.COM */ 311*7899SJames.Marks@Sun.COM static drctl_resp_t * 312*7899SJames.Marks@Sun.COM drctl_generate_err_resp(char *msg, size_t *size) 313*7899SJames.Marks@Sun.COM { 314*7899SJames.Marks@Sun.COM drctl_resp_t *resp; 315*7899SJames.Marks@Sun.COM 316*7899SJames.Marks@Sun.COM ASSERT(msg != NULL); 317*7899SJames.Marks@Sun.COM ASSERT(size != NULL); 318*7899SJames.Marks@Sun.COM 319*7899SJames.Marks@Sun.COM *size = offsetof(drctl_resp_t, resp_err_msg) + strlen(msg) + 1; 320*7899SJames.Marks@Sun.COM resp = kmem_alloc(*size, KM_SLEEP); 321*7899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: alloc addr %p size %ld\n", 322*7899SJames.Marks@Sun.COM __func__, (void *)resp, *size); 323*7899SJames.Marks@Sun.COM 324*7899SJames.Marks@Sun.COM resp->resp_type = DRCTL_RESP_ERR; 325*7899SJames.Marks@Sun.COM (void) strcpy(resp->resp_err_msg, msg); 326*7899SJames.Marks@Sun.COM 327*7899SJames.Marks@Sun.COM return (resp); 3282309Srsmaeda } 3292309Srsmaeda 330*7899SJames.Marks@Sun.COM /* 331*7899SJames.Marks@Sun.COM * Since response comes from userland, verify that it is at least the 332*7899SJames.Marks@Sun.COM * minimum size based on the size of the original request. Verify 333*7899SJames.Marks@Sun.COM * that any offsets to error strings are within the string area of 334*7899SJames.Marks@Sun.COM * the response and, force the string area to be null-terminated. 335*7899SJames.Marks@Sun.COM */ 336*7899SJames.Marks@Sun.COM static int 337*7899SJames.Marks@Sun.COM verify_response(int cmd, 338*7899SJames.Marks@Sun.COM int count, drctl_resp_t *resp, size_t sent_len, size_t resp_len) 339*7899SJames.Marks@Sun.COM { 340*7899SJames.Marks@Sun.COM drctl_rsrc_t *rsrc = resp->resp_resources; 341*7899SJames.Marks@Sun.COM size_t rcvd_len = resp_len - (offsetof(drctl_resp_t, resp_resources)); 342*7899SJames.Marks@Sun.COM int is_cpu = 0; 343*7899SJames.Marks@Sun.COM int i; 344*7899SJames.Marks@Sun.COM 345*7899SJames.Marks@Sun.COM switch (cmd) { 346*7899SJames.Marks@Sun.COM case DRCTL_CPU_CONFIG_REQUEST: 347*7899SJames.Marks@Sun.COM case DRCTL_CPU_UNCONFIG_REQUEST: 348*7899SJames.Marks@Sun.COM if (rcvd_len < sent_len) 349*7899SJames.Marks@Sun.COM return (EIO); 350*7899SJames.Marks@Sun.COM is_cpu = 1; 351*7899SJames.Marks@Sun.COM break; 352*7899SJames.Marks@Sun.COM case DRCTL_IO_UNCONFIG_REQUEST: 353*7899SJames.Marks@Sun.COM case DRCTL_IO_CONFIG_REQUEST: 354*7899SJames.Marks@Sun.COM if (count != 1) 355*7899SJames.Marks@Sun.COM return (EIO); 356*7899SJames.Marks@Sun.COM break; 357*7899SJames.Marks@Sun.COM default: 358*7899SJames.Marks@Sun.COM return (EIO); 359*7899SJames.Marks@Sun.COM } 360*7899SJames.Marks@Sun.COM 361*7899SJames.Marks@Sun.COM for (i = 0; i < count; i++) 362*7899SJames.Marks@Sun.COM if ((rsrc[i].offset > 0) && 363*7899SJames.Marks@Sun.COM /* string can't be inside the bounds of original request */ 364*7899SJames.Marks@Sun.COM (((rsrc[i].offset < sent_len) && is_cpu) || 365*7899SJames.Marks@Sun.COM /* string must start inside the message */ 366*7899SJames.Marks@Sun.COM (rsrc[i].offset >= rcvd_len))) 367*7899SJames.Marks@Sun.COM return (EIO); 368*7899SJames.Marks@Sun.COM 369*7899SJames.Marks@Sun.COM /* If there are any strings, terminate the string area. */ 370*7899SJames.Marks@Sun.COM if (rcvd_len > sent_len) 371*7899SJames.Marks@Sun.COM *((char *)rsrc + rcvd_len - 1) = '\0'; 372*7899SJames.Marks@Sun.COM 373*7899SJames.Marks@Sun.COM return (0); 374*7899SJames.Marks@Sun.COM } 375*7899SJames.Marks@Sun.COM 376*7899SJames.Marks@Sun.COM 3772309Srsmaeda static int 3782309Srsmaeda drctl_config_common(int cmd, int flags, drctl_rsrc_t *res, 379*7899SJames.Marks@Sun.COM int count, drctl_resp_t **rbuf, size_t *rsize, size_t *rq_size) 3802309Srsmaeda { 3812309Srsmaeda int rv = 0; 3822309Srsmaeda size_t size; 3832309Srsmaeda char *bufp; 3842309Srsmaeda 3852309Srsmaeda switch (cmd) { 3862309Srsmaeda case DRCTL_CPU_CONFIG_REQUEST: 3872309Srsmaeda case DRCTL_CPU_CONFIG_NOTIFY: 3882309Srsmaeda case DRCTL_CPU_UNCONFIG_REQUEST: 3892309Srsmaeda case DRCTL_CPU_UNCONFIG_NOTIFY: 3906441Sjm22469 case DRCTL_IO_UNCONFIG_REQUEST: 3916441Sjm22469 case DRCTL_IO_UNCONFIG_NOTIFY: 3926441Sjm22469 case DRCTL_IO_CONFIG_REQUEST: 3936441Sjm22469 case DRCTL_IO_CONFIG_NOTIFY: 3942309Srsmaeda rv = 0; 3952309Srsmaeda break; 3962309Srsmaeda case DRCTL_MEM_CONFIG_REQUEST: 3972309Srsmaeda case DRCTL_MEM_CONFIG_NOTIFY: 3982309Srsmaeda case DRCTL_MEM_UNCONFIG_REQUEST: 3992309Srsmaeda case DRCTL_MEM_UNCONFIG_NOTIFY: 4002309Srsmaeda rv = ENOTSUP; 4012309Srsmaeda break; 4022309Srsmaeda } 4032309Srsmaeda 4042309Srsmaeda if (rv != 0) { 405*7899SJames.Marks@Sun.COM DR_DBG_CTL("%s: invalid cmd %d\n", __func__, cmd); 4062309Srsmaeda return (rv); 4072309Srsmaeda } 4082309Srsmaeda 4092309Srsmaeda /* 4102309Srsmaeda * If the operation is a FORCE, we don't send a message to 4112309Srsmaeda * the daemon. But, the upstream clients still expect a 4122309Srsmaeda * response, so generate a response with all ops 'allowed'. 4132309Srsmaeda */ 4142309Srsmaeda if (flags == DRCTL_FLAG_FORCE) { 415*7899SJames.Marks@Sun.COM if (rbuf != NULL) 416*7899SJames.Marks@Sun.COM *rbuf = drctl_generate_resp(res, 417*7899SJames.Marks@Sun.COM count, rsize, DRCTL_STATUS_ALLOW); 4182309Srsmaeda return (0); 4192309Srsmaeda } 4202309Srsmaeda 421*7899SJames.Marks@Sun.COM bufp = pack_message(cmd, flags, count, (void *)res, &size, rq_size); 4222309Srsmaeda DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n", 423*7899SJames.Marks@Sun.COM __func__, (void *)bufp, size); 4242309Srsmaeda 425*7899SJames.Marks@Sun.COM if (bufp == NULL || size == 0) 426*7899SJames.Marks@Sun.COM return (EINVAL); 4272309Srsmaeda 428*7899SJames.Marks@Sun.COM return (send_message(bufp, size, rbuf, rsize)); 4292309Srsmaeda } 4302309Srsmaeda 4312309Srsmaeda /* 4322309Srsmaeda * Prepare for a reconfig operation. 4332309Srsmaeda */ 4342309Srsmaeda int 4352309Srsmaeda drctl_config_init(int cmd, int flags, drctl_rsrc_t *res, 436*7899SJames.Marks@Sun.COM int count, drctl_resp_t **rbuf, size_t *rsize, drctl_cookie_t ck) 4372309Srsmaeda { 438*7899SJames.Marks@Sun.COM static char inval_msg[] = "Invalid command format received.\n"; 439*7899SJames.Marks@Sun.COM static char unsup_msg[] = "Unsuppported command received.\n"; 440*7899SJames.Marks@Sun.COM static char unk_msg [] = "Failure reason unknown.\n"; 441*7899SJames.Marks@Sun.COM static char rsp_msg [] = "Invalid response from " 442*7899SJames.Marks@Sun.COM "reconfiguration daemon.\n"; 443*7899SJames.Marks@Sun.COM static char drd_msg [] = "Cannot communicate with reconfiguration " 444*7899SJames.Marks@Sun.COM "daemon (drd) in target domain.\n" 445*7899SJames.Marks@Sun.COM "drd(1M) SMF service may not be enabled.\n"; 446*7899SJames.Marks@Sun.COM static char busy_msg [] = "Busy executing earlier command; " 447*7899SJames.Marks@Sun.COM "please try again later.\n"; 448*7899SJames.Marks@Sun.COM size_t rq_size; 449*7899SJames.Marks@Sun.COM char *ermsg; 4502309Srsmaeda int rv; 4512309Srsmaeda 452*7899SJames.Marks@Sun.COM if (ck == 0) { 453*7899SJames.Marks@Sun.COM *rbuf = drctl_generate_err_resp(inval_msg, rsize); 454*7899SJames.Marks@Sun.COM 4552309Srsmaeda return (EINVAL); 456*7899SJames.Marks@Sun.COM } 4572309Srsmaeda 4582309Srsmaeda mutex_enter(&drctlp->drc_lock); 4592309Srsmaeda 4602309Srsmaeda if (drctlp->drc_busy != NULL) { 4612309Srsmaeda mutex_exit(&drctlp->drc_lock); 462*7899SJames.Marks@Sun.COM *rbuf = drctl_generate_err_resp(busy_msg, rsize); 463*7899SJames.Marks@Sun.COM 4642309Srsmaeda return (EBUSY); 4652309Srsmaeda } 4662309Srsmaeda 4672309Srsmaeda DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n", 468*7899SJames.Marks@Sun.COM __func__, cmd, flags, (void *)res, count); 4692309Srsmaeda 4702309Srsmaeda /* Mark the link busy. Below we will fill in the actual cookie. */ 4712309Srsmaeda drctlp->drc_busy = (drctl_cookie_t)-1; 4722309Srsmaeda mutex_exit(&drctlp->drc_lock); 4732309Srsmaeda 474*7899SJames.Marks@Sun.COM rv = drctl_config_common(cmd, flags, res, count, rbuf, rsize, &rq_size); 475*7899SJames.Marks@Sun.COM if (rv == 0) { 4762309Srsmaeda /* 477*7899SJames.Marks@Sun.COM * If the upcall to the daemon returned successfully, we 478*7899SJames.Marks@Sun.COM * still need to validate the format of the returned msg. 4792309Srsmaeda */ 480*7899SJames.Marks@Sun.COM if ((rv = verify_response(cmd, 481*7899SJames.Marks@Sun.COM count, *rbuf, rq_size, *rsize)) != 0) { 482*7899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: free addr %p size %ld\n", 483*7899SJames.Marks@Sun.COM __func__, (void *)*rbuf, *rsize); 484*7899SJames.Marks@Sun.COM kmem_free(*rbuf, *rsize); 485*7899SJames.Marks@Sun.COM *rbuf = drctl_generate_err_resp(rsp_msg, rsize); 486*7899SJames.Marks@Sun.COM drctlp->drc_busy = NULL; 487*7899SJames.Marks@Sun.COM } else { /* message format is valid */ 488*7899SJames.Marks@Sun.COM drctlp->drc_busy = ck; 489*7899SJames.Marks@Sun.COM drctlp->drc_cmd = cmd; 490*7899SJames.Marks@Sun.COM drctlp->drc_flags = flags; 4912309Srsmaeda } 4922309Srsmaeda } else { 493*7899SJames.Marks@Sun.COM switch (rv) { 494*7899SJames.Marks@Sun.COM case ENOTSUP: 495*7899SJames.Marks@Sun.COM ermsg = unsup_msg; 496*7899SJames.Marks@Sun.COM break; 497*7899SJames.Marks@Sun.COM case EIO: 498*7899SJames.Marks@Sun.COM ermsg = drd_msg; 499*7899SJames.Marks@Sun.COM break; 500*7899SJames.Marks@Sun.COM default: 501*7899SJames.Marks@Sun.COM ermsg = unk_msg; 502*7899SJames.Marks@Sun.COM break; 503*7899SJames.Marks@Sun.COM } 504*7899SJames.Marks@Sun.COM 505*7899SJames.Marks@Sun.COM *rbuf = drctl_generate_err_resp(ermsg, rsize); 506*7899SJames.Marks@Sun.COM 5072309Srsmaeda drctlp->drc_cmd = -1; 5082309Srsmaeda drctlp->drc_flags = 0; 5092309Srsmaeda drctlp->drc_busy = NULL; 5102309Srsmaeda } 5112309Srsmaeda 5122309Srsmaeda return (rv); 5132309Srsmaeda } 5142309Srsmaeda 5152309Srsmaeda /* 5162309Srsmaeda * Complete a reconfig operation. 5172309Srsmaeda */ 5182309Srsmaeda int 5192309Srsmaeda drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count) 5202309Srsmaeda { 5212309Srsmaeda int rv; 5222309Srsmaeda int notify_cmd; 5232309Srsmaeda int flags; 524*7899SJames.Marks@Sun.COM size_t rq_size; 5252309Srsmaeda 5262309Srsmaeda mutex_enter(&drctlp->drc_lock); 5272309Srsmaeda 5282309Srsmaeda if (drctlp->drc_busy != ck) { 5292309Srsmaeda mutex_exit(&drctlp->drc_lock); 5302309Srsmaeda return (EBUSY); 5312309Srsmaeda } 5322309Srsmaeda 5332309Srsmaeda mutex_exit(&drctlp->drc_lock); 5342309Srsmaeda 5352309Srsmaeda flags = drctlp->drc_flags; 5362309Srsmaeda /* 5372309Srsmaeda * Flip the saved _REQUEST command to its corresponding 5382309Srsmaeda * _NOTIFY command. 5392309Srsmaeda */ 5402309Srsmaeda switch (drctlp->drc_cmd) { 5412309Srsmaeda case DRCTL_CPU_CONFIG_REQUEST: 5422309Srsmaeda notify_cmd = DRCTL_CPU_CONFIG_NOTIFY; 5432309Srsmaeda break; 5442309Srsmaeda 5452309Srsmaeda case DRCTL_CPU_UNCONFIG_REQUEST: 5462309Srsmaeda notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY; 5472309Srsmaeda break; 5482309Srsmaeda 5496441Sjm22469 case DRCTL_IO_UNCONFIG_REQUEST: 5506441Sjm22469 notify_cmd = DRCTL_IO_UNCONFIG_NOTIFY; 5516441Sjm22469 break; 5526441Sjm22469 5536441Sjm22469 case DRCTL_IO_CONFIG_REQUEST: 5546441Sjm22469 notify_cmd = DRCTL_IO_CONFIG_NOTIFY; 5556441Sjm22469 break; 5566441Sjm22469 5572309Srsmaeda case DRCTL_MEM_CONFIG_REQUEST: 5582309Srsmaeda case DRCTL_MEM_CONFIG_NOTIFY: 5592309Srsmaeda case DRCTL_MEM_UNCONFIG_REQUEST: 5602309Srsmaeda case DRCTL_MEM_UNCONFIG_NOTIFY: 5612309Srsmaeda default: 5622309Srsmaeda /* none of the above should have been accepted in _init */ 5632309Srsmaeda ASSERT(0); 5642309Srsmaeda cmn_err(CE_CONT, 5652309Srsmaeda "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd); 5662309Srsmaeda rv = EINVAL; 5672309Srsmaeda goto done; 5682309Srsmaeda } 5692309Srsmaeda 570*7899SJames.Marks@Sun.COM rv = drctl_config_common(notify_cmd, 571*7899SJames.Marks@Sun.COM flags, res, count, NULL, 0, &rq_size); 5722309Srsmaeda 5732309Srsmaeda done: 5742309Srsmaeda drctlp->drc_cmd = -1; 5752309Srsmaeda drctlp->drc_flags = 0; 5762309Srsmaeda drctlp->drc_busy = NULL; 5772309Srsmaeda 5782309Srsmaeda return (rv); 5792309Srsmaeda } 5802309Srsmaeda 5812309Srsmaeda static int 5822309Srsmaeda drctl_ioctl(dev_t dev, 5832309Srsmaeda int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 5842309Srsmaeda { 5852309Srsmaeda _NOTE(ARGUNUSED(dev, mode, cred_p, rval_p)) 5862309Srsmaeda 5872309Srsmaeda int rv; 5882309Srsmaeda 5892309Srsmaeda switch (cmd) { 5902309Srsmaeda case DRCTL_IOCTL_CONNECT_SERVER: 5912309Srsmaeda rv = i_drctl_ioctl(cmd, arg); 5922309Srsmaeda break; 5932309Srsmaeda default: 5942309Srsmaeda rv = ENOTSUP; 5952309Srsmaeda } 5962309Srsmaeda 5972309Srsmaeda *rval_p = (rv == 0) ? 0 : -1; 5982309Srsmaeda 5992309Srsmaeda return (rv); 6002309Srsmaeda } 6012309Srsmaeda 6022309Srsmaeda /* 6032309Srsmaeda * Accept a preformatted request from caller and send a message to 6042309Srsmaeda * the daemon. A pointer to the daemon's response buffer is passed 6052309Srsmaeda * back in obufp, its size in osize. 6062309Srsmaeda */ 6072309Srsmaeda static int 608*7899SJames.Marks@Sun.COM send_message(void *msg, size_t size, drctl_resp_t **obufp, size_t *osize) 6092309Srsmaeda { 610*7899SJames.Marks@Sun.COM drctl_resp_t *bufp; 611*7899SJames.Marks@Sun.COM drctl_rsrc_t *rsrcs; 612*7899SJames.Marks@Sun.COM size_t rsrcs_size; 6132309Srsmaeda int rv; 6142309Srsmaeda 615*7899SJames.Marks@Sun.COM rv = i_drctl_send(msg, size, (void **)&rsrcs, &rsrcs_size); 616*7899SJames.Marks@Sun.COM 617*7899SJames.Marks@Sun.COM if ((rv == 0) && ((rsrcs == NULL) ||(rsrcs_size == 0))) 618*7899SJames.Marks@Sun.COM rv = EINVAL; 619*7899SJames.Marks@Sun.COM 620*7899SJames.Marks@Sun.COM if (rv == 0) { 621*7899SJames.Marks@Sun.COM if (obufp != NULL) { 622*7899SJames.Marks@Sun.COM ASSERT(osize != NULL); 6232309Srsmaeda 624*7899SJames.Marks@Sun.COM *osize = 625*7899SJames.Marks@Sun.COM offsetof(drctl_resp_t, resp_resources) + rsrcs_size; 626*7899SJames.Marks@Sun.COM bufp = 627*7899SJames.Marks@Sun.COM kmem_alloc(*osize, KM_SLEEP); 628*7899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: alloc addr %p size %ld\n", 629*7899SJames.Marks@Sun.COM __func__, (void *)bufp, *osize); 630*7899SJames.Marks@Sun.COM bufp->resp_type = DRCTL_RESP_OK; 631*7899SJames.Marks@Sun.COM bcopy(rsrcs, bufp->resp_resources, rsrcs_size); 632*7899SJames.Marks@Sun.COM *obufp = bufp; 633*7899SJames.Marks@Sun.COM } 634*7899SJames.Marks@Sun.COM 635*7899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: free addr %p size %ld\n", 636*7899SJames.Marks@Sun.COM __func__, (void *)rsrcs, rsrcs_size); 637*7899SJames.Marks@Sun.COM kmem_free(rsrcs, rsrcs_size); 638*7899SJames.Marks@Sun.COM } 639*7899SJames.Marks@Sun.COM 640*7899SJames.Marks@Sun.COM DR_DBG_KMEM("%s:free addr %p size %ld\n", __func__, msg, size); 6412309Srsmaeda kmem_free(msg, size); 6422309Srsmaeda 6432309Srsmaeda return (rv); 6442309Srsmaeda } 6452309Srsmaeda 6462309Srsmaeda static void * 647*7899SJames.Marks@Sun.COM pack_message(int cmd, 648*7899SJames.Marks@Sun.COM int flags, int count, void *data, size_t *osize, size_t *data_size) 6492309Srsmaeda { 6506441Sjm22469 drd_msg_t *msgp = NULL; 6512309Srsmaeda size_t hdr_size = offsetof(drd_msg_t, data); 6522309Srsmaeda 6532309Srsmaeda switch (cmd) { 6542309Srsmaeda case DRCTL_CPU_CONFIG_REQUEST: 6552309Srsmaeda case DRCTL_CPU_CONFIG_NOTIFY: 6562309Srsmaeda case DRCTL_CPU_UNCONFIG_REQUEST: 6572309Srsmaeda case DRCTL_CPU_UNCONFIG_NOTIFY: 658*7899SJames.Marks@Sun.COM *data_size = count * sizeof (drctl_rsrc_t); 6596441Sjm22469 break; 6606441Sjm22469 case DRCTL_IO_CONFIG_REQUEST: 6616441Sjm22469 case DRCTL_IO_CONFIG_NOTIFY: 6626441Sjm22469 case DRCTL_IO_UNCONFIG_REQUEST: 6636441Sjm22469 case DRCTL_IO_UNCONFIG_NOTIFY: 664*7899SJames.Marks@Sun.COM *data_size = sizeof (drctl_rsrc_t) + 6656441Sjm22469 strlen(((drctl_rsrc_t *)data)->res_dev_path); 6666441Sjm22469 break; 6676441Sjm22469 default: 6686441Sjm22469 cmn_err(CE_WARN, 6696441Sjm22469 "drctl: pack_message received invalid cmd %d", cmd); 6706441Sjm22469 break; 6716441Sjm22469 } 6722309Srsmaeda 6736441Sjm22469 if (data_size) { 674*7899SJames.Marks@Sun.COM *osize = hdr_size + *data_size; 6752309Srsmaeda msgp = kmem_alloc(*osize, KM_SLEEP); 676*7899SJames.Marks@Sun.COM DR_DBG_KMEM("%s: alloc addr %p size %ld\n", 677*7899SJames.Marks@Sun.COM __func__, (void *)msgp, *osize); 6782309Srsmaeda msgp->cmd = cmd; 6792309Srsmaeda msgp->count = count; 6802309Srsmaeda msgp->flags = flags; 681*7899SJames.Marks@Sun.COM bcopy(data, msgp->data, *data_size); 6822309Srsmaeda } 6832309Srsmaeda 6842309Srsmaeda return (msgp); 6852309Srsmaeda } 686