1 /* Copyright 2013-2014 IBM Corp.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * 	http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <skiboot.h>
18 #include "spira.h"
19 #include <cpu.h>
20 #include <fsp.h>
21 #include <opal.h>
22 #include <ccan/str/str.h>
23 #include <device.h>
24 #include <types.h>
25 
26 #include "hdata.h"
27 
28 #define PACA_MAX_THREADS 4
29 
paca_index(const struct HDIF_common_hdr * paca)30 static unsigned int paca_index(const struct HDIF_common_hdr *paca)
31 {
32 	void *start = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
33 	return ((void *)paca - start)
34 		/ be32_to_cpu(spira.ntuples.paca.alloc_len);
35 }
36 
add_cpu_node(struct dt_node * cpus,const struct HDIF_common_hdr * paca,const struct sppaca_cpu_id * id,bool okay)37 static struct dt_node *add_cpu_node(struct dt_node *cpus,
38 				    const struct HDIF_common_hdr *paca,
39 				    const struct sppaca_cpu_id *id,
40 				    bool okay)
41 {
42 	const struct sppaca_cpu_timebase *timebase;
43 	const struct sppaca_cpu_cache *cache;
44 	const struct sppaca_cpu_attr *attr;
45 	struct dt_node *cpu;
46 	u32 no, size, ve_flags, l2_phandle, chip_id;
47 
48 	/* We use the process_interrupt_line as the res id */
49 	no = be32_to_cpu(id->process_interrupt_line);
50 
51 	ve_flags = be32_to_cpu(id->verify_exists_flags);
52 	prlog(PR_INFO, "CPU[%i]: PIR=%i RES=%i %s %s(%u threads)\n",
53 	       paca_index(paca), be32_to_cpu(id->pir), no,
54 	       ve_flags & CPU_ID_PACA_RESERVED
55 	       ? "**RESERVED**" : cpu_state(ve_flags),
56 	       ve_flags & CPU_ID_SECONDARY_THREAD
57 	       ? "[secondary] " :
58 	       (be32_to_cpu(id->pir) == boot_cpu->pir ? "[boot] " : ""),
59 	       ((ve_flags & CPU_ID_NUM_SECONDARY_THREAD_MASK)
60 		>> CPU_ID_NUM_SECONDARY_THREAD_SHIFT) + 1);
61 
62 	timebase = HDIF_get_idata(paca, SPPACA_IDATA_TIMEBASE, &size);
63 	if (!timebase || size < sizeof(*timebase)) {
64 		prerror("CPU[%i]: bad timebase size %u @ %p\n",
65 			paca_index(paca), size, timebase);
66 		return NULL;
67 	}
68 
69 	cache = HDIF_get_idata(paca, SPPACA_IDATA_CACHE_SIZE, &size);
70 	if (!cache || size < sizeof(*cache)) {
71 		prerror("CPU[%i]: bad cache size %u @ %p\n",
72 			paca_index(paca), size, cache);
73 		return NULL;
74 	}
75 
76 	cpu = add_core_common(cpus, cache, timebase, no, okay);
77 
78 	/* Core attributes */
79 	attr = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ATTR, &size);
80 	if (attr)
81 		add_core_attr(cpu, be32_to_cpu(attr->attr));
82 
83 	/* Add cache info */
84 	l2_phandle = add_core_cache_info(cpus, cache, no, okay);
85 	dt_add_property_cells(cpu, "l2-cache", l2_phandle);
86 
87 	/* We append the secondary cpus in __cpu_parse */
88 	dt_add_property_cells(cpu, "ibm,ppc-interrupt-server#s", no);
89 
90 	dt_add_property_cells(cpu, DT_PRIVATE "hw_proc_id",
91 			      be32_to_cpu(id->hardware_proc_id));
92 	dt_add_property_cells(cpu, "ibm,pir", be32_to_cpu(id->pir));
93 
94 	chip_id = pcid_to_chip_id(be32_to_cpu(id->processor_chip_id));
95 	dt_add_property_cells(cpu, "ibm,chip-id", chip_id);
96 
97 	return cpu;
98 }
99 
find_cpu_by_hardware_proc_id(struct dt_node * root,u32 hw_proc_id)100 static struct dt_node *find_cpu_by_hardware_proc_id(struct dt_node *root,
101 						    u32 hw_proc_id)
102 {
103 	struct dt_node *i;
104 
105 	dt_for_each_node(root, i) {
106 		const struct dt_property *prop;
107 
108 		if (!dt_has_node_property(i, "device_type", "cpu"))
109 			continue;
110 
111 		prop = dt_find_property(i, DT_PRIVATE "hw_proc_id");
112 		if (!prop)
113 			return NULL;
114 
115 		if (be32_to_cpu(*(be32 *)prop->prop) == hw_proc_id)
116 			return i;
117 	}
118 	return NULL;
119 }
120 
121 /* Note that numbers are small. */
add_be32_sorted(__be32 arr[],__be32 new,unsigned num)122 static void add_be32_sorted(__be32 arr[], __be32 new, unsigned num)
123 {
124 	unsigned int i;
125 
126 	/* Walk until we find where we belong (insertion sort). */
127 	for (i = 0; i < num; i++) {
128 		if (be32_to_cpu(new) < be32_to_cpu(arr[i])) {
129 			__be32 tmp = arr[i];
130 			arr[i] = new;
131 			new = tmp;
132 		}
133 	}
134 	arr[i] = new;
135 }
136 
add_xics_icps(void)137 static void add_xics_icps(void)
138 {
139 	struct dt_node *cpu;
140 	unsigned int i;
141 	u64 reg[PACA_MAX_THREADS * 2];
142 	struct dt_node *icp;
143 
144 	dt_for_each_node(dt_root, cpu) {
145 		u32 irange[2], size, pir;
146 		const struct dt_property *intsrv;
147 		const struct HDIF_common_hdr *paca;
148 		u64 ibase;
149 		unsigned int num_threads;
150 		bool found = false;
151 
152 		if (!dt_has_node_property(cpu, "device_type", "cpu"))
153 			continue;
154 
155 		intsrv = dt_find_property(cpu, "ibm,ppc-interrupt-server#s");
156 		if (!intsrv)
157 			continue;
158 
159 		pir = dt_prop_get_u32(cpu, "ibm,pir");
160 
161 		/* Get ibase address */
162 		paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
163 		for_each_paca(paca) {
164 			const struct sppaca_cpu_id *id;
165 			id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size);
166 
167 			if (!CHECK_SPPTR(id))
168 				continue;
169 
170 			if (pir != be32_to_cpu(id->pir))
171 				continue;
172 			ibase = cleanup_addr(be64_to_cpu(id->ibase));
173 			found = true;
174 			break;
175 		}
176 		if (!found)
177 			return;
178 
179 		num_threads = intsrv->len / sizeof(u32);
180 		assert(num_threads <= PACA_MAX_THREADS);
181 
182 		icp = dt_new_addr(dt_root, "interrupt-controller", ibase);
183 		if (!icp)
184 			continue;
185 
186 		dt_add_property_strings(icp, "compatible",
187 					"IBM,ppc-xicp",
188 					"IBM,power7-xicp");
189 
190 		irange[0] = dt_property_get_cell(intsrv, 0); /* Index */
191 		irange[1] = num_threads;		     /* num servers */
192 		dt_add_property(icp, "ibm,interrupt-server-ranges",
193 				irange, sizeof(irange));
194 		dt_add_property(icp, "interrupt-controller", NULL, 0);
195 		dt_add_property_cells(icp, "#address-cells", 0);
196 		dt_add_property_string(icp, "device_type",
197 				   "PowerPC-External-Interrupt-Presentation");
198 		for (i = 0; i < num_threads*2; i += 2) {
199 			reg[i] = ibase;
200 			/* One page is enough for a handful of regs. */
201 			reg[i+1] = 4096;
202 			ibase += reg[i+1];
203 		}
204 		dt_add_property(icp, "reg", reg, sizeof(reg));
205 	}
206 }
207 
__paca_parse(void)208 static bool __paca_parse(void)
209 {
210 	const struct HDIF_common_hdr *paca;
211 	struct dt_node *cpus;
212 
213 	paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
214 	if (!paca) {
215 		prerror("Invalid PACA (PCIA = %p)\n",
216 			ntuple_addr(&spira.ntuples.pcia));
217 		return false;
218 	}
219 
220 	if (be32_to_cpu(spira.ntuples.paca.act_len) < sizeof(*paca)) {
221 		prerror("PACA: invalid size %u\n",
222 			be32_to_cpu(spira.ntuples.paca.act_len));
223 		return false;
224 	}
225 
226 	cpus = dt_new(dt_root, "cpus");
227 	dt_add_property_cells(cpus, "#address-cells", 1);
228 	dt_add_property_cells(cpus, "#size-cells", 0);
229 
230 	for_each_paca(paca) {
231 		const struct sppaca_cpu_id *id;
232 		u32 size, ve_flags;
233 		bool okay;
234 
235 		id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size);
236 
237 		/* The ID structure on Blade314 is only 0x54 long. We can
238 		 * cope with it as we don't use all the additional fields.
239 		 * The minimum size we support is  0x40
240 		 */
241 		if (!id || size < SPIRA_CPU_ID_MIN_SIZE) {
242 			prerror("CPU[%i]: bad id size %u @ %p\n",
243 				paca_index(paca), size, id);
244 			return false;
245 		}
246 
247 		ve_flags = be32_to_cpu(id->verify_exists_flags);
248 		switch ((ve_flags&CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT) {
249 		case CPU_ID_VERIFY_USABLE_NO_FAILURES:
250 		case CPU_ID_VERIFY_USABLE_FAILURES:
251 			okay = true;
252 			break;
253 		default:
254 			okay = false;
255 		}
256 
257 		prlog(PR_INFO, "CPU[%i]: PIR=%i RES=%i %s\n",
258 		       paca_index(paca), be32_to_cpu(id->pir),
259 		       be32_to_cpu(id->process_interrupt_line),
260 		       okay ? "OK" : "UNAVAILABLE");
261 
262 		/* Secondary threads don't get their own node. */
263 		if (ve_flags & CPU_ID_SECONDARY_THREAD)
264 			continue;
265 
266 		if (!add_cpu_node(cpus, paca, id, okay))
267 			return false;
268 	}
269 
270 	/* Now account for secondaries. */
271 	for_each_paca(paca) {
272 		const struct dt_property *prop;
273 		const struct sppaca_cpu_id *id;
274 		u32 size, state, num, ve_flags;
275 		struct dt_node *cpu;
276 		__be32 *new_prop;
277 
278 		id = HDIF_get_idata(paca, 2, &size);
279 		if (!CHECK_SPPTR(id))
280 			continue;
281 
282 		ve_flags = be32_to_cpu(id->verify_exists_flags);
283 		state = (ve_flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT;
284 		switch (state) {
285 		case CPU_ID_VERIFY_USABLE_NO_FAILURES:
286 		case CPU_ID_VERIFY_USABLE_FAILURES:
287 			break;
288 		default:
289 			continue;
290 		}
291 
292 		/* Only interested in secondary threads. */
293 		if (!(ve_flags & CPU_ID_SECONDARY_THREAD))
294 			continue;
295 
296 		cpu = find_cpu_by_hardware_proc_id(cpus,
297 				   be32_to_cpu(id->hardware_proc_id));
298 		if (!cpu) {
299 			prerror("CPU[%i]: could not find primary hwid %i\n",
300 				paca_index(paca),
301 				be32_to_cpu(id->hardware_proc_id));
302 			return false;
303 		}
304 
305 		/* Add the cpu #. */
306 		prop = dt_find_property(cpu, "ibm,ppc-interrupt-server#s");
307 		if (!prop) {
308 			prerror("CPU[%i]: could not find mapping information\n",
309 				paca_index(paca));
310 			return false;
311 		}
312 		num = prop->len / sizeof(u32);
313 		new_prop = malloc((num + 1) * sizeof(u32));
314 		if (!new_prop) {
315 			prerror("Property allocation length %zu failed\n",
316 				(num + 1) * sizeof(u32));
317 			return false;
318 		}
319 		memcpy(new_prop, prop->prop, prop->len);
320 		add_be32_sorted(new_prop, id->process_interrupt_line, num);
321 		dt_del_property(cpu, (struct dt_property *)prop);
322 		dt_add_property(cpu, "ibm,ppc-interrupt-server#s",
323 				new_prop, (num + 1) * sizeof(__be32));
324 		free(new_prop);
325 	}
326 
327 	/*
328 	 * P7 and P8 use the XICS interrupt controller which has a per-core
329 	 * interrupt controller node.
330 	 */
331 	if (proc_gen <= proc_gen_p8)
332 		add_xics_icps();
333 
334 	return true;
335 }
336 
paca_parse(void)337 int paca_parse(void)
338 {
339 	if (!__paca_parse()) {
340 		prerror("CPU: Initial CPU parsing failed\n");
341 		return -1;
342 	}
343 	return 0;
344 }
345