.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Routines providing a simple monitor for use on the PowerMac. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 1996-2005 Paul Mackerras. |
---|
5 | 6 | * Copyright (C) 2001 PPC64 Team, IBM Corp |
---|
6 | 7 | * Copyrignt (C) 2006 Michael Ellerman, IBM Corp |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public License |
---|
10 | | - * as published by the Free Software Foundation; either version |
---|
11 | | - * 2 of the License, or (at your option) any later version. |
---|
12 | 8 | */ |
---|
13 | 9 | |
---|
14 | 10 | #include <linux/kernel.h> |
---|
.. | .. |
---|
29 | 25 | #include <linux/nmi.h> |
---|
30 | 26 | #include <linux/ctype.h> |
---|
31 | 27 | #include <linux/highmem.h> |
---|
| 28 | +#include <linux/security.h> |
---|
32 | 29 | |
---|
33 | 30 | #include <asm/debugfs.h> |
---|
34 | 31 | #include <asm/ptrace.h> |
---|
.. | .. |
---|
38 | 35 | #include <asm/machdep.h> |
---|
39 | 36 | #include <asm/xmon.h> |
---|
40 | 37 | #include <asm/processor.h> |
---|
41 | | -#include <asm/pgtable.h> |
---|
42 | 38 | #include <asm/mmu.h> |
---|
43 | 39 | #include <asm/mmu_context.h> |
---|
44 | 40 | #include <asm/plpar_wrappers.h> |
---|
.. | .. |
---|
57 | 53 | #include <asm/firmware.h> |
---|
58 | 54 | #include <asm/code-patching.h> |
---|
59 | 55 | #include <asm/sections.h> |
---|
| 56 | +#include <asm/inst.h> |
---|
60 | 57 | |
---|
61 | 58 | #ifdef CONFIG_PPC64 |
---|
62 | 59 | #include <asm/hvcall.h> |
---|
63 | 60 | #include <asm/paca.h> |
---|
| 61 | +#include <asm/lppaca.h> |
---|
64 | 62 | #endif |
---|
65 | 63 | |
---|
66 | 64 | #include "nonstdio.h" |
---|
67 | 65 | #include "dis-asm.h" |
---|
| 66 | +#include "xmon_bpts.h" |
---|
68 | 67 | |
---|
69 | 68 | #ifdef CONFIG_SMP |
---|
70 | 69 | static cpumask_t cpus_in_xmon = CPU_MASK_NONE; |
---|
.. | .. |
---|
80 | 79 | #endif |
---|
81 | 80 | static unsigned long in_xmon __read_mostly = 0; |
---|
82 | 81 | static int xmon_on = IS_ENABLED(CONFIG_XMON_DEFAULT); |
---|
| 82 | +static bool xmon_is_ro = IS_ENABLED(CONFIG_XMON_DEFAULT_RO_MODE); |
---|
83 | 83 | |
---|
84 | 84 | static unsigned long adrs; |
---|
85 | 85 | static int size = 1; |
---|
86 | | -#define MAX_DUMP (128 * 1024) |
---|
| 86 | +#define MAX_DUMP (64 * 1024) |
---|
87 | 87 | static unsigned long ndump = 64; |
---|
| 88 | +#define MAX_IDUMP (MAX_DUMP >> 2) |
---|
88 | 89 | static unsigned long nidump = 16; |
---|
89 | 90 | static unsigned long ncsum = 4096; |
---|
90 | 91 | static int termch; |
---|
.. | .. |
---|
99 | 100 | /* Breakpoint stuff */ |
---|
100 | 101 | struct bpt { |
---|
101 | 102 | unsigned long address; |
---|
102 | | - unsigned int instr[2]; |
---|
| 103 | + struct ppc_inst *instr; |
---|
103 | 104 | atomic_t ref_count; |
---|
104 | 105 | int enabled; |
---|
105 | 106 | unsigned long pad; |
---|
.. | .. |
---|
110 | 111 | #define BP_TRAP 2 |
---|
111 | 112 | #define BP_DABR 4 |
---|
112 | 113 | |
---|
113 | | -#define NBPTS 256 |
---|
114 | 114 | static struct bpt bpts[NBPTS]; |
---|
115 | | -static struct bpt dabr; |
---|
| 115 | +static struct bpt dabr[HBP_NUM_MAX]; |
---|
116 | 116 | static struct bpt *iabr; |
---|
117 | 117 | static unsigned bpinstr = 0x7fe00008; /* trap */ |
---|
118 | 118 | |
---|
.. | .. |
---|
122 | 122 | static int cmds(struct pt_regs *); |
---|
123 | 123 | static int mread(unsigned long, void *, int); |
---|
124 | 124 | static int mwrite(unsigned long, void *, int); |
---|
| 125 | +static int mread_instr(unsigned long, struct ppc_inst *); |
---|
125 | 126 | static int handle_fault(struct pt_regs *); |
---|
126 | 127 | static void byterev(unsigned char *, int); |
---|
127 | 128 | static void memex(void); |
---|
.. | .. |
---|
190 | 191 | static void dump_tlb_book3e(void); |
---|
191 | 192 | #endif |
---|
192 | 193 | |
---|
| 194 | +static void clear_all_bpt(void); |
---|
| 195 | + |
---|
193 | 196 | #ifdef CONFIG_PPC64 |
---|
194 | 197 | #define REG "%.16lx" |
---|
195 | 198 | #else |
---|
.. | .. |
---|
201 | 204 | #else |
---|
202 | 205 | #define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3]) |
---|
203 | 206 | #endif |
---|
| 207 | + |
---|
| 208 | +static const char *xmon_ro_msg = "Operation disabled: xmon in read-only mode\n"; |
---|
204 | 209 | |
---|
205 | 210 | static char *help_string = "\ |
---|
206 | 211 | Commands:\n\ |
---|
.. | .. |
---|
276 | 281 | X exit monitor and don't recover\n" |
---|
277 | 282 | #if defined(CONFIG_PPC64) && !defined(CONFIG_PPC_BOOK3E) |
---|
278 | 283 | " u dump segment table or SLB\n" |
---|
279 | | -#elif defined(CONFIG_PPC_STD_MMU_32) |
---|
| 284 | +#elif defined(CONFIG_PPC_BOOK3S_32) |
---|
280 | 285 | " u dump segment registers\n" |
---|
281 | 286 | #elif defined(CONFIG_44x) || defined(CONFIG_PPC_BOOK3E) |
---|
282 | 287 | " u dump TLB\n" |
---|
.. | .. |
---|
284 | 289 | " U show uptime information\n" |
---|
285 | 290 | " ? help\n" |
---|
286 | 291 | " # n limit output to n lines per page (for dp, dpa, dl)\n" |
---|
287 | | -" zr reboot\n\ |
---|
288 | | - zh halt\n" |
---|
| 292 | +" zr reboot\n" |
---|
| 293 | +" zh halt\n" |
---|
289 | 294 | ; |
---|
| 295 | + |
---|
| 296 | +#ifdef CONFIG_SECURITY |
---|
| 297 | +static bool xmon_is_locked_down(void) |
---|
| 298 | +{ |
---|
| 299 | + static bool lockdown; |
---|
| 300 | + |
---|
| 301 | + if (!lockdown) { |
---|
| 302 | + lockdown = !!security_locked_down(LOCKDOWN_XMON_RW); |
---|
| 303 | + if (lockdown) { |
---|
| 304 | + printf("xmon: Disabled due to kernel lockdown\n"); |
---|
| 305 | + xmon_is_ro = true; |
---|
| 306 | + } |
---|
| 307 | + } |
---|
| 308 | + |
---|
| 309 | + if (!xmon_is_ro) { |
---|
| 310 | + xmon_is_ro = !!security_locked_down(LOCKDOWN_XMON_WR); |
---|
| 311 | + if (xmon_is_ro) |
---|
| 312 | + printf("xmon: Read-only due to kernel lockdown\n"); |
---|
| 313 | + } |
---|
| 314 | + |
---|
| 315 | + return lockdown; |
---|
| 316 | +} |
---|
| 317 | +#else /* CONFIG_SECURITY */ |
---|
| 318 | +static inline bool xmon_is_locked_down(void) |
---|
| 319 | +{ |
---|
| 320 | + return false; |
---|
| 321 | +} |
---|
| 322 | +#endif |
---|
290 | 323 | |
---|
291 | 324 | static struct pt_regs *xmon_regs; |
---|
292 | 325 | |
---|
293 | 326 | static inline void sync(void) |
---|
294 | 327 | { |
---|
295 | 328 | asm volatile("sync; isync"); |
---|
296 | | -} |
---|
297 | | - |
---|
298 | | -static inline void store_inst(void *p) |
---|
299 | | -{ |
---|
300 | | - asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p)); |
---|
301 | 329 | } |
---|
302 | 330 | |
---|
303 | 331 | static inline void cflush(void *p) |
---|
.. | .. |
---|
439 | 467 | |
---|
440 | 468 | return false; |
---|
441 | 469 | } |
---|
442 | | -#endif /* CONFIG_SMP */ |
---|
| 470 | +#else /* CONFIG_SMP */ |
---|
| 471 | +static inline void get_output_lock(void) {} |
---|
| 472 | +static inline void release_output_lock(void) {} |
---|
| 473 | +#endif |
---|
443 | 474 | |
---|
444 | 475 | static inline int unrecoverable_excp(struct pt_regs *regs) |
---|
445 | 476 | { |
---|
.. | .. |
---|
451 | 482 | #endif |
---|
452 | 483 | } |
---|
453 | 484 | |
---|
| 485 | +static void xmon_touch_watchdogs(void) |
---|
| 486 | +{ |
---|
| 487 | + touch_softlockup_watchdog_sync(); |
---|
| 488 | + rcu_cpu_stall_reset(); |
---|
| 489 | + touch_nmi_watchdog(); |
---|
| 490 | +} |
---|
| 491 | + |
---|
454 | 492 | static int xmon_core(struct pt_regs *regs, int fromipi) |
---|
455 | 493 | { |
---|
456 | 494 | int cmd = 0; |
---|
457 | 495 | struct bpt *bp; |
---|
458 | 496 | long recurse_jmp[JMP_BUF_LEN]; |
---|
| 497 | + bool locked_down; |
---|
459 | 498 | unsigned long offset; |
---|
460 | 499 | unsigned long flags; |
---|
461 | 500 | #ifdef CONFIG_SMP |
---|
.. | .. |
---|
465 | 504 | |
---|
466 | 505 | local_irq_save(flags); |
---|
467 | 506 | hard_irq_disable(); |
---|
| 507 | + |
---|
| 508 | + locked_down = xmon_is_locked_down(); |
---|
468 | 509 | |
---|
469 | 510 | if (!fromipi) { |
---|
470 | 511 | tracing_enabled = tracing_is_on(); |
---|
.. | .. |
---|
519 | 560 | |
---|
520 | 561 | if (!fromipi) { |
---|
521 | 562 | get_output_lock(); |
---|
522 | | - excprint(regs); |
---|
| 563 | + if (!locked_down) |
---|
| 564 | + excprint(regs); |
---|
523 | 565 | if (bp) { |
---|
524 | 566 | printf("cpu 0x%x stopped at breakpoint 0x%tx (", |
---|
525 | 567 | cpu, BP_NUM(bp)); |
---|
.. | .. |
---|
571 | 613 | } |
---|
572 | 614 | remove_bpts(); |
---|
573 | 615 | disable_surveillance(); |
---|
574 | | - /* for breakpoint or single step, print the current instr. */ |
---|
575 | | - if (bp || TRAP(regs) == 0xd00) |
---|
576 | | - ppc_inst_dump(regs->nip, 1, 0); |
---|
577 | | - printf("enter ? for help\n"); |
---|
| 616 | + |
---|
| 617 | + if (!locked_down) { |
---|
| 618 | + /* for breakpoint or single step, print curr insn */ |
---|
| 619 | + if (bp || TRAP(regs) == 0xd00) |
---|
| 620 | + ppc_inst_dump(regs->nip, 1, 0); |
---|
| 621 | + printf("enter ? for help\n"); |
---|
| 622 | + } |
---|
| 623 | + |
---|
578 | 624 | mb(); |
---|
579 | 625 | xmon_gate = 1; |
---|
580 | 626 | barrier(); |
---|
.. | .. |
---|
598 | 644 | spin_cpu_relax(); |
---|
599 | 645 | touch_nmi_watchdog(); |
---|
600 | 646 | } else { |
---|
601 | | - cmd = cmds(regs); |
---|
602 | | - if (cmd != 0) { |
---|
| 647 | + if (!locked_down) |
---|
| 648 | + cmd = cmds(regs); |
---|
| 649 | + if (locked_down || cmd != 0) { |
---|
603 | 650 | /* exiting xmon */ |
---|
604 | 651 | insert_bpts(); |
---|
605 | 652 | xmon_gate = 0; |
---|
.. | .. |
---|
636 | 683 | "can't continue\n"); |
---|
637 | 684 | remove_bpts(); |
---|
638 | 685 | disable_surveillance(); |
---|
639 | | - /* for breakpoint or single step, print the current instr. */ |
---|
640 | | - if (bp || TRAP(regs) == 0xd00) |
---|
641 | | - ppc_inst_dump(regs->nip, 1, 0); |
---|
642 | | - printf("enter ? for help\n"); |
---|
| 686 | + if (!locked_down) { |
---|
| 687 | + /* for breakpoint or single step, print current insn */ |
---|
| 688 | + if (bp || TRAP(regs) == 0xd00) |
---|
| 689 | + ppc_inst_dump(regs->nip, 1, 0); |
---|
| 690 | + printf("enter ? for help\n"); |
---|
| 691 | + } |
---|
643 | 692 | } |
---|
644 | 693 | |
---|
645 | | - cmd = cmds(regs); |
---|
| 694 | + if (!locked_down) |
---|
| 695 | + cmd = cmds(regs); |
---|
646 | 696 | |
---|
647 | 697 | insert_bpts(); |
---|
648 | 698 | in_xmon = 0; |
---|
.. | .. |
---|
660 | 710 | if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT)) { |
---|
661 | 711 | bp = at_breakpoint(regs->nip); |
---|
662 | 712 | if (bp != NULL) { |
---|
663 | | - int stepped = emulate_step(regs, bp->instr[0]); |
---|
| 713 | + int stepped = emulate_step(regs, ppc_inst_read(bp->instr)); |
---|
664 | 714 | if (stepped == 0) { |
---|
665 | 715 | regs->nip = (unsigned long) &bp->instr[0]; |
---|
666 | 716 | atomic_inc(&bp->ref_count); |
---|
667 | 717 | } else if (stepped < 0) { |
---|
668 | 718 | printf("Couldn't single-step %s instruction\n", |
---|
669 | | - (IS_RFID(bp->instr[0])? "rfid": "mtmsrd")); |
---|
| 719 | + IS_RFID(ppc_inst_read(bp->instr))? "rfid": "mtmsrd"); |
---|
670 | 720 | } |
---|
671 | 721 | } |
---|
672 | 722 | } |
---|
673 | 723 | #endif |
---|
674 | | - insert_cpu_bpts(); |
---|
| 724 | + if (locked_down) |
---|
| 725 | + clear_all_bpt(); |
---|
| 726 | + else |
---|
| 727 | + insert_cpu_bpts(); |
---|
675 | 728 | |
---|
676 | | - touch_nmi_watchdog(); |
---|
| 729 | + xmon_touch_watchdogs(); |
---|
677 | 730 | local_irq_restore(flags); |
---|
678 | 731 | |
---|
679 | 732 | return cmd != 'X' && cmd != EOF; |
---|
.. | .. |
---|
712 | 765 | |
---|
713 | 766 | /* Are we at the trap at bp->instr[1] for some bp? */ |
---|
714 | 767 | bp = in_breakpoint_table(regs->nip, &offset); |
---|
715 | | - if (bp != NULL && offset == 4) { |
---|
716 | | - regs->nip = bp->address + 4; |
---|
| 768 | + if (bp != NULL && (offset == 4 || offset == 8)) { |
---|
| 769 | + regs->nip = bp->address + offset; |
---|
717 | 770 | atomic_dec(&bp->ref_count); |
---|
718 | 771 | return 1; |
---|
719 | 772 | } |
---|
.. | .. |
---|
738 | 791 | |
---|
739 | 792 | static int xmon_break_match(struct pt_regs *regs) |
---|
740 | 793 | { |
---|
| 794 | + int i; |
---|
| 795 | + |
---|
741 | 796 | if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT)) |
---|
742 | 797 | return 0; |
---|
743 | | - if (dabr.enabled == 0) |
---|
744 | | - return 0; |
---|
| 798 | + for (i = 0; i < nr_wp_slots(); i++) { |
---|
| 799 | + if (dabr[i].enabled) |
---|
| 800 | + goto found; |
---|
| 801 | + } |
---|
| 802 | + return 0; |
---|
| 803 | + |
---|
| 804 | +found: |
---|
745 | 805 | xmon_core(regs, 0); |
---|
746 | 806 | return 1; |
---|
747 | 807 | } |
---|
.. | .. |
---|
810 | 870 | { |
---|
811 | 871 | unsigned long off; |
---|
812 | 872 | |
---|
813 | | - off = nip - (unsigned long) bpts; |
---|
814 | | - if (off >= sizeof(bpts)) |
---|
| 873 | + off = nip - (unsigned long)bpt_table; |
---|
| 874 | + if (off >= sizeof(bpt_table)) |
---|
815 | 875 | return NULL; |
---|
816 | | - off %= sizeof(struct bpt); |
---|
817 | | - if (off != offsetof(struct bpt, instr[0]) |
---|
818 | | - && off != offsetof(struct bpt, instr[1])) |
---|
| 876 | + *offp = off & (BPT_SIZE - 1); |
---|
| 877 | + if (off & 3) |
---|
819 | 878 | return NULL; |
---|
820 | | - *offp = off - offsetof(struct bpt, instr[0]); |
---|
821 | | - return (struct bpt *) (nip - off); |
---|
| 879 | + return bpts + (off / BPT_SIZE); |
---|
822 | 880 | } |
---|
823 | 881 | |
---|
824 | 882 | static struct bpt *new_breakpoint(unsigned long a) |
---|
.. | .. |
---|
833 | 891 | for (bp = bpts; bp < &bpts[NBPTS]; ++bp) { |
---|
834 | 892 | if (!bp->enabled && atomic_read(&bp->ref_count) == 0) { |
---|
835 | 893 | bp->address = a; |
---|
836 | | - bp->instr[1] = bpinstr; |
---|
837 | | - store_inst(&bp->instr[1]); |
---|
| 894 | + bp->instr = (void *)(bpt_table + ((bp - bpts) * BPT_WORDS)); |
---|
838 | 895 | return bp; |
---|
839 | 896 | } |
---|
840 | 897 | } |
---|
.. | .. |
---|
846 | 903 | static void insert_bpts(void) |
---|
847 | 904 | { |
---|
848 | 905 | int i; |
---|
849 | | - struct bpt *bp; |
---|
| 906 | + struct ppc_inst instr, instr2; |
---|
| 907 | + struct bpt *bp, *bp2; |
---|
850 | 908 | |
---|
851 | 909 | bp = bpts; |
---|
852 | 910 | for (i = 0; i < NBPTS; ++i, ++bp) { |
---|
853 | 911 | if ((bp->enabled & (BP_TRAP|BP_CIABR)) == 0) |
---|
854 | 912 | continue; |
---|
855 | | - if (mread(bp->address, &bp->instr[0], 4) != 4) { |
---|
| 913 | + if (!mread_instr(bp->address, &instr)) { |
---|
856 | 914 | printf("Couldn't read instruction at %lx, " |
---|
857 | 915 | "disabling breakpoint there\n", bp->address); |
---|
858 | 916 | bp->enabled = 0; |
---|
859 | 917 | continue; |
---|
860 | 918 | } |
---|
861 | | - if (IS_MTMSRD(bp->instr[0]) || IS_RFID(bp->instr[0])) { |
---|
| 919 | + if (IS_MTMSRD(instr) || IS_RFID(instr)) { |
---|
862 | 920 | printf("Breakpoint at %lx is on an mtmsrd or rfid " |
---|
863 | 921 | "instruction, disabling it\n", bp->address); |
---|
864 | 922 | bp->enabled = 0; |
---|
865 | 923 | continue; |
---|
866 | 924 | } |
---|
867 | | - store_inst(&bp->instr[0]); |
---|
| 925 | + /* |
---|
| 926 | + * Check the address is not a suffix by looking for a prefix in |
---|
| 927 | + * front of it. |
---|
| 928 | + */ |
---|
| 929 | + if (mread_instr(bp->address - 4, &instr2) == 8) { |
---|
| 930 | + printf("Breakpoint at %lx is on the second word of a prefixed instruction, disabling it\n", |
---|
| 931 | + bp->address); |
---|
| 932 | + bp->enabled = 0; |
---|
| 933 | + continue; |
---|
| 934 | + } |
---|
| 935 | + /* |
---|
| 936 | + * We might still be a suffix - if the prefix has already been |
---|
| 937 | + * replaced by a breakpoint we won't catch it with the above |
---|
| 938 | + * test. |
---|
| 939 | + */ |
---|
| 940 | + bp2 = at_breakpoint(bp->address - 4); |
---|
| 941 | + if (bp2 && ppc_inst_prefixed(ppc_inst_read(bp2->instr))) { |
---|
| 942 | + printf("Breakpoint at %lx is on the second word of a prefixed instruction, disabling it\n", |
---|
| 943 | + bp->address); |
---|
| 944 | + bp->enabled = 0; |
---|
| 945 | + continue; |
---|
| 946 | + } |
---|
| 947 | + |
---|
| 948 | + patch_instruction(bp->instr, instr); |
---|
| 949 | + patch_instruction(ppc_inst_next(bp->instr, &instr), |
---|
| 950 | + ppc_inst(bpinstr)); |
---|
868 | 951 | if (bp->enabled & BP_CIABR) |
---|
869 | 952 | continue; |
---|
870 | | - if (patch_instruction((unsigned int *)bp->address, |
---|
871 | | - bpinstr) != 0) { |
---|
| 953 | + if (patch_instruction((struct ppc_inst *)bp->address, |
---|
| 954 | + ppc_inst(bpinstr)) != 0) { |
---|
872 | 955 | printf("Couldn't write instruction at %lx, " |
---|
873 | 956 | "disabling breakpoint there\n", bp->address); |
---|
874 | 957 | bp->enabled &= ~BP_TRAP; |
---|
875 | 958 | continue; |
---|
876 | 959 | } |
---|
877 | | - store_inst((void *)bp->address); |
---|
878 | 960 | } |
---|
879 | 961 | } |
---|
880 | 962 | |
---|
881 | 963 | static void insert_cpu_bpts(void) |
---|
882 | 964 | { |
---|
| 965 | + int i; |
---|
883 | 966 | struct arch_hw_breakpoint brk; |
---|
884 | 967 | |
---|
885 | | - if (dabr.enabled) { |
---|
886 | | - brk.address = dabr.address; |
---|
887 | | - brk.type = (dabr.enabled & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL; |
---|
888 | | - brk.len = 8; |
---|
889 | | - __set_breakpoint(&brk); |
---|
| 968 | + for (i = 0; i < nr_wp_slots(); i++) { |
---|
| 969 | + if (dabr[i].enabled) { |
---|
| 970 | + brk.address = dabr[i].address; |
---|
| 971 | + brk.type = (dabr[i].enabled & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL; |
---|
| 972 | + brk.len = 8; |
---|
| 973 | + brk.hw_len = 8; |
---|
| 974 | + __set_breakpoint(i, &brk); |
---|
| 975 | + } |
---|
890 | 976 | } |
---|
891 | 977 | |
---|
892 | 978 | if (iabr) |
---|
.. | .. |
---|
897 | 983 | { |
---|
898 | 984 | int i; |
---|
899 | 985 | struct bpt *bp; |
---|
900 | | - unsigned instr; |
---|
| 986 | + struct ppc_inst instr; |
---|
901 | 987 | |
---|
902 | 988 | bp = bpts; |
---|
903 | 989 | for (i = 0; i < NBPTS; ++i, ++bp) { |
---|
904 | 990 | if ((bp->enabled & (BP_TRAP|BP_CIABR)) != BP_TRAP) |
---|
905 | 991 | continue; |
---|
906 | | - if (mread(bp->address, &instr, 4) == 4 |
---|
907 | | - && instr == bpinstr |
---|
| 992 | + if (mread_instr(bp->address, &instr) |
---|
| 993 | + && ppc_inst_equal(instr, ppc_inst(bpinstr)) |
---|
908 | 994 | && patch_instruction( |
---|
909 | | - (unsigned int *)bp->address, bp->instr[0]) != 0) |
---|
| 995 | + (struct ppc_inst *)bp->address, ppc_inst_read(bp->instr)) != 0) |
---|
910 | 996 | printf("Couldn't remove breakpoint at %lx\n", |
---|
911 | 997 | bp->address); |
---|
912 | | - else |
---|
913 | | - store_inst((void *)bp->address); |
---|
914 | 998 | } |
---|
915 | 999 | } |
---|
916 | 1000 | |
---|
.. | .. |
---|
991 | 1075 | memlocate(); |
---|
992 | 1076 | break; |
---|
993 | 1077 | case 'z': |
---|
| 1078 | + if (xmon_is_ro) { |
---|
| 1079 | + printf(xmon_ro_msg); |
---|
| 1080 | + break; |
---|
| 1081 | + } |
---|
994 | 1082 | memzcan(); |
---|
995 | 1083 | break; |
---|
996 | 1084 | case 'i': |
---|
.. | .. |
---|
1057 | 1145 | bootcmds(); |
---|
1058 | 1146 | break; |
---|
1059 | 1147 | case 'p': |
---|
| 1148 | + if (xmon_is_ro) { |
---|
| 1149 | + printf(xmon_ro_msg); |
---|
| 1150 | + break; |
---|
| 1151 | + } |
---|
1060 | 1152 | proccall(); |
---|
1061 | 1153 | break; |
---|
1062 | 1154 | case 'P': |
---|
1063 | 1155 | show_tasks(); |
---|
1064 | 1156 | break; |
---|
1065 | | -#ifdef CONFIG_PPC_STD_MMU |
---|
| 1157 | +#ifdef CONFIG_PPC_BOOK3S |
---|
1066 | 1158 | case 'u': |
---|
1067 | 1159 | dump_segments(); |
---|
1068 | 1160 | break; |
---|
.. | .. |
---|
1107 | 1199 | */ |
---|
1108 | 1200 | static int do_step(struct pt_regs *regs) |
---|
1109 | 1201 | { |
---|
1110 | | - unsigned int instr; |
---|
| 1202 | + struct ppc_inst instr; |
---|
1111 | 1203 | int stepped; |
---|
1112 | 1204 | |
---|
1113 | 1205 | force_enable_xmon(); |
---|
1114 | 1206 | /* check we are in 64-bit kernel mode, translation enabled */ |
---|
1115 | 1207 | if ((regs->msr & (MSR_64BIT|MSR_PR|MSR_IR)) == (MSR_64BIT|MSR_IR)) { |
---|
1116 | | - if (mread(regs->nip, &instr, 4) == 4) { |
---|
| 1208 | + if (mread_instr(regs->nip, &instr)) { |
---|
1117 | 1209 | stepped = emulate_step(regs, instr); |
---|
1118 | 1210 | if (stepped < 0) { |
---|
1119 | 1211 | printf("Couldn't single-step %s instruction\n", |
---|
.. | .. |
---|
1121 | 1213 | return 0; |
---|
1122 | 1214 | } |
---|
1123 | 1215 | if (stepped > 0) { |
---|
1124 | | - regs->trap = 0xd00 | (regs->trap & 1); |
---|
| 1216 | + set_trap(regs, 0xd00); |
---|
1125 | 1217 | printf("stepped to "); |
---|
1126 | 1218 | xmon_print_symbol(regs->nip, " ", "\n"); |
---|
1127 | 1219 | ppc_inst_dump(regs->nip, 1, 0); |
---|
.. | .. |
---|
1136 | 1228 | |
---|
1137 | 1229 | static void bootcmds(void) |
---|
1138 | 1230 | { |
---|
| 1231 | + char tmp[64]; |
---|
1139 | 1232 | int cmd; |
---|
1140 | 1233 | |
---|
1141 | 1234 | cmd = inchar(); |
---|
1142 | | - if (cmd == 'r') |
---|
1143 | | - ppc_md.restart(NULL); |
---|
1144 | | - else if (cmd == 'h') |
---|
| 1235 | + if (cmd == 'r') { |
---|
| 1236 | + getstring(tmp, 64); |
---|
| 1237 | + ppc_md.restart(tmp); |
---|
| 1238 | + } else if (cmd == 'h') { |
---|
1145 | 1239 | ppc_md.halt(); |
---|
1146 | | - else if (cmd == 'p') |
---|
| 1240 | + } else if (cmd == 'p') { |
---|
1147 | 1241 | if (pm_power_off) |
---|
1148 | 1242 | pm_power_off(); |
---|
| 1243 | + } |
---|
1149 | 1244 | } |
---|
1150 | 1245 | |
---|
1151 | 1246 | static int cpu_cmd(void) |
---|
.. | .. |
---|
1270 | 1365 | */ |
---|
1271 | 1366 | static long check_bp_loc(unsigned long addr) |
---|
1272 | 1367 | { |
---|
1273 | | - unsigned int instr; |
---|
| 1368 | + struct ppc_inst instr; |
---|
1274 | 1369 | |
---|
1275 | 1370 | addr &= ~3; |
---|
1276 | 1371 | if (!is_kernel_addr(addr)) { |
---|
1277 | 1372 | printf("Breakpoints may only be placed at kernel addresses\n"); |
---|
1278 | 1373 | return 0; |
---|
1279 | 1374 | } |
---|
1280 | | - if (!mread(addr, &instr, sizeof(instr))) { |
---|
| 1375 | + if (!mread_instr(addr, &instr)) { |
---|
1281 | 1376 | printf("Can't read instruction at address %lx\n", addr); |
---|
1282 | 1377 | return 0; |
---|
1283 | 1378 | } |
---|
.. | .. |
---|
1287 | 1382 | return 0; |
---|
1288 | 1383 | } |
---|
1289 | 1384 | return 1; |
---|
| 1385 | +} |
---|
| 1386 | + |
---|
| 1387 | +static int find_free_data_bpt(void) |
---|
| 1388 | +{ |
---|
| 1389 | + int i; |
---|
| 1390 | + |
---|
| 1391 | + for (i = 0; i < nr_wp_slots(); i++) { |
---|
| 1392 | + if (!dabr[i].enabled) |
---|
| 1393 | + return i; |
---|
| 1394 | + } |
---|
| 1395 | + printf("Couldn't find free breakpoint register\n"); |
---|
| 1396 | + return -1; |
---|
| 1397 | +} |
---|
| 1398 | + |
---|
| 1399 | +static void print_data_bpts(void) |
---|
| 1400 | +{ |
---|
| 1401 | + int i; |
---|
| 1402 | + |
---|
| 1403 | + for (i = 0; i < nr_wp_slots(); i++) { |
---|
| 1404 | + if (!dabr[i].enabled) |
---|
| 1405 | + continue; |
---|
| 1406 | + |
---|
| 1407 | + printf(" data "REG" [", dabr[i].address); |
---|
| 1408 | + if (dabr[i].enabled & 1) |
---|
| 1409 | + printf("r"); |
---|
| 1410 | + if (dabr[i].enabled & 2) |
---|
| 1411 | + printf("w"); |
---|
| 1412 | + printf("]\n"); |
---|
| 1413 | + } |
---|
1290 | 1414 | } |
---|
1291 | 1415 | |
---|
1292 | 1416 | static char *breakpoint_help_string = |
---|
.. | .. |
---|
1308 | 1432 | struct bpt *bp; |
---|
1309 | 1433 | |
---|
1310 | 1434 | cmd = inchar(); |
---|
| 1435 | + |
---|
1311 | 1436 | switch (cmd) { |
---|
1312 | | -#ifndef CONFIG_PPC_8xx |
---|
1313 | | - static const char badaddr[] = "Only kernel addresses are permitted for breakpoints\n"; |
---|
1314 | | - int mode; |
---|
1315 | | - case 'd': /* bd - hardware data breakpoint */ |
---|
| 1437 | + case 'd': { /* bd - hardware data breakpoint */ |
---|
| 1438 | + static const char badaddr[] = "Only kernel addresses are permitted for breakpoints\n"; |
---|
| 1439 | + int mode; |
---|
| 1440 | + if (xmon_is_ro) { |
---|
| 1441 | + printf(xmon_ro_msg); |
---|
| 1442 | + break; |
---|
| 1443 | + } |
---|
1316 | 1444 | if (!ppc_breakpoint_available()) { |
---|
1317 | 1445 | printf("Hardware data breakpoint not supported on this cpu\n"); |
---|
1318 | 1446 | break; |
---|
1319 | 1447 | } |
---|
| 1448 | + i = find_free_data_bpt(); |
---|
| 1449 | + if (i < 0) |
---|
| 1450 | + break; |
---|
1320 | 1451 | mode = 7; |
---|
1321 | 1452 | cmd = inchar(); |
---|
1322 | 1453 | if (cmd == 'r') |
---|
.. | .. |
---|
1325 | 1456 | mode = 6; |
---|
1326 | 1457 | else |
---|
1327 | 1458 | termch = cmd; |
---|
1328 | | - dabr.address = 0; |
---|
1329 | | - dabr.enabled = 0; |
---|
1330 | | - if (scanhex(&dabr.address)) { |
---|
1331 | | - if (!is_kernel_addr(dabr.address)) { |
---|
| 1459 | + dabr[i].address = 0; |
---|
| 1460 | + dabr[i].enabled = 0; |
---|
| 1461 | + if (scanhex(&dabr[i].address)) { |
---|
| 1462 | + if (!is_kernel_addr(dabr[i].address)) { |
---|
1332 | 1463 | printf(badaddr); |
---|
1333 | 1464 | break; |
---|
1334 | 1465 | } |
---|
1335 | | - dabr.address &= ~HW_BRK_TYPE_DABR; |
---|
1336 | | - dabr.enabled = mode | BP_DABR; |
---|
| 1466 | + dabr[i].address &= ~HW_BRK_TYPE_DABR; |
---|
| 1467 | + dabr[i].enabled = mode | BP_DABR; |
---|
1337 | 1468 | } |
---|
1338 | 1469 | |
---|
1339 | 1470 | force_enable_xmon(); |
---|
1340 | 1471 | break; |
---|
| 1472 | + } |
---|
1341 | 1473 | |
---|
1342 | 1474 | case 'i': /* bi - hardware instr breakpoint */ |
---|
| 1475 | + if (xmon_is_ro) { |
---|
| 1476 | + printf(xmon_ro_msg); |
---|
| 1477 | + break; |
---|
| 1478 | + } |
---|
1343 | 1479 | if (!cpu_has_feature(CPU_FTR_ARCH_207S)) { |
---|
1344 | 1480 | printf("Hardware instruction breakpoint " |
---|
1345 | 1481 | "not supported on this cpu\n"); |
---|
.. | .. |
---|
1360 | 1496 | force_enable_xmon(); |
---|
1361 | 1497 | } |
---|
1362 | 1498 | break; |
---|
1363 | | -#endif |
---|
1364 | 1499 | |
---|
1365 | 1500 | case 'c': |
---|
1366 | 1501 | if (!scanhex(&a)) { |
---|
.. | .. |
---|
1368 | 1503 | for (i = 0; i < NBPTS; ++i) |
---|
1369 | 1504 | bpts[i].enabled = 0; |
---|
1370 | 1505 | iabr = NULL; |
---|
1371 | | - dabr.enabled = 0; |
---|
| 1506 | + for (i = 0; i < nr_wp_slots(); i++) |
---|
| 1507 | + dabr[i].enabled = 0; |
---|
| 1508 | + |
---|
1372 | 1509 | printf("All breakpoints cleared\n"); |
---|
1373 | 1510 | break; |
---|
1374 | 1511 | } |
---|
.. | .. |
---|
1398 | 1535 | break; |
---|
1399 | 1536 | } |
---|
1400 | 1537 | termch = cmd; |
---|
1401 | | - if (!scanhex(&a)) { |
---|
| 1538 | + |
---|
| 1539 | + if (xmon_is_ro || !scanhex(&a)) { |
---|
1402 | 1540 | /* print all breakpoints */ |
---|
1403 | 1541 | printf(" type address\n"); |
---|
1404 | | - if (dabr.enabled) { |
---|
1405 | | - printf(" data "REG" [", dabr.address); |
---|
1406 | | - if (dabr.enabled & 1) |
---|
1407 | | - printf("r"); |
---|
1408 | | - if (dabr.enabled & 2) |
---|
1409 | | - printf("w"); |
---|
1410 | | - printf("]\n"); |
---|
1411 | | - } |
---|
| 1542 | + print_data_bpts(); |
---|
1412 | 1543 | for (bp = bpts; bp < &bpts[NBPTS]; ++bp) { |
---|
1413 | 1544 | if (!bp->enabled) |
---|
1414 | 1545 | continue; |
---|
.. | .. |
---|
1470 | 1601 | case 0x1300: ret = "(Instruction Breakpoint)"; break; |
---|
1471 | 1602 | case 0x1500: ret = "(Denormalisation)"; break; |
---|
1472 | 1603 | case 0x1700: ret = "(Altivec Assist)"; break; |
---|
| 1604 | + case 0x3000: ret = "(System Call Vectored)"; break; |
---|
1473 | 1605 | default: ret = ""; |
---|
1474 | 1606 | } |
---|
1475 | 1607 | return ret; |
---|
.. | .. |
---|
1706 | 1838 | #endif |
---|
1707 | 1839 | printf("pc = "); |
---|
1708 | 1840 | xmon_print_symbol(fp->nip, " ", "\n"); |
---|
1709 | | - if (TRAP(fp) != 0xc00 && cpu_has_feature(CPU_FTR_CFAR)) { |
---|
| 1841 | + if (!trap_is_syscall(fp) && cpu_has_feature(CPU_FTR_CFAR)) { |
---|
1710 | 1842 | printf("cfar= "); |
---|
1711 | 1843 | xmon_print_symbol(fp->orig_gpr3, " ", "\n"); |
---|
1712 | 1844 | } |
---|
.. | .. |
---|
1738 | 1870 | catch_memory_errors = 1; |
---|
1739 | 1871 | sync(); |
---|
1740 | 1872 | |
---|
1741 | | - if (cmd != 'i') { |
---|
| 1873 | + if (cmd != 'i' || IS_ENABLED(CONFIG_PPC_BOOK3S_64)) { |
---|
1742 | 1874 | for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES) |
---|
1743 | 1875 | cflush((void *) adrs); |
---|
1744 | 1876 | } else { |
---|
.. | .. |
---|
1779 | 1911 | static void |
---|
1780 | 1912 | write_spr(int n, unsigned long val) |
---|
1781 | 1913 | { |
---|
| 1914 | + if (xmon_is_ro) { |
---|
| 1915 | + printf(xmon_ro_msg); |
---|
| 1916 | + return; |
---|
| 1917 | + } |
---|
| 1918 | + |
---|
1782 | 1919 | if (setjmp(bus_error_jmp) == 0) { |
---|
1783 | 1920 | catch_spr_faults = 1; |
---|
1784 | 1921 | sync(); |
---|
.. | .. |
---|
1863 | 2000 | |
---|
1864 | 2001 | printf("hfscr = %.16lx dhdes = %.16lx rpr = %.16lx\n", |
---|
1865 | 2002 | mfspr(SPRN_HFSCR), mfspr(SPRN_DHDES), mfspr(SPRN_RPR)); |
---|
1866 | | - printf("dawr = %.16lx dawrx = %.16lx ciabr = %.16lx\n", |
---|
1867 | | - mfspr(SPRN_DAWR), mfspr(SPRN_DAWRX), mfspr(SPRN_CIABR)); |
---|
| 2003 | + printf("dawr0 = %.16lx dawrx0 = %.16lx\n", |
---|
| 2004 | + mfspr(SPRN_DAWR0), mfspr(SPRN_DAWRX0)); |
---|
| 2005 | + if (nr_wp_slots() > 1) { |
---|
| 2006 | + printf("dawr1 = %.16lx dawrx1 = %.16lx\n", |
---|
| 2007 | + mfspr(SPRN_DAWR1), mfspr(SPRN_DAWRX1)); |
---|
| 2008 | + } |
---|
| 2009 | + printf("ciabr = %.16lx\n", mfspr(SPRN_CIABR)); |
---|
1868 | 2010 | #endif |
---|
1869 | 2011 | } |
---|
1870 | 2012 | |
---|
.. | .. |
---|
1886 | 2028 | |
---|
1887 | 2029 | printf("ptcr = %.16lx asdr = %.16lx\n", |
---|
1888 | 2030 | mfspr(SPRN_PTCR), mfspr(SPRN_ASDR)); |
---|
| 2031 | +#endif |
---|
| 2032 | +} |
---|
| 2033 | + |
---|
| 2034 | +static void dump_310_sprs(void) |
---|
| 2035 | +{ |
---|
| 2036 | +#ifdef CONFIG_PPC64 |
---|
| 2037 | + if (!cpu_has_feature(CPU_FTR_ARCH_31)) |
---|
| 2038 | + return; |
---|
| 2039 | + |
---|
| 2040 | + printf("mmcr3 = %.16lx, sier2 = %.16lx, sier3 = %.16lx\n", |
---|
| 2041 | + mfspr(SPRN_MMCR3), mfspr(SPRN_SIER2), mfspr(SPRN_SIER3)); |
---|
| 2042 | + |
---|
1889 | 2043 | #endif |
---|
1890 | 2044 | } |
---|
1891 | 2045 | |
---|
.. | .. |
---|
1943 | 2097 | dump_206_sprs(); |
---|
1944 | 2098 | dump_207_sprs(); |
---|
1945 | 2099 | dump_300_sprs(); |
---|
| 2100 | + dump_310_sprs(); |
---|
1946 | 2101 | |
---|
1947 | 2102 | return; |
---|
1948 | 2103 | } |
---|
.. | .. |
---|
2017 | 2172 | char *p, *q; |
---|
2018 | 2173 | |
---|
2019 | 2174 | n = 0; |
---|
| 2175 | + |
---|
| 2176 | + if (xmon_is_ro) { |
---|
| 2177 | + printf(xmon_ro_msg); |
---|
| 2178 | + return n; |
---|
| 2179 | + } |
---|
| 2180 | + |
---|
2020 | 2181 | if (setjmp(bus_error_jmp) == 0) { |
---|
2021 | 2182 | catch_memory_errors = 1; |
---|
2022 | 2183 | sync(); |
---|
.. | .. |
---|
2044 | 2205 | n = size; |
---|
2045 | 2206 | } else { |
---|
2046 | 2207 | printf("*** Error writing address "REG"\n", adrs + n); |
---|
| 2208 | + } |
---|
| 2209 | + catch_memory_errors = 0; |
---|
| 2210 | + return n; |
---|
| 2211 | +} |
---|
| 2212 | + |
---|
| 2213 | +static int |
---|
| 2214 | +mread_instr(unsigned long adrs, struct ppc_inst *instr) |
---|
| 2215 | +{ |
---|
| 2216 | + volatile int n; |
---|
| 2217 | + |
---|
| 2218 | + n = 0; |
---|
| 2219 | + if (setjmp(bus_error_jmp) == 0) { |
---|
| 2220 | + catch_memory_errors = 1; |
---|
| 2221 | + sync(); |
---|
| 2222 | + *instr = ppc_inst_read((struct ppc_inst *)adrs); |
---|
| 2223 | + sync(); |
---|
| 2224 | + /* wait a little while to see if we get a machine check */ |
---|
| 2225 | + __delay(200); |
---|
| 2226 | + n = ppc_inst_len(*instr); |
---|
2047 | 2227 | } |
---|
2048 | 2228 | catch_memory_errors = 0; |
---|
2049 | 2229 | return n; |
---|
.. | .. |
---|
2381 | 2561 | DUMP(p, cpu_start, "%#-*x"); |
---|
2382 | 2562 | DUMP(p, kexec_state, "%#-*x"); |
---|
2383 | 2563 | #ifdef CONFIG_PPC_BOOK3S_64 |
---|
2384 | | - for (i = 0; i < SLB_NUM_BOLTED; i++) { |
---|
2385 | | - u64 esid, vsid; |
---|
| 2564 | + if (!early_radix_enabled()) { |
---|
| 2565 | + for (i = 0; i < SLB_NUM_BOLTED; i++) { |
---|
| 2566 | + u64 esid, vsid; |
---|
2386 | 2567 | |
---|
2387 | | - if (!p->slb_shadow_ptr) |
---|
2388 | | - continue; |
---|
| 2568 | + if (!p->slb_shadow_ptr) |
---|
| 2569 | + continue; |
---|
2389 | 2570 | |
---|
2390 | | - esid = be64_to_cpu(p->slb_shadow_ptr->save_area[i].esid); |
---|
2391 | | - vsid = be64_to_cpu(p->slb_shadow_ptr->save_area[i].vsid); |
---|
| 2571 | + esid = be64_to_cpu(p->slb_shadow_ptr->save_area[i].esid); |
---|
| 2572 | + vsid = be64_to_cpu(p->slb_shadow_ptr->save_area[i].vsid); |
---|
2392 | 2573 | |
---|
2393 | | - if (esid || vsid) { |
---|
2394 | | - printf(" %-*s[%d] = 0x%016llx 0x%016llx\n", |
---|
2395 | | - 22, "slb_shadow", i, esid, vsid); |
---|
| 2574 | + if (esid || vsid) { |
---|
| 2575 | + printf(" %-*s[%d] = 0x%016llx 0x%016llx\n", |
---|
| 2576 | + 22, "slb_shadow", i, esid, vsid); |
---|
| 2577 | + } |
---|
| 2578 | + } |
---|
| 2579 | + DUMP(p, vmalloc_sllp, "%#-*x"); |
---|
| 2580 | + DUMP(p, stab_rr, "%#-*x"); |
---|
| 2581 | + DUMP(p, slb_used_bitmap, "%#-*x"); |
---|
| 2582 | + DUMP(p, slb_kern_bitmap, "%#-*x"); |
---|
| 2583 | + |
---|
| 2584 | + if (!early_cpu_has_feature(CPU_FTR_ARCH_300)) { |
---|
| 2585 | + DUMP(p, slb_cache_ptr, "%#-*x"); |
---|
| 2586 | + for (i = 0; i < SLB_CACHE_ENTRIES; i++) |
---|
| 2587 | + printf(" %-*s[%d] = 0x%016x\n", |
---|
| 2588 | + 22, "slb_cache", i, p->slb_cache[i]); |
---|
2396 | 2589 | } |
---|
2397 | 2590 | } |
---|
2398 | | - DUMP(p, vmalloc_sllp, "%#-*x"); |
---|
2399 | | - DUMP(p, slb_cache_ptr, "%#-*x"); |
---|
2400 | | - for (i = 0; i < SLB_CACHE_ENTRIES; i++) |
---|
2401 | | - printf(" %-*s[%d] = 0x%016x\n", |
---|
2402 | | - 22, "slb_cache", i, p->slb_cache[i]); |
---|
2403 | 2591 | |
---|
2404 | 2592 | DUMP(p, rfi_flush_fallback_area, "%-*px"); |
---|
2405 | 2593 | #endif |
---|
.. | .. |
---|
2415 | 2603 | DUMP(p, __current, "%-*px"); |
---|
2416 | 2604 | DUMP(p, kstack, "%#-*llx"); |
---|
2417 | 2605 | printf(" %-*s = 0x%016llx\n", 25, "kstack_base", p->kstack & ~(THREAD_SIZE - 1)); |
---|
2418 | | - DUMP(p, stab_rr, "%#-*llx"); |
---|
| 2606 | +#ifdef CONFIG_STACKPROTECTOR |
---|
| 2607 | + DUMP(p, canary, "%#-*lx"); |
---|
| 2608 | +#endif |
---|
2419 | 2609 | DUMP(p, saved_r1, "%#-*llx"); |
---|
| 2610 | +#ifdef CONFIG_PPC_BOOK3E |
---|
2420 | 2611 | DUMP(p, trap_save, "%#-*x"); |
---|
| 2612 | +#endif |
---|
2421 | 2613 | DUMP(p, irq_soft_mask, "%#-*x"); |
---|
2422 | 2614 | DUMP(p, irq_happened, "%#-*x"); |
---|
2423 | | - DUMP(p, io_sync, "%#-*x"); |
---|
| 2615 | +#ifdef CONFIG_MMIOWB |
---|
| 2616 | + DUMP(p, mmiowb_state.nesting_count, "%#-*x"); |
---|
| 2617 | + DUMP(p, mmiowb_state.mmiowb_pending, "%#-*x"); |
---|
| 2618 | +#endif |
---|
2424 | 2619 | DUMP(p, irq_work_pending, "%#-*x"); |
---|
2425 | | - DUMP(p, nap_state_lost, "%#-*x"); |
---|
2426 | 2620 | DUMP(p, sprg_vdso, "%#-*llx"); |
---|
2427 | 2621 | |
---|
2428 | 2622 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
---|
.. | .. |
---|
2430 | 2624 | #endif |
---|
2431 | 2625 | |
---|
2432 | 2626 | #ifdef CONFIG_PPC_POWERNV |
---|
2433 | | - DUMP(p, core_idle_state_ptr, "%-*px"); |
---|
2434 | | - DUMP(p, thread_idle_state, "%#-*x"); |
---|
2435 | | - DUMP(p, thread_mask, "%#-*x"); |
---|
2436 | | - DUMP(p, subcore_sibling_mask, "%#-*x"); |
---|
2437 | | - DUMP(p, requested_psscr, "%#-*llx"); |
---|
2438 | | - DUMP(p, stop_sprs.pid, "%#-*llx"); |
---|
2439 | | - DUMP(p, stop_sprs.ldbar, "%#-*llx"); |
---|
2440 | | - DUMP(p, stop_sprs.fscr, "%#-*llx"); |
---|
2441 | | - DUMP(p, stop_sprs.hfscr, "%#-*llx"); |
---|
2442 | | - DUMP(p, stop_sprs.mmcr1, "%#-*llx"); |
---|
2443 | | - DUMP(p, stop_sprs.mmcr2, "%#-*llx"); |
---|
2444 | | - DUMP(p, stop_sprs.mmcra, "%#-*llx"); |
---|
2445 | | - DUMP(p, dont_stop.counter, "%#-*x"); |
---|
| 2627 | + DUMP(p, idle_state, "%#-*lx"); |
---|
| 2628 | + if (!early_cpu_has_feature(CPU_FTR_ARCH_300)) { |
---|
| 2629 | + DUMP(p, thread_idle_state, "%#-*x"); |
---|
| 2630 | + DUMP(p, subcore_sibling_mask, "%#-*x"); |
---|
| 2631 | + } else { |
---|
| 2632 | +#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE |
---|
| 2633 | + DUMP(p, requested_psscr, "%#-*llx"); |
---|
| 2634 | + DUMP(p, dont_stop.counter, "%#-*x"); |
---|
| 2635 | +#endif |
---|
| 2636 | + } |
---|
2446 | 2637 | #endif |
---|
2447 | 2638 | |
---|
2448 | 2639 | DUMP(p, accounting.utime, "%#-*lx"); |
---|
2449 | 2640 | DUMP(p, accounting.stime, "%#-*lx"); |
---|
| 2641 | +#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME |
---|
2450 | 2642 | DUMP(p, accounting.utime_scaled, "%#-*lx"); |
---|
| 2643 | +#endif |
---|
2451 | 2644 | DUMP(p, accounting.starttime, "%#-*lx"); |
---|
2452 | 2645 | DUMP(p, accounting.starttime_user, "%#-*lx"); |
---|
| 2646 | +#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME |
---|
2453 | 2647 | DUMP(p, accounting.startspurr, "%#-*lx"); |
---|
2454 | 2648 | DUMP(p, accounting.utime_sspurr, "%#-*lx"); |
---|
| 2649 | +#endif |
---|
2455 | 2650 | DUMP(p, accounting.steal_time, "%#-*lx"); |
---|
2456 | 2651 | #undef DUMP |
---|
2457 | 2652 | |
---|
.. | .. |
---|
2534 | 2729 | dump_one_xive(cpu); |
---|
2535 | 2730 | } |
---|
2536 | 2731 | |
---|
2537 | | -static void dump_one_xive_irq(u32 num) |
---|
| 2732 | +static void dump_one_xive_irq(u32 num, struct irq_data *d) |
---|
2538 | 2733 | { |
---|
2539 | | - s64 rc; |
---|
2540 | | - __be64 vp; |
---|
2541 | | - u8 prio; |
---|
2542 | | - __be32 lirq; |
---|
| 2734 | + xmon_xive_get_irq_config(num, d); |
---|
| 2735 | +} |
---|
2543 | 2736 | |
---|
2544 | | - rc = opal_xive_get_irq_config(num, &vp, &prio, &lirq); |
---|
2545 | | - xmon_printf("IRQ 0x%x config: vp=0x%llx prio=%d lirq=0x%x (rc=%lld)\n", |
---|
2546 | | - num, be64_to_cpu(vp), prio, be32_to_cpu(lirq), rc); |
---|
| 2737 | +static void dump_all_xive_irq(void) |
---|
| 2738 | +{ |
---|
| 2739 | + unsigned int i; |
---|
| 2740 | + struct irq_desc *desc; |
---|
| 2741 | + |
---|
| 2742 | + for_each_irq_desc(i, desc) { |
---|
| 2743 | + struct irq_data *d = irq_desc_get_irq_data(desc); |
---|
| 2744 | + unsigned int hwirq; |
---|
| 2745 | + |
---|
| 2746 | + if (!d) |
---|
| 2747 | + continue; |
---|
| 2748 | + |
---|
| 2749 | + hwirq = (unsigned int)irqd_to_hwirq(d); |
---|
| 2750 | + /* IPIs are special (HW number 0) */ |
---|
| 2751 | + if (hwirq) |
---|
| 2752 | + dump_one_xive_irq(hwirq, d); |
---|
| 2753 | + } |
---|
2547 | 2754 | } |
---|
2548 | 2755 | |
---|
2549 | 2756 | static void dump_xives(void) |
---|
.. | .. |
---|
2562 | 2769 | return; |
---|
2563 | 2770 | } else if (c == 'i') { |
---|
2564 | 2771 | if (scanhex(&num)) |
---|
2565 | | - dump_one_xive_irq(num); |
---|
| 2772 | + dump_one_xive_irq(num, NULL); |
---|
| 2773 | + else |
---|
| 2774 | + dump_all_xive_irq(); |
---|
2566 | 2775 | return; |
---|
2567 | 2776 | } |
---|
2568 | 2777 | |
---|
.. | .. |
---|
2603 | 2812 | |
---|
2604 | 2813 | printf("%0*llx", size * 2, val); |
---|
2605 | 2814 | } |
---|
2606 | | - printf("\n"); |
---|
| 2815 | + printf(" |"); |
---|
| 2816 | + for (j = 0; j < 16; ++j) { |
---|
| 2817 | + val = temp[j]; |
---|
| 2818 | + putchar(' ' <= val && val <= '~' ? val : '.'); |
---|
| 2819 | + } |
---|
| 2820 | + printf("|\n"); |
---|
2607 | 2821 | } |
---|
2608 | 2822 | } |
---|
2609 | 2823 | |
---|
.. | .. |
---|
2647 | 2861 | scanhex(&nidump); |
---|
2648 | 2862 | if (nidump == 0) |
---|
2649 | 2863 | nidump = 16; |
---|
2650 | | - else if (nidump > MAX_DUMP) |
---|
2651 | | - nidump = MAX_DUMP; |
---|
| 2864 | + else if (nidump > MAX_IDUMP) |
---|
| 2865 | + nidump = MAX_IDUMP; |
---|
2652 | 2866 | adrs += ppc_inst_dump(adrs, nidump, 1); |
---|
2653 | 2867 | last_cmd = "di\n"; |
---|
2654 | 2868 | } else if (c == 'l') { |
---|
.. | .. |
---|
2741 | 2955 | { |
---|
2742 | 2956 | int nr, dotted; |
---|
2743 | 2957 | unsigned long first_adr; |
---|
2744 | | - unsigned int inst, last_inst = 0; |
---|
2745 | | - unsigned char val[4]; |
---|
| 2958 | + struct ppc_inst inst, last_inst = ppc_inst(0); |
---|
2746 | 2959 | |
---|
2747 | 2960 | dotted = 0; |
---|
2748 | | - for (first_adr = adr; count > 0; --count, adr += 4) { |
---|
2749 | | - nr = mread(adr, val, 4); |
---|
| 2961 | + for (first_adr = adr; count > 0; --count, adr += ppc_inst_len(inst)) { |
---|
| 2962 | + nr = mread_instr(adr, &inst); |
---|
2750 | 2963 | if (nr == 0) { |
---|
2751 | 2964 | if (praddr) { |
---|
2752 | 2965 | const char *x = fault_chars[fault_type]; |
---|
.. | .. |
---|
2754 | 2967 | } |
---|
2755 | 2968 | break; |
---|
2756 | 2969 | } |
---|
2757 | | - inst = GETWORD(val); |
---|
2758 | | - if (adr > first_adr && inst == last_inst) { |
---|
| 2970 | + if (adr > first_adr && ppc_inst_equal(inst, last_inst)) { |
---|
2759 | 2971 | if (!dotted) { |
---|
2760 | 2972 | printf(" ...\n"); |
---|
2761 | 2973 | dotted = 1; |
---|
.. | .. |
---|
2765 | 2977 | dotted = 0; |
---|
2766 | 2978 | last_inst = inst; |
---|
2767 | 2979 | if (praddr) |
---|
2768 | | - printf(REG" %.8x", adr, inst); |
---|
| 2980 | + printf(REG" %s", adr, ppc_inst_as_str(inst)); |
---|
2769 | 2981 | printf("\t"); |
---|
2770 | | - dump_func(inst, adr); |
---|
| 2982 | + if (!ppc_inst_prefixed(inst)) |
---|
| 2983 | + dump_func(ppc_inst_val(inst), adr); |
---|
| 2984 | + else |
---|
| 2985 | + dump_func(ppc_inst_as_u64(inst), adr); |
---|
2771 | 2986 | printf("\n"); |
---|
2772 | 2987 | } |
---|
2773 | 2988 | return adr - first_adr; |
---|
.. | .. |
---|
2785 | 3000 | xmon_print_symbol(addr, "\t# ", ""); |
---|
2786 | 3001 | } |
---|
2787 | 3002 | |
---|
2788 | | -void |
---|
| 3003 | +static void |
---|
2789 | 3004 | dump_log_buf(void) |
---|
2790 | 3005 | { |
---|
2791 | 3006 | struct kmsg_dumper dumper = { .active = 1 }; |
---|
.. | .. |
---|
2874 | 3089 | scanhex((void *)&mcount); |
---|
2875 | 3090 | switch( cmd ){ |
---|
2876 | 3091 | case 'm': |
---|
| 3092 | + if (xmon_is_ro) { |
---|
| 3093 | + printf(xmon_ro_msg); |
---|
| 3094 | + break; |
---|
| 3095 | + } |
---|
2877 | 3096 | memmove((void *)mdest, (void *)msrc, mcount); |
---|
2878 | 3097 | break; |
---|
2879 | 3098 | case 's': |
---|
| 3099 | + if (xmon_is_ro) { |
---|
| 3100 | + printf(xmon_ro_msg); |
---|
| 3101 | + break; |
---|
| 3102 | + } |
---|
2880 | 3103 | memset((void *)mdest, mval, mcount); |
---|
2881 | 3104 | break; |
---|
2882 | 3105 | case 'd': |
---|
.. | .. |
---|
2984 | 3207 | (tsk->exit_state & EXIT_DEAD) ? 'E' : |
---|
2985 | 3208 | (tsk->state & TASK_INTERRUPTIBLE) ? 'S' : '?'; |
---|
2986 | 3209 | |
---|
2987 | | - printf("%px %016lx %6d %6d %c %2d %s\n", tsk, |
---|
2988 | | - tsk->thread.ksp, |
---|
2989 | | - tsk->pid, tsk->parent->pid, |
---|
2990 | | - state, task_thread_info(tsk)->cpu, |
---|
| 3210 | + printf("%16px %16lx %16px %6d %6d %c %2d %s\n", tsk, |
---|
| 3211 | + tsk->thread.ksp, tsk->thread.regs, |
---|
| 3212 | + tsk->pid, rcu_dereference(tsk->parent)->pid, |
---|
| 3213 | + state, task_cpu(tsk), |
---|
2991 | 3214 | tsk->comm); |
---|
2992 | 3215 | } |
---|
2993 | 3216 | |
---|
2994 | 3217 | #ifdef CONFIG_PPC_BOOK3S_64 |
---|
2995 | | -void format_pte(void *ptep, unsigned long pte) |
---|
| 3218 | +static void format_pte(void *ptep, unsigned long pte) |
---|
2996 | 3219 | { |
---|
| 3220 | + pte_t entry = __pte(pte); |
---|
| 3221 | + |
---|
2997 | 3222 | printf("ptep @ 0x%016lx = 0x%016lx\n", (unsigned long)ptep, pte); |
---|
2998 | 3223 | printf("Maps physical address = 0x%016lx\n", pte & PTE_RPN_MASK); |
---|
2999 | 3224 | |
---|
3000 | 3225 | printf("Flags = %s%s%s%s%s\n", |
---|
3001 | | - (pte & _PAGE_ACCESSED) ? "Accessed " : "", |
---|
3002 | | - (pte & _PAGE_DIRTY) ? "Dirty " : "", |
---|
3003 | | - (pte & _PAGE_READ) ? "Read " : "", |
---|
3004 | | - (pte & _PAGE_WRITE) ? "Write " : "", |
---|
3005 | | - (pte & _PAGE_EXEC) ? "Exec " : ""); |
---|
| 3226 | + pte_young(entry) ? "Accessed " : "", |
---|
| 3227 | + pte_dirty(entry) ? "Dirty " : "", |
---|
| 3228 | + pte_read(entry) ? "Read " : "", |
---|
| 3229 | + pte_write(entry) ? "Write " : "", |
---|
| 3230 | + pte_exec(entry) ? "Exec " : ""); |
---|
3006 | 3231 | } |
---|
3007 | 3232 | |
---|
3008 | 3233 | static void show_pte(unsigned long addr) |
---|
.. | .. |
---|
3010 | 3235 | unsigned long tskv = 0; |
---|
3011 | 3236 | struct task_struct *tsk = NULL; |
---|
3012 | 3237 | struct mm_struct *mm; |
---|
3013 | | - pgd_t *pgdp, *pgdir; |
---|
| 3238 | + pgd_t *pgdp; |
---|
| 3239 | + p4d_t *p4dp; |
---|
3014 | 3240 | pud_t *pudp; |
---|
3015 | 3241 | pmd_t *pmdp; |
---|
3016 | 3242 | pte_t *ptep; |
---|
.. | .. |
---|
3034 | 3260 | catch_memory_errors = 1; |
---|
3035 | 3261 | sync(); |
---|
3036 | 3262 | |
---|
3037 | | - if (mm == &init_mm) { |
---|
| 3263 | + if (mm == &init_mm) |
---|
3038 | 3264 | pgdp = pgd_offset_k(addr); |
---|
3039 | | - pgdir = pgd_offset_k(0); |
---|
3040 | | - } else { |
---|
| 3265 | + else |
---|
3041 | 3266 | pgdp = pgd_offset(mm, addr); |
---|
3042 | | - pgdir = pgd_offset(mm, 0); |
---|
3043 | | - } |
---|
3044 | 3267 | |
---|
3045 | | - if (pgd_none(*pgdp)) { |
---|
3046 | | - printf("no linux page table for address\n"); |
---|
| 3268 | + p4dp = p4d_offset(pgdp, addr); |
---|
| 3269 | + |
---|
| 3270 | + if (p4d_none(*p4dp)) { |
---|
| 3271 | + printf("No valid P4D\n"); |
---|
3047 | 3272 | return; |
---|
3048 | 3273 | } |
---|
3049 | 3274 | |
---|
3050 | | - printf("pgd @ 0x%px\n", pgdir); |
---|
3051 | | - |
---|
3052 | | - if (pgd_huge(*pgdp)) { |
---|
3053 | | - format_pte(pgdp, pgd_val(*pgdp)); |
---|
| 3275 | + if (p4d_is_leaf(*p4dp)) { |
---|
| 3276 | + format_pte(p4dp, p4d_val(*p4dp)); |
---|
3054 | 3277 | return; |
---|
3055 | 3278 | } |
---|
3056 | | - printf("pgdp @ 0x%px = 0x%016lx\n", pgdp, pgd_val(*pgdp)); |
---|
3057 | 3279 | |
---|
3058 | | - pudp = pud_offset(pgdp, addr); |
---|
| 3280 | + printf("p4dp @ 0x%px = 0x%016lx\n", p4dp, p4d_val(*p4dp)); |
---|
| 3281 | + |
---|
| 3282 | + pudp = pud_offset(p4dp, addr); |
---|
3059 | 3283 | |
---|
3060 | 3284 | if (pud_none(*pudp)) { |
---|
3061 | 3285 | printf("No valid PUD\n"); |
---|
3062 | 3286 | return; |
---|
3063 | 3287 | } |
---|
3064 | 3288 | |
---|
3065 | | - if (pud_huge(*pudp)) { |
---|
| 3289 | + if (pud_is_leaf(*pudp)) { |
---|
3066 | 3290 | format_pte(pudp, pud_val(*pudp)); |
---|
3067 | 3291 | return; |
---|
3068 | 3292 | } |
---|
.. | .. |
---|
3076 | 3300 | return; |
---|
3077 | 3301 | } |
---|
3078 | 3302 | |
---|
3079 | | - if (pmd_huge(*pmdp)) { |
---|
| 3303 | + if (pmd_is_leaf(*pmdp)) { |
---|
3080 | 3304 | format_pte(pmdp, pmd_val(*pmdp)); |
---|
3081 | 3305 | return; |
---|
3082 | 3306 | } |
---|
.. | .. |
---|
3106 | 3330 | unsigned long tskv; |
---|
3107 | 3331 | struct task_struct *tsk = NULL; |
---|
3108 | 3332 | |
---|
3109 | | - printf(" task_struct ->thread.ksp PID PPID S P CMD\n"); |
---|
| 3333 | + printf(" task_struct ->thread.ksp ->thread.regs PID PPID S P CMD\n"); |
---|
3110 | 3334 | |
---|
3111 | 3335 | if (scanhex(&tskv)) |
---|
3112 | 3336 | tsk = (struct task_struct *)tskv; |
---|
.. | .. |
---|
3316 | 3540 | int c; |
---|
3317 | 3541 | |
---|
3318 | 3542 | c = skipbl(); |
---|
| 3543 | + if (c == '\n') { |
---|
| 3544 | + *s = 0; |
---|
| 3545 | + return; |
---|
| 3546 | + } |
---|
| 3547 | + |
---|
3319 | 3548 | do { |
---|
3320 | 3549 | if( size > 1 ){ |
---|
3321 | 3550 | *s++ = c; |
---|
.. | .. |
---|
3485 | 3714 | } |
---|
3486 | 3715 | #endif |
---|
3487 | 3716 | |
---|
3488 | | -#ifdef CONFIG_PPC_STD_MMU_32 |
---|
| 3717 | +#ifdef CONFIG_PPC_BOOK3S_32 |
---|
3489 | 3718 | void dump_segments(void) |
---|
3490 | 3719 | { |
---|
3491 | 3720 | int i; |
---|
.. | .. |
---|
3700 | 3929 | #ifdef CONFIG_MAGIC_SYSRQ |
---|
3701 | 3930 | static void sysrq_handle_xmon(int key) |
---|
3702 | 3931 | { |
---|
| 3932 | + if (xmon_is_locked_down()) { |
---|
| 3933 | + clear_all_bpt(); |
---|
| 3934 | + xmon_init(0); |
---|
| 3935 | + return; |
---|
| 3936 | + } |
---|
3703 | 3937 | /* ensure xmon is enabled */ |
---|
3704 | 3938 | xmon_init(1); |
---|
3705 | 3939 | debugger(get_irq_regs()); |
---|
.. | .. |
---|
3707 | 3941 | xmon_init(0); |
---|
3708 | 3942 | } |
---|
3709 | 3943 | |
---|
3710 | | -static struct sysrq_key_op sysrq_xmon_op = { |
---|
| 3944 | +static const struct sysrq_key_op sysrq_xmon_op = { |
---|
3711 | 3945 | .handler = sysrq_handle_xmon, |
---|
3712 | 3946 | .help_msg = "xmon(x)", |
---|
3713 | 3947 | .action_msg = "Entering xmon", |
---|
.. | .. |
---|
3721 | 3955 | device_initcall(setup_xmon_sysrq); |
---|
3722 | 3956 | #endif /* CONFIG_MAGIC_SYSRQ */ |
---|
3723 | 3957 | |
---|
3724 | | -#ifdef CONFIG_DEBUG_FS |
---|
3725 | 3958 | static void clear_all_bpt(void) |
---|
3726 | 3959 | { |
---|
3727 | 3960 | int i; |
---|
.. | .. |
---|
3735 | 3968 | bpts[i].enabled = 0; |
---|
3736 | 3969 | |
---|
3737 | 3970 | /* Clear any data or iabr breakpoints */ |
---|
3738 | | - if (iabr || dabr.enabled) { |
---|
3739 | | - iabr = NULL; |
---|
3740 | | - dabr.enabled = 0; |
---|
3741 | | - } |
---|
3742 | | - |
---|
3743 | | - printf("xmon: All breakpoints cleared\n"); |
---|
| 3971 | + iabr = NULL; |
---|
| 3972 | + for (i = 0; i < nr_wp_slots(); i++) |
---|
| 3973 | + dabr[i].enabled = 0; |
---|
3744 | 3974 | } |
---|
3745 | 3975 | |
---|
| 3976 | +#ifdef CONFIG_DEBUG_FS |
---|
3746 | 3977 | static int xmon_dbgfs_set(void *data, u64 val) |
---|
3747 | 3978 | { |
---|
3748 | 3979 | xmon_on = !!val; |
---|
3749 | 3980 | xmon_init(xmon_on); |
---|
3750 | 3981 | |
---|
3751 | 3982 | /* make sure all breakpoints removed when disabling */ |
---|
3752 | | - if (!xmon_on) |
---|
| 3983 | + if (!xmon_on) { |
---|
3753 | 3984 | clear_all_bpt(); |
---|
| 3985 | + get_output_lock(); |
---|
| 3986 | + printf("xmon: All breakpoints cleared\n"); |
---|
| 3987 | + release_output_lock(); |
---|
| 3988 | + } |
---|
| 3989 | + |
---|
3754 | 3990 | return 0; |
---|
3755 | 3991 | } |
---|
3756 | 3992 | |
---|
.. | .. |
---|
3776 | 4012 | |
---|
3777 | 4013 | static int __init early_parse_xmon(char *p) |
---|
3778 | 4014 | { |
---|
3779 | | - if (!p || strncmp(p, "early", 5) == 0) { |
---|
| 4015 | + if (xmon_is_locked_down()) { |
---|
| 4016 | + xmon_init(0); |
---|
| 4017 | + xmon_early = 0; |
---|
| 4018 | + xmon_on = 0; |
---|
| 4019 | + } else if (!p || strncmp(p, "early", 5) == 0) { |
---|
3780 | 4020 | /* just "xmon" is equivalent to "xmon=early" */ |
---|
3781 | 4021 | xmon_init(1); |
---|
3782 | 4022 | xmon_early = 1; |
---|
.. | .. |
---|
3784 | 4024 | } else if (strncmp(p, "on", 2) == 0) { |
---|
3785 | 4025 | xmon_init(1); |
---|
3786 | 4026 | xmon_on = 1; |
---|
| 4027 | + } else if (strncmp(p, "rw", 2) == 0) { |
---|
| 4028 | + xmon_init(1); |
---|
| 4029 | + xmon_on = 1; |
---|
| 4030 | + xmon_is_ro = false; |
---|
| 4031 | + } else if (strncmp(p, "ro", 2) == 0) { |
---|
| 4032 | + xmon_init(1); |
---|
| 4033 | + xmon_on = 1; |
---|
| 4034 | + xmon_is_ro = true; |
---|
3787 | 4035 | } else if (strncmp(p, "off", 3) == 0) |
---|
3788 | 4036 | xmon_on = 0; |
---|
3789 | 4037 | else |
---|
.. | .. |
---|
4031 | 4279 | subcmd = inchar(); |
---|
4032 | 4280 | if (isxdigit(subcmd) || subcmd == '\n') |
---|
4033 | 4281 | termch = subcmd; |
---|
| 4282 | + fallthrough; |
---|
4034 | 4283 | case 'f': |
---|
4035 | 4284 | scanhex(&num); |
---|
4036 | 4285 | if (num >= XMON_NUM_SPUS || !spu_info[num].spu) { |
---|