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 */
2107d06da5SSurya Prakki
2225cf1a30Sjl /*
2356f33205SJonathan Adams * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2425cf1a30Sjl * Use is subject to license terms.
2525cf1a30Sjl */
2625cf1a30Sjl
27*3fe80ca4SDan Cross /*
28*3fe80ca4SDan Cross * Copyright 2023 Oxide Computer Company
29*3fe80ca4SDan Cross */
30*3fe80ca4SDan Cross
3125cf1a30Sjl #include <sys/debug.h>
3225cf1a30Sjl #include <sys/types.h>
3325cf1a30Sjl #include <sys/varargs.h>
3425cf1a30Sjl #include <sys/errno.h>
3525cf1a30Sjl #include <sys/cred.h>
3625cf1a30Sjl #include <sys/dditypes.h>
3725cf1a30Sjl #include <sys/devops.h>
3825cf1a30Sjl #include <sys/modctl.h>
3925cf1a30Sjl #include <sys/poll.h>
4025cf1a30Sjl #include <sys/conf.h>
4125cf1a30Sjl #include <sys/ddi.h>
4225cf1a30Sjl #include <sys/sunddi.h>
4325cf1a30Sjl #include <sys/sunndi.h>
4425cf1a30Sjl #include <sys/ndi_impldefs.h>
4525cf1a30Sjl #include <sys/stat.h>
4625cf1a30Sjl #include <sys/kmem.h>
4725cf1a30Sjl #include <sys/vmem.h>
4825cf1a30Sjl #include <sys/opl_olympus_regs.h>
4925cf1a30Sjl #include <sys/cpuvar.h>
5025cf1a30Sjl #include <sys/cpupart.h>
5125cf1a30Sjl #include <sys/mem_config.h>
5225cf1a30Sjl #include <sys/ddi_impldefs.h>
5325cf1a30Sjl #include <sys/systm.h>
5425cf1a30Sjl #include <sys/machsystm.h>
5525cf1a30Sjl #include <sys/autoconf.h>
5625cf1a30Sjl #include <sys/cmn_err.h>
5725cf1a30Sjl #include <sys/sysmacros.h>
5825cf1a30Sjl #include <sys/x_call.h>
5925cf1a30Sjl #include <sys/promif.h>
6025cf1a30Sjl #include <sys/prom_plat.h>
6125cf1a30Sjl #include <sys/membar.h>
6225cf1a30Sjl #include <vm/seg_kmem.h>
6325cf1a30Sjl #include <sys/mem_cage.h>
6425cf1a30Sjl #include <sys/stack.h>
6525cf1a30Sjl #include <sys/archsystm.h>
6625cf1a30Sjl #include <vm/hat_sfmmu.h>
6725cf1a30Sjl #include <sys/pte.h>
6825cf1a30Sjl #include <sys/mmu.h>
6925cf1a30Sjl #include <sys/cpu_module.h>
7025cf1a30Sjl #include <sys/obpdefs.h>
7125cf1a30Sjl #include <sys/note.h>
7225cf1a30Sjl #include <sys/ontrap.h>
7325cf1a30Sjl #include <sys/cpu_sgnblk_defs.h>
7425cf1a30Sjl #include <sys/opl.h>
75e98fafb9Sjl #include <sys/cpu_impl.h>
7625cf1a30Sjl
7725cf1a30Sjl
7825cf1a30Sjl #include <sys/promimpl.h>
7925cf1a30Sjl #include <sys/prom_plat.h>
8025cf1a30Sjl #include <sys/kobj.h>
8125cf1a30Sjl
8225cf1a30Sjl #include <sys/sysevent.h>
8325cf1a30Sjl #include <sys/sysevent/dr.h>
8425cf1a30Sjl #include <sys/sysevent/eventdefs.h>
8525cf1a30Sjl
8625cf1a30Sjl #include <sys/drmach.h>
8725cf1a30Sjl #include <sys/dr_util.h>
8825cf1a30Sjl
8925cf1a30Sjl #include <sys/fcode.h>
9025cf1a30Sjl #include <sys/opl_cfg.h>
9125cf1a30Sjl
9225cf1a30Sjl extern void bcopy32_il(uint64_t, uint64_t);
9325cf1a30Sjl extern void flush_cache_il(void);
9425cf1a30Sjl extern void drmach_sleep_il(void);
9525cf1a30Sjl
9625cf1a30Sjl typedef struct {
9725cf1a30Sjl struct drmach_node *node;
9825cf1a30Sjl void *data;
9925cf1a30Sjl } drmach_node_walk_args_t;
10025cf1a30Sjl
10125cf1a30Sjl typedef struct drmach_node {
10225cf1a30Sjl void *here;
10325cf1a30Sjl
10425cf1a30Sjl pnode_t (*get_dnode)(struct drmach_node *node);
10525cf1a30Sjl int (*walk)(struct drmach_node *node, void *data,
10625cf1a30Sjl int (*cb)(drmach_node_walk_args_t *args));
10725cf1a30Sjl dev_info_t *(*n_getdip)(struct drmach_node *node);
10825cf1a30Sjl int (*n_getproplen)(struct drmach_node *node, char *name,
10925cf1a30Sjl int *len);
11025cf1a30Sjl int (*n_getprop)(struct drmach_node *node, char *name,
11125cf1a30Sjl void *buf, int len);
11225cf1a30Sjl int (*get_parent)(struct drmach_node *node,
11325cf1a30Sjl struct drmach_node *pnode);
11425cf1a30Sjl } drmach_node_t;
11525cf1a30Sjl
11625cf1a30Sjl typedef struct {
11725cf1a30Sjl int min_index;
11825cf1a30Sjl int max_index;
11925cf1a30Sjl int arr_sz;
12025cf1a30Sjl drmachid_t *arr;
12125cf1a30Sjl } drmach_array_t;
12225cf1a30Sjl
12325cf1a30Sjl typedef struct {
12425cf1a30Sjl void *isa;
12525cf1a30Sjl
12625cf1a30Sjl void (*dispose)(drmachid_t);
12725cf1a30Sjl sbd_error_t *(*release)(drmachid_t);
12825cf1a30Sjl sbd_error_t *(*status)(drmachid_t, drmach_status_t *);
12925cf1a30Sjl
13025cf1a30Sjl char name[MAXNAMELEN];
13125cf1a30Sjl } drmach_common_t;
13225cf1a30Sjl
13325cf1a30Sjl typedef struct {
13425cf1a30Sjl uint32_t core_present;
13525cf1a30Sjl uint32_t core_hotadded;
13625cf1a30Sjl uint32_t core_started;
13725cf1a30Sjl } drmach_cmp_t;
13825cf1a30Sjl
13925cf1a30Sjl typedef struct {
14025cf1a30Sjl drmach_common_t cm;
14125cf1a30Sjl int bnum;
14225cf1a30Sjl int assigned;
14325cf1a30Sjl int powered;
14425cf1a30Sjl int connected;
14525cf1a30Sjl int cond;
14625cf1a30Sjl drmach_node_t *tree;
14725cf1a30Sjl drmach_array_t *devices;
14825cf1a30Sjl int boot_board; /* if board exists on bootup */
14925cf1a30Sjl drmach_cmp_t cores[OPL_MAX_COREID_PER_BOARD];
15025cf1a30Sjl } drmach_board_t;
15125cf1a30Sjl
15225cf1a30Sjl typedef struct {
15325cf1a30Sjl drmach_common_t cm;
15425cf1a30Sjl drmach_board_t *bp;
15525cf1a30Sjl int unum;
15625cf1a30Sjl int portid;
15725cf1a30Sjl int busy;
15825cf1a30Sjl int powered;
15925cf1a30Sjl const char *type;
16025cf1a30Sjl drmach_node_t *node;
16125cf1a30Sjl } drmach_device_t;
16225cf1a30Sjl
16325cf1a30Sjl typedef struct drmach_cpu {
16425cf1a30Sjl drmach_device_t dev;
16525cf1a30Sjl processorid_t cpuid;
16625cf1a30Sjl int sb;
16725cf1a30Sjl int chipid;
16825cf1a30Sjl int coreid;
16925cf1a30Sjl int strandid;
17025cf1a30Sjl int status;
17125cf1a30Sjl #define OPL_CPU_HOTADDED 1
17225cf1a30Sjl } drmach_cpu_t;
17325cf1a30Sjl
17425cf1a30Sjl typedef struct drmach_mem {
17525cf1a30Sjl drmach_device_t dev;
17625cf1a30Sjl uint64_t slice_base;
17725cf1a30Sjl uint64_t slice_size;
17825cf1a30Sjl uint64_t base_pa; /* lowest installed memory base */
17925cf1a30Sjl uint64_t nbytes; /* size of installed memory */
18025cf1a30Sjl struct memlist *memlist;
18125cf1a30Sjl } drmach_mem_t;
18225cf1a30Sjl
18325cf1a30Sjl typedef struct drmach_io {
18425cf1a30Sjl drmach_device_t dev;
18525cf1a30Sjl int channel;
18625cf1a30Sjl int leaf;
18725cf1a30Sjl } drmach_io_t;
18825cf1a30Sjl
18925cf1a30Sjl typedef struct drmach_domain_info {
19025cf1a30Sjl uint32_t floating;
19125cf1a30Sjl int allow_dr;
19225cf1a30Sjl } drmach_domain_info_t;
19325cf1a30Sjl
19425cf1a30Sjl drmach_domain_info_t drmach_domain;
19525cf1a30Sjl
19625cf1a30Sjl typedef struct {
19725cf1a30Sjl int flags;
19825cf1a30Sjl drmach_device_t *dp;
19925cf1a30Sjl sbd_error_t *err;
20025cf1a30Sjl dev_info_t *dip;
20125cf1a30Sjl } drmach_config_args_t;
20225cf1a30Sjl
20325cf1a30Sjl typedef struct {
20425cf1a30Sjl drmach_board_t *obj;
20525cf1a30Sjl int ndevs;
20625cf1a30Sjl void *a;
20725cf1a30Sjl sbd_error_t *(*found)(void *a, const char *, int, drmachid_t);
20825cf1a30Sjl sbd_error_t *err;
20925cf1a30Sjl } drmach_board_cb_data_t;
21025cf1a30Sjl
21125cf1a30Sjl static drmach_array_t *drmach_boards;
21225cf1a30Sjl
21325cf1a30Sjl static sbd_error_t *drmach_device_new(drmach_node_t *,
21425cf1a30Sjl drmach_board_t *, int, drmachid_t *);
21525cf1a30Sjl static sbd_error_t *drmach_cpu_new(drmach_device_t *, drmachid_t *);
21625cf1a30Sjl static sbd_error_t *drmach_mem_new(drmach_device_t *, drmachid_t *);
21725cf1a30Sjl static sbd_error_t *drmach_io_new(drmach_device_t *, drmachid_t *);
21825cf1a30Sjl
21925cf1a30Sjl static dev_info_t *drmach_node_ddi_get_dip(drmach_node_t *np);
22025cf1a30Sjl static int drmach_node_ddi_get_prop(drmach_node_t *np,
22125cf1a30Sjl char *name, void *buf, int len);
22225cf1a30Sjl static int drmach_node_ddi_get_proplen(drmach_node_t *np,
22325cf1a30Sjl char *name, int *len);
22425cf1a30Sjl
225bbe1232eSToomas Soome static int drmach_get_portid(drmach_node_t *);
22625cf1a30Sjl static sbd_error_t *drmach_i_status(drmachid_t, drmach_status_t *);
22725cf1a30Sjl static int opl_check_dr_status();
22825cf1a30Sjl static void drmach_io_dispose(drmachid_t);
22925cf1a30Sjl static sbd_error_t *drmach_io_release(drmachid_t);
23025cf1a30Sjl static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
231bbe1232eSToomas Soome static int drmach_init(void);
232bbe1232eSToomas Soome static void drmach_fini(void);
23325cf1a30Sjl static void drmach_swap_pa(drmach_mem_t *, drmach_mem_t *);
23425cf1a30Sjl static drmach_board_t *drmach_get_board_by_bnum(int);
23525cf1a30Sjl
2368682d1efSRichard Lowe static sbd_error_t *drmach_board_release(drmachid_t);
2378682d1efSRichard Lowe static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
2388682d1efSRichard Lowe static void drmach_cpu_dispose(drmachid_t);
2398682d1efSRichard Lowe static sbd_error_t *drmach_cpu_release(drmachid_t);
2408682d1efSRichard Lowe static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
2418682d1efSRichard Lowe static void drmach_mem_dispose(drmachid_t);
2428682d1efSRichard Lowe static sbd_error_t *drmach_mem_release(drmachid_t);
2438682d1efSRichard Lowe static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
2448682d1efSRichard Lowe
24525cf1a30Sjl /* options for the second argument in drmach_add_remove_cpu() */
24625cf1a30Sjl #define HOTADD_CPU 1
24725cf1a30Sjl #define HOTREMOVE_CPU 2
24825cf1a30Sjl
24925cf1a30Sjl #define ON_BOARD_CORE_NUM(x) (((uint_t)(x) / OPL_MAX_STRANDID_PER_CORE) & \
25025cf1a30Sjl (OPL_MAX_COREID_PER_BOARD - 1))
25125cf1a30Sjl
25225cf1a30Sjl extern struct cpu *SIGBCPU;
25325cf1a30Sjl
25425cf1a30Sjl static int drmach_name2type_idx(char *);
25525cf1a30Sjl static drmach_board_t *drmach_board_new(int, int);
25625cf1a30Sjl
25725cf1a30Sjl #ifdef DEBUG
25825cf1a30Sjl
25925cf1a30Sjl #define DRMACH_PR if (drmach_debug) printf
26025cf1a30Sjl int drmach_debug = 1; /* set to non-zero to enable debug messages */
26125cf1a30Sjl #else
26225cf1a30Sjl
26325cf1a30Sjl #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf
26425cf1a30Sjl #endif /* DEBUG */
26525cf1a30Sjl
26625cf1a30Sjl
26725cf1a30Sjl #define DRMACH_OBJ(id) ((drmach_common_t *)id)
26825cf1a30Sjl
269ddf95635Sbm #define DRMACH_NULL_ID(id) ((id) == 0)
270ddf95635Sbm
27125cf1a30Sjl #define DRMACH_IS_BOARD_ID(id) \
27225cf1a30Sjl ((id != 0) && \
27325cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
27425cf1a30Sjl
27525cf1a30Sjl #define DRMACH_IS_CPU_ID(id) \
27625cf1a30Sjl ((id != 0) && \
27725cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
27825cf1a30Sjl
27925cf1a30Sjl #define DRMACH_IS_MEM_ID(id) \
28025cf1a30Sjl ((id != 0) && \
28125cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
28225cf1a30Sjl
28325cf1a30Sjl #define DRMACH_IS_IO_ID(id) \
28425cf1a30Sjl ((id != 0) && \
28525cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
28625cf1a30Sjl
28725cf1a30Sjl #define DRMACH_IS_DEVICE_ID(id) \
28825cf1a30Sjl ((id != 0) && \
28925cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
29025cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
29125cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
29225cf1a30Sjl
29325cf1a30Sjl #define DRMACH_IS_ID(id) \
29425cf1a30Sjl ((id != 0) && \
29525cf1a30Sjl (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \
29625cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
29725cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
29825cf1a30Sjl DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
29925cf1a30Sjl
30025cf1a30Sjl #define DRMACH_INTERNAL_ERROR() \
30125cf1a30Sjl drerr_new(1, EOPL_INTERNAL, drmach_ie_fmt, __LINE__)
30225cf1a30Sjl
30325cf1a30Sjl static char *drmach_ie_fmt = "drmach.c %d";
30425cf1a30Sjl
30525cf1a30Sjl static struct {
30625cf1a30Sjl const char *name;
30725cf1a30Sjl const char *type;
30825cf1a30Sjl sbd_error_t *(*new)(drmach_device_t *, drmachid_t *);
30925cf1a30Sjl } drmach_name2type[] = {
31025cf1a30Sjl { "cpu", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
31125cf1a30Sjl { "pseudo-mc", DRMACH_DEVTYPE_MEM, drmach_mem_new },
31225cf1a30Sjl { "pci", DRMACH_DEVTYPE_PCI, drmach_io_new },
31325cf1a30Sjl };
31425cf1a30Sjl
31525cf1a30Sjl /* utility */
31625cf1a30Sjl #define MBYTE (1048576ull)
31725cf1a30Sjl
31825cf1a30Sjl /*
31925cf1a30Sjl * drmach autoconfiguration data structures and interfaces
32025cf1a30Sjl */
32125cf1a30Sjl
32225cf1a30Sjl extern struct mod_ops mod_miscops;
32325cf1a30Sjl
32425cf1a30Sjl static struct modlmisc modlmisc = {
32525cf1a30Sjl &mod_miscops,
32625cf1a30Sjl "OPL DR 1.1"
32725cf1a30Sjl };
32825cf1a30Sjl
32925cf1a30Sjl static struct modlinkage modlinkage = {
33025cf1a30Sjl MODREV_1,
33125cf1a30Sjl (void *)&modlmisc,
33225cf1a30Sjl NULL
33325cf1a30Sjl };
33425cf1a30Sjl
33525cf1a30Sjl static krwlock_t drmach_boards_rwlock;
33625cf1a30Sjl
33725cf1a30Sjl typedef const char *fn_t;
33825cf1a30Sjl
33925cf1a30Sjl int
_init(void)34025cf1a30Sjl _init(void)
34125cf1a30Sjl {
34225cf1a30Sjl int err;
34325cf1a30Sjl
34425cf1a30Sjl if ((err = drmach_init()) != 0) {
34525cf1a30Sjl return (err);
34625cf1a30Sjl }
34725cf1a30Sjl
34825cf1a30Sjl if ((err = mod_install(&modlinkage)) != 0) {
34925cf1a30Sjl drmach_fini();
35025cf1a30Sjl }
35125cf1a30Sjl
35225cf1a30Sjl return (err);
35325cf1a30Sjl }
35425cf1a30Sjl
35525cf1a30Sjl int
_fini(void)35625cf1a30Sjl _fini(void)
35725cf1a30Sjl {
35825cf1a30Sjl int err;
35925cf1a30Sjl
36025cf1a30Sjl if ((err = mod_remove(&modlinkage)) == 0)
36125cf1a30Sjl drmach_fini();
36225cf1a30Sjl
36325cf1a30Sjl return (err);
36425cf1a30Sjl }
36525cf1a30Sjl
36625cf1a30Sjl int
_info(struct modinfo * modinfop)36725cf1a30Sjl _info(struct modinfo *modinfop)
36825cf1a30Sjl {
36925cf1a30Sjl return (mod_info(&modlinkage, modinfop));
37025cf1a30Sjl }
37125cf1a30Sjl
37225cf1a30Sjl struct drmach_mc_lookup {
37325cf1a30Sjl int bnum;
37425cf1a30Sjl drmach_board_t *bp;
37525cf1a30Sjl dev_info_t *dip; /* rv - set if found */
37625cf1a30Sjl };
37725cf1a30Sjl
37825cf1a30Sjl #define _ptob64(p) ((uint64_t)(p) << PAGESHIFT)
37925cf1a30Sjl #define _b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
38025cf1a30Sjl
38125cf1a30Sjl static int
drmach_setup_mc_info(dev_info_t * dip,drmach_mem_t * mp)38225cf1a30Sjl drmach_setup_mc_info(dev_info_t *dip, drmach_mem_t *mp)
38325cf1a30Sjl {
38425cf1a30Sjl uint64_t memory_ranges[128];
38525cf1a30Sjl int len;
38625cf1a30Sjl struct memlist *ml;
38725cf1a30Sjl int rv;
38825cf1a30Sjl hwd_sb_t *hwd;
38925cf1a30Sjl hwd_memory_t *pm;
39025cf1a30Sjl
39125cf1a30Sjl len = sizeof (memory_ranges);
392e98fafb9Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
393e98fafb9Sjl "sb-mem-ranges", (caddr_t)&memory_ranges[0], &len) !=
394e98fafb9Sjl DDI_PROP_SUCCESS) {
39525cf1a30Sjl mp->slice_base = 0;
39625cf1a30Sjl mp->slice_size = 0;
39725cf1a30Sjl return (-1);
39825cf1a30Sjl }
39925cf1a30Sjl mp->slice_base = memory_ranges[0];
40025cf1a30Sjl mp->slice_size = memory_ranges[1];
40125cf1a30Sjl
40225cf1a30Sjl if (!mp->dev.bp->boot_board) {
40325cf1a30Sjl int i;
40425cf1a30Sjl
40525cf1a30Sjl rv = opl_read_hwd(mp->dev.bp->bnum, NULL, NULL, NULL, &hwd);
40625cf1a30Sjl
40725cf1a30Sjl if (rv != 0) {
40825cf1a30Sjl return (-1);
40925cf1a30Sjl }
41025cf1a30Sjl
41125cf1a30Sjl ml = NULL;
41225cf1a30Sjl pm = &hwd->sb_cmu.cmu_memory;
41325cf1a30Sjl for (i = 0; i < HWD_MAX_MEM_CHUNKS; i++) {
41425cf1a30Sjl if (pm->mem_chunks[i].chnk_size > 0) {
41525cf1a30Sjl ml = memlist_add_span(ml,
416e98fafb9Sjl pm->mem_chunks[i].chnk_start_address,
417e98fafb9Sjl pm->mem_chunks[i].chnk_size);
41825cf1a30Sjl }
41925cf1a30Sjl }
42025cf1a30Sjl } else {
42125cf1a30Sjl /*
42225cf1a30Sjl * we intersect phys_install to get base_pa.
42325cf1a30Sjl * This only works at bootup time.
42425cf1a30Sjl */
42525cf1a30Sjl
42625cf1a30Sjl memlist_read_lock();
42725cf1a30Sjl ml = memlist_dup(phys_install);
42825cf1a30Sjl memlist_read_unlock();
42925cf1a30Sjl
43025cf1a30Sjl ml = memlist_del_span(ml, 0ull, mp->slice_base);
43125cf1a30Sjl if (ml) {
43225cf1a30Sjl uint64_t basepa, endpa;
43325cf1a30Sjl endpa = _ptob64(physmax + 1);
43425cf1a30Sjl
43525cf1a30Sjl basepa = mp->slice_base + mp->slice_size;
43625cf1a30Sjl
43725cf1a30Sjl ml = memlist_del_span(ml, basepa, endpa - basepa);
43825cf1a30Sjl }
43925cf1a30Sjl }
44025cf1a30Sjl
44125cf1a30Sjl if (ml) {
44225cf1a30Sjl uint64_t nbytes = 0;
44325cf1a30Sjl struct memlist *p;
44456f33205SJonathan Adams for (p = ml; p; p = p->ml_next) {
44556f33205SJonathan Adams nbytes += p->ml_size;
44625cf1a30Sjl }
44725cf1a30Sjl if ((mp->nbytes = nbytes) > 0)
44856f33205SJonathan Adams mp->base_pa = ml->ml_address;
44925cf1a30Sjl else
45025cf1a30Sjl mp->base_pa = 0;
45125cf1a30Sjl mp->memlist = ml;
45225cf1a30Sjl } else {
45325cf1a30Sjl mp->base_pa = 0;
45425cf1a30Sjl mp->nbytes = 0;
45525cf1a30Sjl }
45625cf1a30Sjl return (0);
45725cf1a30Sjl }
45825cf1a30Sjl
45925cf1a30Sjl
46025cf1a30Sjl struct drmach_hotcpu {
46125cf1a30Sjl drmach_board_t *bp;
46225cf1a30Sjl int bnum;
46325cf1a30Sjl int core_id;
464bbe1232eSToomas Soome int rv;
46525cf1a30Sjl int option;
46625cf1a30Sjl };
46725cf1a30Sjl
46825cf1a30Sjl static int
drmach_cpu_cb(dev_info_t * dip,void * arg)46925cf1a30Sjl drmach_cpu_cb(dev_info_t *dip, void *arg)
47025cf1a30Sjl {
47125cf1a30Sjl struct drmach_hotcpu *p = (struct drmach_hotcpu *)arg;
47225cf1a30Sjl char name[OBP_MAXDRVNAME];
47325cf1a30Sjl int len = OBP_MAXDRVNAME;
47425cf1a30Sjl int bnum, core_id, strand_id;
47525cf1a30Sjl drmach_board_t *bp;
47625cf1a30Sjl
47725cf1a30Sjl if (dip == ddi_root_node()) {
47825cf1a30Sjl return (DDI_WALK_CONTINUE);
47925cf1a30Sjl }
48025cf1a30Sjl
48125cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
48225cf1a30Sjl DDI_PROP_DONTPASS, "name",
48325cf1a30Sjl (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
48425cf1a30Sjl return (DDI_WALK_PRUNECHILD);
48525cf1a30Sjl }
48625cf1a30Sjl
48725cf1a30Sjl /* only cmp has board number */
48825cf1a30Sjl bnum = -1;
48925cf1a30Sjl len = sizeof (bnum);
49025cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
49125cf1a30Sjl DDI_PROP_DONTPASS, OBP_BOARDNUM,
49225cf1a30Sjl (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
49325cf1a30Sjl bnum = -1;
49425cf1a30Sjl }
49525cf1a30Sjl
49625cf1a30Sjl if (strcmp(name, "cmp") == 0) {
49725cf1a30Sjl if (bnum != p->bnum)
49825cf1a30Sjl return (DDI_WALK_PRUNECHILD);
49925cf1a30Sjl return (DDI_WALK_CONTINUE);
50025cf1a30Sjl }
50125cf1a30Sjl /* we have already pruned all unwanted cores and cpu's above */
50225cf1a30Sjl if (strcmp(name, "core") == 0) {
50325cf1a30Sjl return (DDI_WALK_CONTINUE);
50425cf1a30Sjl }
50525cf1a30Sjl if (strcmp(name, "cpu") == 0) {
50625cf1a30Sjl processorid_t cpuid;
50725cf1a30Sjl len = sizeof (cpuid);
50825cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
50925cf1a30Sjl DDI_PROP_DONTPASS, "cpuid",
51025cf1a30Sjl (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
51125cf1a30Sjl p->rv = -1;
51225cf1a30Sjl return (DDI_WALK_TERMINATE);
51325cf1a30Sjl }
51425cf1a30Sjl
51525cf1a30Sjl core_id = p->core_id;
51625cf1a30Sjl
51725cf1a30Sjl bnum = LSB_ID(cpuid);
51825cf1a30Sjl
51925cf1a30Sjl if (ON_BOARD_CORE_NUM(cpuid) != core_id)
52025cf1a30Sjl return (DDI_WALK_CONTINUE);
52125cf1a30Sjl
52225cf1a30Sjl bp = p->bp;
52325cf1a30Sjl ASSERT(bnum == bp->bnum);
52425cf1a30Sjl
52525cf1a30Sjl if (p->option == HOTADD_CPU) {
52625cf1a30Sjl if (prom_hotaddcpu(cpuid) != 0) {
52725cf1a30Sjl p->rv = -1;
52825cf1a30Sjl return (DDI_WALK_TERMINATE);
52925cf1a30Sjl }
53025cf1a30Sjl strand_id = STRAND_ID(cpuid);
53125cf1a30Sjl bp->cores[core_id].core_hotadded |= (1 << strand_id);
53225cf1a30Sjl } else if (p->option == HOTREMOVE_CPU) {
53325cf1a30Sjl if (prom_hotremovecpu(cpuid) != 0) {
53425cf1a30Sjl p->rv = -1;
53525cf1a30Sjl return (DDI_WALK_TERMINATE);
53625cf1a30Sjl }
53725cf1a30Sjl strand_id = STRAND_ID(cpuid);
53825cf1a30Sjl bp->cores[core_id].core_hotadded &= ~(1 << strand_id);
53925cf1a30Sjl }
54025cf1a30Sjl return (DDI_WALK_CONTINUE);
54125cf1a30Sjl }
54225cf1a30Sjl
54325cf1a30Sjl return (DDI_WALK_PRUNECHILD);
54425cf1a30Sjl }
54525cf1a30Sjl
54625cf1a30Sjl
54725cf1a30Sjl static int
drmach_add_remove_cpu(int bnum,int core_id,int option)54825cf1a30Sjl drmach_add_remove_cpu(int bnum, int core_id, int option)
54925cf1a30Sjl {
55025cf1a30Sjl struct drmach_hotcpu arg;
55125cf1a30Sjl drmach_board_t *bp;
55225cf1a30Sjl
55325cf1a30Sjl bp = drmach_get_board_by_bnum(bnum);
55425cf1a30Sjl ASSERT(bp);
55525cf1a30Sjl
55625cf1a30Sjl arg.bp = bp;
55725cf1a30Sjl arg.bnum = bnum;
55825cf1a30Sjl arg.core_id = core_id;
55925cf1a30Sjl arg.rv = 0;
56025cf1a30Sjl arg.option = option;
56125cf1a30Sjl ddi_walk_devs(ddi_root_node(), drmach_cpu_cb, (void *)&arg);
56225cf1a30Sjl return (arg.rv);
56325cf1a30Sjl }
56425cf1a30Sjl
56525cf1a30Sjl struct drmach_setup_core_arg {
56625cf1a30Sjl drmach_board_t *bp;
56725cf1a30Sjl };
56825cf1a30Sjl
56925cf1a30Sjl static int
drmach_setup_core_cb(dev_info_t * dip,void * arg)57025cf1a30Sjl drmach_setup_core_cb(dev_info_t *dip, void *arg)
57125cf1a30Sjl {
57225cf1a30Sjl struct drmach_setup_core_arg *p = (struct drmach_setup_core_arg *)arg;
57325cf1a30Sjl char name[OBP_MAXDRVNAME];
57425cf1a30Sjl int len = OBP_MAXDRVNAME;
57525cf1a30Sjl int bnum;
57625cf1a30Sjl int core_id, strand_id;
57725cf1a30Sjl
57825cf1a30Sjl if (dip == ddi_root_node()) {
57925cf1a30Sjl return (DDI_WALK_CONTINUE);
58025cf1a30Sjl }
58125cf1a30Sjl
58225cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
58325cf1a30Sjl DDI_PROP_DONTPASS, "name",
58425cf1a30Sjl (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
58525cf1a30Sjl return (DDI_WALK_PRUNECHILD);
58625cf1a30Sjl }
58725cf1a30Sjl
58825cf1a30Sjl /* only cmp has board number */
58925cf1a30Sjl bnum = -1;
59025cf1a30Sjl len = sizeof (bnum);
59125cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
59225cf1a30Sjl DDI_PROP_DONTPASS, OBP_BOARDNUM,
59325cf1a30Sjl (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
59425cf1a30Sjl bnum = -1;
59525cf1a30Sjl }
59625cf1a30Sjl
59725cf1a30Sjl if (strcmp(name, "cmp") == 0) {
59825cf1a30Sjl if (bnum != p->bp->bnum)
59925cf1a30Sjl return (DDI_WALK_PRUNECHILD);
60025cf1a30Sjl return (DDI_WALK_CONTINUE);
60125cf1a30Sjl }
60225cf1a30Sjl /* we have already pruned all unwanted cores and cpu's above */
60325cf1a30Sjl if (strcmp(name, "core") == 0) {
60425cf1a30Sjl return (DDI_WALK_CONTINUE);
60525cf1a30Sjl }
60625cf1a30Sjl if (strcmp(name, "cpu") == 0) {
60725cf1a30Sjl processorid_t cpuid;
60825cf1a30Sjl len = sizeof (cpuid);
60925cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
61025cf1a30Sjl DDI_PROP_DONTPASS, "cpuid",
61125cf1a30Sjl (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
61225cf1a30Sjl return (DDI_WALK_TERMINATE);
61325cf1a30Sjl }
61425cf1a30Sjl bnum = LSB_ID(cpuid);
61525cf1a30Sjl ASSERT(bnum == p->bp->bnum);
61625cf1a30Sjl core_id = ON_BOARD_CORE_NUM(cpuid);
61725cf1a30Sjl strand_id = STRAND_ID(cpuid);
61825cf1a30Sjl p->bp->cores[core_id].core_present |= (1 << strand_id);
61925cf1a30Sjl return (DDI_WALK_CONTINUE);
62025cf1a30Sjl }
62125cf1a30Sjl
62225cf1a30Sjl return (DDI_WALK_PRUNECHILD);
62325cf1a30Sjl }
62425cf1a30Sjl
62525cf1a30Sjl
62625cf1a30Sjl static void
drmach_setup_core_info(drmach_board_t * obj)62725cf1a30Sjl drmach_setup_core_info(drmach_board_t *obj)
62825cf1a30Sjl {
62925cf1a30Sjl struct drmach_setup_core_arg arg;
63025cf1a30Sjl int i;
63125cf1a30Sjl
63225cf1a30Sjl for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
63325cf1a30Sjl obj->cores[i].core_present = 0;
63425cf1a30Sjl obj->cores[i].core_hotadded = 0;
63525cf1a30Sjl obj->cores[i].core_started = 0;
63625cf1a30Sjl }
63725cf1a30Sjl arg.bp = obj;
63825cf1a30Sjl ddi_walk_devs(ddi_root_node(), drmach_setup_core_cb, (void *)&arg);
63925cf1a30Sjl
64025cf1a30Sjl for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
64125cf1a30Sjl if (obj->boot_board) {
64225cf1a30Sjl obj->cores[i].core_hotadded =
643e98fafb9Sjl obj->cores[i].core_started =
644e98fafb9Sjl obj->cores[i].core_present;
64525cf1a30Sjl }
64625cf1a30Sjl }
64725cf1a30Sjl }
64825cf1a30Sjl
64925cf1a30Sjl /*
65025cf1a30Sjl * drmach_node_* routines serve the purpose of separating the
65125cf1a30Sjl * rest of the code from the device tree and OBP. This is necessary
65225cf1a30Sjl * because of In-Kernel-Probing. Devices probed after stod, are probed
65325cf1a30Sjl * by the in-kernel-prober, not OBP. These devices, therefore, do not
65425cf1a30Sjl * have dnode ids.
65525cf1a30Sjl */
65625cf1a30Sjl
65725cf1a30Sjl typedef struct {
65825cf1a30Sjl drmach_node_walk_args_t *nwargs;
659bbe1232eSToomas Soome int (*cb)(drmach_node_walk_args_t *args);
66025cf1a30Sjl int err;
66125cf1a30Sjl } drmach_node_ddi_walk_args_t;
66225cf1a30Sjl
66325cf1a30Sjl static int
drmach_node_ddi_walk_cb(dev_info_t * dip,void * arg)66425cf1a30Sjl drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
66525cf1a30Sjl {
66625cf1a30Sjl drmach_node_ddi_walk_args_t *nargs;
66725cf1a30Sjl
66825cf1a30Sjl nargs = (drmach_node_ddi_walk_args_t *)arg;
66925cf1a30Sjl
67025cf1a30Sjl /*
67125cf1a30Sjl * dip doesn't have to be held here as we are called
67225cf1a30Sjl * from ddi_walk_devs() which holds the dip.
67325cf1a30Sjl */
67425cf1a30Sjl nargs->nwargs->node->here = (void *)dip;
67525cf1a30Sjl
67625cf1a30Sjl nargs->err = nargs->cb(nargs->nwargs);
67725cf1a30Sjl
67825cf1a30Sjl
67925cf1a30Sjl /*
68025cf1a30Sjl * Set "here" to NULL so that unheld dip is not accessible
68125cf1a30Sjl * outside ddi_walk_devs()
68225cf1a30Sjl */
68325cf1a30Sjl nargs->nwargs->node->here = NULL;
68425cf1a30Sjl
68525cf1a30Sjl if (nargs->err)
68625cf1a30Sjl return (DDI_WALK_TERMINATE);
68725cf1a30Sjl else
68825cf1a30Sjl return (DDI_WALK_CONTINUE);
68925cf1a30Sjl }
69025cf1a30Sjl
69125cf1a30Sjl static int
drmach_node_ddi_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))69225cf1a30Sjl drmach_node_ddi_walk(drmach_node_t *np, void *data,
693bbe1232eSToomas Soome int (*cb)(drmach_node_walk_args_t *args))
69425cf1a30Sjl {
69525cf1a30Sjl drmach_node_walk_args_t args;
69625cf1a30Sjl drmach_node_ddi_walk_args_t nargs;
69725cf1a30Sjl
69825cf1a30Sjl
69925cf1a30Sjl /* initialized args structure for callback */
70025cf1a30Sjl args.node = np;
70125cf1a30Sjl args.data = data;
70225cf1a30Sjl
70325cf1a30Sjl nargs.nwargs = &args;
70425cf1a30Sjl nargs.cb = cb;
70525cf1a30Sjl nargs.err = 0;
70625cf1a30Sjl
70725cf1a30Sjl /*
70825cf1a30Sjl * Root node doesn't have to be held in any way.
70925cf1a30Sjl */
710e98fafb9Sjl ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
71125cf1a30Sjl
71225cf1a30Sjl return (nargs.err);
71325cf1a30Sjl }
71425cf1a30Sjl
71525cf1a30Sjl static int
drmach_node_ddi_get_parent(drmach_node_t * np,drmach_node_t * pp)71625cf1a30Sjl drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
71725cf1a30Sjl {
71825cf1a30Sjl dev_info_t *ndip;
71925cf1a30Sjl static char *fn = "drmach_node_ddi_get_parent";
72025cf1a30Sjl
72125cf1a30Sjl ndip = np->n_getdip(np);
72225cf1a30Sjl if (ndip == NULL) {
72325cf1a30Sjl cmn_err(CE_WARN, "%s: NULL dip", fn);
72425cf1a30Sjl return (-1);
72525cf1a30Sjl }
72625cf1a30Sjl
72725cf1a30Sjl bcopy(np, pp, sizeof (drmach_node_t));
72825cf1a30Sjl
72925cf1a30Sjl pp->here = (void *)ddi_get_parent(ndip);
73025cf1a30Sjl if (pp->here == NULL) {
73125cf1a30Sjl cmn_err(CE_WARN, "%s: NULL parent dip", fn);
73225cf1a30Sjl return (-1);
73325cf1a30Sjl }
73425cf1a30Sjl
73525cf1a30Sjl return (0);
73625cf1a30Sjl }
73725cf1a30Sjl
73825cf1a30Sjl /*ARGSUSED*/
73925cf1a30Sjl static pnode_t
drmach_node_ddi_get_dnode(drmach_node_t * np)74025cf1a30Sjl drmach_node_ddi_get_dnode(drmach_node_t *np)
74125cf1a30Sjl {
742bbe1232eSToomas Soome return (0);
74325cf1a30Sjl }
74425cf1a30Sjl
74525cf1a30Sjl static drmach_node_t *
drmach_node_new(void)74625cf1a30Sjl drmach_node_new(void)
74725cf1a30Sjl {
74825cf1a30Sjl drmach_node_t *np;
74925cf1a30Sjl
75025cf1a30Sjl np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
75125cf1a30Sjl
75225cf1a30Sjl np->get_dnode = drmach_node_ddi_get_dnode;
75325cf1a30Sjl np->walk = drmach_node_ddi_walk;
75425cf1a30Sjl np->n_getdip = drmach_node_ddi_get_dip;
75525cf1a30Sjl np->n_getproplen = drmach_node_ddi_get_proplen;
75625cf1a30Sjl np->n_getprop = drmach_node_ddi_get_prop;
75725cf1a30Sjl np->get_parent = drmach_node_ddi_get_parent;
75825cf1a30Sjl
75925cf1a30Sjl return (np);
76025cf1a30Sjl }
76125cf1a30Sjl
76225cf1a30Sjl static void
drmach_node_dispose(drmach_node_t * np)76325cf1a30Sjl drmach_node_dispose(drmach_node_t *np)
76425cf1a30Sjl {
76525cf1a30Sjl kmem_free(np, sizeof (*np));
76625cf1a30Sjl }
76725cf1a30Sjl
76825cf1a30Sjl static dev_info_t *
drmach_node_ddi_get_dip(drmach_node_t * np)76925cf1a30Sjl drmach_node_ddi_get_dip(drmach_node_t *np)
77025cf1a30Sjl {
77125cf1a30Sjl return ((dev_info_t *)np->here);
77225cf1a30Sjl }
77325cf1a30Sjl
77425cf1a30Sjl static int
drmach_node_walk(drmach_node_t * np,void * param,int (* cb)(drmach_node_walk_args_t * args))77525cf1a30Sjl drmach_node_walk(drmach_node_t *np, void *param,
776bbe1232eSToomas Soome int (*cb)(drmach_node_walk_args_t *args))
77725cf1a30Sjl {
77825cf1a30Sjl return (np->walk(np, param, cb));
77925cf1a30Sjl }
78025cf1a30Sjl
78125cf1a30Sjl static int
drmach_node_ddi_get_prop(drmach_node_t * np,char * name,void * buf,int len)78225cf1a30Sjl drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
78325cf1a30Sjl {
78425cf1a30Sjl int rv = 0;
78525cf1a30Sjl dev_info_t *ndip;
78625cf1a30Sjl static char *fn = "drmach_node_ddi_get_prop";
78725cf1a30Sjl
78825cf1a30Sjl
78925cf1a30Sjl ndip = np->n_getdip(np);
79025cf1a30Sjl if (ndip == NULL) {
79125cf1a30Sjl cmn_err(CE_WARN, "%s: NULL dip", fn);
79225cf1a30Sjl rv = -1;
79325cf1a30Sjl } else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
79425cf1a30Sjl DDI_PROP_DONTPASS, name,
79525cf1a30Sjl (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
79625cf1a30Sjl rv = -1;
79725cf1a30Sjl }
79825cf1a30Sjl
79925cf1a30Sjl return (rv);
80025cf1a30Sjl }
80125cf1a30Sjl
80225cf1a30Sjl static int
drmach_node_ddi_get_proplen(drmach_node_t * np,char * name,int * len)80325cf1a30Sjl drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
80425cf1a30Sjl {
80525cf1a30Sjl int rv = 0;
80625cf1a30Sjl dev_info_t *ndip;
80725cf1a30Sjl
80825cf1a30Sjl ndip = np->n_getdip(np);
80925cf1a30Sjl if (ndip == NULL) {
81025cf1a30Sjl rv = -1;
811e98fafb9Sjl } else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS, name,
812e98fafb9Sjl len) != DDI_PROP_SUCCESS) {
81325cf1a30Sjl rv = -1;
81425cf1a30Sjl }
81525cf1a30Sjl
81625cf1a30Sjl return (rv);
81725cf1a30Sjl }
81825cf1a30Sjl
81925cf1a30Sjl static drmachid_t
drmach_node_dup(drmach_node_t * np)82025cf1a30Sjl drmach_node_dup(drmach_node_t *np)
82125cf1a30Sjl {
82225cf1a30Sjl drmach_node_t *dup;
82325cf1a30Sjl
82425cf1a30Sjl dup = drmach_node_new();
82525cf1a30Sjl dup->here = np->here;
82625cf1a30Sjl dup->get_dnode = np->get_dnode;
82725cf1a30Sjl dup->walk = np->walk;
82825cf1a30Sjl dup->n_getdip = np->n_getdip;
82925cf1a30Sjl dup->n_getproplen = np->n_getproplen;
83025cf1a30Sjl dup->n_getprop = np->n_getprop;
83125cf1a30Sjl dup->get_parent = np->get_parent;
83225cf1a30Sjl
83325cf1a30Sjl return (dup);
83425cf1a30Sjl }
83525cf1a30Sjl
83625cf1a30Sjl /*
83725cf1a30Sjl * drmach_array provides convenient array construction, access,
83825cf1a30Sjl * bounds checking and array destruction logic.
83925cf1a30Sjl */
84025cf1a30Sjl
84125cf1a30Sjl static drmach_array_t *
drmach_array_new(int min_index,int max_index)84225cf1a30Sjl drmach_array_new(int min_index, int max_index)
84325cf1a30Sjl {
84425cf1a30Sjl drmach_array_t *arr;
84525cf1a30Sjl
84625cf1a30Sjl arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
84725cf1a30Sjl
84825cf1a30Sjl arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
84925cf1a30Sjl if (arr->arr_sz > 0) {
85025cf1a30Sjl arr->min_index = min_index;
85125cf1a30Sjl arr->max_index = max_index;
85225cf1a30Sjl
85325cf1a30Sjl arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
85425cf1a30Sjl return (arr);
85525cf1a30Sjl } else {
85625cf1a30Sjl kmem_free(arr, sizeof (*arr));
85725cf1a30Sjl return (0);
85825cf1a30Sjl }
85925cf1a30Sjl }
86025cf1a30Sjl
86125cf1a30Sjl static int
drmach_array_set(drmach_array_t * arr,int idx,drmachid_t val)86225cf1a30Sjl drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
86325cf1a30Sjl {
86425cf1a30Sjl if (idx < arr->min_index || idx > arr->max_index)
86525cf1a30Sjl return (-1);
86625cf1a30Sjl else {
86725cf1a30Sjl arr->arr[idx - arr->min_index] = val;
86825cf1a30Sjl return (0);
86925cf1a30Sjl }
87025cf1a30Sjl /*NOTREACHED*/
87125cf1a30Sjl }
87225cf1a30Sjl
87325cf1a30Sjl static int
drmach_array_get(drmach_array_t * arr,int idx,drmachid_t * val)87425cf1a30Sjl drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
87525cf1a30Sjl {
87625cf1a30Sjl if (idx < arr->min_index || idx > arr->max_index)
87725cf1a30Sjl return (-1);
87825cf1a30Sjl else {
87925cf1a30Sjl *val = arr->arr[idx - arr->min_index];
88025cf1a30Sjl return (0);
88125cf1a30Sjl }
88225cf1a30Sjl /*NOTREACHED*/
88325cf1a30Sjl }
88425cf1a30Sjl
88525cf1a30Sjl static int
drmach_array_first(drmach_array_t * arr,int * idx,drmachid_t * val)88625cf1a30Sjl drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
88725cf1a30Sjl {
88825cf1a30Sjl int rv;
88925cf1a30Sjl
89025cf1a30Sjl *idx = arr->min_index;
89125cf1a30Sjl while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
89225cf1a30Sjl *idx += 1;
89325cf1a30Sjl
89425cf1a30Sjl return (rv);
89525cf1a30Sjl }
89625cf1a30Sjl
89725cf1a30Sjl static int
drmach_array_next(drmach_array_t * arr,int * idx,drmachid_t * val)89825cf1a30Sjl drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
89925cf1a30Sjl {
90025cf1a30Sjl int rv;
90125cf1a30Sjl
90225cf1a30Sjl *idx += 1;
90325cf1a30Sjl while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
90425cf1a30Sjl *idx += 1;
90525cf1a30Sjl
90625cf1a30Sjl return (rv);
90725cf1a30Sjl }
90825cf1a30Sjl
90925cf1a30Sjl static void
drmach_array_dispose(drmach_array_t * arr,void (* disposer)(drmachid_t))91025cf1a30Sjl drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
91125cf1a30Sjl {
91225cf1a30Sjl drmachid_t val;
91325cf1a30Sjl int idx;
91425cf1a30Sjl int rv;
91525cf1a30Sjl
91625cf1a30Sjl rv = drmach_array_first(arr, &idx, &val);
91725cf1a30Sjl while (rv == 0) {
91825cf1a30Sjl (*disposer)(val);
91925cf1a30Sjl rv = drmach_array_next(arr, &idx, &val);
92025cf1a30Sjl }
92125cf1a30Sjl
92225cf1a30Sjl kmem_free(arr->arr, arr->arr_sz);
92325cf1a30Sjl kmem_free(arr, sizeof (*arr));
92425cf1a30Sjl }
92525cf1a30Sjl
92625cf1a30Sjl static drmach_board_t *
drmach_get_board_by_bnum(int bnum)92725cf1a30Sjl drmach_get_board_by_bnum(int bnum)
92825cf1a30Sjl {
92925cf1a30Sjl drmachid_t id;
93025cf1a30Sjl
93125cf1a30Sjl if (drmach_array_get(drmach_boards, bnum, &id) == 0)
93225cf1a30Sjl return ((drmach_board_t *)id);
93325cf1a30Sjl else
93425cf1a30Sjl return (NULL);
93525cf1a30Sjl }
93625cf1a30Sjl
93725cf1a30Sjl static pnode_t
drmach_node_get_dnode(drmach_node_t * np)93825cf1a30Sjl drmach_node_get_dnode(drmach_node_t *np)
93925cf1a30Sjl {
94025cf1a30Sjl return (np->get_dnode(np));
94125cf1a30Sjl }
94225cf1a30Sjl
94325cf1a30Sjl /*ARGSUSED*/
94425cf1a30Sjl sbd_error_t *
drmach_configure(drmachid_t id,int flags)94525cf1a30Sjl drmach_configure(drmachid_t id, int flags)
94625cf1a30Sjl {
94725cf1a30Sjl drmach_device_t *dp;
94825cf1a30Sjl sbd_error_t *err = NULL;
94925cf1a30Sjl dev_info_t *rdip;
95025cf1a30Sjl dev_info_t *fdip = NULL;
95125cf1a30Sjl
95225cf1a30Sjl if (DRMACH_IS_CPU_ID(id)) {
95325cf1a30Sjl return (NULL);
95425cf1a30Sjl }
95525cf1a30Sjl if (!DRMACH_IS_DEVICE_ID(id))
95625cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
95725cf1a30Sjl dp = id;
95825cf1a30Sjl rdip = dp->node->n_getdip(dp->node);
95925cf1a30Sjl
96025cf1a30Sjl ASSERT(rdip);
96125cf1a30Sjl
96225cf1a30Sjl ASSERT(e_ddi_branch_held(rdip));
96325cf1a30Sjl
96425cf1a30Sjl if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
96525cf1a30Sjl char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
96625cf1a30Sjl dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
96725cf1a30Sjl
96825cf1a30Sjl (void) ddi_pathname(dip, path);
96925cf1a30Sjl err = drerr_new(1, EOPL_DRVFAIL, path);
97025cf1a30Sjl
97125cf1a30Sjl kmem_free(path, MAXPATHLEN);
97225cf1a30Sjl
97325cf1a30Sjl /* If non-NULL, fdip is returned held and must be released */
97425cf1a30Sjl if (fdip != NULL)
97525cf1a30Sjl ddi_release_devi(fdip);
97625cf1a30Sjl }
97725cf1a30Sjl
97825cf1a30Sjl return (err);
97925cf1a30Sjl }
98025cf1a30Sjl
98125cf1a30Sjl
98225cf1a30Sjl static sbd_error_t *
drmach_device_new(drmach_node_t * node,drmach_board_t * bp,int portid,drmachid_t * idp)98325cf1a30Sjl drmach_device_new(drmach_node_t *node,
984bbe1232eSToomas Soome drmach_board_t *bp, int portid, drmachid_t *idp)
98525cf1a30Sjl {
98625cf1a30Sjl int i;
98725cf1a30Sjl int rv;
98825cf1a30Sjl drmach_device_t proto;
98925cf1a30Sjl sbd_error_t *err;
99025cf1a30Sjl char name[OBP_MAXDRVNAME];
99125cf1a30Sjl
99225cf1a30Sjl rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
99325cf1a30Sjl if (rv) {
99425cf1a30Sjl /* every node is expected to have a name */
995e98fafb9Sjl err = drerr_new(1, EOPL_GETPROP, "device node %s: property %s",
996e98fafb9Sjl ddi_node_name(node->n_getdip(node)), "name");
99725cf1a30Sjl return (err);
99825cf1a30Sjl }
99925cf1a30Sjl
100025cf1a30Sjl /*
100125cf1a30Sjl * The node currently being examined is not listed in the name2type[]
100225cf1a30Sjl * array. In this case, the node is no interest to drmach. Both
100325cf1a30Sjl * dp and err are initialized here to yield nothing (no device or
100425cf1a30Sjl * error structure) for this case.
100525cf1a30Sjl */
100625cf1a30Sjl i = drmach_name2type_idx(name);
100725cf1a30Sjl
100825cf1a30Sjl
100925cf1a30Sjl if (i < 0) {
101025cf1a30Sjl *idp = (drmachid_t)0;
101125cf1a30Sjl return (NULL);
101225cf1a30Sjl }
101325cf1a30Sjl
101425cf1a30Sjl /* device specific new function will set unum */
101525cf1a30Sjl
101625cf1a30Sjl bzero(&proto, sizeof (proto));
101725cf1a30Sjl proto.type = drmach_name2type[i].type;
101825cf1a30Sjl proto.bp = bp;
101925cf1a30Sjl proto.node = node;
102025cf1a30Sjl proto.portid = portid;
102125cf1a30Sjl
102225cf1a30Sjl return (drmach_name2type[i].new(&proto, idp));
102325cf1a30Sjl }
102425cf1a30Sjl
102525cf1a30Sjl static void
drmach_device_dispose(drmachid_t id)102625cf1a30Sjl drmach_device_dispose(drmachid_t id)
102725cf1a30Sjl {
102825cf1a30Sjl drmach_device_t *self = id;
102925cf1a30Sjl
103025cf1a30Sjl self->cm.dispose(id);
103125cf1a30Sjl }
103225cf1a30Sjl
103325cf1a30Sjl
103425cf1a30Sjl static drmach_board_t *
drmach_board_new(int bnum,int boot_board)103525cf1a30Sjl drmach_board_new(int bnum, int boot_board)
103625cf1a30Sjl {
103725cf1a30Sjl drmach_board_t *bp;
103825cf1a30Sjl
103925cf1a30Sjl bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
104025cf1a30Sjl
104125cf1a30Sjl bp->cm.isa = (void *)drmach_board_new;
104225cf1a30Sjl bp->cm.release = drmach_board_release;
104325cf1a30Sjl bp->cm.status = drmach_board_status;
104425cf1a30Sjl
104525cf1a30Sjl (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
104625cf1a30Sjl
104725cf1a30Sjl bp->bnum = bnum;
104825cf1a30Sjl bp->devices = NULL;
104925cf1a30Sjl bp->connected = boot_board;
105025cf1a30Sjl bp->tree = drmach_node_new();
105125cf1a30Sjl bp->assigned = boot_board;
105225cf1a30Sjl bp->powered = boot_board;
105325cf1a30Sjl bp->boot_board = boot_board;
105425cf1a30Sjl
105525cf1a30Sjl /*
105625cf1a30Sjl * If this is not bootup initialization, we have to wait till
105725cf1a30Sjl * IKP sets up the device nodes in drmach_board_connect().
105825cf1a30Sjl */
105925cf1a30Sjl if (boot_board)
106025cf1a30Sjl drmach_setup_core_info(bp);
106125cf1a30Sjl
106207d06da5SSurya Prakki (void) drmach_array_set(drmach_boards, bnum, bp);
106325cf1a30Sjl return (bp);
106425cf1a30Sjl }
106525cf1a30Sjl
106625cf1a30Sjl static void
drmach_board_dispose(drmachid_t id)106725cf1a30Sjl drmach_board_dispose(drmachid_t id)
106825cf1a30Sjl {
106925cf1a30Sjl drmach_board_t *bp;
107025cf1a30Sjl
107125cf1a30Sjl ASSERT(DRMACH_IS_BOARD_ID(id));
107225cf1a30Sjl bp = id;
107325cf1a30Sjl
107425cf1a30Sjl if (bp->tree)
107525cf1a30Sjl drmach_node_dispose(bp->tree);
107625cf1a30Sjl
107725cf1a30Sjl if (bp->devices)
107825cf1a30Sjl drmach_array_dispose(bp->devices, drmach_device_dispose);
107925cf1a30Sjl
108025cf1a30Sjl kmem_free(bp, sizeof (*bp));
108125cf1a30Sjl }
108225cf1a30Sjl
108325cf1a30Sjl static sbd_error_t *
drmach_board_status(drmachid_t id,drmach_status_t * stat)108425cf1a30Sjl drmach_board_status(drmachid_t id, drmach_status_t *stat)
108525cf1a30Sjl {
108625cf1a30Sjl sbd_error_t *err = NULL;
108725cf1a30Sjl drmach_board_t *bp;
108825cf1a30Sjl
108925cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
109025cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
109125cf1a30Sjl bp = id;
109225cf1a30Sjl
109325cf1a30Sjl stat->assigned = bp->assigned;
109425cf1a30Sjl stat->powered = bp->powered;
109525cf1a30Sjl stat->busy = 0; /* assume not busy */
109625cf1a30Sjl stat->configured = 0; /* assume not configured */
109725cf1a30Sjl stat->empty = 0;
109825cf1a30Sjl stat->cond = bp->cond = SBD_COND_OK;
109907d06da5SSurya Prakki (void) strncpy(stat->type, "System Brd", sizeof (stat->type));
110025cf1a30Sjl stat->info[0] = '\0';
110125cf1a30Sjl
110225cf1a30Sjl if (bp->devices) {
110325cf1a30Sjl int rv;
110425cf1a30Sjl int d_idx;
110525cf1a30Sjl drmachid_t d_id;
110625cf1a30Sjl
110725cf1a30Sjl rv = drmach_array_first(bp->devices, &d_idx, &d_id);
110825cf1a30Sjl while (rv == 0) {
110925cf1a30Sjl drmach_status_t d_stat;
111025cf1a30Sjl
111125cf1a30Sjl err = drmach_i_status(d_id, &d_stat);
111225cf1a30Sjl if (err)
111325cf1a30Sjl break;
111425cf1a30Sjl
111525cf1a30Sjl stat->busy |= d_stat.busy;
111625cf1a30Sjl stat->configured |= d_stat.configured;
111725cf1a30Sjl
111825cf1a30Sjl rv = drmach_array_next(bp->devices, &d_idx, &d_id);
111925cf1a30Sjl }
112025cf1a30Sjl }
112125cf1a30Sjl
112225cf1a30Sjl return (err);
112325cf1a30Sjl }
112425cf1a30Sjl
112525cf1a30Sjl int
drmach_board_is_floating(drmachid_t id)112625cf1a30Sjl drmach_board_is_floating(drmachid_t id)
112725cf1a30Sjl {
112825cf1a30Sjl drmach_board_t *bp;
112925cf1a30Sjl
113025cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
113125cf1a30Sjl return (0);
113225cf1a30Sjl
113325cf1a30Sjl bp = (drmach_board_t *)id;
113425cf1a30Sjl
113525cf1a30Sjl return ((drmach_domain.floating & (1 << bp->bnum)) ? 1 : 0);
113625cf1a30Sjl }
113725cf1a30Sjl
113825cf1a30Sjl static int
drmach_init(void)113925cf1a30Sjl drmach_init(void)
114025cf1a30Sjl {
114125cf1a30Sjl dev_info_t *rdip;
114225cf1a30Sjl int i, rv, len;
114325cf1a30Sjl int *floating;
114425cf1a30Sjl
114525cf1a30Sjl rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
114625cf1a30Sjl
114725cf1a30Sjl drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
114825cf1a30Sjl
114925cf1a30Sjl rdip = ddi_root_node();
115025cf1a30Sjl
115125cf1a30Sjl if (ddi_getproplen(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1152e98fafb9Sjl "floating-boards", &len) != DDI_PROP_SUCCESS) {
115325cf1a30Sjl cmn_err(CE_WARN, "Cannot get floating-boards proplen\n");
115425cf1a30Sjl } else {
115525cf1a30Sjl floating = (int *)kmem_alloc(len, KM_SLEEP);
1156e98fafb9Sjl rv = ddi_prop_op(DDI_DEV_T_ANY, rdip, PROP_LEN_AND_VAL_BUF,
1157e98fafb9Sjl DDI_PROP_DONTPASS, "floating-boards", (caddr_t)floating,
1158e98fafb9Sjl &len);
115925cf1a30Sjl if (rv != DDI_PROP_SUCCESS) {
116025cf1a30Sjl cmn_err(CE_WARN, "Cannot get floating-boards prop\n");
116125cf1a30Sjl } else {
116225cf1a30Sjl drmach_domain.floating = 0;
116325cf1a30Sjl for (i = 0; i < len / sizeof (int); i++) {
116425cf1a30Sjl drmach_domain.floating |= (1 << floating[i]);
116525cf1a30Sjl }
116625cf1a30Sjl }
116725cf1a30Sjl kmem_free(floating, len);
116825cf1a30Sjl }
116925cf1a30Sjl drmach_domain.allow_dr = opl_check_dr_status();
117025cf1a30Sjl
117125cf1a30Sjl rdip = ddi_get_child(ddi_root_node());
117225cf1a30Sjl do {
117325cf1a30Sjl int bnum;
117425cf1a30Sjl drmachid_t id;
117525cf1a30Sjl
117625cf1a30Sjl bnum = -1;
1177e98fafb9Sjl bnum = ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1178e98fafb9Sjl OBP_BOARDNUM, -1);
117925cf1a30Sjl if (bnum == -1)
118025cf1a30Sjl continue;
118125cf1a30Sjl
118225cf1a30Sjl if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
1183e98fafb9Sjl cmn_err(CE_WARN, "Device node 0x%p has invalid "
118407d06da5SSurya Prakki "property value, %s=%d", (void *)rdip,
118507d06da5SSurya Prakki OBP_BOARDNUM, bnum);
118625cf1a30Sjl goto error;
118725cf1a30Sjl } else if (id == NULL) {
118825cf1a30Sjl (void) drmach_board_new(bnum, 1);
118925cf1a30Sjl }
119025cf1a30Sjl } while ((rdip = ddi_get_next_sibling(rdip)) != NULL);
119125cf1a30Sjl
119225cf1a30Sjl opl_hold_devtree();
119325cf1a30Sjl
119425cf1a30Sjl /*
119525cf1a30Sjl * Initialize the IKP feature.
119625cf1a30Sjl *
119725cf1a30Sjl * This can be done only after DR has acquired a hold on all the
119825cf1a30Sjl * device nodes that are interesting to IKP.
119925cf1a30Sjl */
120025cf1a30Sjl if (opl_init_cfg() != 0) {
120125cf1a30Sjl cmn_err(CE_WARN, "DR - IKP initialization failed");
120225cf1a30Sjl
120325cf1a30Sjl opl_release_devtree();
120425cf1a30Sjl
120525cf1a30Sjl goto error;
120625cf1a30Sjl }
120725cf1a30Sjl
120825cf1a30Sjl return (0);
120925cf1a30Sjl error:
121025cf1a30Sjl drmach_array_dispose(drmach_boards, drmach_board_dispose);
121125cf1a30Sjl rw_destroy(&drmach_boards_rwlock);
121225cf1a30Sjl return (ENXIO);
121325cf1a30Sjl }
121425cf1a30Sjl
121525cf1a30Sjl static void
drmach_fini(void)121625cf1a30Sjl drmach_fini(void)
121725cf1a30Sjl {
121825cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_WRITER);
121925cf1a30Sjl drmach_array_dispose(drmach_boards, drmach_board_dispose);
122025cf1a30Sjl drmach_boards = NULL;
122125cf1a30Sjl rw_exit(&drmach_boards_rwlock);
122225cf1a30Sjl
122325cf1a30Sjl /*
122425cf1a30Sjl * Walk immediate children of the root devinfo node
122525cf1a30Sjl * releasing holds acquired on branches in drmach_init()
122625cf1a30Sjl */
122725cf1a30Sjl
122825cf1a30Sjl opl_release_devtree();
122925cf1a30Sjl
123025cf1a30Sjl rw_destroy(&drmach_boards_rwlock);
123125cf1a30Sjl }
123225cf1a30Sjl
123325cf1a30Sjl /*
123425cf1a30Sjl * Each system board contains 2 Oberon PCI bridge and
123525cf1a30Sjl * 1 CMUCH.
123625cf1a30Sjl * Each oberon has 2 channels.
123725cf1a30Sjl * Each channel has 2 pci-ex leaf.
123825cf1a30Sjl * Each CMUCH has 1 pci bus.
123925cf1a30Sjl *
124025cf1a30Sjl *
124125cf1a30Sjl * Device Path:
124225cf1a30Sjl * /pci@<portid>,reg
124325cf1a30Sjl *
124425cf1a30Sjl * where
124525cf1a30Sjl * portid[10] = 0
124625cf1a30Sjl * portid[9:0] = LLEAF_ID[9:0] of the Oberon Channel
124725cf1a30Sjl *
124825cf1a30Sjl * LLEAF_ID[9:8] = 0
124925cf1a30Sjl * LLEAF_ID[8:4] = LSB_ID[4:0]
125025cf1a30Sjl * LLEAF_ID[3:1] = IO Channel#[2:0] (0,1,2,3 for Oberon)
125125cf1a30Sjl * channel 4 is pcicmu
125225cf1a30Sjl * LLEAF_ID[0] = PCI Leaf Number (0 for leaf-A, 1 for leaf-B)
125325cf1a30Sjl *
125425cf1a30Sjl * Properties:
125525cf1a30Sjl * name = pci
125625cf1a30Sjl * device_type = "pciex"
125725cf1a30Sjl * board# = LSBID
125825cf1a30Sjl * reg = int32 * 2, Oberon CSR space of the leaf and the UBC space
125925cf1a30Sjl * portid = Jupiter Bus Device ID ((LSB_ID << 3)|pciport#)
126025cf1a30Sjl */
126125cf1a30Sjl
126225cf1a30Sjl static sbd_error_t *
drmach_io_new(drmach_device_t * proto,drmachid_t * idp)126325cf1a30Sjl drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
126425cf1a30Sjl {
126525cf1a30Sjl drmach_io_t *ip;
126625cf1a30Sjl
126725cf1a30Sjl int portid;
126825cf1a30Sjl
126925cf1a30Sjl portid = proto->portid;
127025cf1a30Sjl ASSERT(portid != -1);
127125cf1a30Sjl proto->unum = portid & (MAX_IO_UNITS_PER_BOARD - 1);
127225cf1a30Sjl
127325cf1a30Sjl ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
127425cf1a30Sjl bcopy(proto, &ip->dev, sizeof (ip->dev));
127525cf1a30Sjl ip->dev.node = drmach_node_dup(proto->node);
127625cf1a30Sjl ip->dev.cm.isa = (void *)drmach_io_new;
127725cf1a30Sjl ip->dev.cm.dispose = drmach_io_dispose;
127825cf1a30Sjl ip->dev.cm.release = drmach_io_release;
127925cf1a30Sjl ip->dev.cm.status = drmach_io_status;
128025cf1a30Sjl ip->channel = (portid >> 1) & 0x7;
128125cf1a30Sjl ip->leaf = (portid & 0x1);
128225cf1a30Sjl
128307d06da5SSurya Prakki (void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
1284e98fafb9Sjl ip->dev.type, ip->dev.unum);
128525cf1a30Sjl
128625cf1a30Sjl *idp = (drmachid_t)ip;
128725cf1a30Sjl return (NULL);
128825cf1a30Sjl }
128925cf1a30Sjl
129025cf1a30Sjl
129125cf1a30Sjl static void
drmach_io_dispose(drmachid_t id)129225cf1a30Sjl drmach_io_dispose(drmachid_t id)
129325cf1a30Sjl {
129425cf1a30Sjl drmach_io_t *self;
129525cf1a30Sjl
129625cf1a30Sjl ASSERT(DRMACH_IS_IO_ID(id));
129725cf1a30Sjl
129825cf1a30Sjl self = id;
129925cf1a30Sjl if (self->dev.node)
130025cf1a30Sjl drmach_node_dispose(self->dev.node);
130125cf1a30Sjl
130225cf1a30Sjl kmem_free(self, sizeof (*self));
130325cf1a30Sjl }
130425cf1a30Sjl
130525cf1a30Sjl /*ARGSUSED*/
130625cf1a30Sjl sbd_error_t *
drmach_pre_op(int cmd,drmachid_t id,drmach_opts_t * opts)130725cf1a30Sjl drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
130825cf1a30Sjl {
130925cf1a30Sjl drmach_board_t *bp = (drmach_board_t *)id;
131025cf1a30Sjl sbd_error_t *err = NULL;
131125cf1a30Sjl
131225cf1a30Sjl /* allow status and ncm operations to always succeed */
131325cf1a30Sjl if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
131425cf1a30Sjl return (NULL);
131525cf1a30Sjl }
131625cf1a30Sjl
131725cf1a30Sjl /* check all other commands for the required option string */
131825cf1a30Sjl
131925cf1a30Sjl if ((opts->size > 0) && (opts->copts != NULL)) {
132025cf1a30Sjl
132125cf1a30Sjl DRMACH_PR("platform options: %s\n", opts->copts);
132225cf1a30Sjl
132325cf1a30Sjl if (strstr(opts->copts, "opldr") == NULL) {
132425cf1a30Sjl err = drerr_new(1, EOPL_SUPPORT, NULL);
132525cf1a30Sjl }
132625cf1a30Sjl } else {
132725cf1a30Sjl err = drerr_new(1, EOPL_SUPPORT, NULL);
132825cf1a30Sjl }
132925cf1a30Sjl
133025cf1a30Sjl if (!err && id && DRMACH_IS_BOARD_ID(id)) {
133125cf1a30Sjl switch (cmd) {
133225cf1a30Sjl case SBD_CMD_TEST:
133325cf1a30Sjl case SBD_CMD_STATUS:
133425cf1a30Sjl case SBD_CMD_GETNCM:
133525cf1a30Sjl break;
133625cf1a30Sjl case SBD_CMD_CONNECT:
133725cf1a30Sjl if (bp->connected)
133825cf1a30Sjl err = drerr_new(0, ESBD_STATE, NULL);
133925cf1a30Sjl else if (!drmach_domain.allow_dr)
1340e98fafb9Sjl err = drerr_new(1, EOPL_SUPPORT, NULL);
134125cf1a30Sjl break;
134225cf1a30Sjl case SBD_CMD_DISCONNECT:
134325cf1a30Sjl if (!bp->connected)
134425cf1a30Sjl err = drerr_new(0, ESBD_STATE, NULL);
134525cf1a30Sjl else if (!drmach_domain.allow_dr)
1346e98fafb9Sjl err = drerr_new(1, EOPL_SUPPORT, NULL);
134725cf1a30Sjl break;
134825cf1a30Sjl default:
134925cf1a30Sjl if (!drmach_domain.allow_dr)
1350e98fafb9Sjl err = drerr_new(1, EOPL_SUPPORT, NULL);
135125cf1a30Sjl break;
135225cf1a30Sjl
135325cf1a30Sjl }
135425cf1a30Sjl }
135525cf1a30Sjl
135625cf1a30Sjl return (err);
135725cf1a30Sjl }
135825cf1a30Sjl
135925cf1a30Sjl /*ARGSUSED*/
136025cf1a30Sjl sbd_error_t *
drmach_post_op(int cmd,drmachid_t id,drmach_opts_t * opts)136125cf1a30Sjl drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
136225cf1a30Sjl {
136325cf1a30Sjl return (NULL);
136425cf1a30Sjl }
136525cf1a30Sjl
136625cf1a30Sjl sbd_error_t *
drmach_board_assign(int bnum,drmachid_t * id)136725cf1a30Sjl drmach_board_assign(int bnum, drmachid_t *id)
136825cf1a30Sjl {
136925cf1a30Sjl sbd_error_t *err = NULL;
137025cf1a30Sjl
137125cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_WRITER);
137225cf1a30Sjl
137325cf1a30Sjl if (drmach_array_get(drmach_boards, bnum, id) == -1) {
137425cf1a30Sjl err = drerr_new(1, EOPL_BNUM, "%d", bnum);
137525cf1a30Sjl } else {
137625cf1a30Sjl drmach_board_t *bp;
137725cf1a30Sjl
137825cf1a30Sjl if (*id)
137925cf1a30Sjl rw_downgrade(&drmach_boards_rwlock);
138025cf1a30Sjl
138125cf1a30Sjl bp = *id;
138225cf1a30Sjl if (!(*id))
138325cf1a30Sjl bp = *id =
1384e98fafb9Sjl (drmachid_t)drmach_board_new(bnum, 0);
138525cf1a30Sjl bp->assigned = 1;
138625cf1a30Sjl }
138725cf1a30Sjl
138825cf1a30Sjl rw_exit(&drmach_boards_rwlock);
138925cf1a30Sjl
139025cf1a30Sjl return (err);
139125cf1a30Sjl }
139225cf1a30Sjl
139325cf1a30Sjl /*ARGSUSED*/
139425cf1a30Sjl sbd_error_t *
drmach_board_connect(drmachid_t id,drmach_opts_t * opts)139525cf1a30Sjl drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
139625cf1a30Sjl {
1397e98fafb9Sjl extern int cpu_alljupiter;
139825cf1a30Sjl drmach_board_t *obj = (drmach_board_t *)id;
1399e98fafb9Sjl unsigned cpu_impl;
140025cf1a30Sjl
140125cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
140225cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
140325cf1a30Sjl
1404e98fafb9Sjl if (opl_probe_sb(obj->bnum, &cpu_impl) != 0)
14058eafe49bSbm return (drerr_new(1, EOPL_PROBE, NULL));
140625cf1a30Sjl
1407e98fafb9Sjl if (cpu_alljupiter) {
14088a040953Swh if (cpu_impl & (1 << OLYMPUS_C_IMPL)) {
14098a040953Swh (void) opl_unprobe_sb(obj->bnum);
1410e98fafb9Sjl return (drerr_new(1, EOPL_MIXED_CPU, NULL));
14118a040953Swh }
1412e98fafb9Sjl }
1413e98fafb9Sjl
141425cf1a30Sjl (void) prom_attach_notice(obj->bnum);
141525cf1a30Sjl
141625cf1a30Sjl drmach_setup_core_info(obj);
141725cf1a30Sjl
141825cf1a30Sjl obj->connected = 1;
141925cf1a30Sjl
142025cf1a30Sjl return (NULL);
142125cf1a30Sjl }
142225cf1a30Sjl
142325cf1a30Sjl static int drmach_cache_flush_flag[NCPU];
142425cf1a30Sjl
142525cf1a30Sjl /*ARGSUSED*/
142625cf1a30Sjl static void
drmach_flush_cache(uint64_t id,uint64_t dummy)142725cf1a30Sjl drmach_flush_cache(uint64_t id, uint64_t dummy)
142825cf1a30Sjl {
142925cf1a30Sjl extern void cpu_flush_ecache(void);
143025cf1a30Sjl
143125cf1a30Sjl cpu_flush_ecache();
143225cf1a30Sjl drmach_cache_flush_flag[id] = 0;
143325cf1a30Sjl }
143425cf1a30Sjl
143525cf1a30Sjl static void
drmach_flush_all()143625cf1a30Sjl drmach_flush_all()
143725cf1a30Sjl {
143825cf1a30Sjl cpuset_t xc_cpuset;
143925cf1a30Sjl int i;
144025cf1a30Sjl
144125cf1a30Sjl xc_cpuset = cpu_ready_set;
144225cf1a30Sjl for (i = 0; i < NCPU; i++) {
144325cf1a30Sjl if (CPU_IN_SET(xc_cpuset, i)) {
144425cf1a30Sjl drmach_cache_flush_flag[i] = 1;
144525cf1a30Sjl xc_one(i, drmach_flush_cache, i, 0);
144625cf1a30Sjl while (drmach_cache_flush_flag[i]) {
144725cf1a30Sjl DELAY(1000);
144825cf1a30Sjl }
144925cf1a30Sjl }
145025cf1a30Sjl }
145125cf1a30Sjl }
145225cf1a30Sjl
145325cf1a30Sjl static int
drmach_disconnect_cpus(drmach_board_t * bp)145425cf1a30Sjl drmach_disconnect_cpus(drmach_board_t *bp)
145525cf1a30Sjl {
145625cf1a30Sjl int i, bnum;
145725cf1a30Sjl
145825cf1a30Sjl bnum = bp->bnum;
145925cf1a30Sjl
146025cf1a30Sjl for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
1461e98fafb9Sjl if (bp->cores[i].core_present) {
1462e98fafb9Sjl if (bp->cores[i].core_started)
1463e98fafb9Sjl return (-1);
1464e98fafb9Sjl if (bp->cores[i].core_hotadded) {
1465e98fafb9Sjl if (drmach_add_remove_cpu(bnum, i,
1466e98fafb9Sjl HOTREMOVE_CPU)) {
1467e98fafb9Sjl cmn_err(CE_WARN, "Failed to remove "
1468e98fafb9Sjl "CMP %d on board %d\n", i, bnum);
1469e98fafb9Sjl return (-1);
1470e98fafb9Sjl }
1471e98fafb9Sjl }
147225cf1a30Sjl }
147325cf1a30Sjl }
147425cf1a30Sjl return (0);
147525cf1a30Sjl }
147625cf1a30Sjl
147725cf1a30Sjl /*ARGSUSED*/
147825cf1a30Sjl sbd_error_t *
drmach_board_disconnect(drmachid_t id,drmach_opts_t * opts)147925cf1a30Sjl drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
148025cf1a30Sjl {
148125cf1a30Sjl drmach_board_t *obj;
148225cf1a30Sjl int rv = 0;
148325cf1a30Sjl sbd_error_t *err = NULL;
148425cf1a30Sjl
1485ddf95635Sbm if (DRMACH_NULL_ID(id))
1486ddf95635Sbm return (NULL);
148725cf1a30Sjl
148825cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
148925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
149025cf1a30Sjl
149125cf1a30Sjl obj = (drmach_board_t *)id;
149225cf1a30Sjl
149325cf1a30Sjl if (drmach_disconnect_cpus(obj)) {
14948eafe49bSbm err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
149525cf1a30Sjl return (err);
149625cf1a30Sjl }
149725cf1a30Sjl
149825cf1a30Sjl rv = opl_unprobe_sb(obj->bnum);
149925cf1a30Sjl
150025cf1a30Sjl if (rv == 0) {
150107d06da5SSurya Prakki (void) prom_detach_notice(obj->bnum);
150225cf1a30Sjl obj->connected = 0;
150325cf1a30Sjl
150425cf1a30Sjl } else
15058eafe49bSbm err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
150625cf1a30Sjl
150725cf1a30Sjl return (err);
150825cf1a30Sjl }
150925cf1a30Sjl
151025cf1a30Sjl static int
drmach_get_portid(drmach_node_t * np)151125cf1a30Sjl drmach_get_portid(drmach_node_t *np)
151225cf1a30Sjl {
151325cf1a30Sjl int portid;
151425cf1a30Sjl char type[OBP_MAXPROPNAME];
151525cf1a30Sjl
151625cf1a30Sjl if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
151725cf1a30Sjl return (portid);
151825cf1a30Sjl
151925cf1a30Sjl /*
152025cf1a30Sjl * Get the device_type property to see if we should
152125cf1a30Sjl * continue processing this node.
152225cf1a30Sjl */
152325cf1a30Sjl if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
152425cf1a30Sjl return (-1);
152525cf1a30Sjl
152625cf1a30Sjl if (strcmp(type, OPL_CPU_NODE) == 0) {
152725cf1a30Sjl /*
152825cf1a30Sjl * We return cpuid because it has no portid
152925cf1a30Sjl */
153025cf1a30Sjl if (np->n_getprop(np, "cpuid", &portid, sizeof (portid)) == 0)
153125cf1a30Sjl return (portid);
153225cf1a30Sjl }
153325cf1a30Sjl
153425cf1a30Sjl return (-1);
153525cf1a30Sjl }
153625cf1a30Sjl
153725cf1a30Sjl /*
153825cf1a30Sjl * This is a helper function to determine if a given
153925cf1a30Sjl * node should be considered for a dr operation according
154025cf1a30Sjl * to predefined dr type nodes and the node's name.
154125cf1a30Sjl * Formal Parameter : The name of a device node.
154225cf1a30Sjl * Return Value: -1, name does not map to a valid dr type.
154325cf1a30Sjl * A value greater or equal to 0, name is a valid dr type.
154425cf1a30Sjl */
154525cf1a30Sjl static int
drmach_name2type_idx(char * name)154625cf1a30Sjl drmach_name2type_idx(char *name)
154725cf1a30Sjl {
1548bbe1232eSToomas Soome int index, ntypes;
154925cf1a30Sjl
155025cf1a30Sjl if (name == NULL)
155125cf1a30Sjl return (-1);
155225cf1a30Sjl
155325cf1a30Sjl /*
155425cf1a30Sjl * Determine how many possible types are currently supported
155525cf1a30Sjl * for dr.
155625cf1a30Sjl */
155725cf1a30Sjl ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
155825cf1a30Sjl
155925cf1a30Sjl /* Determine if the node's name correspond to a predefined type. */
156025cf1a30Sjl for (index = 0; index < ntypes; index++) {
156125cf1a30Sjl if (strcmp(drmach_name2type[index].name, name) == 0)
156225cf1a30Sjl /* The node is an allowed type for dr. */
156325cf1a30Sjl return (index);
156425cf1a30Sjl }
156525cf1a30Sjl
156625cf1a30Sjl /*
156725cf1a30Sjl * If the name of the node does not map to any of the
156825cf1a30Sjl * types in the array drmach_name2type then the node is not of
156925cf1a30Sjl * interest to dr.
157025cf1a30Sjl */
157125cf1a30Sjl return (-1);
157225cf1a30Sjl }
157325cf1a30Sjl
157425cf1a30Sjl /*
157525cf1a30Sjl * there is some complication on OPL:
157625cf1a30Sjl * - pseudo-mc nodes do not have portid property
157725cf1a30Sjl * - portid[9:5] of cmp node is LSB #, portid[7:3] of pci is LSB#
157825cf1a30Sjl * - cmp has board#
157925cf1a30Sjl * - core and cpu nodes do not have portid and board# properties
158025cf1a30Sjl * starcat uses portid to derive the board# but that does not work
158125cf1a30Sjl * for us. starfire reads board# property to filter the devices.
158225cf1a30Sjl * That does not work either. So for these specific device,
158325cf1a30Sjl * we use specific hard coded methods to get the board# -
158425cf1a30Sjl * cpu: LSB# = CPUID[9:5]
158525cf1a30Sjl */
158625cf1a30Sjl
158725cf1a30Sjl static int
drmach_board_find_devices_cb(drmach_node_walk_args_t * args)158825cf1a30Sjl drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
158925cf1a30Sjl {
159025cf1a30Sjl drmach_node_t *node = args->node;
159125cf1a30Sjl drmach_board_cb_data_t *data = args->data;
159225cf1a30Sjl drmach_board_t *obj = data->obj;
159325cf1a30Sjl
159425cf1a30Sjl int rv, portid;
159525cf1a30Sjl int bnum;
159625cf1a30Sjl drmachid_t id;
159725cf1a30Sjl drmach_device_t *device;
159825cf1a30Sjl char name[OBP_MAXDRVNAME];
159925cf1a30Sjl
160025cf1a30Sjl portid = drmach_get_portid(node);
160125cf1a30Sjl /*
160225cf1a30Sjl * core, cpu and pseudo-mc do not have portid
160325cf1a30Sjl * we use cpuid as the portid of the cpu node
160425cf1a30Sjl * for pseudo-mc, we do not use portid info.
160525cf1a30Sjl */
160625cf1a30Sjl
160725cf1a30Sjl rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
160825cf1a30Sjl if (rv)
160925cf1a30Sjl return (0);
161025cf1a30Sjl
161125cf1a30Sjl
161225cf1a30Sjl rv = node->n_getprop(node, OBP_BOARDNUM, &bnum, sizeof (bnum));
161325cf1a30Sjl
161425cf1a30Sjl if (rv) {
161525cf1a30Sjl /*
161625cf1a30Sjl * cpu does not have board# property. We use
161725cf1a30Sjl * CPUID[9:5]
161825cf1a30Sjl */
161925cf1a30Sjl if (strcmp("cpu", name) == 0) {
162025cf1a30Sjl bnum = (portid >> 5) & 0x1f;
162125cf1a30Sjl } else
162225cf1a30Sjl return (0);
162325cf1a30Sjl }
162425cf1a30Sjl
162525cf1a30Sjl
162625cf1a30Sjl if (bnum != obj->bnum)
162725cf1a30Sjl return (0);
162825cf1a30Sjl
162925cf1a30Sjl if (drmach_name2type_idx(name) < 0) {
163025cf1a30Sjl return (0);
163125cf1a30Sjl }
163225cf1a30Sjl
163325cf1a30Sjl /*
163425cf1a30Sjl * Create a device data structure from this node data.
163525cf1a30Sjl * The call may yield nothing if the node is not of interest
163625cf1a30Sjl * to drmach.
163725cf1a30Sjl */
163825cf1a30Sjl data->err = drmach_device_new(node, obj, portid, &id);
163925cf1a30Sjl if (data->err)
164025cf1a30Sjl return (-1);
164125cf1a30Sjl else if (!id) {
164225cf1a30Sjl /*
164325cf1a30Sjl * drmach_device_new examined the node we passed in
164425cf1a30Sjl * and determined that it was one not of interest to
164525cf1a30Sjl * drmach. So, it is skipped.
164625cf1a30Sjl */
164725cf1a30Sjl return (0);
164825cf1a30Sjl }
164925cf1a30Sjl
165025cf1a30Sjl rv = drmach_array_set(obj->devices, data->ndevs++, id);
165125cf1a30Sjl if (rv) {
165225cf1a30Sjl data->err = DRMACH_INTERNAL_ERROR();
165325cf1a30Sjl return (-1);
165425cf1a30Sjl }
165525cf1a30Sjl device = id;
165625cf1a30Sjl
165725cf1a30Sjl data->err = (*data->found)(data->a, device->type, device->unum, id);
165825cf1a30Sjl return (data->err == NULL ? 0 : -1);
165925cf1a30Sjl }
166025cf1a30Sjl
166125cf1a30Sjl sbd_error_t *
drmach_board_find_devices(drmachid_t id,void * a,sbd_error_t * (* found)(void * a,const char *,int,drmachid_t))166225cf1a30Sjl drmach_board_find_devices(drmachid_t id, void *a,
1663bbe1232eSToomas Soome sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
166425cf1a30Sjl {
166525cf1a30Sjl drmach_board_t *bp = (drmach_board_t *)id;
166625cf1a30Sjl sbd_error_t *err;
166725cf1a30Sjl int max_devices;
166825cf1a30Sjl int rv;
166925cf1a30Sjl drmach_board_cb_data_t data;
167025cf1a30Sjl
167125cf1a30Sjl
167225cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
167325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
167425cf1a30Sjl
167525cf1a30Sjl max_devices = MAX_CPU_UNITS_PER_BOARD;
167625cf1a30Sjl max_devices += MAX_MEM_UNITS_PER_BOARD;
167725cf1a30Sjl max_devices += MAX_IO_UNITS_PER_BOARD;
167825cf1a30Sjl
167925cf1a30Sjl bp->devices = drmach_array_new(0, max_devices);
168025cf1a30Sjl
168125cf1a30Sjl if (bp->tree == NULL)
168225cf1a30Sjl bp->tree = drmach_node_new();
168325cf1a30Sjl
168425cf1a30Sjl data.obj = bp;
168525cf1a30Sjl data.ndevs = 0;
168625cf1a30Sjl data.found = found;
168725cf1a30Sjl data.a = a;
168825cf1a30Sjl data.err = NULL;
168925cf1a30Sjl
169025cf1a30Sjl rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
169125cf1a30Sjl if (rv == 0)
169225cf1a30Sjl err = NULL;
169325cf1a30Sjl else {
169425cf1a30Sjl drmach_array_dispose(bp->devices, drmach_device_dispose);
169525cf1a30Sjl bp->devices = NULL;
169625cf1a30Sjl
169725cf1a30Sjl if (data.err)
169825cf1a30Sjl err = data.err;
169925cf1a30Sjl else
170025cf1a30Sjl err = DRMACH_INTERNAL_ERROR();
170125cf1a30Sjl }
170225cf1a30Sjl
170325cf1a30Sjl return (err);
170425cf1a30Sjl }
170525cf1a30Sjl
170625cf1a30Sjl int
drmach_board_lookup(int bnum,drmachid_t * id)170725cf1a30Sjl drmach_board_lookup(int bnum, drmachid_t *id)
170825cf1a30Sjl {
170925cf1a30Sjl int rv = 0;
171025cf1a30Sjl
171125cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_READER);
171225cf1a30Sjl if (drmach_array_get(drmach_boards, bnum, id)) {
171325cf1a30Sjl *id = 0;
171425cf1a30Sjl rv = -1;
171525cf1a30Sjl }
171625cf1a30Sjl rw_exit(&drmach_boards_rwlock);
171725cf1a30Sjl return (rv);
171825cf1a30Sjl }
171925cf1a30Sjl
172025cf1a30Sjl sbd_error_t *
drmach_board_name(int bnum,char * buf,int buflen)172125cf1a30Sjl drmach_board_name(int bnum, char *buf, int buflen)
172225cf1a30Sjl {
172307d06da5SSurya Prakki (void) snprintf(buf, buflen, "SB%d", bnum);
172425cf1a30Sjl return (NULL);
172525cf1a30Sjl }
172625cf1a30Sjl
172725cf1a30Sjl sbd_error_t *
drmach_board_poweroff(drmachid_t id)172825cf1a30Sjl drmach_board_poweroff(drmachid_t id)
172925cf1a30Sjl {
173025cf1a30Sjl drmach_board_t *bp;
173125cf1a30Sjl sbd_error_t *err;
173225cf1a30Sjl drmach_status_t stat;
173325cf1a30Sjl
1734ddf95635Sbm if (DRMACH_NULL_ID(id))
1735ddf95635Sbm return (NULL);
1736ddf95635Sbm
173725cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
173825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
173925cf1a30Sjl bp = id;
174025cf1a30Sjl
174125cf1a30Sjl err = drmach_board_status(id, &stat);
174225cf1a30Sjl
174325cf1a30Sjl if (!err) {
174425cf1a30Sjl if (stat.configured || stat.busy)
174525cf1a30Sjl err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
174625cf1a30Sjl else {
174725cf1a30Sjl bp->powered = 0;
174825cf1a30Sjl }
174925cf1a30Sjl }
175025cf1a30Sjl return (err);
175125cf1a30Sjl }
175225cf1a30Sjl
175325cf1a30Sjl sbd_error_t *
drmach_board_poweron(drmachid_t id)175425cf1a30Sjl drmach_board_poweron(drmachid_t id)
175525cf1a30Sjl {
175625cf1a30Sjl drmach_board_t *bp;
175725cf1a30Sjl
175825cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
175925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
176025cf1a30Sjl bp = id;
176125cf1a30Sjl
176225cf1a30Sjl bp->powered = 1;
176325cf1a30Sjl
176425cf1a30Sjl return (NULL);
176525cf1a30Sjl }
176625cf1a30Sjl
176725cf1a30Sjl static sbd_error_t *
drmach_board_release(drmachid_t id)176825cf1a30Sjl drmach_board_release(drmachid_t id)
176925cf1a30Sjl {
177025cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
177125cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
177225cf1a30Sjl return (NULL);
177325cf1a30Sjl }
177425cf1a30Sjl
177525cf1a30Sjl /*ARGSUSED*/
177625cf1a30Sjl sbd_error_t *
drmach_board_test(drmachid_t id,drmach_opts_t * opts,int force)177725cf1a30Sjl drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
177825cf1a30Sjl {
177925cf1a30Sjl return (NULL);
178025cf1a30Sjl }
178125cf1a30Sjl
178225cf1a30Sjl sbd_error_t *
drmach_board_unassign(drmachid_t id)178325cf1a30Sjl drmach_board_unassign(drmachid_t id)
178425cf1a30Sjl {
178525cf1a30Sjl drmach_board_t *bp;
178625cf1a30Sjl sbd_error_t *err;
178725cf1a30Sjl drmach_status_t stat;
178825cf1a30Sjl
1789ddf95635Sbm if (DRMACH_NULL_ID(id))
1790ddf95635Sbm return (NULL);
179125cf1a30Sjl
179225cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id)) {
179325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
179425cf1a30Sjl }
179525cf1a30Sjl bp = id;
179625cf1a30Sjl
179725cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_WRITER);
179825cf1a30Sjl
179925cf1a30Sjl err = drmach_board_status(id, &stat);
180025cf1a30Sjl if (err) {
180125cf1a30Sjl rw_exit(&drmach_boards_rwlock);
180225cf1a30Sjl return (err);
180325cf1a30Sjl }
180425cf1a30Sjl if (stat.configured || stat.busy) {
180525cf1a30Sjl err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
180625cf1a30Sjl } else {
180725cf1a30Sjl if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
180825cf1a30Sjl err = DRMACH_INTERNAL_ERROR();
180925cf1a30Sjl else
181025cf1a30Sjl drmach_board_dispose(bp);
181125cf1a30Sjl }
181225cf1a30Sjl rw_exit(&drmach_boards_rwlock);
181325cf1a30Sjl return (err);
181425cf1a30Sjl }
181525cf1a30Sjl
181625cf1a30Sjl /*
181725cf1a30Sjl * We have to do more on OPL - e.g. set up sram tte, read cpuid, strand id,
181825cf1a30Sjl * implementation #, etc
181925cf1a30Sjl */
182025cf1a30Sjl
182125cf1a30Sjl static sbd_error_t *
drmach_cpu_new(drmach_device_t * proto,drmachid_t * idp)182225cf1a30Sjl drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
182325cf1a30Sjl {
182425cf1a30Sjl int portid;
182525cf1a30Sjl drmach_cpu_t *cp = NULL;
182625cf1a30Sjl
182725cf1a30Sjl /* portid is CPUID of the node */
182825cf1a30Sjl portid = proto->portid;
182925cf1a30Sjl ASSERT(portid != -1);
183025cf1a30Sjl
183125cf1a30Sjl /* unum = (CMP/CHIP ID) + (ON_BOARD_CORE_NUM * MAX_CMPID_PER_BOARD) */
183225cf1a30Sjl proto->unum = ((portid/OPL_MAX_CPUID_PER_CMP) &
1833e98fafb9Sjl (OPL_MAX_CMPID_PER_BOARD - 1)) +
1834e98fafb9Sjl ((portid & (OPL_MAX_CPUID_PER_CMP - 1)) *
1835e98fafb9Sjl (OPL_MAX_CMPID_PER_BOARD));
183625cf1a30Sjl
183725cf1a30Sjl cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
183825cf1a30Sjl bcopy(proto, &cp->dev, sizeof (cp->dev));
183925cf1a30Sjl cp->dev.node = drmach_node_dup(proto->node);
184025cf1a30Sjl cp->dev.cm.isa = (void *)drmach_cpu_new;
184125cf1a30Sjl cp->dev.cm.dispose = drmach_cpu_dispose;
184225cf1a30Sjl cp->dev.cm.release = drmach_cpu_release;
184325cf1a30Sjl cp->dev.cm.status = drmach_cpu_status;
184425cf1a30Sjl
184507d06da5SSurya Prakki (void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
1846e98fafb9Sjl cp->dev.type, cp->dev.unum);
184725cf1a30Sjl
184825cf1a30Sjl /*
184925cf1a30Sjl * CPU ID representation
185025cf1a30Sjl * CPUID[9:5] = SB#
185125cf1a30Sjl * CPUID[4:3] = Chip#
185225cf1a30Sjl * CPUID[2:1] = Core# (Only 2 core for OPL)
185325cf1a30Sjl * CPUID[0:0] = Strand#
185425cf1a30Sjl */
185525cf1a30Sjl
185625cf1a30Sjl /*
185725cf1a30Sjl * reg property of the strand contains strand ID
185825cf1a30Sjl * reg property of the parent node contains core ID
185925cf1a30Sjl * We should use them.
186025cf1a30Sjl */
186125cf1a30Sjl cp->cpuid = portid;
186225cf1a30Sjl cp->sb = (portid >> 5) & 0x1f;
186325cf1a30Sjl cp->chipid = (portid >> 3) & 0x3;
186425cf1a30Sjl cp->coreid = (portid >> 1) & 0x3;
186525cf1a30Sjl cp->strandid = portid & 0x1;
186625cf1a30Sjl
186725cf1a30Sjl *idp = (drmachid_t)cp;
186825cf1a30Sjl return (NULL);
186925cf1a30Sjl }
187025cf1a30Sjl
187125cf1a30Sjl
187225cf1a30Sjl static void
drmach_cpu_dispose(drmachid_t id)187325cf1a30Sjl drmach_cpu_dispose(drmachid_t id)
187425cf1a30Sjl {
187525cf1a30Sjl drmach_cpu_t *self;
187625cf1a30Sjl
187725cf1a30Sjl ASSERT(DRMACH_IS_CPU_ID(id));
187825cf1a30Sjl
187925cf1a30Sjl self = id;
188025cf1a30Sjl if (self->dev.node)
188125cf1a30Sjl drmach_node_dispose(self->dev.node);
188225cf1a30Sjl
188325cf1a30Sjl kmem_free(self, sizeof (*self));
188425cf1a30Sjl }
188525cf1a30Sjl
188625cf1a30Sjl static int
drmach_cpu_start(struct cpu * cp)188725cf1a30Sjl drmach_cpu_start(struct cpu *cp)
188825cf1a30Sjl {
188925cf1a30Sjl int cpuid = cp->cpu_id;
189025cf1a30Sjl extern int restart_other_cpu(int);
189125cf1a30Sjl
189225cf1a30Sjl ASSERT(MUTEX_HELD(&cpu_lock));
189325cf1a30Sjl ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
189425cf1a30Sjl
189525cf1a30Sjl cp->cpu_flags &= ~CPU_POWEROFF;
189625cf1a30Sjl
189725cf1a30Sjl /*
189825cf1a30Sjl * NOTE: restart_other_cpu pauses cpus during the
189925cf1a30Sjl * slave cpu start. This helps to quiesce the
190025cf1a30Sjl * bus traffic a bit which makes the tick sync
190125cf1a30Sjl * routine in the prom more robust.
190225cf1a30Sjl */
190325cf1a30Sjl DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
190425cf1a30Sjl
190507d06da5SSurya Prakki (void) restart_other_cpu(cpuid);
190625cf1a30Sjl
190725cf1a30Sjl return (0);
190825cf1a30Sjl }
190925cf1a30Sjl
191025cf1a30Sjl static sbd_error_t *
drmach_cpu_release(drmachid_t id)191125cf1a30Sjl drmach_cpu_release(drmachid_t id)
191225cf1a30Sjl {
191325cf1a30Sjl if (!DRMACH_IS_CPU_ID(id))
191425cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
191525cf1a30Sjl
191625cf1a30Sjl return (NULL);
191725cf1a30Sjl }
191825cf1a30Sjl
191925cf1a30Sjl static sbd_error_t *
drmach_cpu_status(drmachid_t id,drmach_status_t * stat)192025cf1a30Sjl drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
192125cf1a30Sjl {
192225cf1a30Sjl drmach_cpu_t *cp;
192325cf1a30Sjl drmach_device_t *dp;
192425cf1a30Sjl
192525cf1a30Sjl ASSERT(DRMACH_IS_CPU_ID(id));
192625cf1a30Sjl cp = (drmach_cpu_t *)id;
192725cf1a30Sjl dp = &cp->dev;
192825cf1a30Sjl
192925cf1a30Sjl stat->assigned = dp->bp->assigned;
193025cf1a30Sjl stat->powered = dp->bp->powered;
193125cf1a30Sjl mutex_enter(&cpu_lock);
193225cf1a30Sjl stat->configured = (cpu_get(cp->cpuid) != NULL);
193325cf1a30Sjl mutex_exit(&cpu_lock);
193425cf1a30Sjl stat->busy = dp->busy;
193507d06da5SSurya Prakki (void) strncpy(stat->type, dp->type, sizeof (stat->type));
193625cf1a30Sjl stat->info[0] = '\0';
193725cf1a30Sjl
193825cf1a30Sjl return (NULL);
193925cf1a30Sjl }
194025cf1a30Sjl
194125cf1a30Sjl sbd_error_t *
drmach_cpu_disconnect(drmachid_t id)194225cf1a30Sjl drmach_cpu_disconnect(drmachid_t id)
194325cf1a30Sjl {
194425cf1a30Sjl
194525cf1a30Sjl if (!DRMACH_IS_CPU_ID(id))
194625cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
194725cf1a30Sjl
194825cf1a30Sjl return (NULL);
194925cf1a30Sjl }
195025cf1a30Sjl
195125cf1a30Sjl sbd_error_t *
drmach_cpu_get_id(drmachid_t id,processorid_t * cpuid)195225cf1a30Sjl drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
195325cf1a30Sjl {
195425cf1a30Sjl drmach_cpu_t *cpu;
195525cf1a30Sjl
195625cf1a30Sjl if (!DRMACH_IS_CPU_ID(id))
195725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
195825cf1a30Sjl cpu = (drmach_cpu_t *)id;
195925cf1a30Sjl
196025cf1a30Sjl /* get from cpu directly on OPL */
196125cf1a30Sjl *cpuid = cpu->cpuid;
196225cf1a30Sjl return (NULL);
196325cf1a30Sjl }
196425cf1a30Sjl
196525cf1a30Sjl sbd_error_t *
drmach_cpu_get_impl(drmachid_t id,int * ip)196625cf1a30Sjl drmach_cpu_get_impl(drmachid_t id, int *ip)
196725cf1a30Sjl {
196825cf1a30Sjl drmach_device_t *cpu;
196925cf1a30Sjl drmach_node_t *np;
197025cf1a30Sjl drmach_node_t pp;
197125cf1a30Sjl int impl;
197225cf1a30Sjl char type[OBP_MAXPROPNAME];
197325cf1a30Sjl
197425cf1a30Sjl if (!DRMACH_IS_CPU_ID(id))
197525cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
197625cf1a30Sjl
197725cf1a30Sjl cpu = id;
197825cf1a30Sjl np = cpu->node;
197925cf1a30Sjl
198025cf1a30Sjl if (np->get_parent(np, &pp) != 0) {
198125cf1a30Sjl return (DRMACH_INTERNAL_ERROR());
198225cf1a30Sjl }
198325cf1a30Sjl
198425cf1a30Sjl /* the parent should be core */
198525cf1a30Sjl
198625cf1a30Sjl if (pp.n_getprop(&pp, "device_type", &type, sizeof (type)) != 0) {
198725cf1a30Sjl return (drerr_new(0, EOPL_GETPROP, NULL));
198825cf1a30Sjl }
198925cf1a30Sjl
199025cf1a30Sjl if (strcmp(type, OPL_CORE_NODE) == 0) {
1991e98fafb9Sjl if (pp.n_getprop(&pp, "implementation#", &impl,
1992e98fafb9Sjl sizeof (impl)) != 0) {
199325cf1a30Sjl return (drerr_new(0, EOPL_GETPROP, NULL));
199425cf1a30Sjl }
199525cf1a30Sjl } else {
199625cf1a30Sjl return (DRMACH_INTERNAL_ERROR());
199725cf1a30Sjl }
199825cf1a30Sjl
199925cf1a30Sjl *ip = impl;
200025cf1a30Sjl
200125cf1a30Sjl return (NULL);
200225cf1a30Sjl }
200325cf1a30Sjl
200425cf1a30Sjl sbd_error_t *
drmach_get_dip(drmachid_t id,dev_info_t ** dip)200525cf1a30Sjl drmach_get_dip(drmachid_t id, dev_info_t **dip)
200625cf1a30Sjl {
200725cf1a30Sjl drmach_device_t *dp;
200825cf1a30Sjl
200925cf1a30Sjl if (!DRMACH_IS_DEVICE_ID(id))
201025cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
201125cf1a30Sjl dp = id;
201225cf1a30Sjl
201325cf1a30Sjl *dip = dp->node->n_getdip(dp->node);
201425cf1a30Sjl return (NULL);
201525cf1a30Sjl }
201625cf1a30Sjl
201725cf1a30Sjl sbd_error_t *
drmach_io_is_attached(drmachid_t id,int * yes)201825cf1a30Sjl drmach_io_is_attached(drmachid_t id, int *yes)
201925cf1a30Sjl {
202025cf1a30Sjl drmach_device_t *dp;
202125cf1a30Sjl dev_info_t *dip;
202225cf1a30Sjl int state;
202325cf1a30Sjl
202425cf1a30Sjl if (!DRMACH_IS_IO_ID(id))
202525cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
202625cf1a30Sjl dp = id;
202725cf1a30Sjl
202825cf1a30Sjl dip = dp->node->n_getdip(dp->node);
202925cf1a30Sjl if (dip == NULL) {
203025cf1a30Sjl *yes = 0;
203125cf1a30Sjl return (NULL);
203225cf1a30Sjl }
203325cf1a30Sjl
203425cf1a30Sjl state = ddi_get_devstate(dip);
203525cf1a30Sjl *yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) ||
203625cf1a30Sjl (state == DDI_DEVSTATE_UP));
203725cf1a30Sjl
203825cf1a30Sjl return (NULL);
203925cf1a30Sjl }
204025cf1a30Sjl
204125cf1a30Sjl struct drmach_io_cb {
204225cf1a30Sjl char *name; /* name of the node */
204325cf1a30Sjl int (*func)(dev_info_t *);
204425cf1a30Sjl int rv;
204568ac2337Sjl dev_info_t *dip;
204625cf1a30Sjl };
204725cf1a30Sjl
204825cf1a30Sjl #define DRMACH_IO_POST_ATTACH 0
204925cf1a30Sjl #define DRMACH_IO_PRE_RELEASE 1
205025cf1a30Sjl
205125cf1a30Sjl static int
drmach_io_cb_check(dev_info_t * dip,void * arg)205225cf1a30Sjl drmach_io_cb_check(dev_info_t *dip, void *arg)
205325cf1a30Sjl {
205425cf1a30Sjl struct drmach_io_cb *p = (struct drmach_io_cb *)arg;
205525cf1a30Sjl char name[OBP_MAXDRVNAME];
205625cf1a30Sjl int len = OBP_MAXDRVNAME;
205725cf1a30Sjl
2058e98fafb9Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "name",
205925cf1a30Sjl (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
206025cf1a30Sjl return (DDI_WALK_PRUNECHILD);
206125cf1a30Sjl }
206225cf1a30Sjl
206325cf1a30Sjl if (strcmp(name, p->name) == 0) {
206468ac2337Sjl ndi_hold_devi(dip);
206568ac2337Sjl p->dip = dip;
206625cf1a30Sjl return (DDI_WALK_TERMINATE);
206725cf1a30Sjl }
206825cf1a30Sjl
206925cf1a30Sjl return (DDI_WALK_CONTINUE);
207025cf1a30Sjl }
207125cf1a30Sjl
207225cf1a30Sjl
207325cf1a30Sjl static int
drmach_console_ops(drmachid_t * id,int state)207425cf1a30Sjl drmach_console_ops(drmachid_t *id, int state)
207525cf1a30Sjl {
207625cf1a30Sjl drmach_io_t *obj = (drmach_io_t *)id;
207725cf1a30Sjl struct drmach_io_cb arg;
207825cf1a30Sjl int (*msudetp)(dev_info_t *);
207925cf1a30Sjl int (*msuattp)(dev_info_t *);
208025cf1a30Sjl dev_info_t *dip, *pdip;
208125cf1a30Sjl
208225cf1a30Sjl /* 4 is pcicmu channel */
208325cf1a30Sjl if (obj->channel != 4)
208425cf1a30Sjl return (0);
208525cf1a30Sjl
208625cf1a30Sjl arg.name = "serial";
208725cf1a30Sjl arg.func = NULL;
208825cf1a30Sjl if (state == DRMACH_IO_PRE_RELEASE) {
208925cf1a30Sjl msudetp = (int (*)(dev_info_t *))
209025cf1a30Sjl modgetsymvalue("oplmsu_dr_detach", 0);
209125cf1a30Sjl if (msudetp != NULL)
209225cf1a30Sjl arg.func = msudetp;
209325cf1a30Sjl } else if (state == DRMACH_IO_POST_ATTACH) {
209425cf1a30Sjl msuattp = (int (*)(dev_info_t *))
209525cf1a30Sjl modgetsymvalue("oplmsu_dr_attach", 0);
209625cf1a30Sjl if (msuattp != NULL)
209725cf1a30Sjl arg.func = msuattp;
209868ac2337Sjl } else {
209925cf1a30Sjl return (0);
210068ac2337Sjl }
210125cf1a30Sjl
210225cf1a30Sjl if (arg.func == NULL) {
210325cf1a30Sjl return (0);
210425cf1a30Sjl }
210525cf1a30Sjl
210625cf1a30Sjl arg.rv = 0;
210768ac2337Sjl arg.dip = NULL;
210825cf1a30Sjl
210925cf1a30Sjl dip = obj->dev.node->n_getdip(obj->dev.node);
2110*3fe80ca4SDan Cross pdip = ddi_get_parent(dip);
2111*3fe80ca4SDan Cross if (pdip == NULL) {
211225cf1a30Sjl /* this cannot happen unless something bad happens */
211325cf1a30Sjl return (-1);
211425cf1a30Sjl }
2115*3fe80ca4SDan Cross ndi_hold_devi(pdip);
2116*3fe80ca4SDan Cross ndi_devi_enter(pdip);
211725cf1a30Sjl
211825cf1a30Sjl ddi_walk_devs(dip, drmach_io_cb_check, (void *)&arg);
211925cf1a30Sjl
2120*3fe80ca4SDan Cross ndi_devi_exit(pdip);
212168ac2337Sjl ndi_rele_devi(pdip);
212268ac2337Sjl
212368ac2337Sjl if (arg.dip) {
212468ac2337Sjl arg.rv = (*arg.func)(arg.dip);
212568ac2337Sjl ndi_rele_devi(arg.dip);
212668ac2337Sjl } else {
212768ac2337Sjl arg.rv = -1;
212825cf1a30Sjl }
212925cf1a30Sjl
213025cf1a30Sjl return (arg.rv);
213125cf1a30Sjl }
213225cf1a30Sjl
213325cf1a30Sjl sbd_error_t *
drmach_io_pre_release(drmachid_t id)213425cf1a30Sjl drmach_io_pre_release(drmachid_t id)
213525cf1a30Sjl {
213625cf1a30Sjl int rv;
213725cf1a30Sjl
213825cf1a30Sjl if (!DRMACH_IS_IO_ID(id))
213925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
214025cf1a30Sjl
214125cf1a30Sjl rv = drmach_console_ops(id, DRMACH_IO_PRE_RELEASE);
214225cf1a30Sjl
214325cf1a30Sjl if (rv != 0)
214425cf1a30Sjl cmn_err(CE_WARN, "IO callback failed in pre-release\n");
214525cf1a30Sjl
214625cf1a30Sjl return (NULL);
214725cf1a30Sjl }
214825cf1a30Sjl
214925cf1a30Sjl static sbd_error_t *
drmach_io_release(drmachid_t id)215025cf1a30Sjl drmach_io_release(drmachid_t id)
215125cf1a30Sjl {
215225cf1a30Sjl if (!DRMACH_IS_IO_ID(id))
215325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
215425cf1a30Sjl return (NULL);
215525cf1a30Sjl }
215625cf1a30Sjl
215725cf1a30Sjl sbd_error_t *
drmach_io_unrelease(drmachid_t id)215825cf1a30Sjl drmach_io_unrelease(drmachid_t id)
215925cf1a30Sjl {
216025cf1a30Sjl if (!DRMACH_IS_IO_ID(id))
216125cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
216225cf1a30Sjl return (NULL);
216325cf1a30Sjl }
216425cf1a30Sjl
216525cf1a30Sjl /*ARGSUSED*/
216625cf1a30Sjl sbd_error_t *
drmach_io_post_release(drmachid_t id)216725cf1a30Sjl drmach_io_post_release(drmachid_t id)
216825cf1a30Sjl {
216925cf1a30Sjl return (NULL);
217025cf1a30Sjl }
217125cf1a30Sjl
217225cf1a30Sjl /*ARGSUSED*/
217325cf1a30Sjl sbd_error_t *
drmach_io_post_attach(drmachid_t id)217425cf1a30Sjl drmach_io_post_attach(drmachid_t id)
217525cf1a30Sjl {
217625cf1a30Sjl int rv;
217725cf1a30Sjl
217825cf1a30Sjl if (!DRMACH_IS_IO_ID(id))
217925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
218025cf1a30Sjl
218125cf1a30Sjl rv = drmach_console_ops(id, DRMACH_IO_POST_ATTACH);
218225cf1a30Sjl
218325cf1a30Sjl if (rv != 0)
218425cf1a30Sjl cmn_err(CE_WARN, "IO callback failed in post-attach\n");
218525cf1a30Sjl
218625cf1a30Sjl return (0);
218725cf1a30Sjl }
218825cf1a30Sjl
218925cf1a30Sjl static sbd_error_t *
drmach_io_status(drmachid_t id,drmach_status_t * stat)219025cf1a30Sjl drmach_io_status(drmachid_t id, drmach_status_t *stat)
219125cf1a30Sjl {
219225cf1a30Sjl drmach_device_t *dp;
219325cf1a30Sjl sbd_error_t *err;
219425cf1a30Sjl int configured;
219525cf1a30Sjl
219625cf1a30Sjl ASSERT(DRMACH_IS_IO_ID(id));
219725cf1a30Sjl dp = id;
219825cf1a30Sjl
219925cf1a30Sjl err = drmach_io_is_attached(id, &configured);
220025cf1a30Sjl if (err)
220125cf1a30Sjl return (err);
220225cf1a30Sjl
220325cf1a30Sjl stat->assigned = dp->bp->assigned;
220425cf1a30Sjl stat->powered = dp->bp->powered;
220525cf1a30Sjl stat->configured = (configured != 0);
220625cf1a30Sjl stat->busy = dp->busy;
220707d06da5SSurya Prakki (void) strncpy(stat->type, dp->type, sizeof (stat->type));
220825cf1a30Sjl stat->info[0] = '\0';
220925cf1a30Sjl
221025cf1a30Sjl return (NULL);
221125cf1a30Sjl }
221225cf1a30Sjl
221325cf1a30Sjl static sbd_error_t *
drmach_mem_new(drmach_device_t * proto,drmachid_t * idp)221425cf1a30Sjl drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
221525cf1a30Sjl {
221625cf1a30Sjl dev_info_t *dip;
221768ac2337Sjl int rv;
221825cf1a30Sjl
221925cf1a30Sjl drmach_mem_t *mp;
222025cf1a30Sjl
222168ac2337Sjl rv = 0;
222268ac2337Sjl
222368ac2337Sjl if ((proto->node->n_getproplen(proto->node, "mc-addr", &rv) < 0) ||
2224e98fafb9Sjl (rv <= 0)) {
222568ac2337Sjl *idp = (drmachid_t)0;
222668ac2337Sjl return (NULL);
222768ac2337Sjl }
222868ac2337Sjl
222925cf1a30Sjl mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
223025cf1a30Sjl proto->unum = 0;
223125cf1a30Sjl
223225cf1a30Sjl bcopy(proto, &mp->dev, sizeof (mp->dev));
223325cf1a30Sjl mp->dev.node = drmach_node_dup(proto->node);
223425cf1a30Sjl mp->dev.cm.isa = (void *)drmach_mem_new;
223525cf1a30Sjl mp->dev.cm.dispose = drmach_mem_dispose;
223625cf1a30Sjl mp->dev.cm.release = drmach_mem_release;
223725cf1a30Sjl mp->dev.cm.status = drmach_mem_status;
223825cf1a30Sjl
223907d06da5SSurya Prakki (void) snprintf(mp->dev.cm.name, sizeof (mp->dev.cm.name), "%s",
224007d06da5SSurya Prakki mp->dev.type);
224125cf1a30Sjl
224225cf1a30Sjl dip = mp->dev.node->n_getdip(mp->dev.node);
224325cf1a30Sjl if (drmach_setup_mc_info(dip, mp) != 0) {
22448eafe49bSbm return (drerr_new(1, EOPL_MC_SETUP, NULL));
224525cf1a30Sjl }
224625cf1a30Sjl
224768ac2337Sjl /* make sure we do not create memoryless nodes */
224868ac2337Sjl if (mp->nbytes == 0) {
224968ac2337Sjl *idp = (drmachid_t)NULL;
225068ac2337Sjl kmem_free(mp, sizeof (drmach_mem_t));
225168ac2337Sjl } else
225268ac2337Sjl *idp = (drmachid_t)mp;
225368ac2337Sjl
225425cf1a30Sjl return (NULL);
225525cf1a30Sjl }
225625cf1a30Sjl
225725cf1a30Sjl static void
drmach_mem_dispose(drmachid_t id)225825cf1a30Sjl drmach_mem_dispose(drmachid_t id)
225925cf1a30Sjl {
226025cf1a30Sjl drmach_mem_t *mp;
226125cf1a30Sjl
226225cf1a30Sjl ASSERT(DRMACH_IS_MEM_ID(id));
226325cf1a30Sjl
226425cf1a30Sjl
226525cf1a30Sjl mp = id;
226625cf1a30Sjl
226725cf1a30Sjl if (mp->dev.node)
226825cf1a30Sjl drmach_node_dispose(mp->dev.node);
226925cf1a30Sjl
227025cf1a30Sjl if (mp->memlist) {
227125cf1a30Sjl memlist_delete(mp->memlist);
227225cf1a30Sjl mp->memlist = NULL;
227325cf1a30Sjl }
227468ac2337Sjl
227568ac2337Sjl kmem_free(mp, sizeof (*mp));
227625cf1a30Sjl }
227725cf1a30Sjl
227825cf1a30Sjl sbd_error_t *
drmach_mem_add_span(drmachid_t id,uint64_t basepa,uint64_t size)227925cf1a30Sjl drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
228025cf1a30Sjl {
228125cf1a30Sjl pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
228225cf1a30Sjl pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
228325cf1a30Sjl int rv;
228425cf1a30Sjl
228525cf1a30Sjl ASSERT(size != 0);
228625cf1a30Sjl
228725cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
228825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
228925cf1a30Sjl
229085f58038Sdp rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
229125cf1a30Sjl if (rv == ENOMEM) {
229207d06da5SSurya Prakki cmn_err(CE_WARN, "%lu megabytes not available to kernel cage",
229307d06da5SSurya Prakki (ulong_t)(size == 0 ? 0 : size / MBYTE));
229425cf1a30Sjl } else if (rv != 0) {
229525cf1a30Sjl /* catch this in debug kernels */
229625cf1a30Sjl ASSERT(0);
229725cf1a30Sjl
2298e98fafb9Sjl cmn_err(CE_WARN, "unexpected kcage_range_add return value %d",
2299e98fafb9Sjl rv);
230025cf1a30Sjl }
230125cf1a30Sjl
230225cf1a30Sjl if (rv) {
230325cf1a30Sjl return (DRMACH_INTERNAL_ERROR());
230425cf1a30Sjl }
230525cf1a30Sjl else
230625cf1a30Sjl return (NULL);
230725cf1a30Sjl }
230825cf1a30Sjl
230925cf1a30Sjl sbd_error_t *
drmach_mem_del_span(drmachid_t id,uint64_t basepa,uint64_t size)231025cf1a30Sjl drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
231125cf1a30Sjl {
231225cf1a30Sjl pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
231325cf1a30Sjl pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
231425cf1a30Sjl int rv;
231525cf1a30Sjl
231625cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
231725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
231825cf1a30Sjl
231925cf1a30Sjl if (size > 0) {
232025cf1a30Sjl rv = kcage_range_delete_post_mem_del(basepfn, npages);
232125cf1a30Sjl if (rv != 0) {
232225cf1a30Sjl cmn_err(CE_WARN,
232325cf1a30Sjl "unexpected kcage_range_delete_post_mem_del"
232425cf1a30Sjl " return value %d", rv);
232525cf1a30Sjl return (DRMACH_INTERNAL_ERROR());
232625cf1a30Sjl }
232725cf1a30Sjl }
232825cf1a30Sjl
232925cf1a30Sjl return (NULL);
233025cf1a30Sjl }
233125cf1a30Sjl
233225cf1a30Sjl sbd_error_t *
drmach_mem_disable(drmachid_t id)233325cf1a30Sjl drmach_mem_disable(drmachid_t id)
233425cf1a30Sjl {
233525cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
233625cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
233725cf1a30Sjl else {
233825cf1a30Sjl drmach_flush_all();
233925cf1a30Sjl return (NULL);
234025cf1a30Sjl }
234125cf1a30Sjl }
234225cf1a30Sjl
234325cf1a30Sjl sbd_error_t *
drmach_mem_enable(drmachid_t id)234425cf1a30Sjl drmach_mem_enable(drmachid_t id)
234525cf1a30Sjl {
234625cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
234725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
234825cf1a30Sjl else
234925cf1a30Sjl return (NULL);
235025cf1a30Sjl }
235125cf1a30Sjl
235225cf1a30Sjl sbd_error_t *
drmach_mem_get_info(drmachid_t id,drmach_mem_info_t * mem)235325cf1a30Sjl drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem)
235425cf1a30Sjl {
235525cf1a30Sjl drmach_mem_t *mp;
235625cf1a30Sjl
235725cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
235825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
235925cf1a30Sjl
236025cf1a30Sjl mp = (drmach_mem_t *)id;
236125cf1a30Sjl
236225cf1a30Sjl /*
236325cf1a30Sjl * This is only used by dr to round up/down the memory
236425cf1a30Sjl * for copying. Our unit of memory isolation is 64 MB.
236525cf1a30Sjl */
236625cf1a30Sjl
236725cf1a30Sjl mem->mi_alignment_mask = (64 * 1024 * 1024 - 1);
236825cf1a30Sjl mem->mi_basepa = mp->base_pa;
236925cf1a30Sjl mem->mi_size = mp->nbytes;
237025cf1a30Sjl mem->mi_slice_size = mp->slice_size;
237125cf1a30Sjl
237225cf1a30Sjl return (NULL);
237325cf1a30Sjl }
237425cf1a30Sjl
237525cf1a30Sjl sbd_error_t *
drmach_mem_get_base_physaddr(drmachid_t id,uint64_t * pa)237625cf1a30Sjl drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa)
237725cf1a30Sjl {
237825cf1a30Sjl drmach_mem_t *mp;
237925cf1a30Sjl
238025cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
238125cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
238225cf1a30Sjl
238325cf1a30Sjl mp = (drmach_mem_t *)id;
238425cf1a30Sjl
238525cf1a30Sjl *pa = mp->base_pa;
238625cf1a30Sjl return (NULL);
238725cf1a30Sjl }
238825cf1a30Sjl
238925cf1a30Sjl sbd_error_t *
drmach_mem_get_memlist(drmachid_t id,struct memlist ** ml)239025cf1a30Sjl drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
239125cf1a30Sjl {
239225cf1a30Sjl drmach_mem_t *mem;
2393b307f191Sbm #ifdef DEBUG
239425cf1a30Sjl int rv;
2395b307f191Sbm #endif
239625cf1a30Sjl struct memlist *mlist;
239725cf1a30Sjl
239825cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
239925cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
240025cf1a30Sjl
240125cf1a30Sjl mem = (drmach_mem_t *)id;
240225cf1a30Sjl mlist = memlist_dup(mem->memlist);
240325cf1a30Sjl
240425cf1a30Sjl #ifdef DEBUG
240525cf1a30Sjl /*
240625cf1a30Sjl * Make sure the incoming memlist doesn't already
240725cf1a30Sjl * intersect with what's present in the system (phys_install).
240825cf1a30Sjl */
240925cf1a30Sjl memlist_read_lock();
241025cf1a30Sjl rv = memlist_intersect(phys_install, mlist);
241125cf1a30Sjl memlist_read_unlock();
241225cf1a30Sjl if (rv) {
2413e98fafb9Sjl DRMACH_PR("Derived memlist intersects with phys_install\n");
241425cf1a30Sjl memlist_dump(mlist);
241525cf1a30Sjl
241625cf1a30Sjl DRMACH_PR("phys_install memlist:\n");
241725cf1a30Sjl memlist_dump(phys_install);
241825cf1a30Sjl
241925cf1a30Sjl memlist_delete(mlist);
242025cf1a30Sjl return (DRMACH_INTERNAL_ERROR());
242125cf1a30Sjl }
242225cf1a30Sjl
242325cf1a30Sjl DRMACH_PR("Derived memlist:");
242425cf1a30Sjl memlist_dump(mlist);
242525cf1a30Sjl #endif
242625cf1a30Sjl *ml = mlist;
242725cf1a30Sjl
242825cf1a30Sjl return (NULL);
242925cf1a30Sjl }
243025cf1a30Sjl
243125cf1a30Sjl sbd_error_t *
drmach_mem_get_slice_size(drmachid_t id,uint64_t * bytes)243225cf1a30Sjl drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
243325cf1a30Sjl {
243425cf1a30Sjl drmach_mem_t *mem;
243525cf1a30Sjl
243625cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
243725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
243825cf1a30Sjl
243925cf1a30Sjl mem = (drmach_mem_t *)id;
244025cf1a30Sjl
244125cf1a30Sjl *bytes = mem->slice_size;
244225cf1a30Sjl
244325cf1a30Sjl return (NULL);
244425cf1a30Sjl }
244525cf1a30Sjl
244625cf1a30Sjl
244725cf1a30Sjl /* ARGSUSED */
244825cf1a30Sjl processorid_t
drmach_mem_cpu_affinity(drmachid_t id)244925cf1a30Sjl drmach_mem_cpu_affinity(drmachid_t id)
245025cf1a30Sjl {
245125cf1a30Sjl return (CPU_CURRENT);
245225cf1a30Sjl }
245325cf1a30Sjl
245425cf1a30Sjl static sbd_error_t *
drmach_mem_release(drmachid_t id)245525cf1a30Sjl drmach_mem_release(drmachid_t id)
245625cf1a30Sjl {
245725cf1a30Sjl if (!DRMACH_IS_MEM_ID(id))
245825cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
245925cf1a30Sjl return (NULL);
246025cf1a30Sjl }
246125cf1a30Sjl
246225cf1a30Sjl static sbd_error_t *
drmach_mem_status(drmachid_t id,drmach_status_t * stat)246325cf1a30Sjl drmach_mem_status(drmachid_t id, drmach_status_t *stat)
246425cf1a30Sjl {
246525cf1a30Sjl drmach_mem_t *dp;
246625cf1a30Sjl uint64_t pa, slice_size;
246725cf1a30Sjl struct memlist *ml;
246825cf1a30Sjl
246925cf1a30Sjl ASSERT(DRMACH_IS_MEM_ID(id));
247025cf1a30Sjl dp = id;
247125cf1a30Sjl
247225cf1a30Sjl /* get starting physical address of target memory */
247325cf1a30Sjl pa = dp->base_pa;
247425cf1a30Sjl
247525cf1a30Sjl /* round down to slice boundary */
247625cf1a30Sjl slice_size = dp->slice_size;
247725cf1a30Sjl pa &= ~(slice_size - 1);
247825cf1a30Sjl
247925cf1a30Sjl /* stop at first span that is in slice */
248025cf1a30Sjl memlist_read_lock();
248156f33205SJonathan Adams for (ml = phys_install; ml; ml = ml->ml_next)
248256f33205SJonathan Adams if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
248325cf1a30Sjl break;
248425cf1a30Sjl memlist_read_unlock();
248525cf1a30Sjl
248625cf1a30Sjl stat->assigned = dp->dev.bp->assigned;
248725cf1a30Sjl stat->powered = dp->dev.bp->powered;
248825cf1a30Sjl stat->configured = (ml != NULL);
248925cf1a30Sjl stat->busy = dp->dev.busy;
249007d06da5SSurya Prakki (void) strncpy(stat->type, dp->dev.type, sizeof (stat->type));
249125cf1a30Sjl stat->info[0] = '\0';
249225cf1a30Sjl
249325cf1a30Sjl return (NULL);
249425cf1a30Sjl }
249525cf1a30Sjl
249625cf1a30Sjl
249725cf1a30Sjl sbd_error_t *
drmach_board_deprobe(drmachid_t id)249825cf1a30Sjl drmach_board_deprobe(drmachid_t id)
249925cf1a30Sjl {
250025cf1a30Sjl drmach_board_t *bp;
250125cf1a30Sjl
250225cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
250325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
250425cf1a30Sjl
250525cf1a30Sjl bp = id;
250625cf1a30Sjl
250768ac2337Sjl cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum);
250825cf1a30Sjl
250925cf1a30Sjl if (bp->tree) {
251025cf1a30Sjl drmach_node_dispose(bp->tree);
251125cf1a30Sjl bp->tree = NULL;
251225cf1a30Sjl }
251325cf1a30Sjl if (bp->devices) {
251425cf1a30Sjl drmach_array_dispose(bp->devices, drmach_device_dispose);
251525cf1a30Sjl bp->devices = NULL;
251625cf1a30Sjl }
251725cf1a30Sjl
251825cf1a30Sjl bp->boot_board = 0;
251925cf1a30Sjl
252025cf1a30Sjl return (NULL);
252125cf1a30Sjl }
252225cf1a30Sjl
252325cf1a30Sjl /*ARGSUSED*/
252425cf1a30Sjl static sbd_error_t *
drmach_pt_ikprobe(drmachid_t id,drmach_opts_t * opts)252525cf1a30Sjl drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
252625cf1a30Sjl {
252725cf1a30Sjl drmach_board_t *bp = (drmach_board_t *)id;
252825cf1a30Sjl sbd_error_t *err = NULL;
252925cf1a30Sjl int rv;
2530e98fafb9Sjl unsigned cpu_impl;
253125cf1a30Sjl
253225cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
253325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
253425cf1a30Sjl
253525cf1a30Sjl DRMACH_PR("calling opl_probe_board for bnum=%d\n", bp->bnum);
2536e98fafb9Sjl rv = opl_probe_sb(bp->bnum, &cpu_impl);
253725cf1a30Sjl if (rv != 0) {
25388eafe49bSbm err = drerr_new(1, EOPL_PROBE, bp->cm.name);
253925cf1a30Sjl return (err);
254025cf1a30Sjl }
254125cf1a30Sjl return (err);
254225cf1a30Sjl }
254325cf1a30Sjl
254425cf1a30Sjl /*ARGSUSED*/
254525cf1a30Sjl static sbd_error_t *
drmach_pt_ikdeprobe(drmachid_t id,drmach_opts_t * opts)254625cf1a30Sjl drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
254725cf1a30Sjl {
254825cf1a30Sjl drmach_board_t *bp;
254925cf1a30Sjl sbd_error_t *err = NULL;
255025cf1a30Sjl int rv;
255125cf1a30Sjl
255225cf1a30Sjl if (!DRMACH_IS_BOARD_ID(id))
255325cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
255425cf1a30Sjl bp = (drmach_board_t *)id;
255525cf1a30Sjl
255625cf1a30Sjl cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
255725cf1a30Sjl
255825cf1a30Sjl rv = opl_unprobe_sb(bp->bnum);
255925cf1a30Sjl if (rv != 0) {
25608eafe49bSbm err = drerr_new(1, EOPL_DEPROBE, bp->cm.name);
256125cf1a30Sjl }
256225cf1a30Sjl
256325cf1a30Sjl return (err);
256425cf1a30Sjl }
256525cf1a30Sjl
256625cf1a30Sjl
256725cf1a30Sjl /*ARGSUSED*/
256825cf1a30Sjl sbd_error_t *
drmach_pt_readmem(drmachid_t id,drmach_opts_t * opts)256925cf1a30Sjl drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
257025cf1a30Sjl {
257125cf1a30Sjl struct memlist *ml;
257225cf1a30Sjl uint64_t src_pa;
257325cf1a30Sjl uint64_t dst_pa;
257425cf1a30Sjl uint64_t dst;
257525cf1a30Sjl
257625cf1a30Sjl dst_pa = va_to_pa(&dst);
257725cf1a30Sjl
257825cf1a30Sjl memlist_read_lock();
257956f33205SJonathan Adams for (ml = phys_install; ml; ml = ml->ml_next) {
258025cf1a30Sjl uint64_t nbytes;
258125cf1a30Sjl
258256f33205SJonathan Adams src_pa = ml->ml_address;
258356f33205SJonathan Adams nbytes = ml->ml_size;
258425cf1a30Sjl
258525cf1a30Sjl while (nbytes != 0ull) {
258625cf1a30Sjl
258725cf1a30Sjl /* copy 32 bytes at arc_pa to dst_pa */
258825cf1a30Sjl bcopy32_il(src_pa, dst_pa);
258925cf1a30Sjl
259025cf1a30Sjl /* increment by 32 bytes */
259125cf1a30Sjl src_pa += (4 * sizeof (uint64_t));
259225cf1a30Sjl
259325cf1a30Sjl /* decrement by 32 bytes */
259425cf1a30Sjl nbytes -= (4 * sizeof (uint64_t));
259525cf1a30Sjl }
259625cf1a30Sjl }
259725cf1a30Sjl memlist_read_unlock();
259825cf1a30Sjl
259925cf1a30Sjl return (NULL);
260025cf1a30Sjl }
260125cf1a30Sjl
260225cf1a30Sjl static struct {
260325cf1a30Sjl const char *name;
260425cf1a30Sjl sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts);
260525cf1a30Sjl } drmach_pt_arr[] = {
260625cf1a30Sjl { "readmem", drmach_pt_readmem },
260725cf1a30Sjl { "ikprobe", drmach_pt_ikprobe },
260825cf1a30Sjl { "ikdeprobe", drmach_pt_ikdeprobe },
260925cf1a30Sjl
261025cf1a30Sjl /* the following line must always be last */
261125cf1a30Sjl { NULL, NULL }
261225cf1a30Sjl };
261325cf1a30Sjl
261425cf1a30Sjl /*ARGSUSED*/
261525cf1a30Sjl sbd_error_t *
drmach_passthru(drmachid_t id,drmach_opts_t * opts)261625cf1a30Sjl drmach_passthru(drmachid_t id, drmach_opts_t *opts)
261725cf1a30Sjl {
261825cf1a30Sjl int i;
261925cf1a30Sjl sbd_error_t *err;
262025cf1a30Sjl
262125cf1a30Sjl i = 0;
262225cf1a30Sjl while (drmach_pt_arr[i].name != NULL) {
262325cf1a30Sjl int len = strlen(drmach_pt_arr[i].name);
262425cf1a30Sjl
262525cf1a30Sjl if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
262625cf1a30Sjl break;
262725cf1a30Sjl
262825cf1a30Sjl i += 1;
262925cf1a30Sjl }
263025cf1a30Sjl
263125cf1a30Sjl if (drmach_pt_arr[i].name == NULL)
263225cf1a30Sjl err = drerr_new(0, EOPL_UNKPTCMD, opts->copts);
263325cf1a30Sjl else
263425cf1a30Sjl err = (*drmach_pt_arr[i].handler)(id, opts);
263525cf1a30Sjl
263625cf1a30Sjl return (err);
263725cf1a30Sjl }
263825cf1a30Sjl
263925cf1a30Sjl sbd_error_t *
drmach_release(drmachid_t id)264025cf1a30Sjl drmach_release(drmachid_t id)
264125cf1a30Sjl {
264225cf1a30Sjl drmach_common_t *cp;
264325cf1a30Sjl
264425cf1a30Sjl if (!DRMACH_IS_DEVICE_ID(id))
264525cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
264625cf1a30Sjl cp = id;
264725cf1a30Sjl
264825cf1a30Sjl return (cp->release(id));
264925cf1a30Sjl }
265025cf1a30Sjl
265125cf1a30Sjl sbd_error_t *
drmach_status(drmachid_t id,drmach_status_t * stat)265225cf1a30Sjl drmach_status(drmachid_t id, drmach_status_t *stat)
265325cf1a30Sjl {
265425cf1a30Sjl drmach_common_t *cp;
265525cf1a30Sjl sbd_error_t *err;
265625cf1a30Sjl
265725cf1a30Sjl rw_enter(&drmach_boards_rwlock, RW_READER);
265825cf1a30Sjl
265925cf1a30Sjl if (!DRMACH_IS_ID(id)) {
266025cf1a30Sjl rw_exit(&drmach_boards_rwlock);
266125cf1a30Sjl return (drerr_new(0, EOPL_NOTID, NULL));
266225cf1a30Sjl }
266325cf1a30Sjl cp = (drmach_common_t *)id;
266425cf1a30Sjl err = cp->status(id, stat);
266525cf1a30Sjl
266625cf1a30Sjl rw_exit(&drmach_boards_rwlock);
266725cf1a30Sjl
266825cf1a30Sjl return (err);
266925cf1a30Sjl }
267025cf1a30Sjl
267125cf1a30Sjl static sbd_error_t *
drmach_i_status(drmachid_t id,drmach_status_t * stat)267225cf1a30Sjl drmach_i_status(drmachid_t id, drmach_status_t *stat)
267325cf1a30Sjl {
267425cf1a30Sjl drmach_common_t *cp;
267525cf1a30Sjl
267625cf1a30Sjl if (!DRMACH_IS_ID(id))
267725cf1a30Sjl return (drerr_new(0, EOPL_NOTID, NULL));
267825cf1a30Sjl cp = id;
267925cf1a30Sjl
268025cf1a30Sjl return (cp->status(id, stat));
268125cf1a30Sjl }
268225cf1a30Sjl
268325cf1a30Sjl /*ARGSUSED*/
268425cf1a30Sjl sbd_error_t *
drmach_unconfigure(drmachid_t id,int flags)268525cf1a30Sjl drmach_unconfigure(drmachid_t id, int flags)
268625cf1a30Sjl {
268725cf1a30Sjl drmach_device_t *dp;
268825cf1a30Sjl dev_info_t *rdip, *fdip = NULL;
268925cf1a30Sjl char name[OBP_MAXDRVNAME];
269025cf1a30Sjl int rv;
269125cf1a30Sjl
269225cf1a30Sjl if (DRMACH_IS_CPU_ID(id))
269325cf1a30Sjl return (NULL);
269425cf1a30Sjl
269525cf1a30Sjl if (!DRMACH_IS_DEVICE_ID(id))
269625cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
269725cf1a30Sjl
269825cf1a30Sjl dp = id;
269925cf1a30Sjl
270025cf1a30Sjl rdip = dp->node->n_getdip(dp->node);
270125cf1a30Sjl
270225cf1a30Sjl ASSERT(rdip);
270325cf1a30Sjl
270425cf1a30Sjl rv = dp->node->n_getprop(dp->node, "name", name, OBP_MAXDRVNAME);
270525cf1a30Sjl
270625cf1a30Sjl if (rv)
270725cf1a30Sjl return (NULL);
270825cf1a30Sjl
270925cf1a30Sjl /*
271025cf1a30Sjl * Note: FORCE flag is no longer necessary under devfs
271125cf1a30Sjl */
271225cf1a30Sjl
271325cf1a30Sjl ASSERT(e_ddi_branch_held(rdip));
271425cf1a30Sjl if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) {
271525cf1a30Sjl sbd_error_t *err;
271625cf1a30Sjl char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
271725cf1a30Sjl
271825cf1a30Sjl /*
271925cf1a30Sjl * If non-NULL, fdip is returned held and must be released.
272025cf1a30Sjl */
272125cf1a30Sjl if (fdip != NULL) {
272225cf1a30Sjl (void) ddi_pathname(fdip, path);
272325cf1a30Sjl ndi_rele_devi(fdip);
272425cf1a30Sjl } else {
272525cf1a30Sjl (void) ddi_pathname(rdip, path);
272625cf1a30Sjl }
272725cf1a30Sjl
272825cf1a30Sjl err = drerr_new(1, EOPL_DRVFAIL, path);
272925cf1a30Sjl
273025cf1a30Sjl kmem_free(path, MAXPATHLEN);
273125cf1a30Sjl
273225cf1a30Sjl return (err);
273325cf1a30Sjl }
273425cf1a30Sjl
273525cf1a30Sjl return (NULL);
273625cf1a30Sjl }
273725cf1a30Sjl
273825cf1a30Sjl
273925cf1a30Sjl int
drmach_cpu_poweron(struct cpu * cp)274025cf1a30Sjl drmach_cpu_poweron(struct cpu *cp)
274125cf1a30Sjl {
274225cf1a30Sjl int bnum, cpuid, onb_core_num, strand_id;
274325cf1a30Sjl drmach_board_t *bp;
274425cf1a30Sjl
274525cf1a30Sjl DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
274625cf1a30Sjl
274725cf1a30Sjl cpuid = cp->cpu_id;
274825cf1a30Sjl bnum = LSB_ID(cpuid);
274925cf1a30Sjl onb_core_num = ON_BOARD_CORE_NUM(cpuid);
275025cf1a30Sjl strand_id = STRAND_ID(cpuid);
275125cf1a30Sjl bp = drmach_get_board_by_bnum(bnum);
275225cf1a30Sjl
275325cf1a30Sjl ASSERT(bp);
275425cf1a30Sjl if (bp->cores[onb_core_num].core_hotadded == 0) {
275525cf1a30Sjl if (drmach_add_remove_cpu(bnum, onb_core_num,
2756e98fafb9Sjl HOTADD_CPU) != 0) {
275725cf1a30Sjl cmn_err(CE_WARN, "Failed to add CMP %d on board %d\n",
2758e98fafb9Sjl onb_core_num, bnum);
275925cf1a30Sjl return (EIO);
276025cf1a30Sjl }
276125cf1a30Sjl }
276225cf1a30Sjl
276325cf1a30Sjl ASSERT(MUTEX_HELD(&cpu_lock));
276425cf1a30Sjl
276525cf1a30Sjl if (drmach_cpu_start(cp) != 0) {
276625cf1a30Sjl if (bp->cores[onb_core_num].core_started == 0) {
276725cf1a30Sjl /*
276825cf1a30Sjl * we must undo the hotadd or no one will do that
276925cf1a30Sjl * If this fails, we will do this again in
277025cf1a30Sjl * drmach_board_disconnect.
277125cf1a30Sjl */
277225cf1a30Sjl if (drmach_add_remove_cpu(bnum, onb_core_num,
2773e98fafb9Sjl HOTREMOVE_CPU) != 0) {
277425cf1a30Sjl cmn_err(CE_WARN, "Failed to remove CMP %d "
2775e98fafb9Sjl "on board %d\n", onb_core_num, bnum);
277625cf1a30Sjl }
277725cf1a30Sjl }
277825cf1a30Sjl return (EBUSY);
277925cf1a30Sjl } else {
278025cf1a30Sjl bp->cores[onb_core_num].core_started |= (1 << strand_id);
278125cf1a30Sjl return (0);
278225cf1a30Sjl }
278325cf1a30Sjl }
278425cf1a30Sjl
278525cf1a30Sjl int
drmach_cpu_poweroff(struct cpu * cp)278625cf1a30Sjl drmach_cpu_poweroff(struct cpu *cp)
278725cf1a30Sjl {
2788bbe1232eSToomas Soome int rv = 0;
278925cf1a30Sjl processorid_t cpuid = cp->cpu_id;
279025cf1a30Sjl
279125cf1a30Sjl DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
279225cf1a30Sjl
279325cf1a30Sjl ASSERT(MUTEX_HELD(&cpu_lock));
279425cf1a30Sjl
279525cf1a30Sjl /*
279625cf1a30Sjl * Capture all CPUs (except for detaching proc) to prevent
279725cf1a30Sjl * crosscalls to the detaching proc until it has cleared its
279825cf1a30Sjl * bit in cpu_ready_set.
279925cf1a30Sjl *
280025cf1a30Sjl * The CPU's remain paused and the prom_mutex is known to be free.
280125cf1a30Sjl * This prevents the x-trap victim from blocking when doing prom
280225cf1a30Sjl * IEEE-1275 calls at a high PIL level.
280325cf1a30Sjl */
280425cf1a30Sjl
280525cf1a30Sjl promsafe_pause_cpus();
280625cf1a30Sjl
280725cf1a30Sjl /*
280825cf1a30Sjl * Quiesce interrupts on the target CPU. We do this by setting
280925cf1a30Sjl * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
281025cf1a30Sjl * prevent it from receiving cross calls and cross traps.
281125cf1a30Sjl * This prevents the processor from receiving any new soft interrupts.
281225cf1a30Sjl */
281325cf1a30Sjl mp_cpu_quiesce(cp);
281425cf1a30Sjl
281525cf1a30Sjl rv = prom_stopcpu_bycpuid(cpuid);
281625cf1a30Sjl if (rv == 0)
281725cf1a30Sjl cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
281825cf1a30Sjl
281925cf1a30Sjl start_cpus();
282025cf1a30Sjl
282125cf1a30Sjl if (rv == 0) {
282225cf1a30Sjl int bnum, onb_core_num, strand_id;
282325cf1a30Sjl drmach_board_t *bp;
282425cf1a30Sjl
282525cf1a30Sjl CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
282625cf1a30Sjl
282725cf1a30Sjl bnum = LSB_ID(cpuid);
282825cf1a30Sjl onb_core_num = ON_BOARD_CORE_NUM(cpuid);
282925cf1a30Sjl strand_id = STRAND_ID(cpuid);
283025cf1a30Sjl bp = drmach_get_board_by_bnum(bnum);
283125cf1a30Sjl ASSERT(bp);
283225cf1a30Sjl
283325cf1a30Sjl bp->cores[onb_core_num].core_started &= ~(1 << strand_id);
283425cf1a30Sjl if (bp->cores[onb_core_num].core_started == 0) {
283525cf1a30Sjl if (drmach_add_remove_cpu(bnum, onb_core_num,
2836e98fafb9Sjl HOTREMOVE_CPU) != 0) {
2837e98fafb9Sjl cmn_err(CE_WARN, "Failed to remove CMP %d LSB "
2838e98fafb9Sjl "%d\n", onb_core_num, bnum);
283925cf1a30Sjl return (EIO);
284025cf1a30Sjl }
284125cf1a30Sjl }
284225cf1a30Sjl }
284325cf1a30Sjl
284425cf1a30Sjl return (rv);
284525cf1a30Sjl }
284625cf1a30Sjl
284725cf1a30Sjl /*ARGSUSED*/
284825cf1a30Sjl int
drmach_verify_sr(dev_info_t * dip,int sflag)284925cf1a30Sjl drmach_verify_sr(dev_info_t *dip, int sflag)
285025cf1a30Sjl {
285125cf1a30Sjl return (0);
285225cf1a30Sjl }
285325cf1a30Sjl
285425cf1a30Sjl void
drmach_suspend_last(void)285525cf1a30Sjl drmach_suspend_last(void)
285625cf1a30Sjl {
285725cf1a30Sjl }
285825cf1a30Sjl
285925cf1a30Sjl void
drmach_resume_first(void)286025cf1a30Sjl drmach_resume_first(void)
286125cf1a30Sjl {
286225cf1a30Sjl }
286325cf1a30Sjl
286425cf1a30Sjl /*
286525cf1a30Sjl * Log a DR sysevent.
286625cf1a30Sjl * Return value: 0 success, non-zero failure.
286725cf1a30Sjl */
286825cf1a30Sjl int
drmach_log_sysevent(int board,char * hint,int flag,int verbose)286925cf1a30Sjl drmach_log_sysevent(int board, char *hint, int flag, int verbose)
287025cf1a30Sjl {
287125cf1a30Sjl sysevent_t *ev;
287225cf1a30Sjl sysevent_id_t eid;
287325cf1a30Sjl int rv, km_flag;
287425cf1a30Sjl sysevent_value_t evnt_val;
287525cf1a30Sjl sysevent_attr_list_t *evnt_attr_list = NULL;
287625cf1a30Sjl char attach_pnt[MAXNAMELEN];
287725cf1a30Sjl
287825cf1a30Sjl km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
287925cf1a30Sjl attach_pnt[0] = '\0';
288025cf1a30Sjl if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
288125cf1a30Sjl rv = -1;
288225cf1a30Sjl goto logexit;
288325cf1a30Sjl }
2884b307f191Sbm if (verbose) {
288525cf1a30Sjl DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
2886e98fafb9Sjl attach_pnt, hint, flag, verbose);
2887b307f191Sbm }
288825cf1a30Sjl
288925cf1a30Sjl if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
2890e98fafb9Sjl SUNW_KERN_PUB"dr", km_flag)) == NULL) {
289125cf1a30Sjl rv = -2;
289225cf1a30Sjl goto logexit;
289325cf1a30Sjl }
289425cf1a30Sjl evnt_val.value_type = SE_DATA_TYPE_STRING;
289525cf1a30Sjl evnt_val.value.sv_string = attach_pnt;
2896e98fafb9Sjl if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
2897e98fafb9Sjl km_flag)) != 0)
289825cf1a30Sjl goto logexit;
289925cf1a30Sjl
290025cf1a30Sjl evnt_val.value_type = SE_DATA_TYPE_STRING;
290125cf1a30Sjl evnt_val.value.sv_string = hint;
2902e98fafb9Sjl if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
2903e98fafb9Sjl km_flag)) != 0) {
290425cf1a30Sjl sysevent_free_attr(evnt_attr_list);
290525cf1a30Sjl goto logexit;
290625cf1a30Sjl }
290725cf1a30Sjl
290825cf1a30Sjl (void) sysevent_attach_attributes(ev, evnt_attr_list);
290925cf1a30Sjl
291025cf1a30Sjl /*
291125cf1a30Sjl * Log the event but do not sleep waiting for its
291225cf1a30Sjl * delivery. This provides insulation from syseventd.
291325cf1a30Sjl */
291425cf1a30Sjl rv = log_sysevent(ev, SE_NOSLEEP, &eid);
291525cf1a30Sjl
291625cf1a30Sjl logexit:
291725cf1a30Sjl if (ev)
291825cf1a30Sjl sysevent_free(ev);
291925cf1a30Sjl if ((rv != 0) && verbose)
2920e98fafb9Sjl cmn_err(CE_WARN, "drmach_log_sysevent failed (rv %d) for %s "
2921e98fafb9Sjl " %s\n", rv, attach_pnt, hint);
292225cf1a30Sjl
292325cf1a30Sjl return (rv);
292425cf1a30Sjl }
292525cf1a30Sjl
292625cf1a30Sjl #define OPL_DR_STATUS_PROP "dr-status"
292725cf1a30Sjl
292825cf1a30Sjl static int
opl_check_dr_status()292925cf1a30Sjl opl_check_dr_status()
293025cf1a30Sjl {
293125cf1a30Sjl pnode_t node;
293225cf1a30Sjl int rtn, len;
293325cf1a30Sjl char *str;
293425cf1a30Sjl
293525cf1a30Sjl node = prom_rootnode();
293625cf1a30Sjl if (node == OBP_BADNODE) {
293725cf1a30Sjl return (1);
293825cf1a30Sjl }
293925cf1a30Sjl
294025cf1a30Sjl len = prom_getproplen(node, OPL_DR_STATUS_PROP);
294125cf1a30Sjl if (len == -1) {
294225cf1a30Sjl /*
294325cf1a30Sjl * dr-status doesn't exist when DR is activated and
294425cf1a30Sjl * any warning messages aren't needed.
294525cf1a30Sjl */
294625cf1a30Sjl return (1);
294725cf1a30Sjl }
294825cf1a30Sjl
294925cf1a30Sjl str = (char *)kmem_zalloc(len+1, KM_SLEEP);
295025cf1a30Sjl rtn = prom_getprop(node, OPL_DR_STATUS_PROP, str);
295125cf1a30Sjl kmem_free(str, len + 1);
295225cf1a30Sjl if (rtn == -1) {
295325cf1a30Sjl return (1);
295425cf1a30Sjl } else {
295525cf1a30Sjl return (0);
295625cf1a30Sjl }
295725cf1a30Sjl }
295825cf1a30Sjl
295925cf1a30Sjl /* we are allocating memlist from TLB locked pages to avoid tlbmisses */
296025cf1a30Sjl
296125cf1a30Sjl static struct memlist *
drmach_memlist_add_span(drmach_copy_rename_program_t * p,struct memlist * mlist,uint64_t base,uint64_t len)296225cf1a30Sjl drmach_memlist_add_span(drmach_copy_rename_program_t *p,
2963bbe1232eSToomas Soome struct memlist *mlist, uint64_t base, uint64_t len)
296425cf1a30Sjl {
296525cf1a30Sjl struct memlist *ml, *tl, *nl;
296625cf1a30Sjl
296725cf1a30Sjl if (len == 0ull)
296825cf1a30Sjl return (NULL);
296925cf1a30Sjl
297025cf1a30Sjl if (mlist == NULL) {
297125cf1a30Sjl mlist = p->free_mlist;
297225cf1a30Sjl if (mlist == NULL)
297325cf1a30Sjl return (NULL);
297456f33205SJonathan Adams p->free_mlist = mlist->ml_next;
297556f33205SJonathan Adams mlist->ml_address = base;
297656f33205SJonathan Adams mlist->ml_size = len;
297756f33205SJonathan Adams mlist->ml_next = mlist->ml_prev = NULL;
297825cf1a30Sjl
297925cf1a30Sjl return (mlist);
298025cf1a30Sjl }
298125cf1a30Sjl
298256f33205SJonathan Adams for (tl = ml = mlist; ml; tl = ml, ml = ml->ml_next) {
298356f33205SJonathan Adams if (base < ml->ml_address) {
298456f33205SJonathan Adams if ((base + len) < ml->ml_address) {
298525cf1a30Sjl nl = p->free_mlist;
298625cf1a30Sjl if (nl == NULL)
298725cf1a30Sjl return (NULL);
298856f33205SJonathan Adams p->free_mlist = nl->ml_next;
298956f33205SJonathan Adams nl->ml_address = base;
299056f33205SJonathan Adams nl->ml_size = len;
299156f33205SJonathan Adams nl->ml_next = ml;
299256f33205SJonathan Adams if ((nl->ml_prev = ml->ml_prev) != NULL)
299356f33205SJonathan Adams nl->ml_prev->ml_next = nl;
299456f33205SJonathan Adams ml->ml_prev = nl;
299525cf1a30Sjl if (mlist == ml)
299625cf1a30Sjl mlist = nl;
299725cf1a30Sjl } else {
299856f33205SJonathan Adams ml->ml_size = MAX((base + len),
299956f33205SJonathan Adams (ml->ml_address + ml->ml_size)) - base;
300056f33205SJonathan Adams ml->ml_address = base;
300125cf1a30Sjl }
300225cf1a30Sjl break;
300325cf1a30Sjl
300456f33205SJonathan Adams } else if (base <= (ml->ml_address + ml->ml_size)) {
300556f33205SJonathan Adams ml->ml_size =
300656f33205SJonathan Adams MAX((base + len), (ml->ml_address + ml->ml_size)) -
300756f33205SJonathan Adams MIN(ml->ml_address, base);
300856f33205SJonathan Adams ml->ml_address = MIN(ml->ml_address, base);
300925cf1a30Sjl break;
301025cf1a30Sjl }
301125cf1a30Sjl }
301225cf1a30Sjl if (ml == NULL) {
301325cf1a30Sjl nl = p->free_mlist;
301425cf1a30Sjl if (nl == NULL)
301525cf1a30Sjl return (NULL);
301656f33205SJonathan Adams p->free_mlist = nl->ml_next;
301756f33205SJonathan Adams nl->ml_address = base;
301856f33205SJonathan Adams nl->ml_size = len;
301956f33205SJonathan Adams nl->ml_next = NULL;
302056f33205SJonathan Adams nl->ml_prev = tl;
302156f33205SJonathan Adams tl->ml_next = nl;
302225cf1a30Sjl }
302325cf1a30Sjl
302425cf1a30Sjl return (mlist);
302525cf1a30Sjl }
302625cf1a30Sjl
302725cf1a30Sjl /*
302825cf1a30Sjl * The routine performs the necessary memory COPY and MC adr SWITCH.
302925cf1a30Sjl * Both operations MUST be at the same "level" so that the stack is
303025cf1a30Sjl * maintained correctly between the copy and switch. The switch
303125cf1a30Sjl * portion implements a caching mechanism to guarantee the code text
303225cf1a30Sjl * is cached prior to execution. This is to guard against possible
303325cf1a30Sjl * memory access while the MC adr's are being modified.
303425cf1a30Sjl *
303525cf1a30Sjl * IMPORTANT: The _drmach_copy_rename_end() function must immediately
303625cf1a30Sjl * follow drmach_copy_rename_prog__relocatable() so that the correct
303725cf1a30Sjl * "length" of the drmach_copy_rename_prog__relocatable can be
303825cf1a30Sjl * calculated. This routine MUST be a LEAF function, i.e. it can
303925cf1a30Sjl * make NO function calls, primarily for two reasons:
304025cf1a30Sjl *
304125cf1a30Sjl * 1. We must keep the stack consistent across the "switch".
304225cf1a30Sjl * 2. Function calls are compiled to relative offsets, and
304325cf1a30Sjl * we execute this function we'll be executing it from
304425cf1a30Sjl * a copied version in a different area of memory, thus
304525cf1a30Sjl * the relative offsets will be bogus.
304625cf1a30Sjl *
304725cf1a30Sjl * Moreover, it must have the "__relocatable" suffix to inform DTrace
304825cf1a30Sjl * providers (and anything else, for that matter) that this
304925cf1a30Sjl * function's text is manually relocated elsewhere before it is
305025cf1a30Sjl * executed. That is, it cannot be safely instrumented with any
305125cf1a30Sjl * methodology that is PC-relative.
305225cf1a30Sjl */
305325cf1a30Sjl
305425cf1a30Sjl /*
305525cf1a30Sjl * We multiply this to system_clock_frequency so we
305625cf1a30Sjl * are setting a delay of fmem_timeout second for
3057b307f191Sbm * the rename command.
3058b307f191Sbm *
3059b307f191Sbm * FMEM command itself should complete within 15 sec.
3060b307f191Sbm * We add 2 more sec to be conservative.
3061b307f191Sbm *
3062b307f191Sbm * Note that there is also a SCF BUSY bit checking
3063b307f191Sbm * in drmach_asm.s right before FMEM command is
3064b307f191Sbm * issued. XSCF sets the SCF BUSY bit when the
3065b307f191Sbm * other domain on the same PSB reboots and it
3066b307f191Sbm * will not be able to service the FMEM command
3067b307f191Sbm * within 15 sec. After setting the SCF BUSY
3068b307f191Sbm * bit, XSCF will wait a while before servicing
3069b307f191Sbm * other reboot command so there is no race
3070b307f191Sbm * condition.
307125cf1a30Sjl */
3072b307f191Sbm
307325cf1a30Sjl static int fmem_timeout = 17;
3074b307f191Sbm
3075b307f191Sbm /*
3076b307f191Sbm * The empirical data on some OPL system shows that
3077b307f191Sbm * we can copy 250 MB per second. We set it to
3078bbe1232eSToomas Soome * 80 MB to be conservative. In normal case,
3079b307f191Sbm * this timeout does not affect anything.
3080b307f191Sbm */
3081b307f191Sbm
3082b307f191Sbm static int min_copy_size_per_sec = 80 * 1024 * 1024;
3083b307f191Sbm
3084b307f191Sbm /*
3085b307f191Sbm * This is the timeout value for the xcall synchronization
3086b307f191Sbm * to get all the CPU ready to do the parallel copying.
3087b307f191Sbm * Even on a fully loaded system, 10 sec. should be long
3088b307f191Sbm * enough.
3089b307f191Sbm */
3090b307f191Sbm
3091b307f191Sbm static int cpu_xcall_delay = 10;
309225cf1a30Sjl int drmach_disable_mcopy = 0;
309325cf1a30Sjl
309468ac2337Sjl /*
309568ac2337Sjl * The following delay loop executes sleep instruction to yield the
309668ac2337Sjl * CPU to other strands. If this is not done, some strand will tie
309768ac2337Sjl * up the CPU in busy loops while the other strand cannot do useful
309868ac2337Sjl * work. The copy procedure will take a much longer time without this.
309968ac2337Sjl */
310025cf1a30Sjl #define DR_DELAY_IL(ms, freq) \
310125cf1a30Sjl { \
310225cf1a30Sjl uint64_t start; \
310325cf1a30Sjl uint64_t nstick; \
310425cf1a30Sjl volatile uint64_t now; \
310525cf1a30Sjl nstick = ((uint64_t)ms * freq)/1000; \
310625cf1a30Sjl start = drmach_get_stick_il(); \
310725cf1a30Sjl now = start; \
310825cf1a30Sjl while ((now - start) <= nstick) { \
310925cf1a30Sjl drmach_sleep_il(); \
311025cf1a30Sjl now = drmach_get_stick_il(); \
311125cf1a30Sjl } \
311225cf1a30Sjl }
311325cf1a30Sjl
31146534c6f0Swh /* Each loop is 2ms, timeout at 1000ms */
31156534c6f0Swh static int drmach_copy_rename_timeout = 500;
31166534c6f0Swh
311725cf1a30Sjl static int
drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t * prog,int cpuid)311825cf1a30Sjl drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog,
3119bbe1232eSToomas Soome int cpuid)
312025cf1a30Sjl {
312125cf1a30Sjl struct memlist *ml;
312225cf1a30Sjl register int rtn;
312325cf1a30Sjl int i;
312425cf1a30Sjl register uint64_t curr, limit;
312525cf1a30Sjl extern uint64_t drmach_get_stick_il();
312625cf1a30Sjl extern void membar_sync_il();
312725cf1a30Sjl extern void flush_instr_mem_il(void*);
312868ac2337Sjl extern void flush_windows_il(void);
312925cf1a30Sjl uint64_t copy_start;
313025cf1a30Sjl
313168ac2337Sjl /*
313268ac2337Sjl * flush_windows is moved here to make sure all
313368ac2337Sjl * registers used in the callers are flushed to
313468ac2337Sjl * memory before the copy.
313568ac2337Sjl *
313668ac2337Sjl * If flush_windows() is called too early in the
313768ac2337Sjl * calling function, the compiler might put some
313868ac2337Sjl * data in the local registers after flush_windows().
313968ac2337Sjl * After FMA, if there is any fill trap, the registers
314068ac2337Sjl * will contain stale data.
314168ac2337Sjl */
314268ac2337Sjl
314368ac2337Sjl flush_windows_il();
314468ac2337Sjl
314525cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_COPY_READY;
314625cf1a30Sjl membar_sync_il();
314725cf1a30Sjl
314825cf1a30Sjl if (prog->data->cpuid == cpuid) {
314925cf1a30Sjl limit = drmach_get_stick_il();
3150b307f191Sbm limit += cpu_xcall_delay * system_clock_freq;
315125cf1a30Sjl for (i = 0; i < NCPU; i++) {
315225cf1a30Sjl if (CPU_IN_SET(prog->data->cpu_slave_set, i)) {
3153e98fafb9Sjl /* wait for all CPU's to be ready */
3154e98fafb9Sjl for (;;) {
3155e98fafb9Sjl if (prog->critical->stat[i] ==
3156e98fafb9Sjl FMEM_LOOP_COPY_READY) {
3157e98fafb9Sjl break;
3158e98fafb9Sjl }
3159e98fafb9Sjl DR_DELAY_IL(1, prog->data->stick_freq);
3160e98fafb9Sjl }
3161e98fafb9Sjl curr = drmach_get_stick_il();
3162e98fafb9Sjl if (curr > limit) {
3163e98fafb9Sjl prog->data->fmem_status.error =
3164e98fafb9Sjl EOPL_FMEM_XC_TIMEOUT;
3165e98fafb9Sjl return (EOPL_FMEM_XC_TIMEOUT);
316625cf1a30Sjl }
316725cf1a30Sjl }
316825cf1a30Sjl }
316925cf1a30Sjl prog->data->fmem_status.stat = FMEM_LOOP_COPY_READY;
317025cf1a30Sjl membar_sync_il();
317125cf1a30Sjl copy_start = drmach_get_stick_il();
317225cf1a30Sjl } else {
317325cf1a30Sjl for (;;) {
317425cf1a30Sjl if (prog->data->fmem_status.stat ==
3175e98fafb9Sjl FMEM_LOOP_COPY_READY) {
317625cf1a30Sjl break;
317725cf1a30Sjl }
317825cf1a30Sjl if (prog->data->fmem_status.error) {
3179e98fafb9Sjl prog->data->error[cpuid] = EOPL_FMEM_TERMINATE;
3180b307f191Sbm return (EOPL_FMEM_TERMINATE);
318125cf1a30Sjl }
318268ac2337Sjl DR_DELAY_IL(1, prog->data->stick_freq);
318325cf1a30Sjl }
318425cf1a30Sjl }
318525cf1a30Sjl
318625cf1a30Sjl /*
318725cf1a30Sjl * DO COPY.
318825cf1a30Sjl */
318925cf1a30Sjl if (CPU_IN_SET(prog->data->cpu_copy_set, cpuid)) {
319056f33205SJonathan Adams for (ml = prog->data->cpu_ml[cpuid]; ml; ml = ml->ml_next) {
3191e98fafb9Sjl uint64_t s_pa, t_pa;
3192e98fafb9Sjl uint64_t nbytes;
3193e98fafb9Sjl
319456f33205SJonathan Adams s_pa = prog->data->s_copybasepa + ml->ml_address;
319556f33205SJonathan Adams t_pa = prog->data->t_copybasepa + ml->ml_address;
319656f33205SJonathan Adams nbytes = ml->ml_size;
3197e98fafb9Sjl
3198e98fafb9Sjl while (nbytes != 0ull) {
3199e98fafb9Sjl /*
3200e98fafb9Sjl * If the master has detected error, we just
3201e98fafb9Sjl * bail out
3202e98fafb9Sjl */
3203e98fafb9Sjl if (prog->data->fmem_status.error !=
3204e98fafb9Sjl ESBD_NOERROR) {
3205e98fafb9Sjl prog->data->error[cpuid] =
3206e98fafb9Sjl EOPL_FMEM_TERMINATE;
3207e98fafb9Sjl return (EOPL_FMEM_TERMINATE);
3208e98fafb9Sjl }
3209e98fafb9Sjl /*
3210e98fafb9Sjl * This copy does NOT use an ASI
3211e98fafb9Sjl * that avoids the Ecache, therefore
3212e98fafb9Sjl * the dst_pa addresses may remain
3213e98fafb9Sjl * in our Ecache after the dst_pa
3214e98fafb9Sjl * has been removed from the system.
3215e98fafb9Sjl * A subsequent write-back to memory
3216e98fafb9Sjl * will cause an ARB-stop because the
3217e98fafb9Sjl * physical address no longer exists
3218e98fafb9Sjl * in the system. Therefore we must
3219e98fafb9Sjl * flush out local Ecache after we
3220e98fafb9Sjl * finish the copy.
3221e98fafb9Sjl */
3222e98fafb9Sjl
3223e98fafb9Sjl /* copy 32 bytes at src_pa to dst_pa */
3224e98fafb9Sjl bcopy32_il(s_pa, t_pa);
3225e98fafb9Sjl
3226e98fafb9Sjl /*
3227e98fafb9Sjl * increment the counter to signal that we are
3228e98fafb9Sjl * alive
3229e98fafb9Sjl */
3230e98fafb9Sjl prog->stat->nbytes[cpuid] += 32;
3231e98fafb9Sjl
3232e98fafb9Sjl /* increment by 32 bytes */
3233e98fafb9Sjl s_pa += (4 * sizeof (uint64_t));
3234e98fafb9Sjl t_pa += (4 * sizeof (uint64_t));
3235e98fafb9Sjl
3236e98fafb9Sjl /* decrement by 32 bytes */
3237e98fafb9Sjl nbytes -= (4 * sizeof (uint64_t));
323825cf1a30Sjl }
323925cf1a30Sjl }
3240e98fafb9Sjl prog->critical->stat[cpuid] = FMEM_LOOP_COPY_DONE;
3241e98fafb9Sjl membar_sync_il();
324225cf1a30Sjl }
324325cf1a30Sjl
324425cf1a30Sjl /*
324525cf1a30Sjl * Since bcopy32_il() does NOT use an ASI to bypass
324625cf1a30Sjl * the Ecache, we need to flush our Ecache after
324725cf1a30Sjl * the copy is complete.
324825cf1a30Sjl */
324925cf1a30Sjl flush_cache_il();
325025cf1a30Sjl
325125cf1a30Sjl /*
325225cf1a30Sjl * drmach_fmem_exec_script()
325325cf1a30Sjl */
325425cf1a30Sjl if (prog->data->cpuid == cpuid) {
325525cf1a30Sjl uint64_t last, now;
325625cf1a30Sjl
325725cf1a30Sjl limit = copy_start + prog->data->copy_delay;
325825cf1a30Sjl for (i = 0; i < NCPU; i++) {
3259e98fafb9Sjl if (!CPU_IN_SET(prog->data->cpu_slave_set, i))
3260e98fafb9Sjl continue;
3261e98fafb9Sjl
3262e98fafb9Sjl for (;;) {
3263e98fafb9Sjl /*
3264e98fafb9Sjl * we get FMEM_LOOP_FMEM_READY in
3265e98fafb9Sjl * normal case
3266e98fafb9Sjl */
326725cf1a30Sjl if (prog->critical->stat[i] ==
3268e98fafb9Sjl FMEM_LOOP_FMEM_READY) {
326925cf1a30Sjl break;
327025cf1a30Sjl }
327125cf1a30Sjl /* got error traps */
3272b307f191Sbm if (prog->data->error[i] ==
3273e98fafb9Sjl EOPL_FMEM_COPY_ERROR) {
327425cf1a30Sjl prog->data->fmem_status.error =
3275e98fafb9Sjl EOPL_FMEM_COPY_ERROR;
3276b307f191Sbm return (EOPL_FMEM_COPY_ERROR);
327725cf1a30Sjl }
3278e98fafb9Sjl /*
3279e98fafb9Sjl * if we have not reached limit, wait
3280e98fafb9Sjl * more
3281e98fafb9Sjl */
328225cf1a30Sjl curr = drmach_get_stick_il();
328325cf1a30Sjl if (curr <= limit)
328425cf1a30Sjl continue;
328525cf1a30Sjl
328625cf1a30Sjl prog->data->slowest_cpuid = i;
3287e98fafb9Sjl prog->data->copy_wait_time = curr - copy_start;
328825cf1a30Sjl
328925cf1a30Sjl /* now check if slave is alive */
329025cf1a30Sjl last = prog->stat->nbytes[i];
329125cf1a30Sjl
329225cf1a30Sjl DR_DELAY_IL(1, prog->data->stick_freq);
329325cf1a30Sjl
329425cf1a30Sjl now = prog->stat->nbytes[i];
329525cf1a30Sjl if (now <= last) {
3296e98fafb9Sjl /*
3297e98fafb9Sjl * no progress, perhaps just
3298e98fafb9Sjl * finished
3299e98fafb9Sjl */
330025cf1a30Sjl DR_DELAY_IL(1, prog->data->stick_freq);
330125cf1a30Sjl if (prog->critical->stat[i] ==
3302e98fafb9Sjl FMEM_LOOP_FMEM_READY)
330325cf1a30Sjl break;
330425cf1a30Sjl /* copy error */
3305b307f191Sbm if (prog->data->error[i] ==
3306e98fafb9Sjl EOPL_FMEM_COPY_ERROR) {
3307e98fafb9Sjl prog->data-> fmem_status.error =
3308e98fafb9Sjl EOPL_FMEM_COPY_ERROR;
3309b307f191Sbm return (EOPL_FMEM_COPY_ERROR);
331025cf1a30Sjl }
33116534c6f0Swh
33126534c6f0Swh prog->data->copy_rename_count++;
33136534c6f0Swh if (prog->data->copy_rename_count
33146534c6f0Swh < drmach_copy_rename_timeout) {
33156534c6f0Swh continue;
33166534c6f0Swh } else {
33176534c6f0Swh prog->data->fmem_status.error =
33186534c6f0Swh EOPL_FMEM_COPY_TIMEOUT;
33196534c6f0Swh return (EOPL_FMEM_COPY_TIMEOUT);
33206534c6f0Swh }
332125cf1a30Sjl }
332225cf1a30Sjl }
332325cf1a30Sjl }
3324b307f191Sbm
332525cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_FMEM_READY;
332625cf1a30Sjl prog->data->fmem_status.stat = FMEM_LOOP_FMEM_READY;
332725cf1a30Sjl
332825cf1a30Sjl membar_sync_il();
332925cf1a30Sjl flush_instr_mem_il((void*) (prog->critical));
333025cf1a30Sjl /*
333125cf1a30Sjl * drmach_fmem_exec_script()
333225cf1a30Sjl */
333325cf1a30Sjl rtn = prog->critical->fmem((void *)prog->critical, PAGESIZE);
333425cf1a30Sjl return (rtn);
333525cf1a30Sjl } else {
333625cf1a30Sjl flush_instr_mem_il((void*) (prog->critical));
333725cf1a30Sjl /*
333825cf1a30Sjl * drmach_fmem_loop_script()
333925cf1a30Sjl */
3340e98fafb9Sjl rtn = prog->critical->loop((void *)(prog->critical), PAGESIZE,
3341e98fafb9Sjl (void *)&(prog->critical->stat[cpuid]));
334225cf1a30Sjl prog->data->error[cpuid] = rtn;
334325cf1a30Sjl /* slave thread does not care the rv */
334425cf1a30Sjl return (0);
334525cf1a30Sjl }
334625cf1a30Sjl }
334725cf1a30Sjl
334825cf1a30Sjl static void
drmach_copy_rename_end(void)334925cf1a30Sjl drmach_copy_rename_end(void)
335025cf1a30Sjl {
335125cf1a30Sjl /*
335225cf1a30Sjl * IMPORTANT: This function's location MUST be located immediately
335325cf1a30Sjl * following drmach_copy_rename_prog__relocatable to
335425cf1a30Sjl * accurately estimate its size. Note that this assumes
335525cf1a30Sjl * the compiler keeps these functions in the order in
335625cf1a30Sjl * which they appear :-o
335725cf1a30Sjl */
335825cf1a30Sjl }
335925cf1a30Sjl
336025cf1a30Sjl
33616534c6f0Swh static int
drmach_setup_memlist(drmach_copy_rename_program_t * p)336225cf1a30Sjl drmach_setup_memlist(drmach_copy_rename_program_t *p)
336325cf1a30Sjl {
336425cf1a30Sjl struct memlist *ml;
336525cf1a30Sjl caddr_t buf;
33666534c6f0Swh int nbytes, s, n_elements;
336725cf1a30Sjl
336825cf1a30Sjl nbytes = PAGESIZE;
33696534c6f0Swh n_elements = 0;
337025cf1a30Sjl s = roundup(sizeof (struct memlist), sizeof (void *));
337125cf1a30Sjl p->free_mlist = NULL;
337225cf1a30Sjl buf = p->memlist_buffer;
337325cf1a30Sjl while (nbytes >= sizeof (struct memlist)) {
337425cf1a30Sjl ml = (struct memlist *)buf;
337556f33205SJonathan Adams ml->ml_next = p->free_mlist;
337625cf1a30Sjl p->free_mlist = ml;
337725cf1a30Sjl buf += s;
33786534c6f0Swh n_elements++;
337925cf1a30Sjl nbytes -= s;
338025cf1a30Sjl }
33816534c6f0Swh return (n_elements);
338225cf1a30Sjl }
338325cf1a30Sjl
338468ac2337Sjl static void
drmach_lock_critical(caddr_t va,caddr_t new_va)338568ac2337Sjl drmach_lock_critical(caddr_t va, caddr_t new_va)
338668ac2337Sjl {
338768ac2337Sjl tte_t tte;
338868ac2337Sjl int i;
338968ac2337Sjl
339068ac2337Sjl kpreempt_disable();
339168ac2337Sjl
339268ac2337Sjl for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
339368ac2337Sjl vtag_flushpage(new_va, (uint64_t)ksfmmup);
3394e98fafb9Sjl sfmmu_memtte(&tte, va_to_pfn(va), PROC_DATA|HAT_NOSYNC, TTE8K);
339568ac2337Sjl tte.tte_intlo |= TTE_LCK_INT;
339668ac2337Sjl sfmmu_dtlb_ld_kva(new_va, &tte);
339768ac2337Sjl sfmmu_itlb_ld_kva(new_va, &tte);
339868ac2337Sjl va += PAGESIZE;
339968ac2337Sjl new_va += PAGESIZE;
340068ac2337Sjl }
340168ac2337Sjl }
340268ac2337Sjl
340368ac2337Sjl static void
drmach_unlock_critical(caddr_t va)340468ac2337Sjl drmach_unlock_critical(caddr_t va)
340568ac2337Sjl {
340668ac2337Sjl int i;
340768ac2337Sjl
340868ac2337Sjl for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
340968ac2337Sjl vtag_flushpage(va, (uint64_t)ksfmmup);
341068ac2337Sjl va += PAGESIZE;
341168ac2337Sjl }
341268ac2337Sjl
341368ac2337Sjl kpreempt_enable();
341468ac2337Sjl }
341568ac2337Sjl
341625cf1a30Sjl sbd_error_t *
drmach_copy_rename_init(drmachid_t t_id,drmachid_t s_id,struct memlist * c_ml,drmachid_t * pgm_id)341725cf1a30Sjl drmach_copy_rename_init(drmachid_t t_id, drmachid_t s_id,
3418bbe1232eSToomas Soome struct memlist *c_ml, drmachid_t *pgm_id)
341925cf1a30Sjl {
342025cf1a30Sjl drmach_mem_t *s_mem;
342125cf1a30Sjl drmach_mem_t *t_mem;
342225cf1a30Sjl struct memlist *x_ml;
342325cf1a30Sjl uint64_t s_copybasepa, t_copybasepa;
342425cf1a30Sjl uint_t len;
342525cf1a30Sjl caddr_t bp, wp;
34266534c6f0Swh int s_bd, t_bd, cpuid, active_cpus, i;
34276534c6f0Swh int max_elms, mlist_size, rv;
34286534c6f0Swh uint64_t c_addr;
34296534c6f0Swh size_t c_size, copy_sz, sz;
34306534c6f0Swh extern void drmach_fmem_loop_script();
34316534c6f0Swh extern void drmach_fmem_loop_script_rtn();
34326534c6f0Swh extern int drmach_fmem_exec_script();
34336534c6f0Swh extern void drmach_fmem_exec_script_end();
343425cf1a30Sjl sbd_error_t *err;
343568ac2337Sjl drmach_copy_rename_program_t *prog = NULL;
343668ac2337Sjl drmach_copy_rename_program_t *prog_kmem = NULL;
343725cf1a30Sjl void (*mc_suspend)(void);
343825cf1a30Sjl void (*mc_resume)(void);
343925cf1a30Sjl int (*scf_fmem_start)(int, int);
344025cf1a30Sjl int (*scf_fmem_end)(void);
344125cf1a30Sjl int (*scf_fmem_cancel)(void);
344268ac2337Sjl uint64_t (*scf_get_base_addr)(void);
344325cf1a30Sjl
344425cf1a30Sjl if (!DRMACH_IS_MEM_ID(s_id))
344525cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
344625cf1a30Sjl if (!DRMACH_IS_MEM_ID(t_id))
344725cf1a30Sjl return (drerr_new(0, EOPL_INAPPROP, NULL));
344825cf1a30Sjl
344925cf1a30Sjl for (i = 0; i < NCPU; i++) {
345025cf1a30Sjl int lsb_id, onb_core_num, strand_id;
345125cf1a30Sjl drmach_board_t *bp;
345225cf1a30Sjl
345325cf1a30Sjl /*
345425cf1a30Sjl * this kind of CPU will spin in cache
345525cf1a30Sjl */
345625cf1a30Sjl if (CPU_IN_SET(cpu_ready_set, i))
345725cf1a30Sjl continue;
345825cf1a30Sjl
345925cf1a30Sjl /*
346025cf1a30Sjl * Now check for any inactive CPU's that
346125cf1a30Sjl * have been hotadded. This can only occur in
346225cf1a30Sjl * error condition in drmach_cpu_poweron().
346325cf1a30Sjl */
346425cf1a30Sjl lsb_id = LSB_ID(i);
346525cf1a30Sjl onb_core_num = ON_BOARD_CORE_NUM(i);
346625cf1a30Sjl strand_id = STRAND_ID(i);
346725cf1a30Sjl bp = drmach_get_board_by_bnum(lsb_id);
346825cf1a30Sjl if (bp == NULL)
346925cf1a30Sjl continue;
347025cf1a30Sjl if (bp->cores[onb_core_num].core_hotadded &
347125cf1a30Sjl (1 << strand_id)) {
3472e98fafb9Sjl if (!(bp->cores[onb_core_num].core_started &
3473e98fafb9Sjl (1 << strand_id))) {
3474e98fafb9Sjl return (drerr_new(1, EOPL_CPU_STATE, NULL));
3475e98fafb9Sjl }
347625cf1a30Sjl }
347725cf1a30Sjl }
347825cf1a30Sjl
347925cf1a30Sjl mc_suspend = (void (*)(void))
348025cf1a30Sjl modgetsymvalue("opl_mc_suspend", 0);
348125cf1a30Sjl mc_resume = (void (*)(void))
348225cf1a30Sjl modgetsymvalue("opl_mc_resume", 0);
348325cf1a30Sjl
348425cf1a30Sjl if (mc_suspend == NULL || mc_resume == NULL) {
34858eafe49bSbm return (drerr_new(1, EOPL_MC_OPL, NULL));
348625cf1a30Sjl }
348725cf1a30Sjl
348825cf1a30Sjl scf_fmem_start = (int (*)(int, int))
348925cf1a30Sjl modgetsymvalue("scf_fmem_start", 0);
349025cf1a30Sjl if (scf_fmem_start == NULL) {
34918eafe49bSbm return (drerr_new(1, EOPL_SCF_FMEM, NULL));
349225cf1a30Sjl }
349325cf1a30Sjl scf_fmem_end = (int (*)(void))
349425cf1a30Sjl modgetsymvalue("scf_fmem_end", 0);
349525cf1a30Sjl if (scf_fmem_end == NULL) {
34968eafe49bSbm return (drerr_new(1, EOPL_SCF_FMEM, NULL));
349725cf1a30Sjl }
349825cf1a30Sjl scf_fmem_cancel = (int (*)(void))
349925cf1a30Sjl modgetsymvalue("scf_fmem_cancel", 0);
350025cf1a30Sjl if (scf_fmem_cancel == NULL) {
35018eafe49bSbm return (drerr_new(1, EOPL_SCF_FMEM, NULL));
350268ac2337Sjl }
350368ac2337Sjl scf_get_base_addr = (uint64_t (*)(void))
350468ac2337Sjl modgetsymvalue("scf_get_base_addr", 0);
350568ac2337Sjl if (scf_get_base_addr == NULL) {
35068eafe49bSbm return (drerr_new(1, EOPL_SCF_FMEM, NULL));
350725cf1a30Sjl }
350825cf1a30Sjl s_mem = s_id;
350925cf1a30Sjl t_mem = t_id;
351025cf1a30Sjl
351125cf1a30Sjl s_bd = s_mem->dev.bp->bnum;
351225cf1a30Sjl t_bd = t_mem->dev.bp->bnum;
351325cf1a30Sjl
351425cf1a30Sjl /* calculate source and target base pa */
351525cf1a30Sjl
351625cf1a30Sjl s_copybasepa = s_mem->slice_base;
351725cf1a30Sjl t_copybasepa = t_mem->slice_base;
351825cf1a30Sjl
351925cf1a30Sjl /* adjust copy memlist addresses to be relative to copy base pa */
352025cf1a30Sjl x_ml = c_ml;
35216534c6f0Swh mlist_size = 0;
352225cf1a30Sjl while (x_ml != NULL) {
352356f33205SJonathan Adams x_ml->ml_address -= s_copybasepa;
352456f33205SJonathan Adams x_ml = x_ml->ml_next;
35256534c6f0Swh mlist_size++;
352625cf1a30Sjl }
352725cf1a30Sjl
352825cf1a30Sjl /*
352925cf1a30Sjl * bp will be page aligned, since we're calling
353025cf1a30Sjl * kmem_zalloc() with an exact multiple of PAGESIZE.
353125cf1a30Sjl */
353225cf1a30Sjl
353368ac2337Sjl prog_kmem = (drmach_copy_rename_program_t *)kmem_zalloc(
3534e98fafb9Sjl DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, KM_SLEEP);
353568ac2337Sjl
353668ac2337Sjl prog_kmem->prog = prog_kmem;
353768ac2337Sjl
353868ac2337Sjl /*
353968ac2337Sjl * To avoid MTLB hit, we allocate a new VM space and remap
354068ac2337Sjl * the kmem_alloc buffer to that address. This solves
354168ac2337Sjl * 2 problems we found:
354268ac2337Sjl * - the kmem_alloc buffer can be just a chunk inside
354368ac2337Sjl * a much larger, e.g. 4MB buffer and MTLB will occur
354468ac2337Sjl * if there are both a 4MB and a 8K TLB mapping to
354568ac2337Sjl * the same VA range.
354668ac2337Sjl * - the kmem mapping got dropped into the TLB by other
354768ac2337Sjl * strands, unintentionally.
354868ac2337Sjl * Note that the pointers like data, critical, memlist_buffer,
354968ac2337Sjl * and stat inside the copy rename structure are mapped to this
355068ac2337Sjl * alternate VM space so we must make sure we lock the TLB mapping
355168ac2337Sjl * whenever we access data pointed to by these pointers.
355268ac2337Sjl */
355368ac2337Sjl
355468ac2337Sjl prog = prog_kmem->locked_prog = vmem_alloc(heap_arena,
3555e98fafb9Sjl DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, VM_SLEEP);
355668ac2337Sjl wp = bp = (caddr_t)prog;
355768ac2337Sjl
355868ac2337Sjl /* Now remap prog_kmem to prog */
355968ac2337Sjl drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
356068ac2337Sjl
356168ac2337Sjl /* All pointers in prog are based on the alternate mapping */
356225cf1a30Sjl prog->data = (drmach_copy_rename_data_t *)roundup(((uint64_t)prog +
3563e98fafb9Sjl sizeof (drmach_copy_rename_program_t)), sizeof (void *));
356425cf1a30Sjl
356525cf1a30Sjl ASSERT(((uint64_t)prog->data + sizeof (drmach_copy_rename_data_t))
3566e98fafb9Sjl <= ((uint64_t)prog + PAGESIZE));
356725cf1a30Sjl
356825cf1a30Sjl prog->critical = (drmach_copy_rename_critical_t *)
3569e98fafb9Sjl (wp + DRMACH_FMEM_CRITICAL_PAGE * PAGESIZE);
357025cf1a30Sjl
3571e98fafb9Sjl prog->memlist_buffer = (caddr_t)(wp + DRMACH_FMEM_MLIST_PAGE *
3572e98fafb9Sjl PAGESIZE);
357325cf1a30Sjl
3574e98fafb9Sjl prog->stat = (drmach_cr_stat_t *)(wp + DRMACH_FMEM_STAT_PAGE *
3575e98fafb9Sjl PAGESIZE);
357625cf1a30Sjl
357725cf1a30Sjl /* LINTED */
3578e98fafb9Sjl ASSERT(sizeof (drmach_cr_stat_t) <= ((DRMACH_FMEM_LOCKED_PAGES -
3579e98fafb9Sjl DRMACH_FMEM_STAT_PAGE) * PAGESIZE));
358025cf1a30Sjl
358125cf1a30Sjl prog->critical->scf_reg_base = (uint64_t)-1;
358225cf1a30Sjl prog->critical->scf_td[0] = (s_bd & 0xff);
358325cf1a30Sjl prog->critical->scf_td[1] = (t_bd & 0xff);
358425cf1a30Sjl for (i = 2; i < 15; i++) {
358525cf1a30Sjl prog->critical->scf_td[i] = 0;
358625cf1a30Sjl }
358725cf1a30Sjl prog->critical->scf_td[15] = ((0xaa + s_bd + t_bd) & 0xff);
358825cf1a30Sjl
358925cf1a30Sjl bp = (caddr_t)prog->critical;
359025cf1a30Sjl len = sizeof (drmach_copy_rename_critical_t);
359125cf1a30Sjl wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
359225cf1a30Sjl
359325cf1a30Sjl len = (uint_t)((ulong_t)drmach_copy_rename_end -
3594e98fafb9Sjl (ulong_t)drmach_copy_rename_prog__relocatable);
359525cf1a30Sjl
359625cf1a30Sjl /*
359725cf1a30Sjl * We always leave 1K nop's to prevent the processor from
359825cf1a30Sjl * speculative execution that causes memory access
359925cf1a30Sjl */
360025cf1a30Sjl wp = wp + len + 1024;
360125cf1a30Sjl
360225cf1a30Sjl len = (uint_t)((ulong_t)drmach_fmem_exec_script_end -
3603e98fafb9Sjl (ulong_t)drmach_fmem_exec_script);
360425cf1a30Sjl /* this is the entry point of the loop script */
360525cf1a30Sjl wp = wp + len + 1024;
360625cf1a30Sjl
360725cf1a30Sjl len = (uint_t)((ulong_t)drmach_fmem_exec_script -
3608e98fafb9Sjl (ulong_t)drmach_fmem_loop_script);
360925cf1a30Sjl wp = wp + len + 1024;
361025cf1a30Sjl
361125cf1a30Sjl /* now we make sure there is 1K extra */
361225cf1a30Sjl
361325cf1a30Sjl if ((wp - bp) > PAGESIZE) {
36148eafe49bSbm err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
361568ac2337Sjl goto out;
361625cf1a30Sjl }
361725cf1a30Sjl
361825cf1a30Sjl bp = (caddr_t)prog->critical;
361925cf1a30Sjl len = sizeof (drmach_copy_rename_critical_t);
362025cf1a30Sjl wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
362125cf1a30Sjl
362225cf1a30Sjl prog->critical->run = (int (*)())(wp);
362325cf1a30Sjl len = (uint_t)((ulong_t)drmach_copy_rename_end -
3624e98fafb9Sjl (ulong_t)drmach_copy_rename_prog__relocatable);
362525cf1a30Sjl
362625cf1a30Sjl bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len);
362725cf1a30Sjl
362825cf1a30Sjl wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
362925cf1a30Sjl
363025cf1a30Sjl prog->critical->fmem = (int (*)())(wp);
363125cf1a30Sjl len = (int)((ulong_t)drmach_fmem_exec_script_end -
3632e98fafb9Sjl (ulong_t)drmach_fmem_exec_script);
363325cf1a30Sjl bcopy((caddr_t)drmach_fmem_exec_script, wp, len);
363425cf1a30Sjl
363525cf1a30Sjl len = (int)((ulong_t)drmach_fmem_exec_script_end -
3636e98fafb9Sjl (ulong_t)drmach_fmem_exec_script);
363725cf1a30Sjl wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
363825cf1a30Sjl
363925cf1a30Sjl prog->critical->loop = (int (*)())(wp);
364025cf1a30Sjl len = (int)((ulong_t)drmach_fmem_exec_script -
3641e98fafb9Sjl (ulong_t)drmach_fmem_loop_script);
364225cf1a30Sjl bcopy((caddr_t)drmach_fmem_loop_script, (void *)wp, len);
364325cf1a30Sjl len = (int)((ulong_t)drmach_fmem_loop_script_rtn-
3644e98fafb9Sjl (ulong_t)drmach_fmem_loop_script);
364525cf1a30Sjl prog->critical->loop_rtn = (void (*)()) (wp+len);
364625cf1a30Sjl
3647b307f191Sbm prog->data->fmem_status.error = ESBD_NOERROR;
3648b307f191Sbm
364925cf1a30Sjl /* now we are committed, call SCF, soft suspend mac patrol */
365025cf1a30Sjl if ((*scf_fmem_start)(s_bd, t_bd)) {
36518eafe49bSbm err = drerr_new(1, EOPL_SCF_FMEM_START, NULL);
365268ac2337Sjl goto out;
365325cf1a30Sjl }
365425cf1a30Sjl prog->data->scf_fmem_end = scf_fmem_end;
365525cf1a30Sjl prog->data->scf_fmem_cancel = scf_fmem_cancel;
365668ac2337Sjl prog->data->scf_get_base_addr = scf_get_base_addr;
365725cf1a30Sjl prog->data->fmem_status.op |= OPL_FMEM_SCF_START;
3658b307f191Sbm
365925cf1a30Sjl /* soft suspend mac patrol */
366025cf1a30Sjl (*mc_suspend)();
366125cf1a30Sjl prog->data->fmem_status.op |= OPL_FMEM_MC_SUSPEND;
366225cf1a30Sjl prog->data->mc_resume = mc_resume;
366325cf1a30Sjl
366425cf1a30Sjl prog->critical->inst_loop_ret =
3665e98fafb9Sjl *(uint64_t *)(prog->critical->loop_rtn);
366625cf1a30Sjl
366725cf1a30Sjl /*
366825cf1a30Sjl * 0x30800000 is op code "ba,a +0"
366925cf1a30Sjl */
367025cf1a30Sjl
367125cf1a30Sjl *(uint_t *)(prog->critical->loop_rtn) = (uint_t)(0x30800000);
367225cf1a30Sjl
367325cf1a30Sjl /*
367425cf1a30Sjl * set the value of SCF FMEM TIMEOUT
367525cf1a30Sjl */
367625cf1a30Sjl prog->critical->delay = fmem_timeout * system_clock_freq;
367725cf1a30Sjl
367825cf1a30Sjl prog->data->s_mem = (drmachid_t)s_mem;
367925cf1a30Sjl prog->data->t_mem = (drmachid_t)t_mem;
368025cf1a30Sjl
368125cf1a30Sjl cpuid = CPU->cpu_id;
368225cf1a30Sjl prog->data->cpuid = cpuid;
368325cf1a30Sjl prog->data->cpu_ready_set = cpu_ready_set;
368425cf1a30Sjl prog->data->cpu_slave_set = cpu_ready_set;
368525cf1a30Sjl prog->data->slowest_cpuid = (processorid_t)-1;
368625cf1a30Sjl prog->data->copy_wait_time = 0;
36876534c6f0Swh prog->data->copy_rename_count = 0;
368825cf1a30Sjl CPUSET_DEL(prog->data->cpu_slave_set, cpuid);
368925cf1a30Sjl
369025cf1a30Sjl for (i = 0; i < NCPU; i++) {
369125cf1a30Sjl prog->data->cpu_ml[i] = NULL;
369225cf1a30Sjl }
369325cf1a30Sjl
36946534c6f0Swh /*
36956534c6f0Swh * max_elms - max number of memlist structures that
3696bbe1232eSToomas Soome * may be allocated for the CPU memory list.
36976534c6f0Swh * If there are too many memory span (because
36986534c6f0Swh * of fragmentation) than number of memlist
36996534c6f0Swh * available, we should return error.
37006534c6f0Swh */
37016534c6f0Swh max_elms = drmach_setup_memlist(prog);
37026534c6f0Swh if (max_elms < mlist_size) {
37036534c6f0Swh err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
37046534c6f0Swh goto err_out;
37056534c6f0Swh }
37066534c6f0Swh
370725cf1a30Sjl active_cpus = 0;
370825cf1a30Sjl if (drmach_disable_mcopy) {
370925cf1a30Sjl active_cpus = 1;
371025cf1a30Sjl CPUSET_ADD(prog->data->cpu_copy_set, cpuid);
371125cf1a30Sjl } else {
37126534c6f0Swh int max_cpu_num;
37136534c6f0Swh /*
37146534c6f0Swh * The parallel copy procedure is going to split some
37156534c6f0Swh * of the elements of the original memory copy list.
37166534c6f0Swh * The number of added elements can be up to
37176534c6f0Swh * (max_cpu_num - 1). It means that max_cpu_num
37186534c6f0Swh * should satisfy the following condition:
37196534c6f0Swh * (max_cpu_num - 1) + mlist_size <= max_elms.
37206534c6f0Swh */
37216534c6f0Swh max_cpu_num = max_elms - mlist_size + 1;
37226534c6f0Swh
372325cf1a30Sjl for (i = 0; i < NCPU; i++) {
372425cf1a30Sjl if (CPU_IN_SET(cpu_ready_set, i) &&
3725e98fafb9Sjl CPU_ACTIVE(cpu[i])) {
37266534c6f0Swh /*
37276534c6f0Swh * To reduce the level-2 cache contention only
37286534c6f0Swh * one strand per core will participate
37296534c6f0Swh * in the copy. If the strand with even cpu_id
37306534c6f0Swh * number is present in the ready set, we will
37316534c6f0Swh * include this strand in the copy set. If it
37326534c6f0Swh * is not present in the ready set, we check for
37336534c6f0Swh * the strand with the consecutive odd cpu_id
37346534c6f0Swh * and include it, provided that it is
37356534c6f0Swh * present in the ready set.
37366534c6f0Swh */
37376534c6f0Swh if (!(i & 0x1) ||
37386534c6f0Swh !CPU_IN_SET(prog->data->cpu_copy_set,
37396534c6f0Swh i - 1)) {
37406534c6f0Swh CPUSET_ADD(prog->data->cpu_copy_set, i);
37416534c6f0Swh active_cpus++;
37426534c6f0Swh /*
37436534c6f0Swh * We cannot have more than
37446534c6f0Swh * max_cpu_num CPUs in the copy
37456534c6f0Swh * set, because each CPU has to
37466534c6f0Swh * have at least one element
37476534c6f0Swh * long memory copy list.
37486534c6f0Swh */
37496534c6f0Swh if (active_cpus >= max_cpu_num)
37506534c6f0Swh break;
37516534c6f0Swh
37526534c6f0Swh }
375325cf1a30Sjl }
375425cf1a30Sjl }
375525cf1a30Sjl }
375625cf1a30Sjl
375725cf1a30Sjl x_ml = c_ml;
375825cf1a30Sjl sz = 0;
375925cf1a30Sjl while (x_ml != NULL) {
376056f33205SJonathan Adams sz += x_ml->ml_size;
376156f33205SJonathan Adams x_ml = x_ml->ml_next;
376225cf1a30Sjl }
376325cf1a30Sjl
376425cf1a30Sjl copy_sz = sz/active_cpus;
376525cf1a30Sjl copy_sz = roundup(copy_sz, MMU_PAGESIZE4M);
376625cf1a30Sjl
376725cf1a30Sjl while (sz > copy_sz*active_cpus) {
376825cf1a30Sjl copy_sz += MMU_PAGESIZE4M;
376925cf1a30Sjl }
377025cf1a30Sjl
377125cf1a30Sjl prog->data->stick_freq = system_clock_freq;
377225cf1a30Sjl prog->data->copy_delay = ((copy_sz / min_copy_size_per_sec) + 2) *
3773e98fafb9Sjl system_clock_freq;
377425cf1a30Sjl
377525cf1a30Sjl x_ml = c_ml;
377656f33205SJonathan Adams c_addr = x_ml->ml_address;
377756f33205SJonathan Adams c_size = x_ml->ml_size;
377825cf1a30Sjl
377925cf1a30Sjl for (i = 0; i < NCPU; i++) {
378025cf1a30Sjl prog->stat->nbytes[i] = 0;
378125cf1a30Sjl if (!CPU_IN_SET(prog->data->cpu_copy_set, i)) {
378225cf1a30Sjl continue;
378325cf1a30Sjl }
378425cf1a30Sjl sz = copy_sz;
378525cf1a30Sjl
378625cf1a30Sjl while (sz) {
378725cf1a30Sjl if (c_size > sz) {
37886534c6f0Swh if ((prog->data->cpu_ml[i] =
3789e98fafb9Sjl drmach_memlist_add_span(prog,
37906534c6f0Swh prog->data->cpu_ml[i],
37916534c6f0Swh c_addr, sz)) == NULL) {
37926534c6f0Swh cmn_err(CE_WARN,
37936534c6f0Swh "Unexpected drmach_memlist_add_span"
37946534c6f0Swh " failure.");
37956534c6f0Swh err = drerr_new(1, EOPL_FMEM_SETUP,
37966534c6f0Swh NULL);
37976534c6f0Swh mc_resume();
37986534c6f0Swh goto out;
37996534c6f0Swh }
380025cf1a30Sjl c_addr += sz;
380125cf1a30Sjl c_size -= sz;
380225cf1a30Sjl break;
380325cf1a30Sjl } else {
380425cf1a30Sjl sz -= c_size;
38056534c6f0Swh if ((prog->data->cpu_ml[i] =
3806e98fafb9Sjl drmach_memlist_add_span(prog,
38076534c6f0Swh prog->data->cpu_ml[i],
38086534c6f0Swh c_addr, c_size)) == NULL) {
38096534c6f0Swh cmn_err(CE_WARN,
38106534c6f0Swh "Unexpected drmach_memlist_add_span"
38116534c6f0Swh " failure.");
38126534c6f0Swh err = drerr_new(1, EOPL_FMEM_SETUP,
38136534c6f0Swh NULL);
38146534c6f0Swh mc_resume();
38156534c6f0Swh goto out;
38166534c6f0Swh }
38176534c6f0Swh
381856f33205SJonathan Adams x_ml = x_ml->ml_next;
381925cf1a30Sjl if (x_ml != NULL) {
382056f33205SJonathan Adams c_addr = x_ml->ml_address;
382156f33205SJonathan Adams c_size = x_ml->ml_size;
382225cf1a30Sjl } else {
382325cf1a30Sjl goto end;
382425cf1a30Sjl }
382525cf1a30Sjl }
382625cf1a30Sjl }
382725cf1a30Sjl }
382825cf1a30Sjl end:
382925cf1a30Sjl prog->data->s_copybasepa = s_copybasepa;
383025cf1a30Sjl prog->data->t_copybasepa = t_copybasepa;
383125cf1a30Sjl prog->data->c_ml = c_ml;
383268ac2337Sjl *pgm_id = prog_kmem;
383325cf1a30Sjl
383468ac2337Sjl /* Unmap the alternate space. It will have to be remapped again */
383568ac2337Sjl drmach_unlock_critical((caddr_t)prog);
383625cf1a30Sjl return (NULL);
38376534c6f0Swh
38386534c6f0Swh err_out:
38396534c6f0Swh mc_resume();
38406534c6f0Swh rv = (*prog->data->scf_fmem_cancel)();
38416534c6f0Swh if (rv) {
38426534c6f0Swh cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x", rv);
38436534c6f0Swh }
384468ac2337Sjl out:
384568ac2337Sjl if (prog != NULL) {
384668ac2337Sjl drmach_unlock_critical((caddr_t)prog);
3847e98fafb9Sjl vmem_free(heap_arena, prog, DRMACH_FMEM_LOCKED_PAGES *
3848e98fafb9Sjl PAGESIZE);
384968ac2337Sjl }
385068ac2337Sjl if (prog_kmem != NULL) {
385168ac2337Sjl kmem_free(prog_kmem, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
385268ac2337Sjl }
385368ac2337Sjl return (err);
385425cf1a30Sjl }
385525cf1a30Sjl
385625cf1a30Sjl sbd_error_t *
drmach_copy_rename_fini(drmachid_t id)385725cf1a30Sjl drmach_copy_rename_fini(drmachid_t id)
385825cf1a30Sjl {
385925cf1a30Sjl drmach_copy_rename_program_t *prog = id;
386025cf1a30Sjl sbd_error_t *err = NULL;
386125cf1a30Sjl int rv;
3862b307f191Sbm uint_t fmem_error;
386325cf1a30Sjl
386425cf1a30Sjl /*
386525cf1a30Sjl * Note that we have to delay calling SCF to find out the
386625cf1a30Sjl * status of the FMEM operation here because SCF cannot
386725cf1a30Sjl * respond while it is suspended.
386825cf1a30Sjl * This create a small window when we are sure about the
386925cf1a30Sjl * base address of the system board.
387025cf1a30Sjl * If there is any call to mc-opl to get memory unum,
387125cf1a30Sjl * mc-opl will return UNKNOWN as the unum.
387225cf1a30Sjl */
387325cf1a30Sjl
387468ac2337Sjl /*
387568ac2337Sjl * we have to remap again because all the pointer like data,
387668ac2337Sjl * critical in prog are based on the alternate vmem space.
387768ac2337Sjl */
387868ac2337Sjl (void) drmach_lock_critical((caddr_t)prog, (caddr_t)prog->locked_prog);
387968ac2337Sjl
388025cf1a30Sjl if (prog->data->c_ml != NULL)
388125cf1a30Sjl memlist_delete(prog->data->c_ml);
388225cf1a30Sjl
388325cf1a30Sjl if ((prog->data->fmem_status.op &
3884e98fafb9Sjl (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) !=
3885e98fafb9Sjl (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) {
3886e98fafb9Sjl cmn_err(CE_PANIC, "drmach_copy_rename_fini: invalid op "
3887e98fafb9Sjl "code %x\n", prog->data->fmem_status.op);
388825cf1a30Sjl }
388925cf1a30Sjl
3890b307f191Sbm fmem_error = prog->data->fmem_status.error;
3891b307f191Sbm if (fmem_error != ESBD_NOERROR) {
3892b307f191Sbm err = drerr_new(1, fmem_error, NULL);
3893b307f191Sbm }
3894b307f191Sbm
389525cf1a30Sjl /* possible ops are SCF_START, MC_SUSPEND */
389625cf1a30Sjl if (prog->critical->fmem_issued) {
3897b307f191Sbm if (fmem_error != ESBD_NOERROR) {
3898e98fafb9Sjl cmn_err(CE_PANIC, "Irrecoverable FMEM error %d\n",
3899e98fafb9Sjl fmem_error);
3900b307f191Sbm }
390125cf1a30Sjl rv = (*prog->data->scf_fmem_end)();
390225cf1a30Sjl if (rv) {
390368ac2337Sjl cmn_err(CE_PANIC, "scf_fmem_end() failed rv=%d", rv);
390425cf1a30Sjl }
390525cf1a30Sjl /*
390625cf1a30Sjl * If we get here, rename is successful.
390725cf1a30Sjl * Do all the copy rename post processing.
390825cf1a30Sjl */
390925cf1a30Sjl drmach_swap_pa((drmach_mem_t *)prog->data->s_mem,
3910e98fafb9Sjl (drmach_mem_t *)prog->data->t_mem);
391125cf1a30Sjl } else {
391225cf1a30Sjl rv = (*prog->data->scf_fmem_cancel)();
391325cf1a30Sjl if (rv) {
3914e98fafb9Sjl cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x",
3915e98fafb9Sjl rv);
3916e98fafb9Sjl if (!err) {
3917e98fafb9Sjl err = drerr_new(1, EOPL_SCF_FMEM_CANCEL,
3918e98fafb9Sjl "scf_fmem_cancel() failed. rv = 0x%x", rv);
3919e98fafb9Sjl }
392025cf1a30Sjl }
392125cf1a30Sjl }
392225cf1a30Sjl /* soft resume mac patrol */
392325cf1a30Sjl (*prog->data->mc_resume)();
392425cf1a30Sjl
392568ac2337Sjl drmach_unlock_critical((caddr_t)prog->locked_prog);
392668ac2337Sjl
392768ac2337Sjl vmem_free(heap_arena, prog->locked_prog,
3928e98fafb9Sjl DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
392925cf1a30Sjl kmem_free(prog, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
393025cf1a30Sjl return (err);
393125cf1a30Sjl }
393225cf1a30Sjl
393325cf1a30Sjl /*ARGSUSED*/
393425cf1a30Sjl static void
drmach_copy_rename_slave(struct regs * rp,drmachid_t id)393525cf1a30Sjl drmach_copy_rename_slave(struct regs *rp, drmachid_t id)
393625cf1a30Sjl {
393768ac2337Sjl drmach_copy_rename_program_t *prog =
3938e98fafb9Sjl (drmach_copy_rename_program_t *)id;
393925cf1a30Sjl register int cpuid;
394025cf1a30Sjl extern void drmach_flush();
394125cf1a30Sjl extern void membar_sync_il();
394225cf1a30Sjl extern void drmach_flush_icache();
394325cf1a30Sjl on_trap_data_t otd;
394425cf1a30Sjl
394525cf1a30Sjl cpuid = CPU->cpu_id;
394625cf1a30Sjl
394725cf1a30Sjl if (on_trap(&otd, OT_DATA_EC)) {
394825cf1a30Sjl no_trap();
3949b307f191Sbm prog->data->error[cpuid] = EOPL_FMEM_COPY_ERROR;
395025cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
395168ac2337Sjl drmach_flush_icache();
395268ac2337Sjl membar_sync_il();
395325cf1a30Sjl return;
395425cf1a30Sjl }
395525cf1a30Sjl
395625cf1a30Sjl
395725cf1a30Sjl /*
395825cf1a30Sjl * jmp drmach_copy_rename_prog().
395925cf1a30Sjl */
396025cf1a30Sjl
396125cf1a30Sjl drmach_flush(prog->critical, PAGESIZE);
396225cf1a30Sjl (void) prog->critical->run(prog, cpuid);
396325cf1a30Sjl drmach_flush_icache();
396425cf1a30Sjl
396525cf1a30Sjl no_trap();
396625cf1a30Sjl
396725cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
396868ac2337Sjl
396925cf1a30Sjl membar_sync_il();
397025cf1a30Sjl }
397125cf1a30Sjl
397225cf1a30Sjl static void
drmach_swap_pa(drmach_mem_t * s_mem,drmach_mem_t * t_mem)397325cf1a30Sjl drmach_swap_pa(drmach_mem_t *s_mem, drmach_mem_t *t_mem)
397425cf1a30Sjl {
397525cf1a30Sjl uint64_t s_base, t_base;
397625cf1a30Sjl drmach_board_t *s_board, *t_board;
397725cf1a30Sjl struct memlist *ml;
397825cf1a30Sjl
397925cf1a30Sjl s_board = s_mem->dev.bp;
398025cf1a30Sjl t_board = t_mem->dev.bp;
398125cf1a30Sjl if (s_board == NULL || t_board == NULL) {
398225cf1a30Sjl cmn_err(CE_PANIC, "Cannot locate source or target board\n");
398325cf1a30Sjl return;
398425cf1a30Sjl }
398525cf1a30Sjl s_base = s_mem->slice_base;
398625cf1a30Sjl t_base = t_mem->slice_base;
398725cf1a30Sjl
398825cf1a30Sjl s_mem->slice_base = t_base;
398925cf1a30Sjl s_mem->base_pa = (s_mem->base_pa - s_base) + t_base;
399025cf1a30Sjl
399156f33205SJonathan Adams for (ml = s_mem->memlist; ml; ml = ml->ml_next) {
399256f33205SJonathan Adams ml->ml_address = ml->ml_address - s_base + t_base;
399325cf1a30Sjl }
399425cf1a30Sjl
399525cf1a30Sjl t_mem->slice_base = s_base;
399625cf1a30Sjl t_mem->base_pa = (t_mem->base_pa - t_base) + s_base;
399725cf1a30Sjl
399856f33205SJonathan Adams for (ml = t_mem->memlist; ml; ml = ml->ml_next) {
399956f33205SJonathan Adams ml->ml_address = ml->ml_address - t_base + s_base;
400025cf1a30Sjl }
400125cf1a30Sjl
400225cf1a30Sjl /*
400325cf1a30Sjl * IKP has to update the sb-mem-ranges for mac patrol driver
400425cf1a30Sjl * when it resumes, it will re-read the sb-mem-range property
400525cf1a30Sjl * to get the new base address
400625cf1a30Sjl */
400725cf1a30Sjl if (oplcfg_pa_swap(s_board->bnum, t_board->bnum) != 0)
400825cf1a30Sjl cmn_err(CE_PANIC, "Could not update device nodes\n");
400925cf1a30Sjl }
401025cf1a30Sjl
401125cf1a30Sjl void
drmach_copy_rename(drmachid_t id)401225cf1a30Sjl drmach_copy_rename(drmachid_t id)
401325cf1a30Sjl {
401468ac2337Sjl drmach_copy_rename_program_t *prog_kmem = id;
401568ac2337Sjl drmach_copy_rename_program_t *prog;
401625cf1a30Sjl cpuset_t cpuset;
401725cf1a30Sjl int cpuid;
401825cf1a30Sjl uint64_t inst;
401925cf1a30Sjl register int rtn;
402025cf1a30Sjl extern int in_sync;
402125cf1a30Sjl int old_in_sync;
402225cf1a30Sjl extern void drmach_sys_trap();
402325cf1a30Sjl extern void drmach_flush();
402425cf1a30Sjl extern void drmach_flush_icache();
402525cf1a30Sjl extern uint64_t patch_inst(uint64_t *, uint64_t);
402625cf1a30Sjl on_trap_data_t otd;
402725cf1a30Sjl
40288eafe49bSbm
402968ac2337Sjl prog = prog_kmem->locked_prog;
403068ac2337Sjl
40318eafe49bSbm
403268ac2337Sjl /*
403368ac2337Sjl * We must immediately drop in the TLB because all pointers
403468ac2337Sjl * are based on the alternate vmem space.
403568ac2337Sjl */
403668ac2337Sjl
403768ac2337Sjl (void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
403868ac2337Sjl
403968ac2337Sjl /*
404068ac2337Sjl * we call scf to get the base address here becuase if scf
404168ac2337Sjl * has not been suspended yet, the active path can be changing and
404268ac2337Sjl * sometimes it is not even mapped. We call the interface when
404368ac2337Sjl * the OS has been quiesced.
404468ac2337Sjl */
404568ac2337Sjl prog->critical->scf_reg_base = (*prog->data->scf_get_base_addr)();
404668ac2337Sjl
404768ac2337Sjl if (prog->critical->scf_reg_base == (uint64_t)-1 ||
4048bbe1232eSToomas Soome prog->critical->scf_reg_base == 0) {
4049b307f191Sbm prog->data->fmem_status.error = EOPL_FMEM_SCF_ERR;
405068ac2337Sjl drmach_unlock_critical((caddr_t)prog);
405125cf1a30Sjl return;
405225cf1a30Sjl }
405325cf1a30Sjl
405425cf1a30Sjl cpuset = prog->data->cpu_ready_set;
405525cf1a30Sjl
405625cf1a30Sjl for (cpuid = 0; cpuid < NCPU; cpuid++) {
405725cf1a30Sjl if (CPU_IN_SET(cpuset, cpuid)) {
405825cf1a30Sjl prog->critical->stat[cpuid] = FMEM_LOOP_START;
4059b307f191Sbm prog->data->error[cpuid] = ESBD_NOERROR;
406025cf1a30Sjl }
406125cf1a30Sjl }
406225cf1a30Sjl
406325cf1a30Sjl old_in_sync = in_sync;
406425cf1a30Sjl in_sync = 1;
406525cf1a30Sjl cpuid = CPU->cpu_id;
406625cf1a30Sjl
406725cf1a30Sjl CPUSET_DEL(cpuset, cpuid);
406825cf1a30Sjl
406968ac2337Sjl for (cpuid = 0; cpuid < NCPU; cpuid++) {
407068ac2337Sjl if (CPU_IN_SET(cpuset, cpuid)) {
407168ac2337Sjl xc_one(cpuid, (xcfunc_t *)drmach_lock_critical,
4072e98fafb9Sjl (uint64_t)prog_kmem, (uint64_t)prog);
407368ac2337Sjl }
407468ac2337Sjl }
407568ac2337Sjl
407668ac2337Sjl cpuid = CPU->cpu_id;
407725cf1a30Sjl
407825cf1a30Sjl xt_some(cpuset, (xcfunc_t *)drmach_sys_trap,
4079e98fafb9Sjl (uint64_t)drmach_copy_rename_slave, (uint64_t)prog);
408025cf1a30Sjl xt_sync(cpuset);
408125cf1a30Sjl
408225cf1a30Sjl if (on_trap(&otd, OT_DATA_EC)) {
4083b307f191Sbm rtn = EOPL_FMEM_COPY_ERROR;
408468ac2337Sjl drmach_flush_icache();
408525cf1a30Sjl goto done;
408625cf1a30Sjl }
408725cf1a30Sjl
408825cf1a30Sjl /*
408925cf1a30Sjl * jmp drmach_copy_rename_prog().
409025cf1a30Sjl */
40918eafe49bSbm
409225cf1a30Sjl drmach_flush(prog->critical, PAGESIZE);
409325cf1a30Sjl rtn = prog->critical->run(prog, cpuid);
4094e98fafb9Sjl
409525cf1a30Sjl drmach_flush_icache();
409625cf1a30Sjl
409725cf1a30Sjl
409825cf1a30Sjl done:
409925cf1a30Sjl no_trap();
4100b307f191Sbm if (rtn == EOPL_FMEM_HW_ERROR) {
410125cf1a30Sjl kpreempt_enable();
4102e98fafb9Sjl prom_panic("URGENT_ERROR_TRAP is detected during FMEM.\n");
410325cf1a30Sjl }
410425cf1a30Sjl
410525cf1a30Sjl /*
410625cf1a30Sjl * In normal case, all slave CPU's are still spinning in
410725cf1a30Sjl * the assembly code. The master has to patch the instruction
410825cf1a30Sjl * to get them out.
410925cf1a30Sjl * In error case, e.g. COPY_ERROR, some slave CPU's might
411025cf1a30Sjl * have aborted and already returned and sset LOOP_EXIT status.
411125cf1a30Sjl * Some CPU might still be copying.
411225cf1a30Sjl * In any case, some delay is necessary to give them
411325cf1a30Sjl * enough time to set the LOOP_EXIT status.
411425cf1a30Sjl */
411525cf1a30Sjl
411625cf1a30Sjl for (;;) {
411725cf1a30Sjl inst = patch_inst((uint64_t *)prog->critical->loop_rtn,
4118e98fafb9Sjl prog->critical->inst_loop_ret);
411925cf1a30Sjl if (prog->critical->inst_loop_ret == inst) {
412025cf1a30Sjl break;
412125cf1a30Sjl }
412225cf1a30Sjl }
412325cf1a30Sjl
412425cf1a30Sjl for (cpuid = 0; cpuid < NCPU; cpuid++) {
412525cf1a30Sjl uint64_t last, now;
412625cf1a30Sjl if (!CPU_IN_SET(cpuset, cpuid)) {
412725cf1a30Sjl continue;
412825cf1a30Sjl }
412925cf1a30Sjl last = prog->stat->nbytes[cpuid];
413025cf1a30Sjl /*
413125cf1a30Sjl * Wait for all CPU to exit.
413225cf1a30Sjl * However we do not want an infinite loop
413325cf1a30Sjl * so we detect hangup situation here.
413425cf1a30Sjl * If the slave CPU is still copying data,
413525cf1a30Sjl * we will continue to wait.
413625cf1a30Sjl * In error cases, the master has already set
413725cf1a30Sjl * fmem_status.error to abort the copying.
413825cf1a30Sjl * 1 m.s delay for them to abort copying and
413925cf1a30Sjl * return to drmach_copy_rename_slave to set
414025cf1a30Sjl * FMEM_LOOP_EXIT status should be enough.
414125cf1a30Sjl */
414225cf1a30Sjl for (;;) {
414325cf1a30Sjl if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT)
414425cf1a30Sjl break;
414525cf1a30Sjl drmach_sleep_il();
414625cf1a30Sjl drv_usecwait(1000);
414725cf1a30Sjl now = prog->stat->nbytes[cpuid];
414825cf1a30Sjl if (now <= last) {
4149e98fafb9Sjl drv_usecwait(1000);
4150e98fafb9Sjl if (prog->critical->stat[cpuid] ==
4151e98fafb9Sjl FMEM_LOOP_EXIT)
4152e98fafb9Sjl break;
4153e98fafb9Sjl cmn_err(CE_PANIC, "CPU %d hang during Copy "
4154e98fafb9Sjl "Rename", cpuid);
415525cf1a30Sjl }
415625cf1a30Sjl last = now;
415725cf1a30Sjl }
4158b307f191Sbm if (prog->data->error[cpuid] == EOPL_FMEM_HW_ERROR) {
4159e98fafb9Sjl prom_panic("URGENT_ERROR_TRAP is detected during "
4160e98fafb9Sjl "FMEM.\n");
416125cf1a30Sjl }
416225cf1a30Sjl }
416368ac2337Sjl
416468ac2337Sjl /*
416568ac2337Sjl * This must be done after all strands have exit.
416668ac2337Sjl * Removing the TLB entry will affect both strands
416768ac2337Sjl * in the same core.
416868ac2337Sjl */
416968ac2337Sjl
417068ac2337Sjl for (cpuid = 0; cpuid < NCPU; cpuid++) {
417168ac2337Sjl if (CPU_IN_SET(cpuset, cpuid)) {
417268ac2337Sjl xc_one(cpuid, (xcfunc_t *)drmach_unlock_critical,
4173e98fafb9Sjl (uint64_t)prog, 0);
417468ac2337Sjl }
417568ac2337Sjl }
417625cf1a30Sjl
417725cf1a30Sjl in_sync = old_in_sync;
417825cf1a30Sjl
417968ac2337Sjl /*
418068ac2337Sjl * we should unlock before the following lock to keep the kpreempt
418168ac2337Sjl * count correct.
418268ac2337Sjl */
418368ac2337Sjl (void) drmach_unlock_critical((caddr_t)prog);
418468ac2337Sjl
418568ac2337Sjl /*
418668ac2337Sjl * we must remap again. TLB might have been removed in above xcall.
418768ac2337Sjl */
418868ac2337Sjl
418968ac2337Sjl (void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
419068ac2337Sjl
4191b307f191Sbm if (prog->data->fmem_status.error == ESBD_NOERROR)
419225cf1a30Sjl prog->data->fmem_status.error = rtn;
419325cf1a30Sjl
419425cf1a30Sjl if (prog->data->copy_wait_time > 0) {
419525cf1a30Sjl DRMACH_PR("Unexpected long wait time %ld seconds "
4196e98fafb9Sjl "during copy rename on CPU %d\n",
4197e98fafb9Sjl prog->data->copy_wait_time/prog->data->stick_freq,
4198e98fafb9Sjl prog->data->slowest_cpuid);
419925cf1a30Sjl }
420068ac2337Sjl drmach_unlock_critical((caddr_t)prog);
420125cf1a30Sjl }
4202