| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * PowerNV OPAL high level interfaces |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright 2011 IBM Corp. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public License |
|---|
| 8 | | - * as published by the Free Software Foundation; either version |
|---|
| 9 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #define pr_fmt(fmt) "opal: " fmt |
|---|
| .. | .. |
|---|
| 26 | 22 | #include <linux/memblock.h> |
|---|
| 27 | 23 | #include <linux/kthread.h> |
|---|
| 28 | 24 | #include <linux/freezer.h> |
|---|
| 29 | | -#include <linux/printk.h> |
|---|
| 30 | 25 | #include <linux/kmsg_dump.h> |
|---|
| 31 | 26 | #include <linux/console.h> |
|---|
| 32 | 27 | #include <linux/sched/debug.h> |
|---|
| .. | .. |
|---|
| 39 | 34 | #include <asm/bug.h> |
|---|
| 40 | 35 | |
|---|
| 41 | 36 | #include "powernv.h" |
|---|
| 37 | + |
|---|
| 38 | +#define OPAL_MSG_QUEUE_MAX 16 |
|---|
| 39 | + |
|---|
| 40 | +struct opal_msg_node { |
|---|
| 41 | + struct list_head list; |
|---|
| 42 | + struct opal_msg msg; |
|---|
| 43 | +}; |
|---|
| 44 | + |
|---|
| 45 | +static DEFINE_SPINLOCK(msg_list_lock); |
|---|
| 46 | +static LIST_HEAD(msg_list); |
|---|
| 42 | 47 | |
|---|
| 43 | 48 | /* /sys/firmware/opal */ |
|---|
| 44 | 49 | struct kobject *opal_kobj; |
|---|
| .. | .. |
|---|
| 55 | 60 | u64 recover_addr; |
|---|
| 56 | 61 | }; |
|---|
| 57 | 62 | |
|---|
| 63 | +static int msg_list_size; |
|---|
| 64 | + |
|---|
| 58 | 65 | static struct mcheck_recoverable_range *mc_recoverable_range; |
|---|
| 59 | 66 | static int mc_recoverable_range_len; |
|---|
| 60 | 67 | |
|---|
| .. | .. |
|---|
| 63 | 70 | static struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX]; |
|---|
| 64 | 71 | static uint32_t opal_heartbeat; |
|---|
| 65 | 72 | static struct task_struct *kopald_tsk; |
|---|
| 73 | +static struct opal_msg *opal_msg; |
|---|
| 74 | +static u32 opal_msg_size __ro_after_init; |
|---|
| 66 | 75 | |
|---|
| 67 | 76 | void opal_configure_cores(void) |
|---|
| 68 | 77 | { |
|---|
| .. | .. |
|---|
| 171 | 180 | /* |
|---|
| 172 | 181 | * Allocate a buffer to hold the MC recoverable ranges. |
|---|
| 173 | 182 | */ |
|---|
| 174 | | - mc_recoverable_range =__va(memblock_alloc(size, __alignof__(u64))); |
|---|
| 175 | | - memset(mc_recoverable_range, 0, size); |
|---|
| 183 | + mc_recoverable_range = memblock_alloc(size, __alignof__(u64)); |
|---|
| 184 | + if (!mc_recoverable_range) |
|---|
| 185 | + panic("%s: Failed to allocate %u bytes align=0x%lx\n", |
|---|
| 186 | + __func__, size, __alignof__(u64)); |
|---|
| 176 | 187 | |
|---|
| 177 | 188 | for (i = 0; i < mc_recoverable_range_len; i++) { |
|---|
| 178 | 189 | mc_recoverable_range[i].start_addr = |
|---|
| .. | .. |
|---|
| 205 | 216 | glue = 0x7000; |
|---|
| 206 | 217 | |
|---|
| 207 | 218 | /* |
|---|
| 208 | | - * Check if we are running on newer firmware that exports |
|---|
| 209 | | - * OPAL_HANDLE_HMI token. If yes, then don't ask OPAL to patch |
|---|
| 210 | | - * the HMI interrupt and we catch it directly in Linux. |
|---|
| 219 | + * Only ancient OPAL firmware requires this. |
|---|
| 220 | + * Specifically, firmware from FW810.00 (released June 2014) |
|---|
| 221 | + * through FW810.20 (Released October 2014). |
|---|
| 211 | 222 | * |
|---|
| 212 | | - * For older firmware (i.e currently released POWER8 System Firmware |
|---|
| 213 | | - * as of today <= SV810_087), we fallback to old behavior and let OPAL |
|---|
| 214 | | - * patch the HMI vector and handle it inside OPAL firmware. |
|---|
| 223 | + * Check if we are running on newer (post Oct 2014) firmware that |
|---|
| 224 | + * exports the OPAL_HANDLE_HMI token. If yes, then don't ask OPAL to |
|---|
| 225 | + * patch the HMI interrupt and we catch it directly in Linux. |
|---|
| 215 | 226 | * |
|---|
| 216 | | - * For newer firmware (in development/yet to be released) we will |
|---|
| 217 | | - * start catching/handling HMI directly in Linux. |
|---|
| 227 | + * For older firmware (i.e < FW810.20), we fallback to old behavior and |
|---|
| 228 | + * let OPAL patch the HMI vector and handle it inside OPAL firmware. |
|---|
| 229 | + * |
|---|
| 230 | + * For newer firmware we catch/handle the HMI directly in Linux. |
|---|
| 218 | 231 | */ |
|---|
| 219 | 232 | if (!opal_check_token(OPAL_HANDLE_HMI)) { |
|---|
| 220 | 233 | pr_info("Old firmware detected, OPAL handles HMIs.\n"); |
|---|
| .. | .. |
|---|
| 224 | 237 | glue += 128; |
|---|
| 225 | 238 | } |
|---|
| 226 | 239 | |
|---|
| 240 | + /* |
|---|
| 241 | + * Only applicable to ancient firmware, all modern |
|---|
| 242 | + * (post March 2015/skiboot 5.0) firmware will just return |
|---|
| 243 | + * OPAL_UNSUPPORTED. |
|---|
| 244 | + */ |
|---|
| 227 | 245 | opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); |
|---|
| 228 | 246 | #endif |
|---|
| 229 | 247 | |
|---|
| 230 | 248 | return 0; |
|---|
| 231 | 249 | } |
|---|
| 232 | 250 | machine_early_initcall(powernv, opal_register_exception_handlers); |
|---|
| 251 | + |
|---|
| 252 | +static void queue_replay_msg(void *msg) |
|---|
| 253 | +{ |
|---|
| 254 | + struct opal_msg_node *msg_node; |
|---|
| 255 | + |
|---|
| 256 | + if (msg_list_size < OPAL_MSG_QUEUE_MAX) { |
|---|
| 257 | + msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC); |
|---|
| 258 | + if (msg_node) { |
|---|
| 259 | + INIT_LIST_HEAD(&msg_node->list); |
|---|
| 260 | + memcpy(&msg_node->msg, msg, sizeof(struct opal_msg)); |
|---|
| 261 | + list_add_tail(&msg_node->list, &msg_list); |
|---|
| 262 | + msg_list_size++; |
|---|
| 263 | + } else |
|---|
| 264 | + pr_warn_once("message queue no memory\n"); |
|---|
| 265 | + |
|---|
| 266 | + if (msg_list_size >= OPAL_MSG_QUEUE_MAX) |
|---|
| 267 | + pr_warn_once("message queue full\n"); |
|---|
| 268 | + } |
|---|
| 269 | +} |
|---|
| 270 | + |
|---|
| 271 | +static void dequeue_replay_msg(enum opal_msg_type msg_type) |
|---|
| 272 | +{ |
|---|
| 273 | + struct opal_msg_node *msg_node, *tmp; |
|---|
| 274 | + |
|---|
| 275 | + list_for_each_entry_safe(msg_node, tmp, &msg_list, list) { |
|---|
| 276 | + if (be32_to_cpu(msg_node->msg.msg_type) != msg_type) |
|---|
| 277 | + continue; |
|---|
| 278 | + |
|---|
| 279 | + atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type], |
|---|
| 280 | + msg_type, |
|---|
| 281 | + &msg_node->msg); |
|---|
| 282 | + |
|---|
| 283 | + list_del(&msg_node->list); |
|---|
| 284 | + kfree(msg_node); |
|---|
| 285 | + msg_list_size--; |
|---|
| 286 | + } |
|---|
| 287 | +} |
|---|
| 233 | 288 | |
|---|
| 234 | 289 | /* |
|---|
| 235 | 290 | * Opal message notifier based on message type. Allow subscribers to get |
|---|
| .. | .. |
|---|
| 238 | 293 | int opal_message_notifier_register(enum opal_msg_type msg_type, |
|---|
| 239 | 294 | struct notifier_block *nb) |
|---|
| 240 | 295 | { |
|---|
| 296 | + int ret; |
|---|
| 297 | + unsigned long flags; |
|---|
| 298 | + |
|---|
| 241 | 299 | if (!nb || msg_type >= OPAL_MSG_TYPE_MAX) { |
|---|
| 242 | 300 | pr_warn("%s: Invalid arguments, msg_type:%d\n", |
|---|
| 243 | 301 | __func__, msg_type); |
|---|
| 244 | 302 | return -EINVAL; |
|---|
| 245 | 303 | } |
|---|
| 246 | 304 | |
|---|
| 247 | | - return atomic_notifier_chain_register( |
|---|
| 248 | | - &opal_msg_notifier_head[msg_type], nb); |
|---|
| 305 | + spin_lock_irqsave(&msg_list_lock, flags); |
|---|
| 306 | + ret = atomic_notifier_chain_register( |
|---|
| 307 | + &opal_msg_notifier_head[msg_type], nb); |
|---|
| 308 | + |
|---|
| 309 | + /* |
|---|
| 310 | + * If the registration succeeded, replay any queued messages that came |
|---|
| 311 | + * in prior to the notifier chain registration. msg_list_lock held here |
|---|
| 312 | + * to ensure they're delivered prior to any subsequent messages. |
|---|
| 313 | + */ |
|---|
| 314 | + if (ret == 0) |
|---|
| 315 | + dequeue_replay_msg(msg_type); |
|---|
| 316 | + |
|---|
| 317 | + spin_unlock_irqrestore(&msg_list_lock, flags); |
|---|
| 318 | + |
|---|
| 319 | + return ret; |
|---|
| 249 | 320 | } |
|---|
| 250 | 321 | EXPORT_SYMBOL_GPL(opal_message_notifier_register); |
|---|
| 251 | 322 | |
|---|
| .. | .. |
|---|
| 259 | 330 | |
|---|
| 260 | 331 | static void opal_message_do_notify(uint32_t msg_type, void *msg) |
|---|
| 261 | 332 | { |
|---|
| 333 | + unsigned long flags; |
|---|
| 334 | + bool queued = false; |
|---|
| 335 | + |
|---|
| 336 | + spin_lock_irqsave(&msg_list_lock, flags); |
|---|
| 337 | + if (opal_msg_notifier_head[msg_type].head == NULL) { |
|---|
| 338 | + /* |
|---|
| 339 | + * Queue up the msg since no notifiers have registered |
|---|
| 340 | + * yet for this msg_type. |
|---|
| 341 | + */ |
|---|
| 342 | + queue_replay_msg(msg); |
|---|
| 343 | + queued = true; |
|---|
| 344 | + } |
|---|
| 345 | + spin_unlock_irqrestore(&msg_list_lock, flags); |
|---|
| 346 | + |
|---|
| 347 | + if (queued) |
|---|
| 348 | + return; |
|---|
| 349 | + |
|---|
| 262 | 350 | /* notify subscribers */ |
|---|
| 263 | 351 | atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type], |
|---|
| 264 | 352 | msg_type, msg); |
|---|
| .. | .. |
|---|
| 267 | 355 | static void opal_handle_message(void) |
|---|
| 268 | 356 | { |
|---|
| 269 | 357 | s64 ret; |
|---|
| 270 | | - /* |
|---|
| 271 | | - * TODO: pre-allocate a message buffer depending on opal-msg-size |
|---|
| 272 | | - * value in /proc/device-tree. |
|---|
| 273 | | - */ |
|---|
| 274 | | - static struct opal_msg msg; |
|---|
| 275 | 358 | u32 type; |
|---|
| 276 | 359 | |
|---|
| 277 | | - ret = opal_get_msg(__pa(&msg), sizeof(msg)); |
|---|
| 360 | + ret = opal_get_msg(__pa(opal_msg), opal_msg_size); |
|---|
| 278 | 361 | /* No opal message pending. */ |
|---|
| 279 | 362 | if (ret == OPAL_RESOURCE) |
|---|
| 280 | 363 | return; |
|---|
| .. | .. |
|---|
| 286 | 369 | return; |
|---|
| 287 | 370 | } |
|---|
| 288 | 371 | |
|---|
| 289 | | - type = be32_to_cpu(msg.msg_type); |
|---|
| 372 | + type = be32_to_cpu(opal_msg->msg_type); |
|---|
| 290 | 373 | |
|---|
| 291 | 374 | /* Sanity check */ |
|---|
| 292 | 375 | if (type >= OPAL_MSG_TYPE_MAX) { |
|---|
| 293 | 376 | pr_warn_once("%s: Unknown message type: %u\n", __func__, type); |
|---|
| 294 | 377 | return; |
|---|
| 295 | 378 | } |
|---|
| 296 | | - opal_message_do_notify(type, (void *)&msg); |
|---|
| 379 | + opal_message_do_notify(type, (void *)opal_msg); |
|---|
| 297 | 380 | } |
|---|
| 298 | 381 | |
|---|
| 299 | 382 | static irqreturn_t opal_message_notify(int irq, void *data) |
|---|
| .. | .. |
|---|
| 302 | 385 | return IRQ_HANDLED; |
|---|
| 303 | 386 | } |
|---|
| 304 | 387 | |
|---|
| 305 | | -static int __init opal_message_init(void) |
|---|
| 388 | +static int __init opal_message_init(struct device_node *opal_node) |
|---|
| 306 | 389 | { |
|---|
| 307 | 390 | int ret, i, irq; |
|---|
| 391 | + |
|---|
| 392 | + ret = of_property_read_u32(opal_node, "opal-msg-size", &opal_msg_size); |
|---|
| 393 | + if (ret) { |
|---|
| 394 | + pr_notice("Failed to read opal-msg-size property\n"); |
|---|
| 395 | + opal_msg_size = sizeof(struct opal_msg); |
|---|
| 396 | + } |
|---|
| 397 | + |
|---|
| 398 | + opal_msg = kmalloc(opal_msg_size, GFP_KERNEL); |
|---|
| 399 | + if (!opal_msg) { |
|---|
| 400 | + opal_msg_size = sizeof(struct opal_msg); |
|---|
| 401 | + /* Try to allocate fixed message size */ |
|---|
| 402 | + opal_msg = kmalloc(opal_msg_size, GFP_KERNEL); |
|---|
| 403 | + BUG_ON(opal_msg == NULL); |
|---|
| 404 | + } |
|---|
| 308 | 405 | |
|---|
| 309 | 406 | for (i = 0; i < OPAL_MSG_TYPE_MAX; i++) |
|---|
| 310 | 407 | ATOMIC_INIT_NOTIFIER_HEAD(&opal_msg_notifier_head[i]); |
|---|
| .. | .. |
|---|
| 504 | 601 | recovered = 0; |
|---|
| 505 | 602 | } |
|---|
| 506 | 603 | |
|---|
| 507 | | - if (!recovered && evt->severity == MCE_SEV_ERROR_SYNC) { |
|---|
| 604 | + if (!recovered && evt->sync_error) { |
|---|
| 508 | 605 | /* |
|---|
| 509 | 606 | * Try to kill processes if we get a synchronous machine check |
|---|
| 510 | 607 | * (e.g., one caused by execution of this instruction). This |
|---|
| .. | .. |
|---|
| 535 | 632 | return recovered; |
|---|
| 536 | 633 | } |
|---|
| 537 | 634 | |
|---|
| 538 | | -void pnv_platform_error_reboot(struct pt_regs *regs, const char *msg) |
|---|
| 635 | +void __noreturn pnv_platform_error_reboot(struct pt_regs *regs, const char *msg) |
|---|
| 539 | 636 | { |
|---|
| 540 | 637 | panic_flush_kmsg_start(); |
|---|
| 541 | 638 | |
|---|
| .. | .. |
|---|
| 587 | 684 | evt.version); |
|---|
| 588 | 685 | return 0; |
|---|
| 589 | 686 | } |
|---|
| 590 | | - machine_check_print_event_info(&evt, user_mode(regs)); |
|---|
| 687 | + machine_check_print_event_info(&evt, user_mode(regs), false); |
|---|
| 591 | 688 | |
|---|
| 592 | 689 | if (opal_recover_mce(regs, &evt)) |
|---|
| 593 | 690 | return 1; |
|---|
| .. | .. |
|---|
| 613 | 710 | return 0; |
|---|
| 614 | 711 | } |
|---|
| 615 | 712 | |
|---|
| 616 | | -/* HMI exception handler called in virtual mode during check_irq_replay. */ |
|---|
| 713 | +int opal_hmi_exception_early2(struct pt_regs *regs) |
|---|
| 714 | +{ |
|---|
| 715 | + s64 rc; |
|---|
| 716 | + __be64 out_flags; |
|---|
| 717 | + |
|---|
| 718 | + /* |
|---|
| 719 | + * call opal hmi handler. |
|---|
| 720 | + * Check 64-bit flag mask to find out if an event was generated, |
|---|
| 721 | + * and whether TB is still valid or not etc. |
|---|
| 722 | + */ |
|---|
| 723 | + rc = opal_handle_hmi2(&out_flags); |
|---|
| 724 | + if (rc != OPAL_SUCCESS) |
|---|
| 725 | + return 0; |
|---|
| 726 | + |
|---|
| 727 | + if (be64_to_cpu(out_flags) & OPAL_HMI_FLAGS_NEW_EVENT) |
|---|
| 728 | + local_paca->hmi_event_available = 1; |
|---|
| 729 | + if (be64_to_cpu(out_flags) & OPAL_HMI_FLAGS_TOD_TB_FAIL) |
|---|
| 730 | + tb_invalid = true; |
|---|
| 731 | + return 1; |
|---|
| 732 | +} |
|---|
| 733 | + |
|---|
| 734 | +/* HMI exception handler called in virtual mode when irqs are next enabled. */ |
|---|
| 617 | 735 | int opal_handle_hmi_exception(struct pt_regs *regs) |
|---|
| 618 | 736 | { |
|---|
| 619 | 737 | /* |
|---|
| .. | .. |
|---|
| 672 | 790 | return 0; |
|---|
| 673 | 791 | } |
|---|
| 674 | 792 | |
|---|
| 675 | | -static ssize_t symbol_map_read(struct file *fp, struct kobject *kobj, |
|---|
| 676 | | - struct bin_attribute *bin_attr, |
|---|
| 677 | | - char *buf, loff_t off, size_t count) |
|---|
| 678 | | -{ |
|---|
| 679 | | - return memory_read_from_buffer(buf, count, &off, bin_attr->private, |
|---|
| 680 | | - bin_attr->size); |
|---|
| 681 | | -} |
|---|
| 682 | | - |
|---|
| 683 | | -static struct bin_attribute symbol_map_attr = { |
|---|
| 684 | | - .attr = {.name = "symbol_map", .mode = 0400}, |
|---|
| 685 | | - .read = symbol_map_read |
|---|
| 686 | | -}; |
|---|
| 687 | | - |
|---|
| 688 | | -static void opal_export_symmap(void) |
|---|
| 689 | | -{ |
|---|
| 690 | | - const __be64 *syms; |
|---|
| 691 | | - unsigned int size; |
|---|
| 692 | | - struct device_node *fw; |
|---|
| 693 | | - int rc; |
|---|
| 694 | | - |
|---|
| 695 | | - fw = of_find_node_by_path("/ibm,opal/firmware"); |
|---|
| 696 | | - if (!fw) |
|---|
| 697 | | - return; |
|---|
| 698 | | - syms = of_get_property(fw, "symbol-map", &size); |
|---|
| 699 | | - if (!syms || size != 2 * sizeof(__be64)) |
|---|
| 700 | | - return; |
|---|
| 701 | | - |
|---|
| 702 | | - /* Setup attributes */ |
|---|
| 703 | | - symbol_map_attr.private = __va(be64_to_cpu(syms[0])); |
|---|
| 704 | | - symbol_map_attr.size = be64_to_cpu(syms[1]); |
|---|
| 705 | | - |
|---|
| 706 | | - rc = sysfs_create_bin_file(opal_kobj, &symbol_map_attr); |
|---|
| 707 | | - if (rc) |
|---|
| 708 | | - pr_warn("Error %d creating OPAL symbols file\n", rc); |
|---|
| 709 | | -} |
|---|
| 710 | | - |
|---|
| 711 | 793 | static ssize_t export_attr_read(struct file *fp, struct kobject *kobj, |
|---|
| 712 | 794 | struct bin_attribute *bin_attr, char *buf, |
|---|
| 713 | 795 | loff_t off, size_t count) |
|---|
| 714 | 796 | { |
|---|
| 715 | 797 | return memory_read_from_buffer(buf, count, &off, bin_attr->private, |
|---|
| 716 | 798 | bin_attr->size); |
|---|
| 799 | +} |
|---|
| 800 | + |
|---|
| 801 | +static int opal_add_one_export(struct kobject *parent, const char *export_name, |
|---|
| 802 | + struct device_node *np, const char *prop_name) |
|---|
| 803 | +{ |
|---|
| 804 | + struct bin_attribute *attr = NULL; |
|---|
| 805 | + const char *name = NULL; |
|---|
| 806 | + u64 vals[2]; |
|---|
| 807 | + int rc; |
|---|
| 808 | + |
|---|
| 809 | + rc = of_property_read_u64_array(np, prop_name, &vals[0], 2); |
|---|
| 810 | + if (rc) |
|---|
| 811 | + goto out; |
|---|
| 812 | + |
|---|
| 813 | + attr = kzalloc(sizeof(*attr), GFP_KERNEL); |
|---|
| 814 | + if (!attr) { |
|---|
| 815 | + rc = -ENOMEM; |
|---|
| 816 | + goto out; |
|---|
| 817 | + } |
|---|
| 818 | + name = kstrdup(export_name, GFP_KERNEL); |
|---|
| 819 | + if (!name) { |
|---|
| 820 | + rc = -ENOMEM; |
|---|
| 821 | + goto out; |
|---|
| 822 | + } |
|---|
| 823 | + |
|---|
| 824 | + sysfs_bin_attr_init(attr); |
|---|
| 825 | + attr->attr.name = name; |
|---|
| 826 | + attr->attr.mode = 0400; |
|---|
| 827 | + attr->read = export_attr_read; |
|---|
| 828 | + attr->private = __va(vals[0]); |
|---|
| 829 | + attr->size = vals[1]; |
|---|
| 830 | + |
|---|
| 831 | + rc = sysfs_create_bin_file(parent, attr); |
|---|
| 832 | +out: |
|---|
| 833 | + if (rc) { |
|---|
| 834 | + kfree(name); |
|---|
| 835 | + kfree(attr); |
|---|
| 836 | + } |
|---|
| 837 | + |
|---|
| 838 | + return rc; |
|---|
| 839 | +} |
|---|
| 840 | + |
|---|
| 841 | +static void opal_add_exported_attrs(struct device_node *np, |
|---|
| 842 | + struct kobject *kobj) |
|---|
| 843 | +{ |
|---|
| 844 | + struct device_node *child; |
|---|
| 845 | + struct property *prop; |
|---|
| 846 | + |
|---|
| 847 | + for_each_property_of_node(np, prop) { |
|---|
| 848 | + int rc; |
|---|
| 849 | + |
|---|
| 850 | + if (!strcmp(prop->name, "name") || |
|---|
| 851 | + !strcmp(prop->name, "phandle")) |
|---|
| 852 | + continue; |
|---|
| 853 | + |
|---|
| 854 | + rc = opal_add_one_export(kobj, prop->name, np, prop->name); |
|---|
| 855 | + if (rc) { |
|---|
| 856 | + pr_warn("Unable to add export %pOF/%s, rc = %d!\n", |
|---|
| 857 | + np, prop->name, rc); |
|---|
| 858 | + } |
|---|
| 859 | + } |
|---|
| 860 | + |
|---|
| 861 | + for_each_child_of_node(np, child) { |
|---|
| 862 | + struct kobject *child_kobj; |
|---|
| 863 | + |
|---|
| 864 | + child_kobj = kobject_create_and_add(child->name, kobj); |
|---|
| 865 | + if (!child_kobj) { |
|---|
| 866 | + pr_err("Unable to create export dir for %pOF\n", child); |
|---|
| 867 | + continue; |
|---|
| 868 | + } |
|---|
| 869 | + |
|---|
| 870 | + opal_add_exported_attrs(child, child_kobj); |
|---|
| 871 | + } |
|---|
| 717 | 872 | } |
|---|
| 718 | 873 | |
|---|
| 719 | 874 | /* |
|---|
| .. | .. |
|---|
| 725 | 880 | */ |
|---|
| 726 | 881 | static void opal_export_attrs(void) |
|---|
| 727 | 882 | { |
|---|
| 728 | | - struct bin_attribute *attr; |
|---|
| 729 | 883 | struct device_node *np; |
|---|
| 730 | | - struct property *prop; |
|---|
| 731 | 884 | struct kobject *kobj; |
|---|
| 732 | | - u64 vals[2]; |
|---|
| 733 | 885 | int rc; |
|---|
| 734 | 886 | |
|---|
| 735 | 887 | np = of_find_node_by_path("/ibm,opal/firmware/exports"); |
|---|
| .. | .. |
|---|
| 740 | 892 | kobj = kobject_create_and_add("exports", opal_kobj); |
|---|
| 741 | 893 | if (!kobj) { |
|---|
| 742 | 894 | pr_warn("kobject_create_and_add() of exports failed\n"); |
|---|
| 895 | + of_node_put(np); |
|---|
| 743 | 896 | return; |
|---|
| 744 | 897 | } |
|---|
| 745 | 898 | |
|---|
| 746 | | - for_each_property_of_node(np, prop) { |
|---|
| 747 | | - if (!strcmp(prop->name, "name") || !strcmp(prop->name, "phandle")) |
|---|
| 748 | | - continue; |
|---|
| 899 | + opal_add_exported_attrs(np, kobj); |
|---|
| 749 | 900 | |
|---|
| 750 | | - if (of_property_read_u64_array(np, prop->name, &vals[0], 2)) |
|---|
| 751 | | - continue; |
|---|
| 752 | | - |
|---|
| 753 | | - attr = kzalloc(sizeof(*attr), GFP_KERNEL); |
|---|
| 754 | | - |
|---|
| 755 | | - if (attr == NULL) { |
|---|
| 756 | | - pr_warn("Failed kmalloc for bin_attribute!"); |
|---|
| 757 | | - continue; |
|---|
| 758 | | - } |
|---|
| 759 | | - |
|---|
| 760 | | - sysfs_bin_attr_init(attr); |
|---|
| 761 | | - attr->attr.name = kstrdup(prop->name, GFP_KERNEL); |
|---|
| 762 | | - attr->attr.mode = 0400; |
|---|
| 763 | | - attr->read = export_attr_read; |
|---|
| 764 | | - attr->private = __va(vals[0]); |
|---|
| 765 | | - attr->size = vals[1]; |
|---|
| 766 | | - |
|---|
| 767 | | - if (attr->attr.name == NULL) { |
|---|
| 768 | | - pr_warn("Failed kstrdup for bin_attribute attr.name"); |
|---|
| 769 | | - kfree(attr); |
|---|
| 770 | | - continue; |
|---|
| 771 | | - } |
|---|
| 772 | | - |
|---|
| 773 | | - rc = sysfs_create_bin_file(kobj, attr); |
|---|
| 774 | | - if (rc) { |
|---|
| 775 | | - pr_warn("Error %d creating OPAL sysfs exports/%s file\n", |
|---|
| 776 | | - rc, prop->name); |
|---|
| 777 | | - kfree(attr->attr.name); |
|---|
| 778 | | - kfree(attr); |
|---|
| 779 | | - } |
|---|
| 780 | | - } |
|---|
| 901 | + /* |
|---|
| 902 | + * NB: symbol_map existed before the generic export interface so it |
|---|
| 903 | + * lives under the top level opal_kobj. |
|---|
| 904 | + */ |
|---|
| 905 | + rc = opal_add_one_export(opal_kobj, "symbol_map", |
|---|
| 906 | + np->parent, "symbol-map"); |
|---|
| 907 | + if (rc) |
|---|
| 908 | + pr_warn("Error %d creating OPAL symbols file\n", rc); |
|---|
| 781 | 909 | |
|---|
| 782 | 910 | of_node_put(np); |
|---|
| 783 | 911 | } |
|---|
| .. | .. |
|---|
| 880 | 1008 | consoles = of_find_node_by_path("/ibm,opal/consoles"); |
|---|
| 881 | 1009 | if (consoles) { |
|---|
| 882 | 1010 | for_each_child_of_node(consoles, np) { |
|---|
| 883 | | - if (strcmp(np->name, "serial")) |
|---|
| 1011 | + if (!of_node_name_eq(np, "serial")) |
|---|
| 884 | 1012 | continue; |
|---|
| 885 | 1013 | of_platform_device_create(np, NULL, NULL); |
|---|
| 886 | 1014 | } |
|---|
| .. | .. |
|---|
| 888 | 1016 | } |
|---|
| 889 | 1017 | |
|---|
| 890 | 1018 | /* Initialise OPAL messaging system */ |
|---|
| 891 | | - opal_message_init(); |
|---|
| 1019 | + opal_message_init(opal_node); |
|---|
| 892 | 1020 | |
|---|
| 893 | 1021 | /* Initialise OPAL asynchronous completion interface */ |
|---|
| 894 | 1022 | opal_async_comp_init(); |
|---|
| .. | .. |
|---|
| 924 | 1052 | /* Create "opal" kobject under /sys/firmware */ |
|---|
| 925 | 1053 | rc = opal_sysfs_init(); |
|---|
| 926 | 1054 | if (rc == 0) { |
|---|
| 927 | | - /* Export symbol map to userspace */ |
|---|
| 928 | | - opal_export_symmap(); |
|---|
| 929 | 1055 | /* Setup dump region interface */ |
|---|
| 930 | 1056 | opal_dump_region_init(); |
|---|
| 931 | 1057 | /* Setup error log interface */ |
|---|
| .. | .. |
|---|
| 938 | 1064 | opal_sys_param_init(); |
|---|
| 939 | 1065 | /* Setup message log sysfs interface. */ |
|---|
| 940 | 1066 | opal_msglog_sysfs_init(); |
|---|
| 1067 | + /* Add all export properties*/ |
|---|
| 1068 | + opal_export_attrs(); |
|---|
| 941 | 1069 | } |
|---|
| 942 | | - |
|---|
| 943 | | - /* Export all properties */ |
|---|
| 944 | | - opal_export_attrs(); |
|---|
| 945 | 1070 | |
|---|
| 946 | 1071 | /* Initialize platform devices: IPMI backend, PRD & flash interface */ |
|---|
| 947 | 1072 | opal_pdev_init("ibm,opal-ipmi"); |
|---|
| .. | .. |
|---|
| 963 | 1088 | /* Initialise OPAL sensor groups */ |
|---|
| 964 | 1089 | opal_sensor_groups_init(); |
|---|
| 965 | 1090 | |
|---|
| 1091 | + /* Initialise OPAL Power control interface */ |
|---|
| 1092 | + opal_power_control_init(); |
|---|
| 1093 | + |
|---|
| 1094 | + /* Initialize OPAL secure variables */ |
|---|
| 1095 | + opal_pdev_init("ibm,secvar-backend"); |
|---|
| 1096 | + |
|---|
| 966 | 1097 | return 0; |
|---|
| 967 | 1098 | } |
|---|
| 968 | 1099 | machine_subsys_initcall(powernv, opal_init); |
|---|