blob: 8da173cc7e4397912c8669c7baf218be996bffec [file] [log] [blame]
James Morsead6eb312018-01-08 15:38:09 +00001// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2017 Arm Ltd.
3#define pr_fmt(fmt) "sdei: " fmt
4
5#include <linux/acpi.h>
6#include <linux/arm_sdei.h>
7#include <linux/arm-smccc.h>
8#include <linux/bitops.h>
9#include <linux/compiler.h>
10#include <linux/errno.h>
11#include <linux/hardirq.h>
12#include <linux/kernel.h>
13#include <linux/kprobes.h>
14#include <linux/kvm_host.h>
15#include <linux/list.h>
16#include <linux/mutex.h>
17#include <linux/of.h>
18#include <linux/of_platform.h>
19#include <linux/percpu.h>
20#include <linux/platform_device.h>
21#include <linux/ptrace.h>
22#include <linux/preempt.h>
23#include <linux/slab.h>
24#include <linux/smp.h>
25#include <linux/spinlock.h>
26#include <linux/uaccess.h>
27
28/*
29 * The call to use to reach the firmware.
30 */
31static asmlinkage void (*sdei_firmware_call)(unsigned long function_id,
32 unsigned long arg0, unsigned long arg1,
33 unsigned long arg2, unsigned long arg3,
34 unsigned long arg4, struct arm_smccc_res *res);
35
36/* entry point from firmware to arch asm code */
37static unsigned long sdei_entry_point;
38
39struct sdei_event {
40 struct list_head list;
41 u32 event_num;
42 u8 type;
43 u8 priority;
44
45 /* This pointer is handed to firmware as the event argument. */
46 struct sdei_registered_event *registered;
47};
48
49/* Take the mutex for any API call or modification. Take the mutex first. */
50static DEFINE_MUTEX(sdei_events_lock);
51
52/* and then hold this when modifying the list */
53static DEFINE_SPINLOCK(sdei_list_lock);
54static LIST_HEAD(sdei_list);
55
56static int sdei_to_linux_errno(unsigned long sdei_err)
57{
58 switch (sdei_err) {
59 case SDEI_NOT_SUPPORTED:
60 return -EOPNOTSUPP;
61 case SDEI_INVALID_PARAMETERS:
62 return -EINVAL;
63 case SDEI_DENIED:
64 return -EPERM;
65 case SDEI_PENDING:
66 return -EINPROGRESS;
67 case SDEI_OUT_OF_RESOURCE:
68 return -ENOMEM;
69 }
70
71 /* Not an error value ... */
72 return sdei_err;
73}
74
75/*
76 * If x0 is any of these values, then the call failed, use sdei_to_linux_errno()
77 * to translate.
78 */
79static int sdei_is_err(struct arm_smccc_res *res)
80{
81 switch (res->a0) {
82 case SDEI_NOT_SUPPORTED:
83 case SDEI_INVALID_PARAMETERS:
84 case SDEI_DENIED:
85 case SDEI_PENDING:
86 case SDEI_OUT_OF_RESOURCE:
87 return true;
88 }
89
90 return false;
91}
92
93static int invoke_sdei_fn(unsigned long function_id, unsigned long arg0,
94 unsigned long arg1, unsigned long arg2,
95 unsigned long arg3, unsigned long arg4,
96 u64 *result)
97{
98 int err = 0;
99 struct arm_smccc_res res;
100
101 if (sdei_firmware_call) {
102 sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4,
103 &res);
104 if (sdei_is_err(&res))
105 err = sdei_to_linux_errno(res.a0);
106 } else {
107 /*
108 * !sdei_firmware_call means we failed to probe or called
109 * sdei_mark_interface_broken(). -EIO is not an error returned
110 * by sdei_to_linux_errno() and is used to suppress messages
111 * from this driver.
112 */
113 err = -EIO;
114 res.a0 = SDEI_NOT_SUPPORTED;
115 }
116
117 if (result)
118 *result = res.a0;
119
120 return err;
121}
122
123static struct sdei_event *sdei_event_find(u32 event_num)
124{
125 struct sdei_event *e, *found = NULL;
126
127 lockdep_assert_held(&sdei_events_lock);
128
129 spin_lock(&sdei_list_lock);
130 list_for_each_entry(e, &sdei_list, list) {
131 if (e->event_num == event_num) {
132 found = e;
133 break;
134 }
135 }
136 spin_unlock(&sdei_list_lock);
137
138 return found;
139}
140
141int sdei_api_event_context(u32 query, u64 *result)
142{
143 return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_CONTEXT, query, 0, 0, 0, 0,
144 result);
145}
146NOKPROBE_SYMBOL(sdei_api_event_context);
147
148static int sdei_api_event_get_info(u32 event, u32 info, u64 *result)
149{
150 return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_GET_INFO, event, info, 0,
151 0, 0, result);
152}
153
154static struct sdei_event *sdei_event_create(u32 event_num,
155 sdei_event_callback *cb,
156 void *cb_arg)
157{
158 int err;
159 u64 result;
160 struct sdei_event *event;
161 struct sdei_registered_event *reg;
162
163 lockdep_assert_held(&sdei_events_lock);
164
165 event = kzalloc(sizeof(*event), GFP_KERNEL);
166 if (!event)
167 return ERR_PTR(-ENOMEM);
168
169 INIT_LIST_HEAD(&event->list);
170 event->event_num = event_num;
171
172 err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY,
173 &result);
174 if (err) {
175 kfree(event);
176 return ERR_PTR(err);
177 }
178 event->priority = result;
179
180 err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_TYPE,
181 &result);
182 if (err) {
183 kfree(event);
184 return ERR_PTR(err);
185 }
186 event->type = result;
187
188 if (event->type == SDEI_EVENT_TYPE_SHARED) {
189 reg = kzalloc(sizeof(*reg), GFP_KERNEL);
190 if (!reg) {
191 kfree(event);
192 return ERR_PTR(-ENOMEM);
193 }
194
195 reg->event_num = event_num;
196 reg->priority = event->priority;
197
198 reg->callback = cb;
199 reg->callback_arg = cb_arg;
200 event->registered = reg;
201 }
202
203 if (sdei_event_find(event_num)) {
204 kfree(event->registered);
205 kfree(event);
206 event = ERR_PTR(-EBUSY);
207 } else {
208 spin_lock(&sdei_list_lock);
209 list_add(&event->list, &sdei_list);
210 spin_unlock(&sdei_list_lock);
211 }
212
213 return event;
214}
215
216static void sdei_event_destroy(struct sdei_event *event)
217{
218 lockdep_assert_held(&sdei_events_lock);
219
220 spin_lock(&sdei_list_lock);
221 list_del(&event->list);
222 spin_unlock(&sdei_list_lock);
223
224 if (event->type == SDEI_EVENT_TYPE_SHARED)
225 kfree(event->registered);
226
227 kfree(event);
228}
229
230static int sdei_api_get_version(u64 *version)
231{
232 return invoke_sdei_fn(SDEI_1_0_FN_SDEI_VERSION, 0, 0, 0, 0, 0, version);
233}
234
235int sdei_mask_local_cpu(void)
236{
237 int err;
238
239 WARN_ON_ONCE(preemptible());
240
241 err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0, NULL);
242 if (err && err != -EIO) {
243 pr_warn_once("failed to mask CPU[%u]: %d\n",
244 smp_processor_id(), err);
245 return err;
246 }
247
248 return 0;
249}
250
251static void _ipi_mask_cpu(void *ignored)
252{
253 sdei_mask_local_cpu();
254}
255
256int sdei_unmask_local_cpu(void)
257{
258 int err;
259
260 WARN_ON_ONCE(preemptible());
261
262 err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PE_UNMASK, 0, 0, 0, 0, 0, NULL);
263 if (err && err != -EIO) {
264 pr_warn_once("failed to unmask CPU[%u]: %d\n",
265 smp_processor_id(), err);
266 return err;
267 }
268
269 return 0;
270}
271
272static void _ipi_unmask_cpu(void *ignored)
273{
274 sdei_unmask_local_cpu();
275}
276
277static void _ipi_private_reset(void *ignored)
278{
279 int err;
280
281 err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PRIVATE_RESET, 0, 0, 0, 0, 0,
282 NULL);
283 if (err && err != -EIO)
284 pr_warn_once("failed to reset CPU[%u]: %d\n",
285 smp_processor_id(), err);
286}
287
288static int sdei_api_shared_reset(void)
289{
290 return invoke_sdei_fn(SDEI_1_0_FN_SDEI_SHARED_RESET, 0, 0, 0, 0, 0,
291 NULL);
292}
293
294static void sdei_mark_interface_broken(void)
295{
296 pr_err("disabling SDEI firmware interface\n");
297 on_each_cpu(&_ipi_mask_cpu, NULL, true);
298 sdei_firmware_call = NULL;
299}
300
301static int sdei_platform_reset(void)
302{
303 int err;
304
305 on_each_cpu(&_ipi_private_reset, NULL, true);
306 err = sdei_api_shared_reset();
307 if (err) {
308 pr_err("Failed to reset platform: %d\n", err);
309 sdei_mark_interface_broken();
310 }
311
312 return err;
313}
314
315static int sdei_api_event_enable(u32 event_num)
316{
317 return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_ENABLE, event_num, 0, 0, 0,
318 0, NULL);
319}
320
321int sdei_event_enable(u32 event_num)
322{
323 int err = -EINVAL;
324 struct sdei_event *event;
325
326 mutex_lock(&sdei_events_lock);
327 event = sdei_event_find(event_num);
328 if (!event) {
329 mutex_unlock(&sdei_events_lock);
330 return -ENOENT;
331 }
332
333 if (event->type == SDEI_EVENT_TYPE_SHARED)
334 err = sdei_api_event_enable(event->event_num);
335 mutex_unlock(&sdei_events_lock);
336
337 return err;
338}
339EXPORT_SYMBOL(sdei_event_enable);
340
341static int sdei_api_event_disable(u32 event_num)
342{
343 return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_DISABLE, event_num, 0, 0,
344 0, 0, NULL);
345}
346
347int sdei_event_disable(u32 event_num)
348{
349 int err = -EINVAL;
350 struct sdei_event *event;
351
352 mutex_lock(&sdei_events_lock);
353 event = sdei_event_find(event_num);
354 if (!event) {
355 mutex_unlock(&sdei_events_lock);
356 return -ENOENT;
357 }
358
359 if (event->type == SDEI_EVENT_TYPE_SHARED)
360 err = sdei_api_event_disable(event->event_num);
361 mutex_unlock(&sdei_events_lock);
362
363 return err;
364}
365EXPORT_SYMBOL(sdei_event_disable);
366
367static int sdei_api_event_unregister(u32 event_num)
368{
369 return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_UNREGISTER, event_num, 0,
370 0, 0, 0, NULL);
371}
372
373static int _sdei_event_unregister(struct sdei_event *event)
374{
375 lockdep_assert_held(&sdei_events_lock);
376
377 if (event->type == SDEI_EVENT_TYPE_SHARED)
378 return sdei_api_event_unregister(event->event_num);
379
380 return -EINVAL;
381}
382
383int sdei_event_unregister(u32 event_num)
384{
385 int err;
386 struct sdei_event *event;
387
388 WARN_ON(in_nmi());
389
390 mutex_lock(&sdei_events_lock);
391 event = sdei_event_find(event_num);
392 do {
393 if (!event) {
394 pr_warn("Event %u not registered\n", event_num);
395 err = -ENOENT;
396 break;
397 }
398
399 err = _sdei_event_unregister(event);
400 if (err)
401 break;
402
403 sdei_event_destroy(event);
404 } while (0);
405 mutex_unlock(&sdei_events_lock);
406
407 return err;
408}
409EXPORT_SYMBOL(sdei_event_unregister);
410
411static int sdei_api_event_register(u32 event_num, unsigned long entry_point,
412 void *arg, u64 flags, u64 affinity)
413{
414 return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_REGISTER, event_num,
415 (unsigned long)entry_point, (unsigned long)arg,
416 flags, affinity, NULL);
417}
418
419static int _sdei_event_register(struct sdei_event *event)
420{
421 lockdep_assert_held(&sdei_events_lock);
422
423 if (event->type == SDEI_EVENT_TYPE_SHARED)
424 return sdei_api_event_register(event->event_num,
425 sdei_entry_point,
426 event->registered,
427 SDEI_EVENT_REGISTER_RM_ANY, 0);
428
429 return -EINVAL;
430}
431
432int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
433{
434 int err;
435 struct sdei_event *event;
436
437 WARN_ON(in_nmi());
438
439 mutex_lock(&sdei_events_lock);
440 do {
441 if (sdei_event_find(event_num)) {
442 pr_warn("Event %u already registered\n", event_num);
443 err = -EBUSY;
444 break;
445 }
446
447 event = sdei_event_create(event_num, cb, arg);
448 if (IS_ERR(event)) {
449 err = PTR_ERR(event);
450 pr_warn("Failed to create event %u: %d\n", event_num,
451 err);
452 break;
453 }
454
455 err = _sdei_event_register(event);
456 if (err) {
457 sdei_event_destroy(event);
458 pr_warn("Failed to register event %u: %d\n", event_num,
459 err);
460 }
461 } while (0);
462 mutex_unlock(&sdei_events_lock);
463
464 return err;
465}
466EXPORT_SYMBOL(sdei_event_register);
467
468static void sdei_smccc_smc(unsigned long function_id,
469 unsigned long arg0, unsigned long arg1,
470 unsigned long arg2, unsigned long arg3,
471 unsigned long arg4, struct arm_smccc_res *res)
472{
473 arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
474}
475
476static void sdei_smccc_hvc(unsigned long function_id,
477 unsigned long arg0, unsigned long arg1,
478 unsigned long arg2, unsigned long arg3,
479 unsigned long arg4, struct arm_smccc_res *res)
480{
481 arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
482}
483
484static int sdei_get_conduit(struct platform_device *pdev)
485{
486 const char *method;
487 struct device_node *np = pdev->dev.of_node;
488
489 sdei_firmware_call = NULL;
490 if (np) {
491 if (of_property_read_string(np, "method", &method)) {
492 pr_warn("missing \"method\" property\n");
493 return CONDUIT_INVALID;
494 }
495
496 if (!strcmp("hvc", method)) {
497 sdei_firmware_call = &sdei_smccc_hvc;
498 return CONDUIT_HVC;
499 } else if (!strcmp("smc", method)) {
500 sdei_firmware_call = &sdei_smccc_smc;
501 return CONDUIT_SMC;
502 }
503
504 pr_warn("invalid \"method\" property: %s\n", method);
505 }
506
507 return CONDUIT_INVALID;
508}
509
510static int sdei_probe(struct platform_device *pdev)
511{
512 int err;
513 u64 ver = 0;
514 int conduit;
515
516 conduit = sdei_get_conduit(pdev);
517 if (!sdei_firmware_call)
518 return 0;
519
520 err = sdei_api_get_version(&ver);
521 if (err == -EOPNOTSUPP)
522 pr_err("advertised but not implemented in platform firmware\n");
523 if (err) {
524 pr_err("Failed to get SDEI version: %d\n", err);
525 sdei_mark_interface_broken();
526 return err;
527 }
528
529 pr_info("SDEIv%d.%d (0x%x) detected in firmware.\n",
530 (int)SDEI_VERSION_MAJOR(ver), (int)SDEI_VERSION_MINOR(ver),
531 (int)SDEI_VERSION_VENDOR(ver));
532
533 if (SDEI_VERSION_MAJOR(ver) != 1) {
534 pr_warn("Conflicting SDEI version detected.\n");
535 sdei_mark_interface_broken();
536 return -EINVAL;
537 }
538
539 err = sdei_platform_reset();
540 if (err)
541 return err;
542
543 sdei_entry_point = sdei_arch_get_entry_point(conduit);
544 if (!sdei_entry_point) {
545 /* Not supported due to hardware or boot configuration */
546 sdei_mark_interface_broken();
547 return 0;
548 }
549
550 on_each_cpu(&_ipi_unmask_cpu, NULL, false);
551
552 return 0;
553}
554
555static const struct of_device_id sdei_of_match[] = {
556 { .compatible = "arm,sdei-1.0" },
557 {}
558};
559
560static struct platform_driver sdei_driver = {
561 .driver = {
562 .name = "sdei",
563 .of_match_table = sdei_of_match,
564 },
565 .probe = sdei_probe,
566};
567
568static bool __init sdei_present_dt(void)
569{
570 struct platform_device *pdev;
571 struct device_node *np, *fw_np;
572
573 fw_np = of_find_node_by_name(NULL, "firmware");
574 if (!fw_np)
575 return false;
576
577 np = of_find_matching_node(fw_np, sdei_of_match);
578 of_node_put(fw_np);
579 if (!np)
580 return false;
581
582 pdev = of_platform_device_create(np, sdei_driver.driver.name, NULL);
583 of_node_put(np);
584 if (IS_ERR(pdev))
585 return false;
586
587 return true;
588}
589
590static int __init sdei_init(void)
591{
592 if (sdei_present_dt())
593 platform_driver_register(&sdei_driver);
594
595 return 0;
596}
597
598subsys_initcall_sync(sdei_init);
599
600int sdei_event_handler(struct pt_regs *regs,
601 struct sdei_registered_event *arg)
602{
603 int err;
604 mm_segment_t orig_addr_limit;
605 u32 event_num = arg->event_num;
606
607 orig_addr_limit = get_fs();
608 set_fs(USER_DS);
609
610 err = arg->callback(event_num, regs, arg->callback_arg);
611 if (err)
612 pr_err_ratelimited("event %u on CPU %u failed with error: %d\n",
613 event_num, smp_processor_id(), err);
614
615 set_fs(orig_addr_limit);
616
617 return err;
618}
619NOKPROBE_SYMBOL(sdei_event_handler);