1*45d9ca49SDean Nelson /* 2*45d9ca49SDean Nelson * This file is subject to the terms and conditions of the GNU General Public 3*45d9ca49SDean Nelson * License. See the file "COPYING" in the main directory of this archive 4*45d9ca49SDean Nelson * for more details. 5*45d9ca49SDean Nelson * 6*45d9ca49SDean Nelson * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. 7*45d9ca49SDean Nelson */ 8*45d9ca49SDean Nelson 9*45d9ca49SDean Nelson 10*45d9ca49SDean Nelson /* 11*45d9ca49SDean Nelson * Cross Partition Communication (XPC) partition support. 12*45d9ca49SDean Nelson * 13*45d9ca49SDean Nelson * This is the part of XPC that detects the presence/absence of 14*45d9ca49SDean Nelson * other partitions. It provides a heartbeat and monitors the 15*45d9ca49SDean Nelson * heartbeats of other partitions. 16*45d9ca49SDean Nelson * 17*45d9ca49SDean Nelson */ 18*45d9ca49SDean Nelson 19*45d9ca49SDean Nelson 20*45d9ca49SDean Nelson #include <linux/kernel.h> 21*45d9ca49SDean Nelson #include <linux/sysctl.h> 22*45d9ca49SDean Nelson #include <linux/cache.h> 23*45d9ca49SDean Nelson #include <linux/mmzone.h> 24*45d9ca49SDean Nelson #include <linux/nodemask.h> 25*45d9ca49SDean Nelson #include <asm/uncached.h> 26*45d9ca49SDean Nelson #include <asm/sn/bte.h> 27*45d9ca49SDean Nelson #include <asm/sn/intr.h> 28*45d9ca49SDean Nelson #include <asm/sn/sn_sal.h> 29*45d9ca49SDean Nelson #include <asm/sn/nodepda.h> 30*45d9ca49SDean Nelson #include <asm/sn/addrs.h> 31*45d9ca49SDean Nelson #include "xpc.h" 32*45d9ca49SDean Nelson 33*45d9ca49SDean Nelson 34*45d9ca49SDean Nelson /* XPC is exiting flag */ 35*45d9ca49SDean Nelson int xpc_exiting; 36*45d9ca49SDean Nelson 37*45d9ca49SDean Nelson 38*45d9ca49SDean Nelson /* SH_IPI_ACCESS shub register value on startup */ 39*45d9ca49SDean Nelson static u64 xpc_sh1_IPI_access; 40*45d9ca49SDean Nelson static u64 xpc_sh2_IPI_access0; 41*45d9ca49SDean Nelson static u64 xpc_sh2_IPI_access1; 42*45d9ca49SDean Nelson static u64 xpc_sh2_IPI_access2; 43*45d9ca49SDean Nelson static u64 xpc_sh2_IPI_access3; 44*45d9ca49SDean Nelson 45*45d9ca49SDean Nelson 46*45d9ca49SDean Nelson /* original protection values for each node */ 47*45d9ca49SDean Nelson u64 xpc_prot_vec[MAX_NUMNODES]; 48*45d9ca49SDean Nelson 49*45d9ca49SDean Nelson 50*45d9ca49SDean Nelson /* this partition's reserved page pointers */ 51*45d9ca49SDean Nelson struct xpc_rsvd_page *xpc_rsvd_page; 52*45d9ca49SDean Nelson static u64 *xpc_part_nasids; 53*45d9ca49SDean Nelson static u64 *xpc_mach_nasids; 54*45d9ca49SDean Nelson struct xpc_vars *xpc_vars; 55*45d9ca49SDean Nelson struct xpc_vars_part *xpc_vars_part; 56*45d9ca49SDean Nelson 57*45d9ca49SDean Nelson static int xp_nasid_mask_bytes; /* actual size in bytes of nasid mask */ 58*45d9ca49SDean Nelson static int xp_nasid_mask_words; /* actual size in words of nasid mask */ 59*45d9ca49SDean Nelson 60*45d9ca49SDean Nelson 61*45d9ca49SDean Nelson /* 62*45d9ca49SDean Nelson * For performance reasons, each entry of xpc_partitions[] is cacheline 63*45d9ca49SDean Nelson * aligned. And xpc_partitions[] is padded with an additional entry at the 64*45d9ca49SDean Nelson * end so that the last legitimate entry doesn't share its cacheline with 65*45d9ca49SDean Nelson * another variable. 66*45d9ca49SDean Nelson */ 67*45d9ca49SDean Nelson struct xpc_partition xpc_partitions[XP_MAX_PARTITIONS + 1]; 68*45d9ca49SDean Nelson 69*45d9ca49SDean Nelson 70*45d9ca49SDean Nelson /* 71*45d9ca49SDean Nelson * Generic buffer used to store a local copy of portions of a remote 72*45d9ca49SDean Nelson * partition's reserved page (either its header and part_nasids mask, 73*45d9ca49SDean Nelson * or its vars). 74*45d9ca49SDean Nelson */ 75*45d9ca49SDean Nelson char *xpc_remote_copy_buffer; 76*45d9ca49SDean Nelson void *xpc_remote_copy_buffer_base; 77*45d9ca49SDean Nelson 78*45d9ca49SDean Nelson 79*45d9ca49SDean Nelson /* 80*45d9ca49SDean Nelson * Guarantee that the kmalloc'd memory is cacheline aligned. 81*45d9ca49SDean Nelson */ 82*45d9ca49SDean Nelson void * 83*45d9ca49SDean Nelson xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) 84*45d9ca49SDean Nelson { 85*45d9ca49SDean Nelson /* see if kmalloc will give us cachline aligned memory by default */ 86*45d9ca49SDean Nelson *base = kmalloc(size, flags); 87*45d9ca49SDean Nelson if (*base == NULL) { 88*45d9ca49SDean Nelson return NULL; 89*45d9ca49SDean Nelson } 90*45d9ca49SDean Nelson if ((u64) *base == L1_CACHE_ALIGN((u64) *base)) { 91*45d9ca49SDean Nelson return *base; 92*45d9ca49SDean Nelson } 93*45d9ca49SDean Nelson kfree(*base); 94*45d9ca49SDean Nelson 95*45d9ca49SDean Nelson /* nope, we'll have to do it ourselves */ 96*45d9ca49SDean Nelson *base = kmalloc(size + L1_CACHE_BYTES, flags); 97*45d9ca49SDean Nelson if (*base == NULL) { 98*45d9ca49SDean Nelson return NULL; 99*45d9ca49SDean Nelson } 100*45d9ca49SDean Nelson return (void *) L1_CACHE_ALIGN((u64) *base); 101*45d9ca49SDean Nelson } 102*45d9ca49SDean Nelson 103*45d9ca49SDean Nelson 104*45d9ca49SDean Nelson /* 105*45d9ca49SDean Nelson * Given a nasid, get the physical address of the partition's reserved page 106*45d9ca49SDean Nelson * for that nasid. This function returns 0 on any error. 107*45d9ca49SDean Nelson */ 108*45d9ca49SDean Nelson static u64 109*45d9ca49SDean Nelson xpc_get_rsvd_page_pa(int nasid) 110*45d9ca49SDean Nelson { 111*45d9ca49SDean Nelson bte_result_t bte_res; 112*45d9ca49SDean Nelson s64 status; 113*45d9ca49SDean Nelson u64 cookie = 0; 114*45d9ca49SDean Nelson u64 rp_pa = nasid; /* seed with nasid */ 115*45d9ca49SDean Nelson u64 len = 0; 116*45d9ca49SDean Nelson u64 buf = buf; 117*45d9ca49SDean Nelson u64 buf_len = 0; 118*45d9ca49SDean Nelson void *buf_base = NULL; 119*45d9ca49SDean Nelson 120*45d9ca49SDean Nelson 121*45d9ca49SDean Nelson while (1) { 122*45d9ca49SDean Nelson 123*45d9ca49SDean Nelson status = sn_partition_reserved_page_pa(buf, &cookie, &rp_pa, 124*45d9ca49SDean Nelson &len); 125*45d9ca49SDean Nelson 126*45d9ca49SDean Nelson dev_dbg(xpc_part, "SAL returned with status=%li, cookie=" 127*45d9ca49SDean Nelson "0x%016lx, address=0x%016lx, len=0x%016lx\n", 128*45d9ca49SDean Nelson status, cookie, rp_pa, len); 129*45d9ca49SDean Nelson 130*45d9ca49SDean Nelson if (status != SALRET_MORE_PASSES) { 131*45d9ca49SDean Nelson break; 132*45d9ca49SDean Nelson } 133*45d9ca49SDean Nelson 134*45d9ca49SDean Nelson if (L1_CACHE_ALIGN(len) > buf_len) { 135*45d9ca49SDean Nelson kfree(buf_base); 136*45d9ca49SDean Nelson buf_len = L1_CACHE_ALIGN(len); 137*45d9ca49SDean Nelson buf = (u64) xpc_kmalloc_cacheline_aligned(buf_len, 138*45d9ca49SDean Nelson GFP_KERNEL, &buf_base); 139*45d9ca49SDean Nelson if (buf_base == NULL) { 140*45d9ca49SDean Nelson dev_err(xpc_part, "unable to kmalloc " 141*45d9ca49SDean Nelson "len=0x%016lx\n", buf_len); 142*45d9ca49SDean Nelson status = SALRET_ERROR; 143*45d9ca49SDean Nelson break; 144*45d9ca49SDean Nelson } 145*45d9ca49SDean Nelson } 146*45d9ca49SDean Nelson 147*45d9ca49SDean Nelson bte_res = xp_bte_copy(rp_pa, buf, buf_len, 148*45d9ca49SDean Nelson (BTE_NOTIFY | BTE_WACQUIRE), NULL); 149*45d9ca49SDean Nelson if (bte_res != BTE_SUCCESS) { 150*45d9ca49SDean Nelson dev_dbg(xpc_part, "xp_bte_copy failed %i\n", bte_res); 151*45d9ca49SDean Nelson status = SALRET_ERROR; 152*45d9ca49SDean Nelson break; 153*45d9ca49SDean Nelson } 154*45d9ca49SDean Nelson } 155*45d9ca49SDean Nelson 156*45d9ca49SDean Nelson kfree(buf_base); 157*45d9ca49SDean Nelson 158*45d9ca49SDean Nelson if (status != SALRET_OK) { 159*45d9ca49SDean Nelson rp_pa = 0; 160*45d9ca49SDean Nelson } 161*45d9ca49SDean Nelson dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa); 162*45d9ca49SDean Nelson return rp_pa; 163*45d9ca49SDean Nelson } 164*45d9ca49SDean Nelson 165*45d9ca49SDean Nelson 166*45d9ca49SDean Nelson /* 167*45d9ca49SDean Nelson * Fill the partition reserved page with the information needed by 168*45d9ca49SDean Nelson * other partitions to discover we are alive and establish initial 169*45d9ca49SDean Nelson * communications. 170*45d9ca49SDean Nelson */ 171*45d9ca49SDean Nelson struct xpc_rsvd_page * 172*45d9ca49SDean Nelson xpc_rsvd_page_init(void) 173*45d9ca49SDean Nelson { 174*45d9ca49SDean Nelson struct xpc_rsvd_page *rp; 175*45d9ca49SDean Nelson AMO_t *amos_page; 176*45d9ca49SDean Nelson u64 rp_pa, nasid_array = 0; 177*45d9ca49SDean Nelson int i, ret; 178*45d9ca49SDean Nelson 179*45d9ca49SDean Nelson 180*45d9ca49SDean Nelson /* get the local reserved page's address */ 181*45d9ca49SDean Nelson 182*45d9ca49SDean Nelson preempt_disable(); 183*45d9ca49SDean Nelson rp_pa = xpc_get_rsvd_page_pa(cpuid_to_nasid(smp_processor_id())); 184*45d9ca49SDean Nelson preempt_enable(); 185*45d9ca49SDean Nelson if (rp_pa == 0) { 186*45d9ca49SDean Nelson dev_err(xpc_part, "SAL failed to locate the reserved page\n"); 187*45d9ca49SDean Nelson return NULL; 188*45d9ca49SDean Nelson } 189*45d9ca49SDean Nelson rp = (struct xpc_rsvd_page *) __va(rp_pa); 190*45d9ca49SDean Nelson 191*45d9ca49SDean Nelson if (rp->partid != sn_partition_id) { 192*45d9ca49SDean Nelson dev_err(xpc_part, "the reserved page's partid of %d should be " 193*45d9ca49SDean Nelson "%d\n", rp->partid, sn_partition_id); 194*45d9ca49SDean Nelson return NULL; 195*45d9ca49SDean Nelson } 196*45d9ca49SDean Nelson 197*45d9ca49SDean Nelson rp->version = XPC_RP_VERSION; 198*45d9ca49SDean Nelson 199*45d9ca49SDean Nelson /* establish the actual sizes of the nasid masks */ 200*45d9ca49SDean Nelson if (rp->SAL_version == 1) { 201*45d9ca49SDean Nelson /* SAL_version 1 didn't set the nasids_size field */ 202*45d9ca49SDean Nelson rp->nasids_size = 128; 203*45d9ca49SDean Nelson } 204*45d9ca49SDean Nelson xp_nasid_mask_bytes = rp->nasids_size; 205*45d9ca49SDean Nelson xp_nasid_mask_words = xp_nasid_mask_bytes / 8; 206*45d9ca49SDean Nelson 207*45d9ca49SDean Nelson /* setup the pointers to the various items in the reserved page */ 208*45d9ca49SDean Nelson xpc_part_nasids = XPC_RP_PART_NASIDS(rp); 209*45d9ca49SDean Nelson xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp); 210*45d9ca49SDean Nelson xpc_vars = XPC_RP_VARS(rp); 211*45d9ca49SDean Nelson xpc_vars_part = XPC_RP_VARS_PART(rp); 212*45d9ca49SDean Nelson 213*45d9ca49SDean Nelson /* 214*45d9ca49SDean Nelson * Before clearing xpc_vars, see if a page of AMOs had been previously 215*45d9ca49SDean Nelson * allocated. If not we'll need to allocate one and set permissions 216*45d9ca49SDean Nelson * so that cross-partition AMOs are allowed. 217*45d9ca49SDean Nelson * 218*45d9ca49SDean Nelson * The allocated AMO page needs MCA reporting to remain disabled after 219*45d9ca49SDean Nelson * XPC has unloaded. To make this work, we keep a copy of the pointer 220*45d9ca49SDean Nelson * to this page (i.e., amos_page) in the struct xpc_vars structure, 221*45d9ca49SDean Nelson * which is pointed to by the reserved page, and re-use that saved copy 222*45d9ca49SDean Nelson * on subsequent loads of XPC. This AMO page is never freed, and its 223*45d9ca49SDean Nelson * memory protections are never restricted. 224*45d9ca49SDean Nelson */ 225*45d9ca49SDean Nelson if ((amos_page = xpc_vars->amos_page) == NULL) { 226*45d9ca49SDean Nelson amos_page = (AMO_t *) TO_AMO(uncached_alloc_page(0)); 227*45d9ca49SDean Nelson if (amos_page == NULL) { 228*45d9ca49SDean Nelson dev_err(xpc_part, "can't allocate page of AMOs\n"); 229*45d9ca49SDean Nelson return NULL; 230*45d9ca49SDean Nelson } 231*45d9ca49SDean Nelson 232*45d9ca49SDean Nelson /* 233*45d9ca49SDean Nelson * Open up AMO-R/W to cpu. This is done for Shub 1.1 systems 234*45d9ca49SDean Nelson * when xpc_allow_IPI_ops() is called via xpc_hb_init(). 235*45d9ca49SDean Nelson */ 236*45d9ca49SDean Nelson if (!enable_shub_wars_1_1()) { 237*45d9ca49SDean Nelson ret = sn_change_memprotect(ia64_tpa((u64) amos_page), 238*45d9ca49SDean Nelson PAGE_SIZE, SN_MEMPROT_ACCESS_CLASS_1, 239*45d9ca49SDean Nelson &nasid_array); 240*45d9ca49SDean Nelson if (ret != 0) { 241*45d9ca49SDean Nelson dev_err(xpc_part, "can't change memory " 242*45d9ca49SDean Nelson "protections\n"); 243*45d9ca49SDean Nelson uncached_free_page(__IA64_UNCACHED_OFFSET | 244*45d9ca49SDean Nelson TO_PHYS((u64) amos_page)); 245*45d9ca49SDean Nelson return NULL; 246*45d9ca49SDean Nelson } 247*45d9ca49SDean Nelson } 248*45d9ca49SDean Nelson } else if (!IS_AMO_ADDRESS((u64) amos_page)) { 249*45d9ca49SDean Nelson /* 250*45d9ca49SDean Nelson * EFI's XPBOOT can also set amos_page in the reserved page, 251*45d9ca49SDean Nelson * but it happens to leave it as an uncached physical address 252*45d9ca49SDean Nelson * and we need it to be an uncached virtual, so we'll have to 253*45d9ca49SDean Nelson * convert it. 254*45d9ca49SDean Nelson */ 255*45d9ca49SDean Nelson if (!IS_AMO_PHYS_ADDRESS((u64) amos_page)) { 256*45d9ca49SDean Nelson dev_err(xpc_part, "previously used amos_page address " 257*45d9ca49SDean Nelson "is bad = 0x%p\n", (void *) amos_page); 258*45d9ca49SDean Nelson return NULL; 259*45d9ca49SDean Nelson } 260*45d9ca49SDean Nelson amos_page = (AMO_t *) TO_AMO((u64) amos_page); 261*45d9ca49SDean Nelson } 262*45d9ca49SDean Nelson 263*45d9ca49SDean Nelson /* clear xpc_vars */ 264*45d9ca49SDean Nelson memset(xpc_vars, 0, sizeof(struct xpc_vars)); 265*45d9ca49SDean Nelson 266*45d9ca49SDean Nelson xpc_vars->version = XPC_V_VERSION; 267*45d9ca49SDean Nelson xpc_vars->act_nasid = cpuid_to_nasid(0); 268*45d9ca49SDean Nelson xpc_vars->act_phys_cpuid = cpu_physical_id(0); 269*45d9ca49SDean Nelson xpc_vars->vars_part_pa = __pa(xpc_vars_part); 270*45d9ca49SDean Nelson xpc_vars->amos_page_pa = ia64_tpa((u64) amos_page); 271*45d9ca49SDean Nelson xpc_vars->amos_page = amos_page; /* save for next load of XPC */ 272*45d9ca49SDean Nelson 273*45d9ca49SDean Nelson 274*45d9ca49SDean Nelson /* clear xpc_vars_part */ 275*45d9ca49SDean Nelson memset((u64 *) xpc_vars_part, 0, sizeof(struct xpc_vars_part) * 276*45d9ca49SDean Nelson XP_MAX_PARTITIONS); 277*45d9ca49SDean Nelson 278*45d9ca49SDean Nelson /* initialize the activate IRQ related AMO variables */ 279*45d9ca49SDean Nelson for (i = 0; i < xp_nasid_mask_words; i++) { 280*45d9ca49SDean Nelson (void) xpc_IPI_init(XPC_ACTIVATE_IRQ_AMOS + i); 281*45d9ca49SDean Nelson } 282*45d9ca49SDean Nelson 283*45d9ca49SDean Nelson /* initialize the engaged remote partitions related AMO variables */ 284*45d9ca49SDean Nelson (void) xpc_IPI_init(XPC_ENGAGED_PARTITIONS_AMO); 285*45d9ca49SDean Nelson (void) xpc_IPI_init(XPC_DISENGAGE_REQUEST_AMO); 286*45d9ca49SDean Nelson 287*45d9ca49SDean Nelson /* timestamp of when reserved page was setup by XPC */ 288*45d9ca49SDean Nelson rp->stamp = CURRENT_TIME; 289*45d9ca49SDean Nelson 290*45d9ca49SDean Nelson /* 291*45d9ca49SDean Nelson * This signifies to the remote partition that our reserved 292*45d9ca49SDean Nelson * page is initialized. 293*45d9ca49SDean Nelson */ 294*45d9ca49SDean Nelson rp->vars_pa = __pa(xpc_vars); 295*45d9ca49SDean Nelson 296*45d9ca49SDean Nelson return rp; 297*45d9ca49SDean Nelson } 298*45d9ca49SDean Nelson 299*45d9ca49SDean Nelson 300*45d9ca49SDean Nelson /* 301*45d9ca49SDean Nelson * Change protections to allow IPI operations (and AMO operations on 302*45d9ca49SDean Nelson * Shub 1.1 systems). 303*45d9ca49SDean Nelson */ 304*45d9ca49SDean Nelson void 305*45d9ca49SDean Nelson xpc_allow_IPI_ops(void) 306*45d9ca49SDean Nelson { 307*45d9ca49SDean Nelson int node; 308*45d9ca49SDean Nelson int nasid; 309*45d9ca49SDean Nelson 310*45d9ca49SDean Nelson 311*45d9ca49SDean Nelson // >>> Change SH_IPI_ACCESS code to use SAL call once it is available. 312*45d9ca49SDean Nelson 313*45d9ca49SDean Nelson if (is_shub2()) { 314*45d9ca49SDean Nelson xpc_sh2_IPI_access0 = 315*45d9ca49SDean Nelson (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS0)); 316*45d9ca49SDean Nelson xpc_sh2_IPI_access1 = 317*45d9ca49SDean Nelson (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS1)); 318*45d9ca49SDean Nelson xpc_sh2_IPI_access2 = 319*45d9ca49SDean Nelson (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS2)); 320*45d9ca49SDean Nelson xpc_sh2_IPI_access3 = 321*45d9ca49SDean Nelson (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS3)); 322*45d9ca49SDean Nelson 323*45d9ca49SDean Nelson for_each_online_node(node) { 324*45d9ca49SDean Nelson nasid = cnodeid_to_nasid(node); 325*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0), 326*45d9ca49SDean Nelson -1UL); 327*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1), 328*45d9ca49SDean Nelson -1UL); 329*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2), 330*45d9ca49SDean Nelson -1UL); 331*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3), 332*45d9ca49SDean Nelson -1UL); 333*45d9ca49SDean Nelson } 334*45d9ca49SDean Nelson 335*45d9ca49SDean Nelson } else { 336*45d9ca49SDean Nelson xpc_sh1_IPI_access = 337*45d9ca49SDean Nelson (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH1_IPI_ACCESS)); 338*45d9ca49SDean Nelson 339*45d9ca49SDean Nelson for_each_online_node(node) { 340*45d9ca49SDean Nelson nasid = cnodeid_to_nasid(node); 341*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS), 342*45d9ca49SDean Nelson -1UL); 343*45d9ca49SDean Nelson 344*45d9ca49SDean Nelson /* 345*45d9ca49SDean Nelson * Since the BIST collides with memory operations on 346*45d9ca49SDean Nelson * SHUB 1.1 sn_change_memprotect() cannot be used. 347*45d9ca49SDean Nelson */ 348*45d9ca49SDean Nelson if (enable_shub_wars_1_1()) { 349*45d9ca49SDean Nelson /* open up everything */ 350*45d9ca49SDean Nelson xpc_prot_vec[node] = (u64) HUB_L((u64 *) 351*45d9ca49SDean Nelson GLOBAL_MMR_ADDR(nasid, 352*45d9ca49SDean Nelson SH1_MD_DQLP_MMR_DIR_PRIVEC0)); 353*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, 354*45d9ca49SDean Nelson SH1_MD_DQLP_MMR_DIR_PRIVEC0), 355*45d9ca49SDean Nelson -1UL); 356*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, 357*45d9ca49SDean Nelson SH1_MD_DQRP_MMR_DIR_PRIVEC0), 358*45d9ca49SDean Nelson -1UL); 359*45d9ca49SDean Nelson } 360*45d9ca49SDean Nelson } 361*45d9ca49SDean Nelson } 362*45d9ca49SDean Nelson } 363*45d9ca49SDean Nelson 364*45d9ca49SDean Nelson 365*45d9ca49SDean Nelson /* 366*45d9ca49SDean Nelson * Restrict protections to disallow IPI operations (and AMO operations on 367*45d9ca49SDean Nelson * Shub 1.1 systems). 368*45d9ca49SDean Nelson */ 369*45d9ca49SDean Nelson void 370*45d9ca49SDean Nelson xpc_restrict_IPI_ops(void) 371*45d9ca49SDean Nelson { 372*45d9ca49SDean Nelson int node; 373*45d9ca49SDean Nelson int nasid; 374*45d9ca49SDean Nelson 375*45d9ca49SDean Nelson 376*45d9ca49SDean Nelson // >>> Change SH_IPI_ACCESS code to use SAL call once it is available. 377*45d9ca49SDean Nelson 378*45d9ca49SDean Nelson if (is_shub2()) { 379*45d9ca49SDean Nelson 380*45d9ca49SDean Nelson for_each_online_node(node) { 381*45d9ca49SDean Nelson nasid = cnodeid_to_nasid(node); 382*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0), 383*45d9ca49SDean Nelson xpc_sh2_IPI_access0); 384*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1), 385*45d9ca49SDean Nelson xpc_sh2_IPI_access1); 386*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2), 387*45d9ca49SDean Nelson xpc_sh2_IPI_access2); 388*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3), 389*45d9ca49SDean Nelson xpc_sh2_IPI_access3); 390*45d9ca49SDean Nelson } 391*45d9ca49SDean Nelson 392*45d9ca49SDean Nelson } else { 393*45d9ca49SDean Nelson 394*45d9ca49SDean Nelson for_each_online_node(node) { 395*45d9ca49SDean Nelson nasid = cnodeid_to_nasid(node); 396*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS), 397*45d9ca49SDean Nelson xpc_sh1_IPI_access); 398*45d9ca49SDean Nelson 399*45d9ca49SDean Nelson if (enable_shub_wars_1_1()) { 400*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, 401*45d9ca49SDean Nelson SH1_MD_DQLP_MMR_DIR_PRIVEC0), 402*45d9ca49SDean Nelson xpc_prot_vec[node]); 403*45d9ca49SDean Nelson HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, 404*45d9ca49SDean Nelson SH1_MD_DQRP_MMR_DIR_PRIVEC0), 405*45d9ca49SDean Nelson xpc_prot_vec[node]); 406*45d9ca49SDean Nelson } 407*45d9ca49SDean Nelson } 408*45d9ca49SDean Nelson } 409*45d9ca49SDean Nelson } 410*45d9ca49SDean Nelson 411*45d9ca49SDean Nelson 412*45d9ca49SDean Nelson /* 413*45d9ca49SDean Nelson * At periodic intervals, scan through all active partitions and ensure 414*45d9ca49SDean Nelson * their heartbeat is still active. If not, the partition is deactivated. 415*45d9ca49SDean Nelson */ 416*45d9ca49SDean Nelson void 417*45d9ca49SDean Nelson xpc_check_remote_hb(void) 418*45d9ca49SDean Nelson { 419*45d9ca49SDean Nelson struct xpc_vars *remote_vars; 420*45d9ca49SDean Nelson struct xpc_partition *part; 421*45d9ca49SDean Nelson partid_t partid; 422*45d9ca49SDean Nelson bte_result_t bres; 423*45d9ca49SDean Nelson 424*45d9ca49SDean Nelson 425*45d9ca49SDean Nelson remote_vars = (struct xpc_vars *) xpc_remote_copy_buffer; 426*45d9ca49SDean Nelson 427*45d9ca49SDean Nelson for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { 428*45d9ca49SDean Nelson 429*45d9ca49SDean Nelson if (xpc_exiting) { 430*45d9ca49SDean Nelson break; 431*45d9ca49SDean Nelson } 432*45d9ca49SDean Nelson 433*45d9ca49SDean Nelson if (partid == sn_partition_id) { 434*45d9ca49SDean Nelson continue; 435*45d9ca49SDean Nelson } 436*45d9ca49SDean Nelson 437*45d9ca49SDean Nelson part = &xpc_partitions[partid]; 438*45d9ca49SDean Nelson 439*45d9ca49SDean Nelson if (part->act_state == XPC_P_INACTIVE || 440*45d9ca49SDean Nelson part->act_state == XPC_P_DEACTIVATING) { 441*45d9ca49SDean Nelson continue; 442*45d9ca49SDean Nelson } 443*45d9ca49SDean Nelson 444*45d9ca49SDean Nelson /* pull the remote_hb cache line */ 445*45d9ca49SDean Nelson bres = xp_bte_copy(part->remote_vars_pa, 446*45d9ca49SDean Nelson (u64) remote_vars, 447*45d9ca49SDean Nelson XPC_RP_VARS_SIZE, 448*45d9ca49SDean Nelson (BTE_NOTIFY | BTE_WACQUIRE), NULL); 449*45d9ca49SDean Nelson if (bres != BTE_SUCCESS) { 450*45d9ca49SDean Nelson XPC_DEACTIVATE_PARTITION(part, 451*45d9ca49SDean Nelson xpc_map_bte_errors(bres)); 452*45d9ca49SDean Nelson continue; 453*45d9ca49SDean Nelson } 454*45d9ca49SDean Nelson 455*45d9ca49SDean Nelson dev_dbg(xpc_part, "partid = %d, heartbeat = %ld, last_heartbeat" 456*45d9ca49SDean Nelson " = %ld, heartbeat_offline = %ld, HB_mask = 0x%lx\n", 457*45d9ca49SDean Nelson partid, remote_vars->heartbeat, part->last_heartbeat, 458*45d9ca49SDean Nelson remote_vars->heartbeat_offline, 459*45d9ca49SDean Nelson remote_vars->heartbeating_to_mask); 460*45d9ca49SDean Nelson 461*45d9ca49SDean Nelson if (((remote_vars->heartbeat == part->last_heartbeat) && 462*45d9ca49SDean Nelson (remote_vars->heartbeat_offline == 0)) || 463*45d9ca49SDean Nelson !xpc_hb_allowed(sn_partition_id, remote_vars)) { 464*45d9ca49SDean Nelson 465*45d9ca49SDean Nelson XPC_DEACTIVATE_PARTITION(part, xpcNoHeartbeat); 466*45d9ca49SDean Nelson continue; 467*45d9ca49SDean Nelson } 468*45d9ca49SDean Nelson 469*45d9ca49SDean Nelson part->last_heartbeat = remote_vars->heartbeat; 470*45d9ca49SDean Nelson } 471*45d9ca49SDean Nelson } 472*45d9ca49SDean Nelson 473*45d9ca49SDean Nelson 474*45d9ca49SDean Nelson /* 475*45d9ca49SDean Nelson * Get a copy of a portion of the remote partition's rsvd page. 476*45d9ca49SDean Nelson * 477*45d9ca49SDean Nelson * remote_rp points to a buffer that is cacheline aligned for BTE copies and 478*45d9ca49SDean Nelson * is large enough to contain a copy of their reserved page header and 479*45d9ca49SDean Nelson * part_nasids mask. 480*45d9ca49SDean Nelson */ 481*45d9ca49SDean Nelson static enum xpc_retval 482*45d9ca49SDean Nelson xpc_get_remote_rp(int nasid, u64 *discovered_nasids, 483*45d9ca49SDean Nelson struct xpc_rsvd_page *remote_rp, u64 *remote_rp_pa) 484*45d9ca49SDean Nelson { 485*45d9ca49SDean Nelson int bres, i; 486*45d9ca49SDean Nelson 487*45d9ca49SDean Nelson 488*45d9ca49SDean Nelson /* get the reserved page's physical address */ 489*45d9ca49SDean Nelson 490*45d9ca49SDean Nelson *remote_rp_pa = xpc_get_rsvd_page_pa(nasid); 491*45d9ca49SDean Nelson if (*remote_rp_pa == 0) { 492*45d9ca49SDean Nelson return xpcNoRsvdPageAddr; 493*45d9ca49SDean Nelson } 494*45d9ca49SDean Nelson 495*45d9ca49SDean Nelson 496*45d9ca49SDean Nelson /* pull over the reserved page header and part_nasids mask */ 497*45d9ca49SDean Nelson bres = xp_bte_copy(*remote_rp_pa, (u64) remote_rp, 498*45d9ca49SDean Nelson XPC_RP_HEADER_SIZE + xp_nasid_mask_bytes, 499*45d9ca49SDean Nelson (BTE_NOTIFY | BTE_WACQUIRE), NULL); 500*45d9ca49SDean Nelson if (bres != BTE_SUCCESS) { 501*45d9ca49SDean Nelson return xpc_map_bte_errors(bres); 502*45d9ca49SDean Nelson } 503*45d9ca49SDean Nelson 504*45d9ca49SDean Nelson 505*45d9ca49SDean Nelson if (discovered_nasids != NULL) { 506*45d9ca49SDean Nelson u64 *remote_part_nasids = XPC_RP_PART_NASIDS(remote_rp); 507*45d9ca49SDean Nelson 508*45d9ca49SDean Nelson 509*45d9ca49SDean Nelson for (i = 0; i < xp_nasid_mask_words; i++) { 510*45d9ca49SDean Nelson discovered_nasids[i] |= remote_part_nasids[i]; 511*45d9ca49SDean Nelson } 512*45d9ca49SDean Nelson } 513*45d9ca49SDean Nelson 514*45d9ca49SDean Nelson 515*45d9ca49SDean Nelson /* check that the partid is for another partition */ 516*45d9ca49SDean Nelson 517*45d9ca49SDean Nelson if (remote_rp->partid < 1 || 518*45d9ca49SDean Nelson remote_rp->partid > (XP_MAX_PARTITIONS - 1)) { 519*45d9ca49SDean Nelson return xpcInvalidPartid; 520*45d9ca49SDean Nelson } 521*45d9ca49SDean Nelson 522*45d9ca49SDean Nelson if (remote_rp->partid == sn_partition_id) { 523*45d9ca49SDean Nelson return xpcLocalPartid; 524*45d9ca49SDean Nelson } 525*45d9ca49SDean Nelson 526*45d9ca49SDean Nelson 527*45d9ca49SDean Nelson if (XPC_VERSION_MAJOR(remote_rp->version) != 528*45d9ca49SDean Nelson XPC_VERSION_MAJOR(XPC_RP_VERSION)) { 529*45d9ca49SDean Nelson return xpcBadVersion; 530*45d9ca49SDean Nelson } 531*45d9ca49SDean Nelson 532*45d9ca49SDean Nelson return xpcSuccess; 533*45d9ca49SDean Nelson } 534*45d9ca49SDean Nelson 535*45d9ca49SDean Nelson 536*45d9ca49SDean Nelson /* 537*45d9ca49SDean Nelson * Get a copy of the remote partition's XPC variables from the reserved page. 538*45d9ca49SDean Nelson * 539*45d9ca49SDean Nelson * remote_vars points to a buffer that is cacheline aligned for BTE copies and 540*45d9ca49SDean Nelson * assumed to be of size XPC_RP_VARS_SIZE. 541*45d9ca49SDean Nelson */ 542*45d9ca49SDean Nelson static enum xpc_retval 543*45d9ca49SDean Nelson xpc_get_remote_vars(u64 remote_vars_pa, struct xpc_vars *remote_vars) 544*45d9ca49SDean Nelson { 545*45d9ca49SDean Nelson int bres; 546*45d9ca49SDean Nelson 547*45d9ca49SDean Nelson 548*45d9ca49SDean Nelson if (remote_vars_pa == 0) { 549*45d9ca49SDean Nelson return xpcVarsNotSet; 550*45d9ca49SDean Nelson } 551*45d9ca49SDean Nelson 552*45d9ca49SDean Nelson /* pull over the cross partition variables */ 553*45d9ca49SDean Nelson bres = xp_bte_copy(remote_vars_pa, (u64) remote_vars, XPC_RP_VARS_SIZE, 554*45d9ca49SDean Nelson (BTE_NOTIFY | BTE_WACQUIRE), NULL); 555*45d9ca49SDean Nelson if (bres != BTE_SUCCESS) { 556*45d9ca49SDean Nelson return xpc_map_bte_errors(bres); 557*45d9ca49SDean Nelson } 558*45d9ca49SDean Nelson 559*45d9ca49SDean Nelson if (XPC_VERSION_MAJOR(remote_vars->version) != 560*45d9ca49SDean Nelson XPC_VERSION_MAJOR(XPC_V_VERSION)) { 561*45d9ca49SDean Nelson return xpcBadVersion; 562*45d9ca49SDean Nelson } 563*45d9ca49SDean Nelson 564*45d9ca49SDean Nelson return xpcSuccess; 565*45d9ca49SDean Nelson } 566*45d9ca49SDean Nelson 567*45d9ca49SDean Nelson 568*45d9ca49SDean Nelson /* 569*45d9ca49SDean Nelson * Update the remote partition's info. 570*45d9ca49SDean Nelson */ 571*45d9ca49SDean Nelson static void 572*45d9ca49SDean Nelson xpc_update_partition_info(struct xpc_partition *part, u8 remote_rp_version, 573*45d9ca49SDean Nelson struct timespec *remote_rp_stamp, u64 remote_rp_pa, 574*45d9ca49SDean Nelson u64 remote_vars_pa, struct xpc_vars *remote_vars) 575*45d9ca49SDean Nelson { 576*45d9ca49SDean Nelson part->remote_rp_version = remote_rp_version; 577*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_rp_version = 0x%016x\n", 578*45d9ca49SDean Nelson part->remote_rp_version); 579*45d9ca49SDean Nelson 580*45d9ca49SDean Nelson part->remote_rp_stamp = *remote_rp_stamp; 581*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_rp_stamp (tv_sec = 0x%lx tv_nsec = 0x%lx\n", 582*45d9ca49SDean Nelson part->remote_rp_stamp.tv_sec, part->remote_rp_stamp.tv_nsec); 583*45d9ca49SDean Nelson 584*45d9ca49SDean Nelson part->remote_rp_pa = remote_rp_pa; 585*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_rp_pa = 0x%016lx\n", part->remote_rp_pa); 586*45d9ca49SDean Nelson 587*45d9ca49SDean Nelson part->remote_vars_pa = remote_vars_pa; 588*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_vars_pa = 0x%016lx\n", 589*45d9ca49SDean Nelson part->remote_vars_pa); 590*45d9ca49SDean Nelson 591*45d9ca49SDean Nelson part->last_heartbeat = remote_vars->heartbeat; 592*45d9ca49SDean Nelson dev_dbg(xpc_part, " last_heartbeat = 0x%016lx\n", 593*45d9ca49SDean Nelson part->last_heartbeat); 594*45d9ca49SDean Nelson 595*45d9ca49SDean Nelson part->remote_vars_part_pa = remote_vars->vars_part_pa; 596*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_vars_part_pa = 0x%016lx\n", 597*45d9ca49SDean Nelson part->remote_vars_part_pa); 598*45d9ca49SDean Nelson 599*45d9ca49SDean Nelson part->remote_act_nasid = remote_vars->act_nasid; 600*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_act_nasid = 0x%x\n", 601*45d9ca49SDean Nelson part->remote_act_nasid); 602*45d9ca49SDean Nelson 603*45d9ca49SDean Nelson part->remote_act_phys_cpuid = remote_vars->act_phys_cpuid; 604*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_act_phys_cpuid = 0x%x\n", 605*45d9ca49SDean Nelson part->remote_act_phys_cpuid); 606*45d9ca49SDean Nelson 607*45d9ca49SDean Nelson part->remote_amos_page_pa = remote_vars->amos_page_pa; 608*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_amos_page_pa = 0x%lx\n", 609*45d9ca49SDean Nelson part->remote_amos_page_pa); 610*45d9ca49SDean Nelson 611*45d9ca49SDean Nelson part->remote_vars_version = remote_vars->version; 612*45d9ca49SDean Nelson dev_dbg(xpc_part, " remote_vars_version = 0x%x\n", 613*45d9ca49SDean Nelson part->remote_vars_version); 614*45d9ca49SDean Nelson } 615*45d9ca49SDean Nelson 616*45d9ca49SDean Nelson 617*45d9ca49SDean Nelson /* 618*45d9ca49SDean Nelson * Prior code has determined the nasid which generated an IPI. Inspect 619*45d9ca49SDean Nelson * that nasid to determine if its partition needs to be activated or 620*45d9ca49SDean Nelson * deactivated. 621*45d9ca49SDean Nelson * 622*45d9ca49SDean Nelson * A partition is consider "awaiting activation" if our partition 623*45d9ca49SDean Nelson * flags indicate it is not active and it has a heartbeat. A 624*45d9ca49SDean Nelson * partition is considered "awaiting deactivation" if our partition 625*45d9ca49SDean Nelson * flags indicate it is active but it has no heartbeat or it is not 626*45d9ca49SDean Nelson * sending its heartbeat to us. 627*45d9ca49SDean Nelson * 628*45d9ca49SDean Nelson * To determine the heartbeat, the remote nasid must have a properly 629*45d9ca49SDean Nelson * initialized reserved page. 630*45d9ca49SDean Nelson */ 631*45d9ca49SDean Nelson static void 632*45d9ca49SDean Nelson xpc_identify_act_IRQ_req(int nasid) 633*45d9ca49SDean Nelson { 634*45d9ca49SDean Nelson struct xpc_rsvd_page *remote_rp; 635*45d9ca49SDean Nelson struct xpc_vars *remote_vars; 636*45d9ca49SDean Nelson u64 remote_rp_pa; 637*45d9ca49SDean Nelson u64 remote_vars_pa; 638*45d9ca49SDean Nelson int remote_rp_version; 639*45d9ca49SDean Nelson int reactivate = 0; 640*45d9ca49SDean Nelson int stamp_diff; 641*45d9ca49SDean Nelson struct timespec remote_rp_stamp = { 0, 0 }; 642*45d9ca49SDean Nelson partid_t partid; 643*45d9ca49SDean Nelson struct xpc_partition *part; 644*45d9ca49SDean Nelson enum xpc_retval ret; 645*45d9ca49SDean Nelson 646*45d9ca49SDean Nelson 647*45d9ca49SDean Nelson /* pull over the reserved page structure */ 648*45d9ca49SDean Nelson 649*45d9ca49SDean Nelson remote_rp = (struct xpc_rsvd_page *) xpc_remote_copy_buffer; 650*45d9ca49SDean Nelson 651*45d9ca49SDean Nelson ret = xpc_get_remote_rp(nasid, NULL, remote_rp, &remote_rp_pa); 652*45d9ca49SDean Nelson if (ret != xpcSuccess) { 653*45d9ca49SDean Nelson dev_warn(xpc_part, "unable to get reserved page from nasid %d, " 654*45d9ca49SDean Nelson "which sent interrupt, reason=%d\n", nasid, ret); 655*45d9ca49SDean Nelson return; 656*45d9ca49SDean Nelson } 657*45d9ca49SDean Nelson 658*45d9ca49SDean Nelson remote_vars_pa = remote_rp->vars_pa; 659*45d9ca49SDean Nelson remote_rp_version = remote_rp->version; 660*45d9ca49SDean Nelson if (XPC_SUPPORTS_RP_STAMP(remote_rp_version)) { 661*45d9ca49SDean Nelson remote_rp_stamp = remote_rp->stamp; 662*45d9ca49SDean Nelson } 663*45d9ca49SDean Nelson partid = remote_rp->partid; 664*45d9ca49SDean Nelson part = &xpc_partitions[partid]; 665*45d9ca49SDean Nelson 666*45d9ca49SDean Nelson 667*45d9ca49SDean Nelson /* pull over the cross partition variables */ 668*45d9ca49SDean Nelson 669*45d9ca49SDean Nelson remote_vars = (struct xpc_vars *) xpc_remote_copy_buffer; 670*45d9ca49SDean Nelson 671*45d9ca49SDean Nelson ret = xpc_get_remote_vars(remote_vars_pa, remote_vars); 672*45d9ca49SDean Nelson if (ret != xpcSuccess) { 673*45d9ca49SDean Nelson 674*45d9ca49SDean Nelson dev_warn(xpc_part, "unable to get XPC variables from nasid %d, " 675*45d9ca49SDean Nelson "which sent interrupt, reason=%d\n", nasid, ret); 676*45d9ca49SDean Nelson 677*45d9ca49SDean Nelson XPC_DEACTIVATE_PARTITION(part, ret); 678*45d9ca49SDean Nelson return; 679*45d9ca49SDean Nelson } 680*45d9ca49SDean Nelson 681*45d9ca49SDean Nelson 682*45d9ca49SDean Nelson part->act_IRQ_rcvd++; 683*45d9ca49SDean Nelson 684*45d9ca49SDean Nelson dev_dbg(xpc_part, "partid for nasid %d is %d; IRQs = %d; HB = " 685*45d9ca49SDean Nelson "%ld:0x%lx\n", (int) nasid, (int) partid, part->act_IRQ_rcvd, 686*45d9ca49SDean Nelson remote_vars->heartbeat, remote_vars->heartbeating_to_mask); 687*45d9ca49SDean Nelson 688*45d9ca49SDean Nelson if (xpc_partition_disengaged(part) && 689*45d9ca49SDean Nelson part->act_state == XPC_P_INACTIVE) { 690*45d9ca49SDean Nelson 691*45d9ca49SDean Nelson xpc_update_partition_info(part, remote_rp_version, 692*45d9ca49SDean Nelson &remote_rp_stamp, remote_rp_pa, 693*45d9ca49SDean Nelson remote_vars_pa, remote_vars); 694*45d9ca49SDean Nelson 695*45d9ca49SDean Nelson if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version)) { 696*45d9ca49SDean Nelson if (xpc_partition_disengage_requested(1UL << partid)) { 697*45d9ca49SDean Nelson /* 698*45d9ca49SDean Nelson * Other side is waiting on us to disengage, 699*45d9ca49SDean Nelson * even though we already have. 700*45d9ca49SDean Nelson */ 701*45d9ca49SDean Nelson return; 702*45d9ca49SDean Nelson } 703*45d9ca49SDean Nelson } else { 704*45d9ca49SDean Nelson /* other side doesn't support disengage requests */ 705*45d9ca49SDean Nelson xpc_clear_partition_disengage_request(1UL << partid); 706*45d9ca49SDean Nelson } 707*45d9ca49SDean Nelson 708*45d9ca49SDean Nelson xpc_activate_partition(part); 709*45d9ca49SDean Nelson return; 710*45d9ca49SDean Nelson } 711*45d9ca49SDean Nelson 712*45d9ca49SDean Nelson DBUG_ON(part->remote_rp_version == 0); 713*45d9ca49SDean Nelson DBUG_ON(part->remote_vars_version == 0); 714*45d9ca49SDean Nelson 715*45d9ca49SDean Nelson if (!XPC_SUPPORTS_RP_STAMP(part->remote_rp_version)) { 716*45d9ca49SDean Nelson DBUG_ON(XPC_SUPPORTS_DISENGAGE_REQUEST(part-> 717*45d9ca49SDean Nelson remote_vars_version)); 718*45d9ca49SDean Nelson 719*45d9ca49SDean Nelson if (!XPC_SUPPORTS_RP_STAMP(remote_rp_version)) { 720*45d9ca49SDean Nelson DBUG_ON(XPC_SUPPORTS_DISENGAGE_REQUEST(remote_vars-> 721*45d9ca49SDean Nelson version)); 722*45d9ca49SDean Nelson /* see if the other side rebooted */ 723*45d9ca49SDean Nelson if (part->remote_amos_page_pa == 724*45d9ca49SDean Nelson remote_vars->amos_page_pa && 725*45d9ca49SDean Nelson xpc_hb_allowed(sn_partition_id, 726*45d9ca49SDean Nelson remote_vars)) { 727*45d9ca49SDean Nelson /* doesn't look that way, so ignore the IPI */ 728*45d9ca49SDean Nelson return; 729*45d9ca49SDean Nelson } 730*45d9ca49SDean Nelson } 731*45d9ca49SDean Nelson 732*45d9ca49SDean Nelson /* 733*45d9ca49SDean Nelson * Other side rebooted and previous XPC didn't support the 734*45d9ca49SDean Nelson * disengage request, so we don't need to do anything special. 735*45d9ca49SDean Nelson */ 736*45d9ca49SDean Nelson 737*45d9ca49SDean Nelson xpc_update_partition_info(part, remote_rp_version, 738*45d9ca49SDean Nelson &remote_rp_stamp, remote_rp_pa, 739*45d9ca49SDean Nelson remote_vars_pa, remote_vars); 740*45d9ca49SDean Nelson part->reactivate_nasid = nasid; 741*45d9ca49SDean Nelson XPC_DEACTIVATE_PARTITION(part, xpcReactivating); 742*45d9ca49SDean Nelson return; 743*45d9ca49SDean Nelson } 744*45d9ca49SDean Nelson 745*45d9ca49SDean Nelson DBUG_ON(!XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version)); 746*45d9ca49SDean Nelson 747*45d9ca49SDean Nelson if (!XPC_SUPPORTS_RP_STAMP(remote_rp_version)) { 748*45d9ca49SDean Nelson DBUG_ON(!XPC_SUPPORTS_DISENGAGE_REQUEST(remote_vars->version)); 749*45d9ca49SDean Nelson 750*45d9ca49SDean Nelson /* 751*45d9ca49SDean Nelson * Other side rebooted and previous XPC did support the 752*45d9ca49SDean Nelson * disengage request, but the new one doesn't. 753*45d9ca49SDean Nelson */ 754*45d9ca49SDean Nelson 755*45d9ca49SDean Nelson xpc_clear_partition_engaged(1UL << partid); 756*45d9ca49SDean Nelson xpc_clear_partition_disengage_request(1UL << partid); 757*45d9ca49SDean Nelson 758*45d9ca49SDean Nelson xpc_update_partition_info(part, remote_rp_version, 759*45d9ca49SDean Nelson &remote_rp_stamp, remote_rp_pa, 760*45d9ca49SDean Nelson remote_vars_pa, remote_vars); 761*45d9ca49SDean Nelson reactivate = 1; 762*45d9ca49SDean Nelson 763*45d9ca49SDean Nelson } else { 764*45d9ca49SDean Nelson DBUG_ON(!XPC_SUPPORTS_DISENGAGE_REQUEST(remote_vars->version)); 765*45d9ca49SDean Nelson 766*45d9ca49SDean Nelson stamp_diff = xpc_compare_stamps(&part->remote_rp_stamp, 767*45d9ca49SDean Nelson &remote_rp_stamp); 768*45d9ca49SDean Nelson if (stamp_diff != 0) { 769*45d9ca49SDean Nelson DBUG_ON(stamp_diff >= 0); 770*45d9ca49SDean Nelson 771*45d9ca49SDean Nelson /* 772*45d9ca49SDean Nelson * Other side rebooted and the previous XPC did support 773*45d9ca49SDean Nelson * the disengage request, as does the new one. 774*45d9ca49SDean Nelson */ 775*45d9ca49SDean Nelson 776*45d9ca49SDean Nelson DBUG_ON(xpc_partition_engaged(1UL << partid)); 777*45d9ca49SDean Nelson DBUG_ON(xpc_partition_disengage_requested(1UL << 778*45d9ca49SDean Nelson partid)); 779*45d9ca49SDean Nelson 780*45d9ca49SDean Nelson xpc_update_partition_info(part, remote_rp_version, 781*45d9ca49SDean Nelson &remote_rp_stamp, remote_rp_pa, 782*45d9ca49SDean Nelson remote_vars_pa, remote_vars); 783*45d9ca49SDean Nelson reactivate = 1; 784*45d9ca49SDean Nelson } 785*45d9ca49SDean Nelson } 786*45d9ca49SDean Nelson 787*45d9ca49SDean Nelson if (part->disengage_request_timeout > 0 && 788*45d9ca49SDean Nelson !xpc_partition_disengaged(part)) { 789*45d9ca49SDean Nelson /* still waiting on other side to disengage from us */ 790*45d9ca49SDean Nelson return; 791*45d9ca49SDean Nelson } 792*45d9ca49SDean Nelson 793*45d9ca49SDean Nelson if (reactivate) { 794*45d9ca49SDean Nelson part->reactivate_nasid = nasid; 795*45d9ca49SDean Nelson XPC_DEACTIVATE_PARTITION(part, xpcReactivating); 796*45d9ca49SDean Nelson 797*45d9ca49SDean Nelson } else if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version) && 798*45d9ca49SDean Nelson xpc_partition_disengage_requested(1UL << partid)) { 799*45d9ca49SDean Nelson XPC_DEACTIVATE_PARTITION(part, xpcOtherGoingDown); 800*45d9ca49SDean Nelson } 801*45d9ca49SDean Nelson } 802*45d9ca49SDean Nelson 803*45d9ca49SDean Nelson 804*45d9ca49SDean Nelson /* 805*45d9ca49SDean Nelson * Loop through the activation AMO variables and process any bits 806*45d9ca49SDean Nelson * which are set. Each bit indicates a nasid sending a partition 807*45d9ca49SDean Nelson * activation or deactivation request. 808*45d9ca49SDean Nelson * 809*45d9ca49SDean Nelson * Return #of IRQs detected. 810*45d9ca49SDean Nelson */ 811*45d9ca49SDean Nelson int 812*45d9ca49SDean Nelson xpc_identify_act_IRQ_sender(void) 813*45d9ca49SDean Nelson { 814*45d9ca49SDean Nelson int word, bit; 815*45d9ca49SDean Nelson u64 nasid_mask; 816*45d9ca49SDean Nelson u64 nasid; /* remote nasid */ 817*45d9ca49SDean Nelson int n_IRQs_detected = 0; 818*45d9ca49SDean Nelson AMO_t *act_amos; 819*45d9ca49SDean Nelson 820*45d9ca49SDean Nelson 821*45d9ca49SDean Nelson act_amos = xpc_vars->amos_page + XPC_ACTIVATE_IRQ_AMOS; 822*45d9ca49SDean Nelson 823*45d9ca49SDean Nelson 824*45d9ca49SDean Nelson /* scan through act AMO variable looking for non-zero entries */ 825*45d9ca49SDean Nelson for (word = 0; word < xp_nasid_mask_words; word++) { 826*45d9ca49SDean Nelson 827*45d9ca49SDean Nelson if (xpc_exiting) { 828*45d9ca49SDean Nelson break; 829*45d9ca49SDean Nelson } 830*45d9ca49SDean Nelson 831*45d9ca49SDean Nelson nasid_mask = xpc_IPI_receive(&act_amos[word]); 832*45d9ca49SDean Nelson if (nasid_mask == 0) { 833*45d9ca49SDean Nelson /* no IRQs from nasids in this variable */ 834*45d9ca49SDean Nelson continue; 835*45d9ca49SDean Nelson } 836*45d9ca49SDean Nelson 837*45d9ca49SDean Nelson dev_dbg(xpc_part, "AMO[%d] gave back 0x%lx\n", word, 838*45d9ca49SDean Nelson nasid_mask); 839*45d9ca49SDean Nelson 840*45d9ca49SDean Nelson 841*45d9ca49SDean Nelson /* 842*45d9ca49SDean Nelson * If this nasid has been added to the machine since 843*45d9ca49SDean Nelson * our partition was reset, this will retain the 844*45d9ca49SDean Nelson * remote nasid in our reserved pages machine mask. 845*45d9ca49SDean Nelson * This is used in the event of module reload. 846*45d9ca49SDean Nelson */ 847*45d9ca49SDean Nelson xpc_mach_nasids[word] |= nasid_mask; 848*45d9ca49SDean Nelson 849*45d9ca49SDean Nelson 850*45d9ca49SDean Nelson /* locate the nasid(s) which sent interrupts */ 851*45d9ca49SDean Nelson 852*45d9ca49SDean Nelson for (bit = 0; bit < (8 * sizeof(u64)); bit++) { 853*45d9ca49SDean Nelson if (nasid_mask & (1UL << bit)) { 854*45d9ca49SDean Nelson n_IRQs_detected++; 855*45d9ca49SDean Nelson nasid = XPC_NASID_FROM_W_B(word, bit); 856*45d9ca49SDean Nelson dev_dbg(xpc_part, "interrupt from nasid %ld\n", 857*45d9ca49SDean Nelson nasid); 858*45d9ca49SDean Nelson xpc_identify_act_IRQ_req(nasid); 859*45d9ca49SDean Nelson } 860*45d9ca49SDean Nelson } 861*45d9ca49SDean Nelson } 862*45d9ca49SDean Nelson return n_IRQs_detected; 863*45d9ca49SDean Nelson } 864*45d9ca49SDean Nelson 865*45d9ca49SDean Nelson 866*45d9ca49SDean Nelson /* 867*45d9ca49SDean Nelson * See if the other side has responded to a partition disengage request 868*45d9ca49SDean Nelson * from us. 869*45d9ca49SDean Nelson */ 870*45d9ca49SDean Nelson int 871*45d9ca49SDean Nelson xpc_partition_disengaged(struct xpc_partition *part) 872*45d9ca49SDean Nelson { 873*45d9ca49SDean Nelson partid_t partid = XPC_PARTID(part); 874*45d9ca49SDean Nelson int disengaged; 875*45d9ca49SDean Nelson 876*45d9ca49SDean Nelson 877*45d9ca49SDean Nelson disengaged = (xpc_partition_engaged(1UL << partid) == 0); 878*45d9ca49SDean Nelson if (part->disengage_request_timeout) { 879*45d9ca49SDean Nelson if (!disengaged) { 880*45d9ca49SDean Nelson if (time_before(jiffies, part->disengage_request_timeout)) { 881*45d9ca49SDean Nelson /* timelimit hasn't been reached yet */ 882*45d9ca49SDean Nelson return 0; 883*45d9ca49SDean Nelson } 884*45d9ca49SDean Nelson 885*45d9ca49SDean Nelson /* 886*45d9ca49SDean Nelson * Other side hasn't responded to our disengage 887*45d9ca49SDean Nelson * request in a timely fashion, so assume it's dead. 888*45d9ca49SDean Nelson */ 889*45d9ca49SDean Nelson 890*45d9ca49SDean Nelson dev_info(xpc_part, "disengage from remote partition %d " 891*45d9ca49SDean Nelson "timed out\n", partid); 892*45d9ca49SDean Nelson xpc_disengage_request_timedout = 1; 893*45d9ca49SDean Nelson xpc_clear_partition_engaged(1UL << partid); 894*45d9ca49SDean Nelson disengaged = 1; 895*45d9ca49SDean Nelson } 896*45d9ca49SDean Nelson part->disengage_request_timeout = 0; 897*45d9ca49SDean Nelson 898*45d9ca49SDean Nelson /* cancel the timer function, provided it's not us */ 899*45d9ca49SDean Nelson if (!in_interrupt()) { 900*45d9ca49SDean Nelson del_singleshot_timer_sync(&part-> 901*45d9ca49SDean Nelson disengage_request_timer); 902*45d9ca49SDean Nelson } 903*45d9ca49SDean Nelson 904*45d9ca49SDean Nelson DBUG_ON(part->act_state != XPC_P_DEACTIVATING && 905*45d9ca49SDean Nelson part->act_state != XPC_P_INACTIVE); 906*45d9ca49SDean Nelson if (part->act_state != XPC_P_INACTIVE) { 907*45d9ca49SDean Nelson xpc_wakeup_channel_mgr(part); 908*45d9ca49SDean Nelson } 909*45d9ca49SDean Nelson 910*45d9ca49SDean Nelson if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version)) { 911*45d9ca49SDean Nelson xpc_cancel_partition_disengage_request(part); 912*45d9ca49SDean Nelson } 913*45d9ca49SDean Nelson } 914*45d9ca49SDean Nelson return disengaged; 915*45d9ca49SDean Nelson } 916*45d9ca49SDean Nelson 917*45d9ca49SDean Nelson 918*45d9ca49SDean Nelson /* 919*45d9ca49SDean Nelson * Mark specified partition as active. 920*45d9ca49SDean Nelson */ 921*45d9ca49SDean Nelson enum xpc_retval 922*45d9ca49SDean Nelson xpc_mark_partition_active(struct xpc_partition *part) 923*45d9ca49SDean Nelson { 924*45d9ca49SDean Nelson unsigned long irq_flags; 925*45d9ca49SDean Nelson enum xpc_retval ret; 926*45d9ca49SDean Nelson 927*45d9ca49SDean Nelson 928*45d9ca49SDean Nelson dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part)); 929*45d9ca49SDean Nelson 930*45d9ca49SDean Nelson spin_lock_irqsave(&part->act_lock, irq_flags); 931*45d9ca49SDean Nelson if (part->act_state == XPC_P_ACTIVATING) { 932*45d9ca49SDean Nelson part->act_state = XPC_P_ACTIVE; 933*45d9ca49SDean Nelson ret = xpcSuccess; 934*45d9ca49SDean Nelson } else { 935*45d9ca49SDean Nelson DBUG_ON(part->reason == xpcSuccess); 936*45d9ca49SDean Nelson ret = part->reason; 937*45d9ca49SDean Nelson } 938*45d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags); 939*45d9ca49SDean Nelson 940*45d9ca49SDean Nelson return ret; 941*45d9ca49SDean Nelson } 942*45d9ca49SDean Nelson 943*45d9ca49SDean Nelson 944*45d9ca49SDean Nelson /* 945*45d9ca49SDean Nelson * Notify XPC that the partition is down. 946*45d9ca49SDean Nelson */ 947*45d9ca49SDean Nelson void 948*45d9ca49SDean Nelson xpc_deactivate_partition(const int line, struct xpc_partition *part, 949*45d9ca49SDean Nelson enum xpc_retval reason) 950*45d9ca49SDean Nelson { 951*45d9ca49SDean Nelson unsigned long irq_flags; 952*45d9ca49SDean Nelson 953*45d9ca49SDean Nelson 954*45d9ca49SDean Nelson spin_lock_irqsave(&part->act_lock, irq_flags); 955*45d9ca49SDean Nelson 956*45d9ca49SDean Nelson if (part->act_state == XPC_P_INACTIVE) { 957*45d9ca49SDean Nelson XPC_SET_REASON(part, reason, line); 958*45d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags); 959*45d9ca49SDean Nelson if (reason == xpcReactivating) { 960*45d9ca49SDean Nelson /* we interrupt ourselves to reactivate partition */ 961*45d9ca49SDean Nelson xpc_IPI_send_reactivate(part); 962*45d9ca49SDean Nelson } 963*45d9ca49SDean Nelson return; 964*45d9ca49SDean Nelson } 965*45d9ca49SDean Nelson if (part->act_state == XPC_P_DEACTIVATING) { 966*45d9ca49SDean Nelson if ((part->reason == xpcUnloading && reason != xpcUnloading) || 967*45d9ca49SDean Nelson reason == xpcReactivating) { 968*45d9ca49SDean Nelson XPC_SET_REASON(part, reason, line); 969*45d9ca49SDean Nelson } 970*45d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags); 971*45d9ca49SDean Nelson return; 972*45d9ca49SDean Nelson } 973*45d9ca49SDean Nelson 974*45d9ca49SDean Nelson part->act_state = XPC_P_DEACTIVATING; 975*45d9ca49SDean Nelson XPC_SET_REASON(part, reason, line); 976*45d9ca49SDean Nelson 977*45d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags); 978*45d9ca49SDean Nelson 979*45d9ca49SDean Nelson if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version)) { 980*45d9ca49SDean Nelson xpc_request_partition_disengage(part); 981*45d9ca49SDean Nelson xpc_IPI_send_disengage(part); 982*45d9ca49SDean Nelson 983*45d9ca49SDean Nelson /* set a timelimit on the disengage request */ 984*45d9ca49SDean Nelson part->disengage_request_timeout = jiffies + 985*45d9ca49SDean Nelson (xpc_disengage_request_timelimit * HZ); 986*45d9ca49SDean Nelson part->disengage_request_timer.expires = 987*45d9ca49SDean Nelson part->disengage_request_timeout; 988*45d9ca49SDean Nelson add_timer(&part->disengage_request_timer); 989*45d9ca49SDean Nelson } 990*45d9ca49SDean Nelson 991*45d9ca49SDean Nelson dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n", 992*45d9ca49SDean Nelson XPC_PARTID(part), reason); 993*45d9ca49SDean Nelson 994*45d9ca49SDean Nelson xpc_partition_going_down(part, reason); 995*45d9ca49SDean Nelson } 996*45d9ca49SDean Nelson 997*45d9ca49SDean Nelson 998*45d9ca49SDean Nelson /* 999*45d9ca49SDean Nelson * Mark specified partition as inactive. 1000*45d9ca49SDean Nelson */ 1001*45d9ca49SDean Nelson void 1002*45d9ca49SDean Nelson xpc_mark_partition_inactive(struct xpc_partition *part) 1003*45d9ca49SDean Nelson { 1004*45d9ca49SDean Nelson unsigned long irq_flags; 1005*45d9ca49SDean Nelson 1006*45d9ca49SDean Nelson 1007*45d9ca49SDean Nelson dev_dbg(xpc_part, "setting partition %d to INACTIVE\n", 1008*45d9ca49SDean Nelson XPC_PARTID(part)); 1009*45d9ca49SDean Nelson 1010*45d9ca49SDean Nelson spin_lock_irqsave(&part->act_lock, irq_flags); 1011*45d9ca49SDean Nelson part->act_state = XPC_P_INACTIVE; 1012*45d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags); 1013*45d9ca49SDean Nelson part->remote_rp_pa = 0; 1014*45d9ca49SDean Nelson } 1015*45d9ca49SDean Nelson 1016*45d9ca49SDean Nelson 1017*45d9ca49SDean Nelson /* 1018*45d9ca49SDean Nelson * SAL has provided a partition and machine mask. The partition mask 1019*45d9ca49SDean Nelson * contains a bit for each even nasid in our partition. The machine 1020*45d9ca49SDean Nelson * mask contains a bit for each even nasid in the entire machine. 1021*45d9ca49SDean Nelson * 1022*45d9ca49SDean Nelson * Using those two bit arrays, we can determine which nasids are 1023*45d9ca49SDean Nelson * known in the machine. Each should also have a reserved page 1024*45d9ca49SDean Nelson * initialized if they are available for partitioning. 1025*45d9ca49SDean Nelson */ 1026*45d9ca49SDean Nelson void 1027*45d9ca49SDean Nelson xpc_discovery(void) 1028*45d9ca49SDean Nelson { 1029*45d9ca49SDean Nelson void *remote_rp_base; 1030*45d9ca49SDean Nelson struct xpc_rsvd_page *remote_rp; 1031*45d9ca49SDean Nelson struct xpc_vars *remote_vars; 1032*45d9ca49SDean Nelson u64 remote_rp_pa; 1033*45d9ca49SDean Nelson u64 remote_vars_pa; 1034*45d9ca49SDean Nelson int region; 1035*45d9ca49SDean Nelson int region_size; 1036*45d9ca49SDean Nelson int max_regions; 1037*45d9ca49SDean Nelson int nasid; 1038*45d9ca49SDean Nelson struct xpc_rsvd_page *rp; 1039*45d9ca49SDean Nelson partid_t partid; 1040*45d9ca49SDean Nelson struct xpc_partition *part; 1041*45d9ca49SDean Nelson u64 *discovered_nasids; 1042*45d9ca49SDean Nelson enum xpc_retval ret; 1043*45d9ca49SDean Nelson 1044*45d9ca49SDean Nelson 1045*45d9ca49SDean Nelson remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE + 1046*45d9ca49SDean Nelson xp_nasid_mask_bytes, 1047*45d9ca49SDean Nelson GFP_KERNEL, &remote_rp_base); 1048*45d9ca49SDean Nelson if (remote_rp == NULL) { 1049*45d9ca49SDean Nelson return; 1050*45d9ca49SDean Nelson } 1051*45d9ca49SDean Nelson remote_vars = (struct xpc_vars *) remote_rp; 1052*45d9ca49SDean Nelson 1053*45d9ca49SDean Nelson 1054*45d9ca49SDean Nelson discovered_nasids = kzalloc(sizeof(u64) * xp_nasid_mask_words, 1055*45d9ca49SDean Nelson GFP_KERNEL); 1056*45d9ca49SDean Nelson if (discovered_nasids == NULL) { 1057*45d9ca49SDean Nelson kfree(remote_rp_base); 1058*45d9ca49SDean Nelson return; 1059*45d9ca49SDean Nelson } 1060*45d9ca49SDean Nelson 1061*45d9ca49SDean Nelson rp = (struct xpc_rsvd_page *) xpc_rsvd_page; 1062*45d9ca49SDean Nelson 1063*45d9ca49SDean Nelson /* 1064*45d9ca49SDean Nelson * The term 'region' in this context refers to the minimum number of 1065*45d9ca49SDean Nelson * nodes that can comprise an access protection grouping. The access 1066*45d9ca49SDean Nelson * protection is in regards to memory, IOI and IPI. 1067*45d9ca49SDean Nelson */ 1068*45d9ca49SDean Nelson max_regions = 64; 1069*45d9ca49SDean Nelson region_size = sn_region_size; 1070*45d9ca49SDean Nelson 1071*45d9ca49SDean Nelson switch (region_size) { 1072*45d9ca49SDean Nelson case 128: 1073*45d9ca49SDean Nelson max_regions *= 2; 1074*45d9ca49SDean Nelson case 64: 1075*45d9ca49SDean Nelson max_regions *= 2; 1076*45d9ca49SDean Nelson case 32: 1077*45d9ca49SDean Nelson max_regions *= 2; 1078*45d9ca49SDean Nelson region_size = 16; 1079*45d9ca49SDean Nelson DBUG_ON(!is_shub2()); 1080*45d9ca49SDean Nelson } 1081*45d9ca49SDean Nelson 1082*45d9ca49SDean Nelson for (region = 0; region < max_regions; region++) { 1083*45d9ca49SDean Nelson 1084*45d9ca49SDean Nelson if ((volatile int) xpc_exiting) { 1085*45d9ca49SDean Nelson break; 1086*45d9ca49SDean Nelson } 1087*45d9ca49SDean Nelson 1088*45d9ca49SDean Nelson dev_dbg(xpc_part, "searching region %d\n", region); 1089*45d9ca49SDean Nelson 1090*45d9ca49SDean Nelson for (nasid = (region * region_size * 2); 1091*45d9ca49SDean Nelson nasid < ((region + 1) * region_size * 2); 1092*45d9ca49SDean Nelson nasid += 2) { 1093*45d9ca49SDean Nelson 1094*45d9ca49SDean Nelson if ((volatile int) xpc_exiting) { 1095*45d9ca49SDean Nelson break; 1096*45d9ca49SDean Nelson } 1097*45d9ca49SDean Nelson 1098*45d9ca49SDean Nelson dev_dbg(xpc_part, "checking nasid %d\n", nasid); 1099*45d9ca49SDean Nelson 1100*45d9ca49SDean Nelson 1101*45d9ca49SDean Nelson if (XPC_NASID_IN_ARRAY(nasid, xpc_part_nasids)) { 1102*45d9ca49SDean Nelson dev_dbg(xpc_part, "PROM indicates Nasid %d is " 1103*45d9ca49SDean Nelson "part of the local partition; skipping " 1104*45d9ca49SDean Nelson "region\n", nasid); 1105*45d9ca49SDean Nelson break; 1106*45d9ca49SDean Nelson } 1107*45d9ca49SDean Nelson 1108*45d9ca49SDean Nelson if (!(XPC_NASID_IN_ARRAY(nasid, xpc_mach_nasids))) { 1109*45d9ca49SDean Nelson dev_dbg(xpc_part, "PROM indicates Nasid %d was " 1110*45d9ca49SDean Nelson "not on Numa-Link network at reset\n", 1111*45d9ca49SDean Nelson nasid); 1112*45d9ca49SDean Nelson continue; 1113*45d9ca49SDean Nelson } 1114*45d9ca49SDean Nelson 1115*45d9ca49SDean Nelson if (XPC_NASID_IN_ARRAY(nasid, discovered_nasids)) { 1116*45d9ca49SDean Nelson dev_dbg(xpc_part, "Nasid %d is part of a " 1117*45d9ca49SDean Nelson "partition which was previously " 1118*45d9ca49SDean Nelson "discovered\n", nasid); 1119*45d9ca49SDean Nelson continue; 1120*45d9ca49SDean Nelson } 1121*45d9ca49SDean Nelson 1122*45d9ca49SDean Nelson 1123*45d9ca49SDean Nelson /* pull over the reserved page structure */ 1124*45d9ca49SDean Nelson 1125*45d9ca49SDean Nelson ret = xpc_get_remote_rp(nasid, discovered_nasids, 1126*45d9ca49SDean Nelson remote_rp, &remote_rp_pa); 1127*45d9ca49SDean Nelson if (ret != xpcSuccess) { 1128*45d9ca49SDean Nelson dev_dbg(xpc_part, "unable to get reserved page " 1129*45d9ca49SDean Nelson "from nasid %d, reason=%d\n", nasid, 1130*45d9ca49SDean Nelson ret); 1131*45d9ca49SDean Nelson 1132*45d9ca49SDean Nelson if (ret == xpcLocalPartid) { 1133*45d9ca49SDean Nelson break; 1134*45d9ca49SDean Nelson } 1135*45d9ca49SDean Nelson continue; 1136*45d9ca49SDean Nelson } 1137*45d9ca49SDean Nelson 1138*45d9ca49SDean Nelson remote_vars_pa = remote_rp->vars_pa; 1139*45d9ca49SDean Nelson 1140*45d9ca49SDean Nelson partid = remote_rp->partid; 1141*45d9ca49SDean Nelson part = &xpc_partitions[partid]; 1142*45d9ca49SDean Nelson 1143*45d9ca49SDean Nelson 1144*45d9ca49SDean Nelson /* pull over the cross partition variables */ 1145*45d9ca49SDean Nelson 1146*45d9ca49SDean Nelson ret = xpc_get_remote_vars(remote_vars_pa, remote_vars); 1147*45d9ca49SDean Nelson if (ret != xpcSuccess) { 1148*45d9ca49SDean Nelson dev_dbg(xpc_part, "unable to get XPC variables " 1149*45d9ca49SDean Nelson "from nasid %d, reason=%d\n", nasid, 1150*45d9ca49SDean Nelson ret); 1151*45d9ca49SDean Nelson 1152*45d9ca49SDean Nelson XPC_DEACTIVATE_PARTITION(part, ret); 1153*45d9ca49SDean Nelson continue; 1154*45d9ca49SDean Nelson } 1155*45d9ca49SDean Nelson 1156*45d9ca49SDean Nelson if (part->act_state != XPC_P_INACTIVE) { 1157*45d9ca49SDean Nelson dev_dbg(xpc_part, "partition %d on nasid %d is " 1158*45d9ca49SDean Nelson "already activating\n", partid, nasid); 1159*45d9ca49SDean Nelson break; 1160*45d9ca49SDean Nelson } 1161*45d9ca49SDean Nelson 1162*45d9ca49SDean Nelson /* 1163*45d9ca49SDean Nelson * Register the remote partition's AMOs with SAL so it 1164*45d9ca49SDean Nelson * can handle and cleanup errors within that address 1165*45d9ca49SDean Nelson * range should the remote partition go down. We don't 1166*45d9ca49SDean Nelson * unregister this range because it is difficult to 1167*45d9ca49SDean Nelson * tell when outstanding writes to the remote partition 1168*45d9ca49SDean Nelson * are finished and thus when it is thus safe to 1169*45d9ca49SDean Nelson * unregister. This should not result in wasted space 1170*45d9ca49SDean Nelson * in the SAL xp_addr_region table because we should 1171*45d9ca49SDean Nelson * get the same page for remote_act_amos_pa after 1172*45d9ca49SDean Nelson * module reloads and system reboots. 1173*45d9ca49SDean Nelson */ 1174*45d9ca49SDean Nelson if (sn_register_xp_addr_region( 1175*45d9ca49SDean Nelson remote_vars->amos_page_pa, 1176*45d9ca49SDean Nelson PAGE_SIZE, 1) < 0) { 1177*45d9ca49SDean Nelson dev_dbg(xpc_part, "partition %d failed to " 1178*45d9ca49SDean Nelson "register xp_addr region 0x%016lx\n", 1179*45d9ca49SDean Nelson partid, remote_vars->amos_page_pa); 1180*45d9ca49SDean Nelson 1181*45d9ca49SDean Nelson XPC_SET_REASON(part, xpcPhysAddrRegFailed, 1182*45d9ca49SDean Nelson __LINE__); 1183*45d9ca49SDean Nelson break; 1184*45d9ca49SDean Nelson } 1185*45d9ca49SDean Nelson 1186*45d9ca49SDean Nelson /* 1187*45d9ca49SDean Nelson * The remote nasid is valid and available. 1188*45d9ca49SDean Nelson * Send an interrupt to that nasid to notify 1189*45d9ca49SDean Nelson * it that we are ready to begin activation. 1190*45d9ca49SDean Nelson */ 1191*45d9ca49SDean Nelson dev_dbg(xpc_part, "sending an interrupt to AMO 0x%lx, " 1192*45d9ca49SDean Nelson "nasid %d, phys_cpuid 0x%x\n", 1193*45d9ca49SDean Nelson remote_vars->amos_page_pa, 1194*45d9ca49SDean Nelson remote_vars->act_nasid, 1195*45d9ca49SDean Nelson remote_vars->act_phys_cpuid); 1196*45d9ca49SDean Nelson 1197*45d9ca49SDean Nelson if (XPC_SUPPORTS_DISENGAGE_REQUEST(remote_vars-> 1198*45d9ca49SDean Nelson version)) { 1199*45d9ca49SDean Nelson part->remote_amos_page_pa = 1200*45d9ca49SDean Nelson remote_vars->amos_page_pa; 1201*45d9ca49SDean Nelson xpc_mark_partition_disengaged(part); 1202*45d9ca49SDean Nelson xpc_cancel_partition_disengage_request(part); 1203*45d9ca49SDean Nelson } 1204*45d9ca49SDean Nelson xpc_IPI_send_activate(remote_vars); 1205*45d9ca49SDean Nelson } 1206*45d9ca49SDean Nelson } 1207*45d9ca49SDean Nelson 1208*45d9ca49SDean Nelson kfree(discovered_nasids); 1209*45d9ca49SDean Nelson kfree(remote_rp_base); 1210*45d9ca49SDean Nelson } 1211*45d9ca49SDean Nelson 1212*45d9ca49SDean Nelson 1213*45d9ca49SDean Nelson /* 1214*45d9ca49SDean Nelson * Given a partid, get the nasids owned by that partition from the 1215*45d9ca49SDean Nelson * remote partition's reserved page. 1216*45d9ca49SDean Nelson */ 1217*45d9ca49SDean Nelson enum xpc_retval 1218*45d9ca49SDean Nelson xpc_initiate_partid_to_nasids(partid_t partid, void *nasid_mask) 1219*45d9ca49SDean Nelson { 1220*45d9ca49SDean Nelson struct xpc_partition *part; 1221*45d9ca49SDean Nelson u64 part_nasid_pa; 1222*45d9ca49SDean Nelson int bte_res; 1223*45d9ca49SDean Nelson 1224*45d9ca49SDean Nelson 1225*45d9ca49SDean Nelson part = &xpc_partitions[partid]; 1226*45d9ca49SDean Nelson if (part->remote_rp_pa == 0) { 1227*45d9ca49SDean Nelson return xpcPartitionDown; 1228*45d9ca49SDean Nelson } 1229*45d9ca49SDean Nelson 1230*45d9ca49SDean Nelson memset(nasid_mask, 0, XP_NASID_MASK_BYTES); 1231*45d9ca49SDean Nelson 1232*45d9ca49SDean Nelson part_nasid_pa = (u64) XPC_RP_PART_NASIDS(part->remote_rp_pa); 1233*45d9ca49SDean Nelson 1234*45d9ca49SDean Nelson bte_res = xp_bte_copy(part_nasid_pa, (u64) nasid_mask, 1235*45d9ca49SDean Nelson xp_nasid_mask_bytes, (BTE_NOTIFY | BTE_WACQUIRE), NULL); 1236*45d9ca49SDean Nelson 1237*45d9ca49SDean Nelson return xpc_map_bte_errors(bte_res); 1238*45d9ca49SDean Nelson } 1239*45d9ca49SDean Nelson 1240