xref: /openbmc/linux/drivers/misc/sgi-xp/xpc_partition.c (revision 45d9ca492e4bd1522d1b5bd125c2908f1cee3d4a)
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