Based on original work by Karl Volz <karl.volz(a)oracle.com>
Signed-off-by: Dave Kleikamp <dave.kleikamp(a)oracle.com>
---
Makefile | 9 +-
configure.c | 23 +
defs.h | 138 ++++++-
diskdump.c | 36 ++-
lkcd_vmdump_v2_v3.h | 2 +-
sparc64.c | 1253 +++++++++++++++++++++++++++++++++++++++++++++++++++
symbols.c | 10 +
task.c | 11 +-
8 files changed, 1475 insertions(+), 7 deletions(-)
create mode 100644 sparc64.c
diff --git a/Makefile b/Makefile
index 7f5b0f1..d5d9497 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@
PROGRAM=crash
#
-# Supported targets: X86 ALPHA PPC IA64 PPC64
+# Supported targets: X86 ALPHA PPC IA64 PPC64 SPARC64
# TARGET and GDB_CONF_FLAGS will be configured automatically by configure
#
TARGET=
@@ -62,7 +62,7 @@ VMWARE_HFILES=vmware_vmss.h
CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \
kernel.c test.c gdb_interface.c configure.c net.c dev.c \
alpha.c x86.c ppc.c ia64.c s390.c s390x.c s390dbf.c ppc64.c x86_64.c \
- arm.c arm64.c mips.c \
+ arm.c arm64.c mips.c sparc64.c \
extensions.c remote.c va_server.c va_server_v1.c symbols.c cmdline.c \
lkcd_common.c lkcd_v1.c lkcd_v2_v3.c lkcd_v5.c lkcd_v7.c lkcd_v8.c\
lkcd_fix_mem.c s390_dump.c lkcd_x86_trace.c \
@@ -81,7 +81,7 @@ SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \
OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \
build_data.o kernel.o test.o gdb_interface.o net.o dev.o \
alpha.o x86.o ppc.o ia64.o s390.o s390x.o s390dbf.o ppc64.o x86_64.o \
- arm.o arm64.o mips.o \
+ arm.o arm64.o mips.o sparc64.o \
extensions.o remote.o va_server.o va_server_v1.o symbols.o cmdline.o \
lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o \
lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o makedumpfile.o xendump.o \
@@ -422,6 +422,9 @@ arm64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} arm64.c
mips.o: ${GENERIC_HFILES} ${REDHAT_HFILES} mips.c
${CC} -c ${CRASH_CFLAGS} mips.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+sparc64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} sparc64.c
+ ${CC} -c ${CRASH_CFLAGS} sparc64.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
s390.o: ${GENERIC_HFILES} ${IBM_HFILES} s390.c
${CC} -c ${CRASH_CFLAGS} s390.c ${WARNING_OPTIONS} ${WARNING_ERROR}
diff --git a/configure.c b/configure.c
index a5167db..a2c033f 100644
--- a/configure.c
+++ b/configure.c
@@ -104,6 +104,7 @@ void add_extra_lib(char *);
#undef X86_64
#undef ARM
#undef ARM64
+#undef SPARC64
#define UNKNOWN 0
#define X86 1
@@ -117,6 +118,7 @@ void add_extra_lib(char *);
#define ARM 9
#define ARM64 10
#define MIPS 11
+#define SPARC64 12
#define TARGET_X86 "TARGET=X86"
#define TARGET_ALPHA "TARGET=ALPHA"
@@ -129,6 +131,7 @@ void add_extra_lib(char *);
#define TARGET_ARM "TARGET=ARM"
#define TARGET_ARM64 "TARGET=ARM64"
#define TARGET_MIPS "TARGET=MIPS"
+#define TARGET_SPARC64 "TARGET=SPARC64"
#define TARGET_CFLAGS_X86 "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64"
#define TARGET_CFLAGS_ALPHA "TARGET_CFLAGS="
@@ -149,6 +152,7 @@ void add_extra_lib(char *);
#define TARGET_CFLAGS_MIPS "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64"
#define TARGET_CFLAGS_MIPS_ON_X86 "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64"
#define TARGET_CFLAGS_MIPS_ON_X86_64 "TARGET_CFLAGS=-m32
-D_FILE_OFFSET_BITS=64"
+#define TARGET_CFLAGS_SPARC64 "TARGET_CFLAGS="
#define GDB_TARGET_DEFAULT "GDB_CONF_FLAGS="
#define GDB_TARGET_ARM_ON_X86 "GDB_CONF_FLAGS=--target=arm-elf-linux"
@@ -378,6 +382,9 @@ get_current_configuration(struct supported_gdb_version *sp)
#ifdef __mips__
target_data.target = MIPS;
#endif
+#ifdef __sparc_v9__
+ target_data.target = SPARC64;
+#endif
set_initial_target(sp);
@@ -510,6 +517,10 @@ get_current_configuration(struct supported_gdb_version *sp)
if ((target_data.target == PPC) &&
(target_data.initial_gdb_target != PPC))
arch_mismatch(sp);
+
+ if ((target_data.target == SPARC64) &&
+ (target_data.initial_gdb_target != SPARC64))
+ arch_mismatch(sp);
}
if ((fp = fopen("Makefile", "r")) == NULL) {
@@ -620,6 +631,9 @@ show_configuration(void)
case MIPS:
printf("TARGET: MIPS\n");
break;
+ case SPARC64:
+ printf("TARGET: SPARC64\n");
+ break;
}
if (strlen(target_data.program)) {
@@ -729,6 +743,10 @@ build_configure(struct supported_gdb_version *sp)
} else
target_CFLAGS = TARGET_CFLAGS_MIPS;
break;
+ case SPARC64:
+ target = TARGET_SPARC64;
+ target_CFLAGS = TARGET_CFLAGS_SPARC64;
+ break;
}
ldflags = get_extra_flags("LDFLAGS.extra", NULL);
@@ -1554,6 +1572,8 @@ set_initial_target(struct supported_gdb_version *sp)
target_data.initial_gdb_target = ARM;
else if (strncmp(buf, "MIPS", strlen("MIPS")) == 0)
target_data.initial_gdb_target = MIPS;
+ else if (strncmp(buf, "SPARC64", strlen("SPARC64")) == 0)
+ target_data.initial_gdb_target = SPARC64;
}
char *
@@ -1572,6 +1592,7 @@ target_to_name(int target)
case ARM: return("ARM");
case ARM64: return("ARM64");
case MIPS: return("MIPS");
+ case SPARC64: return("SPARC64");
}
return "UNKNOWN";
@@ -1630,6 +1651,8 @@ name_to_target(char *name)
return MIPS;
else if (strncmp(name, "MIPS", strlen("MIPS")) == 0)
return MIPS;
+ else if (strncmp(name, "sparc64", strlen("sparc64")) == 0)
+ return SPARC64;
return UNKNOWN;
}
diff --git a/defs.h b/defs.h
index d9d4559..a1746cc 100644
--- a/defs.h
+++ b/defs.h
@@ -71,7 +71,7 @@
#if !defined(X86) && !defined(X86_64) && !defined(ALPHA) &&
!defined(PPC) && \
!defined(IA64) && !defined(PPC64) && !defined(S390) &&
!defined(S390X) && \
- !defined(ARM) && !defined(ARM64) && !defined(MIPS)
+ !defined(ARM) && !defined(ARM64) && !defined(MIPS) &&
!defined(SPARC64)
#ifdef __alpha__
#define ALPHA
#endif
@@ -106,6 +106,9 @@
#ifdef __mipsel__
#define MIPS
#endif
+#ifdef __sparc_v9__
+#define SPARC64
+#endif
#endif
#ifdef X86
@@ -141,6 +144,14 @@
#ifdef MIPS
#define NR_CPUS (32)
#endif
+#ifdef SPARC64
+#define NR_CPUS (4096)
+#endif
+
+/* Some architectures require memory accesses to be aligned. */
+#if defined(SPARC64)
+#define NEED_ALIGNED_MEM_ACCESS
+#endif
#define BUFSIZE (1500)
#define NULLCHAR ('\0')
@@ -3857,6 +3868,110 @@ struct efi_memory_desc_t {
#endif /* S390X */
+#ifdef SPARC64
+#define _64BIT_
+#define MACHINE_TYPE "SPARC64"
+
+#define PTOV(X) \
+ ((unsigned long)(X) + machdep->machspec->page_offset)
+#define VTOP(X) \
+ ((unsigned long)(X) - machdep->machspec->page_offset)
+
+#define PAGE_OFFSET (machdep->machspec->page_offset)
+
+extern int sparc64_IS_VMALLOC_ADDR(ulong vaddr);
+#define IS_VMALLOC_ADDR(X) sparc64_IS_VMALLOC_ADDR((ulong)(X))
+#define PAGE_SHIFT (13)
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+#define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask)
+#define THREAD_SIZE (2 * PAGE_SIZE)
+
+/* S3 Core
+ * Core 48-bit physical address supported.
+ * Bit 47 distinguishes memory or I/O. When set to "1" it is I/O.
+ */
+#define PHYS_MASK_SHIFT (47)
+#define PHYS_MASK (((1UL) << PHYS_MASK_SHIFT) - 1)
+
+typedef signed int s32;
+
+/*
+ * This next two defines are convenience defines for normal page table.
+ */
+#define PTES_PER_PAGE (1UL << (PAGE_SHIFT - 3))
+#define PTES_PER_PAGE_MASK (PTES_PER_PAGE - 1)
+
+/* 4-level page table */
+#define PMD_SHIFT (PAGE_SHIFT + (PAGE_SHIFT-3))
+#define PMD_SIZE (1UL << PMD_SHIFT)
+#define PMD_MASK (~(PMD_SIZE - 1))
+#define PMD_BITS (PAGE_SHIFT - 3)
+
+#define PUD_SHIFT (PMD_SHIFT + PMD_BITS)
+#define PUD_SIZE (1UL << PUD_SHIFT)
+#define PUD_MASK (~(PUD_SIZE - 1))
+#define PUD_BITS (PAGE_SHIFT - 3)
+
+#define PGDIR_SHIFT (PUD_SHIFT + PUD_BITS)
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE - 1))
+#define PGDIR_BITS (PAGE_SHIFT - 3)
+
+#define PTRS_PER_PTE (1UL << (PAGE_SHIFT - 3))
+#define PTRS_PER_PMD (1UL << PMD_BITS)
+#define PTRS_PER_PUD (1UL << PUD_BITS)
+#define PTRS_PER_PGD (1UL << PGDIR_BITS)
+
+#define HPAGE_SHIFT (23)
+/* Down one huge page */
+#define SPARC64_USERSPACE_TOP (-(1UL << HPAGE_SHIFT))
+#define PAGE_PMD_HUGE (0x0100000000000000UL)
+
+/* These are for SUN4V. */
+#define _PAGE_VALID (0x8000000000000000UL)
+#define _PAGE_NFO_4V (0x4000000000000000UL)
+#define _PAGE_MODIFIED_4V (0x2000000000000000UL)
+#define _PAGE_ACCESSED_4V (0x1000000000000000UL)
+#define _PAGE_READ_4V (0x0800000000000000UL)
+#define _PAGE_WRITE_4V (0x0400000000000000UL)
+#define _PAGE_PADDR_4V (0x00FFFFFFFFFFE000UL)
+#define _PAGE_PFN_MASK (_PAGE_PADDR_4V)
+#define _PAGE_P_4V (0x0000000000000100UL)
+#define _PAGE_EXEC_4V (0x0000000000000080UL)
+#define _PAGE_W_4V (0x0000000000000040UL)
+#define _PAGE_PRESENT_4V (0x0000000000000010UL)
+#define _PAGE_SZALL_4V (0x0000000000000007UL)
+/* There are other page sizes. Some supported. */
+#define _PAGE_SZ4MB_4V (0x0000000000000003UL)
+#define _PAGE_SZ512K_4V (0x0000000000000002UL)
+#define _PAGE_SZ64K_4V (0x0000000000000001UL)
+#define _PAGE_SZ8K_4V (0x0000000000000000UL)
+
+#define SPARC64_MODULES_VADDR (0x0000000010000000UL)
+#define SPARC64_MODULES_END (0x00000000f0000000UL)
+#define SPARC64_VMALLOC_START (0x0000000100000000UL)
+
+#define SPARC64_STACK_SIZE 0x4000
+
+/* sparsemem */
+#define _SECTION_SIZE_BITS 30
+#define _MAX_PHYSMEM_BITS 53
+
+#define STACK_BIAS 2047
+
+struct machine_specific {
+ ulong page_offset;
+ ulong vmalloc_end;
+};
+
+#define TIF_SIGPENDING (2)
+#define SWP_OFFSET(E) ((E) >> (PAGE_SHIFT + 8UL))
+#define SWP_TYPE(E) (((E) >> PAGE_SHIFT) & 0xffUL)
+#define __swp_type(E) SWP_TYPE(E)
+#define __swp_offset(E) SWP_OFFSET(E)
+#endif /* SPARC64 */
+
#ifdef PLATFORM
#define SWP_TYPE(entry) (error("PLATFORM_SWP_TYPE: TBD\n"))
@@ -3921,6 +4036,10 @@ struct efi_memory_desc_t {
#define MAX_HEXADDR_STRLEN (8)
#define UVADDR_PRLEN (8)
#endif
+#ifdef SPARC64
+#define MAX_HEXADDR_STRLEN (16)
+#define UVADDR_PRLEN (16)
+#endif
#define BADADDR ((ulong)(-1))
#define BADVAL ((ulong)(-1))
@@ -4471,6 +4590,9 @@ void dump_build_data(void);
#ifdef MIPS
#define machdep_init(X) mips_init(X)
#endif
+#ifdef SPARC64
+#define machdep_init(X) sparc64_init(X)
+#endif
int clean_exit(int);
int untrusted_file(FILE *, char *);
char *readmem_function_name(void);
@@ -4905,6 +5027,9 @@ void display_help_screen(char *);
#ifdef MIPS
#define dump_machdep_table(X) mips_dump_machdep_table(X)
#endif
+#ifdef SPARC64
+#define dump_machdep_table(X) sparc64_dump_machdep_table(X)
+#endif
extern char *help_pointer[];
extern char *help_alias[];
extern char *help_ascii[];
@@ -5757,6 +5882,17 @@ struct machine_specific {
#endif /* MIPS */
/*
+ * sparc64.c
+ */
+#ifdef SPARC64
+void sparc64_init(int);
+void sparc64_dump_machdep_table(ulong);
+int sparc64_vmalloc_addr(ulong);
+#define display_idt_table() \
+ error(FATAL, "The -d option is not applicable to sparc64.\n")
+#endif
+
+/*
* netdump.c
*/
int is_netdump(char *, ulong);
diff --git a/diskdump.c b/diskdump.c
index f5ec23d..48667ad 100644
--- a/diskdump.c
+++ b/diskdump.c
@@ -730,6 +730,8 @@ restart:
dd->machine_type = EM_S390;
else if (machine_type("ARM64"))
dd->machine_type = EM_AARCH64;
+ else if (machine_type("SPARC64"))
+ dd->machine_type = EM_SPARCV9;
else {
error(INFO, "%s: unsupported machine type: %s\n",
DISKDUMP_VALID() ? "diskdump" : "compressed kdump",
@@ -1382,6 +1384,31 @@ get_diskdump_regs_arm64(struct bt_info *bt, ulong *eip, ulong
*esp)
machdep->get_stack_frame(bt, eip, esp);
}
+static void
+get_diskdump_regs_sparc64(struct bt_info *bt, ulong *eip, ulong *esp)
+{
+ Elf64_Nhdr *note;
+ int len;
+
+ if (KDUMP_CMPRS_VALID() &&
+ (bt->task == tt->panic_task ||
+ (is_task_active(bt->task) && dd->num_prstatus_notes > 1))) {
+ note = (Elf64_Nhdr *)dd->nt_prstatus_percpu[bt->tc->processor];
+ if (!note)
+ error(FATAL,
+ "cannot determine NT_PRSTATUS ELF note "
+ "for %s task: %lx\n",
+ (bt->task == tt->panic_task) ?
+ "panic" : "active", bt->task);
+ len = sizeof(Elf64_Nhdr);
+ len = roundup(len + note->n_namesz, 4);
+ bt->machdep = (void *)((char *)note + len +
+ MEMBER_OFFSET("elf_prstatus", "pr_reg"));
+ }
+
+ machdep->get_stack_frame(bt, eip, esp);
+}
+
/*
* Send the request to the proper architecture hander.
*/
@@ -1432,6 +1459,10 @@ get_diskdump_regs(struct bt_info *bt, ulong *eip, ulong *esp)
get_diskdump_regs_arm64(bt, eip, esp);
break;
+ case EM_SPARCV9:
+ get_diskdump_regs_sparc64(bt, eip, esp);
+ break;
+
default:
error(FATAL, "%s: unsupported machine type: %s\n",
DISKDUMP_VALID() ? "diskdump" : "compressed kdump",
@@ -1577,7 +1608,8 @@ dump_note_offsets(FILE *fp)
for (tot = cnt = 0; tot < size; tot += len) {
qemu = FALSE;
if (machine_type("X86_64") || machine_type("S390X") ||
- machine_type("ARM64") || machine_type("PPC64")) {
+ machine_type("ARM64") || machine_type("PPC64") ||
+ machine_type("SPARC64")) {
note64 = (void *)dd->notes_buf + tot;
len = sizeof(Elf64_Nhdr);
if (STRNEQ((char *)note64 + len, "QEMU"))
@@ -1684,6 +1716,8 @@ __diskdump_memory_dump(FILE *fp)
fprintf(fp, "(EM_S390)\n"); break;
case EM_AARCH64:
fprintf(fp, "(EM_AARCH64)\n"); break;
+ case EM_SPARCV9:
+ fprintf(fp, "(EM_SPARCV9)\n"); break;
default:
fprintf(fp, "(unknown)\n"); break;
}
diff --git a/lkcd_vmdump_v2_v3.h b/lkcd_vmdump_v2_v3.h
index 7b014ec..8d5eae4 100644
--- a/lkcd_vmdump_v2_v3.h
+++ b/lkcd_vmdump_v2_v3.h
@@ -36,7 +36,7 @@
#endif
#if defined(ARM) || defined(X86) || defined(PPC) || defined(S390) || \
- defined(S390X) || defined(ARM64) || defined(MIPS)
+ defined(S390X) || defined(ARM64) || defined(MIPS) || defined(SPARC64)
/*
* Kernel header file for Linux crash dumps.
diff --git a/sparc64.c b/sparc64.c
new file mode 100644
index 0000000..b6e1ca7
--- /dev/null
+++ b/sparc64.c
@@ -0,0 +1,1253 @@
+/* sparc64.c - core analysis suite
+ *
+ * Copyright (C) 2016 Oracle Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifdef SPARC64
+
+#include "defs.h"
+#include <stdio.h>
+#include <elf.h>
+#include <asm/ptrace.h>
+#include <linux/const.h>
+
+/* TT (Trap Type) is encoded into magic pt_regs field */
+#define MAGIC_TT_MASK (0x1ff)
+
+static const unsigned long not_valid_pte = ~0UL;
+static struct machine_specific sparc64_machine_specific;
+static unsigned long sparc64_ksp_offset;
+
+static unsigned long
+__va(unsigned long paddr)
+{
+ return paddr + PAGE_OFFSET;
+}
+
+static unsigned long
+__pa(unsigned long vaddr)
+{
+ return vaddr - PAGE_OFFSET;
+}
+
+static void
+sparc64_parse_cmdline_args(void)
+{
+}
+
+/* This interface might not be required. */
+static void
+sparc64_clear_machdep_cache(void)
+{
+}
+
+/*
+ * "mach" command output.
+ */
+static void
+sparc64_display_machine_stats(void)
+{
+ int c;
+ struct new_utsname *uts;
+ char buf[BUFSIZE];
+ ulong mhz;
+
+ uts = &kt->utsname;
+
+ fprintf(fp, " MACHINE TYPE: %s\n", uts->machine);
+ fprintf(fp, " MEMORY SIZE: %s\n", get_memory_size(buf));
+ fprintf(fp, " CPUS: %d\n", kt->cpus);
+ fprintf(fp, " PROCESSOR SPEED: ");
+ if ((mhz = machdep->processor_speed()))
+ fprintf(fp, "%ld Mhz\n", mhz);
+ else
+ fprintf(fp, "(unknown)\n");
+ fprintf(fp, " HZ: %d\n", machdep->hz);
+ fprintf(fp, " PAGE SIZE: %ld\n", PAGE_SIZE);
+ fprintf(fp, " KERNEL VIRTUAL BASE: %lx\n", machdep->kvbase);
+ fprintf(fp, " KERNEL VMALLOC BASE: %lx\n", SPARC64_VMALLOC_START);
+ fprintf(fp, " KERNEL MODULES BASE: %lx\n", SPARC64_MODULES_VADDR);
+ fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE());
+
+ fprintf(fp, "HARD IRQ STACK SIZE: %ld\n", THREAD_SIZE);
+ fprintf(fp, " HARD IRQ STACKS:\n");
+
+ for (c = 0; c < kt->cpus; c++) {
+ if (!tt->hardirq_ctx[c])
+ continue;
+ sprintf(buf, "CPU %d", c);
+ fprintf(fp, "%19s: %lx\n", buf, tt->hardirq_ctx[c]);
+ }
+
+ fprintf(fp, "SOFT IRQ STACK SIZE: %ld\n", THREAD_SIZE);
+ fprintf(fp, " SOFT IRQ STACKS:\n");
+ for (c = 0; c < kt->cpus; c++) {
+ if (!tt->softirq_ctx[c])
+ continue;
+ sprintf(buf, "CPU %d", c);
+ fprintf(fp, "%19s: %lx\n", buf, tt->softirq_ctx[c]);
+ }
+}
+
+static void
+sparc64_display_memmap(void)
+{
+ unsigned long iomem_resource;
+ unsigned long resource;
+ unsigned long start, end, nameptr;
+ int size = STRUCT_SIZE("resource");
+ char *buf;
+ char name[32];
+
+ buf = GETBUF(size);
+ iomem_resource = symbol_value("iomem_resource");
+
+ readmem(iomem_resource + MEMBER_OFFSET("resource", "child"),
KVADDR,
+ &resource, sizeof(resource), "iomem_resource", FAULT_ON_ERROR);
+
+ fprintf(fp, " PHYSICAL ADDRESS RANGE TYPE\n");
+
+ while (resource) {
+ readmem(resource, KVADDR, buf, size, "resource",
+ FAULT_ON_ERROR);
+ start = ULONG(buf + MEMBER_OFFSET("resource", "start"));
+ end = ULONG(buf + MEMBER_OFFSET("resource", "end"));
+ nameptr = ULONG(buf + MEMBER_OFFSET("resource", "name"));
+
+ readmem(nameptr, KVADDR, name, sizeof(name), "resource.name",
+ FAULT_ON_ERROR);
+
+ fprintf(fp, "%016lx - %016lx %-32s\n", start, end, name);
+
+ resource = ULONG(buf + MEMBER_OFFSET("resource", "sibling"));
+ }
+ FREEBUF(buf);
+}
+
+static void
+sparc64_cmd_mach(void)
+{
+ int c;
+ int mflag = 0;
+
+ while ((c = getopt(argcnt, args, "cdmx")) != EOF) {
+ switch (c) {
+ case 'm':
+ mflag++;
+ sparc64_display_memmap();
+ break;
+ case 'c':
+ fprintf(fp, "SPARC64: '-%c' option is not supported\n",
+ c);
+ return;
+ case 'd':
+ case 'x':
+ /* Just ignore these */
+ break;
+ default:
+ argerrs++;
+ break;
+ }
+ }
+
+ if (argerrs)
+ cmd_usage(pc->curcmd, SYNOPSIS);
+
+ if (!mflag)
+ sparc64_display_machine_stats();
+}
+
+struct sparc64_mem_ranges {
+ unsigned long start;
+ unsigned long end;
+};
+
+#define NR_PHYS_RANGES (128)
+static unsigned int nr_phys_ranges;
+struct sparc64_mem_ranges phys_ranges[NR_PHYS_RANGES];
+
+#define NR_IMAGE_RANGES (16)
+static unsigned int nr_kimage_ranges;
+struct sparc64_mem_ranges kimage_ranges[NR_IMAGE_RANGES];
+
+/* There are three live cases:
+ * one) normal kernel
+ * two) --load-panic kernel
+ * and
+ * three) --load kernel
+ * One and two can be treated the same because the kernel is physically
+ * contiguous. Three isn't contiguous. The kernel is allocated in order
+ * nine allocation pages. We don't handle case three yet.
+ */
+
+static int
+sparc64_phys_live_valid(unsigned long paddr)
+{
+ unsigned int nr;
+ int rc = FALSE;
+
+ for (nr = 0; nr != nr_phys_ranges; nr++) {
+ if (paddr >= phys_ranges[nr].start &&
+ paddr < phys_ranges[nr].end) {
+ rc = TRUE;
+ break;
+ }
+ }
+ return rc;
+}
+
+static int
+sparc64_phys_kdump_valid(unsigned long paddr)
+{
+ return TRUE;
+}
+
+static int
+sparc64_verify_paddr(unsigned long paddr)
+{
+ int rc;
+
+ if (ACTIVE())
+ rc = sparc64_phys_live_valid(paddr);
+ else
+ rc = sparc64_phys_kdump_valid(paddr);
+
+ return rc;
+}
+
+static void
+sparc6_phys_base_live_limits(void)
+{
+ if (nr_phys_ranges >= NR_PHYS_RANGES)
+ error(FATAL, "sparc6_phys_base_live_limits: "
+ "NR_PHYS_RANGES exceeded.\n");
+ else if (nr_kimage_ranges >= NR_IMAGE_RANGES)
+ error(FATAL, "sparc6_phys_base_live_limits: "
+ "NR_IMAGE_RANGES exceeded.\n");
+}
+
+static void
+sparc64_phys_base_live_valid(void)
+{
+ if (!nr_phys_ranges)
+ error(FATAL, "No physical memory ranges.");
+ else if (!nr_kimage_ranges)
+ error(FATAL, "No vmlinux memory ranges.");
+}
+
+static void
+sparc64_phys_base_live(void)
+{
+ char line[BUFSIZE];
+ FILE *fp;
+
+ fp = fopen("/proc/iomem", "r");
+ if (fp == NULL)
+ error(FATAL, "Can't open /proc/iomem. We can't proceed.");
+
+ while (fgets(line, sizeof(line), fp) != 0) {
+ unsigned long start, end;
+ int count, consumed;
+ char *ch;
+
+ sparc6_phys_base_live_limits();
+ count = sscanf(line, "%lx-%lx : %n", &start, &end, &consumed);
+ if (count != 2)
+ continue;
+ ch = line + consumed;
+ if (memcmp(ch, "System RAM\n", 11) == 0) {
+ end = end + 1;
+ phys_ranges[nr_phys_ranges].start = start;
+ phys_ranges[nr_phys_ranges].end = end;
+ nr_phys_ranges++;
+ } else if ((memcmp(ch, "Kernel code\n", 12) == 0) ||
+ (memcmp(ch, "Kernel data\n", 12) == 0) ||
+ (memcmp(ch, "Kernel bss\n", 11) == 0)) {
+ kimage_ranges[nr_kimage_ranges].start = start;
+ kimage_ranges[nr_kimage_ranges].end = end;
+ nr_kimage_ranges++;
+ }
+ }
+
+ (void) fclose(fp);
+ sparc64_phys_base_live_valid();
+}
+
+static void
+sparc64_phys_base_kdump(void)
+{
+}
+
+static void
+sparc64_phys_base(void)
+{
+ if (ACTIVE())
+ return sparc64_phys_base_live();
+ else
+ return sparc64_phys_base_kdump();
+}
+
+static unsigned long kva_start, kva_end;
+static unsigned long kpa_start, kpa_end;
+
+static void
+sparc64_kimage_limits_live(void)
+{
+ kpa_start = kimage_ranges[0].start;
+ kpa_end = kpa_start + (kva_end - kva_start);
+}
+
+static void
+sparc64_kimage_limits_kdump(void)
+{
+ unsigned long phys_base;
+
+ if (DISKDUMP_DUMPFILE()) {
+ if (diskdump_phys_base(&phys_base)) {
+ kpa_start = phys_base | (kva_start & 0xffff);
+ kpa_end = kpa_start + (kva_end - kva_start);
+ return;
+ }
+ }
+ fprintf(stderr, "Can't determine phys_base\n");
+}
+
+static unsigned long
+kimage_va_translate(unsigned long addr)
+{
+ unsigned long paddr = (addr - kva_start) + kpa_start;
+
+ return paddr;
+}
+
+static int
+kimage_va_range(unsigned long addr)
+{
+ if (addr >= kva_start && addr < kva_end)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+sparc64_kimage_limits(void)
+{
+ kva_start = symbol_value("_stext");
+ kva_end = symbol_value("_end");
+
+ if (ACTIVE())
+ sparc64_kimage_limits_live();
+ else
+ sparc64_kimage_limits_kdump();
+}
+
+static int
+sparc64_is_linear_mapped(unsigned long vaddr)
+{
+ return (vaddr & PAGE_OFFSET) == PAGE_OFFSET;
+}
+
+static unsigned long
+pte_to_pa(unsigned long pte)
+{
+ unsigned long paddr = pte & _PAGE_PFN_MASK;
+
+ return paddr;
+}
+
+static unsigned long
+fetch_page_table_level(unsigned long pte_kva, unsigned long vaddr,
+ unsigned int shift, unsigned int mask, const char *name,
+ int verbose)
+{
+ unsigned int pte_index = (vaddr >> shift) & mask;
+ unsigned long page_table[PTES_PER_PAGE];
+ unsigned long pte = 0UL;
+ int rc;
+
+ rc = readmem(pte_kva, KVADDR, page_table, sizeof(page_table),
+ (char *)name, RETURN_ON_ERROR);
+ if (!rc)
+ goto out;
+ pte = page_table[pte_index];
+ if (verbose)
+ fprintf(fp,
+ "%s(0x%.16lx) fetch of pte @index[0x%.4x]=0x%.16lx\n",
+ name, pte_kva, pte_index, pte);
+out:
+ return pte;
+}
+
+static unsigned long
+pmd_is_huge(unsigned long pmd, unsigned long vaddr, int verbose)
+{
+ unsigned long hpage_mask;
+ unsigned long paddr = 0UL;
+
+ if ((pmd & PAGE_PMD_HUGE) == 0UL)
+ goto out;
+ hpage_mask = ~((1UL << HPAGE_SHIFT) - 1UL);
+ paddr = pte_to_pa(pmd) + (vaddr & ~hpage_mask);
+ if (verbose)
+ fprintf(fp, "Huge Page/THP pmd=0x%.16lx paddr=0x%.16lx\n",
+ pmd, paddr);
+out:
+ return paddr;
+}
+
+static unsigned long
+sparc64_page_table_walk(unsigned long pgd, unsigned long vaddr, int verbose)
+{
+ static const char *pgd_text = "pgd fetch";
+ static const char *pud_text = "pud fetch";
+ static const char *pmd_text = "pmd fetch";
+ static const char *pte_text = "pte fetch";
+ unsigned long kva = pgd;
+ unsigned long paddr;
+ unsigned long pte;
+
+ if (!sparc64_is_linear_mapped(kva))
+ error(FATAL,
+ "sparc64_page_table_walk: pgd must be identity mapped"
+ " but isn't (0xlx).", pgd);
+
+ pte = fetch_page_table_level(kva, vaddr, PGDIR_SHIFT,
+ PTES_PER_PAGE_MASK, pgd_text, verbose);
+ if (!pte)
+ goto bad;
+ kva = __va(pte);
+
+ pte = fetch_page_table_level(kva, vaddr, PUD_SHIFT, PTES_PER_PAGE_MASK,
+ pud_text, verbose);
+ if (!pte)
+ goto bad;
+
+ kva = __va(pte);
+ pte = fetch_page_table_level(kva, vaddr, PMD_SHIFT,
+ PTES_PER_PAGE_MASK, pmd_text, verbose);
+ if (!pte)
+ goto bad;
+ /* Check for a huge/THP page */
+ paddr = pmd_is_huge(pte, vaddr, verbose);
+ if (paddr)
+ goto out;
+ kva = __va(pte);
+ pte = fetch_page_table_level(kva, vaddr, PAGE_SHIFT,
+ PTRS_PER_PTE - 1, pte_text, verbose);
+ if ((pte & _PAGE_VALID) == 0UL)
+ goto bad;
+ paddr = pte_to_pa(pte);
+ paddr = paddr | (vaddr & ~PAGE_MASK);
+out:
+ return paddr;
+bad:
+ return not_valid_pte;
+}
+
+static void
+sparc64_init_kernel_pgd(void)
+{
+ int cpu, rc;
+ ulong v;
+
+ v = symbol_value("init_mm");
+ rc = readmem(v + OFFSET(mm_struct_pgd), KVADDR, &v, sizeof(v),
+ "init_mm.pgd", RETURN_ON_ERROR);
+ if (!rc) {
+ error(WARNING, "Can not determine pgd location.\n");
+ goto out;
+ }
+
+ for (cpu = 0; cpu < NR_CPUS; cpu++)
+ vt->kernel_pgd[cpu] = v;
+out:
+ return;
+}
+
+static int
+sparc64_get_smp_cpus(void)
+{
+ int ncpu = MAX(get_cpus_online(), get_highest_cpu_online() + 1);
+
+ return ncpu;
+}
+
+static ulong
+sparc64_vmalloc_start(void)
+{
+ return SPARC64_VMALLOC_START;
+}
+
+int
+sparc64_IS_VMALLOC_ADDR(ulong vaddr)
+{
+ return (vaddr >= SPARC64_VMALLOC_START) &&
+ (vaddr < machdep->machspec->vmalloc_end);
+}
+
+static void
+pt_clear_cache(void)
+{
+ machdep->last_pgd_read = 0UL;
+ machdep->last_pud_read = 0UL;
+ machdep->last_pmd_read = 0UL;
+ machdep->last_ptbl_read = 0UL;
+}
+
+static void
+pt_level_alloc(char **lvl, char *name)
+{
+ size_t sz = PAGE_SIZE;
+ void *pointer = malloc(sz);
+
+ if (!pointer)
+ error(FATAL, name);
+ *lvl = pointer;
+}
+
+static int
+sparc64_verify_symbol(const char *name, unsigned long value, char type)
+{
+ return TRUE;
+}
+
+static int
+sparc64_verify_line_number(unsigned long pc, unsigned long low,
+ unsigned long high)
+{
+ return TRUE;
+}
+
+static int
+sparc64_dis_filter(ulong vaddr, char *inbuf, unsigned int radix)
+{
+ return FALSE;
+}
+
+struct eframe {
+ struct sparc_stackf sf;
+ struct pt_regs pr;
+};
+
+/* Need to handle hardirq and softirq stacks. */
+static int
+kstack_valid(struct bt_info *bt, unsigned long sp)
+{
+ unsigned long thread_info = SIZE(thread_info);
+ unsigned long base = bt->stackbase + thread_info;
+ unsigned long top = bt->stacktop - sizeof(struct eframe);
+ int rc = FALSE;
+
+ if (sp & (16U - 1))
+ goto out;
+
+ if ((sp >= base) && (sp <= top))
+ rc = TRUE;
+out:
+ return rc;
+}
+
+static void
+sparc64_print_eframe(struct bt_info *bt)
+{
+ struct eframe k_entry;
+ struct pt_regs *regs = &k_entry.pr;
+ unsigned long efp;
+ unsigned int tt;
+ int rc;
+ struct reg_window window;
+ unsigned long rw;
+
+ efp = bt->stkptr + STACK_BIAS - TRACEREG_SZ - STACKFRAME_SZ;
+ if (!kstack_valid(bt, efp))
+ goto try_stacktop;
+
+ rc = readmem(efp, KVADDR, &k_entry, sizeof(k_entry),
+ "Stack frame and pt_regs.", RETURN_ON_ERROR);
+ if (rc && ((regs->magic & ~MAGIC_TT_MASK) == PT_REGS_MAGIC))
+ goto print_frame;
+
+try_stacktop:
+ efp = bt->stacktop - sizeof(struct eframe);
+ rc = readmem(efp, KVADDR, &k_entry, sizeof(k_entry),
+ "Stack frame and pt_regs.", RETURN_ON_ERROR);
+ if (!rc)
+ goto out;
+ /* Kernel thread or not in kernel any longer? */
+ if ((regs->magic & ~MAGIC_TT_MASK) != PT_REGS_MAGIC)
+ goto out;
+
+print_frame:
+ tt = regs->magic & MAGIC_TT_MASK;
+ fprintf(fp, "TSTATE=0x%lx TT=0x%x TPC=0x%lx TNPC=0x%lx\n",
+ regs->tstate, tt, regs->tpc, regs->tnpc);
+ fprintf(fp, " g0=0x%.16lx g1=0x%.16lx g2=0x%.16lx\n",
+ regs->u_regs[0],
+ regs->u_regs[1],
+ regs->u_regs[2]);
+ fprintf(fp, " g3=0x%.16lx g4=0x%.16lx g5=0x%.16lx\n",
+ regs->u_regs[3],
+ regs->u_regs[4],
+ regs->u_regs[5]);
+#define ___INS (8)
+ fprintf(fp, " g6=0x%.16lx g7=0x%.16lx\n",
+ regs->u_regs[6],
+ regs->u_regs[7]);
+ fprintf(fp, " o0=0x%.16lx o1=0x%.16lx o2=0x%.16lx\n",
+ regs->u_regs[___INS+0],
+ regs->u_regs[___INS+1],
+ regs->u_regs[___INS+2]);
+ fprintf(fp, " o3=0x%.16lx o4=0x%.16lx o5=0x%.16lx\n",
+ regs->u_regs[___INS+3],
+ regs->u_regs[___INS+4],
+ regs->u_regs[___INS+5]);
+ fprintf(fp, " sp=0x%.16lx ret_pc=0x%.16lx\n",
+ regs->u_regs[___INS+6],
+ regs->u_regs[___INS+7]);
+#undef ___INS
+ rw = bt->stkptr + STACK_BIAS;
+ if (!kstack_valid(bt, rw))
+ goto out;
+ rc = readmem(rw, KVADDR, &window, sizeof(window),
+ "Register window.", RETURN_ON_ERROR);
+ if (!rc)
+ goto out;
+ fprintf(fp, " l0=0x%.16lx l1=0x%.16lx l2=0x%.16lx\n",
+ window.locals[0], window.locals[1], window.locals[2]);
+ fprintf(fp, " l3=0x%.16lx l4=0x%.16lx l5=0x%.16lx\n",
+ window.locals[3], window.locals[4], window.locals[5]);
+ fprintf(fp, " l6=0x%.16lx l7=0x%.16lx\n",
+ window.locals[6], window.locals[7]);
+ fprintf(fp, " i0=0x%.16lx i1=0x%.16lx i2=0x%.16lx\n",
+ window.ins[0], window.ins[1], window.ins[2]);
+ fprintf(fp, " i3=0x%.16lx i4=0x%.16lx i5=0x%.16lx\n",
+ window.ins[3], window.ins[4], window.ins[5]);
+ fprintf(fp, " i6=0x%.16lx i7=0x%.16lx\n",
+ window.ins[6], window.ins[7]);
+out:
+ return;
+}
+
+static int
+sparc64_eframe_search(struct bt_info *bt)
+{
+ sparc64_print_eframe(bt);
+ return 0;
+}
+
+static void
+sparc64_print_frame(struct bt_info *bt, int cnt, unsigned long ip,
+ unsigned long ksp)
+{
+ char *symbol = closest_symbol(ip);
+
+ fprintf(fp, "#%d [%lx] %s at %lx\n", cnt, ksp, symbol, ip);
+
+ if (bt->flags & BT_LINE_NUMBERS) {
+ char buf[BUFSIZE];
+
+ get_line_number(ip, buf, FALSE);
+ if (strlen(buf))
+ fprintf(fp, "\t%s\n", buf);
+ }
+}
+
+static void
+sparc64_back_trace(struct bt_info *bt)
+{
+ unsigned long ip = bt->instptr;
+ unsigned long ksp = bt->stkptr;
+ struct reg_window window;
+ int cnt = 0;
+ int rc;
+
+ do {
+ if (!kstack_valid(bt, ksp + STACK_BIAS))
+ break;
+ rc = readmem(ksp + STACK_BIAS, KVADDR, &window, sizeof(window),
+ "KSP window fetch.", RETURN_ON_ERROR);
+ if (!rc)
+ goto out;
+ sparc64_print_frame(bt, cnt, ip, ksp);
+ ksp = window.ins[6];
+ ip = window.ins[7];
+ cnt++;
+ } while (cnt != 50);
+ sparc64_print_eframe(bt);
+out:
+ return;
+}
+
+static ulong
+sparc64_processor_speed(void)
+{
+ int cpu;
+ unsigned long clock_tick;
+ struct syment *sp;
+
+ if (!MEMBER_EXISTS("cpuinfo_sparc", "clock_tick")) {
+ error(WARNING, "sparc64 expects clock_tick\n");
+ return 0UL;
+ }
+
+ sp = per_cpu_symbol_search("__cpu_data");
+ if (!sp)
+ return 0UL;
+ for (cpu = 0; cpu < kt->cpus; cpu++) {
+ if (!in_cpu_map(ONLINE, cpu))
+ continue;
+ if (!readmem(sp->value + kt->__per_cpu_offset[cpu] +
+ MEMBER_OFFSET("cpuinfo_sparc", "clock_tick"),
+ KVADDR, &clock_tick, sizeof(clock_tick),
+ "clock_tick", QUIET|RETURN_ON_ERROR))
+ continue;
+ return clock_tick/1000000;
+ }
+ return 0UL;
+}
+
+static ulong
+sparc64_get_task_pgd(ulong task)
+{
+ struct task_context *tc = task_to_context(task);
+ ulong pgd = NO_TASK;
+
+ if (!tc)
+ goto out;
+ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR,
+ &pgd, sizeof(unsigned long), "User pgd.", RETURN_ON_ERROR);
+out:
+ return pgd;
+}
+
+static int
+sparc64_uvtop(struct task_context *tc, ulong va, physaddr_t *ppaddr,
+ int verbose)
+{
+ unsigned long pgd = sparc64_get_task_pgd(tc->task);
+ unsigned long paddr;
+ int rc = FALSE;
+
+ if (pgd == NO_TASK)
+ goto out;
+ paddr = sparc64_page_table_walk(pgd, va, verbose);
+ /* For now not_valid_pte skips checking for swap pte. */
+ if (paddr == not_valid_pte) {
+ *ppaddr = 0UL;
+ goto out;
+ }
+ *ppaddr = paddr;
+ rc = TRUE;
+out:
+ return rc;
+}
+
+static unsigned long
+sparc64_vmalloc_translate(unsigned long vaddr, int verbose)
+{
+ unsigned long paddr = sparc64_page_table_walk(vt->kernel_pgd[0],
+ vaddr, verbose);
+
+ return paddr;
+}
+
+static unsigned long
+sparc64_linear_translate(unsigned long vaddr)
+{
+ unsigned long paddr = __pa(vaddr);
+
+ if (sparc64_verify_paddr(paddr) == FALSE)
+ error(FATAL,
+ "sparc64_linear_translate: This physical address"
+ " (0x%lx) is invalid.", paddr);
+
+ return paddr;
+}
+
+static int
+sparc64_is_vmalloc_mapped(unsigned long vaddr)
+{
+ struct machine_specific *ms = &sparc64_machine_specific;
+ int rc = 0;
+
+ if ((vaddr >= SPARC64_MODULES_VADDR && vaddr < SPARC64_MODULES_END) ||
+ (vaddr >= SPARC64_VMALLOC_START && vaddr < ms->vmalloc_end))
+ rc = 1;
+ return rc;
+}
+
+static int
+sparc64_is_kvaddr(ulong vaddr)
+{
+ return kimage_va_range(vaddr) ||
+ sparc64_is_linear_mapped(vaddr) ||
+ sparc64_is_vmalloc_mapped(vaddr);
+}
+
+static int
+sparc64_kvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr,
+ int verbose)
+{
+ unsigned long phys_addr;
+ int rc = FALSE;
+
+ if (kimage_va_range(vaddr)) {
+ phys_addr = kimage_va_translate(vaddr);
+ } else if (sparc64_is_vmalloc_mapped(vaddr)) {
+ phys_addr = sparc64_vmalloc_translate(vaddr, verbose);
+ if (phys_addr == not_valid_pte)
+ goto out;
+ } else if (sparc64_is_linear_mapped(vaddr)) {
+ phys_addr = sparc64_linear_translate(vaddr);
+ } else {
+ error(WARNING,
+ "This is an invalid kernel virtual address=0x%lx.",
+ vaddr);
+ goto out;
+ }
+
+ *paddr = phys_addr;
+ rc = TRUE;
+out:
+ return rc;
+}
+
+static int
+sparc64_is_task_addr(ulong task)
+{
+ int rc = FALSE;
+ int cpu;
+
+ if (sparc64_is_linear_mapped(task) || kimage_va_range(task))
+ rc = TRUE;
+ else {
+ for (cpu = 0; cpu < kt->cpus; cpu++)
+ if (task == tt->idle_threads[cpu]) {
+ rc = TRUE;
+ break;
+ }
+ }
+ return rc;
+}
+
+static int
+sparc64_is_uvaddr(ulong vaddr, struct task_context *tc)
+{
+ return vaddr < SPARC64_USERSPACE_TOP;
+}
+
+static const char
+*pte_page_size(unsigned long pte)
+{
+ static const char *_4Mb = "4Mb";
+ static const char *_64Kb = "64Kb";
+ static const char *_8Kb = "8Kb";
+ static const char *_ns = "Not Supported";
+ const char *result;
+
+ switch (pte & _PAGE_SZALL_4V) {
+ case _PAGE_SZ8K_4V:
+ result = _8Kb;
+ break;
+ case _PAGE_SZ64K_4V:
+ result = _64Kb;
+ break;
+ case _PAGE_SZ4MB_4V:
+ result = _4Mb;
+ break;
+ default:
+ result = _ns;
+ }
+ return result;
+}
+
+static int
+sparc64_translate_pte(unsigned long pte, void *physaddr, ulonglong unused)
+{
+ unsigned long paddr = pte_to_pa(pte);
+ int rc = FALSE;
+ int cnt = 0;
+
+ /* Once again not handling swap pte.*/
+ if ((pte & _PAGE_VALID) == 0UL)
+ goto out;
+ if (pte & _PAGE_NFO_4V)
+ fprintf(fp, "%sNoFaultOn", cnt++ ? "|" : "");
+ if (pte & _PAGE_MODIFIED_4V)
+ fprintf(fp, "%sModified", cnt++ ? "|" : "");
+ if (pte & _PAGE_ACCESSED_4V)
+ fprintf(fp, "%sAccessed", cnt++ ? "|" : "");
+ if (pte & _PAGE_READ_4V)
+ fprintf(fp, "%sReadSoftware", cnt++ ? "|" : "");
+ if (pte & _PAGE_WRITE_4V)
+ fprintf(fp, "%sWriteSoftware", cnt++ ? "|" : "");
+ if (pte & _PAGE_P_4V)
+ fprintf(fp, "%sPriv", cnt++ ? "|" : "");
+ if (pte & _PAGE_EXEC_4V)
+ fprintf(fp, "%sExecute", cnt++ ? "|" : "");
+ if (pte & _PAGE_W_4V)
+ fprintf(fp, "%sWritable", cnt++ ? "|" : "");
+ if (pte & _PAGE_PRESENT_4V)
+ fprintf(fp, "%sPresent", cnt++ ? "|" : "");
+ fprintf(fp, "|PageSize(%s)\n", pte_page_size(pte));
+ if (physaddr)
+ *(unsigned long *)physaddr = paddr;
+ rc = TRUE;
+out:
+ return rc;
+}
+
+static void
+sparc64_get_frame(struct bt_info *bt, unsigned long *r14, unsigned long *r15)
+{
+ unsigned long ksp_offset = sparc64_ksp_offset + bt->tc->thread_info;
+ unsigned long ksp;
+ int rc;
+
+ /* We need thread_info's ksp. This is the stack for sleeping threads
+ * and captured during switch_to. The rest is fetchable from there.
+ */
+ rc = readmem(ksp_offset, KVADDR, &ksp, sizeof(ksp), "KSP Fetch.",
+ RETURN_ON_ERROR);
+ if (!rc)
+ goto out;
+ *r14 = ksp;
+ *r15 = symbol_value("switch_to_pc");
+out:
+ return;
+}
+
+static void
+sparc64_get_dumpfile_stack_frame(struct bt_info *bt, unsigned long *psp,
+ unsigned long *ppc)
+{
+ unsigned long *pt_regs;
+
+ pt_regs = (unsigned long *)bt->machdep;
+
+ if (!pt_regs)
+ fprintf(fp, "0%lx: registers not saved\n", bt->task);
+
+ /* pt_regs can be unaligned */
+ BCOPY(&pt_regs[30], psp, sizeof(ulong));
+ BCOPY(&pt_regs[33], ppc, sizeof(ulong));
+}
+
+static void
+sparc64_get_stack_frame(struct bt_info *bt, unsigned long *pcp,
+ unsigned long *psp)
+{
+ unsigned long r14, r15;
+
+ if (DUMPFILE() && is_task_active(bt->task))
+ sparc64_get_dumpfile_stack_frame(bt, &r14, &r15);
+ else
+ sparc64_get_frame(bt, &r14, &r15);
+ if (pcp)
+ *pcp = r15;
+ if (psp)
+ *psp = r14;
+}
+
+static int
+sparc64_get_kvaddr_ranges(struct vaddr_range *vrp)
+{
+ struct machine_specific *ms = &sparc64_machine_specific;
+
+ vrp[0].type = KVADDR_UNITY_MAP;
+ vrp[0].start = ms->page_offset;
+ vrp[0].end = ~0ULL;
+ vrp[1].type = KVADDR_VMALLOC;
+ vrp[1].start = SPARC64_VMALLOC_START;
+ vrp[1].end = ms->vmalloc_end;
+ vrp[2].type = KVADDR_START_MAP;
+ vrp[2].start = symbol_value("_start");
+ vrp[2].end = symbol_value("_end");
+ vrp[3].type = KVADDR_MODULES;
+ vrp[3].start = SPARC64_MODULES_VADDR;
+ vrp[3].end = SPARC64_MODULES_END;
+ return 4;
+}
+
+static void
+sparc64_get_crash_notes(void)
+{
+ unsigned long *notes_ptrs, size, crash_notes_address;
+ int ret;
+
+ if (!symbol_exists("crash_notes")) {
+ error(WARNING, "Could not retrieve crash_notes.");
+ goto out;
+ }
+
+ crash_notes_address = symbol_value("crash_notes");
+ size = kt->cpus * sizeof(notes_ptrs[0]);
+ notes_ptrs = (unsigned long *) GETBUF(size);
+ ret = readmem(crash_notes_address, KVADDR, notes_ptrs, size,
+ "crash_notes", RETURN_ON_ERROR);
+ if (!ret)
+ goto out2;
+out2:
+ FREEBUF(notes_ptrs);
+out:
+ return;
+}
+
+static void
+sparc64_init_kstack_info(void)
+{
+ sparc64_ksp_offset = MEMBER_OFFSET("thread_info", "ksp");
+}
+
+static void
+sparc64_init_irq_stacks(void)
+{
+ void *irq_stack;
+ unsigned long stack_size;
+
+ stack_size = get_array_length("hardirq_stack", NULL, 0) *
+ sizeof(unsigned long);
+ irq_stack = malloc(stack_size);
+ if (!irq_stack)
+ error(FATAL, "malloc failure in sparc64_init_irq_stacks");
+
+ get_symbol_data("hardirq_stack", stack_size, irq_stack);
+ tt->hardirq_ctx = irq_stack;
+
+ stack_size = get_array_length("softirq_stack", NULL, 0) *
+ sizeof(unsigned long);
+ irq_stack = malloc(stack_size);
+ if (!irq_stack)
+ error(FATAL, "malloc failure in sparc64_init_irq_stacks");
+
+ get_symbol_data("softirq_stack", stack_size, irq_stack);
+ tt->softirq_ctx = irq_stack;
+}
+
+static void
+sparc64_init_vmemmap_info(void)
+{
+ struct machine_specific *ms = &sparc64_machine_specific;
+ unsigned long page_struct_size = STRUCT_SIZE("page");
+
+ /*
+ * vmemmap memory is addressed as vmalloc memory, so we
+ * treat it as an etension of the latter.
+ */
+ ms->vmalloc_end +=
+ ((1UL << (machdep->max_physmem_bits - PAGE_SHIFT)) *
+ page_struct_size);
+}
+
+static void
+sparc64_init_cpu_info(void)
+{
+ unsigned long trap_block, per_cpu_base_offset, per_cpu_base;
+ unsigned long trap_per_cpu;
+ int cpu;
+
+ if (!symbol_exists("trap_block"))
+ error(FATAL, "sparc64 requires trap_block symbol.\n");
+
+ trap_block = symbol_value("trap_block");
+ if (!MEMBER_EXISTS("trap_per_cpu", "__per_cpu_base"))
+ error(FATAL, "sparc64 requires __per_cpu_base.\n");
+ trap_per_cpu = STRUCT_SIZE("trap_per_cpu");
+ per_cpu_base_offset = MEMBER_OFFSET("trap_per_cpu",
"__per_cpu_base");
+ for (cpu = 0; cpu < NR_CPUS; cpu++,
+ trap_block = trap_block + trap_per_cpu) {
+
+ if (!in_cpu_map(POSSIBLE, cpu))
+ continue;
+ readmem(trap_block + per_cpu_base_offset, KVADDR,
+ &per_cpu_base, sizeof(per_cpu_base),
+ "sparc64: per_cpu_base", FAULT_ON_ERROR);
+ kt->__per_cpu_offset[cpu] = per_cpu_base;
+ }
+}
+
+void
+sparc64_init(int when)
+{
+ struct machine_specific *ms = &sparc64_machine_specific;
+
+ switch (when) {
+ case SETUP_ENV:
+ machdep->process_elf_notes = process_elf64_notes;
+ break;
+ case PRE_SYMTAB:
+ machdep->machspec = ms;
+ machdep->verify_paddr = sparc64_verify_paddr;
+ machdep->verify_symbol = sparc64_verify_symbol;
+ machdep->verify_line_number = sparc64_verify_line_number;
+
+ if (pc->flags & KERNEL_DEBUG_QUERY)
+ return;
+ machdep->flags |= MACHDEP_BT_TEXT;
+ if (machdep->cmdline_args[0])
+ sparc64_parse_cmdline_args();
+ break;
+
+ case PRE_GDB:
+ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS;
+
+ machdep->pagesize = memory_page_size();
+ machdep->pageshift = ffs(machdep->pagesize) - 1;
+ machdep->pageoffset = machdep->pagesize - 1;
+ machdep->pagemask = ~((ulonglong) machdep->pageoffset);
+ machdep->stacksize = machdep->pagesize * 2;
+
+ machdep->eframe_search = sparc64_eframe_search;
+ machdep->back_trace = sparc64_back_trace;
+ machdep->processor_speed = sparc64_processor_speed;
+
+ machdep->uvtop = sparc64_uvtop;
+ machdep->kvtop = sparc64_kvtop;
+ machdep->get_task_pgd = sparc64_get_task_pgd;
+
+ machdep->dump_irq = generic_dump_irq;
+
+ machdep->get_stack_frame = sparc64_get_stack_frame;
+ machdep->get_stackbase = generic_get_stackbase;
+ machdep->get_stacktop = generic_get_stacktop;
+ machdep->translate_pte = sparc64_translate_pte;
+ machdep->memory_size = generic_memory_size;
+
+ machdep->vmalloc_start = sparc64_vmalloc_start;
+ machdep->is_task_addr = sparc64_is_task_addr;
+ machdep->is_kvaddr = sparc64_is_kvaddr;
+ machdep->is_uvaddr = sparc64_is_uvaddr;
+ machdep->dis_filter = sparc64_dis_filter;
+ machdep->get_smp_cpus = sparc64_get_smp_cpus;
+ machdep->clear_machdep_cache = sparc64_clear_machdep_cache;
+ machdep->get_kvaddr_ranges = sparc64_get_kvaddr_ranges;
+ machdep->cmd_mach = sparc64_cmd_mach;
+ machdep->init_kernel_pgd = sparc64_init_kernel_pgd;
+ machdep->value_to_symbol = generic_machdep_value_to_symbol;
+ machdep->get_irq_affinity = generic_get_irq_affinity;
+ machdep->show_interrupts = generic_show_interrupts;
+
+ pt_level_alloc(&machdep->pgd, "Can't malloc pgd space.");
+ pt_level_alloc(&machdep->pud, "Can't malloc pud space.");
+ pt_level_alloc(&machdep->pmd, "Can't malloc pmd space.");
+ pt_level_alloc(&machdep->ptbl, "Can't malloc ptbl space.");
+ pt_clear_cache();
+ sparc64_phys_base();
+ sparc64_kimage_limits();
+ break;
+
+ case POST_GDB:
+ get_symbol_data("PAGE_OFFSET", sizeof(unsigned long),
+ &ms->page_offset);
+ machdep->kvbase = symbol_value("_stext");
+ machdep->identity_map_base = (ulong) PAGE_OFFSET;
+ machdep->ptrs_per_pgd = PTRS_PER_PGD;
+ get_symbol_data("VMALLOC_END", sizeof(unsigned long),
+ &ms->vmalloc_end);
+ machdep->section_size_bits = _SECTION_SIZE_BITS;
+ if (kernel_symbol_exists("nr_irqs"))
+ get_symbol_data("nr_irqs", sizeof(unsigned int),
+ &machdep->nr_irqs);
+ sparc64_init_vmemmap_info();
+ sparc64_init_cpu_info();
+ sparc64_init_kstack_info();
+ sparc64_init_irq_stacks();
+ break;
+ case POST_VM:
+ if (!ACTIVE())
+ sparc64_get_crash_notes();
+ break;
+ case POST_INIT:
+ break;
+
+ case LOG_ONLY:
+ machdep->machspec = ms;
+ machdep->kvbase = kt->vmcoreinfo._stext_SYMBOL;
+ break;
+ }
+}
+
+void
+sparc64_dump_machdep_table(ulong arg)
+{
+ int i, others;
+
+ others = 0;
+ fprintf(fp, " flags: %lx (", machdep->flags);
+ if (machdep->flags & MACHDEP_BT_TEXT)
+ fprintf(fp, "%sMACHDEP_BT_TEXT", others++ ? "|" : "");
+ fprintf(fp, ")\n");
+
+ fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
+ fprintf(fp, " identity_map_base: %lx\n", machdep->identity_map_base);
+ fprintf(fp, " pagesize: %d\n", machdep->pagesize);
+ fprintf(fp, " pageshift: %d\n", machdep->pageshift);
+ fprintf(fp, " pagemask: %llx\n", machdep->pagemask);
+ fprintf(fp, " pageoffset: %lx\n", machdep->pageoffset);
+ fprintf(fp, " stacksize: %ld\n", machdep->stacksize);
+ fprintf(fp, " hz: %d\n", machdep->hz);
+ fprintf(fp, " mhz: %ld\n", machdep->mhz);
+ fprintf(fp, " memsize: %ld (0x%lx)\n",
+ machdep->memsize, machdep->memsize);
+ fprintf(fp, " bits: %d\n", machdep->bits);
+ fprintf(fp, " nr_irqs: %d\n", machdep->nr_irqs);
+ fprintf(fp, " eframe_search: sparc64_eframe_search()\n");
+ fprintf(fp, " back_trace: sparc64_back_trace()\n");
+ fprintf(fp, " processor_speed: sparc64_processor_speed()\n");
+ fprintf(fp, " uvtop: sparc64_uvtop()\n");
+ fprintf(fp, " kvtop: sparc64_kvtop()\n");
+ fprintf(fp, " get_task_pgd: sparc64_get_task_pgd()\n");
+ fprintf(fp, " dump_irq: generic_dump_irq()\n");
+ fprintf(fp, " get_stack_frame: sparc64_get_stack_frame()\n");
+ fprintf(fp, " get_stackbase: generic_get_stackbase()\n");
+ fprintf(fp, " get_stacktop: generic_get_stacktop()\n");
+ fprintf(fp, " translate_pte: sparc64_translate_pte()\n");
+ fprintf(fp, " memory_size: generic_memory_size()\n");
+ fprintf(fp, " vmalloc_start: sparc64_vmalloc_start()\n");
+ fprintf(fp, " is_task_addr: sparc64_is_task_addr()\n");
+ fprintf(fp, " verify_symbol: sparc64_verify_symbol()\n");
+ fprintf(fp, " dis_filter: sparc64_dis_filter()\n");
+ fprintf(fp, " cmd_mach: sparc64_cmd_mach()\n");
+ fprintf(fp, " get_smp_cpus: sparc64_get_smp_cpus()\n");
+ fprintf(fp, " is_kvaddr: sparc64_is_kvaddr()\n");
+ fprintf(fp, " is_uvaddr: sparc64_is_uvaddr()\n");
+ fprintf(fp, " verify_paddr: sparc64_verify_paddr()\n");
+ fprintf(fp, " get_kvaddr_ranges: sparc64_get_kvaddr_ranges()\n");
+ fprintf(fp, " get_irq_affinity: generic_get_irq_affinity()\n");
+ fprintf(fp, " show_interrupts: generic_show_interrupts()\n");
+ fprintf(fp, " xendump_p2m_create: NULL\n");
+ fprintf(fp, "xen_kdump_p2m_create: NULL\n");
+ fprintf(fp, " line_number_hooks: NULL\n");
+ fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read);
+ fprintf(fp, " last_pmd_read: %lx\n", machdep->last_pmd_read);
+ fprintf(fp, " last_ptbl_read: %lx\n", machdep->last_ptbl_read);
+ fprintf(fp, "clear_machdep_cache: sparc64_clear_machdep_cache()\n");
+ fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd);
+ fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd);
+ fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl);
+ fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd);
+ fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits);
+ fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits);
+ fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root);
+ for (i = 0; i < MAX_MACHDEP_ARGS; i++) {
+ fprintf(fp, " cmdline_args[%d]: %s\n", i,
+ machdep->cmdline_args[i] ?
+ machdep->cmdline_args[i] : "(unused)");
+ }
+ fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec);
+ fprintf(fp, " page_offset: %lx\n",
+ machdep->machspec->page_offset);
+ fprintf(fp, " vmalloc_end: %lx\n",
+ machdep->machspec->vmalloc_end);
+}
+
+#endif /* SPARC64 */
diff --git a/symbols.c b/symbols.c
index 7a3f260..8a837f8 100644
--- a/symbols.c
+++ b/symbols.c
@@ -3398,6 +3398,11 @@ is_kernel(char *file)
goto bailout;
break;
+ case EM_SPARCV9:
+ if (machine_type_mismatch(file, "SPARC64", NULL, 0))
+ goto bailout;
+ break;
+
default:
if (machine_type_mismatch(file, "(unknown)", NULL, 0))
goto bailout;
@@ -3669,6 +3674,11 @@ is_shared_object(char *file)
if (machine_type("ARM64"))
return TRUE;
break;
+
+ case EM_SPARCV9:
+ if (machine_type("SPARC64"))
+ return TRUE;
+ break;
}
if (CRASHDEBUG(1))
diff --git a/task.c b/task.c
index 0743993..f21d0f8 100644
--- a/task.c
+++ b/task.c
@@ -2369,7 +2369,11 @@ store_context(struct task_context *tc, ulong task, char *tp)
tc->pid = (ulong)(*pid_addr);
strlcpy(tc->comm, comm_addr, TASK_COMM_LEN);
- tc->processor = *processor_addr;
+#ifdef SPARC64
+ tc->processor = *(unsigned short *)processor_addr;
+#else
+ tc->processor = *processor_addr;
+#endif
tc->ptask = *parent_addr;
tc->mm_struct = *mm_addr;
tc->task = task;
@@ -7287,6 +7291,11 @@ get_idle_threads(ulong *tasklist, int nr_cpus)
VALID_MEMBER(runqueue_idle)) {
runqbuf = GETBUF(SIZE(runqueue));
for (i = 0; i < nr_cpus; i++) {
+#ifdef SPARC64
+ if (cpu_map_addr("possible") &&
+ !(in_cpu_map(POSSIBLE, i)))
+ continue;
+#endif
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value + kt->__per_cpu_offset[i];
else
--
1.7.1