/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Starcat Specific Glue for Safari Configurator */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int sc_gptwocfg_debug = 0; #define SC_DEBUG(level, args) if (sc_gptwocfg_debug >= level) cmn_err args typedef struct sc_gptwocfg_config { int board; struct gptwocfg_config *port_cookie; gptwo_aid_t portid; struct sc_gptwocfg_config *link; struct sc_gptwocfg_config *next; } sc_gptwocfg_config_t; static kmutex_t sc_gptwo_config_list_lock; static sc_gptwocfg_config_t *sc_gptwo_config_list; static dev_info_t *sc_find_axq_node(uint_t); static sc_gptwocfg_cookie_t sc_configure(uint_t, int); static spcd_t *sc_get_common_pcd(uint_t, uint_t); static void sc_free_common_pcd(spcd_t *); static gptwo_new_nodes_t *sc_gptwocfg_configure_axq(dev_info_t *, uint_t, int); static gptwocfg_config_t *sc_gptwocfg_unconfigure_axq(gptwocfg_config_t *); static void dump_config(sc_gptwocfg_config_t *); static void dump_pcd(spcd_t *); static uint_t sc_get_agent_id(spcd_t *, uint_t, uint_t, uint_t); static char *rsv_string(prdrsv_t); extern gptwo_new_nodes_t *gptwocfg_allocate_node_list(int); extern void gptwocfg_free_node_list(gptwo_new_nodes_t *); static uint8_t *get_memlayout(uint32_t, uint32_t *); #ifdef NO_IOSRAM int iosram_rd(uint32_t, uint32_t, uint32_t, caddr_t); #else extern int iosram_rd(uint32_t, uint32_t, uint32_t, caddr_t); #endif extern void gptwocfg_devi_attach_to_parent(dev_info_t *); /* * Module control operations */ extern struct mod_ops mod_miscops; static struct modlmisc modlmisc = { &mod_miscops, /* Type of module */ "Sun Fire 15000 gptwocfg" }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modlmisc, NULL }; int _init() { int err = 0; mutex_init(&sc_gptwo_config_list_lock, NULL, MUTEX_DRIVER, NULL); sc_gptwo_config_list = NULL; /* * CPU/PCI devices are already registered by their respective modules, * so all we need to do now is install. */ if ((err = mod_install(&modlinkage)) != 0) { SC_DEBUG(1, (CE_WARN, "sc_gptwocfg failed to load, error=%d\n", err)); mutex_destroy(&sc_gptwo_config_list_lock); } else { SC_DEBUG(1, (CE_WARN, "sc_gptwocfg has been loaded.\n")); } return (err); } int _fini(void) { mutex_destroy(&sc_gptwo_config_list_lock); return (mod_remove(&modlinkage)); } int _info(modinfop) struct modinfo *modinfop; { return (mod_info(&modlinkage, modinfop)); } static spcd_t * sc_get_common_pcd(uint_t expander, uint_t prd_slot) { spcd_t *pcd; gdcd_t *gdcd; int portid; int i, j, slot; int dimm; char *label1, *label2; SC_DEBUG(1, (CE_WARN, "sc_get_common_pcd() expander=%d prd_slot=%d\n", expander, prd_slot)); gdcd = (gdcd_t *)kmem_zalloc(sizeof (gdcd_t), KM_SLEEP); /* * Get the Starcat Specific Global DCD Structure from the golden * IOSRAM. */ if (iosram_rd(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd)) { cmn_err(CE_WARN, "sc_gptwocfg: Unable To Read GDCD " "From IOSRAM\n"); kmem_free(gdcd, sizeof (gdcd_t)); return (NULL); } if (gdcd->h.dcd_magic != GDCD_MAGIC) { cmn_err(CE_WARN, "sc_gptwocfg: GDCD Bad Magic 0x%x\n", gdcd->h.dcd_magic); kmem_free(gdcd, sizeof (gdcd_t)); return (NULL); } if (gdcd->h.dcd_version != DCD_VERSION) { cmn_err(CE_WARN, "sc_gptwocfg: GDCD Bad Version: " "GDCD Version 0x%x Expecting 0x%x\n", gdcd->h.dcd_version, DCD_VERSION); kmem_free(gdcd, sizeof (gdcd_t)); return (NULL); } pcd = (spcd_t *)kmem_zalloc(sizeof (spcd_t), KM_SLEEP); /* * Copy various information from the platform specific Port * Resource Descriptor (PRD) To the platform independent * Port Configuration Descriptor. */ pcd->spcd_magic = PCD_MAGIC; pcd->spcd_version = PCD_VERSION; pcd->spcd_ptype = gdcd->dcd_prd[expander][prd_slot].prd_ptype; pcd->spcd_ver_reg = gdcd->dcd_prd[expander][prd_slot].prd_ver_reg; if (pcd->spcd_ptype == SAFPTYPE_CPU) { /* * This will calculate the cpu speed based on the * the actual frequency ratio * interconnect frequency * converted to Mhz. */ pcd->spcd_afreq = gdcd->dcd_prd[expander][prd_slot]. prd_afreq_ratio * (uint16_t)((gdcd->dcd_intercon_freq + 500000) / 1000000); } else { /* * For non-cpu devices, just pass through the frequency * unchanged. */ pcd->spcd_afreq = gdcd->dcd_prd[expander][prd_slot].prd_afreq_ratio; } pcd->spcd_cache = gdcd->dcd_prd[expander][prd_slot].prd_cache; SC_DEBUG(1, (CE_WARN, "Safari Device Status status=0x%x\n", gdcd->dcd_prd[expander][prd_slot].prd_prsv)); /* * Fill in the entire port status. */ if (RSV_GOOD(gdcd->dcd_prd[expander][prd_slot].prd_prsv)) { pcd->spcd_prsv = SPCD_RSV_PASS; } else { pcd->spcd_prsv = SPCD_RSV_FAIL; } /* * Fill in the per agent status. */ if (gdcd->dcd_prd[expander][prd_slot].prd_agent[1] == RSV_UNKNOWN) { pcd->spcd_agent[0] = pcd->spcd_prsv; pcd->spcd_agent[1] = SPCD_RSV_FAIL; } else { for (i = 0; i < AGENTS_PER_PORT; i++) { if (RSV_GOOD( gdcd->dcd_prd[expander][prd_slot].prd_agent[i])) pcd->spcd_agent[i] = SPCD_RSV_PASS; else pcd->spcd_agent[i] = SPCD_RSV_FAIL; } } /* * If this is a CPU device calculate the cpuid for it. For Starcat * the cpuid is in the following format. * * EEEEEPPAPP * * where: EEEEE is the expander * PP_PP is the portid * __A__ is the sub-agent identifier. */ if (pcd->spcd_ptype == SAFPTYPE_CPU) { for (i = 0; i < AGENTS_PER_PORT; i++) { switch (prd_slot) { case 0: case 1: case 2: case 3: portid = (expander << 5) | prd_slot; break; case 4: /* Maxcat */ portid = (expander << 5) | 8; break; case 5: /* Maxcat */ portid = (expander << 5) | 9; break; default: cmn_err(CE_WARN, "sc_gptwocfg: invalid " "prd_slot=%d\n", prd_slot); } pcd->spcd_cpuid[i] = (i << 2) | portid; } } /* * Starcat does not have ports with UPA devices so * spcd_upadev structure will not be filled in. */ /* * Fill in IO Bus Status */ for (i = 0; i < IOBUS_PER_PORT; i++) { SC_DEBUG(1, (CE_WARN, " IO Bus Status " "bus=%d status=0x%x\n", i, gdcd->dcd_prd[expander][prd_slot].prd_iobus_rsv[i])); if (RSV_GOOD( gdcd->dcd_prd[expander][prd_slot].prd_iobus_rsv[i])) { pcd->spcd_iobus_rsv[i] = SPCD_RSV_PASS; } else { pcd->spcd_iobus_rsv[i] = SPCD_RSV_FAIL; } for (j = 0; j < IOCARD_PER_BUS; j++) pcd->spcd_iocard_rsv[i][j] = SPCD_RSV_FAIL; /* * Fill in IO Card Status */ for (j = 0; j < IOCARD_PER_BUS; j++) { SC_DEBUG(1, (CE_WARN, " Card Status bus=%d " "slot=%d status=0x%x\n", i, j, gdcd->dcd_prd[expander][prd_slot]. prd_iocard_rsv[i][j])); if (j == 1) continue; if (j == 0) slot = 1; else slot = j; /* * If POST marked the card as GOOD or if the slot * is empty, we want to probe for the device. */ if (RSV_GOOD(gdcd->dcd_prd[expander][prd_slot]. prd_iocard_rsv[i][j]) || (gdcd->dcd_prd[expander][prd_slot]. prd_iocard_rsv[i][j] == RSV_MISS) || (gdcd->dcd_prd[expander][prd_slot]. prd_iocard_rsv[i][j] == RSV_EMPTY_CASSETTE)) pcd->spcd_iocard_rsv[i][slot] = SPCD_RSV_PASS; else pcd->spcd_iocard_rsv[i][slot] = SPCD_RSV_FAIL; } } /* * Fill in WIC Link Status */ for (i = 0; i < LINKS_PER_PORT; i++) { if (RSV_GOOD( gdcd->dcd_prd[expander][prd_slot].prd_wic_links[i])) { pcd->spcd_wic_links[i] = SPCD_RSV_PASS; } else { pcd->spcd_wic_links[i] = SPCD_RSV_FAIL; } } /* * Get data for the "bank-status" property. */ pcd->sprd_bank_rsv[0] = rsv_string(gdcd->dcd_prd[expander][prd_slot].prd_bank_rsv[0][0]); pcd->sprd_bank_rsv[1] = rsv_string(gdcd->dcd_prd[expander][prd_slot].prd_bank_rsv[1][0]); pcd->sprd_bank_rsv[2] = rsv_string(gdcd->dcd_prd[expander][prd_slot].prd_bank_rsv[0][1]); pcd->sprd_bank_rsv[3] = rsv_string(gdcd->dcd_prd[expander][prd_slot].prd_bank_rsv[1][1]); dimm = 0; for (i = 0; i < PMBANKS_PER_PORT; i++) { for (j = 0; j < DIMMS_PER_PMBANK; j++) { if (dimm < MAX_DIMMS_PER_PORT) { pcd->sprd_dimm[dimm] = rsv_string( gdcd->dcd_prd[expander][prd_slot]. prd_dimm[i][j]); dimm++; } } } /* * Get data for the "ecache-dimm-label" property. * * Right now it is hardcoded, but we should eventually get this * from the SC. */ label1 = NULL; label2 = NULL; switch (prd_slot) { case 0: label1 = "4400"; label2 = "4300"; break; case 1: label1 = "5400"; label2 = "5300"; break; case 2: label1 = "6400"; label2 = "6300"; break; case 3: label1 = "7400"; label2 = "7300"; break; /* * Maxcat labels. */ case 4: label1 = "6400"; label2 = "6300"; break; case 5: label1 = "7400"; label2 = "7300"; break; } i = 0; if (label1) { pcd->sprd_ecache_dimm_label[i] = kmem_alloc(strlen(label1) + 1, KM_SLEEP); (void) strcpy(pcd->sprd_ecache_dimm_label[i], label1); i++; } if (label2) { pcd->sprd_ecache_dimm_label[i] = kmem_alloc(strlen(label2) + 1, KM_SLEEP); (void) strcpy(pcd->sprd_ecache_dimm_label[i], label2); i++; } kmem_free(gdcd, sizeof (gdcd_t)); #ifdef DEBUG dump_pcd(pcd); #endif return (pcd); } void sc_free_common_pcd(spcd_t *pcd) { int i; SC_DEBUG(1, (CE_WARN, "sc_free_common_pcd pcd=%p\n", (void *)pcd)); if (pcd->memory_layout && pcd->memory_layout_size) { SC_DEBUG(1, (CE_WARN, "sc_free_common_pcd: memory_layout %p " "size=%x", (void *)pcd->memory_layout, pcd->memory_layout_size)); kmem_free(pcd->memory_layout, pcd->memory_layout_size); } for (i = 0; i < MAX_BANKS_PER_PORT; i++) { if (pcd->sprd_bank_rsv[i]) { kmem_free(pcd->sprd_bank_rsv[i], strlen(pcd->sprd_bank_rsv[i]) + 1); pcd->sprd_bank_rsv[i] = NULL; } } for (i = 0; i < MAX_DIMMS_PER_PORT; i++) { if (pcd->sprd_dimm[i]) { kmem_free(pcd->sprd_dimm[i], strlen(pcd->sprd_dimm[i]) + 1); pcd->sprd_dimm[i] = NULL; } if (pcd->sprd_ecache_dimm_label[i]) { kmem_free(pcd->sprd_ecache_dimm_label[i], strlen(pcd->sprd_ecache_dimm_label[i]) + 1); pcd->sprd_ecache_dimm_label[i] = NULL; } } kmem_free(pcd, sizeof (spcd_t)); } sc_gptwocfg_cookie_t sc_probe_board(uint_t board) { return (sc_configure(board, 1)); } static sc_gptwocfg_cookie_t sc_configure(uint_t board, int create_nodes) { spcd_t *pcd; dev_info_t *ap, *axq_dip; uint_t agent_id; uint_t prd_slot, prd_slot_start, prd_slot_end; uint_t expander, slot; gptwo_new_nodes_t *new_nodes; gptwocfg_config_t *port_cookie; struct sc_gptwocfg_config *board_config, *last, *new; int created_node = 0; uint32_t size; SC_DEBUG(1, (CE_WARN, "sc_configure: board=%d, create_nodes=%d\n", board, create_nodes)); if (board > 35) { SC_DEBUG(1, (CE_WARN, "sc_gptwocfg - probe_board - " "invalid board 0x%x\n", board)); return (NULL); } slot = board & 1; /* Extract Slot Number */ expander = board >> 1; /* Extract Expander Number */ SC_DEBUG(1, (CE_WARN, "sc_configure: exp=0x%x slot=0x%x\n", expander, slot)); /* * Get the Attachment Point. For Starcat the parent of all * Safari children is root node. */ ap = ddi_root_node(); /* * Get the agent id of the AXQ. */ agent_id = (expander << 5) | 0x1e | slot; /* * Look to see if the board is already configured by searching for * its AXQ. */ if (create_nodes && (axq_dip = sc_find_axq_node(agent_id))) { ddi_release_devi(axq_dip); cmn_err(CE_WARN, "Board %d AXQ is already configured\n", board); return (NULL); } /* * Probe AXQ first */ SC_DEBUG(1, (CE_WARN, "sc_configure: Probing AXQ exp=0x%x brd=0x%x\n", expander, slot)); /* * The generic gptwocfg does not support the AXQ, so we need * to configure it. The AXQ branch is returned held. */ new_nodes = sc_gptwocfg_configure_axq(ap, agent_id, create_nodes); if (new_nodes == NULL) { SC_DEBUG(1, (CE_WARN, "sc_configure: Can not probe AXQ\n")); return (NULL); } port_cookie = kmem_zalloc(sizeof (gptwocfg_config_t), KM_SLEEP); /* * Build a cookie for the AXQ. */ port_cookie->gptwo_ap = ap; port_cookie->gptwo_portid = agent_id; port_cookie->gptwo_nodes = new_nodes; board_config = kmem_zalloc(sizeof (sc_gptwocfg_config_t), KM_SLEEP); board_config->port_cookie = port_cookie; board_config->board = board; board_config->portid = agent_id; board_config->link = NULL; last = board_config; mutex_enter(&sc_gptwo_config_list_lock); board_config->next = sc_gptwo_config_list; sc_gptwo_config_list = board_config; mutex_exit(&sc_gptwo_config_list_lock); SC_DEBUG(1, (CE_WARN, "sc_configure: AXQ Probing Complete. " "%d nodes added\n", new_nodes->gptwo_number_of_nodes)); /* * Determine the starting ending slots of the PRD array. */ switch (slot) { case 0: /* Full Bandwidth Slot */ prd_slot_start = 0; prd_slot_end = 3; break; case 1: /* Half Bandwidth Slot */ prd_slot_start = 4; prd_slot_end = 5; break; default: SC_DEBUG(1, (CE_WARN, "Unknown Board Address - " "Can not probe\n")); return (board_config); } /* * For each valid PRD entry, determine the agent id which is based * on what type of device is described by the slot, and then * call the safari configurator. */ for (prd_slot = prd_slot_start; prd_slot <= prd_slot_end; prd_slot++) { pcd = sc_get_common_pcd(expander, prd_slot); if (pcd == NULL) { /* * We can not get a PCD for this port so skip it. */ cmn_err(CE_WARN, "sc_gptwocfg: Can not get PCD " "expander 0x%x prd slot 0x%x\n", expander, prd_slot); return (board_config); } /* * Only configure good devices. */ if (pcd->spcd_prsv == SPCD_RSV_PASS) { /* * Determine the agent id. */ agent_id = sc_get_agent_id( pcd, expander, slot, prd_slot); pcd->memory_layout = get_memlayout(agent_id, &size); pcd->memory_layout_size = size; /* * Call Platform Independent gptwo configurator to * create node and properties. */ if (create_nodes) { port_cookie = gptwocfg_configure(ap, pcd, agent_id); if (port_cookie) created_node++; } new = kmem_zalloc (sizeof (sc_gptwocfg_config_t), KM_SLEEP); /* * XXX Shouldn't port_cookie be NULL if * !create_nodes ? */ new->port_cookie = port_cookie; new->portid = agent_id; new->link = NULL; last->link = new; last = new; } else { SC_DEBUG(1, (CE_WARN, "sc_configure: Bad Agent " "Exp=0x%x PRD Slot=0x%x prsv Status=0x%x\n", expander, prd_slot, pcd->spcd_prsv)); } sc_free_common_pcd(pcd); } /* for loop */ dump_config(board_config); if (create_nodes && !created_node) { SC_DEBUG(1, (CE_WARN, "sc_configure: GPTWO Devices failed " "to configure - unprobing board %d\n", board)); board_config = sc_unprobe_board(board); } SC_DEBUG(1, (CE_WARN, "sc_configure: Returning 0x%p\n", (void *)board_config)); return (board_config); } sc_gptwocfg_cookie_t sc_unprobe_board(uint_t board) { sc_gptwocfg_config_t *board_config, *axq_config, *prior_config; gptwocfg_cookie_t port_cookie; SC_DEBUG(1, (CE_WARN, "sc_unprobe_board: board=%d\n", board)); if (board > 35) { SC_DEBUG(1, (CE_WARN, "sc_unprobe_board: " "invalid board 0x%x\n", board)); return (NULL); } mutex_enter(&sc_gptwo_config_list_lock); board_config = sc_gptwo_config_list; while (board_config != NULL) { if (board_config->board == board) { break; } board_config = board_config->next; } mutex_exit(&sc_gptwo_config_list_lock); if (board_config == NULL) { SC_DEBUG(1, (CE_WARN, "sc_unprobe_board: No " "config structure board=0x%x\n", board)); /* * Configure the board without creating nodes. */ board_config = sc_configure(board, 0); if (board_config == NULL) { cmn_err(CE_WARN, "sc_gptwocfg: sc_unprobe_board: " "Unable to unconfigure board %d - board is not " "configured\n", board); return (NULL); } } axq_config = board_config; /* * Walk the link of ports on this board and unconfigure them. * Save the AXQ for last. */ while (board_config->link != NULL) { prior_config = board_config; board_config = board_config->link; SC_DEBUG(1, (CE_WARN, "sc_unprobe_board: " "calling gptwocfg_unconfigure(ap=0x%p portid=0x%x)\n", (void *)ddi_root_node(), board_config->portid)); port_cookie = gptwocfg_unconfigure(ddi_root_node(), board_config->portid); SC_DEBUG(1, (CE_WARN, "sc_unprobe_board: " "gptwocfg_unconfigure returned cookie=0x%p\n", port_cookie)); if (port_cookie == NULL) { /* * Can be removed from list. */ prior_config->link = board_config->link; kmem_free(board_config, sizeof (sc_gptwocfg_config_t)); board_config = prior_config; } else { board_config->port_cookie = port_cookie; } } if (axq_config->link == NULL) { /* * If all the other Safari devices have been successfully * unconfigured, then the AXQ can be unconfigured. */ axq_config->port_cookie = sc_gptwocfg_unconfigure_axq(axq_config->port_cookie); if (axq_config->port_cookie == NULL) { /* * If the AXQ was successfully unconfigured, then * the board is removed from the configured list. */ mutex_enter(&sc_gptwo_config_list_lock); if (sc_gptwo_config_list == axq_config) { sc_gptwo_config_list = axq_config->next; } else { board_config = sc_gptwo_config_list; while (board_config->next != axq_config) { board_config = board_config->next; } board_config->next = axq_config->next; } mutex_exit(&sc_gptwo_config_list_lock); kmem_free(axq_config, sizeof (sc_gptwocfg_config_t)); axq_config = NULL; } } dump_config(axq_config); return (axq_config); } int sc_next_node(sc_gptwocfg_cookie_t c, dev_info_t *previous, dev_info_t **next) { dev_info_t *dip; sc_gptwocfg_config_t *cookie; SC_DEBUG(1, (CE_WARN, "sccfg: sccfg_next_node" "(c=0x%p, previous=0x%p, next=0x%p)\n", (void *)c, (void *)previous, (void *)next)); cookie = (sc_gptwocfg_config_t *)c; if (cookie == NULL) { cmn_err(CE_WARN, "sccfg: sccfg_next_node - " "Invalid Cookie\n"); return (0); } if (previous == NULL) { /* * Start with the AXQ node. */ if (gptwocfg_next_node(cookie->port_cookie, NULL, &dip)) { *next = dip; return (1); } else { return (0); } } while (cookie != NULL) { if (gptwocfg_next_node(cookie->port_cookie, previous, &dip)) { if ((dip == NULL) && (cookie->link == NULL)) { *next = NULL; return (1); } if (dip != NULL) { *next = dip; return (1); } /* dip == NULL */ previous = NULL; } cookie = cookie->link; } return (0); } static dev_info_t * sc_find_axq_node(uint_t axq_id) { char *name; int size; gptwo_regspec_t *reg; dev_info_t *dip; uint_t id; int circ; SC_DEBUG(1, (CE_CONT, "sc_find_axq_node: id=0x%x\n", axq_id)); /* * Hold root node busy to walk its child list */ ndi_devi_enter(ddi_root_node(), &circ); dip = ddi_get_child(ddi_root_node()); while (dip != NULL) { SC_DEBUG(1, (CE_CONT, "Searching dip=0x%p for our AXQ\n", (void *)dip)); if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "name", (caddr_t)&name, &size) != DDI_PROP_SUCCESS) { /* * This node does not have a name property. */ SC_DEBUG(1, (CE_CONT, "dip=0x%p does not have a " "'name' property\n", (void *)dip)); dip = ddi_get_next_sibling(dip); continue; } SC_DEBUG(1, (CE_CONT, "dip=0x%p name=%s\n", (void *)dip, name)); if (strcmp(name, "address-extender-queue")) { /* * This node is not a AXQ node. */ SC_DEBUG(1, (CE_CONT, "dip=0x%p is not an AXQ " "node\n", (void *)dip)); kmem_free(name, size); dip = ddi_get_next_sibling(dip); continue; } kmem_free(name, size); if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®, &size) != DDI_PROP_SUCCESS) { /* * This AXQ node does not have a reg property. */ SC_DEBUG(1, (CE_CONT, "dip=0x%p (AXQ Node) does " "have a 'reg' property\n", (void *)dip)); dip = ddi_get_next_sibling(dip); continue; } id = ((reg[0].gptwo_phys_hi & 1) << 9) | ((reg[0].gptwo_phys_low & 0xff800000) >> 23); kmem_free(reg, size); if (axq_id != id) { /* * This is the wrong AXQ node. */ SC_DEBUG(1, (CE_CONT, "dip=0x%p Wrong node id=0x%x\n", (void *)dip, id)); dip = ddi_get_next_sibling(dip); continue; } /* * The correct AXQ node was found. */ SC_DEBUG(1, (CE_CONT, "dip=0x%p Found AXQ Node\n", (void *)dip)); ndi_hold_devi(dip); break; } ndi_devi_exit(ddi_root_node(), circ); SC_DEBUG(1, (CE_CONT, "sc_find_axq_node: Returning 0x%p\n", (void *)dip)); return (dip); } struct axq_arg { uint_t id; dev_info_t *axq_dip; }; /*ARGSUSED*/ static int axq_set_prop(dev_info_t *axq_dip, void *arg, uint_t flags) { struct axq_arg *aqp = (struct axq_arg *)arg; gptwo_regspec_t reg[2]; uint_t id; ASSERT(aqp); id = aqp->id; if (ndi_prop_update_string(DDI_DEV_T_NONE, axq_dip, "name", "address-extender-queue") != DDI_SUCCESS) { SC_DEBUG(1, (CE_CONT, "gptwocfg_configure_pci: failed " "to create name property\n")); return (DDI_WALK_ERROR); } if (ndi_prop_update_string(DDI_DEV_T_NONE, axq_dip, "device_type", "address-extender-queue") != DDI_SUCCESS) { SC_DEBUG(1, (CE_CONT, "gptwocfg_configure_pci: failed " "to create device_type property\n")); return (DDI_WALK_ERROR); } if (ndi_prop_update_string(DDI_DEV_T_NONE, axq_dip, "compatible", "SUNW,axq") != DDI_SUCCESS) { SC_DEBUG(1, (CE_CONT, "sc_gptwocfg: failed " "to create compatible property\n")); return (DDI_WALK_ERROR); } if (ndi_prop_update_int(DDI_DEV_T_NONE, axq_dip, "portid", id) != DDI_SUCCESS) { SC_DEBUG(1, (CE_CONT, "gptwocfg_configure_pci: failed " "to create portid property\n")); return (DDI_WALK_ERROR); } reg[0].gptwo_phys_hi = 0x400 | (id >> 9); reg[0].gptwo_phys_low = (id << 23); reg[0].gptwo_size_hi = 0; reg[0].gptwo_size_low = 0x520; reg[1].gptwo_phys_hi = 0x401; reg[1].gptwo_phys_low = 0xf0000000; reg[1].gptwo_size_hi = 0; reg[1].gptwo_size_low = 0x520; if (ndi_prop_update_int_array(DDI_DEV_T_NONE, axq_dip, "reg", (int *)®, (sizeof (gptwo_regspec_t) * 2)/sizeof (int)) != DDI_SUCCESS) { SC_DEBUG(1, (CE_CONT, "gptwocfg_configure_pci: failed " "to create reg property\n")); return (DDI_WALK_ERROR); } return (DDI_WALK_TERMINATE); } /*ARGSUSED*/ static void get_axq_dip(dev_info_t *rdip, void *arg, uint_t flags) { struct axq_arg *aqp = (struct axq_arg *)arg; ASSERT(aqp); aqp->axq_dip = rdip; } static gptwo_new_nodes_t * sc_gptwocfg_configure_axq(dev_info_t *ap, uint_t id, int create_nodes) { struct axq_arg arg = {0}; devi_branch_t b = {0}; dev_info_t *axq_dip, *fdip = NULL; gptwo_new_nodes_t *new_nodes = NULL; int rv; SC_DEBUG(1, (CE_CONT, "gptwocfg_configure_axq: id=0x%x " "create_nodes=%d\n", id, create_nodes)); if (!create_nodes) { axq_dip = sc_find_axq_node(id); if (axq_dip) { new_nodes = gptwocfg_allocate_node_list(1); new_nodes->gptwo_nodes[0] = axq_dip; ASSERT(!e_ddi_branch_held(axq_dip)); e_ddi_branch_hold(axq_dip); /* * Release hold from sc_find_axq_node() */ ddi_release_devi(axq_dip); } SC_DEBUG(1, (CE_CONT, "gptwocfg_configure_axq: " "Returning 0x%p\n", (void *)new_nodes)); return (new_nodes); } arg.id = id; arg.axq_dip = NULL; b.arg = &arg; b.type = DEVI_BRANCH_SID; b.create.sid_branch_create = axq_set_prop; b.devi_branch_callback = get_axq_dip; rv = e_ddi_branch_create(ap, &b, &fdip, DEVI_BRANCH_CONFIGURE); if (rv != 0) { char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); /* * If non-NULL, fdip is held and must be released. */ if (fdip != NULL) { (void) ddi_pathname(fdip, path); ddi_release_devi(fdip); } else { (void) ddi_pathname(ap, path); } SC_DEBUG(1, (CE_WARN, "e_ddi_branch_create failed: " "path=%s, dip=%p, rv=%d", path, fdip ? (void *)fdip : (void *)ap, rv)); kmem_free(path, MAXPATHLEN); return (NULL); } axq_dip = arg.axq_dip; new_nodes = gptwocfg_allocate_node_list(1); new_nodes->gptwo_nodes[0] = axq_dip; return (new_nodes); } static gptwocfg_config_t * sc_gptwocfg_unconfigure_axq(gptwocfg_config_t *config) { int i; int failure = 0; dev_info_t *saf_dip; if (config == NULL) { cmn_err(CE_WARN, "sc_gptwocfg: sc_gptwocfg_unconfigure_axq: " "Invalid AXQ\n"); return (NULL); } for (i = 0; i < config->gptwo_nodes->gptwo_number_of_nodes; i++) { int rv; dev_info_t *fdip = NULL; saf_dip = config->gptwo_nodes->gptwo_nodes[i]; ASSERT(e_ddi_branch_held(saf_dip)); rv = e_ddi_branch_destroy(saf_dip, &fdip, 0); if (rv != 0) { char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); /* * If non-NULL, fdip is held and must be released. */ if (fdip != NULL) { (void) ddi_pathname(fdip, path); ddi_release_devi(fdip); } else { (void) ddi_pathname(saf_dip, path); } cmn_err(CE_CONT, "AXQ node removal failed: " "path=%s, dip=%p, rv=%d\n", path, fdip ? (void *)fdip : (void *)saf_dip, rv); kmem_free(path, MAXPATHLEN); failure = 1; } else { config->gptwo_nodes->gptwo_nodes[i] = NULL; } } if (!failure) { gptwocfg_free_node_list(config->gptwo_nodes); kmem_free(config, sizeof (gptwocfg_config_t)); config = NULL; } return (config); } static uint_t sc_get_agent_id(spcd_t *pcd, uint_t expander, uint_t slot, uint_t prd_slot) { uint_t agent_id; switch (pcd->spcd_ptype) { case SAFPTYPE_CPU: if (slot == 0) { agent_id = prd_slot; } else { if (prd_slot == 4) { agent_id = 8; } else { agent_id = 9; } } break; case SAFPTYPE_sPCI: case SAFPTYPE_cPCI: case SAFPTYPE_PCIX: if (prd_slot == 4) { agent_id = 0x1c; } else { agent_id = 0x1d; } break; case SAFPTYPE_WCI: agent_id = 0x1d; break; default: cmn_err(CE_WARN, "sc_gptwocfg: Invalid Safari Port " "Type 0x%x Slot 0x%x\n", pcd->spcd_ptype, prd_slot); } /* switch */ agent_id |= (expander << 5); SC_DEBUG(1, (CE_CONT, "sc_get_agent_id(pcd=0x%p, expander=0x%x, " "prd_slot=0x%x) Returning agent_id=0x%x\n", (void *)pcd, expander, prd_slot, agent_id)); return (agent_id); } static void dump_config(sc_gptwocfg_config_t *board_config) { gptwocfg_config_t *port; SC_DEBUG(1, (CE_CONT, "dump_config 0x%p", (void *)board_config)); while (board_config != NULL) { SC_DEBUG(1, (CE_CONT, "************* 0x%p ************\n", (void *)board_config)); SC_DEBUG(1, (CE_CONT, "port_cookie - 0x%p\n", (void *)board_config->port_cookie)); port = board_config->port_cookie; if (port) { SC_DEBUG(1, (CE_CONT, " ap - 0x%p\n", (void *)port->gptwo_ap)); SC_DEBUG(1, (CE_CONT, " portid - 0x%x\n", port->gptwo_portid)); } SC_DEBUG(1, (CE_CONT, "portid - 0x%x\n", board_config->portid)); SC_DEBUG(1, (CE_CONT, "board - 0x%x\n", board_config->board)); SC_DEBUG(1, (CE_CONT, "link - 0x%p\n", (void *)board_config->link)); SC_DEBUG(1, (CE_CONT, "next - 0x%p\n", (void *)board_config->next)); board_config = board_config->link; } } static void dump_pcd(spcd_t *pcd) { int i; SC_DEBUG(1, (CE_CONT, "dump_pcd 0x%p", (void *)pcd)); SC_DEBUG(1, (CE_CONT, " magic - 0x%x\n", pcd->spcd_magic)); SC_DEBUG(1, (CE_CONT, " version - 0x%x\n", pcd->spcd_version)); SC_DEBUG(1, (CE_CONT, " ver.reg - 0x%lx\n", pcd->spcd_ver_reg)); SC_DEBUG(1, (CE_CONT, " afreq - %d\n", pcd->spcd_afreq)); switch (pcd->spcd_ptype) { case SAFPTYPE_CPU: SC_DEBUG(1, (CE_CONT, " ptype - CPU\n")); break; case SAFPTYPE_sPCI: SC_DEBUG(1, (CE_CONT, " ptype - sPCI\n")); break; case SAFPTYPE_cPCI: SC_DEBUG(1, (CE_CONT, " ptype - cPCI\n")); break; case SAFPTYPE_PCIX: SC_DEBUG(1, (CE_CONT, " ptype - sPCI+\n")); break; case SAFPTYPE_WCI: SC_DEBUG(1, (CE_CONT, " ptype - WIC\n")); break; default: SC_DEBUG(1, (CE_CONT, " ptype - 0x%x\n", pcd->spcd_ptype)); break; } SC_DEBUG(1, (CE_CONT, " cache - %d\n", pcd->spcd_cache)); if (pcd->spcd_prsv == SPCD_RSV_PASS) { SC_DEBUG(1, (CE_CONT, " prsv - SPCD_RSV_PASS\n")); } else { SC_DEBUG(1, (CE_CONT, " prsv - 0x%x (FAIL)\n", pcd->spcd_prsv)); } for (i = 0; i < AGENTS_PER_PORT; i++) { if (pcd->spcd_agent[i] == SPCD_RSV_PASS) { SC_DEBUG(1, (CE_CONT, " agent[%d] " "- SPCD_RSV_PASS\n", i)); } else { SC_DEBUG(1, (CE_CONT, " agent[%d] " "- 0x%x (FAIL)\n", i, pcd->spcd_agent[i])); } } if (pcd->spcd_ptype == SAFPTYPE_CPU) { for (i = 0; i < AGENTS_PER_PORT; i++) { SC_DEBUG(1, (CE_CONT, " cpuid[%d] - 0x%x\n", i, pcd->spcd_cpuid[i])); } } SC_DEBUG(1, (CE_CONT, " Banks\n")); for (i = 0; i < MAX_BANKS_PER_PORT; i++) { if (pcd->sprd_bank_rsv[i]) { SC_DEBUG(1, (CE_CONT, " %d %s\n", i, pcd->sprd_bank_rsv[i])); } } SC_DEBUG(1, (CE_CONT, " Dimms\n")); for (i = 0; i < MAX_DIMMS_PER_PORT; i++) { if (pcd->sprd_dimm[i]) { SC_DEBUG(1, (CE_CONT, " %d %s\n", i, pcd->sprd_dimm[i])); } } SC_DEBUG(1, (CE_CONT, " Ecache Dimm Labels\n")); for (i = 0; i < MAX_DIMMS_PER_PORT; i++) { if (pcd->sprd_ecache_dimm_label[i]) { SC_DEBUG(1, (CE_CONT, " %d %s\n", i, pcd->sprd_ecache_dimm_label[i])); } } } typedef struct { char Jnumber[8][8]; uint8_t sym_flag; uint8_t d_dimmtable[144]; uint8_t d_pintable[576]; }m_layout; /* * Use 2 bits to represent each bit at a cache line. The table * is in big endian order, i.e. * dimmtable[0], ... , dimmtable[143] * Q0:data-bits[127 126 125 124], ... , MtagEcc[3 2 1 0] * . * . * Q3:data-bits[127 126 125 124], ... , MtagEcc[3 2 1 0] */ uint8_t J_dimm_pinTable[] = { /* Jnumber */ /* 0 */ 0x4a, 0x31, 0x33, 0x33, 0x30, 0x30, 0x00, 0x00, /* 1 */ 0x4a, 0x31, 0x33, 0x34, 0x30, 0x30, 0x00, 0x00, /* 2 */ 0x4a, 0x31, 0x33, 0x35, 0x30, 0x30, 0x00, 0x00, /* 3 */ 0x4a, 0x31, 0x33, 0x36, 0x30, 0x30, 0x00, 0x00, /* 4 */ 0x4a, 0x31, 0x33, 0x33, 0x30, 0x31, 0x00, 0x00, /* 5 */ 0x4a, 0x31, 0x33, 0x34, 0x30, 0x31, 0x00, 0x00, /* 6 */ 0x4a, 0x31, 0x33, 0x35, 0x30, 0x31, 0x00, 0x00, /* 7 */ 0x4a, 0x31, 0x33, 0x36, 0x30, 0x31, 0x00, 0x00, /* flag */ 0x01, /* -- Q0 -- */ /* 0 */ 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa, 0xff, /* 1 */ 0x00, 0xaa, 0xff, 0x00, 0x56, 0xaf, 0x00, 0x55, /* 2 */ 0xaa, 0x55, 0xaf, 0xc0, 0x55, 0xaa, 0xff, 0x00, /* 3 */ 0x55, 0xff, 0x00, 0x55, 0xaa, 0xff, 0x6d, 0x80, /* 4 */ 0xe7, 0xe3, 0x9b, 0x1b, /* -- Q1 -- */ /* 0 */ 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa, 0xff, /* 1 */ 0x00, 0xaa, 0xff, 0x00, 0x56, 0xaf, 0x00, 0x55, /* 2 */ 0xaa, 0x55, 0xaf, 0xc0, 0x55, 0xaa, 0xff, 0x00, /* 3 */ 0x55, 0xff, 0x00, 0x55, 0xaa, 0xff, 0x6d, 0x80, /* 4 */ 0xe7, 0xe3, 0x9b, 0x1b, /* -- Q2 -- */ /* 0 */ 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa, 0xff, /* 1 */ 0x00, 0xaa, 0xff, 0x00, 0x56, 0xaf, 0x00, 0x55, /* 2 */ 0xaa, 0x55, 0xaf, 0xc0, 0x55, 0xaa, 0xff, 0x00, /* 3 */ 0x55, 0xff, 0x00, 0x55, 0xaa, 0xff, 0x6d, 0x80, /* 4 */ 0xe7, 0xe3, 0x9b, 0x1b, /* -- Q3 -- */ /* 0 */ 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa, 0xff, /* 1 */ 0x00, 0xaa, 0xff, 0x00, 0x56, 0xaf, 0x00, 0x55, /* 2 */ 0xaa, 0x55, 0xaf, 0xc0, 0x55, 0xaa, 0xff, 0x00, /* 3 */ 0x55, 0xff, 0x00, 0x55, 0xaa, 0xff, 0x6d, 0x80, /* 4 */ 0xe7, 0xe3, 0x9b, 0x1b, /* * In the following order * pintable[0], ..., pintable[575] * Quadword3, Quadword2, Quadword1, Quadword0 * MtagEcc, Mtag, Ecc, Data */ /* -- Q3 -- */ /* 0 */ 227, 227, 227, 227, 111, 111, 111, 22, /* 1 */ 22, 32, 138, 222, 81, 117, 117, 117, /* 2 */ 111, 222, 106, 222, 222, 106, 106, 106, /* 3 */ 217, 101, 212, 96, 217, 101, 212, 96, /* 4 */ 217, 101, 212, 96, 217, 101, 212, 96, /* 5 */ 207, 91, 202, 86, 187, 71, 158, 42, /* 6 */ 187, 71, 158, 42, 153, 37, 148, 32, /* 7 */ 153, 37, 148, 32, 153, 37, 148, 32, /* 8 */ 153, 37, 148, 143, 27, 138, 143, 27, /* 9 */ 143, 27, 138, 22, 207, 91, 202, 86, /* 10 */ 207, 91, 202, 86, 207, 91, 202, 86, /* 11 */ 192, 76, 81, 192, 76, 81, 192, 76, /* 12 */ 197, 81, 192, 76, 187, 71, 158, 42, /* 13 */ 187, 71, 158, 42, 143, 27, 138, 22, /* 14 */ 133, 17, 128, 12, 133, 17, 128, 12, /* 15 */ 133, 17, 128, 12, 133, 17, 128, 12, /* 16 */ 123, 07, 118, 2, 123, 07, 118, 2, /* 17 */ 123, 07, 118, 2, 123, 07, 118, 2, /* -- Q2 -- */ /* 0 */ 228, 228, 228, 228, 112, 112, 112, 23, /* 1 */ 23, 33, 139, 223, 82, 118, 118, 118, /* 2 */ 112, 223, 107, 223, 223, 107, 107, 107, /* 3 */ 218, 102, 213, 97, 218, 102, 213, 97, /* 4 */ 218, 102, 213, 97, 218, 102, 213, 97, /* 5 */ 208, 92, 203, 87, 188, 72, 159, 43, /* 6 */ 188, 72, 159, 43, 154, 38, 149, 33, /* 7 */ 154, 38, 149, 33, 154, 38, 149, 33, /* 8 */ 154, 38, 149, 144, 28, 139, 144, 28, /* 9 */ 144, 28, 139, 23, 208, 92, 203, 87, /* 10 */ 208, 92, 203, 87, 208, 92, 203, 87, /* 11 */ 193, 77, 82, 193, 77, 82, 193, 77, /* 12 */ 198, 82, 193, 77, 188, 72, 159, 43, /* 13 */ 188, 72, 159, 43, 144, 28, 139, 23, /* 14 */ 134, 18, 129, 13, 134, 18, 129, 13, /* 15 */ 134, 18, 129, 13, 134, 18, 129, 13, /* 16 */ 124, 8, 119, 3, 124, 8, 119, 3, /* 17 */ 124, 8, 119, 3, 124, 8, 119, 3, /* -- Q1 -- */ /* 0 */ 229, 229, 229, 229, 113, 113, 113, 24, /* 1 */ 24, 34, 140, 224, 83, 119, 119, 119, /* 2 */ 113, 224, 108, 224, 224, 108, 108, 108, /* 3 */ 219, 103, 214, 98, 219, 103, 214, 98, /* 4 */ 219, 103, 214, 98, 219, 103, 214, 98, /* 5 */ 209, 93, 204, 88, 189, 73, 160, 44, /* 6 */ 189, 73, 160, 44, 155, 39, 150, 34, /* 7 */ 155, 39, 150, 34, 155, 39, 150, 34, /* 8 */ 155, 39, 150, 145, 29, 140, 145, 29, /* 9 */ 145, 29, 140, 24, 209, 93, 204, 88, /* 10 */ 209, 93, 204, 88, 209, 93, 204, 88, /* 11 */ 194, 78, 83, 194, 78, 83, 194, 78, /* 12 */ 199, 83, 194, 78, 189, 73, 160, 44, /* 13 */ 189, 73, 160, 44, 145, 29, 140, 24, /* 14 */ 135, 19, 130, 14, 135, 19, 130, 14, /* 15 */ 135, 19, 130, 14, 135, 19, 130, 14, /* 16 */ 125, 9, 120, 4, 125, 9, 120, 4, /* 17 */ 125, 9, 120, 4, 125, 9, 120, 4, /* -- Q0 -- */ /* 0 */ 230, 230, 230, 230, 114, 114, 114, 25, /* 1 */ 25, 35, 141, 225, 84, 200, 200, 200, /* 2 */ 114, 225, 109, 225, 225, 109, 109, 109, /* 3 */ 220, 104, 215, 99, 220, 104, 215, 99, /* 4 */ 220, 104, 215, 99, 220, 104, 215, 99, /* 5 */ 210, 94, 205, 89, 190, 74, 161, 45, /* 6 */ 190, 74, 161, 45, 156, 40, 151, 35, /* 7 */ 156, 40, 151, 35, 156, 40, 151, 35, /* 8 */ 156, 40, 151, 146, 30, 141, 146, 30, /* 9 */ 146, 30, 141, 25, 210, 94, 205, 89, /* 10 */ 210, 94, 205, 89, 210, 94, 205, 89, /* 11 */ 195, 79, 84, 195, 79, 84, 195, 79, /* 12 */ 200, 84, 195, 79, 190, 74, 161, 45, /* 13 */ 190, 74, 161, 45, 146, 30, 141, 25, /* 14 */ 136, 20, 131, 15, 136, 20, 131, 15, /* 15 */ 136, 20, 131, 15, 136, 20, 131, 15, /* 16 */ 126, 10, 121, 5, 126, 10, 121, 5, /* 17 */ 126, 10, 121, 5, 126, 10, 121, 5 }; /* * This table is for internal reference * * pintable_internal[]= { * -- Q0 -- * 0 143,143,143,143,139,139,139,35 * 1 35,51,39,135,91,95,95,95 * 2 139,135,131,135,135,131,131,131 * 3 127,123,119,115,127,123,119,115 * 4 127,123,119,115,127,123,119,115 * 5 111,107,103,99,79,75,71,67 * 6 79,75,71,67,63,59,55,51 * 7 63,59,55,51,63,59,55,51 * 8 63,59,55,47,43,39,47,43 * 9 47,43,39,35,111,107,103,99 * 10 111,107,103,99,111,107,103,99 * 11 87,83,91,87,83,91,87,83 * 12 95,91,87,83,79,75,71,67 * 13 79,75,71,67,47,43,39,35 * 14 31,27,23,19,31,27,23,19 * 15 31,27,23,19,31,27,23,19 * 16 15,11,7,3,15,11,7,3 * 17 15,11,7,3,15,11,7,3 * } */ char *dimm_Jno[] = { /* P0 */ "J13300", "J13400", "J13500", "J13600", "J13301", "J13401", "J13501", "J13601", /* P1 */ "J14300", "J14400", "J14500", "J14600", "J14301", "J14401", "J14501", "J14601", /* P2 */ "J15300", "J15400", "J15500", "J15600", "J15301", "J15401", "J15501", "J15601", /* P3 */ "J16300", "J16400", "J16500", "J16600", "J16301", "J16401", "J16501", "J16601", NULL }; static uint8_t * get_memlayout(uint32_t cpuid, uint32_t *len) { m_layout *LayoutBuf; if ((LayoutBuf = (m_layout *)kmem_zalloc(sizeof (m_layout), KM_SLEEP)) == NULL) { *len = 0; return (NULL); } bcopy(J_dimm_pinTable, LayoutBuf, sizeof (m_layout)); *len = sizeof (m_layout); cpuid &= 0x03; /* last 2 bits of a 10 bit number */ bcopy(dimm_Jno[cpuid << 3], LayoutBuf->Jnumber[0], 64); return ((uint8_t *)LayoutBuf); } static char * rsv_string(prdrsv_t rsv) { char *buffer; char *status; switch (rsv) { case RSV_UNKNOWN: buffer = "unknown"; break; case RSV_PRESENT: buffer = "okay"; break; case RSV_CRUNCH: buffer = "disabled"; break; case RSV_UNDEFINED: buffer = "undefined"; break; case RSV_MISS: buffer = "missing"; break; case RSV_EMPTY_CASSETTE: buffer = "disabled"; break; case RSV_MISCONFIG: buffer = "misconfigured"; break; case RSV_FAIL_OBP: buffer = "fail-obp"; break; case RSV_BLACK: buffer = "blacklisted"; break; case RSV_RED: buffer = "redlisted"; break; case RSV_EXCLUDED: buffer = "disabled"; break; case RSV_UNCONFIG: buffer = "disabled"; break; case RSV_PASS: buffer = "okay"; break; case RSV_FAIL: default: buffer = "fail"; break; } status = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); (void) strcpy(status, buffer); return (status); }