125cf1a30Sjl /* 225cf1a30Sjl * CDDL HEADER START 325cf1a30Sjl * 425cf1a30Sjl * The contents of this file are subject to the terms of the 525cf1a30Sjl * Common Development and Distribution License (the "License"). 625cf1a30Sjl * You may not use this file except in compliance with the License. 725cf1a30Sjl * 825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 925cf1a30Sjl * or http://www.opensolaris.org/os/licensing. 1025cf1a30Sjl * See the License for the specific language governing permissions 1125cf1a30Sjl * and limitations under the License. 1225cf1a30Sjl * 1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each 1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the 1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying 1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner] 1825cf1a30Sjl * 1925cf1a30Sjl * CDDL HEADER END 2025cf1a30Sjl */ 2125cf1a30Sjl /* 2268ac2337Sjl * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 2325cf1a30Sjl * Use is subject to license terms. 2425cf1a30Sjl */ 2525cf1a30Sjl 2625cf1a30Sjl #pragma ident "%Z%%M% %I% %E% SMI" 2725cf1a30Sjl 2868ac2337Sjl 2925cf1a30Sjl #include <sys/debug.h> 3025cf1a30Sjl #include <sys/types.h> 3125cf1a30Sjl #include <sys/varargs.h> 3225cf1a30Sjl #include <sys/errno.h> 3325cf1a30Sjl #include <sys/cred.h> 3425cf1a30Sjl #include <sys/dditypes.h> 3525cf1a30Sjl #include <sys/devops.h> 3625cf1a30Sjl #include <sys/modctl.h> 3725cf1a30Sjl #include <sys/poll.h> 3825cf1a30Sjl #include <sys/conf.h> 3925cf1a30Sjl #include <sys/ddi.h> 4025cf1a30Sjl #include <sys/sunddi.h> 4125cf1a30Sjl #include <sys/sunndi.h> 4225cf1a30Sjl #include <sys/ndi_impldefs.h> 4325cf1a30Sjl #include <sys/stat.h> 4425cf1a30Sjl #include <sys/kmem.h> 4525cf1a30Sjl #include <sys/vmem.h> 4625cf1a30Sjl #include <sys/opl_olympus_regs.h> 4725cf1a30Sjl #include <sys/cpuvar.h> 4825cf1a30Sjl #include <sys/cpupart.h> 4925cf1a30Sjl #include <sys/mem_config.h> 5025cf1a30Sjl #include <sys/ddi_impldefs.h> 5125cf1a30Sjl #include <sys/systm.h> 5225cf1a30Sjl #include <sys/machsystm.h> 5325cf1a30Sjl #include <sys/autoconf.h> 5425cf1a30Sjl #include <sys/cmn_err.h> 5525cf1a30Sjl #include <sys/sysmacros.h> 5625cf1a30Sjl #include <sys/x_call.h> 5725cf1a30Sjl #include <sys/promif.h> 5825cf1a30Sjl #include <sys/prom_plat.h> 5925cf1a30Sjl #include <sys/membar.h> 6025cf1a30Sjl #include <vm/seg_kmem.h> 6125cf1a30Sjl #include <sys/mem_cage.h> 6225cf1a30Sjl #include <sys/stack.h> 6325cf1a30Sjl #include <sys/archsystm.h> 6425cf1a30Sjl #include <vm/hat_sfmmu.h> 6525cf1a30Sjl #include <sys/pte.h> 6625cf1a30Sjl #include <sys/mmu.h> 6725cf1a30Sjl #include <sys/cpu_module.h> 6825cf1a30Sjl #include <sys/obpdefs.h> 6925cf1a30Sjl #include <sys/note.h> 7025cf1a30Sjl #include <sys/ontrap.h> 7125cf1a30Sjl #include <sys/cpu_sgnblk_defs.h> 7225cf1a30Sjl #include <sys/opl.h> 73*e98fafb9Sjl #include <sys/cpu_impl.h> 7425cf1a30Sjl 7525cf1a30Sjl 7625cf1a30Sjl #include <sys/promimpl.h> 7725cf1a30Sjl #include <sys/prom_plat.h> 7825cf1a30Sjl #include <sys/kobj.h> 7925cf1a30Sjl 8025cf1a30Sjl #include <sys/sysevent.h> 8125cf1a30Sjl #include <sys/sysevent/dr.h> 8225cf1a30Sjl #include <sys/sysevent/eventdefs.h> 8325cf1a30Sjl 8425cf1a30Sjl #include <sys/drmach.h> 8525cf1a30Sjl #include <sys/dr_util.h> 8625cf1a30Sjl 8725cf1a30Sjl #include <sys/fcode.h> 8825cf1a30Sjl #include <sys/opl_cfg.h> 8925cf1a30Sjl 9025cf1a30Sjl extern void bcopy32_il(uint64_t, uint64_t); 9125cf1a30Sjl extern void flush_cache_il(void); 9225cf1a30Sjl extern void drmach_sleep_il(void); 9325cf1a30Sjl 9425cf1a30Sjl typedef struct { 9525cf1a30Sjl struct drmach_node *node; 9625cf1a30Sjl void *data; 9725cf1a30Sjl } drmach_node_walk_args_t; 9825cf1a30Sjl 9925cf1a30Sjl typedef struct drmach_node { 10025cf1a30Sjl void *here; 10125cf1a30Sjl 10225cf1a30Sjl pnode_t (*get_dnode)(struct drmach_node *node); 10325cf1a30Sjl int (*walk)(struct drmach_node *node, void *data, 10425cf1a30Sjl int (*cb)(drmach_node_walk_args_t *args)); 10525cf1a30Sjl dev_info_t *(*n_getdip)(struct drmach_node *node); 10625cf1a30Sjl int (*n_getproplen)(struct drmach_node *node, char *name, 10725cf1a30Sjl int *len); 10825cf1a30Sjl int (*n_getprop)(struct drmach_node *node, char *name, 10925cf1a30Sjl void *buf, int len); 11025cf1a30Sjl int (*get_parent)(struct drmach_node *node, 11125cf1a30Sjl struct drmach_node *pnode); 11225cf1a30Sjl } drmach_node_t; 11325cf1a30Sjl 11425cf1a30Sjl typedef struct { 11525cf1a30Sjl int min_index; 11625cf1a30Sjl int max_index; 11725cf1a30Sjl int arr_sz; 11825cf1a30Sjl drmachid_t *arr; 11925cf1a30Sjl } drmach_array_t; 12025cf1a30Sjl 12125cf1a30Sjl typedef struct { 12225cf1a30Sjl void *isa; 12325cf1a30Sjl 12425cf1a30Sjl void (*dispose)(drmachid_t); 12525cf1a30Sjl sbd_error_t *(*release)(drmachid_t); 12625cf1a30Sjl sbd_error_t *(*status)(drmachid_t, drmach_status_t *); 12725cf1a30Sjl 12825cf1a30Sjl char name[MAXNAMELEN]; 12925cf1a30Sjl } drmach_common_t; 13025cf1a30Sjl 13125cf1a30Sjl typedef struct { 13225cf1a30Sjl uint32_t core_present; 13325cf1a30Sjl uint32_t core_hotadded; 13425cf1a30Sjl uint32_t core_started; 13525cf1a30Sjl } drmach_cmp_t; 13625cf1a30Sjl 13725cf1a30Sjl typedef struct { 13825cf1a30Sjl drmach_common_t cm; 13925cf1a30Sjl int bnum; 14025cf1a30Sjl int assigned; 14125cf1a30Sjl int powered; 14225cf1a30Sjl int connected; 14325cf1a30Sjl int cond; 14425cf1a30Sjl drmach_node_t *tree; 14525cf1a30Sjl drmach_array_t *devices; 14625cf1a30Sjl int boot_board; /* if board exists on bootup */ 14725cf1a30Sjl drmach_cmp_t cores[OPL_MAX_COREID_PER_BOARD]; 14825cf1a30Sjl } drmach_board_t; 14925cf1a30Sjl 15025cf1a30Sjl typedef struct { 15125cf1a30Sjl drmach_common_t cm; 15225cf1a30Sjl drmach_board_t *bp; 15325cf1a30Sjl int unum; 15425cf1a30Sjl int portid; 15525cf1a30Sjl int busy; 15625cf1a30Sjl int powered; 15725cf1a30Sjl const char *type; 15825cf1a30Sjl drmach_node_t *node; 15925cf1a30Sjl } drmach_device_t; 16025cf1a30Sjl 16125cf1a30Sjl typedef struct drmach_cpu { 16225cf1a30Sjl drmach_device_t dev; 16325cf1a30Sjl processorid_t cpuid; 16425cf1a30Sjl int sb; 16525cf1a30Sjl int chipid; 16625cf1a30Sjl int coreid; 16725cf1a30Sjl int strandid; 16825cf1a30Sjl int status; 16925cf1a30Sjl #define OPL_CPU_HOTADDED 1 17025cf1a30Sjl } drmach_cpu_t; 17125cf1a30Sjl 17225cf1a30Sjl typedef struct drmach_mem { 17325cf1a30Sjl drmach_device_t dev; 17425cf1a30Sjl uint64_t slice_base; 17525cf1a30Sjl uint64_t slice_size; 17625cf1a30Sjl uint64_t base_pa; /* lowest installed memory base */ 17725cf1a30Sjl uint64_t nbytes; /* size of installed memory */ 17825cf1a30Sjl struct memlist *memlist; 17925cf1a30Sjl } drmach_mem_t; 18025cf1a30Sjl 18125cf1a30Sjl typedef struct drmach_io { 18225cf1a30Sjl drmach_device_t dev; 18325cf1a30Sjl int channel; 18425cf1a30Sjl int leaf; 18525cf1a30Sjl } drmach_io_t; 18625cf1a30Sjl 18725cf1a30Sjl typedef struct drmach_domain_info { 18825cf1a30Sjl uint32_t floating; 18925cf1a30Sjl int allow_dr; 19025cf1a30Sjl } drmach_domain_info_t; 19125cf1a30Sjl 19225cf1a30Sjl drmach_domain_info_t drmach_domain; 19325cf1a30Sjl 19425cf1a30Sjl typedef struct { 19525cf1a30Sjl int flags; 19625cf1a30Sjl drmach_device_t *dp; 19725cf1a30Sjl sbd_error_t *err; 19825cf1a30Sjl dev_info_t *dip; 19925cf1a30Sjl } drmach_config_args_t; 20025cf1a30Sjl 20125cf1a30Sjl typedef struct { 20225cf1a30Sjl drmach_board_t *obj; 20325cf1a30Sjl int ndevs; 20425cf1a30Sjl void *a; 20525cf1a30Sjl sbd_error_t *(*found)(void *a, const char *, int, drmachid_t); 20625cf1a30Sjl sbd_error_t *err; 20725cf1a30Sjl } drmach_board_cb_data_t; 20825cf1a30Sjl 20925cf1a30Sjl static drmach_array_t *drmach_boards; 21025cf1a30Sjl 21125cf1a30Sjl static sbd_error_t *drmach_device_new(drmach_node_t *, 21225cf1a30Sjl drmach_board_t *, int, drmachid_t *); 21325cf1a30Sjl static sbd_error_t *drmach_cpu_new(drmach_device_t *, drmachid_t *); 21425cf1a30Sjl static sbd_error_t *drmach_mem_new(drmach_device_t *, drmachid_t *); 21525cf1a30Sjl static sbd_error_t *drmach_io_new(drmach_device_t *, drmachid_t *); 21625cf1a30Sjl 21725cf1a30Sjl static dev_info_t *drmach_node_ddi_get_dip(drmach_node_t *np); 21825cf1a30Sjl static int drmach_node_ddi_get_prop(drmach_node_t *np, 21925cf1a30Sjl char *name, void *buf, int len); 22025cf1a30Sjl static int drmach_node_ddi_get_proplen(drmach_node_t *np, 22125cf1a30Sjl char *name, int *len); 22225cf1a30Sjl 22325cf1a30Sjl static int drmach_get_portid(drmach_node_t *); 22425cf1a30Sjl static sbd_error_t *drmach_i_status(drmachid_t, drmach_status_t *); 22525cf1a30Sjl static int opl_check_dr_status(); 22625cf1a30Sjl static void drmach_io_dispose(drmachid_t); 22725cf1a30Sjl static sbd_error_t *drmach_io_release(drmachid_t); 22825cf1a30Sjl static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *); 22925cf1a30Sjl static int drmach_init(void); 23025cf1a30Sjl static void drmach_fini(void); 23125cf1a30Sjl static void drmach_swap_pa(drmach_mem_t *, drmach_mem_t *); 23225cf1a30Sjl static drmach_board_t *drmach_get_board_by_bnum(int); 23325cf1a30Sjl 23425cf1a30Sjl /* options for the second argument in drmach_add_remove_cpu() */ 23525cf1a30Sjl #define HOTADD_CPU 1 23625cf1a30Sjl #define HOTREMOVE_CPU 2 23725cf1a30Sjl 23825cf1a30Sjl #define ON_BOARD_CORE_NUM(x) (((uint_t)(x) / OPL_MAX_STRANDID_PER_CORE) & \ 23925cf1a30Sjl (OPL_MAX_COREID_PER_BOARD - 1)) 24025cf1a30Sjl 24125cf1a30Sjl extern struct cpu *SIGBCPU; 24225cf1a30Sjl 24325cf1a30Sjl static int drmach_name2type_idx(char *); 24425cf1a30Sjl static drmach_board_t *drmach_board_new(int, int); 24525cf1a30Sjl 24625cf1a30Sjl #ifdef DEBUG 24725cf1a30Sjl 24825cf1a30Sjl #define DRMACH_PR if (drmach_debug) printf 24925cf1a30Sjl int drmach_debug = 1; /* set to non-zero to enable debug messages */ 25025cf1a30Sjl #else 25125cf1a30Sjl 25225cf1a30Sjl #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf 25325cf1a30Sjl #endif /* DEBUG */ 25425cf1a30Sjl 25525cf1a30Sjl 25625cf1a30Sjl #define DRMACH_OBJ(id) ((drmach_common_t *)id) 25725cf1a30Sjl 258ddf95635Sbm #define DRMACH_NULL_ID(id) ((id) == 0) 259ddf95635Sbm 26025cf1a30Sjl #define DRMACH_IS_BOARD_ID(id) \ 26125cf1a30Sjl ((id != 0) && \ 26225cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_board_new)) 26325cf1a30Sjl 26425cf1a30Sjl #define DRMACH_IS_CPU_ID(id) \ 26525cf1a30Sjl ((id != 0) && \ 26625cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new)) 26725cf1a30Sjl 26825cf1a30Sjl #define DRMACH_IS_MEM_ID(id) \ 26925cf1a30Sjl ((id != 0) && \ 27025cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new)) 27125cf1a30Sjl 27225cf1a30Sjl #define DRMACH_IS_IO_ID(id) \ 27325cf1a30Sjl ((id != 0) && \ 27425cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 27525cf1a30Sjl 27625cf1a30Sjl #define DRMACH_IS_DEVICE_ID(id) \ 27725cf1a30Sjl ((id != 0) && \ 27825cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \ 27925cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \ 28025cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 28125cf1a30Sjl 28225cf1a30Sjl #define DRMACH_IS_ID(id) \ 28325cf1a30Sjl ((id != 0) && \ 28425cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \ 28525cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \ 28625cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \ 28725cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 28825cf1a30Sjl 28925cf1a30Sjl #define DRMACH_INTERNAL_ERROR() \ 29025cf1a30Sjl drerr_new(1, EOPL_INTERNAL, drmach_ie_fmt, __LINE__) 29125cf1a30Sjl 29225cf1a30Sjl static char *drmach_ie_fmt = "drmach.c %d"; 29325cf1a30Sjl 29425cf1a30Sjl static struct { 29525cf1a30Sjl const char *name; 29625cf1a30Sjl const char *type; 29725cf1a30Sjl sbd_error_t *(*new)(drmach_device_t *, drmachid_t *); 29825cf1a30Sjl } drmach_name2type[] = { 29925cf1a30Sjl { "cpu", DRMACH_DEVTYPE_CPU, drmach_cpu_new }, 30025cf1a30Sjl { "pseudo-mc", DRMACH_DEVTYPE_MEM, drmach_mem_new }, 30125cf1a30Sjl { "pci", DRMACH_DEVTYPE_PCI, drmach_io_new }, 30225cf1a30Sjl }; 30325cf1a30Sjl 30425cf1a30Sjl /* utility */ 30525cf1a30Sjl #define MBYTE (1048576ull) 30625cf1a30Sjl 30725cf1a30Sjl /* 30825cf1a30Sjl * drmach autoconfiguration data structures and interfaces 30925cf1a30Sjl */ 31025cf1a30Sjl 31125cf1a30Sjl extern struct mod_ops mod_miscops; 31225cf1a30Sjl 31325cf1a30Sjl static struct modlmisc modlmisc = { 31425cf1a30Sjl &mod_miscops, 31525cf1a30Sjl "OPL DR 1.1" 31625cf1a30Sjl }; 31725cf1a30Sjl 31825cf1a30Sjl static struct modlinkage modlinkage = { 31925cf1a30Sjl MODREV_1, 32025cf1a30Sjl (void *)&modlmisc, 32125cf1a30Sjl NULL 32225cf1a30Sjl }; 32325cf1a30Sjl 32425cf1a30Sjl static krwlock_t drmach_boards_rwlock; 32525cf1a30Sjl 32625cf1a30Sjl typedef const char *fn_t; 32725cf1a30Sjl 32825cf1a30Sjl int 32925cf1a30Sjl _init(void) 33025cf1a30Sjl { 33125cf1a30Sjl int err; 33225cf1a30Sjl 33325cf1a30Sjl if ((err = drmach_init()) != 0) { 33425cf1a30Sjl return (err); 33525cf1a30Sjl } 33625cf1a30Sjl 33725cf1a30Sjl if ((err = mod_install(&modlinkage)) != 0) { 33825cf1a30Sjl drmach_fini(); 33925cf1a30Sjl } 34025cf1a30Sjl 34125cf1a30Sjl return (err); 34225cf1a30Sjl } 34325cf1a30Sjl 34425cf1a30Sjl int 34525cf1a30Sjl _fini(void) 34625cf1a30Sjl { 34725cf1a30Sjl int err; 34825cf1a30Sjl 34925cf1a30Sjl if ((err = mod_remove(&modlinkage)) == 0) 35025cf1a30Sjl drmach_fini(); 35125cf1a30Sjl 35225cf1a30Sjl return (err); 35325cf1a30Sjl } 35425cf1a30Sjl 35525cf1a30Sjl int 35625cf1a30Sjl _info(struct modinfo *modinfop) 35725cf1a30Sjl { 35825cf1a30Sjl return (mod_info(&modlinkage, modinfop)); 35925cf1a30Sjl } 36025cf1a30Sjl 36125cf1a30Sjl struct drmach_mc_lookup { 36225cf1a30Sjl int bnum; 36325cf1a30Sjl drmach_board_t *bp; 36425cf1a30Sjl dev_info_t *dip; /* rv - set if found */ 36525cf1a30Sjl }; 36625cf1a30Sjl 36725cf1a30Sjl #define _ptob64(p) ((uint64_t)(p) << PAGESHIFT) 36825cf1a30Sjl #define _b64top(b) ((pgcnt_t)((b) >> PAGESHIFT)) 36925cf1a30Sjl 37025cf1a30Sjl static int 37125cf1a30Sjl drmach_setup_mc_info(dev_info_t *dip, drmach_mem_t *mp) 37225cf1a30Sjl { 37325cf1a30Sjl uint64_t memory_ranges[128]; 37425cf1a30Sjl int len; 37525cf1a30Sjl struct memlist *ml; 37625cf1a30Sjl int rv; 37725cf1a30Sjl hwd_sb_t *hwd; 37825cf1a30Sjl hwd_memory_t *pm; 37925cf1a30Sjl 38025cf1a30Sjl len = sizeof (memory_ranges); 381*e98fafb9Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 382*e98fafb9Sjl "sb-mem-ranges", (caddr_t)&memory_ranges[0], &len) != 383*e98fafb9Sjl DDI_PROP_SUCCESS) { 38425cf1a30Sjl mp->slice_base = 0; 38525cf1a30Sjl mp->slice_size = 0; 38625cf1a30Sjl return (-1); 38725cf1a30Sjl } 38825cf1a30Sjl mp->slice_base = memory_ranges[0]; 38925cf1a30Sjl mp->slice_size = memory_ranges[1]; 39025cf1a30Sjl 39125cf1a30Sjl if (!mp->dev.bp->boot_board) { 39225cf1a30Sjl int i; 39325cf1a30Sjl 39425cf1a30Sjl rv = opl_read_hwd(mp->dev.bp->bnum, NULL, NULL, NULL, &hwd); 39525cf1a30Sjl 39625cf1a30Sjl if (rv != 0) { 39725cf1a30Sjl return (-1); 39825cf1a30Sjl } 39925cf1a30Sjl 40025cf1a30Sjl ml = NULL; 40125cf1a30Sjl pm = &hwd->sb_cmu.cmu_memory; 40225cf1a30Sjl for (i = 0; i < HWD_MAX_MEM_CHUNKS; i++) { 40325cf1a30Sjl if (pm->mem_chunks[i].chnk_size > 0) { 40425cf1a30Sjl ml = memlist_add_span(ml, 405*e98fafb9Sjl pm->mem_chunks[i].chnk_start_address, 406*e98fafb9Sjl pm->mem_chunks[i].chnk_size); 40725cf1a30Sjl } 40825cf1a30Sjl } 40925cf1a30Sjl } else { 41025cf1a30Sjl /* 41125cf1a30Sjl * we intersect phys_install to get base_pa. 41225cf1a30Sjl * This only works at bootup time. 41325cf1a30Sjl */ 41425cf1a30Sjl 41525cf1a30Sjl memlist_read_lock(); 41625cf1a30Sjl ml = memlist_dup(phys_install); 41725cf1a30Sjl memlist_read_unlock(); 41825cf1a30Sjl 41925cf1a30Sjl ml = memlist_del_span(ml, 0ull, mp->slice_base); 42025cf1a30Sjl if (ml) { 42125cf1a30Sjl uint64_t basepa, endpa; 42225cf1a30Sjl endpa = _ptob64(physmax + 1); 42325cf1a30Sjl 42425cf1a30Sjl basepa = mp->slice_base + mp->slice_size; 42525cf1a30Sjl 42625cf1a30Sjl ml = memlist_del_span(ml, basepa, endpa - basepa); 42725cf1a30Sjl } 42825cf1a30Sjl } 42925cf1a30Sjl 43025cf1a30Sjl if (ml) { 43125cf1a30Sjl uint64_t nbytes = 0; 43225cf1a30Sjl struct memlist *p; 43325cf1a30Sjl for (p = ml; p; p = p->next) { 43425cf1a30Sjl nbytes += p->size; 43525cf1a30Sjl } 43625cf1a30Sjl if ((mp->nbytes = nbytes) > 0) 43725cf1a30Sjl mp->base_pa = ml->address; 43825cf1a30Sjl else 43925cf1a30Sjl mp->base_pa = 0; 44025cf1a30Sjl mp->memlist = ml; 44125cf1a30Sjl } else { 44225cf1a30Sjl mp->base_pa = 0; 44325cf1a30Sjl mp->nbytes = 0; 44425cf1a30Sjl } 44525cf1a30Sjl return (0); 44625cf1a30Sjl } 44725cf1a30Sjl 44825cf1a30Sjl 44925cf1a30Sjl struct drmach_hotcpu { 45025cf1a30Sjl drmach_board_t *bp; 45125cf1a30Sjl int bnum; 45225cf1a30Sjl int core_id; 45325cf1a30Sjl int rv; 45425cf1a30Sjl int option; 45525cf1a30Sjl }; 45625cf1a30Sjl 45725cf1a30Sjl static int 45825cf1a30Sjl drmach_cpu_cb(dev_info_t *dip, void *arg) 45925cf1a30Sjl { 46025cf1a30Sjl struct drmach_hotcpu *p = (struct drmach_hotcpu *)arg; 46125cf1a30Sjl char name[OBP_MAXDRVNAME]; 46225cf1a30Sjl int len = OBP_MAXDRVNAME; 46325cf1a30Sjl int bnum, core_id, strand_id; 46425cf1a30Sjl drmach_board_t *bp; 46525cf1a30Sjl 46625cf1a30Sjl if (dip == ddi_root_node()) { 46725cf1a30Sjl return (DDI_WALK_CONTINUE); 46825cf1a30Sjl } 46925cf1a30Sjl 47025cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 47125cf1a30Sjl DDI_PROP_DONTPASS, "name", 47225cf1a30Sjl (caddr_t)name, &len) != DDI_PROP_SUCCESS) { 47325cf1a30Sjl return (DDI_WALK_PRUNECHILD); 47425cf1a30Sjl } 47525cf1a30Sjl 47625cf1a30Sjl /* only cmp has board number */ 47725cf1a30Sjl bnum = -1; 47825cf1a30Sjl len = sizeof (bnum); 47925cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 48025cf1a30Sjl DDI_PROP_DONTPASS, OBP_BOARDNUM, 48125cf1a30Sjl (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) { 48225cf1a30Sjl bnum = -1; 48325cf1a30Sjl } 48425cf1a30Sjl 48525cf1a30Sjl if (strcmp(name, "cmp") == 0) { 48625cf1a30Sjl if (bnum != p->bnum) 48725cf1a30Sjl return (DDI_WALK_PRUNECHILD); 48825cf1a30Sjl return (DDI_WALK_CONTINUE); 48925cf1a30Sjl } 49025cf1a30Sjl /* we have already pruned all unwanted cores and cpu's above */ 49125cf1a30Sjl if (strcmp(name, "core") == 0) { 49225cf1a30Sjl return (DDI_WALK_CONTINUE); 49325cf1a30Sjl } 49425cf1a30Sjl if (strcmp(name, "cpu") == 0) { 49525cf1a30Sjl processorid_t cpuid; 49625cf1a30Sjl len = sizeof (cpuid); 49725cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 49825cf1a30Sjl DDI_PROP_DONTPASS, "cpuid", 49925cf1a30Sjl (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) { 50025cf1a30Sjl p->rv = -1; 50125cf1a30Sjl return (DDI_WALK_TERMINATE); 50225cf1a30Sjl } 50325cf1a30Sjl 50425cf1a30Sjl core_id = p->core_id; 50525cf1a30Sjl 50625cf1a30Sjl bnum = LSB_ID(cpuid); 50725cf1a30Sjl 50825cf1a30Sjl if (ON_BOARD_CORE_NUM(cpuid) != core_id) 50925cf1a30Sjl return (DDI_WALK_CONTINUE); 51025cf1a30Sjl 51125cf1a30Sjl bp = p->bp; 51225cf1a30Sjl ASSERT(bnum == bp->bnum); 51325cf1a30Sjl 51425cf1a30Sjl if (p->option == HOTADD_CPU) { 51525cf1a30Sjl if (prom_hotaddcpu(cpuid) != 0) { 51625cf1a30Sjl p->rv = -1; 51725cf1a30Sjl return (DDI_WALK_TERMINATE); 51825cf1a30Sjl } 51925cf1a30Sjl strand_id = STRAND_ID(cpuid); 52025cf1a30Sjl bp->cores[core_id].core_hotadded |= (1 << strand_id); 52125cf1a30Sjl } else if (p->option == HOTREMOVE_CPU) { 52225cf1a30Sjl if (prom_hotremovecpu(cpuid) != 0) { 52325cf1a30Sjl p->rv = -1; 52425cf1a30Sjl return (DDI_WALK_TERMINATE); 52525cf1a30Sjl } 52625cf1a30Sjl strand_id = STRAND_ID(cpuid); 52725cf1a30Sjl bp->cores[core_id].core_hotadded &= ~(1 << strand_id); 52825cf1a30Sjl } 52925cf1a30Sjl return (DDI_WALK_CONTINUE); 53025cf1a30Sjl } 53125cf1a30Sjl 53225cf1a30Sjl return (DDI_WALK_PRUNECHILD); 53325cf1a30Sjl } 53425cf1a30Sjl 53525cf1a30Sjl 53625cf1a30Sjl static int 53725cf1a30Sjl drmach_add_remove_cpu(int bnum, int core_id, int option) 53825cf1a30Sjl { 53925cf1a30Sjl struct drmach_hotcpu arg; 54025cf1a30Sjl drmach_board_t *bp; 54125cf1a30Sjl 54225cf1a30Sjl bp = drmach_get_board_by_bnum(bnum); 54325cf1a30Sjl ASSERT(bp); 54425cf1a30Sjl 54525cf1a30Sjl arg.bp = bp; 54625cf1a30Sjl arg.bnum = bnum; 54725cf1a30Sjl arg.core_id = core_id; 54825cf1a30Sjl arg.rv = 0; 54925cf1a30Sjl arg.option = option; 55025cf1a30Sjl ddi_walk_devs(ddi_root_node(), drmach_cpu_cb, (void *)&arg); 55125cf1a30Sjl return (arg.rv); 55225cf1a30Sjl } 55325cf1a30Sjl 55425cf1a30Sjl struct drmach_setup_core_arg { 55525cf1a30Sjl drmach_board_t *bp; 55625cf1a30Sjl }; 55725cf1a30Sjl 55825cf1a30Sjl static int 55925cf1a30Sjl drmach_setup_core_cb(dev_info_t *dip, void *arg) 56025cf1a30Sjl { 56125cf1a30Sjl struct drmach_setup_core_arg *p = (struct drmach_setup_core_arg *)arg; 56225cf1a30Sjl char name[OBP_MAXDRVNAME]; 56325cf1a30Sjl int len = OBP_MAXDRVNAME; 56425cf1a30Sjl int bnum; 56525cf1a30Sjl int core_id, strand_id; 56625cf1a30Sjl 56725cf1a30Sjl if (dip == ddi_root_node()) { 56825cf1a30Sjl return (DDI_WALK_CONTINUE); 56925cf1a30Sjl } 57025cf1a30Sjl 57125cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 57225cf1a30Sjl DDI_PROP_DONTPASS, "name", 57325cf1a30Sjl (caddr_t)name, &len) != DDI_PROP_SUCCESS) { 57425cf1a30Sjl return (DDI_WALK_PRUNECHILD); 57525cf1a30Sjl } 57625cf1a30Sjl 57725cf1a30Sjl /* only cmp has board number */ 57825cf1a30Sjl bnum = -1; 57925cf1a30Sjl len = sizeof (bnum); 58025cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 58125cf1a30Sjl DDI_PROP_DONTPASS, OBP_BOARDNUM, 58225cf1a30Sjl (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) { 58325cf1a30Sjl bnum = -1; 58425cf1a30Sjl } 58525cf1a30Sjl 58625cf1a30Sjl if (strcmp(name, "cmp") == 0) { 58725cf1a30Sjl if (bnum != p->bp->bnum) 58825cf1a30Sjl return (DDI_WALK_PRUNECHILD); 58925cf1a30Sjl return (DDI_WALK_CONTINUE); 59025cf1a30Sjl } 59125cf1a30Sjl /* we have already pruned all unwanted cores and cpu's above */ 59225cf1a30Sjl if (strcmp(name, "core") == 0) { 59325cf1a30Sjl return (DDI_WALK_CONTINUE); 59425cf1a30Sjl } 59525cf1a30Sjl if (strcmp(name, "cpu") == 0) { 59625cf1a30Sjl processorid_t cpuid; 59725cf1a30Sjl len = sizeof (cpuid); 59825cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 59925cf1a30Sjl DDI_PROP_DONTPASS, "cpuid", 60025cf1a30Sjl (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) { 60125cf1a30Sjl return (DDI_WALK_TERMINATE); 60225cf1a30Sjl } 60325cf1a30Sjl bnum = LSB_ID(cpuid); 60425cf1a30Sjl ASSERT(bnum == p->bp->bnum); 60525cf1a30Sjl core_id = ON_BOARD_CORE_NUM(cpuid); 60625cf1a30Sjl strand_id = STRAND_ID(cpuid); 60725cf1a30Sjl p->bp->cores[core_id].core_present |= (1 << strand_id); 60825cf1a30Sjl return (DDI_WALK_CONTINUE); 60925cf1a30Sjl } 61025cf1a30Sjl 61125cf1a30Sjl return (DDI_WALK_PRUNECHILD); 61225cf1a30Sjl } 61325cf1a30Sjl 61425cf1a30Sjl 61525cf1a30Sjl static void 61625cf1a30Sjl drmach_setup_core_info(drmach_board_t *obj) 61725cf1a30Sjl { 61825cf1a30Sjl struct drmach_setup_core_arg arg; 61925cf1a30Sjl int i; 62025cf1a30Sjl 62125cf1a30Sjl for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) { 62225cf1a30Sjl obj->cores[i].core_present = 0; 62325cf1a30Sjl obj->cores[i].core_hotadded = 0; 62425cf1a30Sjl obj->cores[i].core_started = 0; 62525cf1a30Sjl } 62625cf1a30Sjl arg.bp = obj; 62725cf1a30Sjl ddi_walk_devs(ddi_root_node(), drmach_setup_core_cb, (void *)&arg); 62825cf1a30Sjl 62925cf1a30Sjl for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) { 63025cf1a30Sjl if (obj->boot_board) { 63125cf1a30Sjl obj->cores[i].core_hotadded = 632*e98fafb9Sjl obj->cores[i].core_started = 633*e98fafb9Sjl obj->cores[i].core_present; 63425cf1a30Sjl } 63525cf1a30Sjl } 63625cf1a30Sjl } 63725cf1a30Sjl 63825cf1a30Sjl /* 63925cf1a30Sjl * drmach_node_* routines serve the purpose of separating the 64025cf1a30Sjl * rest of the code from the device tree and OBP. This is necessary 64125cf1a30Sjl * because of In-Kernel-Probing. Devices probed after stod, are probed 64225cf1a30Sjl * by the in-kernel-prober, not OBP. These devices, therefore, do not 64325cf1a30Sjl * have dnode ids. 64425cf1a30Sjl */ 64525cf1a30Sjl 64625cf1a30Sjl typedef struct { 64725cf1a30Sjl drmach_node_walk_args_t *nwargs; 64825cf1a30Sjl int (*cb)(drmach_node_walk_args_t *args); 64925cf1a30Sjl int err; 65025cf1a30Sjl } drmach_node_ddi_walk_args_t; 65125cf1a30Sjl 65225cf1a30Sjl static int 65325cf1a30Sjl drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg) 65425cf1a30Sjl { 65525cf1a30Sjl drmach_node_ddi_walk_args_t *nargs; 65625cf1a30Sjl 65725cf1a30Sjl nargs = (drmach_node_ddi_walk_args_t *)arg; 65825cf1a30Sjl 65925cf1a30Sjl /* 66025cf1a30Sjl * dip doesn't have to be held here as we are called 66125cf1a30Sjl * from ddi_walk_devs() which holds the dip. 66225cf1a30Sjl */ 66325cf1a30Sjl nargs->nwargs->node->here = (void *)dip; 66425cf1a30Sjl 66525cf1a30Sjl nargs->err = nargs->cb(nargs->nwargs); 66625cf1a30Sjl 66725cf1a30Sjl 66825cf1a30Sjl /* 66925cf1a30Sjl * Set "here" to NULL so that unheld dip is not accessible 67025cf1a30Sjl * outside ddi_walk_devs() 67125cf1a30Sjl */ 67225cf1a30Sjl nargs->nwargs->node->here = NULL; 67325cf1a30Sjl 67425cf1a30Sjl if (nargs->err) 67525cf1a30Sjl return (DDI_WALK_TERMINATE); 67625cf1a30Sjl else 67725cf1a30Sjl return (DDI_WALK_CONTINUE); 67825cf1a30Sjl } 67925cf1a30Sjl 68025cf1a30Sjl static int 68125cf1a30Sjl drmach_node_ddi_walk(drmach_node_t *np, void *data, 68225cf1a30Sjl int (*cb)(drmach_node_walk_args_t *args)) 68325cf1a30Sjl { 68425cf1a30Sjl drmach_node_walk_args_t args; 68525cf1a30Sjl drmach_node_ddi_walk_args_t nargs; 68625cf1a30Sjl 68725cf1a30Sjl 68825cf1a30Sjl /* initialized args structure for callback */ 68925cf1a30Sjl args.node = np; 69025cf1a30Sjl args.data = data; 69125cf1a30Sjl 69225cf1a30Sjl nargs.nwargs = &args; 69325cf1a30Sjl nargs.cb = cb; 69425cf1a30Sjl nargs.err = 0; 69525cf1a30Sjl 69625cf1a30Sjl /* 69725cf1a30Sjl * Root node doesn't have to be held in any way. 69825cf1a30Sjl */ 699*e98fafb9Sjl ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs); 70025cf1a30Sjl 70125cf1a30Sjl return (nargs.err); 70225cf1a30Sjl } 70325cf1a30Sjl 70425cf1a30Sjl static int 70525cf1a30Sjl drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp) 70625cf1a30Sjl { 70725cf1a30Sjl dev_info_t *ndip; 70825cf1a30Sjl static char *fn = "drmach_node_ddi_get_parent"; 70925cf1a30Sjl 71025cf1a30Sjl ndip = np->n_getdip(np); 71125cf1a30Sjl if (ndip == NULL) { 71225cf1a30Sjl cmn_err(CE_WARN, "%s: NULL dip", fn); 71325cf1a30Sjl return (-1); 71425cf1a30Sjl } 71525cf1a30Sjl 71625cf1a30Sjl bcopy(np, pp, sizeof (drmach_node_t)); 71725cf1a30Sjl 71825cf1a30Sjl pp->here = (void *)ddi_get_parent(ndip); 71925cf1a30Sjl if (pp->here == NULL) { 72025cf1a30Sjl cmn_err(CE_WARN, "%s: NULL parent dip", fn); 72125cf1a30Sjl return (-1); 72225cf1a30Sjl } 72325cf1a30Sjl 72425cf1a30Sjl return (0); 72525cf1a30Sjl } 72625cf1a30Sjl 72725cf1a30Sjl /*ARGSUSED*/ 72825cf1a30Sjl static pnode_t 72925cf1a30Sjl drmach_node_ddi_get_dnode(drmach_node_t *np) 73025cf1a30Sjl { 73125cf1a30Sjl return ((pnode_t)NULL); 73225cf1a30Sjl } 73325cf1a30Sjl 73425cf1a30Sjl static drmach_node_t * 73525cf1a30Sjl drmach_node_new(void) 73625cf1a30Sjl { 73725cf1a30Sjl drmach_node_t *np; 73825cf1a30Sjl 73925cf1a30Sjl np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP); 74025cf1a30Sjl 74125cf1a30Sjl np->get_dnode = drmach_node_ddi_get_dnode; 74225cf1a30Sjl np->walk = drmach_node_ddi_walk; 74325cf1a30Sjl np->n_getdip = drmach_node_ddi_get_dip; 74425cf1a30Sjl np->n_getproplen = drmach_node_ddi_get_proplen; 74525cf1a30Sjl np->n_getprop = drmach_node_ddi_get_prop; 74625cf1a30Sjl np->get_parent = drmach_node_ddi_get_parent; 74725cf1a30Sjl 74825cf1a30Sjl return (np); 74925cf1a30Sjl } 75025cf1a30Sjl 75125cf1a30Sjl static void 75225cf1a30Sjl drmach_node_dispose(drmach_node_t *np) 75325cf1a30Sjl { 75425cf1a30Sjl kmem_free(np, sizeof (*np)); 75525cf1a30Sjl } 75625cf1a30Sjl 75725cf1a30Sjl static dev_info_t * 75825cf1a30Sjl drmach_node_ddi_get_dip(drmach_node_t *np) 75925cf1a30Sjl { 76025cf1a30Sjl return ((dev_info_t *)np->here); 76125cf1a30Sjl } 76225cf1a30Sjl 76325cf1a30Sjl static int 76425cf1a30Sjl drmach_node_walk(drmach_node_t *np, void *param, 76525cf1a30Sjl int (*cb)(drmach_node_walk_args_t *args)) 76625cf1a30Sjl { 76725cf1a30Sjl return (np->walk(np, param, cb)); 76825cf1a30Sjl } 76925cf1a30Sjl 77025cf1a30Sjl static int 77125cf1a30Sjl drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len) 77225cf1a30Sjl { 77325cf1a30Sjl int rv = 0; 77425cf1a30Sjl dev_info_t *ndip; 77525cf1a30Sjl static char *fn = "drmach_node_ddi_get_prop"; 77625cf1a30Sjl 77725cf1a30Sjl 77825cf1a30Sjl ndip = np->n_getdip(np); 77925cf1a30Sjl if (ndip == NULL) { 78025cf1a30Sjl cmn_err(CE_WARN, "%s: NULL dip", fn); 78125cf1a30Sjl rv = -1; 78225cf1a30Sjl } else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip, 78325cf1a30Sjl DDI_PROP_DONTPASS, name, 78425cf1a30Sjl (caddr_t)buf, &len) != DDI_PROP_SUCCESS) { 78525cf1a30Sjl rv = -1; 78625cf1a30Sjl } 78725cf1a30Sjl 78825cf1a30Sjl return (rv); 78925cf1a30Sjl } 79025cf1a30Sjl 79125cf1a30Sjl static int 79225cf1a30Sjl drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len) 79325cf1a30Sjl { 79425cf1a30Sjl int rv = 0; 79525cf1a30Sjl dev_info_t *ndip; 79625cf1a30Sjl 79725cf1a30Sjl ndip = np->n_getdip(np); 79825cf1a30Sjl if (ndip == NULL) { 79925cf1a30Sjl rv = -1; 800*e98fafb9Sjl } else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS, name, 801*e98fafb9Sjl len) != DDI_PROP_SUCCESS) { 80225cf1a30Sjl rv = -1; 80325cf1a30Sjl } 80425cf1a30Sjl 80525cf1a30Sjl return (rv); 80625cf1a30Sjl } 80725cf1a30Sjl 80825cf1a30Sjl static drmachid_t 80925cf1a30Sjl drmach_node_dup(drmach_node_t *np) 81025cf1a30Sjl { 81125cf1a30Sjl drmach_node_t *dup; 81225cf1a30Sjl 81325cf1a30Sjl dup = drmach_node_new(); 81425cf1a30Sjl dup->here = np->here; 81525cf1a30Sjl dup->get_dnode = np->get_dnode; 81625cf1a30Sjl dup->walk = np->walk; 81725cf1a30Sjl dup->n_getdip = np->n_getdip; 81825cf1a30Sjl dup->n_getproplen = np->n_getproplen; 81925cf1a30Sjl dup->n_getprop = np->n_getprop; 82025cf1a30Sjl dup->get_parent = np->get_parent; 82125cf1a30Sjl 82225cf1a30Sjl return (dup); 82325cf1a30Sjl } 82425cf1a30Sjl 82525cf1a30Sjl /* 82625cf1a30Sjl * drmach_array provides convenient array construction, access, 82725cf1a30Sjl * bounds checking and array destruction logic. 82825cf1a30Sjl */ 82925cf1a30Sjl 83025cf1a30Sjl static drmach_array_t * 83125cf1a30Sjl drmach_array_new(int min_index, int max_index) 83225cf1a30Sjl { 83325cf1a30Sjl drmach_array_t *arr; 83425cf1a30Sjl 83525cf1a30Sjl arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP); 83625cf1a30Sjl 83725cf1a30Sjl arr->arr_sz = (max_index - min_index + 1) * sizeof (void *); 83825cf1a30Sjl if (arr->arr_sz > 0) { 83925cf1a30Sjl arr->min_index = min_index; 84025cf1a30Sjl arr->max_index = max_index; 84125cf1a30Sjl 84225cf1a30Sjl arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP); 84325cf1a30Sjl return (arr); 84425cf1a30Sjl } else { 84525cf1a30Sjl kmem_free(arr, sizeof (*arr)); 84625cf1a30Sjl return (0); 84725cf1a30Sjl } 84825cf1a30Sjl } 84925cf1a30Sjl 85025cf1a30Sjl static int 85125cf1a30Sjl drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val) 85225cf1a30Sjl { 85325cf1a30Sjl if (idx < arr->min_index || idx > arr->max_index) 85425cf1a30Sjl return (-1); 85525cf1a30Sjl else { 85625cf1a30Sjl arr->arr[idx - arr->min_index] = val; 85725cf1a30Sjl return (0); 85825cf1a30Sjl } 85925cf1a30Sjl /*NOTREACHED*/ 86025cf1a30Sjl } 86125cf1a30Sjl 86225cf1a30Sjl static int 86325cf1a30Sjl drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val) 86425cf1a30Sjl { 86525cf1a30Sjl if (idx < arr->min_index || idx > arr->max_index) 86625cf1a30Sjl return (-1); 86725cf1a30Sjl else { 86825cf1a30Sjl *val = arr->arr[idx - arr->min_index]; 86925cf1a30Sjl return (0); 87025cf1a30Sjl } 87125cf1a30Sjl /*NOTREACHED*/ 87225cf1a30Sjl } 87325cf1a30Sjl 87425cf1a30Sjl static int 87525cf1a30Sjl drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val) 87625cf1a30Sjl { 87725cf1a30Sjl int rv; 87825cf1a30Sjl 87925cf1a30Sjl *idx = arr->min_index; 88025cf1a30Sjl while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 88125cf1a30Sjl *idx += 1; 88225cf1a30Sjl 88325cf1a30Sjl return (rv); 88425cf1a30Sjl } 88525cf1a30Sjl 88625cf1a30Sjl static int 88725cf1a30Sjl drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val) 88825cf1a30Sjl { 88925cf1a30Sjl int rv; 89025cf1a30Sjl 89125cf1a30Sjl *idx += 1; 89225cf1a30Sjl while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 89325cf1a30Sjl *idx += 1; 89425cf1a30Sjl 89525cf1a30Sjl return (rv); 89625cf1a30Sjl } 89725cf1a30Sjl 89825cf1a30Sjl static void 89925cf1a30Sjl drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t)) 90025cf1a30Sjl { 90125cf1a30Sjl drmachid_t val; 90225cf1a30Sjl int idx; 90325cf1a30Sjl int rv; 90425cf1a30Sjl 90525cf1a30Sjl rv = drmach_array_first(arr, &idx, &val); 90625cf1a30Sjl while (rv == 0) { 90725cf1a30Sjl (*disposer)(val); 90825cf1a30Sjl rv = drmach_array_next(arr, &idx, &val); 90925cf1a30Sjl } 91025cf1a30Sjl 91125cf1a30Sjl kmem_free(arr->arr, arr->arr_sz); 91225cf1a30Sjl kmem_free(arr, sizeof (*arr)); 91325cf1a30Sjl } 91425cf1a30Sjl 91525cf1a30Sjl static drmach_board_t * 91625cf1a30Sjl drmach_get_board_by_bnum(int bnum) 91725cf1a30Sjl { 91825cf1a30Sjl drmachid_t id; 91925cf1a30Sjl 92025cf1a30Sjl if (drmach_array_get(drmach_boards, bnum, &id) == 0) 92125cf1a30Sjl return ((drmach_board_t *)id); 92225cf1a30Sjl else 92325cf1a30Sjl return (NULL); 92425cf1a30Sjl } 92525cf1a30Sjl 92625cf1a30Sjl static pnode_t 92725cf1a30Sjl drmach_node_get_dnode(drmach_node_t *np) 92825cf1a30Sjl { 92925cf1a30Sjl return (np->get_dnode(np)); 93025cf1a30Sjl } 93125cf1a30Sjl 93225cf1a30Sjl /*ARGSUSED*/ 93325cf1a30Sjl sbd_error_t * 93425cf1a30Sjl drmach_configure(drmachid_t id, int flags) 93525cf1a30Sjl { 93625cf1a30Sjl drmach_device_t *dp; 93725cf1a30Sjl sbd_error_t *err = NULL; 93825cf1a30Sjl dev_info_t *rdip; 93925cf1a30Sjl dev_info_t *fdip = NULL; 94025cf1a30Sjl 94125cf1a30Sjl if (DRMACH_IS_CPU_ID(id)) { 94225cf1a30Sjl return (NULL); 94325cf1a30Sjl } 94425cf1a30Sjl if (!DRMACH_IS_DEVICE_ID(id)) 94525cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 94625cf1a30Sjl dp = id; 94725cf1a30Sjl rdip = dp->node->n_getdip(dp->node); 94825cf1a30Sjl 94925cf1a30Sjl ASSERT(rdip); 95025cf1a30Sjl 95125cf1a30Sjl ASSERT(e_ddi_branch_held(rdip)); 95225cf1a30Sjl 95325cf1a30Sjl if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) { 95425cf1a30Sjl char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 95525cf1a30Sjl dev_info_t *dip = (fdip != NULL) ? fdip : rdip; 95625cf1a30Sjl 95725cf1a30Sjl (void) ddi_pathname(dip, path); 95825cf1a30Sjl err = drerr_new(1, EOPL_DRVFAIL, path); 95925cf1a30Sjl 96025cf1a30Sjl kmem_free(path, MAXPATHLEN); 96125cf1a30Sjl 96225cf1a30Sjl /* If non-NULL, fdip is returned held and must be released */ 96325cf1a30Sjl if (fdip != NULL) 96425cf1a30Sjl ddi_release_devi(fdip); 96525cf1a30Sjl } 96625cf1a30Sjl 96725cf1a30Sjl return (err); 96825cf1a30Sjl } 96925cf1a30Sjl 97025cf1a30Sjl 97125cf1a30Sjl static sbd_error_t * 97225cf1a30Sjl drmach_device_new(drmach_node_t *node, 97325cf1a30Sjl drmach_board_t *bp, int portid, drmachid_t *idp) 97425cf1a30Sjl { 97525cf1a30Sjl int i; 97625cf1a30Sjl int rv; 97725cf1a30Sjl drmach_device_t proto; 97825cf1a30Sjl sbd_error_t *err; 97925cf1a30Sjl char name[OBP_MAXDRVNAME]; 98025cf1a30Sjl 98125cf1a30Sjl rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME); 98225cf1a30Sjl if (rv) { 98325cf1a30Sjl /* every node is expected to have a name */ 984*e98fafb9Sjl err = drerr_new(1, EOPL_GETPROP, "device node %s: property %s", 985*e98fafb9Sjl ddi_node_name(node->n_getdip(node)), "name"); 98625cf1a30Sjl return (err); 98725cf1a30Sjl } 98825cf1a30Sjl 98925cf1a30Sjl /* 99025cf1a30Sjl * The node currently being examined is not listed in the name2type[] 99125cf1a30Sjl * array. In this case, the node is no interest to drmach. Both 99225cf1a30Sjl * dp and err are initialized here to yield nothing (no device or 99325cf1a30Sjl * error structure) for this case. 99425cf1a30Sjl */ 99525cf1a30Sjl i = drmach_name2type_idx(name); 99625cf1a30Sjl 99725cf1a30Sjl 99825cf1a30Sjl if (i < 0) { 99925cf1a30Sjl *idp = (drmachid_t)0; 100025cf1a30Sjl return (NULL); 100125cf1a30Sjl } 100225cf1a30Sjl 100325cf1a30Sjl /* device specific new function will set unum */ 100425cf1a30Sjl 100525cf1a30Sjl bzero(&proto, sizeof (proto)); 100625cf1a30Sjl proto.type = drmach_name2type[i].type; 100725cf1a30Sjl proto.bp = bp; 100825cf1a30Sjl proto.node = node; 100925cf1a30Sjl proto.portid = portid; 101025cf1a30Sjl 101125cf1a30Sjl return (drmach_name2type[i].new(&proto, idp)); 101225cf1a30Sjl } 101325cf1a30Sjl 101425cf1a30Sjl static void 101525cf1a30Sjl drmach_device_dispose(drmachid_t id) 101625cf1a30Sjl { 101725cf1a30Sjl drmach_device_t *self = id; 101825cf1a30Sjl 101925cf1a30Sjl self->cm.dispose(id); 102025cf1a30Sjl } 102125cf1a30Sjl 102225cf1a30Sjl 102325cf1a30Sjl static drmach_board_t * 102425cf1a30Sjl drmach_board_new(int bnum, int boot_board) 102525cf1a30Sjl { 102625cf1a30Sjl static sbd_error_t *drmach_board_release(drmachid_t); 102725cf1a30Sjl static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *); 102825cf1a30Sjl 102925cf1a30Sjl drmach_board_t *bp; 103025cf1a30Sjl 103125cf1a30Sjl bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP); 103225cf1a30Sjl 103325cf1a30Sjl bp->cm.isa = (void *)drmach_board_new; 103425cf1a30Sjl bp->cm.release = drmach_board_release; 103525cf1a30Sjl bp->cm.status = drmach_board_status; 103625cf1a30Sjl 103725cf1a30Sjl (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name)); 103825cf1a30Sjl 103925cf1a30Sjl bp->bnum = bnum; 104025cf1a30Sjl bp->devices = NULL; 104125cf1a30Sjl bp->connected = boot_board; 104225cf1a30Sjl bp->tree = drmach_node_new(); 104325cf1a30Sjl bp->assigned = boot_board; 104425cf1a30Sjl bp->powered = boot_board; 104525cf1a30Sjl bp->boot_board = boot_board; 104625cf1a30Sjl 104725cf1a30Sjl /* 104825cf1a30Sjl * If this is not bootup initialization, we have to wait till 104925cf1a30Sjl * IKP sets up the device nodes in drmach_board_connect(). 105025cf1a30Sjl */ 105125cf1a30Sjl if (boot_board) 105225cf1a30Sjl drmach_setup_core_info(bp); 105325cf1a30Sjl 105425cf1a30Sjl drmach_array_set(drmach_boards, bnum, bp); 105525cf1a30Sjl return (bp); 105625cf1a30Sjl } 105725cf1a30Sjl 105825cf1a30Sjl static void 105925cf1a30Sjl drmach_board_dispose(drmachid_t id) 106025cf1a30Sjl { 106125cf1a30Sjl drmach_board_t *bp; 106225cf1a30Sjl 106325cf1a30Sjl ASSERT(DRMACH_IS_BOARD_ID(id)); 106425cf1a30Sjl bp = id; 106525cf1a30Sjl 106625cf1a30Sjl if (bp->tree) 106725cf1a30Sjl drmach_node_dispose(bp->tree); 106825cf1a30Sjl 106925cf1a30Sjl if (bp->devices) 107025cf1a30Sjl drmach_array_dispose(bp->devices, drmach_device_dispose); 107125cf1a30Sjl 107225cf1a30Sjl kmem_free(bp, sizeof (*bp)); 107325cf1a30Sjl } 107425cf1a30Sjl 107525cf1a30Sjl static sbd_error_t * 107625cf1a30Sjl drmach_board_status(drmachid_t id, drmach_status_t *stat) 107725cf1a30Sjl { 107825cf1a30Sjl sbd_error_t *err = NULL; 107925cf1a30Sjl drmach_board_t *bp; 108025cf1a30Sjl 108125cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 108225cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 108325cf1a30Sjl bp = id; 108425cf1a30Sjl 108525cf1a30Sjl stat->assigned = bp->assigned; 108625cf1a30Sjl stat->powered = bp->powered; 108725cf1a30Sjl stat->busy = 0; /* assume not busy */ 108825cf1a30Sjl stat->configured = 0; /* assume not configured */ 108925cf1a30Sjl stat->empty = 0; 109025cf1a30Sjl stat->cond = bp->cond = SBD_COND_OK; 109125cf1a30Sjl strncpy(stat->type, "System Brd", sizeof (stat->type)); 109225cf1a30Sjl stat->info[0] = '\0'; 109325cf1a30Sjl 109425cf1a30Sjl if (bp->devices) { 109525cf1a30Sjl int rv; 109625cf1a30Sjl int d_idx; 109725cf1a30Sjl drmachid_t d_id; 109825cf1a30Sjl 109925cf1a30Sjl rv = drmach_array_first(bp->devices, &d_idx, &d_id); 110025cf1a30Sjl while (rv == 0) { 110125cf1a30Sjl drmach_status_t d_stat; 110225cf1a30Sjl 110325cf1a30Sjl err = drmach_i_status(d_id, &d_stat); 110425cf1a30Sjl if (err) 110525cf1a30Sjl break; 110625cf1a30Sjl 110725cf1a30Sjl stat->busy |= d_stat.busy; 110825cf1a30Sjl stat->configured |= d_stat.configured; 110925cf1a30Sjl 111025cf1a30Sjl rv = drmach_array_next(bp->devices, &d_idx, &d_id); 111125cf1a30Sjl } 111225cf1a30Sjl } 111325cf1a30Sjl 111425cf1a30Sjl return (err); 111525cf1a30Sjl } 111625cf1a30Sjl 111725cf1a30Sjl int 111825cf1a30Sjl drmach_board_is_floating(drmachid_t id) 111925cf1a30Sjl { 112025cf1a30Sjl drmach_board_t *bp; 112125cf1a30Sjl 112225cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 112325cf1a30Sjl return (0); 112425cf1a30Sjl 112525cf1a30Sjl bp = (drmach_board_t *)id; 112625cf1a30Sjl 112725cf1a30Sjl return ((drmach_domain.floating & (1 << bp->bnum)) ? 1 : 0); 112825cf1a30Sjl } 112925cf1a30Sjl 113025cf1a30Sjl static int 113125cf1a30Sjl drmach_init(void) 113225cf1a30Sjl { 113325cf1a30Sjl dev_info_t *rdip; 113425cf1a30Sjl int i, rv, len; 113525cf1a30Sjl int *floating; 113625cf1a30Sjl 113725cf1a30Sjl rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL); 113825cf1a30Sjl 113925cf1a30Sjl drmach_boards = drmach_array_new(0, MAX_BOARDS - 1); 114025cf1a30Sjl 114125cf1a30Sjl rdip = ddi_root_node(); 114225cf1a30Sjl 114325cf1a30Sjl if (ddi_getproplen(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 1144*e98fafb9Sjl "floating-boards", &len) != DDI_PROP_SUCCESS) { 114525cf1a30Sjl cmn_err(CE_WARN, "Cannot get floating-boards proplen\n"); 114625cf1a30Sjl } else { 114725cf1a30Sjl floating = (int *)kmem_alloc(len, KM_SLEEP); 1148*e98fafb9Sjl rv = ddi_prop_op(DDI_DEV_T_ANY, rdip, PROP_LEN_AND_VAL_BUF, 1149*e98fafb9Sjl DDI_PROP_DONTPASS, "floating-boards", (caddr_t)floating, 1150*e98fafb9Sjl &len); 115125cf1a30Sjl if (rv != DDI_PROP_SUCCESS) { 115225cf1a30Sjl cmn_err(CE_WARN, "Cannot get floating-boards prop\n"); 115325cf1a30Sjl } else { 115425cf1a30Sjl drmach_domain.floating = 0; 115525cf1a30Sjl for (i = 0; i < len / sizeof (int); i++) { 115625cf1a30Sjl drmach_domain.floating |= (1 << floating[i]); 115725cf1a30Sjl } 115825cf1a30Sjl } 115925cf1a30Sjl kmem_free(floating, len); 116025cf1a30Sjl } 116125cf1a30Sjl drmach_domain.allow_dr = opl_check_dr_status(); 116225cf1a30Sjl 116325cf1a30Sjl rdip = ddi_get_child(ddi_root_node()); 116425cf1a30Sjl do { 116525cf1a30Sjl int bnum; 116625cf1a30Sjl drmachid_t id; 116725cf1a30Sjl 116825cf1a30Sjl bnum = -1; 1169*e98fafb9Sjl bnum = ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 1170*e98fafb9Sjl OBP_BOARDNUM, -1); 117125cf1a30Sjl if (bnum == -1) 117225cf1a30Sjl continue; 117325cf1a30Sjl 117425cf1a30Sjl if (drmach_array_get(drmach_boards, bnum, &id) == -1) { 1175*e98fafb9Sjl cmn_err(CE_WARN, "Device node 0x%p has invalid " 1176*e98fafb9Sjl "property value, %s=%d", rdip, OBP_BOARDNUM, bnum); 117725cf1a30Sjl goto error; 117825cf1a30Sjl } else if (id == NULL) { 117925cf1a30Sjl (void) drmach_board_new(bnum, 1); 118025cf1a30Sjl } 118125cf1a30Sjl } while ((rdip = ddi_get_next_sibling(rdip)) != NULL); 118225cf1a30Sjl 118325cf1a30Sjl opl_hold_devtree(); 118425cf1a30Sjl 118525cf1a30Sjl /* 118625cf1a30Sjl * Initialize the IKP feature. 118725cf1a30Sjl * 118825cf1a30Sjl * This can be done only after DR has acquired a hold on all the 118925cf1a30Sjl * device nodes that are interesting to IKP. 119025cf1a30Sjl */ 119125cf1a30Sjl if (opl_init_cfg() != 0) { 119225cf1a30Sjl cmn_err(CE_WARN, "DR - IKP initialization failed"); 119325cf1a30Sjl 119425cf1a30Sjl opl_release_devtree(); 119525cf1a30Sjl 119625cf1a30Sjl goto error; 119725cf1a30Sjl } 119825cf1a30Sjl 119925cf1a30Sjl return (0); 120025cf1a30Sjl error: 120125cf1a30Sjl drmach_array_dispose(drmach_boards, drmach_board_dispose); 120225cf1a30Sjl rw_destroy(&drmach_boards_rwlock); 120325cf1a30Sjl return (ENXIO); 120425cf1a30Sjl } 120525cf1a30Sjl 120625cf1a30Sjl static void 120725cf1a30Sjl drmach_fini(void) 120825cf1a30Sjl { 120925cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_WRITER); 121025cf1a30Sjl drmach_array_dispose(drmach_boards, drmach_board_dispose); 121125cf1a30Sjl drmach_boards = NULL; 121225cf1a30Sjl rw_exit(&drmach_boards_rwlock); 121325cf1a30Sjl 121425cf1a30Sjl /* 121525cf1a30Sjl * Walk immediate children of the root devinfo node 121625cf1a30Sjl * releasing holds acquired on branches in drmach_init() 121725cf1a30Sjl */ 121825cf1a30Sjl 121925cf1a30Sjl opl_release_devtree(); 122025cf1a30Sjl 122125cf1a30Sjl rw_destroy(&drmach_boards_rwlock); 122225cf1a30Sjl } 122325cf1a30Sjl 122425cf1a30Sjl /* 122525cf1a30Sjl * Each system board contains 2 Oberon PCI bridge and 122625cf1a30Sjl * 1 CMUCH. 122725cf1a30Sjl * Each oberon has 2 channels. 122825cf1a30Sjl * Each channel has 2 pci-ex leaf. 122925cf1a30Sjl * Each CMUCH has 1 pci bus. 123025cf1a30Sjl * 123125cf1a30Sjl * 123225cf1a30Sjl * Device Path: 123325cf1a30Sjl * /pci@<portid>,reg 123425cf1a30Sjl * 123525cf1a30Sjl * where 123625cf1a30Sjl * portid[10] = 0 123725cf1a30Sjl * portid[9:0] = LLEAF_ID[9:0] of the Oberon Channel 123825cf1a30Sjl * 123925cf1a30Sjl * LLEAF_ID[9:8] = 0 124025cf1a30Sjl * LLEAF_ID[8:4] = LSB_ID[4:0] 124125cf1a30Sjl * LLEAF_ID[3:1] = IO Channel#[2:0] (0,1,2,3 for Oberon) 124225cf1a30Sjl * channel 4 is pcicmu 124325cf1a30Sjl * LLEAF_ID[0] = PCI Leaf Number (0 for leaf-A, 1 for leaf-B) 124425cf1a30Sjl * 124525cf1a30Sjl * Properties: 124625cf1a30Sjl * name = pci 124725cf1a30Sjl * device_type = "pciex" 124825cf1a30Sjl * board# = LSBID 124925cf1a30Sjl * reg = int32 * 2, Oberon CSR space of the leaf and the UBC space 125025cf1a30Sjl * portid = Jupiter Bus Device ID ((LSB_ID << 3)|pciport#) 125125cf1a30Sjl */ 125225cf1a30Sjl 125325cf1a30Sjl static sbd_error_t * 125425cf1a30Sjl drmach_io_new(drmach_device_t *proto, drmachid_t *idp) 125525cf1a30Sjl { 125625cf1a30Sjl drmach_io_t *ip; 125725cf1a30Sjl 125825cf1a30Sjl int portid; 125925cf1a30Sjl 126025cf1a30Sjl portid = proto->portid; 126125cf1a30Sjl ASSERT(portid != -1); 126225cf1a30Sjl proto->unum = portid & (MAX_IO_UNITS_PER_BOARD - 1); 126325cf1a30Sjl 126425cf1a30Sjl ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP); 126525cf1a30Sjl bcopy(proto, &ip->dev, sizeof (ip->dev)); 126625cf1a30Sjl ip->dev.node = drmach_node_dup(proto->node); 126725cf1a30Sjl ip->dev.cm.isa = (void *)drmach_io_new; 126825cf1a30Sjl ip->dev.cm.dispose = drmach_io_dispose; 126925cf1a30Sjl ip->dev.cm.release = drmach_io_release; 127025cf1a30Sjl ip->dev.cm.status = drmach_io_status; 127125cf1a30Sjl ip->channel = (portid >> 1) & 0x7; 127225cf1a30Sjl ip->leaf = (portid & 0x1); 127325cf1a30Sjl 127425cf1a30Sjl snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d", 1275*e98fafb9Sjl ip->dev.type, ip->dev.unum); 127625cf1a30Sjl 127725cf1a30Sjl *idp = (drmachid_t)ip; 127825cf1a30Sjl return (NULL); 127925cf1a30Sjl } 128025cf1a30Sjl 128125cf1a30Sjl 128225cf1a30Sjl static void 128325cf1a30Sjl drmach_io_dispose(drmachid_t id) 128425cf1a30Sjl { 128525cf1a30Sjl drmach_io_t *self; 128625cf1a30Sjl 128725cf1a30Sjl ASSERT(DRMACH_IS_IO_ID(id)); 128825cf1a30Sjl 128925cf1a30Sjl self = id; 129025cf1a30Sjl if (self->dev.node) 129125cf1a30Sjl drmach_node_dispose(self->dev.node); 129225cf1a30Sjl 129325cf1a30Sjl kmem_free(self, sizeof (*self)); 129425cf1a30Sjl } 129525cf1a30Sjl 129625cf1a30Sjl /*ARGSUSED*/ 129725cf1a30Sjl sbd_error_t * 129825cf1a30Sjl drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts) 129925cf1a30Sjl { 130025cf1a30Sjl drmach_board_t *bp = (drmach_board_t *)id; 130125cf1a30Sjl sbd_error_t *err = NULL; 130225cf1a30Sjl 130325cf1a30Sjl /* allow status and ncm operations to always succeed */ 130425cf1a30Sjl if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) { 130525cf1a30Sjl return (NULL); 130625cf1a30Sjl } 130725cf1a30Sjl 130825cf1a30Sjl /* check all other commands for the required option string */ 130925cf1a30Sjl 131025cf1a30Sjl if ((opts->size > 0) && (opts->copts != NULL)) { 131125cf1a30Sjl 131225cf1a30Sjl DRMACH_PR("platform options: %s\n", opts->copts); 131325cf1a30Sjl 131425cf1a30Sjl if (strstr(opts->copts, "opldr") == NULL) { 131525cf1a30Sjl err = drerr_new(1, EOPL_SUPPORT, NULL); 131625cf1a30Sjl } 131725cf1a30Sjl } else { 131825cf1a30Sjl err = drerr_new(1, EOPL_SUPPORT, NULL); 131925cf1a30Sjl } 132025cf1a30Sjl 132125cf1a30Sjl if (!err && id && DRMACH_IS_BOARD_ID(id)) { 132225cf1a30Sjl switch (cmd) { 132325cf1a30Sjl case SBD_CMD_TEST: 132425cf1a30Sjl case SBD_CMD_STATUS: 132525cf1a30Sjl case SBD_CMD_GETNCM: 132625cf1a30Sjl break; 132725cf1a30Sjl case SBD_CMD_CONNECT: 132825cf1a30Sjl if (bp->connected) 132925cf1a30Sjl err = drerr_new(0, ESBD_STATE, NULL); 133025cf1a30Sjl else if (!drmach_domain.allow_dr) 1331*e98fafb9Sjl err = drerr_new(1, EOPL_SUPPORT, NULL); 133225cf1a30Sjl break; 133325cf1a30Sjl case SBD_CMD_DISCONNECT: 133425cf1a30Sjl if (!bp->connected) 133525cf1a30Sjl err = drerr_new(0, ESBD_STATE, NULL); 133625cf1a30Sjl else if (!drmach_domain.allow_dr) 1337*e98fafb9Sjl err = drerr_new(1, EOPL_SUPPORT, NULL); 133825cf1a30Sjl break; 133925cf1a30Sjl default: 134025cf1a30Sjl if (!drmach_domain.allow_dr) 1341*e98fafb9Sjl err = drerr_new(1, EOPL_SUPPORT, NULL); 134225cf1a30Sjl break; 134325cf1a30Sjl 134425cf1a30Sjl } 134525cf1a30Sjl } 134625cf1a30Sjl 134725cf1a30Sjl return (err); 134825cf1a30Sjl } 134925cf1a30Sjl 135025cf1a30Sjl /*ARGSUSED*/ 135125cf1a30Sjl sbd_error_t * 135225cf1a30Sjl drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts) 135325cf1a30Sjl { 135425cf1a30Sjl return (NULL); 135525cf1a30Sjl } 135625cf1a30Sjl 135725cf1a30Sjl sbd_error_t * 135825cf1a30Sjl drmach_board_assign(int bnum, drmachid_t *id) 135925cf1a30Sjl { 136025cf1a30Sjl sbd_error_t *err = NULL; 136125cf1a30Sjl 136225cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_WRITER); 136325cf1a30Sjl 136425cf1a30Sjl if (drmach_array_get(drmach_boards, bnum, id) == -1) { 136525cf1a30Sjl err = drerr_new(1, EOPL_BNUM, "%d", bnum); 136625cf1a30Sjl } else { 136725cf1a30Sjl drmach_board_t *bp; 136825cf1a30Sjl 136925cf1a30Sjl if (*id) 137025cf1a30Sjl rw_downgrade(&drmach_boards_rwlock); 137125cf1a30Sjl 137225cf1a30Sjl bp = *id; 137325cf1a30Sjl if (!(*id)) 137425cf1a30Sjl bp = *id = 1375*e98fafb9Sjl (drmachid_t)drmach_board_new(bnum, 0); 137625cf1a30Sjl bp->assigned = 1; 137725cf1a30Sjl } 137825cf1a30Sjl 137925cf1a30Sjl rw_exit(&drmach_boards_rwlock); 138025cf1a30Sjl 138125cf1a30Sjl return (err); 138225cf1a30Sjl } 138325cf1a30Sjl 138425cf1a30Sjl /*ARGSUSED*/ 138525cf1a30Sjl sbd_error_t * 138625cf1a30Sjl drmach_board_connect(drmachid_t id, drmach_opts_t *opts) 138725cf1a30Sjl { 1388*e98fafb9Sjl extern int cpu_alljupiter; 138925cf1a30Sjl drmach_board_t *obj = (drmach_board_t *)id; 1390*e98fafb9Sjl unsigned cpu_impl; 139125cf1a30Sjl 139225cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 139325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 139425cf1a30Sjl 1395*e98fafb9Sjl if (opl_probe_sb(obj->bnum, &cpu_impl) != 0) 13968eafe49bSbm return (drerr_new(1, EOPL_PROBE, NULL)); 139725cf1a30Sjl 1398*e98fafb9Sjl if (cpu_alljupiter) { 1399*e98fafb9Sjl if (cpu_impl & (1 << OLYMPUS_C_IMPL)) 1400*e98fafb9Sjl return (drerr_new(1, EOPL_MIXED_CPU, NULL)); 1401*e98fafb9Sjl } 1402*e98fafb9Sjl 140325cf1a30Sjl (void) prom_attach_notice(obj->bnum); 140425cf1a30Sjl 140525cf1a30Sjl drmach_setup_core_info(obj); 140625cf1a30Sjl 140725cf1a30Sjl obj->connected = 1; 140825cf1a30Sjl 140925cf1a30Sjl return (NULL); 141025cf1a30Sjl } 141125cf1a30Sjl 141225cf1a30Sjl static int drmach_cache_flush_flag[NCPU]; 141325cf1a30Sjl 141425cf1a30Sjl /*ARGSUSED*/ 141525cf1a30Sjl static void 141625cf1a30Sjl drmach_flush_cache(uint64_t id, uint64_t dummy) 141725cf1a30Sjl { 141825cf1a30Sjl extern void cpu_flush_ecache(void); 141925cf1a30Sjl 142025cf1a30Sjl cpu_flush_ecache(); 142125cf1a30Sjl drmach_cache_flush_flag[id] = 0; 142225cf1a30Sjl } 142325cf1a30Sjl 142425cf1a30Sjl static void 142525cf1a30Sjl drmach_flush_all() 142625cf1a30Sjl { 142725cf1a30Sjl cpuset_t xc_cpuset; 142825cf1a30Sjl int i; 142925cf1a30Sjl 143025cf1a30Sjl xc_cpuset = cpu_ready_set; 143125cf1a30Sjl for (i = 0; i < NCPU; i++) { 143225cf1a30Sjl if (CPU_IN_SET(xc_cpuset, i)) { 143325cf1a30Sjl drmach_cache_flush_flag[i] = 1; 143425cf1a30Sjl xc_one(i, drmach_flush_cache, i, 0); 143525cf1a30Sjl while (drmach_cache_flush_flag[i]) { 143625cf1a30Sjl DELAY(1000); 143725cf1a30Sjl } 143825cf1a30Sjl } 143925cf1a30Sjl } 144025cf1a30Sjl } 144125cf1a30Sjl 144225cf1a30Sjl static int 144325cf1a30Sjl drmach_disconnect_cpus(drmach_board_t *bp) 144425cf1a30Sjl { 144525cf1a30Sjl int i, bnum; 144625cf1a30Sjl 144725cf1a30Sjl bnum = bp->bnum; 144825cf1a30Sjl 144925cf1a30Sjl for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) { 1450*e98fafb9Sjl if (bp->cores[i].core_present) { 1451*e98fafb9Sjl if (bp->cores[i].core_started) 1452*e98fafb9Sjl return (-1); 1453*e98fafb9Sjl if (bp->cores[i].core_hotadded) { 1454*e98fafb9Sjl if (drmach_add_remove_cpu(bnum, i, 1455*e98fafb9Sjl HOTREMOVE_CPU)) { 1456*e98fafb9Sjl cmn_err(CE_WARN, "Failed to remove " 1457*e98fafb9Sjl "CMP %d on board %d\n", i, bnum); 1458*e98fafb9Sjl return (-1); 1459*e98fafb9Sjl } 1460*e98fafb9Sjl } 146125cf1a30Sjl } 146225cf1a30Sjl } 146325cf1a30Sjl return (0); 146425cf1a30Sjl } 146525cf1a30Sjl 146625cf1a30Sjl /*ARGSUSED*/ 146725cf1a30Sjl sbd_error_t * 146825cf1a30Sjl drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts) 146925cf1a30Sjl { 147025cf1a30Sjl drmach_board_t *obj; 147125cf1a30Sjl int rv = 0; 147225cf1a30Sjl sbd_error_t *err = NULL; 147325cf1a30Sjl 1474ddf95635Sbm if (DRMACH_NULL_ID(id)) 1475ddf95635Sbm return (NULL); 147625cf1a30Sjl 147725cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 147825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 147925cf1a30Sjl 148025cf1a30Sjl obj = (drmach_board_t *)id; 148125cf1a30Sjl 148225cf1a30Sjl if (drmach_disconnect_cpus(obj)) { 14838eafe49bSbm err = drerr_new(1, EOPL_DEPROBE, obj->cm.name); 148425cf1a30Sjl return (err); 148525cf1a30Sjl } 148625cf1a30Sjl 148725cf1a30Sjl rv = opl_unprobe_sb(obj->bnum); 148825cf1a30Sjl 148925cf1a30Sjl if (rv == 0) { 149025cf1a30Sjl prom_detach_notice(obj->bnum); 149125cf1a30Sjl obj->connected = 0; 149225cf1a30Sjl 149325cf1a30Sjl } else 14948eafe49bSbm err = drerr_new(1, EOPL_DEPROBE, obj->cm.name); 149525cf1a30Sjl 149625cf1a30Sjl return (err); 149725cf1a30Sjl } 149825cf1a30Sjl 149925cf1a30Sjl static int 150025cf1a30Sjl drmach_get_portid(drmach_node_t *np) 150125cf1a30Sjl { 150225cf1a30Sjl int portid; 150325cf1a30Sjl char type[OBP_MAXPROPNAME]; 150425cf1a30Sjl 150525cf1a30Sjl if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0) 150625cf1a30Sjl return (portid); 150725cf1a30Sjl 150825cf1a30Sjl /* 150925cf1a30Sjl * Get the device_type property to see if we should 151025cf1a30Sjl * continue processing this node. 151125cf1a30Sjl */ 151225cf1a30Sjl if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0) 151325cf1a30Sjl return (-1); 151425cf1a30Sjl 151525cf1a30Sjl if (strcmp(type, OPL_CPU_NODE) == 0) { 151625cf1a30Sjl /* 151725cf1a30Sjl * We return cpuid because it has no portid 151825cf1a30Sjl */ 151925cf1a30Sjl if (np->n_getprop(np, "cpuid", &portid, sizeof (portid)) == 0) 152025cf1a30Sjl return (portid); 152125cf1a30Sjl } 152225cf1a30Sjl 152325cf1a30Sjl return (-1); 152425cf1a30Sjl } 152525cf1a30Sjl 152625cf1a30Sjl /* 152725cf1a30Sjl * This is a helper function to determine if a given 152825cf1a30Sjl * node should be considered for a dr operation according 152925cf1a30Sjl * to predefined dr type nodes and the node's name. 153025cf1a30Sjl * Formal Parameter : The name of a device node. 153125cf1a30Sjl * Return Value: -1, name does not map to a valid dr type. 153225cf1a30Sjl * A value greater or equal to 0, name is a valid dr type. 153325cf1a30Sjl */ 153425cf1a30Sjl static int 153525cf1a30Sjl drmach_name2type_idx(char *name) 153625cf1a30Sjl { 153725cf1a30Sjl int index, ntypes; 153825cf1a30Sjl 153925cf1a30Sjl if (name == NULL) 154025cf1a30Sjl return (-1); 154125cf1a30Sjl 154225cf1a30Sjl /* 154325cf1a30Sjl * Determine how many possible types are currently supported 154425cf1a30Sjl * for dr. 154525cf1a30Sjl */ 154625cf1a30Sjl ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]); 154725cf1a30Sjl 154825cf1a30Sjl /* Determine if the node's name correspond to a predefined type. */ 154925cf1a30Sjl for (index = 0; index < ntypes; index++) { 155025cf1a30Sjl if (strcmp(drmach_name2type[index].name, name) == 0) 155125cf1a30Sjl /* The node is an allowed type for dr. */ 155225cf1a30Sjl return (index); 155325cf1a30Sjl } 155425cf1a30Sjl 155525cf1a30Sjl /* 155625cf1a30Sjl * If the name of the node does not map to any of the 155725cf1a30Sjl * types in the array drmach_name2type then the node is not of 155825cf1a30Sjl * interest to dr. 155925cf1a30Sjl */ 156025cf1a30Sjl return (-1); 156125cf1a30Sjl } 156225cf1a30Sjl 156325cf1a30Sjl /* 156425cf1a30Sjl * there is some complication on OPL: 156525cf1a30Sjl * - pseudo-mc nodes do not have portid property 156625cf1a30Sjl * - portid[9:5] of cmp node is LSB #, portid[7:3] of pci is LSB# 156725cf1a30Sjl * - cmp has board# 156825cf1a30Sjl * - core and cpu nodes do not have portid and board# properties 156925cf1a30Sjl * starcat uses portid to derive the board# but that does not work 157025cf1a30Sjl * for us. starfire reads board# property to filter the devices. 157125cf1a30Sjl * That does not work either. So for these specific device, 157225cf1a30Sjl * we use specific hard coded methods to get the board# - 157325cf1a30Sjl * cpu: LSB# = CPUID[9:5] 157425cf1a30Sjl */ 157525cf1a30Sjl 157625cf1a30Sjl static int 157725cf1a30Sjl drmach_board_find_devices_cb(drmach_node_walk_args_t *args) 157825cf1a30Sjl { 157925cf1a30Sjl drmach_node_t *node = args->node; 158025cf1a30Sjl drmach_board_cb_data_t *data = args->data; 158125cf1a30Sjl drmach_board_t *obj = data->obj; 158225cf1a30Sjl 158325cf1a30Sjl int rv, portid; 158425cf1a30Sjl int bnum; 158525cf1a30Sjl drmachid_t id; 158625cf1a30Sjl drmach_device_t *device; 158725cf1a30Sjl char name[OBP_MAXDRVNAME]; 158825cf1a30Sjl 158925cf1a30Sjl portid = drmach_get_portid(node); 159025cf1a30Sjl /* 159125cf1a30Sjl * core, cpu and pseudo-mc do not have portid 159225cf1a30Sjl * we use cpuid as the portid of the cpu node 159325cf1a30Sjl * for pseudo-mc, we do not use portid info. 159425cf1a30Sjl */ 159525cf1a30Sjl 159625cf1a30Sjl rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME); 159725cf1a30Sjl if (rv) 159825cf1a30Sjl return (0); 159925cf1a30Sjl 160025cf1a30Sjl 160125cf1a30Sjl rv = node->n_getprop(node, OBP_BOARDNUM, &bnum, sizeof (bnum)); 160225cf1a30Sjl 160325cf1a30Sjl if (rv) { 160425cf1a30Sjl /* 160525cf1a30Sjl * cpu does not have board# property. We use 160625cf1a30Sjl * CPUID[9:5] 160725cf1a30Sjl */ 160825cf1a30Sjl if (strcmp("cpu", name) == 0) { 160925cf1a30Sjl bnum = (portid >> 5) & 0x1f; 161025cf1a30Sjl } else 161125cf1a30Sjl return (0); 161225cf1a30Sjl } 161325cf1a30Sjl 161425cf1a30Sjl 161525cf1a30Sjl if (bnum != obj->bnum) 161625cf1a30Sjl return (0); 161725cf1a30Sjl 161825cf1a30Sjl if (drmach_name2type_idx(name) < 0) { 161925cf1a30Sjl return (0); 162025cf1a30Sjl } 162125cf1a30Sjl 162225cf1a30Sjl /* 162325cf1a30Sjl * Create a device data structure from this node data. 162425cf1a30Sjl * The call may yield nothing if the node is not of interest 162525cf1a30Sjl * to drmach. 162625cf1a30Sjl */ 162725cf1a30Sjl data->err = drmach_device_new(node, obj, portid, &id); 162825cf1a30Sjl if (data->err) 162925cf1a30Sjl return (-1); 163025cf1a30Sjl else if (!id) { 163125cf1a30Sjl /* 163225cf1a30Sjl * drmach_device_new examined the node we passed in 163325cf1a30Sjl * and determined that it was one not of interest to 163425cf1a30Sjl * drmach. So, it is skipped. 163525cf1a30Sjl */ 163625cf1a30Sjl return (0); 163725cf1a30Sjl } 163825cf1a30Sjl 163925cf1a30Sjl rv = drmach_array_set(obj->devices, data->ndevs++, id); 164025cf1a30Sjl if (rv) { 164125cf1a30Sjl data->err = DRMACH_INTERNAL_ERROR(); 164225cf1a30Sjl return (-1); 164325cf1a30Sjl } 164425cf1a30Sjl device = id; 164525cf1a30Sjl 164625cf1a30Sjl data->err = (*data->found)(data->a, device->type, device->unum, id); 164725cf1a30Sjl return (data->err == NULL ? 0 : -1); 164825cf1a30Sjl } 164925cf1a30Sjl 165025cf1a30Sjl sbd_error_t * 165125cf1a30Sjl drmach_board_find_devices(drmachid_t id, void *a, 165225cf1a30Sjl sbd_error_t *(*found)(void *a, const char *, int, drmachid_t)) 165325cf1a30Sjl { 165425cf1a30Sjl drmach_board_t *bp = (drmach_board_t *)id; 165525cf1a30Sjl sbd_error_t *err; 165625cf1a30Sjl int max_devices; 165725cf1a30Sjl int rv; 165825cf1a30Sjl drmach_board_cb_data_t data; 165925cf1a30Sjl 166025cf1a30Sjl 166125cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 166225cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 166325cf1a30Sjl 166425cf1a30Sjl max_devices = MAX_CPU_UNITS_PER_BOARD; 166525cf1a30Sjl max_devices += MAX_MEM_UNITS_PER_BOARD; 166625cf1a30Sjl max_devices += MAX_IO_UNITS_PER_BOARD; 166725cf1a30Sjl 166825cf1a30Sjl bp->devices = drmach_array_new(0, max_devices); 166925cf1a30Sjl 167025cf1a30Sjl if (bp->tree == NULL) 167125cf1a30Sjl bp->tree = drmach_node_new(); 167225cf1a30Sjl 167325cf1a30Sjl data.obj = bp; 167425cf1a30Sjl data.ndevs = 0; 167525cf1a30Sjl data.found = found; 167625cf1a30Sjl data.a = a; 167725cf1a30Sjl data.err = NULL; 167825cf1a30Sjl 167925cf1a30Sjl rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb); 168025cf1a30Sjl if (rv == 0) 168125cf1a30Sjl err = NULL; 168225cf1a30Sjl else { 168325cf1a30Sjl drmach_array_dispose(bp->devices, drmach_device_dispose); 168425cf1a30Sjl bp->devices = NULL; 168525cf1a30Sjl 168625cf1a30Sjl if (data.err) 168725cf1a30Sjl err = data.err; 168825cf1a30Sjl else 168925cf1a30Sjl err = DRMACH_INTERNAL_ERROR(); 169025cf1a30Sjl } 169125cf1a30Sjl 169225cf1a30Sjl return (err); 169325cf1a30Sjl } 169425cf1a30Sjl 169525cf1a30Sjl int 169625cf1a30Sjl drmach_board_lookup(int bnum, drmachid_t *id) 169725cf1a30Sjl { 169825cf1a30Sjl int rv = 0; 169925cf1a30Sjl 170025cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_READER); 170125cf1a30Sjl if (drmach_array_get(drmach_boards, bnum, id)) { 170225cf1a30Sjl *id = 0; 170325cf1a30Sjl rv = -1; 170425cf1a30Sjl } 170525cf1a30Sjl rw_exit(&drmach_boards_rwlock); 170625cf1a30Sjl return (rv); 170725cf1a30Sjl } 170825cf1a30Sjl 170925cf1a30Sjl sbd_error_t * 171025cf1a30Sjl drmach_board_name(int bnum, char *buf, int buflen) 171125cf1a30Sjl { 171225cf1a30Sjl snprintf(buf, buflen, "SB%d", bnum); 171325cf1a30Sjl return (NULL); 171425cf1a30Sjl } 171525cf1a30Sjl 171625cf1a30Sjl sbd_error_t * 171725cf1a30Sjl drmach_board_poweroff(drmachid_t id) 171825cf1a30Sjl { 171925cf1a30Sjl drmach_board_t *bp; 172025cf1a30Sjl sbd_error_t *err; 172125cf1a30Sjl drmach_status_t stat; 172225cf1a30Sjl 1723ddf95635Sbm if (DRMACH_NULL_ID(id)) 1724ddf95635Sbm return (NULL); 1725ddf95635Sbm 172625cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 172725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 172825cf1a30Sjl bp = id; 172925cf1a30Sjl 173025cf1a30Sjl err = drmach_board_status(id, &stat); 173125cf1a30Sjl 173225cf1a30Sjl if (!err) { 173325cf1a30Sjl if (stat.configured || stat.busy) 173425cf1a30Sjl err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name); 173525cf1a30Sjl else { 173625cf1a30Sjl bp->powered = 0; 173725cf1a30Sjl } 173825cf1a30Sjl } 173925cf1a30Sjl return (err); 174025cf1a30Sjl } 174125cf1a30Sjl 174225cf1a30Sjl sbd_error_t * 174325cf1a30Sjl drmach_board_poweron(drmachid_t id) 174425cf1a30Sjl { 174525cf1a30Sjl drmach_board_t *bp; 174625cf1a30Sjl 174725cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 174825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 174925cf1a30Sjl bp = id; 175025cf1a30Sjl 175125cf1a30Sjl bp->powered = 1; 175225cf1a30Sjl 175325cf1a30Sjl return (NULL); 175425cf1a30Sjl } 175525cf1a30Sjl 175625cf1a30Sjl static sbd_error_t * 175725cf1a30Sjl drmach_board_release(drmachid_t id) 175825cf1a30Sjl { 175925cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 176025cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 176125cf1a30Sjl return (NULL); 176225cf1a30Sjl } 176325cf1a30Sjl 176425cf1a30Sjl /*ARGSUSED*/ 176525cf1a30Sjl sbd_error_t * 176625cf1a30Sjl drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force) 176725cf1a30Sjl { 176825cf1a30Sjl return (NULL); 176925cf1a30Sjl } 177025cf1a30Sjl 177125cf1a30Sjl sbd_error_t * 177225cf1a30Sjl drmach_board_unassign(drmachid_t id) 177325cf1a30Sjl { 177425cf1a30Sjl drmach_board_t *bp; 177525cf1a30Sjl sbd_error_t *err; 177625cf1a30Sjl drmach_status_t stat; 177725cf1a30Sjl 1778ddf95635Sbm if (DRMACH_NULL_ID(id)) 1779ddf95635Sbm return (NULL); 178025cf1a30Sjl 178125cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) { 178225cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 178325cf1a30Sjl } 178425cf1a30Sjl bp = id; 178525cf1a30Sjl 178625cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_WRITER); 178725cf1a30Sjl 178825cf1a30Sjl err = drmach_board_status(id, &stat); 178925cf1a30Sjl if (err) { 179025cf1a30Sjl rw_exit(&drmach_boards_rwlock); 179125cf1a30Sjl return (err); 179225cf1a30Sjl } 179325cf1a30Sjl if (stat.configured || stat.busy) { 179425cf1a30Sjl err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name); 179525cf1a30Sjl } else { 179625cf1a30Sjl if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0) 179725cf1a30Sjl err = DRMACH_INTERNAL_ERROR(); 179825cf1a30Sjl else 179925cf1a30Sjl drmach_board_dispose(bp); 180025cf1a30Sjl } 180125cf1a30Sjl rw_exit(&drmach_boards_rwlock); 180225cf1a30Sjl return (err); 180325cf1a30Sjl } 180425cf1a30Sjl 180525cf1a30Sjl /* 180625cf1a30Sjl * We have to do more on OPL - e.g. set up sram tte, read cpuid, strand id, 180725cf1a30Sjl * implementation #, etc 180825cf1a30Sjl */ 180925cf1a30Sjl 181025cf1a30Sjl static sbd_error_t * 181125cf1a30Sjl drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp) 181225cf1a30Sjl { 181325cf1a30Sjl static void drmach_cpu_dispose(drmachid_t); 181425cf1a30Sjl static sbd_error_t *drmach_cpu_release(drmachid_t); 181525cf1a30Sjl static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *); 181625cf1a30Sjl 181725cf1a30Sjl int portid; 181825cf1a30Sjl drmach_cpu_t *cp = NULL; 181925cf1a30Sjl 182025cf1a30Sjl /* portid is CPUID of the node */ 182125cf1a30Sjl portid = proto->portid; 182225cf1a30Sjl ASSERT(portid != -1); 182325cf1a30Sjl 182425cf1a30Sjl /* unum = (CMP/CHIP ID) + (ON_BOARD_CORE_NUM * MAX_CMPID_PER_BOARD) */ 182525cf1a30Sjl proto->unum = ((portid/OPL_MAX_CPUID_PER_CMP) & 1826*e98fafb9Sjl (OPL_MAX_CMPID_PER_BOARD - 1)) + 1827*e98fafb9Sjl ((portid & (OPL_MAX_CPUID_PER_CMP - 1)) * 1828*e98fafb9Sjl (OPL_MAX_CMPID_PER_BOARD)); 182925cf1a30Sjl 183025cf1a30Sjl cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP); 183125cf1a30Sjl bcopy(proto, &cp->dev, sizeof (cp->dev)); 183225cf1a30Sjl cp->dev.node = drmach_node_dup(proto->node); 183325cf1a30Sjl cp->dev.cm.isa = (void *)drmach_cpu_new; 183425cf1a30Sjl cp->dev.cm.dispose = drmach_cpu_dispose; 183525cf1a30Sjl cp->dev.cm.release = drmach_cpu_release; 183625cf1a30Sjl cp->dev.cm.status = drmach_cpu_status; 183725cf1a30Sjl 183825cf1a30Sjl snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d", 1839*e98fafb9Sjl cp->dev.type, cp->dev.unum); 184025cf1a30Sjl 184125cf1a30Sjl /* 184225cf1a30Sjl * CPU ID representation 184325cf1a30Sjl * CPUID[9:5] = SB# 184425cf1a30Sjl * CPUID[4:3] = Chip# 184525cf1a30Sjl * CPUID[2:1] = Core# (Only 2 core for OPL) 184625cf1a30Sjl * CPUID[0:0] = Strand# 184725cf1a30Sjl */ 184825cf1a30Sjl 184925cf1a30Sjl /* 185025cf1a30Sjl * reg property of the strand contains strand ID 185125cf1a30Sjl * reg property of the parent node contains core ID 185225cf1a30Sjl * We should use them. 185325cf1a30Sjl */ 185425cf1a30Sjl cp->cpuid = portid; 185525cf1a30Sjl cp->sb = (portid >> 5) & 0x1f; 185625cf1a30Sjl cp->chipid = (portid >> 3) & 0x3; 185725cf1a30Sjl cp->coreid = (portid >> 1) & 0x3; 185825cf1a30Sjl cp->strandid = portid & 0x1; 185925cf1a30Sjl 186025cf1a30Sjl *idp = (drmachid_t)cp; 186125cf1a30Sjl return (NULL); 186225cf1a30Sjl } 186325cf1a30Sjl 186425cf1a30Sjl 186525cf1a30Sjl static void 186625cf1a30Sjl drmach_cpu_dispose(drmachid_t id) 186725cf1a30Sjl { 186825cf1a30Sjl drmach_cpu_t *self; 186925cf1a30Sjl 187025cf1a30Sjl ASSERT(DRMACH_IS_CPU_ID(id)); 187125cf1a30Sjl 187225cf1a30Sjl self = id; 187325cf1a30Sjl if (self->dev.node) 187425cf1a30Sjl drmach_node_dispose(self->dev.node); 187525cf1a30Sjl 187625cf1a30Sjl kmem_free(self, sizeof (*self)); 187725cf1a30Sjl } 187825cf1a30Sjl 187925cf1a30Sjl static int 188025cf1a30Sjl drmach_cpu_start(struct cpu *cp) 188125cf1a30Sjl { 188225cf1a30Sjl int cpuid = cp->cpu_id; 188325cf1a30Sjl extern int restart_other_cpu(int); 188425cf1a30Sjl 188525cf1a30Sjl ASSERT(MUTEX_HELD(&cpu_lock)); 188625cf1a30Sjl ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0); 188725cf1a30Sjl 188825cf1a30Sjl cp->cpu_flags &= ~CPU_POWEROFF; 188925cf1a30Sjl 189025cf1a30Sjl /* 189125cf1a30Sjl * NOTE: restart_other_cpu pauses cpus during the 189225cf1a30Sjl * slave cpu start. This helps to quiesce the 189325cf1a30Sjl * bus traffic a bit which makes the tick sync 189425cf1a30Sjl * routine in the prom more robust. 189525cf1a30Sjl */ 189625cf1a30Sjl DRMACH_PR("COLD START for cpu (%d)\n", cpuid); 189725cf1a30Sjl 189825cf1a30Sjl restart_other_cpu(cpuid); 189925cf1a30Sjl 190025cf1a30Sjl return (0); 190125cf1a30Sjl } 190225cf1a30Sjl 190325cf1a30Sjl static sbd_error_t * 190425cf1a30Sjl drmach_cpu_release(drmachid_t id) 190525cf1a30Sjl { 190625cf1a30Sjl if (!DRMACH_IS_CPU_ID(id)) 190725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 190825cf1a30Sjl 190925cf1a30Sjl return (NULL); 191025cf1a30Sjl } 191125cf1a30Sjl 191225cf1a30Sjl static sbd_error_t * 191325cf1a30Sjl drmach_cpu_status(drmachid_t id, drmach_status_t *stat) 191425cf1a30Sjl { 191525cf1a30Sjl drmach_cpu_t *cp; 191625cf1a30Sjl drmach_device_t *dp; 191725cf1a30Sjl 191825cf1a30Sjl ASSERT(DRMACH_IS_CPU_ID(id)); 191925cf1a30Sjl cp = (drmach_cpu_t *)id; 192025cf1a30Sjl dp = &cp->dev; 192125cf1a30Sjl 192225cf1a30Sjl stat->assigned = dp->bp->assigned; 192325cf1a30Sjl stat->powered = dp->bp->powered; 192425cf1a30Sjl mutex_enter(&cpu_lock); 192525cf1a30Sjl stat->configured = (cpu_get(cp->cpuid) != NULL); 192625cf1a30Sjl mutex_exit(&cpu_lock); 192725cf1a30Sjl stat->busy = dp->busy; 192825cf1a30Sjl strncpy(stat->type, dp->type, sizeof (stat->type)); 192925cf1a30Sjl stat->info[0] = '\0'; 193025cf1a30Sjl 193125cf1a30Sjl return (NULL); 193225cf1a30Sjl } 193325cf1a30Sjl 193425cf1a30Sjl sbd_error_t * 193525cf1a30Sjl drmach_cpu_disconnect(drmachid_t id) 193625cf1a30Sjl { 193725cf1a30Sjl 193825cf1a30Sjl if (!DRMACH_IS_CPU_ID(id)) 193925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 194025cf1a30Sjl 194125cf1a30Sjl return (NULL); 194225cf1a30Sjl } 194325cf1a30Sjl 194425cf1a30Sjl sbd_error_t * 194525cf1a30Sjl drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid) 194625cf1a30Sjl { 194725cf1a30Sjl drmach_cpu_t *cpu; 194825cf1a30Sjl 194925cf1a30Sjl if (!DRMACH_IS_CPU_ID(id)) 195025cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 195125cf1a30Sjl cpu = (drmach_cpu_t *)id; 195225cf1a30Sjl 195325cf1a30Sjl /* get from cpu directly on OPL */ 195425cf1a30Sjl *cpuid = cpu->cpuid; 195525cf1a30Sjl return (NULL); 195625cf1a30Sjl } 195725cf1a30Sjl 195825cf1a30Sjl sbd_error_t * 195925cf1a30Sjl drmach_cpu_get_impl(drmachid_t id, int *ip) 196025cf1a30Sjl { 196125cf1a30Sjl drmach_device_t *cpu; 196225cf1a30Sjl drmach_node_t *np; 196325cf1a30Sjl drmach_node_t pp; 196425cf1a30Sjl int impl; 196525cf1a30Sjl char type[OBP_MAXPROPNAME]; 196625cf1a30Sjl 196725cf1a30Sjl if (!DRMACH_IS_CPU_ID(id)) 196825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 196925cf1a30Sjl 197025cf1a30Sjl cpu = id; 197125cf1a30Sjl np = cpu->node; 197225cf1a30Sjl 197325cf1a30Sjl if (np->get_parent(np, &pp) != 0) { 197425cf1a30Sjl return (DRMACH_INTERNAL_ERROR()); 197525cf1a30Sjl } 197625cf1a30Sjl 197725cf1a30Sjl /* the parent should be core */ 197825cf1a30Sjl 197925cf1a30Sjl if (pp.n_getprop(&pp, "device_type", &type, sizeof (type)) != 0) { 198025cf1a30Sjl return (drerr_new(0, EOPL_GETPROP, NULL)); 198125cf1a30Sjl } 198225cf1a30Sjl 198325cf1a30Sjl if (strcmp(type, OPL_CORE_NODE) == 0) { 1984*e98fafb9Sjl if (pp.n_getprop(&pp, "implementation#", &impl, 1985*e98fafb9Sjl sizeof (impl)) != 0) { 198625cf1a30Sjl return (drerr_new(0, EOPL_GETPROP, NULL)); 198725cf1a30Sjl } 198825cf1a30Sjl } else { 198925cf1a30Sjl return (DRMACH_INTERNAL_ERROR()); 199025cf1a30Sjl } 199125cf1a30Sjl 199225cf1a30Sjl *ip = impl; 199325cf1a30Sjl 199425cf1a30Sjl return (NULL); 199525cf1a30Sjl } 199625cf1a30Sjl 199725cf1a30Sjl sbd_error_t * 199825cf1a30Sjl drmach_get_dip(drmachid_t id, dev_info_t **dip) 199925cf1a30Sjl { 200025cf1a30Sjl drmach_device_t *dp; 200125cf1a30Sjl 200225cf1a30Sjl if (!DRMACH_IS_DEVICE_ID(id)) 200325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 200425cf1a30Sjl dp = id; 200525cf1a30Sjl 200625cf1a30Sjl *dip = dp->node->n_getdip(dp->node); 200725cf1a30Sjl return (NULL); 200825cf1a30Sjl } 200925cf1a30Sjl 201025cf1a30Sjl sbd_error_t * 201125cf1a30Sjl drmach_io_is_attached(drmachid_t id, int *yes) 201225cf1a30Sjl { 201325cf1a30Sjl drmach_device_t *dp; 201425cf1a30Sjl dev_info_t *dip; 201525cf1a30Sjl int state; 201625cf1a30Sjl 201725cf1a30Sjl if (!DRMACH_IS_IO_ID(id)) 201825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 201925cf1a30Sjl dp = id; 202025cf1a30Sjl 202125cf1a30Sjl dip = dp->node->n_getdip(dp->node); 202225cf1a30Sjl if (dip == NULL) { 202325cf1a30Sjl *yes = 0; 202425cf1a30Sjl return (NULL); 202525cf1a30Sjl } 202625cf1a30Sjl 202725cf1a30Sjl state = ddi_get_devstate(dip); 202825cf1a30Sjl *yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) || 202925cf1a30Sjl (state == DDI_DEVSTATE_UP)); 203025cf1a30Sjl 203125cf1a30Sjl return (NULL); 203225cf1a30Sjl } 203325cf1a30Sjl 203425cf1a30Sjl struct drmach_io_cb { 203525cf1a30Sjl char *name; /* name of the node */ 203625cf1a30Sjl int (*func)(dev_info_t *); 203725cf1a30Sjl int rv; 203868ac2337Sjl dev_info_t *dip; 203925cf1a30Sjl }; 204025cf1a30Sjl 204125cf1a30Sjl #define DRMACH_IO_POST_ATTACH 0 204225cf1a30Sjl #define DRMACH_IO_PRE_RELEASE 1 204325cf1a30Sjl 204425cf1a30Sjl static int 204525cf1a30Sjl drmach_io_cb_check(dev_info_t *dip, void *arg) 204625cf1a30Sjl { 204725cf1a30Sjl struct drmach_io_cb *p = (struct drmach_io_cb *)arg; 204825cf1a30Sjl char name[OBP_MAXDRVNAME]; 204925cf1a30Sjl int len = OBP_MAXDRVNAME; 205025cf1a30Sjl 2051*e98fafb9Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "name", 205225cf1a30Sjl (caddr_t)name, &len) != DDI_PROP_SUCCESS) { 205325cf1a30Sjl return (DDI_WALK_PRUNECHILD); 205425cf1a30Sjl } 205525cf1a30Sjl 205625cf1a30Sjl if (strcmp(name, p->name) == 0) { 205768ac2337Sjl ndi_hold_devi(dip); 205868ac2337Sjl p->dip = dip; 205925cf1a30Sjl return (DDI_WALK_TERMINATE); 206025cf1a30Sjl } 206125cf1a30Sjl 206225cf1a30Sjl return (DDI_WALK_CONTINUE); 206325cf1a30Sjl } 206425cf1a30Sjl 206525cf1a30Sjl 206625cf1a30Sjl static int 206725cf1a30Sjl drmach_console_ops(drmachid_t *id, int state) 206825cf1a30Sjl { 206925cf1a30Sjl drmach_io_t *obj = (drmach_io_t *)id; 207025cf1a30Sjl struct drmach_io_cb arg; 207125cf1a30Sjl int (*msudetp)(dev_info_t *); 207225cf1a30Sjl int (*msuattp)(dev_info_t *); 207325cf1a30Sjl dev_info_t *dip, *pdip; 207425cf1a30Sjl int circ; 207525cf1a30Sjl 207625cf1a30Sjl /* 4 is pcicmu channel */ 207725cf1a30Sjl if (obj->channel != 4) 207825cf1a30Sjl return (0); 207925cf1a30Sjl 208025cf1a30Sjl arg.name = "serial"; 208125cf1a30Sjl arg.func = NULL; 208225cf1a30Sjl if (state == DRMACH_IO_PRE_RELEASE) { 208325cf1a30Sjl msudetp = (int (*)(dev_info_t *)) 208425cf1a30Sjl modgetsymvalue("oplmsu_dr_detach", 0); 208525cf1a30Sjl if (msudetp != NULL) 208625cf1a30Sjl arg.func = msudetp; 208725cf1a30Sjl } else if (state == DRMACH_IO_POST_ATTACH) { 208825cf1a30Sjl msuattp = (int (*)(dev_info_t *)) 208925cf1a30Sjl modgetsymvalue("oplmsu_dr_attach", 0); 209025cf1a30Sjl if (msuattp != NULL) 209125cf1a30Sjl arg.func = msuattp; 209268ac2337Sjl } else { 209325cf1a30Sjl return (0); 209468ac2337Sjl } 209525cf1a30Sjl 209625cf1a30Sjl if (arg.func == NULL) { 209725cf1a30Sjl return (0); 209825cf1a30Sjl } 209925cf1a30Sjl 210025cf1a30Sjl arg.rv = 0; 210168ac2337Sjl arg.dip = NULL; 210225cf1a30Sjl 210325cf1a30Sjl dip = obj->dev.node->n_getdip(obj->dev.node); 210425cf1a30Sjl if (pdip = ddi_get_parent(dip)) { 210525cf1a30Sjl ndi_hold_devi(pdip); 210625cf1a30Sjl ndi_devi_enter(pdip, &circ); 210725cf1a30Sjl } else { 210825cf1a30Sjl /* this cannot happen unless something bad happens */ 210925cf1a30Sjl return (-1); 211025cf1a30Sjl } 211125cf1a30Sjl 211225cf1a30Sjl ddi_walk_devs(dip, drmach_io_cb_check, (void *)&arg); 211325cf1a30Sjl 211468ac2337Sjl ndi_devi_exit(pdip, circ); 211568ac2337Sjl ndi_rele_devi(pdip); 211668ac2337Sjl 211768ac2337Sjl if (arg.dip) { 211868ac2337Sjl arg.rv = (*arg.func)(arg.dip); 211968ac2337Sjl ndi_rele_devi(arg.dip); 212068ac2337Sjl } else { 212168ac2337Sjl arg.rv = -1; 212225cf1a30Sjl } 212325cf1a30Sjl 212425cf1a30Sjl return (arg.rv); 212525cf1a30Sjl } 212625cf1a30Sjl 212725cf1a30Sjl sbd_error_t * 212825cf1a30Sjl drmach_io_pre_release(drmachid_t id) 212925cf1a30Sjl { 213025cf1a30Sjl int rv; 213125cf1a30Sjl 213225cf1a30Sjl if (!DRMACH_IS_IO_ID(id)) 213325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 213425cf1a30Sjl 213525cf1a30Sjl rv = drmach_console_ops(id, DRMACH_IO_PRE_RELEASE); 213625cf1a30Sjl 213725cf1a30Sjl if (rv != 0) 213825cf1a30Sjl cmn_err(CE_WARN, "IO callback failed in pre-release\n"); 213925cf1a30Sjl 214025cf1a30Sjl return (NULL); 214125cf1a30Sjl } 214225cf1a30Sjl 214325cf1a30Sjl static sbd_error_t * 214425cf1a30Sjl drmach_io_release(drmachid_t id) 214525cf1a30Sjl { 214625cf1a30Sjl if (!DRMACH_IS_IO_ID(id)) 214725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 214825cf1a30Sjl return (NULL); 214925cf1a30Sjl } 215025cf1a30Sjl 215125cf1a30Sjl sbd_error_t * 215225cf1a30Sjl drmach_io_unrelease(drmachid_t id) 215325cf1a30Sjl { 215425cf1a30Sjl if (!DRMACH_IS_IO_ID(id)) 215525cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 215625cf1a30Sjl return (NULL); 215725cf1a30Sjl } 215825cf1a30Sjl 215925cf1a30Sjl /*ARGSUSED*/ 216025cf1a30Sjl sbd_error_t * 216125cf1a30Sjl drmach_io_post_release(drmachid_t id) 216225cf1a30Sjl { 216325cf1a30Sjl return (NULL); 216425cf1a30Sjl } 216525cf1a30Sjl 216625cf1a30Sjl /*ARGSUSED*/ 216725cf1a30Sjl sbd_error_t * 216825cf1a30Sjl drmach_io_post_attach(drmachid_t id) 216925cf1a30Sjl { 217025cf1a30Sjl int rv; 217125cf1a30Sjl 217225cf1a30Sjl if (!DRMACH_IS_IO_ID(id)) 217325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 217425cf1a30Sjl 217525cf1a30Sjl rv = drmach_console_ops(id, DRMACH_IO_POST_ATTACH); 217625cf1a30Sjl 217725cf1a30Sjl if (rv != 0) 217825cf1a30Sjl cmn_err(CE_WARN, "IO callback failed in post-attach\n"); 217925cf1a30Sjl 218025cf1a30Sjl return (0); 218125cf1a30Sjl } 218225cf1a30Sjl 218325cf1a30Sjl static sbd_error_t * 218425cf1a30Sjl drmach_io_status(drmachid_t id, drmach_status_t *stat) 218525cf1a30Sjl { 218625cf1a30Sjl drmach_device_t *dp; 218725cf1a30Sjl sbd_error_t *err; 218825cf1a30Sjl int configured; 218925cf1a30Sjl 219025cf1a30Sjl ASSERT(DRMACH_IS_IO_ID(id)); 219125cf1a30Sjl dp = id; 219225cf1a30Sjl 219325cf1a30Sjl err = drmach_io_is_attached(id, &configured); 219425cf1a30Sjl if (err) 219525cf1a30Sjl return (err); 219625cf1a30Sjl 219725cf1a30Sjl stat->assigned = dp->bp->assigned; 219825cf1a30Sjl stat->powered = dp->bp->powered; 219925cf1a30Sjl stat->configured = (configured != 0); 220025cf1a30Sjl stat->busy = dp->busy; 220125cf1a30Sjl strncpy(stat->type, dp->type, sizeof (stat->type)); 220225cf1a30Sjl stat->info[0] = '\0'; 220325cf1a30Sjl 220425cf1a30Sjl return (NULL); 220525cf1a30Sjl } 220625cf1a30Sjl 220725cf1a30Sjl static sbd_error_t * 220825cf1a30Sjl drmach_mem_new(drmach_device_t *proto, drmachid_t *idp) 220925cf1a30Sjl { 221025cf1a30Sjl static void drmach_mem_dispose(drmachid_t); 221125cf1a30Sjl static sbd_error_t *drmach_mem_release(drmachid_t); 221225cf1a30Sjl static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *); 221325cf1a30Sjl dev_info_t *dip; 221468ac2337Sjl int rv; 221525cf1a30Sjl 221625cf1a30Sjl drmach_mem_t *mp; 221725cf1a30Sjl 221868ac2337Sjl rv = 0; 221968ac2337Sjl 222068ac2337Sjl if ((proto->node->n_getproplen(proto->node, "mc-addr", &rv) < 0) || 2221*e98fafb9Sjl (rv <= 0)) { 222268ac2337Sjl *idp = (drmachid_t)0; 222368ac2337Sjl return (NULL); 222468ac2337Sjl } 222568ac2337Sjl 222625cf1a30Sjl mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP); 222725cf1a30Sjl proto->unum = 0; 222825cf1a30Sjl 222925cf1a30Sjl bcopy(proto, &mp->dev, sizeof (mp->dev)); 223025cf1a30Sjl mp->dev.node = drmach_node_dup(proto->node); 223125cf1a30Sjl mp->dev.cm.isa = (void *)drmach_mem_new; 223225cf1a30Sjl mp->dev.cm.dispose = drmach_mem_dispose; 223325cf1a30Sjl mp->dev.cm.release = drmach_mem_release; 223425cf1a30Sjl mp->dev.cm.status = drmach_mem_status; 223525cf1a30Sjl 2236*e98fafb9Sjl snprintf(mp->dev.cm.name, sizeof (mp->dev.cm.name), "%s", mp->dev.type); 223725cf1a30Sjl 223825cf1a30Sjl dip = mp->dev.node->n_getdip(mp->dev.node); 223925cf1a30Sjl if (drmach_setup_mc_info(dip, mp) != 0) { 22408eafe49bSbm return (drerr_new(1, EOPL_MC_SETUP, NULL)); 224125cf1a30Sjl } 224225cf1a30Sjl 224368ac2337Sjl /* make sure we do not create memoryless nodes */ 224468ac2337Sjl if (mp->nbytes == 0) { 224568ac2337Sjl *idp = (drmachid_t)NULL; 224668ac2337Sjl kmem_free(mp, sizeof (drmach_mem_t)); 224768ac2337Sjl } else 224868ac2337Sjl *idp = (drmachid_t)mp; 224968ac2337Sjl 225025cf1a30Sjl return (NULL); 225125cf1a30Sjl } 225225cf1a30Sjl 225325cf1a30Sjl static void 225425cf1a30Sjl drmach_mem_dispose(drmachid_t id) 225525cf1a30Sjl { 225625cf1a30Sjl drmach_mem_t *mp; 225725cf1a30Sjl 225825cf1a30Sjl ASSERT(DRMACH_IS_MEM_ID(id)); 225925cf1a30Sjl 226025cf1a30Sjl 226125cf1a30Sjl mp = id; 226225cf1a30Sjl 226325cf1a30Sjl if (mp->dev.node) 226425cf1a30Sjl drmach_node_dispose(mp->dev.node); 226525cf1a30Sjl 226625cf1a30Sjl if (mp->memlist) { 226725cf1a30Sjl memlist_delete(mp->memlist); 226825cf1a30Sjl mp->memlist = NULL; 226925cf1a30Sjl } 227068ac2337Sjl 227168ac2337Sjl kmem_free(mp, sizeof (*mp)); 227225cf1a30Sjl } 227325cf1a30Sjl 227425cf1a30Sjl sbd_error_t * 227525cf1a30Sjl drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size) 227625cf1a30Sjl { 227725cf1a30Sjl pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT); 227825cf1a30Sjl pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT); 227925cf1a30Sjl int rv; 228025cf1a30Sjl 228125cf1a30Sjl ASSERT(size != 0); 228225cf1a30Sjl 228325cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 228425cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 228525cf1a30Sjl 228685f58038Sdp rv = kcage_range_add(basepfn, npages, KCAGE_DOWN); 228725cf1a30Sjl if (rv == ENOMEM) { 228825cf1a30Sjl cmn_err(CE_WARN, "%ld megabytes not available to kernel cage", 2289*e98fafb9Sjl (size == 0 ? 0 : size / MBYTE)); 229025cf1a30Sjl } else if (rv != 0) { 229125cf1a30Sjl /* catch this in debug kernels */ 229225cf1a30Sjl ASSERT(0); 229325cf1a30Sjl 2294*e98fafb9Sjl cmn_err(CE_WARN, "unexpected kcage_range_add return value %d", 2295*e98fafb9Sjl rv); 229625cf1a30Sjl } 229725cf1a30Sjl 229825cf1a30Sjl if (rv) { 229925cf1a30Sjl return (DRMACH_INTERNAL_ERROR()); 230025cf1a30Sjl } 230125cf1a30Sjl else 230225cf1a30Sjl return (NULL); 230325cf1a30Sjl } 230425cf1a30Sjl 230525cf1a30Sjl sbd_error_t * 230625cf1a30Sjl drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size) 230725cf1a30Sjl { 230825cf1a30Sjl pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT); 230925cf1a30Sjl pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT); 231025cf1a30Sjl int rv; 231125cf1a30Sjl 231225cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 231325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 231425cf1a30Sjl 231525cf1a30Sjl if (size > 0) { 231625cf1a30Sjl rv = kcage_range_delete_post_mem_del(basepfn, npages); 231725cf1a30Sjl if (rv != 0) { 231825cf1a30Sjl cmn_err(CE_WARN, 231925cf1a30Sjl "unexpected kcage_range_delete_post_mem_del" 232025cf1a30Sjl " return value %d", rv); 232125cf1a30Sjl return (DRMACH_INTERNAL_ERROR()); 232225cf1a30Sjl } 232325cf1a30Sjl } 232425cf1a30Sjl 232525cf1a30Sjl return (NULL); 232625cf1a30Sjl } 232725cf1a30Sjl 232825cf1a30Sjl sbd_error_t * 232925cf1a30Sjl drmach_mem_disable(drmachid_t id) 233025cf1a30Sjl { 233125cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 233225cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 233325cf1a30Sjl else { 233425cf1a30Sjl drmach_flush_all(); 233525cf1a30Sjl return (NULL); 233625cf1a30Sjl } 233725cf1a30Sjl } 233825cf1a30Sjl 233925cf1a30Sjl sbd_error_t * 234025cf1a30Sjl drmach_mem_enable(drmachid_t id) 234125cf1a30Sjl { 234225cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 234325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 234425cf1a30Sjl else 234525cf1a30Sjl return (NULL); 234625cf1a30Sjl } 234725cf1a30Sjl 234825cf1a30Sjl sbd_error_t * 234925cf1a30Sjl drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem) 235025cf1a30Sjl { 235125cf1a30Sjl drmach_mem_t *mp; 235225cf1a30Sjl 235325cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 235425cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 235525cf1a30Sjl 235625cf1a30Sjl mp = (drmach_mem_t *)id; 235725cf1a30Sjl 235825cf1a30Sjl /* 235925cf1a30Sjl * This is only used by dr to round up/down the memory 236025cf1a30Sjl * for copying. Our unit of memory isolation is 64 MB. 236125cf1a30Sjl */ 236225cf1a30Sjl 236325cf1a30Sjl mem->mi_alignment_mask = (64 * 1024 * 1024 - 1); 236425cf1a30Sjl mem->mi_basepa = mp->base_pa; 236525cf1a30Sjl mem->mi_size = mp->nbytes; 236625cf1a30Sjl mem->mi_slice_size = mp->slice_size; 236725cf1a30Sjl 236825cf1a30Sjl return (NULL); 236925cf1a30Sjl } 237025cf1a30Sjl 237125cf1a30Sjl sbd_error_t * 237225cf1a30Sjl drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa) 237325cf1a30Sjl { 237425cf1a30Sjl drmach_mem_t *mp; 237525cf1a30Sjl 237625cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 237725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 237825cf1a30Sjl 237925cf1a30Sjl mp = (drmach_mem_t *)id; 238025cf1a30Sjl 238125cf1a30Sjl *pa = mp->base_pa; 238225cf1a30Sjl return (NULL); 238325cf1a30Sjl } 238425cf1a30Sjl 238525cf1a30Sjl sbd_error_t * 238625cf1a30Sjl drmach_mem_get_memlist(drmachid_t id, struct memlist **ml) 238725cf1a30Sjl { 238825cf1a30Sjl drmach_mem_t *mem; 2389b307f191Sbm #ifdef DEBUG 239025cf1a30Sjl int rv; 2391b307f191Sbm #endif 239225cf1a30Sjl struct memlist *mlist; 239325cf1a30Sjl 239425cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 239525cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 239625cf1a30Sjl 239725cf1a30Sjl mem = (drmach_mem_t *)id; 239825cf1a30Sjl mlist = memlist_dup(mem->memlist); 239925cf1a30Sjl 240025cf1a30Sjl #ifdef DEBUG 240125cf1a30Sjl /* 240225cf1a30Sjl * Make sure the incoming memlist doesn't already 240325cf1a30Sjl * intersect with what's present in the system (phys_install). 240425cf1a30Sjl */ 240525cf1a30Sjl memlist_read_lock(); 240625cf1a30Sjl rv = memlist_intersect(phys_install, mlist); 240725cf1a30Sjl memlist_read_unlock(); 240825cf1a30Sjl if (rv) { 2409*e98fafb9Sjl DRMACH_PR("Derived memlist intersects with phys_install\n"); 241025cf1a30Sjl memlist_dump(mlist); 241125cf1a30Sjl 241225cf1a30Sjl DRMACH_PR("phys_install memlist:\n"); 241325cf1a30Sjl memlist_dump(phys_install); 241425cf1a30Sjl 241525cf1a30Sjl memlist_delete(mlist); 241625cf1a30Sjl return (DRMACH_INTERNAL_ERROR()); 241725cf1a30Sjl } 241825cf1a30Sjl 241925cf1a30Sjl DRMACH_PR("Derived memlist:"); 242025cf1a30Sjl memlist_dump(mlist); 242125cf1a30Sjl #endif 242225cf1a30Sjl *ml = mlist; 242325cf1a30Sjl 242425cf1a30Sjl return (NULL); 242525cf1a30Sjl } 242625cf1a30Sjl 242725cf1a30Sjl sbd_error_t * 242825cf1a30Sjl drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes) 242925cf1a30Sjl { 243025cf1a30Sjl drmach_mem_t *mem; 243125cf1a30Sjl 243225cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 243325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 243425cf1a30Sjl 243525cf1a30Sjl mem = (drmach_mem_t *)id; 243625cf1a30Sjl 243725cf1a30Sjl *bytes = mem->slice_size; 243825cf1a30Sjl 243925cf1a30Sjl return (NULL); 244025cf1a30Sjl } 244125cf1a30Sjl 244225cf1a30Sjl 244325cf1a30Sjl /* ARGSUSED */ 244425cf1a30Sjl processorid_t 244525cf1a30Sjl drmach_mem_cpu_affinity(drmachid_t id) 244625cf1a30Sjl { 244725cf1a30Sjl return (CPU_CURRENT); 244825cf1a30Sjl } 244925cf1a30Sjl 245025cf1a30Sjl static sbd_error_t * 245125cf1a30Sjl drmach_mem_release(drmachid_t id) 245225cf1a30Sjl { 245325cf1a30Sjl if (!DRMACH_IS_MEM_ID(id)) 245425cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 245525cf1a30Sjl return (NULL); 245625cf1a30Sjl } 245725cf1a30Sjl 245825cf1a30Sjl static sbd_error_t * 245925cf1a30Sjl drmach_mem_status(drmachid_t id, drmach_status_t *stat) 246025cf1a30Sjl { 246125cf1a30Sjl drmach_mem_t *dp; 246225cf1a30Sjl uint64_t pa, slice_size; 246325cf1a30Sjl struct memlist *ml; 246425cf1a30Sjl 246525cf1a30Sjl ASSERT(DRMACH_IS_MEM_ID(id)); 246625cf1a30Sjl dp = id; 246725cf1a30Sjl 246825cf1a30Sjl /* get starting physical address of target memory */ 246925cf1a30Sjl pa = dp->base_pa; 247025cf1a30Sjl 247125cf1a30Sjl /* round down to slice boundary */ 247225cf1a30Sjl slice_size = dp->slice_size; 247325cf1a30Sjl pa &= ~(slice_size - 1); 247425cf1a30Sjl 247525cf1a30Sjl /* stop at first span that is in slice */ 247625cf1a30Sjl memlist_read_lock(); 247725cf1a30Sjl for (ml = phys_install; ml; ml = ml->next) 247825cf1a30Sjl if (ml->address >= pa && ml->address < pa + slice_size) 247925cf1a30Sjl break; 248025cf1a30Sjl memlist_read_unlock(); 248125cf1a30Sjl 248225cf1a30Sjl stat->assigned = dp->dev.bp->assigned; 248325cf1a30Sjl stat->powered = dp->dev.bp->powered; 248425cf1a30Sjl stat->configured = (ml != NULL); 248525cf1a30Sjl stat->busy = dp->dev.busy; 248625cf1a30Sjl strncpy(stat->type, dp->dev.type, sizeof (stat->type)); 248725cf1a30Sjl stat->info[0] = '\0'; 248825cf1a30Sjl 248925cf1a30Sjl return (NULL); 249025cf1a30Sjl } 249125cf1a30Sjl 249225cf1a30Sjl 249325cf1a30Sjl sbd_error_t * 249425cf1a30Sjl drmach_board_deprobe(drmachid_t id) 249525cf1a30Sjl { 249625cf1a30Sjl drmach_board_t *bp; 249725cf1a30Sjl 249825cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 249925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 250025cf1a30Sjl 250125cf1a30Sjl bp = id; 250225cf1a30Sjl 250368ac2337Sjl cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum); 250425cf1a30Sjl 250525cf1a30Sjl if (bp->tree) { 250625cf1a30Sjl drmach_node_dispose(bp->tree); 250725cf1a30Sjl bp->tree = NULL; 250825cf1a30Sjl } 250925cf1a30Sjl if (bp->devices) { 251025cf1a30Sjl drmach_array_dispose(bp->devices, drmach_device_dispose); 251125cf1a30Sjl bp->devices = NULL; 251225cf1a30Sjl } 251325cf1a30Sjl 251425cf1a30Sjl bp->boot_board = 0; 251525cf1a30Sjl 251625cf1a30Sjl return (NULL); 251725cf1a30Sjl } 251825cf1a30Sjl 251925cf1a30Sjl /*ARGSUSED*/ 252025cf1a30Sjl static sbd_error_t * 252125cf1a30Sjl drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts) 252225cf1a30Sjl { 252325cf1a30Sjl drmach_board_t *bp = (drmach_board_t *)id; 252425cf1a30Sjl sbd_error_t *err = NULL; 252525cf1a30Sjl int rv; 2526*e98fafb9Sjl unsigned cpu_impl; 252725cf1a30Sjl 252825cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 252925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 253025cf1a30Sjl 253125cf1a30Sjl DRMACH_PR("calling opl_probe_board for bnum=%d\n", bp->bnum); 2532*e98fafb9Sjl rv = opl_probe_sb(bp->bnum, &cpu_impl); 253325cf1a30Sjl if (rv != 0) { 25348eafe49bSbm err = drerr_new(1, EOPL_PROBE, bp->cm.name); 253525cf1a30Sjl return (err); 253625cf1a30Sjl } 253725cf1a30Sjl return (err); 253825cf1a30Sjl } 253925cf1a30Sjl 254025cf1a30Sjl /*ARGSUSED*/ 254125cf1a30Sjl static sbd_error_t * 254225cf1a30Sjl drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts) 254325cf1a30Sjl { 254425cf1a30Sjl drmach_board_t *bp; 254525cf1a30Sjl sbd_error_t *err = NULL; 254625cf1a30Sjl int rv; 254725cf1a30Sjl 254825cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) 254925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 255025cf1a30Sjl bp = (drmach_board_t *)id; 255125cf1a30Sjl 255225cf1a30Sjl cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum); 255325cf1a30Sjl 255425cf1a30Sjl rv = opl_unprobe_sb(bp->bnum); 255525cf1a30Sjl if (rv != 0) { 25568eafe49bSbm err = drerr_new(1, EOPL_DEPROBE, bp->cm.name); 255725cf1a30Sjl } 255825cf1a30Sjl 255925cf1a30Sjl return (err); 256025cf1a30Sjl } 256125cf1a30Sjl 256225cf1a30Sjl 256325cf1a30Sjl /*ARGSUSED*/ 256425cf1a30Sjl sbd_error_t * 256525cf1a30Sjl drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts) 256625cf1a30Sjl { 256725cf1a30Sjl struct memlist *ml; 256825cf1a30Sjl uint64_t src_pa; 256925cf1a30Sjl uint64_t dst_pa; 257025cf1a30Sjl uint64_t dst; 257125cf1a30Sjl 257225cf1a30Sjl dst_pa = va_to_pa(&dst); 257325cf1a30Sjl 257425cf1a30Sjl memlist_read_lock(); 257525cf1a30Sjl for (ml = phys_install; ml; ml = ml->next) { 257625cf1a30Sjl uint64_t nbytes; 257725cf1a30Sjl 257825cf1a30Sjl src_pa = ml->address; 257925cf1a30Sjl nbytes = ml->size; 258025cf1a30Sjl 258125cf1a30Sjl while (nbytes != 0ull) { 258225cf1a30Sjl 258325cf1a30Sjl /* copy 32 bytes at arc_pa to dst_pa */ 258425cf1a30Sjl bcopy32_il(src_pa, dst_pa); 258525cf1a30Sjl 258625cf1a30Sjl /* increment by 32 bytes */ 258725cf1a30Sjl src_pa += (4 * sizeof (uint64_t)); 258825cf1a30Sjl 258925cf1a30Sjl /* decrement by 32 bytes */ 259025cf1a30Sjl nbytes -= (4 * sizeof (uint64_t)); 259125cf1a30Sjl } 259225cf1a30Sjl } 259325cf1a30Sjl memlist_read_unlock(); 259425cf1a30Sjl 259525cf1a30Sjl return (NULL); 259625cf1a30Sjl } 259725cf1a30Sjl 259825cf1a30Sjl static struct { 259925cf1a30Sjl const char *name; 260025cf1a30Sjl sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts); 260125cf1a30Sjl } drmach_pt_arr[] = { 260225cf1a30Sjl { "readmem", drmach_pt_readmem }, 260325cf1a30Sjl { "ikprobe", drmach_pt_ikprobe }, 260425cf1a30Sjl { "ikdeprobe", drmach_pt_ikdeprobe }, 260525cf1a30Sjl 260625cf1a30Sjl /* the following line must always be last */ 260725cf1a30Sjl { NULL, NULL } 260825cf1a30Sjl }; 260925cf1a30Sjl 261025cf1a30Sjl /*ARGSUSED*/ 261125cf1a30Sjl sbd_error_t * 261225cf1a30Sjl drmach_passthru(drmachid_t id, drmach_opts_t *opts) 261325cf1a30Sjl { 261425cf1a30Sjl int i; 261525cf1a30Sjl sbd_error_t *err; 261625cf1a30Sjl 261725cf1a30Sjl i = 0; 261825cf1a30Sjl while (drmach_pt_arr[i].name != NULL) { 261925cf1a30Sjl int len = strlen(drmach_pt_arr[i].name); 262025cf1a30Sjl 262125cf1a30Sjl if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0) 262225cf1a30Sjl break; 262325cf1a30Sjl 262425cf1a30Sjl i += 1; 262525cf1a30Sjl } 262625cf1a30Sjl 262725cf1a30Sjl if (drmach_pt_arr[i].name == NULL) 262825cf1a30Sjl err = drerr_new(0, EOPL_UNKPTCMD, opts->copts); 262925cf1a30Sjl else 263025cf1a30Sjl err = (*drmach_pt_arr[i].handler)(id, opts); 263125cf1a30Sjl 263225cf1a30Sjl return (err); 263325cf1a30Sjl } 263425cf1a30Sjl 263525cf1a30Sjl sbd_error_t * 263625cf1a30Sjl drmach_release(drmachid_t id) 263725cf1a30Sjl { 263825cf1a30Sjl drmach_common_t *cp; 263925cf1a30Sjl 264025cf1a30Sjl if (!DRMACH_IS_DEVICE_ID(id)) 264125cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 264225cf1a30Sjl cp = id; 264325cf1a30Sjl 264425cf1a30Sjl return (cp->release(id)); 264525cf1a30Sjl } 264625cf1a30Sjl 264725cf1a30Sjl sbd_error_t * 264825cf1a30Sjl drmach_status(drmachid_t id, drmach_status_t *stat) 264925cf1a30Sjl { 265025cf1a30Sjl drmach_common_t *cp; 265125cf1a30Sjl sbd_error_t *err; 265225cf1a30Sjl 265325cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_READER); 265425cf1a30Sjl 265525cf1a30Sjl if (!DRMACH_IS_ID(id)) { 265625cf1a30Sjl rw_exit(&drmach_boards_rwlock); 265725cf1a30Sjl return (drerr_new(0, EOPL_NOTID, NULL)); 265825cf1a30Sjl } 265925cf1a30Sjl cp = (drmach_common_t *)id; 266025cf1a30Sjl err = cp->status(id, stat); 266125cf1a30Sjl 266225cf1a30Sjl rw_exit(&drmach_boards_rwlock); 266325cf1a30Sjl 266425cf1a30Sjl return (err); 266525cf1a30Sjl } 266625cf1a30Sjl 266725cf1a30Sjl static sbd_error_t * 266825cf1a30Sjl drmach_i_status(drmachid_t id, drmach_status_t *stat) 266925cf1a30Sjl { 267025cf1a30Sjl drmach_common_t *cp; 267125cf1a30Sjl 267225cf1a30Sjl if (!DRMACH_IS_ID(id)) 267325cf1a30Sjl return (drerr_new(0, EOPL_NOTID, NULL)); 267425cf1a30Sjl cp = id; 267525cf1a30Sjl 267625cf1a30Sjl return (cp->status(id, stat)); 267725cf1a30Sjl } 267825cf1a30Sjl 267925cf1a30Sjl /*ARGSUSED*/ 268025cf1a30Sjl sbd_error_t * 268125cf1a30Sjl drmach_unconfigure(drmachid_t id, int flags) 268225cf1a30Sjl { 268325cf1a30Sjl drmach_device_t *dp; 268425cf1a30Sjl dev_info_t *rdip, *fdip = NULL; 268525cf1a30Sjl char name[OBP_MAXDRVNAME]; 268625cf1a30Sjl int rv; 268725cf1a30Sjl 268825cf1a30Sjl if (DRMACH_IS_CPU_ID(id)) 268925cf1a30Sjl return (NULL); 269025cf1a30Sjl 269125cf1a30Sjl if (!DRMACH_IS_DEVICE_ID(id)) 269225cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 269325cf1a30Sjl 269425cf1a30Sjl dp = id; 269525cf1a30Sjl 269625cf1a30Sjl rdip = dp->node->n_getdip(dp->node); 269725cf1a30Sjl 269825cf1a30Sjl ASSERT(rdip); 269925cf1a30Sjl 270025cf1a30Sjl rv = dp->node->n_getprop(dp->node, "name", name, OBP_MAXDRVNAME); 270125cf1a30Sjl 270225cf1a30Sjl if (rv) 270325cf1a30Sjl return (NULL); 270425cf1a30Sjl 270525cf1a30Sjl /* 270625cf1a30Sjl * Note: FORCE flag is no longer necessary under devfs 270725cf1a30Sjl */ 270825cf1a30Sjl 270925cf1a30Sjl ASSERT(e_ddi_branch_held(rdip)); 271025cf1a30Sjl if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) { 271125cf1a30Sjl sbd_error_t *err; 271225cf1a30Sjl char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 271325cf1a30Sjl 271425cf1a30Sjl /* 271525cf1a30Sjl * If non-NULL, fdip is returned held and must be released. 271625cf1a30Sjl */ 271725cf1a30Sjl if (fdip != NULL) { 271825cf1a30Sjl (void) ddi_pathname(fdip, path); 271925cf1a30Sjl ndi_rele_devi(fdip); 272025cf1a30Sjl } else { 272125cf1a30Sjl (void) ddi_pathname(rdip, path); 272225cf1a30Sjl } 272325cf1a30Sjl 272425cf1a30Sjl err = drerr_new(1, EOPL_DRVFAIL, path); 272525cf1a30Sjl 272625cf1a30Sjl kmem_free(path, MAXPATHLEN); 272725cf1a30Sjl 272825cf1a30Sjl return (err); 272925cf1a30Sjl } 273025cf1a30Sjl 273125cf1a30Sjl return (NULL); 273225cf1a30Sjl } 273325cf1a30Sjl 273425cf1a30Sjl 273525cf1a30Sjl int 273625cf1a30Sjl drmach_cpu_poweron(struct cpu *cp) 273725cf1a30Sjl { 273825cf1a30Sjl int bnum, cpuid, onb_core_num, strand_id; 273925cf1a30Sjl drmach_board_t *bp; 274025cf1a30Sjl 274125cf1a30Sjl DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id); 274225cf1a30Sjl 274325cf1a30Sjl cpuid = cp->cpu_id; 274425cf1a30Sjl bnum = LSB_ID(cpuid); 274525cf1a30Sjl onb_core_num = ON_BOARD_CORE_NUM(cpuid); 274625cf1a30Sjl strand_id = STRAND_ID(cpuid); 274725cf1a30Sjl bp = drmach_get_board_by_bnum(bnum); 274825cf1a30Sjl 274925cf1a30Sjl ASSERT(bp); 275025cf1a30Sjl if (bp->cores[onb_core_num].core_hotadded == 0) { 275125cf1a30Sjl if (drmach_add_remove_cpu(bnum, onb_core_num, 2752*e98fafb9Sjl HOTADD_CPU) != 0) { 275325cf1a30Sjl cmn_err(CE_WARN, "Failed to add CMP %d on board %d\n", 2754*e98fafb9Sjl onb_core_num, bnum); 275525cf1a30Sjl return (EIO); 275625cf1a30Sjl } 275725cf1a30Sjl } 275825cf1a30Sjl 275925cf1a30Sjl ASSERT(MUTEX_HELD(&cpu_lock)); 276025cf1a30Sjl 276125cf1a30Sjl if (drmach_cpu_start(cp) != 0) { 276225cf1a30Sjl if (bp->cores[onb_core_num].core_started == 0) { 276325cf1a30Sjl /* 276425cf1a30Sjl * we must undo the hotadd or no one will do that 276525cf1a30Sjl * If this fails, we will do this again in 276625cf1a30Sjl * drmach_board_disconnect. 276725cf1a30Sjl */ 276825cf1a30Sjl if (drmach_add_remove_cpu(bnum, onb_core_num, 2769*e98fafb9Sjl HOTREMOVE_CPU) != 0) { 277025cf1a30Sjl cmn_err(CE_WARN, "Failed to remove CMP %d " 2771*e98fafb9Sjl "on board %d\n", onb_core_num, bnum); 277225cf1a30Sjl } 277325cf1a30Sjl } 277425cf1a30Sjl return (EBUSY); 277525cf1a30Sjl } else { 277625cf1a30Sjl bp->cores[onb_core_num].core_started |= (1 << strand_id); 277725cf1a30Sjl return (0); 277825cf1a30Sjl } 277925cf1a30Sjl } 278025cf1a30Sjl 278125cf1a30Sjl int 278225cf1a30Sjl drmach_cpu_poweroff(struct cpu *cp) 278325cf1a30Sjl { 278425cf1a30Sjl int rv = 0; 278525cf1a30Sjl processorid_t cpuid = cp->cpu_id; 278625cf1a30Sjl 278725cf1a30Sjl DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id); 278825cf1a30Sjl 278925cf1a30Sjl ASSERT(MUTEX_HELD(&cpu_lock)); 279025cf1a30Sjl 279125cf1a30Sjl /* 279225cf1a30Sjl * Capture all CPUs (except for detaching proc) to prevent 279325cf1a30Sjl * crosscalls to the detaching proc until it has cleared its 279425cf1a30Sjl * bit in cpu_ready_set. 279525cf1a30Sjl * 279625cf1a30Sjl * The CPU's remain paused and the prom_mutex is known to be free. 279725cf1a30Sjl * This prevents the x-trap victim from blocking when doing prom 279825cf1a30Sjl * IEEE-1275 calls at a high PIL level. 279925cf1a30Sjl */ 280025cf1a30Sjl 280125cf1a30Sjl promsafe_pause_cpus(); 280225cf1a30Sjl 280325cf1a30Sjl /* 280425cf1a30Sjl * Quiesce interrupts on the target CPU. We do this by setting 280525cf1a30Sjl * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to 280625cf1a30Sjl * prevent it from receiving cross calls and cross traps. 280725cf1a30Sjl * This prevents the processor from receiving any new soft interrupts. 280825cf1a30Sjl */ 280925cf1a30Sjl mp_cpu_quiesce(cp); 281025cf1a30Sjl 281125cf1a30Sjl rv = prom_stopcpu_bycpuid(cpuid); 281225cf1a30Sjl if (rv == 0) 281325cf1a30Sjl cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF; 281425cf1a30Sjl 281525cf1a30Sjl start_cpus(); 281625cf1a30Sjl 281725cf1a30Sjl if (rv == 0) { 281825cf1a30Sjl int bnum, onb_core_num, strand_id; 281925cf1a30Sjl drmach_board_t *bp; 282025cf1a30Sjl 282125cf1a30Sjl CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid); 282225cf1a30Sjl 282325cf1a30Sjl bnum = LSB_ID(cpuid); 282425cf1a30Sjl onb_core_num = ON_BOARD_CORE_NUM(cpuid); 282525cf1a30Sjl strand_id = STRAND_ID(cpuid); 282625cf1a30Sjl bp = drmach_get_board_by_bnum(bnum); 282725cf1a30Sjl ASSERT(bp); 282825cf1a30Sjl 282925cf1a30Sjl bp->cores[onb_core_num].core_started &= ~(1 << strand_id); 283025cf1a30Sjl if (bp->cores[onb_core_num].core_started == 0) { 283125cf1a30Sjl if (drmach_add_remove_cpu(bnum, onb_core_num, 2832*e98fafb9Sjl HOTREMOVE_CPU) != 0) { 2833*e98fafb9Sjl cmn_err(CE_WARN, "Failed to remove CMP %d LSB " 2834*e98fafb9Sjl "%d\n", onb_core_num, bnum); 283525cf1a30Sjl return (EIO); 283625cf1a30Sjl } 283725cf1a30Sjl } 283825cf1a30Sjl } 283925cf1a30Sjl 284025cf1a30Sjl return (rv); 284125cf1a30Sjl } 284225cf1a30Sjl 284325cf1a30Sjl /*ARGSUSED*/ 284425cf1a30Sjl int 284525cf1a30Sjl drmach_verify_sr(dev_info_t *dip, int sflag) 284625cf1a30Sjl { 284725cf1a30Sjl return (0); 284825cf1a30Sjl } 284925cf1a30Sjl 285025cf1a30Sjl void 285125cf1a30Sjl drmach_suspend_last(void) 285225cf1a30Sjl { 285325cf1a30Sjl } 285425cf1a30Sjl 285525cf1a30Sjl void 285625cf1a30Sjl drmach_resume_first(void) 285725cf1a30Sjl { 285825cf1a30Sjl } 285925cf1a30Sjl 286025cf1a30Sjl /* 286125cf1a30Sjl * Log a DR sysevent. 286225cf1a30Sjl * Return value: 0 success, non-zero failure. 286325cf1a30Sjl */ 286425cf1a30Sjl int 286525cf1a30Sjl drmach_log_sysevent(int board, char *hint, int flag, int verbose) 286625cf1a30Sjl { 286725cf1a30Sjl sysevent_t *ev; 286825cf1a30Sjl sysevent_id_t eid; 286925cf1a30Sjl int rv, km_flag; 287025cf1a30Sjl sysevent_value_t evnt_val; 287125cf1a30Sjl sysevent_attr_list_t *evnt_attr_list = NULL; 287225cf1a30Sjl char attach_pnt[MAXNAMELEN]; 287325cf1a30Sjl 287425cf1a30Sjl km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP; 287525cf1a30Sjl attach_pnt[0] = '\0'; 287625cf1a30Sjl if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) { 287725cf1a30Sjl rv = -1; 287825cf1a30Sjl goto logexit; 287925cf1a30Sjl } 2880b307f191Sbm if (verbose) { 288125cf1a30Sjl DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n", 2882*e98fafb9Sjl attach_pnt, hint, flag, verbose); 2883b307f191Sbm } 288425cf1a30Sjl 288525cf1a30Sjl if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, 2886*e98fafb9Sjl SUNW_KERN_PUB"dr", km_flag)) == NULL) { 288725cf1a30Sjl rv = -2; 288825cf1a30Sjl goto logexit; 288925cf1a30Sjl } 289025cf1a30Sjl evnt_val.value_type = SE_DATA_TYPE_STRING; 289125cf1a30Sjl evnt_val.value.sv_string = attach_pnt; 2892*e98fafb9Sjl if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, 2893*e98fafb9Sjl km_flag)) != 0) 289425cf1a30Sjl goto logexit; 289525cf1a30Sjl 289625cf1a30Sjl evnt_val.value_type = SE_DATA_TYPE_STRING; 289725cf1a30Sjl evnt_val.value.sv_string = hint; 2898*e98fafb9Sjl if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, 2899*e98fafb9Sjl km_flag)) != 0) { 290025cf1a30Sjl sysevent_free_attr(evnt_attr_list); 290125cf1a30Sjl goto logexit; 290225cf1a30Sjl } 290325cf1a30Sjl 290425cf1a30Sjl (void) sysevent_attach_attributes(ev, evnt_attr_list); 290525cf1a30Sjl 290625cf1a30Sjl /* 290725cf1a30Sjl * Log the event but do not sleep waiting for its 290825cf1a30Sjl * delivery. This provides insulation from syseventd. 290925cf1a30Sjl */ 291025cf1a30Sjl rv = log_sysevent(ev, SE_NOSLEEP, &eid); 291125cf1a30Sjl 291225cf1a30Sjl logexit: 291325cf1a30Sjl if (ev) 291425cf1a30Sjl sysevent_free(ev); 291525cf1a30Sjl if ((rv != 0) && verbose) 2916*e98fafb9Sjl cmn_err(CE_WARN, "drmach_log_sysevent failed (rv %d) for %s " 2917*e98fafb9Sjl " %s\n", rv, attach_pnt, hint); 291825cf1a30Sjl 291925cf1a30Sjl return (rv); 292025cf1a30Sjl } 292125cf1a30Sjl 292225cf1a30Sjl #define OPL_DR_STATUS_PROP "dr-status" 292325cf1a30Sjl 292425cf1a30Sjl static int 292525cf1a30Sjl opl_check_dr_status() 292625cf1a30Sjl { 292725cf1a30Sjl pnode_t node; 292825cf1a30Sjl int rtn, len; 292925cf1a30Sjl char *str; 293025cf1a30Sjl 293125cf1a30Sjl node = prom_rootnode(); 293225cf1a30Sjl if (node == OBP_BADNODE) { 293325cf1a30Sjl return (1); 293425cf1a30Sjl } 293525cf1a30Sjl 293625cf1a30Sjl len = prom_getproplen(node, OPL_DR_STATUS_PROP); 293725cf1a30Sjl if (len == -1) { 293825cf1a30Sjl /* 293925cf1a30Sjl * dr-status doesn't exist when DR is activated and 294025cf1a30Sjl * any warning messages aren't needed. 294125cf1a30Sjl */ 294225cf1a30Sjl return (1); 294325cf1a30Sjl } 294425cf1a30Sjl 294525cf1a30Sjl str = (char *)kmem_zalloc(len+1, KM_SLEEP); 294625cf1a30Sjl rtn = prom_getprop(node, OPL_DR_STATUS_PROP, str); 294725cf1a30Sjl kmem_free(str, len + 1); 294825cf1a30Sjl if (rtn == -1) { 294925cf1a30Sjl return (1); 295025cf1a30Sjl } else { 295125cf1a30Sjl return (0); 295225cf1a30Sjl } 295325cf1a30Sjl } 295425cf1a30Sjl 295525cf1a30Sjl /* we are allocating memlist from TLB locked pages to avoid tlbmisses */ 295625cf1a30Sjl 295725cf1a30Sjl static struct memlist * 295825cf1a30Sjl drmach_memlist_add_span(drmach_copy_rename_program_t *p, 295925cf1a30Sjl struct memlist *mlist, uint64_t base, uint64_t len) 296025cf1a30Sjl { 296125cf1a30Sjl struct memlist *ml, *tl, *nl; 296225cf1a30Sjl 296325cf1a30Sjl if (len == 0ull) 296425cf1a30Sjl return (NULL); 296525cf1a30Sjl 296625cf1a30Sjl if (mlist == NULL) { 296725cf1a30Sjl mlist = p->free_mlist; 296825cf1a30Sjl if (mlist == NULL) 296925cf1a30Sjl return (NULL); 297025cf1a30Sjl p->free_mlist = mlist->next; 297125cf1a30Sjl mlist->address = base; 297225cf1a30Sjl mlist->size = len; 297325cf1a30Sjl mlist->next = mlist->prev = NULL; 297425cf1a30Sjl 297525cf1a30Sjl return (mlist); 297625cf1a30Sjl } 297725cf1a30Sjl 297825cf1a30Sjl for (tl = ml = mlist; ml; tl = ml, ml = ml->next) { 297925cf1a30Sjl if (base < ml->address) { 298025cf1a30Sjl if ((base + len) < ml->address) { 298125cf1a30Sjl nl = p->free_mlist; 298225cf1a30Sjl if (nl == NULL) 298325cf1a30Sjl return (NULL); 298425cf1a30Sjl p->free_mlist = nl->next; 298525cf1a30Sjl nl->address = base; 298625cf1a30Sjl nl->size = len; 298725cf1a30Sjl nl->next = ml; 298825cf1a30Sjl if ((nl->prev = ml->prev) != NULL) 298925cf1a30Sjl nl->prev->next = nl; 299025cf1a30Sjl ml->prev = nl; 299125cf1a30Sjl if (mlist == ml) 299225cf1a30Sjl mlist = nl; 299325cf1a30Sjl } else { 2994*e98fafb9Sjl ml->size = MAX((base + len), (ml->address + 2995*e98fafb9Sjl ml->size)) - base; 299625cf1a30Sjl ml->address = base; 299725cf1a30Sjl } 299825cf1a30Sjl break; 299925cf1a30Sjl 300025cf1a30Sjl } else if (base <= (ml->address + ml->size)) { 3001*e98fafb9Sjl ml->size = MAX((base + len), (ml->address + ml->size)) - 3002*e98fafb9Sjl MIN(ml->address, base); 300325cf1a30Sjl ml->address = MIN(ml->address, base); 300425cf1a30Sjl break; 300525cf1a30Sjl } 300625cf1a30Sjl } 300725cf1a30Sjl if (ml == NULL) { 300825cf1a30Sjl nl = p->free_mlist; 300925cf1a30Sjl if (nl == NULL) 301025cf1a30Sjl return (NULL); 301125cf1a30Sjl p->free_mlist = nl->next; 301225cf1a30Sjl nl->address = base; 301325cf1a30Sjl nl->size = len; 301425cf1a30Sjl nl->next = NULL; 301525cf1a30Sjl nl->prev = tl; 301625cf1a30Sjl tl->next = nl; 301725cf1a30Sjl } 301825cf1a30Sjl 301925cf1a30Sjl return (mlist); 302025cf1a30Sjl } 302125cf1a30Sjl 302225cf1a30Sjl /* 302325cf1a30Sjl * The routine performs the necessary memory COPY and MC adr SWITCH. 302425cf1a30Sjl * Both operations MUST be at the same "level" so that the stack is 302525cf1a30Sjl * maintained correctly between the copy and switch. The switch 302625cf1a30Sjl * portion implements a caching mechanism to guarantee the code text 302725cf1a30Sjl * is cached prior to execution. This is to guard against possible 302825cf1a30Sjl * memory access while the MC adr's are being modified. 302925cf1a30Sjl * 303025cf1a30Sjl * IMPORTANT: The _drmach_copy_rename_end() function must immediately 303125cf1a30Sjl * follow drmach_copy_rename_prog__relocatable() so that the correct 303225cf1a30Sjl * "length" of the drmach_copy_rename_prog__relocatable can be 303325cf1a30Sjl * calculated. This routine MUST be a LEAF function, i.e. it can 303425cf1a30Sjl * make NO function calls, primarily for two reasons: 303525cf1a30Sjl * 303625cf1a30Sjl * 1. We must keep the stack consistent across the "switch". 303725cf1a30Sjl * 2. Function calls are compiled to relative offsets, and 303825cf1a30Sjl * we execute this function we'll be executing it from 303925cf1a30Sjl * a copied version in a different area of memory, thus 304025cf1a30Sjl * the relative offsets will be bogus. 304125cf1a30Sjl * 304225cf1a30Sjl * Moreover, it must have the "__relocatable" suffix to inform DTrace 304325cf1a30Sjl * providers (and anything else, for that matter) that this 304425cf1a30Sjl * function's text is manually relocated elsewhere before it is 304525cf1a30Sjl * executed. That is, it cannot be safely instrumented with any 304625cf1a30Sjl * methodology that is PC-relative. 304725cf1a30Sjl */ 304825cf1a30Sjl 304925cf1a30Sjl /* 305025cf1a30Sjl * We multiply this to system_clock_frequency so we 305125cf1a30Sjl * are setting a delay of fmem_timeout second for 3052b307f191Sbm * the rename command. 3053b307f191Sbm * 3054b307f191Sbm * FMEM command itself should complete within 15 sec. 3055b307f191Sbm * We add 2 more sec to be conservative. 3056b307f191Sbm * 3057b307f191Sbm * Note that there is also a SCF BUSY bit checking 3058b307f191Sbm * in drmach_asm.s right before FMEM command is 3059b307f191Sbm * issued. XSCF sets the SCF BUSY bit when the 3060b307f191Sbm * other domain on the same PSB reboots and it 3061b307f191Sbm * will not be able to service the FMEM command 3062b307f191Sbm * within 15 sec. After setting the SCF BUSY 3063b307f191Sbm * bit, XSCF will wait a while before servicing 3064b307f191Sbm * other reboot command so there is no race 3065b307f191Sbm * condition. 306625cf1a30Sjl */ 3067b307f191Sbm 306825cf1a30Sjl static int fmem_timeout = 17; 3069b307f191Sbm 3070b307f191Sbm /* 3071b307f191Sbm * The empirical data on some OPL system shows that 3072b307f191Sbm * we can copy 250 MB per second. We set it to 3073b307f191Sbm * 80 MB to be conservative. In normal case, 3074b307f191Sbm * this timeout does not affect anything. 3075b307f191Sbm */ 3076b307f191Sbm 3077b307f191Sbm static int min_copy_size_per_sec = 80 * 1024 * 1024; 3078b307f191Sbm 3079b307f191Sbm /* 3080b307f191Sbm * This is the timeout value for the xcall synchronization 3081b307f191Sbm * to get all the CPU ready to do the parallel copying. 3082b307f191Sbm * Even on a fully loaded system, 10 sec. should be long 3083b307f191Sbm * enough. 3084b307f191Sbm */ 3085b307f191Sbm 3086b307f191Sbm static int cpu_xcall_delay = 10; 308725cf1a30Sjl int drmach_disable_mcopy = 0; 308825cf1a30Sjl 308968ac2337Sjl /* 309068ac2337Sjl * The following delay loop executes sleep instruction to yield the 309168ac2337Sjl * CPU to other strands. If this is not done, some strand will tie 309268ac2337Sjl * up the CPU in busy loops while the other strand cannot do useful 309368ac2337Sjl * work. The copy procedure will take a much longer time without this. 309468ac2337Sjl */ 309525cf1a30Sjl #define DR_DELAY_IL(ms, freq) \ 309625cf1a30Sjl { \ 309725cf1a30Sjl uint64_t start; \ 309825cf1a30Sjl uint64_t nstick; \ 309925cf1a30Sjl volatile uint64_t now; \ 310025cf1a30Sjl nstick = ((uint64_t)ms * freq)/1000; \ 310125cf1a30Sjl start = drmach_get_stick_il(); \ 310225cf1a30Sjl now = start; \ 310325cf1a30Sjl while ((now - start) <= nstick) { \ 310425cf1a30Sjl drmach_sleep_il(); \ 310525cf1a30Sjl now = drmach_get_stick_il(); \ 310625cf1a30Sjl } \ 310725cf1a30Sjl } 310825cf1a30Sjl 310925cf1a30Sjl static int 311025cf1a30Sjl drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog, 311125cf1a30Sjl int cpuid) 311225cf1a30Sjl { 311325cf1a30Sjl struct memlist *ml; 311425cf1a30Sjl register int rtn; 311525cf1a30Sjl int i; 311625cf1a30Sjl register uint64_t curr, limit; 311725cf1a30Sjl extern uint64_t drmach_get_stick_il(); 311825cf1a30Sjl extern void membar_sync_il(); 311925cf1a30Sjl extern void flush_instr_mem_il(void*); 312068ac2337Sjl extern void flush_windows_il(void); 312125cf1a30Sjl uint64_t copy_start; 312225cf1a30Sjl 312368ac2337Sjl /* 312468ac2337Sjl * flush_windows is moved here to make sure all 312568ac2337Sjl * registers used in the callers are flushed to 312668ac2337Sjl * memory before the copy. 312768ac2337Sjl * 312868ac2337Sjl * If flush_windows() is called too early in the 312968ac2337Sjl * calling function, the compiler might put some 313068ac2337Sjl * data in the local registers after flush_windows(). 313168ac2337Sjl * After FMA, if there is any fill trap, the registers 313268ac2337Sjl * will contain stale data. 313368ac2337Sjl */ 313468ac2337Sjl 313568ac2337Sjl flush_windows_il(); 313668ac2337Sjl 313725cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_COPY_READY; 313825cf1a30Sjl membar_sync_il(); 313925cf1a30Sjl 314025cf1a30Sjl if (prog->data->cpuid == cpuid) { 314125cf1a30Sjl limit = drmach_get_stick_il(); 3142b307f191Sbm limit += cpu_xcall_delay * system_clock_freq; 314325cf1a30Sjl for (i = 0; i < NCPU; i++) { 314425cf1a30Sjl if (CPU_IN_SET(prog->data->cpu_slave_set, i)) { 3145*e98fafb9Sjl /* wait for all CPU's to be ready */ 3146*e98fafb9Sjl for (;;) { 3147*e98fafb9Sjl if (prog->critical->stat[i] == 3148*e98fafb9Sjl FMEM_LOOP_COPY_READY) { 3149*e98fafb9Sjl break; 3150*e98fafb9Sjl } 3151*e98fafb9Sjl DR_DELAY_IL(1, prog->data->stick_freq); 3152*e98fafb9Sjl } 3153*e98fafb9Sjl curr = drmach_get_stick_il(); 3154*e98fafb9Sjl if (curr > limit) { 3155*e98fafb9Sjl prog->data->fmem_status.error = 3156*e98fafb9Sjl EOPL_FMEM_XC_TIMEOUT; 3157*e98fafb9Sjl return (EOPL_FMEM_XC_TIMEOUT); 315825cf1a30Sjl } 315925cf1a30Sjl } 316025cf1a30Sjl } 316125cf1a30Sjl prog->data->fmem_status.stat = FMEM_LOOP_COPY_READY; 316225cf1a30Sjl membar_sync_il(); 316325cf1a30Sjl copy_start = drmach_get_stick_il(); 316425cf1a30Sjl } else { 316525cf1a30Sjl for (;;) { 316625cf1a30Sjl if (prog->data->fmem_status.stat == 3167*e98fafb9Sjl FMEM_LOOP_COPY_READY) { 316825cf1a30Sjl break; 316925cf1a30Sjl } 317025cf1a30Sjl if (prog->data->fmem_status.error) { 3171*e98fafb9Sjl prog->data->error[cpuid] = EOPL_FMEM_TERMINATE; 3172b307f191Sbm return (EOPL_FMEM_TERMINATE); 317325cf1a30Sjl } 317468ac2337Sjl DR_DELAY_IL(1, prog->data->stick_freq); 317525cf1a30Sjl } 317625cf1a30Sjl } 317725cf1a30Sjl 317825cf1a30Sjl /* 317925cf1a30Sjl * DO COPY. 318025cf1a30Sjl */ 318125cf1a30Sjl if (CPU_IN_SET(prog->data->cpu_copy_set, cpuid)) { 3182*e98fafb9Sjl for (ml = prog->data->cpu_ml[cpuid]; ml; ml = ml->next) { 3183*e98fafb9Sjl uint64_t s_pa, t_pa; 3184*e98fafb9Sjl uint64_t nbytes; 3185*e98fafb9Sjl 3186*e98fafb9Sjl s_pa = prog->data->s_copybasepa + ml->address; 3187*e98fafb9Sjl t_pa = prog->data->t_copybasepa + ml->address; 3188*e98fafb9Sjl nbytes = ml->size; 3189*e98fafb9Sjl 3190*e98fafb9Sjl while (nbytes != 0ull) { 3191*e98fafb9Sjl /* 3192*e98fafb9Sjl * If the master has detected error, we just 3193*e98fafb9Sjl * bail out 3194*e98fafb9Sjl */ 3195*e98fafb9Sjl if (prog->data->fmem_status.error != 3196*e98fafb9Sjl ESBD_NOERROR) { 3197*e98fafb9Sjl prog->data->error[cpuid] = 3198*e98fafb9Sjl EOPL_FMEM_TERMINATE; 3199*e98fafb9Sjl return (EOPL_FMEM_TERMINATE); 3200*e98fafb9Sjl } 3201*e98fafb9Sjl /* 3202*e98fafb9Sjl * This copy does NOT use an ASI 3203*e98fafb9Sjl * that avoids the Ecache, therefore 3204*e98fafb9Sjl * the dst_pa addresses may remain 3205*e98fafb9Sjl * in our Ecache after the dst_pa 3206*e98fafb9Sjl * has been removed from the system. 3207*e98fafb9Sjl * A subsequent write-back to memory 3208*e98fafb9Sjl * will cause an ARB-stop because the 3209*e98fafb9Sjl * physical address no longer exists 3210*e98fafb9Sjl * in the system. Therefore we must 3211*e98fafb9Sjl * flush out local Ecache after we 3212*e98fafb9Sjl * finish the copy. 3213*e98fafb9Sjl */ 3214*e98fafb9Sjl 3215*e98fafb9Sjl /* copy 32 bytes at src_pa to dst_pa */ 3216*e98fafb9Sjl bcopy32_il(s_pa, t_pa); 3217*e98fafb9Sjl 3218*e98fafb9Sjl /* 3219*e98fafb9Sjl * increment the counter to signal that we are 3220*e98fafb9Sjl * alive 3221*e98fafb9Sjl */ 3222*e98fafb9Sjl prog->stat->nbytes[cpuid] += 32; 3223*e98fafb9Sjl 3224*e98fafb9Sjl /* increment by 32 bytes */ 3225*e98fafb9Sjl s_pa += (4 * sizeof (uint64_t)); 3226*e98fafb9Sjl t_pa += (4 * sizeof (uint64_t)); 3227*e98fafb9Sjl 3228*e98fafb9Sjl /* decrement by 32 bytes */ 3229*e98fafb9Sjl nbytes -= (4 * sizeof (uint64_t)); 323025cf1a30Sjl } 323125cf1a30Sjl } 3232*e98fafb9Sjl prog->critical->stat[cpuid] = FMEM_LOOP_COPY_DONE; 3233*e98fafb9Sjl membar_sync_il(); 323425cf1a30Sjl } 323525cf1a30Sjl 323625cf1a30Sjl /* 323725cf1a30Sjl * Since bcopy32_il() does NOT use an ASI to bypass 323825cf1a30Sjl * the Ecache, we need to flush our Ecache after 323925cf1a30Sjl * the copy is complete. 324025cf1a30Sjl */ 324125cf1a30Sjl flush_cache_il(); 324225cf1a30Sjl 324325cf1a30Sjl /* 324425cf1a30Sjl * drmach_fmem_exec_script() 324525cf1a30Sjl */ 324625cf1a30Sjl if (prog->data->cpuid == cpuid) { 324725cf1a30Sjl uint64_t last, now; 324825cf1a30Sjl 324925cf1a30Sjl limit = copy_start + prog->data->copy_delay; 325025cf1a30Sjl for (i = 0; i < NCPU; i++) { 3251*e98fafb9Sjl if (!CPU_IN_SET(prog->data->cpu_slave_set, i)) 3252*e98fafb9Sjl continue; 3253*e98fafb9Sjl 3254*e98fafb9Sjl for (;;) { 3255*e98fafb9Sjl /* 3256*e98fafb9Sjl * we get FMEM_LOOP_FMEM_READY in 3257*e98fafb9Sjl * normal case 3258*e98fafb9Sjl */ 325925cf1a30Sjl if (prog->critical->stat[i] == 3260*e98fafb9Sjl FMEM_LOOP_FMEM_READY) { 326125cf1a30Sjl break; 326225cf1a30Sjl } 326325cf1a30Sjl /* got error traps */ 3264b307f191Sbm if (prog->data->error[i] == 3265*e98fafb9Sjl EOPL_FMEM_COPY_ERROR) { 326625cf1a30Sjl prog->data->fmem_status.error = 3267*e98fafb9Sjl EOPL_FMEM_COPY_ERROR; 3268b307f191Sbm return (EOPL_FMEM_COPY_ERROR); 326925cf1a30Sjl } 3270*e98fafb9Sjl /* 3271*e98fafb9Sjl * if we have not reached limit, wait 3272*e98fafb9Sjl * more 3273*e98fafb9Sjl */ 327425cf1a30Sjl curr = drmach_get_stick_il(); 327525cf1a30Sjl if (curr <= limit) 327625cf1a30Sjl continue; 327725cf1a30Sjl 327825cf1a30Sjl prog->data->slowest_cpuid = i; 3279*e98fafb9Sjl prog->data->copy_wait_time = curr - copy_start; 328025cf1a30Sjl 328125cf1a30Sjl /* now check if slave is alive */ 328225cf1a30Sjl last = prog->stat->nbytes[i]; 328325cf1a30Sjl 328425cf1a30Sjl DR_DELAY_IL(1, prog->data->stick_freq); 328525cf1a30Sjl 328625cf1a30Sjl now = prog->stat->nbytes[i]; 328725cf1a30Sjl if (now <= last) { 3288*e98fafb9Sjl /* 3289*e98fafb9Sjl * no progress, perhaps just 3290*e98fafb9Sjl * finished 3291*e98fafb9Sjl */ 329225cf1a30Sjl DR_DELAY_IL(1, prog->data->stick_freq); 329325cf1a30Sjl if (prog->critical->stat[i] == 3294*e98fafb9Sjl FMEM_LOOP_FMEM_READY) 329525cf1a30Sjl break; 329625cf1a30Sjl /* copy error */ 3297b307f191Sbm if (prog->data->error[i] == 3298*e98fafb9Sjl EOPL_FMEM_COPY_ERROR) { 3299*e98fafb9Sjl prog->data-> fmem_status.error = 3300*e98fafb9Sjl EOPL_FMEM_COPY_ERROR; 3301b307f191Sbm return (EOPL_FMEM_COPY_ERROR); 330225cf1a30Sjl } 330325cf1a30Sjl prog->data->fmem_status.error = 3304b307f191Sbm EOPL_FMEM_COPY_TIMEOUT; 3305b307f191Sbm return (EOPL_FMEM_COPY_TIMEOUT); 330625cf1a30Sjl } 330725cf1a30Sjl } 330825cf1a30Sjl } 3309b307f191Sbm 331025cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_FMEM_READY; 331125cf1a30Sjl prog->data->fmem_status.stat = FMEM_LOOP_FMEM_READY; 331225cf1a30Sjl 331325cf1a30Sjl membar_sync_il(); 331425cf1a30Sjl flush_instr_mem_il((void*) (prog->critical)); 331525cf1a30Sjl /* 331625cf1a30Sjl * drmach_fmem_exec_script() 331725cf1a30Sjl */ 331825cf1a30Sjl rtn = prog->critical->fmem((void *)prog->critical, PAGESIZE); 331925cf1a30Sjl return (rtn); 332025cf1a30Sjl } else { 332125cf1a30Sjl flush_instr_mem_il((void*) (prog->critical)); 332225cf1a30Sjl /* 332325cf1a30Sjl * drmach_fmem_loop_script() 332425cf1a30Sjl */ 3325*e98fafb9Sjl rtn = prog->critical->loop((void *)(prog->critical), PAGESIZE, 3326*e98fafb9Sjl (void *)&(prog->critical->stat[cpuid])); 332725cf1a30Sjl prog->data->error[cpuid] = rtn; 332825cf1a30Sjl /* slave thread does not care the rv */ 332925cf1a30Sjl return (0); 333025cf1a30Sjl } 333125cf1a30Sjl } 333225cf1a30Sjl 333325cf1a30Sjl static void 333425cf1a30Sjl drmach_copy_rename_end(void) 333525cf1a30Sjl { 333625cf1a30Sjl /* 333725cf1a30Sjl * IMPORTANT: This function's location MUST be located immediately 333825cf1a30Sjl * following drmach_copy_rename_prog__relocatable to 333925cf1a30Sjl * accurately estimate its size. Note that this assumes 334025cf1a30Sjl * the compiler keeps these functions in the order in 334125cf1a30Sjl * which they appear :-o 334225cf1a30Sjl */ 334325cf1a30Sjl } 334425cf1a30Sjl 334525cf1a30Sjl 334625cf1a30Sjl static void 334725cf1a30Sjl drmach_setup_memlist(drmach_copy_rename_program_t *p) 334825cf1a30Sjl { 334925cf1a30Sjl struct memlist *ml; 335025cf1a30Sjl caddr_t buf; 335125cf1a30Sjl int nbytes, s; 335225cf1a30Sjl 335325cf1a30Sjl nbytes = PAGESIZE; 335425cf1a30Sjl s = roundup(sizeof (struct memlist), sizeof (void *)); 335525cf1a30Sjl p->free_mlist = NULL; 335625cf1a30Sjl buf = p->memlist_buffer; 335725cf1a30Sjl while (nbytes >= sizeof (struct memlist)) { 335825cf1a30Sjl ml = (struct memlist *)buf; 335925cf1a30Sjl ml->next = p->free_mlist; 336025cf1a30Sjl p->free_mlist = ml; 336125cf1a30Sjl buf += s; 336225cf1a30Sjl nbytes -= s; 336325cf1a30Sjl } 336425cf1a30Sjl } 336525cf1a30Sjl 336668ac2337Sjl static void 336768ac2337Sjl drmach_lock_critical(caddr_t va, caddr_t new_va) 336868ac2337Sjl { 336968ac2337Sjl tte_t tte; 337068ac2337Sjl int i; 337168ac2337Sjl 337268ac2337Sjl kpreempt_disable(); 337368ac2337Sjl 337468ac2337Sjl for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) { 337568ac2337Sjl vtag_flushpage(new_va, (uint64_t)ksfmmup); 3376*e98fafb9Sjl sfmmu_memtte(&tte, va_to_pfn(va), PROC_DATA|HAT_NOSYNC, TTE8K); 337768ac2337Sjl tte.tte_intlo |= TTE_LCK_INT; 337868ac2337Sjl sfmmu_dtlb_ld_kva(new_va, &tte); 337968ac2337Sjl sfmmu_itlb_ld_kva(new_va, &tte); 338068ac2337Sjl va += PAGESIZE; 338168ac2337Sjl new_va += PAGESIZE; 338268ac2337Sjl } 338368ac2337Sjl } 338468ac2337Sjl 338568ac2337Sjl static void 338668ac2337Sjl drmach_unlock_critical(caddr_t va) 338768ac2337Sjl { 338868ac2337Sjl int i; 338968ac2337Sjl 339068ac2337Sjl for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) { 339168ac2337Sjl vtag_flushpage(va, (uint64_t)ksfmmup); 339268ac2337Sjl va += PAGESIZE; 339368ac2337Sjl } 339468ac2337Sjl 339568ac2337Sjl kpreempt_enable(); 339668ac2337Sjl } 339768ac2337Sjl 339825cf1a30Sjl sbd_error_t * 339925cf1a30Sjl drmach_copy_rename_init(drmachid_t t_id, drmachid_t s_id, 340025cf1a30Sjl struct memlist *c_ml, drmachid_t *pgm_id) 340125cf1a30Sjl { 340225cf1a30Sjl drmach_mem_t *s_mem; 340325cf1a30Sjl drmach_mem_t *t_mem; 340425cf1a30Sjl struct memlist *x_ml; 340525cf1a30Sjl uint64_t s_copybasepa, t_copybasepa; 340625cf1a30Sjl uint_t len; 340725cf1a30Sjl caddr_t bp, wp; 340825cf1a30Sjl int s_bd, t_bd, cpuid, active_cpus, i; 340925cf1a30Sjl uint64_t c_addr; 341025cf1a30Sjl size_t c_size, copy_sz, sz; 341125cf1a30Sjl extern void drmach_fmem_loop_script(); 341225cf1a30Sjl extern void drmach_fmem_loop_script_rtn(); 341325cf1a30Sjl extern int drmach_fmem_exec_script(); 341425cf1a30Sjl extern void drmach_fmem_exec_script_end(); 341525cf1a30Sjl sbd_error_t *err; 341668ac2337Sjl drmach_copy_rename_program_t *prog = NULL; 341768ac2337Sjl drmach_copy_rename_program_t *prog_kmem = NULL; 341825cf1a30Sjl void (*mc_suspend)(void); 341925cf1a30Sjl void (*mc_resume)(void); 342025cf1a30Sjl int (*scf_fmem_start)(int, int); 342125cf1a30Sjl int (*scf_fmem_end)(void); 342225cf1a30Sjl int (*scf_fmem_cancel)(void); 342368ac2337Sjl uint64_t (*scf_get_base_addr)(void); 342425cf1a30Sjl 342525cf1a30Sjl if (!DRMACH_IS_MEM_ID(s_id)) 342625cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 342725cf1a30Sjl if (!DRMACH_IS_MEM_ID(t_id)) 342825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL)); 342925cf1a30Sjl 343025cf1a30Sjl for (i = 0; i < NCPU; i++) { 343125cf1a30Sjl int lsb_id, onb_core_num, strand_id; 343225cf1a30Sjl drmach_board_t *bp; 343325cf1a30Sjl 343425cf1a30Sjl /* 343525cf1a30Sjl * this kind of CPU will spin in cache 343625cf1a30Sjl */ 343725cf1a30Sjl if (CPU_IN_SET(cpu_ready_set, i)) 343825cf1a30Sjl continue; 343925cf1a30Sjl 344025cf1a30Sjl /* 344125cf1a30Sjl * Now check for any inactive CPU's that 344225cf1a30Sjl * have been hotadded. This can only occur in 344325cf1a30Sjl * error condition in drmach_cpu_poweron(). 344425cf1a30Sjl */ 344525cf1a30Sjl lsb_id = LSB_ID(i); 344625cf1a30Sjl onb_core_num = ON_BOARD_CORE_NUM(i); 344725cf1a30Sjl strand_id = STRAND_ID(i); 344825cf1a30Sjl bp = drmach_get_board_by_bnum(lsb_id); 344925cf1a30Sjl if (bp == NULL) 345025cf1a30Sjl continue; 345125cf1a30Sjl if (bp->cores[onb_core_num].core_hotadded & 345225cf1a30Sjl (1 << strand_id)) { 3453*e98fafb9Sjl if (!(bp->cores[onb_core_num].core_started & 3454*e98fafb9Sjl (1 << strand_id))) { 3455*e98fafb9Sjl return (drerr_new(1, EOPL_CPU_STATE, NULL)); 3456*e98fafb9Sjl } 345725cf1a30Sjl } 345825cf1a30Sjl } 345925cf1a30Sjl 346025cf1a30Sjl mc_suspend = (void (*)(void)) 346125cf1a30Sjl modgetsymvalue("opl_mc_suspend", 0); 346225cf1a30Sjl mc_resume = (void (*)(void)) 346325cf1a30Sjl modgetsymvalue("opl_mc_resume", 0); 346425cf1a30Sjl 346525cf1a30Sjl if (mc_suspend == NULL || mc_resume == NULL) { 34668eafe49bSbm return (drerr_new(1, EOPL_MC_OPL, NULL)); 346725cf1a30Sjl } 346825cf1a30Sjl 346925cf1a30Sjl scf_fmem_start = (int (*)(int, int)) 347025cf1a30Sjl modgetsymvalue("scf_fmem_start", 0); 347125cf1a30Sjl if (scf_fmem_start == NULL) { 34728eafe49bSbm return (drerr_new(1, EOPL_SCF_FMEM, NULL)); 347325cf1a30Sjl } 347425cf1a30Sjl scf_fmem_end = (int (*)(void)) 347525cf1a30Sjl modgetsymvalue("scf_fmem_end", 0); 347625cf1a30Sjl if (scf_fmem_end == NULL) { 34778eafe49bSbm return (drerr_new(1, EOPL_SCF_FMEM, NULL)); 347825cf1a30Sjl } 347925cf1a30Sjl scf_fmem_cancel = (int (*)(void)) 348025cf1a30Sjl modgetsymvalue("scf_fmem_cancel", 0); 348125cf1a30Sjl if (scf_fmem_cancel == NULL) { 34828eafe49bSbm return (drerr_new(1, EOPL_SCF_FMEM, NULL)); 348368ac2337Sjl } 348468ac2337Sjl scf_get_base_addr = (uint64_t (*)(void)) 348568ac2337Sjl modgetsymvalue("scf_get_base_addr", 0); 348668ac2337Sjl if (scf_get_base_addr == NULL) { 34878eafe49bSbm return (drerr_new(1, EOPL_SCF_FMEM, NULL)); 348825cf1a30Sjl } 348925cf1a30Sjl s_mem = s_id; 349025cf1a30Sjl t_mem = t_id; 349125cf1a30Sjl 349225cf1a30Sjl s_bd = s_mem->dev.bp->bnum; 349325cf1a30Sjl t_bd = t_mem->dev.bp->bnum; 349425cf1a30Sjl 349525cf1a30Sjl /* calculate source and target base pa */ 349625cf1a30Sjl 349725cf1a30Sjl s_copybasepa = s_mem->slice_base; 349825cf1a30Sjl t_copybasepa = t_mem->slice_base; 349925cf1a30Sjl 350025cf1a30Sjl /* adjust copy memlist addresses to be relative to copy base pa */ 350125cf1a30Sjl x_ml = c_ml; 350225cf1a30Sjl while (x_ml != NULL) { 350325cf1a30Sjl x_ml->address -= s_copybasepa; 350425cf1a30Sjl x_ml = x_ml->next; 350525cf1a30Sjl } 350625cf1a30Sjl 350725cf1a30Sjl /* 350825cf1a30Sjl * bp will be page aligned, since we're calling 350925cf1a30Sjl * kmem_zalloc() with an exact multiple of PAGESIZE. 351025cf1a30Sjl */ 351125cf1a30Sjl 351268ac2337Sjl prog_kmem = (drmach_copy_rename_program_t *)kmem_zalloc( 3513*e98fafb9Sjl DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, KM_SLEEP); 351468ac2337Sjl 351568ac2337Sjl prog_kmem->prog = prog_kmem; 351668ac2337Sjl 351768ac2337Sjl /* 351868ac2337Sjl * To avoid MTLB hit, we allocate a new VM space and remap 351968ac2337Sjl * the kmem_alloc buffer to that address. This solves 352068ac2337Sjl * 2 problems we found: 352168ac2337Sjl * - the kmem_alloc buffer can be just a chunk inside 352268ac2337Sjl * a much larger, e.g. 4MB buffer and MTLB will occur 352368ac2337Sjl * if there are both a 4MB and a 8K TLB mapping to 352468ac2337Sjl * the same VA range. 352568ac2337Sjl * - the kmem mapping got dropped into the TLB by other 352668ac2337Sjl * strands, unintentionally. 352768ac2337Sjl * Note that the pointers like data, critical, memlist_buffer, 352868ac2337Sjl * and stat inside the copy rename structure are mapped to this 352968ac2337Sjl * alternate VM space so we must make sure we lock the TLB mapping 353068ac2337Sjl * whenever we access data pointed to by these pointers. 353168ac2337Sjl */ 353268ac2337Sjl 353368ac2337Sjl prog = prog_kmem->locked_prog = vmem_alloc(heap_arena, 3534*e98fafb9Sjl DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, VM_SLEEP); 353568ac2337Sjl wp = bp = (caddr_t)prog; 353668ac2337Sjl 353768ac2337Sjl /* Now remap prog_kmem to prog */ 353868ac2337Sjl drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog); 353968ac2337Sjl 354068ac2337Sjl /* All pointers in prog are based on the alternate mapping */ 354125cf1a30Sjl prog->data = (drmach_copy_rename_data_t *)roundup(((uint64_t)prog + 3542*e98fafb9Sjl sizeof (drmach_copy_rename_program_t)), sizeof (void *)); 354325cf1a30Sjl 354425cf1a30Sjl ASSERT(((uint64_t)prog->data + sizeof (drmach_copy_rename_data_t)) 3545*e98fafb9Sjl <= ((uint64_t)prog + PAGESIZE)); 354625cf1a30Sjl 354725cf1a30Sjl prog->critical = (drmach_copy_rename_critical_t *) 3548*e98fafb9Sjl (wp + DRMACH_FMEM_CRITICAL_PAGE * PAGESIZE); 354925cf1a30Sjl 3550*e98fafb9Sjl prog->memlist_buffer = (caddr_t)(wp + DRMACH_FMEM_MLIST_PAGE * 3551*e98fafb9Sjl PAGESIZE); 355225cf1a30Sjl 3553*e98fafb9Sjl prog->stat = (drmach_cr_stat_t *)(wp + DRMACH_FMEM_STAT_PAGE * 3554*e98fafb9Sjl PAGESIZE); 355525cf1a30Sjl 355625cf1a30Sjl /* LINTED */ 3557*e98fafb9Sjl ASSERT(sizeof (drmach_cr_stat_t) <= ((DRMACH_FMEM_LOCKED_PAGES - 3558*e98fafb9Sjl DRMACH_FMEM_STAT_PAGE) * PAGESIZE)); 355925cf1a30Sjl 356025cf1a30Sjl prog->critical->scf_reg_base = (uint64_t)-1; 356125cf1a30Sjl prog->critical->scf_td[0] = (s_bd & 0xff); 356225cf1a30Sjl prog->critical->scf_td[1] = (t_bd & 0xff); 356325cf1a30Sjl for (i = 2; i < 15; i++) { 356425cf1a30Sjl prog->critical->scf_td[i] = 0; 356525cf1a30Sjl } 356625cf1a30Sjl prog->critical->scf_td[15] = ((0xaa + s_bd + t_bd) & 0xff); 356725cf1a30Sjl 356825cf1a30Sjl bp = (caddr_t)prog->critical; 356925cf1a30Sjl len = sizeof (drmach_copy_rename_critical_t); 357025cf1a30Sjl wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *)); 357125cf1a30Sjl 357225cf1a30Sjl len = (uint_t)((ulong_t)drmach_copy_rename_end - 3573*e98fafb9Sjl (ulong_t)drmach_copy_rename_prog__relocatable); 357425cf1a30Sjl 357525cf1a30Sjl /* 357625cf1a30Sjl * We always leave 1K nop's to prevent the processor from 357725cf1a30Sjl * speculative execution that causes memory access 357825cf1a30Sjl */ 357925cf1a30Sjl wp = wp + len + 1024; 358025cf1a30Sjl 358125cf1a30Sjl len = (uint_t)((ulong_t)drmach_fmem_exec_script_end - 3582*e98fafb9Sjl (ulong_t)drmach_fmem_exec_script); 358325cf1a30Sjl /* this is the entry point of the loop script */ 358425cf1a30Sjl wp = wp + len + 1024; 358525cf1a30Sjl 358625cf1a30Sjl len = (uint_t)((ulong_t)drmach_fmem_exec_script - 3587*e98fafb9Sjl (ulong_t)drmach_fmem_loop_script); 358825cf1a30Sjl wp = wp + len + 1024; 358925cf1a30Sjl 359025cf1a30Sjl /* now we make sure there is 1K extra */ 359125cf1a30Sjl 359225cf1a30Sjl if ((wp - bp) > PAGESIZE) { 35938eafe49bSbm err = drerr_new(1, EOPL_FMEM_SETUP, NULL); 359468ac2337Sjl goto out; 359525cf1a30Sjl } 359625cf1a30Sjl 359725cf1a30Sjl bp = (caddr_t)prog->critical; 359825cf1a30Sjl len = sizeof (drmach_copy_rename_critical_t); 359925cf1a30Sjl wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *)); 360025cf1a30Sjl 360125cf1a30Sjl prog->critical->run = (int (*)())(wp); 360225cf1a30Sjl len = (uint_t)((ulong_t)drmach_copy_rename_end - 3603*e98fafb9Sjl (ulong_t)drmach_copy_rename_prog__relocatable); 360425cf1a30Sjl 360525cf1a30Sjl bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len); 360625cf1a30Sjl 360725cf1a30Sjl wp = (caddr_t)roundup((uint64_t)wp + len, 1024); 360825cf1a30Sjl 360925cf1a30Sjl prog->critical->fmem = (int (*)())(wp); 361025cf1a30Sjl len = (int)((ulong_t)drmach_fmem_exec_script_end - 3611*e98fafb9Sjl (ulong_t)drmach_fmem_exec_script); 361225cf1a30Sjl bcopy((caddr_t)drmach_fmem_exec_script, wp, len); 361325cf1a30Sjl 361425cf1a30Sjl len = (int)((ulong_t)drmach_fmem_exec_script_end - 3615*e98fafb9Sjl (ulong_t)drmach_fmem_exec_script); 361625cf1a30Sjl wp = (caddr_t)roundup((uint64_t)wp + len, 1024); 361725cf1a30Sjl 361825cf1a30Sjl prog->critical->loop = (int (*)())(wp); 361925cf1a30Sjl len = (int)((ulong_t)drmach_fmem_exec_script - 3620*e98fafb9Sjl (ulong_t)drmach_fmem_loop_script); 362125cf1a30Sjl bcopy((caddr_t)drmach_fmem_loop_script, (void *)wp, len); 362225cf1a30Sjl len = (int)((ulong_t)drmach_fmem_loop_script_rtn- 3623*e98fafb9Sjl (ulong_t)drmach_fmem_loop_script); 362425cf1a30Sjl prog->critical->loop_rtn = (void (*)()) (wp+len); 362525cf1a30Sjl 3626b307f191Sbm prog->data->fmem_status.error = ESBD_NOERROR; 3627b307f191Sbm 362825cf1a30Sjl /* now we are committed, call SCF, soft suspend mac patrol */ 362925cf1a30Sjl if ((*scf_fmem_start)(s_bd, t_bd)) { 36308eafe49bSbm err = drerr_new(1, EOPL_SCF_FMEM_START, NULL); 363168ac2337Sjl goto out; 363225cf1a30Sjl } 363325cf1a30Sjl prog->data->scf_fmem_end = scf_fmem_end; 363425cf1a30Sjl prog->data->scf_fmem_cancel = scf_fmem_cancel; 363568ac2337Sjl prog->data->scf_get_base_addr = scf_get_base_addr; 363625cf1a30Sjl prog->data->fmem_status.op |= OPL_FMEM_SCF_START; 3637b307f191Sbm 363825cf1a30Sjl /* soft suspend mac patrol */ 363925cf1a30Sjl (*mc_suspend)(); 364025cf1a30Sjl prog->data->fmem_status.op |= OPL_FMEM_MC_SUSPEND; 364125cf1a30Sjl prog->data->mc_resume = mc_resume; 364225cf1a30Sjl 364325cf1a30Sjl prog->critical->inst_loop_ret = 3644*e98fafb9Sjl *(uint64_t *)(prog->critical->loop_rtn); 364525cf1a30Sjl 364625cf1a30Sjl /* 364725cf1a30Sjl * 0x30800000 is op code "ba,a +0" 364825cf1a30Sjl */ 364925cf1a30Sjl 365025cf1a30Sjl *(uint_t *)(prog->critical->loop_rtn) = (uint_t)(0x30800000); 365125cf1a30Sjl 365225cf1a30Sjl /* 365325cf1a30Sjl * set the value of SCF FMEM TIMEOUT 365425cf1a30Sjl */ 365525cf1a30Sjl prog->critical->delay = fmem_timeout * system_clock_freq; 365625cf1a30Sjl 365725cf1a30Sjl prog->data->s_mem = (drmachid_t)s_mem; 365825cf1a30Sjl prog->data->t_mem = (drmachid_t)t_mem; 365925cf1a30Sjl 366025cf1a30Sjl cpuid = CPU->cpu_id; 366125cf1a30Sjl prog->data->cpuid = cpuid; 366225cf1a30Sjl prog->data->cpu_ready_set = cpu_ready_set; 366325cf1a30Sjl prog->data->cpu_slave_set = cpu_ready_set; 366425cf1a30Sjl prog->data->slowest_cpuid = (processorid_t)-1; 366525cf1a30Sjl prog->data->copy_wait_time = 0; 366625cf1a30Sjl CPUSET_DEL(prog->data->cpu_slave_set, cpuid); 366725cf1a30Sjl 366825cf1a30Sjl for (i = 0; i < NCPU; i++) { 366925cf1a30Sjl prog->data->cpu_ml[i] = NULL; 367025cf1a30Sjl } 367125cf1a30Sjl 367225cf1a30Sjl active_cpus = 0; 367325cf1a30Sjl if (drmach_disable_mcopy) { 367425cf1a30Sjl active_cpus = 1; 367525cf1a30Sjl CPUSET_ADD(prog->data->cpu_copy_set, cpuid); 367625cf1a30Sjl } else { 367725cf1a30Sjl for (i = 0; i < NCPU; i++) { 367825cf1a30Sjl if (CPU_IN_SET(cpu_ready_set, i) && 3679*e98fafb9Sjl CPU_ACTIVE(cpu[i])) { 368025cf1a30Sjl CPUSET_ADD(prog->data->cpu_copy_set, i); 368125cf1a30Sjl active_cpus++; 368225cf1a30Sjl } 368325cf1a30Sjl } 368425cf1a30Sjl } 368525cf1a30Sjl 368625cf1a30Sjl drmach_setup_memlist(prog); 368725cf1a30Sjl 368825cf1a30Sjl x_ml = c_ml; 368925cf1a30Sjl sz = 0; 369025cf1a30Sjl while (x_ml != NULL) { 369125cf1a30Sjl sz += x_ml->size; 369225cf1a30Sjl x_ml = x_ml->next; 369325cf1a30Sjl } 369425cf1a30Sjl 369525cf1a30Sjl copy_sz = sz/active_cpus; 369625cf1a30Sjl copy_sz = roundup(copy_sz, MMU_PAGESIZE4M); 369725cf1a30Sjl 369825cf1a30Sjl while (sz > copy_sz*active_cpus) { 369925cf1a30Sjl copy_sz += MMU_PAGESIZE4M; 370025cf1a30Sjl } 370125cf1a30Sjl 370225cf1a30Sjl prog->data->stick_freq = system_clock_freq; 370325cf1a30Sjl prog->data->copy_delay = ((copy_sz / min_copy_size_per_sec) + 2) * 3704*e98fafb9Sjl system_clock_freq; 370525cf1a30Sjl 370625cf1a30Sjl x_ml = c_ml; 370725cf1a30Sjl c_addr = x_ml->address; 370825cf1a30Sjl c_size = x_ml->size; 370925cf1a30Sjl 371025cf1a30Sjl for (i = 0; i < NCPU; i++) { 371125cf1a30Sjl prog->stat->nbytes[i] = 0; 371225cf1a30Sjl if (!CPU_IN_SET(prog->data->cpu_copy_set, i)) { 371325cf1a30Sjl continue; 371425cf1a30Sjl } 371525cf1a30Sjl sz = copy_sz; 371625cf1a30Sjl 371725cf1a30Sjl while (sz) { 371825cf1a30Sjl if (c_size > sz) { 371925cf1a30Sjl prog->data->cpu_ml[i] = 3720*e98fafb9Sjl drmach_memlist_add_span(prog, 3721*e98fafb9Sjl prog->data->cpu_ml[i], c_addr, sz); 372225cf1a30Sjl c_addr += sz; 372325cf1a30Sjl c_size -= sz; 372425cf1a30Sjl break; 372525cf1a30Sjl } else { 372625cf1a30Sjl sz -= c_size; 3727*e98fafb9Sjl prog->data->cpu_ml[i] = 3728*e98fafb9Sjl drmach_memlist_add_span(prog, 3729*e98fafb9Sjl prog->data->cpu_ml[i], c_addr, c_size); 373025cf1a30Sjl x_ml = x_ml->next; 373125cf1a30Sjl if (x_ml != NULL) { 373225cf1a30Sjl c_addr = x_ml->address; 373325cf1a30Sjl c_size = x_ml->size; 373425cf1a30Sjl } else { 373525cf1a30Sjl goto end; 373625cf1a30Sjl } 373725cf1a30Sjl } 373825cf1a30Sjl } 373925cf1a30Sjl } 374025cf1a30Sjl end: 374125cf1a30Sjl prog->data->s_copybasepa = s_copybasepa; 374225cf1a30Sjl prog->data->t_copybasepa = t_copybasepa; 374325cf1a30Sjl prog->data->c_ml = c_ml; 374468ac2337Sjl *pgm_id = prog_kmem; 374525cf1a30Sjl 374668ac2337Sjl /* Unmap the alternate space. It will have to be remapped again */ 374768ac2337Sjl drmach_unlock_critical((caddr_t)prog); 374825cf1a30Sjl return (NULL); 374968ac2337Sjl out: 375068ac2337Sjl if (prog != NULL) { 375168ac2337Sjl drmach_unlock_critical((caddr_t)prog); 3752*e98fafb9Sjl vmem_free(heap_arena, prog, DRMACH_FMEM_LOCKED_PAGES * 3753*e98fafb9Sjl PAGESIZE); 375468ac2337Sjl } 375568ac2337Sjl if (prog_kmem != NULL) { 375668ac2337Sjl kmem_free(prog_kmem, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE); 375768ac2337Sjl } 375868ac2337Sjl return (err); 375925cf1a30Sjl } 376025cf1a30Sjl 376125cf1a30Sjl sbd_error_t * 376225cf1a30Sjl drmach_copy_rename_fini(drmachid_t id) 376325cf1a30Sjl { 376425cf1a30Sjl drmach_copy_rename_program_t *prog = id; 376525cf1a30Sjl sbd_error_t *err = NULL; 376625cf1a30Sjl int rv; 3767b307f191Sbm uint_t fmem_error; 376825cf1a30Sjl 376925cf1a30Sjl /* 377025cf1a30Sjl * Note that we have to delay calling SCF to find out the 377125cf1a30Sjl * status of the FMEM operation here because SCF cannot 377225cf1a30Sjl * respond while it is suspended. 377325cf1a30Sjl * This create a small window when we are sure about the 377425cf1a30Sjl * base address of the system board. 377525cf1a30Sjl * If there is any call to mc-opl to get memory unum, 377625cf1a30Sjl * mc-opl will return UNKNOWN as the unum. 377725cf1a30Sjl */ 377825cf1a30Sjl 377968ac2337Sjl /* 378068ac2337Sjl * we have to remap again because all the pointer like data, 378168ac2337Sjl * critical in prog are based on the alternate vmem space. 378268ac2337Sjl */ 378368ac2337Sjl (void) drmach_lock_critical((caddr_t)prog, (caddr_t)prog->locked_prog); 378468ac2337Sjl 378525cf1a30Sjl if (prog->data->c_ml != NULL) 378625cf1a30Sjl memlist_delete(prog->data->c_ml); 378725cf1a30Sjl 378825cf1a30Sjl if ((prog->data->fmem_status.op & 3789*e98fafb9Sjl (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) != 3790*e98fafb9Sjl (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) { 3791*e98fafb9Sjl cmn_err(CE_PANIC, "drmach_copy_rename_fini: invalid op " 3792*e98fafb9Sjl "code %x\n", prog->data->fmem_status.op); 379325cf1a30Sjl } 379425cf1a30Sjl 3795b307f191Sbm fmem_error = prog->data->fmem_status.error; 3796b307f191Sbm if (fmem_error != ESBD_NOERROR) { 3797b307f191Sbm err = drerr_new(1, fmem_error, NULL); 3798b307f191Sbm } 3799b307f191Sbm 380025cf1a30Sjl /* possible ops are SCF_START, MC_SUSPEND */ 380125cf1a30Sjl if (prog->critical->fmem_issued) { 3802b307f191Sbm if (fmem_error != ESBD_NOERROR) { 3803*e98fafb9Sjl cmn_err(CE_PANIC, "Irrecoverable FMEM error %d\n", 3804*e98fafb9Sjl fmem_error); 3805b307f191Sbm } 380625cf1a30Sjl rv = (*prog->data->scf_fmem_end)(); 380725cf1a30Sjl if (rv) { 380868ac2337Sjl cmn_err(CE_PANIC, "scf_fmem_end() failed rv=%d", rv); 380925cf1a30Sjl } 381025cf1a30Sjl /* 381125cf1a30Sjl * If we get here, rename is successful. 381225cf1a30Sjl * Do all the copy rename post processing. 381325cf1a30Sjl */ 381425cf1a30Sjl drmach_swap_pa((drmach_mem_t *)prog->data->s_mem, 3815*e98fafb9Sjl (drmach_mem_t *)prog->data->t_mem); 381625cf1a30Sjl } else { 381725cf1a30Sjl rv = (*prog->data->scf_fmem_cancel)(); 381825cf1a30Sjl if (rv) { 3819*e98fafb9Sjl cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x", 3820*e98fafb9Sjl rv); 3821*e98fafb9Sjl if (!err) { 3822*e98fafb9Sjl err = drerr_new(1, EOPL_SCF_FMEM_CANCEL, 3823*e98fafb9Sjl "scf_fmem_cancel() failed. rv = 0x%x", rv); 3824*e98fafb9Sjl } 382525cf1a30Sjl } 382625cf1a30Sjl } 382725cf1a30Sjl /* soft resume mac patrol */ 382825cf1a30Sjl (*prog->data->mc_resume)(); 382925cf1a30Sjl 383068ac2337Sjl drmach_unlock_critical((caddr_t)prog->locked_prog); 383168ac2337Sjl 383268ac2337Sjl vmem_free(heap_arena, prog->locked_prog, 3833*e98fafb9Sjl DRMACH_FMEM_LOCKED_PAGES * PAGESIZE); 383425cf1a30Sjl kmem_free(prog, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE); 383525cf1a30Sjl return (err); 383625cf1a30Sjl } 383725cf1a30Sjl 383825cf1a30Sjl /*ARGSUSED*/ 383925cf1a30Sjl static void 384025cf1a30Sjl drmach_copy_rename_slave(struct regs *rp, drmachid_t id) 384125cf1a30Sjl { 384268ac2337Sjl drmach_copy_rename_program_t *prog = 3843*e98fafb9Sjl (drmach_copy_rename_program_t *)id; 384425cf1a30Sjl register int cpuid; 384525cf1a30Sjl extern void drmach_flush(); 384625cf1a30Sjl extern void membar_sync_il(); 384725cf1a30Sjl extern void drmach_flush_icache(); 384825cf1a30Sjl on_trap_data_t otd; 384925cf1a30Sjl 385025cf1a30Sjl cpuid = CPU->cpu_id; 385125cf1a30Sjl 385225cf1a30Sjl if (on_trap(&otd, OT_DATA_EC)) { 385325cf1a30Sjl no_trap(); 3854b307f191Sbm prog->data->error[cpuid] = EOPL_FMEM_COPY_ERROR; 385525cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_EXIT; 385668ac2337Sjl drmach_flush_icache(); 385768ac2337Sjl membar_sync_il(); 385825cf1a30Sjl return; 385925cf1a30Sjl } 386025cf1a30Sjl 386125cf1a30Sjl 386225cf1a30Sjl /* 386325cf1a30Sjl * jmp drmach_copy_rename_prog(). 386425cf1a30Sjl */ 386525cf1a30Sjl 386625cf1a30Sjl drmach_flush(prog->critical, PAGESIZE); 386725cf1a30Sjl (void) prog->critical->run(prog, cpuid); 386825cf1a30Sjl drmach_flush_icache(); 386925cf1a30Sjl 387025cf1a30Sjl no_trap(); 387125cf1a30Sjl 387225cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_EXIT; 387368ac2337Sjl 387425cf1a30Sjl membar_sync_il(); 387525cf1a30Sjl } 387625cf1a30Sjl 387725cf1a30Sjl static void 387825cf1a30Sjl drmach_swap_pa(drmach_mem_t *s_mem, drmach_mem_t *t_mem) 387925cf1a30Sjl { 388025cf1a30Sjl uint64_t s_base, t_base; 388125cf1a30Sjl drmach_board_t *s_board, *t_board; 388225cf1a30Sjl struct memlist *ml; 388325cf1a30Sjl 388425cf1a30Sjl s_board = s_mem->dev.bp; 388525cf1a30Sjl t_board = t_mem->dev.bp; 388625cf1a30Sjl if (s_board == NULL || t_board == NULL) { 388725cf1a30Sjl cmn_err(CE_PANIC, "Cannot locate source or target board\n"); 388825cf1a30Sjl return; 388925cf1a30Sjl } 389025cf1a30Sjl s_base = s_mem->slice_base; 389125cf1a30Sjl t_base = t_mem->slice_base; 389225cf1a30Sjl 389325cf1a30Sjl s_mem->slice_base = t_base; 389425cf1a30Sjl s_mem->base_pa = (s_mem->base_pa - s_base) + t_base; 389525cf1a30Sjl 389625cf1a30Sjl for (ml = s_mem->memlist; ml; ml = ml->next) { 389725cf1a30Sjl ml->address = ml->address - s_base + t_base; 389825cf1a30Sjl } 389925cf1a30Sjl 390025cf1a30Sjl t_mem->slice_base = s_base; 390125cf1a30Sjl t_mem->base_pa = (t_mem->base_pa - t_base) + s_base; 390225cf1a30Sjl 390325cf1a30Sjl for (ml = t_mem->memlist; ml; ml = ml->next) { 390425cf1a30Sjl ml->address = ml->address - t_base + s_base; 390525cf1a30Sjl } 390625cf1a30Sjl 390725cf1a30Sjl /* 390825cf1a30Sjl * IKP has to update the sb-mem-ranges for mac patrol driver 390925cf1a30Sjl * when it resumes, it will re-read the sb-mem-range property 391025cf1a30Sjl * to get the new base address 391125cf1a30Sjl */ 391225cf1a30Sjl if (oplcfg_pa_swap(s_board->bnum, t_board->bnum) != 0) 391325cf1a30Sjl cmn_err(CE_PANIC, "Could not update device nodes\n"); 391425cf1a30Sjl } 391525cf1a30Sjl 391625cf1a30Sjl void 391725cf1a30Sjl drmach_copy_rename(drmachid_t id) 391825cf1a30Sjl { 391968ac2337Sjl drmach_copy_rename_program_t *prog_kmem = id; 392068ac2337Sjl drmach_copy_rename_program_t *prog; 392125cf1a30Sjl cpuset_t cpuset; 392225cf1a30Sjl int cpuid; 392325cf1a30Sjl uint64_t inst; 392425cf1a30Sjl register int rtn; 392525cf1a30Sjl extern int in_sync; 392625cf1a30Sjl int old_in_sync; 392725cf1a30Sjl extern void drmach_sys_trap(); 392825cf1a30Sjl extern void drmach_flush(); 392925cf1a30Sjl extern void drmach_flush_icache(); 393025cf1a30Sjl extern uint64_t patch_inst(uint64_t *, uint64_t); 393125cf1a30Sjl on_trap_data_t otd; 393225cf1a30Sjl 39338eafe49bSbm 393468ac2337Sjl prog = prog_kmem->locked_prog; 393568ac2337Sjl 39368eafe49bSbm 393768ac2337Sjl /* 393868ac2337Sjl * We must immediately drop in the TLB because all pointers 393968ac2337Sjl * are based on the alternate vmem space. 394068ac2337Sjl */ 394168ac2337Sjl 394268ac2337Sjl (void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog); 394368ac2337Sjl 394468ac2337Sjl /* 394568ac2337Sjl * we call scf to get the base address here becuase if scf 394668ac2337Sjl * has not been suspended yet, the active path can be changing and 394768ac2337Sjl * sometimes it is not even mapped. We call the interface when 394868ac2337Sjl * the OS has been quiesced. 394968ac2337Sjl */ 395068ac2337Sjl prog->critical->scf_reg_base = (*prog->data->scf_get_base_addr)(); 395168ac2337Sjl 395268ac2337Sjl if (prog->critical->scf_reg_base == (uint64_t)-1 || 3953*e98fafb9Sjl prog->critical->scf_reg_base == NULL) { 3954b307f191Sbm prog->data->fmem_status.error = EOPL_FMEM_SCF_ERR; 395568ac2337Sjl drmach_unlock_critical((caddr_t)prog); 395625cf1a30Sjl return; 395725cf1a30Sjl } 395825cf1a30Sjl 395925cf1a30Sjl cpuset = prog->data->cpu_ready_set; 396025cf1a30Sjl 396125cf1a30Sjl for (cpuid = 0; cpuid < NCPU; cpuid++) { 396225cf1a30Sjl if (CPU_IN_SET(cpuset, cpuid)) { 396325cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_START; 3964b307f191Sbm prog->data->error[cpuid] = ESBD_NOERROR; 396525cf1a30Sjl } 396625cf1a30Sjl } 396725cf1a30Sjl 396825cf1a30Sjl old_in_sync = in_sync; 396925cf1a30Sjl in_sync = 1; 397025cf1a30Sjl cpuid = CPU->cpu_id; 397125cf1a30Sjl 397225cf1a30Sjl CPUSET_DEL(cpuset, cpuid); 397325cf1a30Sjl 397468ac2337Sjl for (cpuid = 0; cpuid < NCPU; cpuid++) { 397568ac2337Sjl if (CPU_IN_SET(cpuset, cpuid)) { 397668ac2337Sjl xc_one(cpuid, (xcfunc_t *)drmach_lock_critical, 3977*e98fafb9Sjl (uint64_t)prog_kmem, (uint64_t)prog); 397868ac2337Sjl } 397968ac2337Sjl } 398068ac2337Sjl 398168ac2337Sjl cpuid = CPU->cpu_id; 398225cf1a30Sjl 398325cf1a30Sjl xt_some(cpuset, (xcfunc_t *)drmach_sys_trap, 3984*e98fafb9Sjl (uint64_t)drmach_copy_rename_slave, (uint64_t)prog); 398525cf1a30Sjl xt_sync(cpuset); 398625cf1a30Sjl 398725cf1a30Sjl if (on_trap(&otd, OT_DATA_EC)) { 3988b307f191Sbm rtn = EOPL_FMEM_COPY_ERROR; 398968ac2337Sjl drmach_flush_icache(); 399025cf1a30Sjl goto done; 399125cf1a30Sjl } 399225cf1a30Sjl 399325cf1a30Sjl /* 399425cf1a30Sjl * jmp drmach_copy_rename_prog(). 399525cf1a30Sjl */ 39968eafe49bSbm 399725cf1a30Sjl drmach_flush(prog->critical, PAGESIZE); 399825cf1a30Sjl rtn = prog->critical->run(prog, cpuid); 3999*e98fafb9Sjl 400025cf1a30Sjl drmach_flush_icache(); 400125cf1a30Sjl 400225cf1a30Sjl 400325cf1a30Sjl done: 400425cf1a30Sjl no_trap(); 4005b307f191Sbm if (rtn == EOPL_FMEM_HW_ERROR) { 400625cf1a30Sjl kpreempt_enable(); 4007*e98fafb9Sjl prom_panic("URGENT_ERROR_TRAP is detected during FMEM.\n"); 400825cf1a30Sjl } 400925cf1a30Sjl 401025cf1a30Sjl /* 401125cf1a30Sjl * In normal case, all slave CPU's are still spinning in 401225cf1a30Sjl * the assembly code. The master has to patch the instruction 401325cf1a30Sjl * to get them out. 401425cf1a30Sjl * In error case, e.g. COPY_ERROR, some slave CPU's might 401525cf1a30Sjl * have aborted and already returned and sset LOOP_EXIT status. 401625cf1a30Sjl * Some CPU might still be copying. 401725cf1a30Sjl * In any case, some delay is necessary to give them 401825cf1a30Sjl * enough time to set the LOOP_EXIT status. 401925cf1a30Sjl */ 402025cf1a30Sjl 402125cf1a30Sjl for (;;) { 402225cf1a30Sjl inst = patch_inst((uint64_t *)prog->critical->loop_rtn, 4023*e98fafb9Sjl prog->critical->inst_loop_ret); 402425cf1a30Sjl if (prog->critical->inst_loop_ret == inst) { 402525cf1a30Sjl break; 402625cf1a30Sjl } 402725cf1a30Sjl } 402825cf1a30Sjl 402925cf1a30Sjl for (cpuid = 0; cpuid < NCPU; cpuid++) { 403025cf1a30Sjl uint64_t last, now; 403125cf1a30Sjl if (!CPU_IN_SET(cpuset, cpuid)) { 403225cf1a30Sjl continue; 403325cf1a30Sjl } 403425cf1a30Sjl last = prog->stat->nbytes[cpuid]; 403525cf1a30Sjl /* 403625cf1a30Sjl * Wait for all CPU to exit. 403725cf1a30Sjl * However we do not want an infinite loop 403825cf1a30Sjl * so we detect hangup situation here. 403925cf1a30Sjl * If the slave CPU is still copying data, 404025cf1a30Sjl * we will continue to wait. 404125cf1a30Sjl * In error cases, the master has already set 404225cf1a30Sjl * fmem_status.error to abort the copying. 404325cf1a30Sjl * 1 m.s delay for them to abort copying and 404425cf1a30Sjl * return to drmach_copy_rename_slave to set 404525cf1a30Sjl * FMEM_LOOP_EXIT status should be enough. 404625cf1a30Sjl */ 404725cf1a30Sjl for (;;) { 404825cf1a30Sjl if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT) 404925cf1a30Sjl break; 405025cf1a30Sjl drmach_sleep_il(); 405125cf1a30Sjl drv_usecwait(1000); 405225cf1a30Sjl now = prog->stat->nbytes[cpuid]; 405325cf1a30Sjl if (now <= last) { 4054*e98fafb9Sjl drv_usecwait(1000); 4055*e98fafb9Sjl if (prog->critical->stat[cpuid] == 4056*e98fafb9Sjl FMEM_LOOP_EXIT) 4057*e98fafb9Sjl break; 4058*e98fafb9Sjl cmn_err(CE_PANIC, "CPU %d hang during Copy " 4059*e98fafb9Sjl "Rename", cpuid); 406025cf1a30Sjl } 406125cf1a30Sjl last = now; 406225cf1a30Sjl } 4063b307f191Sbm if (prog->data->error[cpuid] == EOPL_FMEM_HW_ERROR) { 4064*e98fafb9Sjl prom_panic("URGENT_ERROR_TRAP is detected during " 4065*e98fafb9Sjl "FMEM.\n"); 406625cf1a30Sjl } 406725cf1a30Sjl } 406868ac2337Sjl 406968ac2337Sjl /* 407068ac2337Sjl * This must be done after all strands have exit. 407168ac2337Sjl * Removing the TLB entry will affect both strands 407268ac2337Sjl * in the same core. 407368ac2337Sjl */ 407468ac2337Sjl 407568ac2337Sjl for (cpuid = 0; cpuid < NCPU; cpuid++) { 407668ac2337Sjl if (CPU_IN_SET(cpuset, cpuid)) { 407768ac2337Sjl xc_one(cpuid, (xcfunc_t *)drmach_unlock_critical, 4078*e98fafb9Sjl (uint64_t)prog, 0); 407968ac2337Sjl } 408068ac2337Sjl } 408125cf1a30Sjl 408225cf1a30Sjl in_sync = old_in_sync; 408325cf1a30Sjl 408468ac2337Sjl /* 408568ac2337Sjl * we should unlock before the following lock to keep the kpreempt 408668ac2337Sjl * count correct. 408768ac2337Sjl */ 408868ac2337Sjl (void) drmach_unlock_critical((caddr_t)prog); 408968ac2337Sjl 409068ac2337Sjl /* 409168ac2337Sjl * we must remap again. TLB might have been removed in above xcall. 409268ac2337Sjl */ 409368ac2337Sjl 409468ac2337Sjl (void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog); 409568ac2337Sjl 4096b307f191Sbm if (prog->data->fmem_status.error == ESBD_NOERROR) 409725cf1a30Sjl prog->data->fmem_status.error = rtn; 409825cf1a30Sjl 409925cf1a30Sjl if (prog->data->copy_wait_time > 0) { 410025cf1a30Sjl DRMACH_PR("Unexpected long wait time %ld seconds " 4101*e98fafb9Sjl "during copy rename on CPU %d\n", 4102*e98fafb9Sjl prog->data->copy_wait_time/prog->data->stick_freq, 4103*e98fafb9Sjl prog->data->slowest_cpuid); 410425cf1a30Sjl } 410568ac2337Sjl drmach_unlock_critical((caddr_t)prog); 410625cf1a30Sjl } 4107