.. | .. |
---|
2 | 2 | // Copyright (C) 2017 Arm Ltd. |
---|
3 | 3 | #define pr_fmt(fmt) "sdei: " fmt |
---|
4 | 4 | |
---|
| 5 | +#include <acpi/ghes.h> |
---|
5 | 6 | #include <linux/acpi.h> |
---|
6 | 7 | #include <linux/arm_sdei.h> |
---|
7 | 8 | #include <linux/arm-smccc.h> |
---|
.. | .. |
---|
77 | 78 | int first_error; |
---|
78 | 79 | }; |
---|
79 | 80 | |
---|
80 | | -#define CROSSCALL_INIT(arg, event) (arg.event = event, \ |
---|
81 | | - arg.first_error = 0, \ |
---|
82 | | - atomic_set(&arg.errors, 0)) |
---|
| 81 | +#define CROSSCALL_INIT(arg, event) \ |
---|
| 82 | + do { \ |
---|
| 83 | + arg.event = event; \ |
---|
| 84 | + arg.first_error = 0; \ |
---|
| 85 | + atomic_set(&arg.errors, 0); \ |
---|
| 86 | + } while (0) |
---|
83 | 87 | |
---|
84 | | -static inline int sdei_do_cross_call(void *fn, struct sdei_event * event) |
---|
| 88 | +static inline int sdei_do_local_call(smp_call_func_t fn, |
---|
| 89 | + struct sdei_event *event) |
---|
| 90 | +{ |
---|
| 91 | + struct sdei_crosscall_args arg; |
---|
| 92 | + |
---|
| 93 | + CROSSCALL_INIT(arg, event); |
---|
| 94 | + fn(&arg); |
---|
| 95 | + |
---|
| 96 | + return arg.first_error; |
---|
| 97 | +} |
---|
| 98 | + |
---|
| 99 | +static inline int sdei_do_cross_call(smp_call_func_t fn, |
---|
| 100 | + struct sdei_event *event) |
---|
85 | 101 | { |
---|
86 | 102 | struct sdei_crosscall_args arg; |
---|
87 | 103 | |
---|
.. | .. |
---|
113 | 129 | return -ENOMEM; |
---|
114 | 130 | } |
---|
115 | 131 | |
---|
116 | | - /* Not an error value ... */ |
---|
117 | | - return sdei_err; |
---|
118 | | -} |
---|
119 | | - |
---|
120 | | -/* |
---|
121 | | - * If x0 is any of these values, then the call failed, use sdei_to_linux_errno() |
---|
122 | | - * to translate. |
---|
123 | | - */ |
---|
124 | | -static int sdei_is_err(struct arm_smccc_res *res) |
---|
125 | | -{ |
---|
126 | | - switch (res->a0) { |
---|
127 | | - case SDEI_NOT_SUPPORTED: |
---|
128 | | - case SDEI_INVALID_PARAMETERS: |
---|
129 | | - case SDEI_DENIED: |
---|
130 | | - case SDEI_PENDING: |
---|
131 | | - case SDEI_OUT_OF_RESOURCE: |
---|
132 | | - return true; |
---|
133 | | - } |
---|
134 | | - |
---|
135 | | - return false; |
---|
| 132 | + return 0; |
---|
136 | 133 | } |
---|
137 | 134 | |
---|
138 | 135 | static int invoke_sdei_fn(unsigned long function_id, unsigned long arg0, |
---|
.. | .. |
---|
140 | 137 | unsigned long arg3, unsigned long arg4, |
---|
141 | 138 | u64 *result) |
---|
142 | 139 | { |
---|
143 | | - int err = 0; |
---|
| 140 | + int err; |
---|
144 | 141 | struct arm_smccc_res res; |
---|
145 | 142 | |
---|
146 | 143 | if (sdei_firmware_call) { |
---|
147 | 144 | sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4, |
---|
148 | 145 | &res); |
---|
149 | | - if (sdei_is_err(&res)) |
---|
150 | | - err = sdei_to_linux_errno(res.a0); |
---|
| 146 | + err = sdei_to_linux_errno(res.a0); |
---|
151 | 147 | } else { |
---|
152 | 148 | /* |
---|
153 | 149 | * !sdei_firmware_call means we failed to probe or called |
---|
.. | .. |
---|
164 | 160 | |
---|
165 | 161 | return err; |
---|
166 | 162 | } |
---|
| 163 | +NOKPROBE_SYMBOL(invoke_sdei_fn); |
---|
167 | 164 | |
---|
168 | 165 | static struct sdei_event *sdei_event_find(u32 event_num) |
---|
169 | 166 | { |
---|
.. | .. |
---|
208 | 205 | lockdep_assert_held(&sdei_events_lock); |
---|
209 | 206 | |
---|
210 | 207 | event = kzalloc(sizeof(*event), GFP_KERNEL); |
---|
211 | | - if (!event) |
---|
212 | | - return ERR_PTR(-ENOMEM); |
---|
| 208 | + if (!event) { |
---|
| 209 | + err = -ENOMEM; |
---|
| 210 | + goto fail; |
---|
| 211 | + } |
---|
213 | 212 | |
---|
214 | 213 | INIT_LIST_HEAD(&event->list); |
---|
215 | 214 | event->event_num = event_num; |
---|
216 | 215 | |
---|
217 | 216 | err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY, |
---|
218 | 217 | &result); |
---|
219 | | - if (err) { |
---|
220 | | - kfree(event); |
---|
221 | | - return ERR_PTR(err); |
---|
222 | | - } |
---|
| 218 | + if (err) |
---|
| 219 | + goto fail; |
---|
223 | 220 | event->priority = result; |
---|
224 | 221 | |
---|
225 | 222 | err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_TYPE, |
---|
226 | 223 | &result); |
---|
227 | | - if (err) { |
---|
228 | | - kfree(event); |
---|
229 | | - return ERR_PTR(err); |
---|
230 | | - } |
---|
| 224 | + if (err) |
---|
| 225 | + goto fail; |
---|
231 | 226 | event->type = result; |
---|
232 | 227 | |
---|
233 | 228 | if (event->type == SDEI_EVENT_TYPE_SHARED) { |
---|
234 | 229 | reg = kzalloc(sizeof(*reg), GFP_KERNEL); |
---|
235 | 230 | if (!reg) { |
---|
236 | | - kfree(event); |
---|
237 | | - return ERR_PTR(-ENOMEM); |
---|
| 231 | + err = -ENOMEM; |
---|
| 232 | + goto fail; |
---|
238 | 233 | } |
---|
239 | 234 | |
---|
240 | | - reg->event_num = event_num; |
---|
| 235 | + reg->event_num = event->event_num; |
---|
241 | 236 | reg->priority = event->priority; |
---|
242 | 237 | |
---|
243 | 238 | reg->callback = cb; |
---|
.. | .. |
---|
249 | 244 | |
---|
250 | 245 | regs = alloc_percpu(struct sdei_registered_event); |
---|
251 | 246 | if (!regs) { |
---|
252 | | - kfree(event); |
---|
253 | | - return ERR_PTR(-ENOMEM); |
---|
| 247 | + err = -ENOMEM; |
---|
| 248 | + goto fail; |
---|
254 | 249 | } |
---|
255 | 250 | |
---|
256 | 251 | for_each_possible_cpu(cpu) { |
---|
.. | .. |
---|
265 | 260 | event->private_registered = regs; |
---|
266 | 261 | } |
---|
267 | 262 | |
---|
268 | | - if (sdei_event_find(event_num)) { |
---|
269 | | - kfree(event->registered); |
---|
270 | | - kfree(event); |
---|
271 | | - event = ERR_PTR(-EBUSY); |
---|
272 | | - } else { |
---|
273 | | - spin_lock(&sdei_list_lock); |
---|
274 | | - list_add(&event->list, &sdei_list); |
---|
275 | | - spin_unlock(&sdei_list_lock); |
---|
276 | | - } |
---|
| 263 | + spin_lock(&sdei_list_lock); |
---|
| 264 | + list_add(&event->list, &sdei_list); |
---|
| 265 | + spin_unlock(&sdei_list_lock); |
---|
277 | 266 | |
---|
278 | 267 | return event; |
---|
| 268 | + |
---|
| 269 | +fail: |
---|
| 270 | + kfree(event); |
---|
| 271 | + return ERR_PTR(err); |
---|
279 | 272 | } |
---|
280 | 273 | |
---|
281 | | -static void sdei_event_destroy(struct sdei_event *event) |
---|
| 274 | +static void sdei_event_destroy_llocked(struct sdei_event *event) |
---|
282 | 275 | { |
---|
283 | 276 | lockdep_assert_held(&sdei_events_lock); |
---|
| 277 | + lockdep_assert_held(&sdei_list_lock); |
---|
284 | 278 | |
---|
285 | | - spin_lock(&sdei_list_lock); |
---|
286 | 279 | list_del(&event->list); |
---|
287 | | - spin_unlock(&sdei_list_lock); |
---|
288 | 280 | |
---|
289 | 281 | if (event->type == SDEI_EVENT_TYPE_SHARED) |
---|
290 | 282 | kfree(event->registered); |
---|
.. | .. |
---|
292 | 284 | free_percpu(event->private_registered); |
---|
293 | 285 | |
---|
294 | 286 | kfree(event); |
---|
| 287 | +} |
---|
| 288 | + |
---|
| 289 | +static void sdei_event_destroy(struct sdei_event *event) |
---|
| 290 | +{ |
---|
| 291 | + spin_lock(&sdei_list_lock); |
---|
| 292 | + sdei_event_destroy_llocked(event); |
---|
| 293 | + spin_unlock(&sdei_list_lock); |
---|
295 | 294 | } |
---|
296 | 295 | |
---|
297 | 296 | static int sdei_api_get_version(u64 *version) |
---|
.. | .. |
---|
427 | 426 | |
---|
428 | 427 | return err; |
---|
429 | 428 | } |
---|
430 | | -EXPORT_SYMBOL(sdei_event_enable); |
---|
431 | 429 | |
---|
432 | 430 | static int sdei_api_event_disable(u32 event_num) |
---|
433 | 431 | { |
---|
.. | .. |
---|
469 | 467 | |
---|
470 | 468 | return err; |
---|
471 | 469 | } |
---|
472 | | -EXPORT_SYMBOL(sdei_event_disable); |
---|
473 | 470 | |
---|
474 | 471 | static int sdei_api_event_unregister(u32 event_num) |
---|
475 | 472 | { |
---|
.. | .. |
---|
490 | 487 | sdei_cross_call_return(arg, err); |
---|
491 | 488 | } |
---|
492 | 489 | |
---|
493 | | -static int _sdei_event_unregister(struct sdei_event *event) |
---|
494 | | -{ |
---|
495 | | - lockdep_assert_held(&sdei_events_lock); |
---|
496 | | - |
---|
497 | | - if (event->type == SDEI_EVENT_TYPE_SHARED) |
---|
498 | | - return sdei_api_event_unregister(event->event_num); |
---|
499 | | - |
---|
500 | | - return sdei_do_cross_call(_local_event_unregister, event); |
---|
501 | | -} |
---|
502 | | - |
---|
503 | 490 | int sdei_event_unregister(u32 event_num) |
---|
504 | 491 | { |
---|
505 | 492 | int err; |
---|
.. | .. |
---|
509 | 496 | |
---|
510 | 497 | mutex_lock(&sdei_events_lock); |
---|
511 | 498 | event = sdei_event_find(event_num); |
---|
512 | | - do { |
---|
513 | | - if (!event) { |
---|
514 | | - pr_warn("Event %u not registered\n", event_num); |
---|
515 | | - err = -ENOENT; |
---|
516 | | - break; |
---|
517 | | - } |
---|
| 499 | + if (!event) { |
---|
| 500 | + pr_warn("Event %u not registered\n", event_num); |
---|
| 501 | + err = -ENOENT; |
---|
| 502 | + goto unlock; |
---|
| 503 | + } |
---|
518 | 504 | |
---|
519 | | - spin_lock(&sdei_list_lock); |
---|
520 | | - event->reregister = false; |
---|
521 | | - event->reenable = false; |
---|
522 | | - spin_unlock(&sdei_list_lock); |
---|
| 505 | + spin_lock(&sdei_list_lock); |
---|
| 506 | + event->reregister = false; |
---|
| 507 | + event->reenable = false; |
---|
| 508 | + spin_unlock(&sdei_list_lock); |
---|
523 | 509 | |
---|
524 | | - err = _sdei_event_unregister(event); |
---|
525 | | - if (err) |
---|
526 | | - break; |
---|
| 510 | + if (event->type == SDEI_EVENT_TYPE_SHARED) |
---|
| 511 | + err = sdei_api_event_unregister(event->event_num); |
---|
| 512 | + else |
---|
| 513 | + err = sdei_do_cross_call(_local_event_unregister, event); |
---|
527 | 514 | |
---|
528 | | - sdei_event_destroy(event); |
---|
529 | | - } while (0); |
---|
| 515 | + if (err) |
---|
| 516 | + goto unlock; |
---|
| 517 | + |
---|
| 518 | + sdei_event_destroy(event); |
---|
| 519 | +unlock: |
---|
530 | 520 | mutex_unlock(&sdei_events_lock); |
---|
531 | 521 | |
---|
532 | 522 | return err; |
---|
533 | 523 | } |
---|
534 | | -EXPORT_SYMBOL(sdei_event_unregister); |
---|
535 | 524 | |
---|
536 | 525 | /* |
---|
537 | 526 | * unregister events, but don't destroy them as they are re-registered by |
---|
.. | .. |
---|
548 | 537 | if (event->type != SDEI_EVENT_TYPE_SHARED) |
---|
549 | 538 | continue; |
---|
550 | 539 | |
---|
551 | | - err = _sdei_event_unregister(event); |
---|
| 540 | + err = sdei_api_event_unregister(event->event_num); |
---|
552 | 541 | if (err) |
---|
553 | 542 | break; |
---|
554 | 543 | } |
---|
.. | .. |
---|
582 | 571 | sdei_cross_call_return(arg, err); |
---|
583 | 572 | } |
---|
584 | 573 | |
---|
585 | | -static int _sdei_event_register(struct sdei_event *event) |
---|
586 | | -{ |
---|
587 | | - int err; |
---|
588 | | - |
---|
589 | | - lockdep_assert_held(&sdei_events_lock); |
---|
590 | | - |
---|
591 | | - if (event->type == SDEI_EVENT_TYPE_SHARED) |
---|
592 | | - return sdei_api_event_register(event->event_num, |
---|
593 | | - sdei_entry_point, |
---|
594 | | - event->registered, |
---|
595 | | - SDEI_EVENT_REGISTER_RM_ANY, 0); |
---|
596 | | - |
---|
597 | | - err = sdei_do_cross_call(_local_event_register, event); |
---|
598 | | - if (err) |
---|
599 | | - sdei_do_cross_call(_local_event_unregister, event); |
---|
600 | | - |
---|
601 | | - return err; |
---|
602 | | -} |
---|
603 | | - |
---|
604 | 574 | int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg) |
---|
605 | 575 | { |
---|
606 | 576 | int err; |
---|
.. | .. |
---|
609 | 579 | WARN_ON(in_nmi()); |
---|
610 | 580 | |
---|
611 | 581 | mutex_lock(&sdei_events_lock); |
---|
612 | | - do { |
---|
613 | | - if (sdei_event_find(event_num)) { |
---|
614 | | - pr_warn("Event %u already registered\n", event_num); |
---|
615 | | - err = -EBUSY; |
---|
616 | | - break; |
---|
617 | | - } |
---|
| 582 | + if (sdei_event_find(event_num)) { |
---|
| 583 | + pr_warn("Event %u already registered\n", event_num); |
---|
| 584 | + err = -EBUSY; |
---|
| 585 | + goto unlock; |
---|
| 586 | + } |
---|
618 | 587 | |
---|
619 | | - event = sdei_event_create(event_num, cb, arg); |
---|
620 | | - if (IS_ERR(event)) { |
---|
621 | | - err = PTR_ERR(event); |
---|
622 | | - pr_warn("Failed to create event %u: %d\n", event_num, |
---|
623 | | - err); |
---|
624 | | - break; |
---|
625 | | - } |
---|
| 588 | + event = sdei_event_create(event_num, cb, arg); |
---|
| 589 | + if (IS_ERR(event)) { |
---|
| 590 | + err = PTR_ERR(event); |
---|
| 591 | + pr_warn("Failed to create event %u: %d\n", event_num, err); |
---|
| 592 | + goto unlock; |
---|
| 593 | + } |
---|
626 | 594 | |
---|
627 | | - cpus_read_lock(); |
---|
628 | | - err = _sdei_event_register(event); |
---|
629 | | - if (err) { |
---|
630 | | - sdei_event_destroy(event); |
---|
631 | | - pr_warn("Failed to register event %u: %d\n", event_num, |
---|
632 | | - err); |
---|
633 | | - } else { |
---|
634 | | - spin_lock(&sdei_list_lock); |
---|
635 | | - event->reregister = true; |
---|
636 | | - spin_unlock(&sdei_list_lock); |
---|
637 | | - } |
---|
638 | | - cpus_read_unlock(); |
---|
639 | | - } while (0); |
---|
640 | | - mutex_unlock(&sdei_events_lock); |
---|
| 595 | + cpus_read_lock(); |
---|
| 596 | + if (event->type == SDEI_EVENT_TYPE_SHARED) { |
---|
| 597 | + err = sdei_api_event_register(event->event_num, |
---|
| 598 | + sdei_entry_point, |
---|
| 599 | + event->registered, |
---|
| 600 | + SDEI_EVENT_REGISTER_RM_ANY, 0); |
---|
| 601 | + } else { |
---|
| 602 | + err = sdei_do_cross_call(_local_event_register, event); |
---|
| 603 | + if (err) |
---|
| 604 | + sdei_do_cross_call(_local_event_unregister, event); |
---|
| 605 | + } |
---|
641 | 606 | |
---|
642 | | - return err; |
---|
643 | | -} |
---|
644 | | -EXPORT_SYMBOL(sdei_event_register); |
---|
645 | | - |
---|
646 | | -static int sdei_reregister_event(struct sdei_event *event) |
---|
647 | | -{ |
---|
648 | | - int err; |
---|
649 | | - |
---|
650 | | - lockdep_assert_held(&sdei_events_lock); |
---|
651 | | - |
---|
652 | | - err = _sdei_event_register(event); |
---|
653 | 607 | if (err) { |
---|
654 | | - pr_err("Failed to re-register event %u\n", event->event_num); |
---|
655 | 608 | sdei_event_destroy(event); |
---|
656 | | - return err; |
---|
| 609 | + pr_warn("Failed to register event %u: %d\n", event_num, err); |
---|
| 610 | + goto cpu_unlock; |
---|
657 | 611 | } |
---|
658 | 612 | |
---|
659 | | - if (event->reenable) { |
---|
660 | | - if (event->type == SDEI_EVENT_TYPE_SHARED) |
---|
661 | | - err = sdei_api_event_enable(event->event_num); |
---|
662 | | - else |
---|
663 | | - err = sdei_do_cross_call(_local_event_enable, event); |
---|
664 | | - } |
---|
665 | | - |
---|
666 | | - if (err) |
---|
667 | | - pr_err("Failed to re-enable event %u\n", event->event_num); |
---|
668 | | - |
---|
| 613 | + spin_lock(&sdei_list_lock); |
---|
| 614 | + event->reregister = true; |
---|
| 615 | + spin_unlock(&sdei_list_lock); |
---|
| 616 | +cpu_unlock: |
---|
| 617 | + cpus_read_unlock(); |
---|
| 618 | +unlock: |
---|
| 619 | + mutex_unlock(&sdei_events_lock); |
---|
669 | 620 | return err; |
---|
670 | 621 | } |
---|
671 | 622 | |
---|
.. | .. |
---|
681 | 632 | continue; |
---|
682 | 633 | |
---|
683 | 634 | if (event->reregister) { |
---|
684 | | - err = sdei_reregister_event(event); |
---|
685 | | - if (err) |
---|
| 635 | + err = sdei_api_event_register(event->event_num, |
---|
| 636 | + sdei_entry_point, event->registered, |
---|
| 637 | + SDEI_EVENT_REGISTER_RM_ANY, 0); |
---|
| 638 | + if (err) { |
---|
| 639 | + pr_err("Failed to re-register event %u\n", |
---|
| 640 | + event->event_num); |
---|
| 641 | + sdei_event_destroy_llocked(event); |
---|
686 | 642 | break; |
---|
| 643 | + } |
---|
| 644 | + } |
---|
| 645 | + |
---|
| 646 | + if (event->reenable) { |
---|
| 647 | + err = sdei_api_event_enable(event->event_num); |
---|
| 648 | + if (err) { |
---|
| 649 | + pr_err("Failed to re-enable event %u\n", |
---|
| 650 | + event->event_num); |
---|
| 651 | + break; |
---|
| 652 | + } |
---|
687 | 653 | } |
---|
688 | 654 | } |
---|
689 | 655 | spin_unlock(&sdei_list_lock); |
---|
.. | .. |
---|
695 | 661 | static int sdei_cpuhp_down(unsigned int cpu) |
---|
696 | 662 | { |
---|
697 | 663 | struct sdei_event *event; |
---|
698 | | - struct sdei_crosscall_args arg; |
---|
| 664 | + int err; |
---|
699 | 665 | |
---|
700 | 666 | /* un-register private events */ |
---|
701 | 667 | spin_lock(&sdei_list_lock); |
---|
.. | .. |
---|
703 | 669 | if (event->type == SDEI_EVENT_TYPE_SHARED) |
---|
704 | 670 | continue; |
---|
705 | 671 | |
---|
706 | | - CROSSCALL_INIT(arg, event); |
---|
707 | | - /* call the cross-call function locally... */ |
---|
708 | | - _local_event_unregister(&arg); |
---|
709 | | - if (arg.first_error) |
---|
| 672 | + err = sdei_do_local_call(_local_event_unregister, event); |
---|
| 673 | + if (err) { |
---|
710 | 674 | pr_err("Failed to unregister event %u: %d\n", |
---|
711 | | - event->event_num, arg.first_error); |
---|
| 675 | + event->event_num, err); |
---|
| 676 | + } |
---|
712 | 677 | } |
---|
713 | 678 | spin_unlock(&sdei_list_lock); |
---|
714 | 679 | |
---|
.. | .. |
---|
718 | 683 | static int sdei_cpuhp_up(unsigned int cpu) |
---|
719 | 684 | { |
---|
720 | 685 | struct sdei_event *event; |
---|
721 | | - struct sdei_crosscall_args arg; |
---|
| 686 | + int err; |
---|
722 | 687 | |
---|
723 | 688 | /* re-register/enable private events */ |
---|
724 | 689 | spin_lock(&sdei_list_lock); |
---|
.. | .. |
---|
727 | 692 | continue; |
---|
728 | 693 | |
---|
729 | 694 | if (event->reregister) { |
---|
730 | | - CROSSCALL_INIT(arg, event); |
---|
731 | | - /* call the cross-call function locally... */ |
---|
732 | | - _local_event_register(&arg); |
---|
733 | | - if (arg.first_error) |
---|
| 695 | + err = sdei_do_local_call(_local_event_register, event); |
---|
| 696 | + if (err) { |
---|
734 | 697 | pr_err("Failed to re-register event %u: %d\n", |
---|
735 | | - event->event_num, arg.first_error); |
---|
| 698 | + event->event_num, err); |
---|
| 699 | + } |
---|
736 | 700 | } |
---|
737 | 701 | |
---|
738 | 702 | if (event->reenable) { |
---|
739 | | - CROSSCALL_INIT(arg, event); |
---|
740 | | - _local_event_enable(&arg); |
---|
741 | | - if (arg.first_error) |
---|
| 703 | + err = sdei_do_local_call(_local_event_enable, event); |
---|
| 704 | + if (err) { |
---|
742 | 705 | pr_err("Failed to re-enable event %u: %d\n", |
---|
743 | | - event->event_num, arg.first_error); |
---|
| 706 | + event->event_num, err); |
---|
| 707 | + } |
---|
744 | 708 | } |
---|
745 | 709 | } |
---|
746 | 710 | spin_unlock(&sdei_list_lock); |
---|
.. | .. |
---|
878 | 842 | { |
---|
879 | 843 | arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); |
---|
880 | 844 | } |
---|
| 845 | +NOKPROBE_SYMBOL(sdei_smccc_smc); |
---|
881 | 846 | |
---|
882 | 847 | static void sdei_smccc_hvc(unsigned long function_id, |
---|
883 | 848 | unsigned long arg0, unsigned long arg1, |
---|
.. | .. |
---|
885 | 850 | unsigned long arg4, struct arm_smccc_res *res) |
---|
886 | 851 | { |
---|
887 | 852 | arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); |
---|
| 853 | +} |
---|
| 854 | +NOKPROBE_SYMBOL(sdei_smccc_hvc); |
---|
| 855 | + |
---|
| 856 | +int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb, |
---|
| 857 | + sdei_event_callback *critical_cb) |
---|
| 858 | +{ |
---|
| 859 | + int err; |
---|
| 860 | + u64 result; |
---|
| 861 | + u32 event_num; |
---|
| 862 | + sdei_event_callback *cb; |
---|
| 863 | + |
---|
| 864 | + if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) |
---|
| 865 | + return -EOPNOTSUPP; |
---|
| 866 | + |
---|
| 867 | + event_num = ghes->generic->notify.vector; |
---|
| 868 | + if (event_num == 0) { |
---|
| 869 | + /* |
---|
| 870 | + * Event 0 is reserved by the specification for |
---|
| 871 | + * SDEI_EVENT_SIGNAL. |
---|
| 872 | + */ |
---|
| 873 | + return -EINVAL; |
---|
| 874 | + } |
---|
| 875 | + |
---|
| 876 | + err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY, |
---|
| 877 | + &result); |
---|
| 878 | + if (err) |
---|
| 879 | + return err; |
---|
| 880 | + |
---|
| 881 | + if (result == SDEI_EVENT_PRIORITY_CRITICAL) |
---|
| 882 | + cb = critical_cb; |
---|
| 883 | + else |
---|
| 884 | + cb = normal_cb; |
---|
| 885 | + |
---|
| 886 | + err = sdei_event_register(event_num, cb, ghes); |
---|
| 887 | + if (!err) |
---|
| 888 | + err = sdei_event_enable(event_num); |
---|
| 889 | + |
---|
| 890 | + return err; |
---|
| 891 | +} |
---|
| 892 | + |
---|
| 893 | +int sdei_unregister_ghes(struct ghes *ghes) |
---|
| 894 | +{ |
---|
| 895 | + int i; |
---|
| 896 | + int err; |
---|
| 897 | + u32 event_num = ghes->generic->notify.vector; |
---|
| 898 | + |
---|
| 899 | + might_sleep(); |
---|
| 900 | + |
---|
| 901 | + if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) |
---|
| 902 | + return -EOPNOTSUPP; |
---|
| 903 | + |
---|
| 904 | + /* |
---|
| 905 | + * The event may be running on another CPU. Disable it |
---|
| 906 | + * to stop new events, then try to unregister a few times. |
---|
| 907 | + */ |
---|
| 908 | + err = sdei_event_disable(event_num); |
---|
| 909 | + if (err) |
---|
| 910 | + return err; |
---|
| 911 | + |
---|
| 912 | + for (i = 0; i < 3; i++) { |
---|
| 913 | + err = sdei_event_unregister(event_num); |
---|
| 914 | + if (err != -EINPROGRESS) |
---|
| 915 | + break; |
---|
| 916 | + |
---|
| 917 | + schedule(); |
---|
| 918 | + } |
---|
| 919 | + |
---|
| 920 | + return err; |
---|
888 | 921 | } |
---|
889 | 922 | |
---|
890 | 923 | #ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE |
---|
.. | .. |
---|
976 | 1009 | if (np) { |
---|
977 | 1010 | if (of_property_read_string(np, "method", &method)) { |
---|
978 | 1011 | pr_warn("missing \"method\" property\n"); |
---|
979 | | - return CONDUIT_INVALID; |
---|
| 1012 | + return SMCCC_CONDUIT_NONE; |
---|
980 | 1013 | } |
---|
981 | 1014 | |
---|
982 | 1015 | if (!strcmp("hvc", method)) { |
---|
983 | 1016 | sdei_firmware_call = &sdei_smccc_hvc; |
---|
984 | | - return CONDUIT_HVC; |
---|
| 1017 | + return SMCCC_CONDUIT_HVC; |
---|
985 | 1018 | } else if (!strcmp("smc", method)) { |
---|
986 | 1019 | sdei_firmware_call = &sdei_smccc_smc; |
---|
987 | | - return CONDUIT_SMC; |
---|
| 1020 | + return SMCCC_CONDUIT_SMC; |
---|
988 | 1021 | } |
---|
989 | 1022 | |
---|
990 | 1023 | pr_warn("invalid \"method\" property: %s\n", method); |
---|
991 | | - } else if (IS_ENABLED(CONFIG_ACPI) && !acpi_disabled) { |
---|
| 1024 | + } else if (!acpi_disabled) { |
---|
992 | 1025 | if (acpi_psci_use_hvc()) { |
---|
993 | 1026 | sdei_firmware_call = &sdei_smccc_hvc; |
---|
994 | | - return CONDUIT_HVC; |
---|
| 1027 | + return SMCCC_CONDUIT_HVC; |
---|
995 | 1028 | } else { |
---|
996 | 1029 | sdei_firmware_call = &sdei_smccc_smc; |
---|
997 | | - return CONDUIT_SMC; |
---|
| 1030 | + return SMCCC_CONDUIT_SMC; |
---|
998 | 1031 | } |
---|
999 | 1032 | } |
---|
1000 | 1033 | |
---|
1001 | | - return CONDUIT_INVALID; |
---|
| 1034 | + return SMCCC_CONDUIT_NONE; |
---|
1002 | 1035 | } |
---|
1003 | 1036 | |
---|
1004 | 1037 | static int sdei_probe(struct platform_device *pdev) |
---|
.. | .. |
---|
1012 | 1045 | return 0; |
---|
1013 | 1046 | |
---|
1014 | 1047 | err = sdei_api_get_version(&ver); |
---|
1015 | | - if (err == -EOPNOTSUPP) |
---|
1016 | | - pr_err("advertised but not implemented in platform firmware\n"); |
---|
1017 | 1048 | if (err) { |
---|
1018 | 1049 | pr_err("Failed to get SDEI version: %d\n", err); |
---|
1019 | 1050 | sdei_mark_interface_broken(); |
---|
.. | .. |
---|
1087 | 1118 | .probe = sdei_probe, |
---|
1088 | 1119 | }; |
---|
1089 | 1120 | |
---|
1090 | | -static bool __init sdei_present_dt(void) |
---|
1091 | | -{ |
---|
1092 | | - struct device_node *np, *fw_np; |
---|
1093 | | - |
---|
1094 | | - fw_np = of_find_node_by_name(NULL, "firmware"); |
---|
1095 | | - if (!fw_np) |
---|
1096 | | - return false; |
---|
1097 | | - |
---|
1098 | | - np = of_find_matching_node(fw_np, sdei_of_match); |
---|
1099 | | - if (!np) |
---|
1100 | | - return false; |
---|
1101 | | - of_node_put(np); |
---|
1102 | | - |
---|
1103 | | - return true; |
---|
1104 | | -} |
---|
1105 | | - |
---|
1106 | 1121 | static bool __init sdei_present_acpi(void) |
---|
1107 | 1122 | { |
---|
1108 | 1123 | acpi_status status; |
---|
1109 | | - struct platform_device *pdev; |
---|
1110 | 1124 | struct acpi_table_header *sdei_table_header; |
---|
1111 | 1125 | |
---|
1112 | 1126 | if (acpi_disabled) |
---|
.. | .. |
---|
1121 | 1135 | if (ACPI_FAILURE(status)) |
---|
1122 | 1136 | return false; |
---|
1123 | 1137 | |
---|
1124 | | - pdev = platform_device_register_simple(sdei_driver.driver.name, 0, NULL, |
---|
1125 | | - 0); |
---|
1126 | | - if (IS_ERR(pdev)) |
---|
1127 | | - return false; |
---|
| 1138 | + acpi_put_table(sdei_table_header); |
---|
1128 | 1139 | |
---|
1129 | 1140 | return true; |
---|
1130 | 1141 | } |
---|
1131 | 1142 | |
---|
1132 | | -static int __init sdei_init(void) |
---|
| 1143 | +void __init sdei_init(void) |
---|
1133 | 1144 | { |
---|
1134 | | - if (sdei_present_dt() || sdei_present_acpi()) |
---|
1135 | | - platform_driver_register(&sdei_driver); |
---|
| 1145 | + struct platform_device *pdev; |
---|
| 1146 | + int ret; |
---|
1136 | 1147 | |
---|
1137 | | - return 0; |
---|
| 1148 | + ret = platform_driver_register(&sdei_driver); |
---|
| 1149 | + if (ret || !sdei_present_acpi()) |
---|
| 1150 | + return; |
---|
| 1151 | + |
---|
| 1152 | + pdev = platform_device_register_simple(sdei_driver.driver.name, |
---|
| 1153 | + 0, NULL, 0); |
---|
| 1154 | + if (IS_ERR(pdev)) { |
---|
| 1155 | + ret = PTR_ERR(pdev); |
---|
| 1156 | + platform_driver_unregister(&sdei_driver); |
---|
| 1157 | + pr_info("Failed to register ACPI:SDEI platform device %d\n", |
---|
| 1158 | + ret); |
---|
| 1159 | + } |
---|
1138 | 1160 | } |
---|
1139 | | - |
---|
1140 | | -/* |
---|
1141 | | - * On an ACPI system SDEI needs to be ready before HEST:GHES tries to register |
---|
1142 | | - * its events. ACPI is initialised from a subsys_initcall(), GHES is initialised |
---|
1143 | | - * by device_initcall(). We want to be called in the middle. |
---|
1144 | | - */ |
---|
1145 | | -subsys_initcall_sync(sdei_init); |
---|
1146 | 1161 | |
---|
1147 | 1162 | int sdei_event_handler(struct pt_regs *regs, |
---|
1148 | 1163 | struct sdei_registered_event *arg) |
---|
.. | .. |
---|
1151 | 1166 | mm_segment_t orig_addr_limit; |
---|
1152 | 1167 | u32 event_num = arg->event_num; |
---|
1153 | 1168 | |
---|
1154 | | - orig_addr_limit = get_fs(); |
---|
1155 | | - set_fs(USER_DS); |
---|
| 1169 | + /* |
---|
| 1170 | + * Save restore 'fs'. |
---|
| 1171 | + * The architecture's entry code save/restores 'fs' when taking an |
---|
| 1172 | + * exception from the kernel. This ensures addr_limit isn't inherited |
---|
| 1173 | + * if you interrupted something that allowed the uaccess routines to |
---|
| 1174 | + * access kernel memory. |
---|
| 1175 | + * Do the same here because this doesn't come via the same entry code. |
---|
| 1176 | + */ |
---|
| 1177 | + orig_addr_limit = force_uaccess_begin(); |
---|
1156 | 1178 | |
---|
1157 | 1179 | err = arg->callback(event_num, regs, arg->callback_arg); |
---|
1158 | 1180 | if (err) |
---|
1159 | 1181 | pr_err_ratelimited("event %u on CPU %u failed with error: %d\n", |
---|
1160 | 1182 | event_num, smp_processor_id(), err); |
---|
1161 | 1183 | |
---|
1162 | | - set_fs(orig_addr_limit); |
---|
| 1184 | + force_uaccess_end(orig_addr_limit); |
---|
1163 | 1185 | |
---|
1164 | 1186 | return err; |
---|
1165 | 1187 | } |
---|
1166 | 1188 | NOKPROBE_SYMBOL(sdei_event_handler); |
---|
| 1189 | + |
---|
| 1190 | +void sdei_handler_abort(void) |
---|
| 1191 | +{ |
---|
| 1192 | + /* |
---|
| 1193 | + * If the crash happened in an SDEI event handler then we need to |
---|
| 1194 | + * finish the handler with the firmware so that we can have working |
---|
| 1195 | + * interrupts in the crash kernel. |
---|
| 1196 | + */ |
---|
| 1197 | + if (__this_cpu_read(sdei_active_critical_event)) { |
---|
| 1198 | + pr_warn("still in SDEI critical event context, attempting to finish handler.\n"); |
---|
| 1199 | + __sdei_handler_abort(); |
---|
| 1200 | + __this_cpu_write(sdei_active_critical_event, NULL); |
---|
| 1201 | + } |
---|
| 1202 | + if (__this_cpu_read(sdei_active_normal_event)) { |
---|
| 1203 | + pr_warn("still in SDEI normal event context, attempting to finish handler.\n"); |
---|
| 1204 | + __sdei_handler_abort(); |
---|
| 1205 | + __this_cpu_write(sdei_active_normal_event, NULL); |
---|
| 1206 | + } |
---|
| 1207 | +} |
---|