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 #include <skiboot.h>
17 #include <fsp.h>
18 #include <opal.h>
19 #include <device.h>
20 #include <lock.h>
21 #include <processor.h>
22 #include <psi.h>
23 #include <opal-msg.h>
24 #include <fsp-sysparam.h>
25 
26 struct sysparam_comp_data {
27 	uint32_t param_len;
28 	uint64_t async_token;
29 };
30 
31 struct sysparam_req {
32 	sysparam_compl_t	completion;
33 	void			*comp_data;
34 	void			*ubuf;
35 	uint32_t		ulen;
36 	struct fsp_msg		msg;
37 	struct fsp_msg		resp;
38 	bool			done;
39 };
40 
41 static struct sysparam_attr {
42 	const char	*name;
43 	uint32_t	id;
44 	uint32_t	length;
45 	uint8_t		perm;
46 } sysparam_attrs[] = {
47 #define _R	OPAL_SYSPARAM_READ
48 #define _W	OPAL_SYSPARAM_WRITE
49 #define _RW	OPAL_SYSPARAM_RW
50 	{"surveillance",	SYS_PARAM_SURV, 	4,	_RW},
51 	{"hmc-management", 	SYS_PARAM_HMC_MANAGED,	4,	_R},
52 	{"cupd-policy",		SYS_PARAM_FLASH_POLICY, 4,	_RW},
53 	{"plat-hmc-managed",	SYS_PARAM_NEED_HMC,	4,	_RW},
54 	{"fw-license-policy",	SYS_PARAM_FW_LICENSE,	4,	_RW},
55 	{"world-wide-port-num", SYS_PARAM_WWPN,		12,	_W},
56 	{"default-boot-device",	SYS_PARAM_DEF_BOOT_DEV,	1,	_RW},
57 	{"next-boot-device",	SYS_PARAM_NEXT_BOOT_DEV,1,	_RW},
58 	{"console-select",	SYS_PARAM_CONSOLE_SELECT,1,	_RW},
59 	{"boot-device-path",	SYS_PARAM_BOOT_DEV_PATH,48,	_RW}
60 #undef _R
61 #undef _W
62 #undef _RW
63 };
64 
fsp_sysparam_process(struct sysparam_req * r)65 static int fsp_sysparam_process(struct sysparam_req *r)
66 {
67 	u32 param_id, len;
68 	int stlen = 0;
69 	u8 fstat;
70 	/* Snapshot completion before we set the "done" flag */
71 	sysparam_compl_t comp = r->completion;
72 	void *cdata = r->comp_data;
73 
74 	if (r->msg.state != fsp_msg_done) {
75 		prerror("FSP: Request for sysparam 0x%x got FSP failure!\n",
76 			r->msg.data.words[0]);
77 		stlen = -1; /* XXX Find saner error codes */
78 		goto complete;
79 	}
80 
81 	param_id = r->resp.data.words[0];
82 	len = r->resp.data.words[1] & 0xffff;
83 
84 	/* Check params validity */
85 	if (param_id != r->msg.data.words[0]) {
86 		prerror("FSP: Request for sysparam 0x%x got resp. for 0x%x!\n",
87 			r->msg.data.words[0], param_id);
88 		stlen = -2; /* XXX Sane error codes */
89 		goto complete;
90 	}
91 	if (len > r->ulen) {
92 		prerror("FSP: Request for sysparam 0x%x truncated!\n",
93 			param_id);
94 		len = r->ulen;
95 	}
96 
97 	/* Decode the request status */
98 	fstat = (r->msg.resp->word1 >> 8) & 0xff;
99 	switch(fstat) {
100 	case 0x00: /* XXX Is that even possible ? */
101 	case 0x11: /* Data in request */
102 		memcpy(r->ubuf, &r->resp.data.words[2], len);
103 		/* fallthrough */
104 	case 0x12: /* Data in TCE */
105 		stlen = len;
106 		break;
107 	default:
108 		stlen = -fstat;
109 	}
110  complete:
111 	/* Call completion if any */
112 	if (comp)
113 		comp(r->msg.data.words[0], stlen, cdata);
114 
115 	free(r);
116 
117 	return stlen;
118 }
119 
fsp_sysparam_get_complete(struct fsp_msg * msg)120 static void fsp_sysparam_get_complete(struct fsp_msg *msg)
121 {
122 	struct sysparam_req *r = container_of(msg, struct sysparam_req, msg);
123 
124 	/* If it's an asynchronous request, process it now */
125 	if (r->completion) {
126 		fsp_sysparam_process(r);
127 		return;
128 	}
129 
130 	/* Else just set the done flag */
131 
132 	/* Another CPU can be polling on the "done" flag without the
133 	 * lock held, so let's order the udpates to the structure
134 	 */
135 	lwsync();
136 	r->done = true;
137 }
138 
fsp_get_sys_param(uint32_t param_id,void * buffer,uint32_t length,sysparam_compl_t async_complete,void * comp_data)139 int fsp_get_sys_param(uint32_t param_id, void *buffer, uint32_t length,
140 		      sysparam_compl_t async_complete, void *comp_data)
141 {
142 	struct sysparam_req *r;
143 	uint64_t baddr, tce_token;
144 	int rc;
145 
146 	if (!fsp_present())
147 		return -ENODEV;
148 	/*
149 	 * XXX FIXME: We currently always allocate the sysparam_req here
150 	 * however, we want to avoid runtime allocations as much as
151 	 * possible, so if this is going to be used a lot at runtime,
152 	 * we probably want to pre-allocate a pool of these
153 	 */
154 	if (length > 4096)
155 		return -EINVAL;
156 	r = zalloc(sizeof(struct sysparam_req));
157 	if (!r)
158 		return -ENOMEM;
159 	r->completion = async_complete;
160 	r->comp_data = comp_data;
161 	r->done = false;
162 	r->ubuf = buffer;
163 	r->ulen = length;
164 	r->msg.resp = &r->resp;
165 
166 	/* Map always 1 page ... easier that way and none of that
167 	 * is performance critical
168 	 */
169 	baddr = (uint64_t)buffer;
170 	fsp_tce_map(PSI_DMA_GET_SYSPARAM, (void *)(baddr & ~0xffful), 0x1000);
171 	tce_token = PSI_DMA_GET_SYSPARAM | (baddr & 0xfff);
172 	fsp_fillmsg(&r->msg, FSP_CMD_QUERY_SPARM, 3,
173 		    param_id, length, tce_token);
174 	rc = fsp_queue_msg(&r->msg, fsp_sysparam_get_complete);
175 
176 	if (rc)
177 		free(r);
178 
179 	/* Asynchronous operation or queueing failure, return */
180 	if (rc || async_complete)
181 		return rc;
182 
183 	/* Synchronous operation requested, spin and process */
184 	while(!r->done)
185 		opal_run_pollers();
186 
187 	/* Will free the request */
188 	return fsp_sysparam_process(r);
189 }
190 
fsp_opal_getparam_complete(uint32_t param_id __unused,int err_len,void * data)191 static void fsp_opal_getparam_complete(uint32_t param_id __unused, int err_len,
192 		void *data)
193 {
194 	struct sysparam_comp_data *comp_data = data;
195 	int rc = OPAL_SUCCESS;
196 
197 	if (comp_data->param_len != err_len)
198 		rc = OPAL_INTERNAL_ERROR;
199 
200 	opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
201 			comp_data->async_token, rc);
202 	free(comp_data);
203 }
204 
fsp_opal_setparam_complete(struct fsp_msg * msg)205 static void fsp_opal_setparam_complete(struct fsp_msg *msg)
206 {
207 	struct sysparam_comp_data *comp_data = msg->user_data;
208 	u8 fstat;
209 	uint32_t param_id;
210 	int rc = OPAL_SUCCESS;
211 
212 	if (msg->state != fsp_msg_done) {
213 		prerror("FSP: Request for set sysparam 0x%x got FSP failure!\n",
214 				msg->data.words[0]);
215 		rc = OPAL_INTERNAL_ERROR;
216 		goto out;
217 	}
218 
219 	param_id = msg->resp->data.words[0];
220 	if (param_id != msg->data.words[0]) {
221 		prerror("FSP: Request for set sysparam 0x%x got resp. for 0x%x!"
222 				"\n", msg->data.words[0], param_id);
223 		rc = OPAL_INTERNAL_ERROR;
224 		goto out;
225 	}
226 
227 	fstat = (msg->resp->word1 >> 8) & 0xff;
228 	switch (fstat) {
229 	case 0x00:
230 		rc = OPAL_SUCCESS;
231 		break;
232 	case 0x22:
233 		prerror("%s: Response status 0x%x, invalid data\n", __func__,
234 				fstat);
235 		rc = OPAL_INTERNAL_ERROR;
236 		break;
237 	case 0x24:
238 		prerror("%s: Response status 0x%x, DMA error\n", __func__,
239 				fstat);
240 		rc = OPAL_INTERNAL_ERROR;
241 		break;
242 	default:
243 		rc = OPAL_INTERNAL_ERROR;
244 		break;
245 	}
246 
247 out:
248 	opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
249 			comp_data->async_token, rc);
250 	free(comp_data);
251 	fsp_freemsg(msg);
252 }
253 
254 /* OPAL interface for PowerNV to read the system parameter from FSP */
fsp_opal_get_param(uint64_t async_token,uint32_t param_id,uint64_t buffer,uint64_t length)255 static int64_t fsp_opal_get_param(uint64_t async_token, uint32_t param_id,
256 				  uint64_t buffer, uint64_t length)
257 {
258 	struct sysparam_comp_data *comp_data;
259 	int count, rc, i;
260 
261 	if (!fsp_present())
262 		return OPAL_HARDWARE;
263 
264 	count = ARRAY_SIZE(sysparam_attrs);
265 	for (i = 0; i < count; i++)
266 		if (sysparam_attrs[i].id == param_id)
267 			break;
268 	if (i == count)
269 		return OPAL_PARAMETER;
270 
271 	if (length < sysparam_attrs[i].length)
272 		return OPAL_PARAMETER;
273 	if (!(sysparam_attrs[i].perm & OPAL_SYSPARAM_READ))
274 		return OPAL_PERMISSION;
275 
276 	comp_data = zalloc(sizeof(struct sysparam_comp_data));
277 	if (!comp_data)
278 		return OPAL_NO_MEM;
279 
280 	comp_data->param_len = sysparam_attrs[i].length;
281 	comp_data->async_token = async_token;
282 	rc = fsp_get_sys_param(param_id, (void *)buffer,
283 			sysparam_attrs[i].length, fsp_opal_getparam_complete,
284 			comp_data);
285 	if (rc) {
286 		free(comp_data);
287 		prerror("%s: Error %d queuing param request\n", __func__, rc);
288 		return OPAL_INTERNAL_ERROR;
289 	}
290 
291 	return OPAL_ASYNC_COMPLETION;
292 }
293 
294 /* OPAL interface for PowerNV to update the system parameter to FSP */
fsp_opal_set_param(uint64_t async_token,uint32_t param_id,uint64_t buffer,uint64_t length)295 static int64_t fsp_opal_set_param(uint64_t async_token, uint32_t param_id,
296 				  uint64_t buffer, uint64_t length)
297 {
298 	struct sysparam_comp_data *comp_data;
299 	struct fsp_msg *msg;
300 	uint64_t tce_token;
301 	int count, rc, i;
302 
303 	if (!fsp_present())
304 		return OPAL_HARDWARE;
305 
306 	count = ARRAY_SIZE(sysparam_attrs);
307 	for (i = 0; i < count; i++)
308 		if (sysparam_attrs[i].id == param_id)
309 			break;
310 	if (i == count)
311 		return OPAL_PARAMETER;
312 
313 	if (length < sysparam_attrs[i].length)
314 		return OPAL_PARAMETER;
315 	if (!(sysparam_attrs[i].perm & OPAL_SYSPARAM_WRITE))
316 		return OPAL_PERMISSION;
317 
318 	fsp_tce_map(PSI_DMA_SET_SYSPARAM, (void *)(buffer & ~0xffful), 0x1000);
319 	tce_token = PSI_DMA_SET_SYSPARAM | (buffer & 0xfff);
320 
321 	msg = fsp_mkmsg(FSP_CMD_SET_SPARM_2, 4, param_id, length,
322 			tce_token >> 32, tce_token);
323 	if (!msg) {
324 		prerror("%s: Failed to allocate the message\n", __func__);
325 		return OPAL_INTERNAL_ERROR;
326 	}
327 
328 	comp_data = zalloc(sizeof(struct sysparam_comp_data));
329 	if (!comp_data) {
330 		fsp_freemsg(msg);
331 		return OPAL_NO_MEM;
332 	}
333 
334 	comp_data->param_len = length;
335 	comp_data->async_token = async_token;
336 	msg->user_data = comp_data;
337 
338 	rc = fsp_queue_msg(msg, fsp_opal_setparam_complete);
339 	if (rc) {
340 		free(comp_data);
341 		fsp_freemsg(msg);
342 		prerror("%s: Failed to queue the message\n", __func__);
343 		return OPAL_INTERNAL_ERROR;
344 	}
345 
346 	return OPAL_ASYNC_COMPLETION;
347 }
348 
349 struct sysparam_notify_entry {
350 	struct	list_node	link;
351 	sysparam_update_notify	notify;
352 };
353 
354 static LIST_HEAD(sysparam_update_notifiers);
355 
356 /* Add client to notifier chain */
sysparam_add_update_notifier(sysparam_update_notify notify)357 void sysparam_add_update_notifier(sysparam_update_notify notify)
358 {
359 	struct sysparam_notify_entry *entry;
360 
361 	entry = zalloc(sizeof(struct sysparam_notify_entry));
362 	assert(entry);
363 
364 	entry->notify = notify;
365 	list_add_tail(&sysparam_update_notifiers, &entry->link);
366 }
367 
368 /* Remove client from notifier chain */
sysparam_del_update_notifier(sysparam_update_notify notify)369 void sysparam_del_update_notifier(sysparam_update_notify notify)
370 {
371 	struct sysparam_notify_entry *entry;
372 
373 	list_for_each(&sysparam_update_notifiers, entry, link) {
374 		if (entry->notify == notify) {
375 			list_del(&entry->link);
376 			free(entry);
377 			return;
378 		}
379 	}
380 }
381 
382 /* Update notification chain */
sysparam_run_update_notifier(struct fsp_msg * msg)383 static void sysparam_run_update_notifier(struct fsp_msg *msg)
384 {
385 	bool ret;
386 	struct sysparam_notify_entry *entry;
387 
388 	list_for_each(&sysparam_update_notifiers, entry, link) {
389 		ret = entry->notify(msg);
390 		if (ret == true)
391 			break;
392 	}
393 }
394 
fsp_sysparam_msg(u32 cmd_sub_mod,struct fsp_msg * msg)395 static bool fsp_sysparam_msg(u32 cmd_sub_mod, struct fsp_msg *msg)
396 {
397 	struct fsp_msg *rsp;
398 	int rc = -ENOMEM;
399 
400 	switch(cmd_sub_mod) {
401 	case FSP_CMD_SP_SPARM_UPD_0:
402 	case FSP_CMD_SP_SPARM_UPD_1:
403 		printf("FSP: Got sysparam update, param ID 0x%x\n",
404 		       msg->data.words[0]);
405 
406 		sysparam_run_update_notifier(msg);
407 
408 		rsp = fsp_mkmsg((cmd_sub_mod & 0xffff00) | 0x008000, 0);
409 		if (rsp)
410 			rc = fsp_queue_msg(rsp, fsp_freemsg);
411 		if (rc) {
412 			prerror("FSP: Error %d queuing sysparam reply\n", rc);
413 			/* What to do here ? R/R ? */
414 			fsp_freemsg(rsp);
415 		}
416 		return true;
417 	}
418 	return false;
419 }
420 
421 static struct fsp_client fsp_sysparam_client = {
422 	.message = fsp_sysparam_msg,
423 };
424 
add_opal_sysparam_node(void)425 static void add_opal_sysparam_node(void)
426 {
427 	struct dt_node *sysparams;
428 	char *names, *s;
429 	uint32_t *ids, *lens;
430 	uint8_t *perms;
431 	unsigned int i, count, size = 0;
432 
433 	if (!fsp_present())
434 		return;
435 
436 	sysparams = dt_new(opal_node, "sysparams");
437 	dt_add_property_string(sysparams, "compatible", "ibm,opal-sysparams");
438 
439 	count = ARRAY_SIZE(sysparam_attrs);
440 	for (i = 0; i < count; i++)
441 		size = size + strlen(sysparam_attrs[i].name) + 1;
442 
443 	names = zalloc(size);
444 	if (!names) {
445 		prerror("%s: Failed to allocate memory for parameter names\n",
446 				__func__);
447 		return;
448 	}
449 
450 	ids = zalloc(count * sizeof(*ids));
451 	if (!ids) {
452 		prerror("%s: Failed to allocate memory for parameter ids\n",
453 				__func__);
454 		goto out_free_name;
455 	}
456 
457 	lens = zalloc(count * sizeof(*lens));
458 	if (!lens) {
459 		prerror("%s: Failed to allocate memory for parameter length\n",
460 				__func__);
461 		goto out_free_id;
462 	}
463 
464 	perms = zalloc(count * sizeof(*perms));
465 	if (!perms) {
466 		prerror("%s: Failed to allocate memory for parameter length\n",
467 				__func__);
468 		goto out_free_len;
469 	}
470 
471 	s = names;
472 	for (i = 0; i < count; i++) {
473 		strcpy(s, sysparam_attrs[i].name);
474 		s = s + strlen(sysparam_attrs[i].name) + 1;
475 
476 		ids[i] = sysparam_attrs[i].id;
477 		lens[i] = sysparam_attrs[i].length;
478 		perms[i] = sysparam_attrs[i].perm;
479 	}
480 
481 	dt_add_property(sysparams, "param-name", names, size);
482 	dt_add_property(sysparams, "param-id", ids, count * sizeof(*ids));
483 	dt_add_property(sysparams, "param-len", lens, count * sizeof(*lens));
484 	dt_add_property(sysparams, "param-perm", perms, count * sizeof(*perms));
485 
486 	free(perms);
487 
488 out_free_len:
489 	free(lens);
490 out_free_id:
491 	free(ids);
492 out_free_name:
493 	free(names);
494 }
495 
fsp_sysparam_init(void)496 void fsp_sysparam_init(void)
497 {
498 	if (!fsp_present())
499 		return;
500 
501 	/* Register change notifications */
502 	fsp_register_client(&fsp_sysparam_client, FSP_MCLASS_SERVICE);
503 
504 	/* Register OPAL interfaces */
505 	opal_register(OPAL_GET_PARAM, fsp_opal_get_param, 4);
506 	opal_register(OPAL_SET_PARAM, fsp_opal_set_param, 4);
507 
508 	/* Add device-tree nodes */
509 	add_opal_sysparam_node();
510 }
511