| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright 2016,2017 IBM Corporation. |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or |
|---|
| 5 | | - * modify it under the terms of the GNU General Public License |
|---|
| 6 | | - * as published by the Free Software Foundation; either version |
|---|
| 7 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 8 | 4 | */ |
|---|
| 9 | 5 | |
|---|
| 10 | 6 | #define pr_fmt(fmt) "xive: " fmt |
|---|
| .. | .. |
|---|
| 20 | 16 | #include <linux/cpumask.h> |
|---|
| 21 | 17 | #include <linux/mm.h> |
|---|
| 22 | 18 | #include <linux/delay.h> |
|---|
| 19 | +#include <linux/libfdt.h> |
|---|
| 23 | 20 | |
|---|
| 21 | +#include <asm/machdep.h> |
|---|
| 24 | 22 | #include <asm/prom.h> |
|---|
| 25 | 23 | #include <asm/io.h> |
|---|
| 26 | 24 | #include <asm/smp.h> |
|---|
| .. | .. |
|---|
| 29 | 27 | #include <asm/xive.h> |
|---|
| 30 | 28 | #include <asm/xive-regs.h> |
|---|
| 31 | 29 | #include <asm/hvcall.h> |
|---|
| 30 | +#include <asm/svm.h> |
|---|
| 31 | +#include <asm/ultravisor.h> |
|---|
| 32 | 32 | |
|---|
| 33 | 33 | #include "xive-internal.h" |
|---|
| 34 | 34 | |
|---|
| .. | .. |
|---|
| 48 | 48 | { |
|---|
| 49 | 49 | struct xive_irq_bitmap *xibm; |
|---|
| 50 | 50 | |
|---|
| 51 | | - xibm = kzalloc(sizeof(*xibm), GFP_ATOMIC); |
|---|
| 51 | + xibm = kzalloc(sizeof(*xibm), GFP_KERNEL); |
|---|
| 52 | 52 | if (!xibm) |
|---|
| 53 | 53 | return -ENOMEM; |
|---|
| 54 | 54 | |
|---|
| .. | .. |
|---|
| 56 | 56 | xibm->base = base; |
|---|
| 57 | 57 | xibm->count = count; |
|---|
| 58 | 58 | xibm->bitmap = kzalloc(xibm->count, GFP_KERNEL); |
|---|
| 59 | + if (!xibm->bitmap) { |
|---|
| 60 | + kfree(xibm); |
|---|
| 61 | + return -ENOMEM; |
|---|
| 62 | + } |
|---|
| 59 | 63 | list_add(&xibm->list, &xive_irq_bitmaps); |
|---|
| 60 | 64 | |
|---|
| 61 | 65 | pr_info("Using IRQ range [%x-%x]", xibm->base, |
|---|
| .. | .. |
|---|
| 210 | 214 | lisn, target, prio, rc); |
|---|
| 211 | 215 | return rc; |
|---|
| 212 | 216 | } |
|---|
| 217 | + |
|---|
| 218 | + return 0; |
|---|
| 219 | +} |
|---|
| 220 | + |
|---|
| 221 | +static long plpar_int_get_source_config(unsigned long flags, |
|---|
| 222 | + unsigned long lisn, |
|---|
| 223 | + unsigned long *target, |
|---|
| 224 | + unsigned long *prio, |
|---|
| 225 | + unsigned long *sw_irq) |
|---|
| 226 | +{ |
|---|
| 227 | + unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; |
|---|
| 228 | + long rc; |
|---|
| 229 | + |
|---|
| 230 | + pr_devel("H_INT_GET_SOURCE_CONFIG flags=%lx lisn=%lx\n", flags, lisn); |
|---|
| 231 | + |
|---|
| 232 | + do { |
|---|
| 233 | + rc = plpar_hcall(H_INT_GET_SOURCE_CONFIG, retbuf, flags, lisn, |
|---|
| 234 | + target, prio, sw_irq); |
|---|
| 235 | + } while (plpar_busy_delay(rc)); |
|---|
| 236 | + |
|---|
| 237 | + if (rc) { |
|---|
| 238 | + pr_err("H_INT_GET_SOURCE_CONFIG lisn=%ld failed %ld\n", |
|---|
| 239 | + lisn, rc); |
|---|
| 240 | + return rc; |
|---|
| 241 | + } |
|---|
| 242 | + |
|---|
| 243 | + *target = retbuf[0]; |
|---|
| 244 | + *prio = retbuf[1]; |
|---|
| 245 | + *sw_irq = retbuf[2]; |
|---|
| 246 | + |
|---|
| 247 | + pr_devel("H_INT_GET_SOURCE_CONFIG target=%lx prio=%lx sw_irq=%lx\n", |
|---|
| 248 | + retbuf[0], retbuf[1], retbuf[2]); |
|---|
| 213 | 249 | |
|---|
| 214 | 250 | return 0; |
|---|
| 215 | 251 | } |
|---|
| .. | .. |
|---|
| 405 | 441 | return rc == 0 ? 0 : -ENXIO; |
|---|
| 406 | 442 | } |
|---|
| 407 | 443 | |
|---|
| 444 | +static int xive_spapr_get_irq_config(u32 hw_irq, u32 *target, u8 *prio, |
|---|
| 445 | + u32 *sw_irq) |
|---|
| 446 | +{ |
|---|
| 447 | + long rc; |
|---|
| 448 | + unsigned long h_target; |
|---|
| 449 | + unsigned long h_prio; |
|---|
| 450 | + unsigned long h_sw_irq; |
|---|
| 451 | + |
|---|
| 452 | + rc = plpar_int_get_source_config(0, hw_irq, &h_target, &h_prio, |
|---|
| 453 | + &h_sw_irq); |
|---|
| 454 | + |
|---|
| 455 | + *target = h_target; |
|---|
| 456 | + *prio = h_prio; |
|---|
| 457 | + *sw_irq = h_sw_irq; |
|---|
| 458 | + |
|---|
| 459 | + return rc == 0 ? 0 : -ENXIO; |
|---|
| 460 | +} |
|---|
| 461 | + |
|---|
| 408 | 462 | /* This can be called multiple time to change a queue configuration */ |
|---|
| 409 | 463 | static int xive_spapr_configure_queue(u32 target, struct xive_q *q, u8 prio, |
|---|
| 410 | 464 | __be32 *qpage, u32 order) |
|---|
| .. | .. |
|---|
| 450 | 504 | rc = -EIO; |
|---|
| 451 | 505 | } else { |
|---|
| 452 | 506 | q->qpage = qpage; |
|---|
| 507 | + if (is_secure_guest()) |
|---|
| 508 | + uv_share_page(PHYS_PFN(qpage_phys), |
|---|
| 509 | + 1 << xive_alloc_order(order)); |
|---|
| 453 | 510 | } |
|---|
| 454 | 511 | fail: |
|---|
| 455 | 512 | return rc; |
|---|
| .. | .. |
|---|
| 483 | 540 | hw_cpu, prio); |
|---|
| 484 | 541 | |
|---|
| 485 | 542 | alloc_order = xive_alloc_order(xive_queue_shift); |
|---|
| 543 | + if (is_secure_guest()) |
|---|
| 544 | + uv_unshare_page(PHYS_PFN(__pa(q->qpage)), 1 << alloc_order); |
|---|
| 486 | 545 | free_pages((unsigned long)q->qpage, alloc_order); |
|---|
| 487 | 546 | q->qpage = NULL; |
|---|
| 488 | 547 | } |
|---|
| .. | .. |
|---|
| 594 | 653 | plpar_int_sync(0, hw_irq); |
|---|
| 595 | 654 | } |
|---|
| 596 | 655 | |
|---|
| 656 | +static int xive_spapr_debug_show(struct seq_file *m, void *private) |
|---|
| 657 | +{ |
|---|
| 658 | + struct xive_irq_bitmap *xibm; |
|---|
| 659 | + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); |
|---|
| 660 | + |
|---|
| 661 | + if (!buf) |
|---|
| 662 | + return -ENOMEM; |
|---|
| 663 | + |
|---|
| 664 | + list_for_each_entry(xibm, &xive_irq_bitmaps, list) { |
|---|
| 665 | + memset(buf, 0, PAGE_SIZE); |
|---|
| 666 | + bitmap_print_to_pagebuf(true, buf, xibm->bitmap, xibm->count); |
|---|
| 667 | + seq_printf(m, "bitmap #%d: %s", xibm->count, buf); |
|---|
| 668 | + } |
|---|
| 669 | + kfree(buf); |
|---|
| 670 | + |
|---|
| 671 | + return 0; |
|---|
| 672 | +} |
|---|
| 673 | + |
|---|
| 597 | 674 | static const struct xive_ops xive_spapr_ops = { |
|---|
| 598 | 675 | .populate_irq_data = xive_spapr_populate_irq_data, |
|---|
| 599 | 676 | .configure_irq = xive_spapr_configure_irq, |
|---|
| 677 | + .get_irq_config = xive_spapr_get_irq_config, |
|---|
| 600 | 678 | .setup_queue = xive_spapr_setup_queue, |
|---|
| 601 | 679 | .cleanup_queue = xive_spapr_cleanup_queue, |
|---|
| 602 | 680 | .match = xive_spapr_match, |
|---|
| .. | .. |
|---|
| 610 | 688 | #ifdef CONFIG_SMP |
|---|
| 611 | 689 | .get_ipi = xive_spapr_get_ipi, |
|---|
| 612 | 690 | .put_ipi = xive_spapr_put_ipi, |
|---|
| 691 | + .debug_show = xive_spapr_debug_show, |
|---|
| 613 | 692 | #endif /* CONFIG_SMP */ |
|---|
| 614 | 693 | .name = "spapr", |
|---|
| 615 | 694 | }; |
|---|
| .. | .. |
|---|
| 631 | 710 | } |
|---|
| 632 | 711 | |
|---|
| 633 | 712 | reg = of_get_property(rootdn, "ibm,plat-res-int-priorities", &len); |
|---|
| 713 | + of_node_put(rootdn); |
|---|
| 634 | 714 | if (!reg) { |
|---|
| 635 | 715 | pr_err("Failed to read 'ibm,plat-res-int-priorities' property\n"); |
|---|
| 636 | 716 | return false; |
|---|
| .. | .. |
|---|
| 671 | 751 | return true; |
|---|
| 672 | 752 | } |
|---|
| 673 | 753 | |
|---|
| 754 | +static const u8 *get_vec5_feature(unsigned int index) |
|---|
| 755 | +{ |
|---|
| 756 | + unsigned long root, chosen; |
|---|
| 757 | + int size; |
|---|
| 758 | + const u8 *vec5; |
|---|
| 759 | + |
|---|
| 760 | + root = of_get_flat_dt_root(); |
|---|
| 761 | + chosen = of_get_flat_dt_subnode_by_name(root, "chosen"); |
|---|
| 762 | + if (chosen == -FDT_ERR_NOTFOUND) |
|---|
| 763 | + return NULL; |
|---|
| 764 | + |
|---|
| 765 | + vec5 = of_get_flat_dt_prop(chosen, "ibm,architecture-vec-5", &size); |
|---|
| 766 | + if (!vec5) |
|---|
| 767 | + return NULL; |
|---|
| 768 | + |
|---|
| 769 | + if (size <= index) |
|---|
| 770 | + return NULL; |
|---|
| 771 | + |
|---|
| 772 | + return vec5 + index; |
|---|
| 773 | +} |
|---|
| 774 | + |
|---|
| 775 | +static bool __init xive_spapr_disabled(void) |
|---|
| 776 | +{ |
|---|
| 777 | + const u8 *vec5_xive; |
|---|
| 778 | + |
|---|
| 779 | + vec5_xive = get_vec5_feature(OV5_INDX(OV5_XIVE_SUPPORT)); |
|---|
| 780 | + if (vec5_xive) { |
|---|
| 781 | + u8 val; |
|---|
| 782 | + |
|---|
| 783 | + val = *vec5_xive & OV5_FEAT(OV5_XIVE_SUPPORT); |
|---|
| 784 | + switch (val) { |
|---|
| 785 | + case OV5_FEAT(OV5_XIVE_EITHER): |
|---|
| 786 | + case OV5_FEAT(OV5_XIVE_LEGACY): |
|---|
| 787 | + break; |
|---|
| 788 | + case OV5_FEAT(OV5_XIVE_EXPLOIT): |
|---|
| 789 | + /* Hypervisor only supports XIVE */ |
|---|
| 790 | + if (xive_cmdline_disabled) |
|---|
| 791 | + pr_warn("WARNING: Ignoring cmdline option xive=off\n"); |
|---|
| 792 | + return false; |
|---|
| 793 | + default: |
|---|
| 794 | + pr_warn("%s: Unknown xive support option: 0x%x\n", |
|---|
| 795 | + __func__, val); |
|---|
| 796 | + break; |
|---|
| 797 | + } |
|---|
| 798 | + } |
|---|
| 799 | + |
|---|
| 800 | + return xive_cmdline_disabled; |
|---|
| 801 | +} |
|---|
| 802 | + |
|---|
| 674 | 803 | bool __init xive_spapr_init(void) |
|---|
| 675 | 804 | { |
|---|
| 676 | 805 | struct device_node *np; |
|---|
| .. | .. |
|---|
| 683 | 812 | const __be32 *reg; |
|---|
| 684 | 813 | int i; |
|---|
| 685 | 814 | |
|---|
| 686 | | - if (xive_cmdline_disabled) |
|---|
| 815 | + if (xive_spapr_disabled()) |
|---|
| 687 | 816 | return false; |
|---|
| 688 | 817 | |
|---|
| 689 | 818 | pr_devel("%s()\n", __func__); |
|---|
| .. | .. |
|---|
| 738 | 867 | pr_info("Using %dkB queues\n", 1 << (xive_queue_shift - 10)); |
|---|
| 739 | 868 | return true; |
|---|
| 740 | 869 | } |
|---|
| 870 | + |
|---|
| 871 | +machine_arch_initcall(pseries, xive_core_debug_init); |
|---|