diff options
author | zi <zi@FreeBSD.org> | 2011-10-15 10:53:31 +0800 |
---|---|---|
committer | zi <zi@FreeBSD.org> | 2011-10-15 10:53:31 +0800 |
commit | dd8c67cff130ca22eef2d1d64ee1f301a6d16763 (patch) | |
tree | 075cf6b67a0fe7e30380fb94565447420b56a4d3 /sysutils/mcelog | |
parent | 912c310dc579db0d3c005b8fdbd5a30ea57d9aa3 (diff) | |
download | freebsd-ports-gnome-dd8c67cff130ca22eef2d1d64ee1f301a6d16763.tar.gz freebsd-ports-gnome-dd8c67cff130ca22eef2d1d64ee1f301a6d16763.tar.zst freebsd-ports-gnome-dd8c67cff130ca22eef2d1d64ee1f301a6d16763.zip |
New port: sysutils/mcelog
mcelog processes machine checks (in particular memory and CPU
hardware errors) on modern x86-based unix systems and
produces human-readable output.
FreeBSD conversion patches were originally written by John
Baldwin <jhb@freebsd.org> and later incorporated into this
port.
WWW: http://mcelog.org/
PR: ports/161395
Submitted by: Jeremy Chadwick <freebsd@jdc.parodius.com>
Diffstat (limited to 'sysutils/mcelog')
-rw-r--r-- | sysutils/mcelog/Makefile | 38 | ||||
-rw-r--r-- | sysutils/mcelog/distinfo | 2 | ||||
-rw-r--r-- | sysutils/mcelog/files/memstream.c | 133 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-Makefile | 48 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-cache.c | 26 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-client.c | 20 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-config.c | 34 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-eventloop.c | 38 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-intel.c | 21 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-mcelog.c | 680 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-mcelog.h | 25 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-memdb.c | 12 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-p4.c | 13 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-server.c | 79 | ||||
-rw-r--r-- | sysutils/mcelog/files/patch-tsc.c | 95 | ||||
-rw-r--r-- | sysutils/mcelog/pkg-descr | 15 | ||||
-rw-r--r-- | sysutils/mcelog/pkg-message | 7 |
17 files changed, 1286 insertions, 0 deletions
diff --git a/sysutils/mcelog/Makefile b/sysutils/mcelog/Makefile new file mode 100644 index 000000000000..84058cf05e78 --- /dev/null +++ b/sysutils/mcelog/Makefile @@ -0,0 +1,38 @@ +# New ports collection makefile for: mcelog +# Date created: 1 October 2011 +# Whom: Jeremy Chadwick <freebsd@jdc.parodius.com> +# +# $FreeBSD$ +# + +PORTNAME= mcelog +DISTVERSION= 1.0pre2 +CATEGORIES= sysutils +MASTER_SITES= ftp://ftp.kernel.org/pub/linux/utils/cpu/mce/ \ + http://147.52.159.12/mirrors/ftp.kernel.org/pub/linux/utils/cpu/mce/ + +MAINTAINER= freebsd@jdc.parodius.com +COMMENT= Collects and decodes Machine Check Exception data + +LICENSE= GPLv2 + +PATCH_STRIP= -p1 + +USE_GMAKE= yes +MAKE_ENV+= FREEBSD=1 +LDFLAGS= -lkvm + +PLIST_FILES= bin/mcelog +MAN8= mcelog.8 + +post-patch: + @${CP} ${FILESDIR}/memstream.c ${WRKSRC}/memstream.c + +do-install: + ${INSTALL_PROGRAM} ${WRKSRC}/mcelog ${PREFIX}/bin + ${INSTALL_MAN} ${WRKSRC}/mcelog.8 ${MAN8PREFIX}/man/man8 + +post-install: + @${CAT} ${PKGMESSAGE} + +.include <bsd.port.mk> diff --git a/sysutils/mcelog/distinfo b/sysutils/mcelog/distinfo new file mode 100644 index 000000000000..9da29a94e70b --- /dev/null +++ b/sysutils/mcelog/distinfo @@ -0,0 +1,2 @@ +SHA256 (mcelog-1.0pre2.tar.gz) = 39bec2a19e2548afe9dbc80f6f9dcee6664fffa7ccc142aeb5e1f8c217c1705c +SIZE (mcelog-1.0pre2.tar.gz) = 174553 diff --git a/sysutils/mcelog/files/memstream.c b/sysutils/mcelog/files/memstream.c new file mode 100644 index 000000000000..20cbf29b472c --- /dev/null +++ b/sysutils/mcelog/files/memstream.c @@ -0,0 +1,133 @@ +/* Use funopen(3) to provide open_memstream(3) like functionality. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +struct memstream { + char **cp; + size_t *lenp; + size_t offset; +}; + +FILE * +open_memstream(char **cp, size_t *lenp); + +static void +memstream_grow(struct memstream *ms, size_t newsize) +{ + char *buf; + + if (newsize > *ms->lenp) { + buf = realloc(*ms->cp, newsize + 1); + if (buf != NULL) { +#ifdef DEBUG + fprintf(stderr, "MS: %p growing from %zd to %zd\n", + ms, *ms->lenp, newsize); +#endif + memset(buf + *ms->lenp + 1, 0, newsize - *ms->lenp); + *ms->cp = buf; + *ms->lenp = newsize; + } + } +} + +static int +memstream_read(void *cookie, char *buf, int len) +{ + struct memstream *ms; + int tocopy; + + ms = cookie; + memstream_grow(ms, ms->offset + len); + tocopy = *ms->lenp - ms->offset; + if (len < tocopy) + tocopy = len; + memcpy(buf, *ms->cp + ms->offset, tocopy); + ms->offset += tocopy; +#ifdef DEBUG + fprintf(stderr, "MS: read(%p, %d) = %d\n", ms, len, tocopy); +#endif + return (tocopy); +} + +static int +memstream_write(void *cookie, const char *buf, int len) +{ + struct memstream *ms; + int tocopy; + + ms = cookie; + memstream_grow(ms, ms->offset + len); + tocopy = *ms->lenp - ms->offset; + if (len < tocopy) + tocopy = len; + memcpy(*ms->cp + ms->offset, buf, tocopy); + ms->offset += tocopy; +#ifdef DEBUG + fprintf(stderr, "MS: write(%p, %d) = %d\n", ms, len, tocopy); +#endif + return (tocopy); +} + +static fpos_t +memstream_seek(void *cookie, fpos_t pos, int whence) +{ + struct memstream *ms; +#ifdef DEBUG + size_t old; +#endif + + ms = cookie; +#ifdef DEBUG + old = ms->offset; +#endif + switch (whence) { + case SEEK_SET: + ms->offset = pos; + break; + case SEEK_CUR: + ms->offset += pos; + break; + case SEEK_END: + ms->offset = *ms->lenp + pos; + break; + } +#ifdef DEBUG + fprintf(stderr, "MS: seek(%p, %zd, %d) %zd -> %zd\n", ms, pos, whence, + old, ms->offset); +#endif + return (ms->offset); +} + +static int +memstream_close(void *cookie) +{ + + free(cookie); + return (0); +} + +FILE * +open_memstream(char **cp, size_t *lenp) +{ + struct memstream *ms; + int save_errno; + FILE *fp; + + *cp = NULL; + *lenp = 0; + ms = malloc(sizeof(*ms)); + ms->cp = cp; + ms->lenp = lenp; + ms->offset = 0; + fp = funopen(ms, memstream_read, memstream_write, memstream_seek, + memstream_close); + if (fp == NULL) { + save_errno = errno; + free(ms); + errno = save_errno; + } + return (fp); +} diff --git a/sysutils/mcelog/files/patch-Makefile b/sysutils/mcelog/files/patch-Makefile new file mode 100644 index 000000000000..96fa1ad39bdd --- /dev/null +++ b/sysutils/mcelog/files/patch-Makefile @@ -0,0 +1,48 @@ +--- ./Makefile.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./Makefile 2011-10-14 22:36:47.000000000 -0400 +@@ -1,5 +1,5 @@ + CFLAGS := -g -Os +-prefix := /usr ++prefix := /usr/local + etcprefix := + # Define appropiately for your distribution + # DOCDIR := /usr/share/doc/packages/mcelog +@@ -28,10 +28,18 @@ + + .PHONY: install clean depend + ++LIBS := + OBJ := p4.o k8.o mcelog.o dmi.o tsc.o core2.o bitfield.o intel.o \ + nehalem.o dunnington.o tulsa.o config.o memutil.o msg.o \ +- eventloop.o leaky-bucket.o memdb.o server.o trigger.o \ +- client.o cache.o sysfs.o yellow.o page.o rbtree.o ++ eventloop.o leaky-bucket.o memdb.o server.o client.o \ ++ cache.o rbtree.o ++ifndef FREEBSD ++OBJ += page.o trigger.o sysfs.o yellow.o ++endif ++ifdef FREEBSD ++OBJ += memstream.o ++LIBS += -lkvm ++endif + DISKDB_OBJ := diskdb.o dimm.o db.o + CLEAN := mcelog dmi tsc dbquery .depend .depend.X dbquery.o ${DISKDB_OBJ} + DOC := mce.pdf +@@ -47,7 +55,7 @@ + + SRC := $(OBJ:.o=.c) + +-mcelog: ${OBJ} ++mcelog: ${OBJ} ${LIBS} + + # dbquery intentionally not installed by default + install: mcelog +@@ -81,8 +89,6 @@ + .depend: ${SRC} + ${CC} -MM -I. ${SRC} > .depend.X && mv .depend.X .depend + +-include .depend +- + Makefile: .depend + + .PHONY: iccverify src test diff --git a/sysutils/mcelog/files/patch-cache.c b/sysutils/mcelog/files/patch-cache.c new file mode 100644 index 000000000000..f9ebf538b5ff --- /dev/null +++ b/sysutils/mcelog/files/patch-cache.c @@ -0,0 +1,26 @@ +--- ./cache.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./cache.c 2011-10-14 22:36:47.000000000 -0400 +@@ -27,6 +27,7 @@ + #include "sysfs.h" + #include "cache.h" + ++#ifdef __Linux__ + struct cache { + unsigned level; + /* Numerical values must match MCACOD */ +@@ -164,6 +165,15 @@ + Wprintf("Cannot find sysfs cache for CPU %d", cpu); + return -1; + } ++#endif ++ ++#ifdef __FreeBSD__ ++int cache_to_cpus(int cpu, unsigned level, unsigned type, ++ int *cpulen, unsigned **cpumap) ++{ ++ return -1; ++} ++#endif + + #ifdef TEST + main() diff --git a/sysutils/mcelog/files/patch-client.c b/sysutils/mcelog/files/patch-client.c new file mode 100644 index 000000000000..222b7ad4fbe4 --- /dev/null +++ b/sysutils/mcelog/files/patch-client.c @@ -0,0 +1,20 @@ +--- ./client.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./client.c 2011-10-14 22:36:47.000000000 -0400 +@@ -18,6 +18,7 @@ + #include <stdio.h> + #include <sys/socket.h> + #include <sys/un.h> ++#include <string.h> + #include <unistd.h> + #include "mcelog.h" + #include "client.h" +@@ -48,6 +49,9 @@ + sizeof(struct sockaddr_un)) < 0) + SYSERRprintf("client connect"); + ++#ifdef __FreeBSD__ ++ /* XXX: Need to use sendmsg() to send a SCM_CREDS control message. */ ++#endif + n = strlen(command); + if (write(fd, command, n) != n) + SYSERRprintf("client command write"); diff --git a/sysutils/mcelog/files/patch-config.c b/sysutils/mcelog/files/patch-config.c new file mode 100644 index 000000000000..d8f716f9b1bb --- /dev/null +++ b/sysutils/mcelog/files/patch-config.c @@ -0,0 +1,34 @@ +--- ./config.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./config.c 2011-10-14 22:36:47.000000000 -0400 +@@ -18,6 +18,9 @@ + Author: Andi Kleen + */ + #define _GNU_SOURCE 1 ++#ifdef __FreeBSD__ ++#include <sys/param.h> ++#endif + #include <stdio.h> + #include <string.h> + #include <ctype.h> +@@ -126,6 +129,21 @@ + return s; + } + ++#if defined(__FreeBSD__) && __FreeBSD_version < 800067 ++/* Provide a stub getline() for older versions of FreeBSD. */ ++static ssize_t getline(char **cp, size_t *lenp, FILE *f) ++{ ++ ++ if (*cp == NULL) { ++ *cp = malloc(4096); ++ *lenp = 4096; ++ } ++ if (fgets(*cp, *lenp, f) == NULL) ++ return (0); ++ return (strlen(*cp)); ++} ++#endif ++ + int parse_config_file(const char *fn) + { + FILE *f; diff --git a/sysutils/mcelog/files/patch-eventloop.c b/sysutils/mcelog/files/patch-eventloop.c new file mode 100644 index 000000000000..e9d47c4640cc --- /dev/null +++ b/sysutils/mcelog/files/patch-eventloop.c @@ -0,0 +1,38 @@ +--- ./eventloop.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./eventloop.c 2011-10-14 22:36:47.000000000 -0400 +@@ -38,7 +38,9 @@ + static struct pollfd pollfds[MAX_POLLFD]; + static struct pollcb pollcbs[MAX_POLLFD]; + ++#ifdef __Linux__ + static sigset_t event_sigs; ++#endif + + static int closeonexec(int fd) + { +@@ -97,6 +99,7 @@ + } + + /* Run signal handler only directly after event loop */ ++#ifdef __Linux__ + int event_signal(int sig) + { + static int first = 1; +@@ -111,11 +114,17 @@ + return -1; + return 0; + } ++#endif + + void eventloop(void) + { + for (;;) { ++#ifdef __Linux__ + int n = ppoll(pollfds, max_pollfd, NULL, &event_sigs); ++#endif ++#ifdef __FreeBSD__ ++ int n = poll(pollfds, max_pollfd, -1); ++#endif + if (n <= 0) { + if (n < 0 && errno != EINTR) + SYSERRprintf("poll error"); diff --git a/sysutils/mcelog/files/patch-intel.c b/sysutils/mcelog/files/patch-intel.c new file mode 100644 index 000000000000..50ba80932891 --- /dev/null +++ b/sysutils/mcelog/files/patch-intel.c @@ -0,0 +1,21 @@ +--- ./intel.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./intel.c 2011-10-14 22:36:47.000000000 -0400 +@@ -38,7 +38,7 @@ + return CPU_CORE2; + else if (model == 0x1d) + return CPU_DUNNINGTON; +- else if (model == 0x1a) ++ else if (model == 0x1a || model == 0x2c) /* Nehalem/Westmere */ + return CPU_NEHALEM; + + if (model >= 0x1a) +@@ -79,7 +79,9 @@ + corr_err_cnt = EXTRACT(m->status, 38, 52); + memory_error(m, channel, dimm, corr_err_cnt, recordlen); + ++#ifdef __Linux__ + account_page_error(m, channel, dimm, corr_err_cnt); ++#endif + + return 1; + } diff --git a/sysutils/mcelog/files/patch-mcelog.c b/sysutils/mcelog/files/patch-mcelog.c new file mode 100644 index 000000000000..b70bdc8bc416 --- /dev/null +++ b/sysutils/mcelog/files/patch-mcelog.c @@ -0,0 +1,680 @@ +--- ./mcelog.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./mcelog.c 2011-10-14 22:37:22.000000000 -0400 +@@ -20,8 +20,21 @@ + #define _GNU_SOURCE 1 + #include <sys/fcntl.h> + #include <sys/ioctl.h> ++#ifdef __Linux__ + #include <asm/types.h> + #include <asm/ioctls.h> ++#endif ++#ifdef __FreeBSD__ ++#include <sys/types.h> ++#include <sys/sysctl.h> ++#include <machine/cpufunc.h> ++#include <machine/cputypes.h> ++#include <machine/specialreg.h> ++#include <err.h> ++#include <kvm.h> ++#include <limits.h> ++#endif ++#undef CPU_P4 + #include <stdlib.h> + #include <stdio.h> + #include <string.h> +@@ -57,9 +70,25 @@ + #include "yellow.h" + #include "page.h" + ++struct mca_record { ++ uint64_t mr_status; ++ uint64_t mr_addr; ++ uint64_t mr_misc; ++ uint64_t mr_tsc; ++ int mr_apic_id; ++ int mr_bank; ++ uint64_t mr_mcg_cap; ++ uint64_t mr_mcg_status; ++ int mr_cpu_id; ++ int mr_cpu_vendor_id; ++ int mr_cpu; ++}; ++ + enum cputype cputype = CPU_GENERIC; + ++#ifdef __Linux__ + char *logfn = LOG_DEV_FILENAME; ++#endif + + int ignore_nodev; + int filter_bogus = 1; +@@ -70,12 +99,18 @@ + int dump_raw_ascii; + int daemon_mode; + static char *inputfile; ++#ifdef __Linux__ + char *processor_flags; ++#endif + static int foreground; + int filter_memory_errors; + static struct config_cred runcred = { .uid = -1U, .gid = -1U }; + static int numerrors; + static char *pidfile; ++#ifdef __FreeBSD__ ++static char *execfile; ++static char *corefile; ++#endif + + static void check_cpu(void); + +@@ -388,6 +423,7 @@ + Wprintf("\n"); + } + ++#ifdef __Linux__ + void check_cpu(void) + { + enum { +@@ -455,7 +491,44 @@ + } else + Eprintf("warning: Cannot open /proc/cpuinfo\n"); + } ++#endif ++ ++#ifdef __FreeBSD__ ++void check_cpu(void) ++{ ++ char vendor[20]; ++ u_int regs[4]; ++ u_int cpu_id; ++ int family, model; ++ static int checked; ++ ++ if (checked) ++ return; ++ checked = 1; ++ ++ do_cpuid(0, regs); ++ ((u_int *)vendor)[0] = regs[1]; ++ ((u_int *)vendor)[1] = regs[3]; ++ ((u_int *)vendor)[2] = regs[2]; ++ vendor[12] = 0; ++ ++ do_cpuid(1, regs); ++ cpu_id = regs[0]; ++ family = CPUID_TO_FAMILY(cpu_id); ++ model = CPUID_TO_MODEL(cpu_id); + ++ if (cpu_forced) ++ ; ++ else if (!strcmp(vendor,"AuthenticAMD") && ++ (family == 15 || family == 16 || family == 17)) ++ cputype = CPU_K8; ++ else if (!strcmp(vendor,"GenuineIntel")) ++ cputype = select_intel_cputype(family, model); ++ /* Add checks for other CPUs here */ ++} ++#endif ++ ++#ifdef __Linux__ + static char *skipspace(char *s) + { + while (isspace(*s)) +@@ -479,6 +552,7 @@ + } + return skipspace(s); + } ++#endif + + static void dump_mce_final(struct mce *m, char *symbol, int missing, int recordlen, + int dseen) +@@ -501,6 +575,7 @@ + if (recordlen < endof_field(struct mce, f)) \ + recordlen = endof_field(struct mce, f) + ++#ifdef __Linux__ + /* Decode ASCII input for fatal messages */ + static void decodefatal(FILE *inf) + { +@@ -646,6 +721,227 @@ + if (data) + dump_mce_final(&m, symbol, missing, recordlen, disclaimer_seen); + } ++#endif ++ ++#ifdef __FreeBSD__ ++/* ++ * Table used to map cpuid vendor strings and FreeBSD CPU vendor IDs ++ * to Linux cpuvendor values. ++ */ ++static struct { ++ char *name; ++ int vendor_id; ++ u_char cpuvendor; ++} vendor_ids[] = { ++ { "GenuineIntel", CPU_VENDOR_INTEL, 0 }, ++ { "AuthenticAMD", CPU_VENDOR_AMD, 2 }, ++ { "CentaurHauls", CPU_VENDOR_CENTAUR, 5 }, ++#ifdef __i386__ ++ { "CyrixInstead", CPU_VENDOR_CYRIX, 1 }, ++ { "UMC UMC UMC ", CPU_VENDOR_UMC, 3 }, ++ { "GenuineTMx86", CPU_VENDOR_TRANSMETA, 7 }, ++ { "Geode by NSC", CPU_VENDOR_NSC, 8 }, ++#endif ++}; ++ ++static int find_cpu_vendor(const char *vendor) ++{ ++ u_int i; ++ ++ for (i = 0; i < sizeof(vendor_ids) / sizeof(vendor_ids[0]); i++) ++ if (strcmp(vendor, vendor_ids[i].name) == 0) ++ return (vendor_ids[i].cpuvendor); ++ return (0xff); ++} ++ ++static int find_cpu_vendor_id(const char *vendor) ++{ ++ u_int i; ++ ++ for (i = 0; i < sizeof(vendor_ids) / sizeof(vendor_ids[0]); i++) ++ if (strcmp(vendor, vendor_ids[i].name) == 0) ++ return (vendor_ids[i].vendor_id); ++ return (0); ++} ++ ++static int map_cpu_vendor(int vendor_id) ++{ ++ u_int i; ++ ++ for (i = 0; i < sizeof(vendor_ids) / sizeof(vendor_ids[0]); i++) ++ if (vendor_ids[i].vendor_id == vendor_id) ++ return (vendor_ids[i].cpuvendor); ++ return (0xff); ++} ++ ++/* Convert FreeBSD's struct mca_record into a struct mce. */ ++static void convert_mca(struct mca_record *mr, struct mce *mce, int live, ++ size_t len) ++{ ++ memset(mce, 0, sizeof(*mce)); ++ mce->status = mr->mr_status; ++ mce->misc = mr->mr_misc; ++ mce->addr = mr->mr_addr; ++ mce->mcgstatus = mr->mr_mcg_status; ++ mce->tsc = mr->mr_tsc; ++ mce->cpuvendor = map_cpu_vendor(mr->mr_cpu_vendor_id); ++ mce->cpuid = mr->mr_cpu_id; ++ mce->bank = mr->mr_bank; ++ mce->finished = 1; ++ mce->extcpu = mr->mr_cpu; ++ mce->apicid = mr->mr_apic_id; ++ mce->mcgcap = mr->mr_mcg_cap; ++ ++ /* ++ * For older live records (from sysctl), fill in some fields ++ * using registers from the current CPU. ++ */ ++ if (len < offsetof(struct mca_record, mr_cpu_id) && live) { ++ char vendor[20]; ++ u_int regs[4]; ++ ++ do_cpuid(0, regs); ++ ((u_int *)vendor)[0] = regs[1]; ++ ((u_int *)vendor)[1] = regs[3]; ++ ((u_int *)vendor)[2] = regs[2]; ++ vendor[12] = 0; ++ mce->cpuvendor = find_cpu_vendor(vendor); ++ ++ do_cpuid(1, regs); ++ mce->cpuid = regs[0]; ++ } ++} ++ ++/* Decode ASCII input for fatal messages */ ++static void decodefatal(FILE *inf) ++{ ++ struct mca_record mr; ++ struct mce m; ++ long long val, val2; ++ char *cp, line[100], *s, symbol[1]; ++ const char *fmt; ++ int cpu, data, old, missing; ++ enum rows { ++ BANK = 0x1, ++ MCG = 0x2, ++ VENDOR = 0x4, ++ CPU = 0x8, ++ ADDR = 0x10, ++ MISC = 0x20, ++ }; ++ ++ symbol[0] = '\0'; ++ data = 0; ++ missing = 0; ++ old = 0; ++ memset(&mr, 0, sizeof(mr)); ++ while ((s = fgets(line, sizeof(line), inf)) != NULL) { ++ s = strstr(s, "MCA: "); ++ if (s == NULL) ++ continue; ++ s += strlen("MCA: "); ++ ++ if (strncmp(s, "bank", 4) == 0 || strncmp(s, "Bank", 4) == 0) { ++ /* Start of a new record, dump the previous one. */ ++ if (data != 0) { ++ /* Require some minimum data. */ ++ if (data & BANK) { ++ if (mr.mr_status & MC_STATUS_ADDRV && ++ !(data & ADDR)) ++ missing = 1; ++ if (mr.mr_status & MC_STATUS_MISCV && ++ !(data & MISC)) ++ missing = 1; ++ convert_mca(&mr, &m, 0, sizeof(mr)); ++ mce_cpuid(&m); ++ dump_mce_final(&m, symbol, missing, ++ sizeof(struct mce), 0); ++ } ++ data = 0; ++ missing = 0; ++ memset(&mr, 0, sizeof(mr)); ++ } ++ ++ if (s[0] == 'b') { ++ old = 1; ++ fmt = "bank %d, status 0x%llx"; ++ } else { ++ old = 0; ++ fmt = "Bank %d, Status 0x%llx"; ++ } ++ if (sscanf(s, fmt, &mr.mr_bank, &val) != 2) ++ missing = 1; ++ else { ++ data |= BANK; ++ mr.mr_status = val; ++ } ++ } ++ if (strncmp(s, "Global", 6) == 0) { ++ if (sscanf(s, "Global Cap 0x%llx, Status 0x%llx", &val, ++ &val2) != 2) ++ missing = 1; ++ else { ++ data |= MCG; ++ mr.mr_mcg_cap = val; ++ mr.mr_mcg_status = val2; ++ } ++ } ++ if (strncmp(s, "Vendor \"", 8) == 0) { ++ s += 8; ++ cp = index(s, '"'); ++ if (cp != NULL) { ++ *cp = '\0'; ++ mr.mr_cpu_vendor_id = find_cpu_vendor_id(s); ++ s = cp + 1; ++ if (sscanf(s, ", ID 0x%x, APIC ID %d", ++ &mr.mr_cpu_id, &mr.mr_apic_id) != 2) ++ missing = 1; ++ else ++ data |= VENDOR; ++ } else ++ missing = 1; ++ } ++ if (strncmp(s, "CPU", 3) == 0) { ++ if (sscanf(s, "CPU %d ", &cpu) != 1) ++ missing = 1; ++ else { ++ data |= CPU; ++ if (old) ++ mr.mr_apic_id = cpu; ++ else ++ mr.mr_cpu = cpu; ++ } ++ } ++ if (strncmp(s, "Address", 7) == 0) { ++ if (sscanf(s, "Address 0x%llx", &val) != 1) ++ missing = 1; ++ else { ++ data |= ADDR; ++ mr.mr_addr = val; ++ } ++ } ++ if (strncmp(s, "Misc", 4) == 0) { ++ if (sscanf(s, "Misc 0x%llx", &val) != 1) ++ missing = 1; ++ else { ++ data |= MISC; ++ mr.mr_misc = val; ++ } ++ } ++ } ++ ++ /* Dump the last record. */ ++ if (data & BANK) { ++ if (mr.mr_status & MC_STATUS_ADDRV && !(data & ADDR)) ++ missing = 1; ++ if (mr.mr_status & MC_STATUS_MISCV && !(data & MISC)) ++ missing = 1; ++ convert_mca(&mr, &m, 0, sizeof(mr)); ++ mce_cpuid(&m); ++ dump_mce_final(&m, symbol, missing, sizeof(struct mce), 0); ++ } ++} ++#endif + + static void remove_pidfile(void) + { +@@ -686,6 +982,10 @@ + " mcelog [options] --ascii < log\n" + " mcelog [options] --ascii --file log\n" + "Decode machine check ASCII output from kernel logs\n" ++#ifdef __FreeBSD__ ++" mcelog [options] -M vmcore -N kernel\n" ++"Decode machine check error records from kernel crashdump.\n" ++#endif + "Options:\n" + "--cpu CPU Set CPU type CPU to decode (see below for valid types)\n" + "--cpumhz MHZ Set CPU Mhz to decode time (output unreliable, not needed on new kernels)\n" +@@ -866,6 +1166,14 @@ + case O_CONFIG_FILE: + /* parsed in config.c */ + break; ++#ifdef __FreeBSD__ ++ case 'M': ++ corefile = strdup(optarg); ++ break; ++ case 'N': ++ execfile = strdup(optarg); ++ break; ++#endif + case 0: + break; + default: +@@ -900,8 +1208,10 @@ + + static void general_setup(void) + { ++#ifdef __Linux__ + trigger_setup(); + yellow_setup(); ++#endif + config_cred("global", "run-credentials", &runcred); + if (config_bool("global", "filter-memory-errors") == 1) + filter_memory_errors = 1; +@@ -924,6 +1234,7 @@ + } + } + ++#ifdef __Linux__ + static void process(int fd, unsigned recordlen, unsigned loglen, char *buf) + { + int i; +@@ -964,6 +1275,173 @@ + if (finish) + exit(0); + } ++#endif ++ ++#ifdef __FreeBSD__ ++#ifdef LOCAL_HACK ++struct mca_record_old { ++ uint64_t mr_status; ++ uint64_t mr_addr; ++ uint64_t mr_misc; ++ uint64_t mr_tsc; ++ int mr_apic_id; ++ int mr_bank; ++}; ++#endif ++ ++struct mca_record_internal { ++ struct mca_record rec; ++ int logged; ++ STAILQ_ENTRY(mca_internal) link; ++}; ++ ++#ifdef LOCAL_HACK ++struct mca_record_internal_old { ++ struct mca_record_old rec; ++ int logged; ++ STAILQ_ENTRY(mca_internal) link; ++}; ++#endif ++ ++static struct nlist nl[] = { ++#define X_MCA_RECORDS 0 ++ { .n_name = "_mca_records" }, ++#ifdef LOCAL_HACK ++#define X_SNAPDATE 1 ++ { .n_name = "_snapdate" }, ++#endif ++ { .n_name = NULL }, ++}; ++ ++static int ++kread(kvm_t *kvm, void *kvm_pointer, void *buf, size_t size, size_t offset) ++{ ++ ssize_t ret; ++ ++ ret = kvm_read(kvm, (unsigned long)kvm_pointer + offset, buf, size); ++ if (ret < 0 || (size_t)ret != size) ++ return (-1); ++ return (0); ++} ++ ++static int ++kread_symbol(kvm_t *kvm, int index, void *buf, size_t size) ++{ ++ ssize_t ret; ++ ++ ret = kvm_read(kvm, nl[index].n_value, buf, size); ++ if (ret < 0 || (size_t)ret != size) ++ return (-1); ++ return (0); ++} ++ ++static void process_kvm(const char *execfile, const char *corefile) ++{ ++ struct mca_record mr, *mrp; ++ struct mce mce; ++ char errbuf[_POSIX2_LINE_MAX]; ++ kvm_t *kvm; ++ size_t record_size, link_offset; ++ int i; ++#ifdef LOCAL_HACK ++ int snapdate; ++#endif ++ ++ kvm = kvm_openfiles(execfile, corefile, NULL, O_RDONLY, errbuf); ++ if (kvm == NULL) ++ errx(1, "kvm_openfiles: %s", errbuf); ++ if (kvm_nlist(kvm, nl) != 0) ++ errx(1, "kvm_nlist: %s", kvm_geterr(kvm)); ++ ++#ifdef LOCAL_HACK ++ if (kread_symbol(kvm, X_SNAPDATE, &snapdate, sizeof(snapdate)) < 0) ++ errx(1, "kvm_read(snapdate) failed"); ++#endif ++ /* stqh_first is the first pointer at this address. */ ++ if (kread_symbol(kvm, X_MCA_RECORDS, &mrp, sizeof(mrp)) < 0) ++ errx(1, "kvm_read(mca_records) failed"); ++#ifdef LOCAL_HACK ++ if (snapdate >= 20100329) { ++#endif ++ record_size = sizeof(struct mca_record); ++ link_offset = __offsetof(struct mca_record_internal, ++ link.stqe_next); ++#ifdef LOCAL_HACK ++ } else { ++ record_size = sizeof(struct mca_record_old); ++ link_offset = __offsetof(struct mca_record_internal_old, ++ link.stqe_next); ++ } ++#endif ++ ++ for (i = 0; mrp != NULL; i++) { ++ memset(&mr, 0, sizeof(mr)); ++ if (kread(kvm, mrp, &mr, record_size, 0) < 0) ++ break; ++ if (kread(kvm, mrp, &mrp, sizeof(mrp), link_offset) < 0) ++ mrp = NULL; ++ ++ convert_mca(&mr, &mce, 1, record_size); ++ mce_prepare(&mce); ++ if (!mce_filter(&mce, sizeof(struct mce))) ++ continue; ++ if (!dump_raw_ascii) { ++ disclaimer(); ++ Wprintf("MCE %d\n", i); ++ dump_mce(&mce, sizeof(struct mce)); ++ } else ++ dump_mce_raw_ascii(&mce, sizeof(struct mce)); ++ flushlog(); ++ } ++ ++ exit(0); ++} ++ ++static void process_live(void) ++{ ++ struct mca_record mr; ++ struct mce mce; ++ int mib[4]; ++ size_t len; ++ int count, finish, i; ++ ++ len = sizeof(count); ++ if (sysctlbyname("hw.mca.count", &count, &len, NULL, 0) < 0) ++ return; ++ ++ len = 4; ++ if (sysctlnametomib("hw.mca.records", mib, &len) < 0) ++ return; ++ ++ finish = 0; ++ for (i = 0; i < count; i++) { ++ mib[3] = i; ++ len = sizeof(mr); ++ memset(&mr, 0, sizeof(mr)); ++ if (sysctl(mib, 4, &mr, &len, NULL, 0) < 0) { ++ warn("sysctl(hw.mca.records.%d)", i); ++ continue; ++ } ++ ++ convert_mca(&mr, &mce, 1, len); ++ mce_prepare(&mce); ++ if (numerrors > 0 && --numerrors == 0) ++ finish = 1; ++ if (!mce_filter(&mce, sizeof(struct mce))) ++ continue; ++ if (!dump_raw_ascii) { ++ disclaimer(); ++ Wprintf("MCE %d\n", i); ++ dump_mce(&mce, sizeof(struct mce)); ++ } else ++ dump_mce_raw_ascii(&mce, sizeof(struct mce)); ++ flushlog(); ++ } ++ ++ if (finish) ++ exit(0); ++} ++#endif + + static void noargs(int ac, char **av) + { +@@ -1022,22 +1500,30 @@ + char *buf; + }; + ++#ifdef __Linux__ + static void process_mcefd(struct pollfd *pfd, void *data) + { + struct mcefd_data *d = (struct mcefd_data *)data; + assert((pfd->revents & POLLIN) != 0); + process(pfd->fd, d->recordlen, d->loglen, d->buf); + } ++#endif + + int main(int ac, char **av) + { ++#ifdef __Linux__ + struct mcefd_data d = {}; +- int opt; + int fd; ++#endif ++ int opt; + + parse_config(av); + +- while ((opt = getopt_long(ac, av, "", options, NULL)) != -1) { ++#ifdef __FreeBSD__ ++ while ((opt = getopt_long(ac, av, "M:N:", options, NULL)) != -1) { ++#else ++ while ((opt = getopt_long(ac, av, "", options, NULL)) != -1) { ++#endif + if (opt == '?') { + usage(); + } else if (combined_modifier(opt) > 0) { +@@ -1057,13 +1543,21 @@ + } else if (opt == 0) + break; + } ++#ifdef __Linux__ + if (av[optind]) + logfn = av[optind++]; ++#endif + if (av[optind]) + usage(); ++#ifdef __FreeBSD__ ++ if ((corefile != NULL) ^ (execfile != NULL) || ++ (corefile != NULL && daemon_mode)) ++ usage(); ++#endif + checkdmi(); + general_setup(); + ++#ifdef __Linux__ + fd = open(logfn, O_RDONLY); + if (fd < 0) { + if (ignore_nodev) +@@ -1078,24 +1572,39 @@ + err("MCE_GET_LOG_LEN"); + + d.buf = xalloc(d.recordlen * d.loglen); ++#endif + if (daemon_mode) { + check_cpu(); + prefill_memdb(); + if (!do_dmi) + closedmi(); + server_setup(); ++#ifdef __Linux__ + page_setup(); ++#endif + drop_cred(); ++#ifdef __Linux__ + register_pollcb(fd, POLLIN, process_mcefd, &d); ++#endif + if (!foreground && daemon(0, need_stdout()) < 0) + err("daemon"); + if (pidfile) + write_pidfile(); + eventloop(); + } else { ++#ifdef __Linux__ + process(fd, d.recordlen, d.loglen, d.buf); ++#endif ++#ifdef __FreeBSD__ ++ if (corefile != NULL) ++ process_kvm(execfile, corefile); ++ else ++ process_live(); ++#endif + } ++#ifdef __Linux__ + trigger_wait(); ++#endif + + exit(0); + } diff --git a/sysutils/mcelog/files/patch-mcelog.h b/sysutils/mcelog/files/patch-mcelog.h new file mode 100644 index 000000000000..7315be854410 --- /dev/null +++ b/sysutils/mcelog/files/patch-mcelog.h @@ -0,0 +1,25 @@ +--- ./mcelog.h.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./mcelog.h 2011-10-14 22:37:06.000000000 -0400 +@@ -64,9 +64,11 @@ + #define MCI_STATUS_ADDRV (1ULL<<58) /* addr reg. valid */ + #define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */ + ++#ifndef MCG_STATUS_RIPV + #define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */ + #define MCG_STATUS_EIPV (1ULL<<1) /* eip points to correct instruction */ + #define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */ ++#endif + + #define MCG_CMCI_P (1ULL<<10) /* CMCI supported */ + #define MCG_TES_P (1ULL<<11) /* Yellow bit cache threshold supported */ +@@ -89,6 +91,10 @@ + #define PRINTFLIKE + #endif + ++#if defined(__FreeBSD__) && defined(_STDIO_H_) ++FILE *open_memstream(char **cp, size_t *lenp); ++#endif ++ + int Wprintf(char *fmt, ...) PRINTFLIKE; + void Eprintf(char *fmt, ...) PRINTFLIKE; + void SYSERRprintf(char *fmt, ...) PRINTFLIKE; diff --git a/sysutils/mcelog/files/patch-memdb.c b/sysutils/mcelog/files/patch-memdb.c new file mode 100644 index 000000000000..32024fb9e1b0 --- /dev/null +++ b/sysutils/mcelog/files/patch-memdb.c @@ -0,0 +1,12 @@ +--- ./memdb.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./memdb.c 2011-10-14 22:36:47.000000000 -0400 +@@ -170,7 +170,9 @@ + asprintf(&env[ei++], "THRESHOLD_COUNT=%d", bucket->count + bucket->excess); + env[ei] = NULL; + assert(ei < MAX_ENV); ++#ifdef __Linux__ + run_trigger(bc->trigger, NULL, env); ++#endif + for (i = 0; i < ei; i++) + free(env[i]); + out: diff --git a/sysutils/mcelog/files/patch-p4.c b/sysutils/mcelog/files/patch-p4.c new file mode 100644 index 000000000000..f512eb8d6658 --- /dev/null +++ b/sysutils/mcelog/files/patch-p4.c @@ -0,0 +1,13 @@ +--- ./p4.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./p4.c 2011-10-14 22:36:47.000000000 -0400 +@@ -175,8 +175,10 @@ + Wprintf("%s CACHE %s %s Error\n", type, level, + get_RRRR_str((mca & CACHE_RRRR_MASK) >> + CACHE_RRRR_SHIFT)); ++#ifdef __Linux__ + if (track == 2) + run_yellow_trigger(cpu, typenum, levelnum, type, level, socket); ++#endif + } else if (test_prefix(10, mca)) { + if (mca == 0x400) + Wprintf("Internal Timer error\n"); diff --git a/sysutils/mcelog/files/patch-server.c b/sysutils/mcelog/files/patch-server.c new file mode 100644 index 000000000000..372265eb3282 --- /dev/null +++ b/sysutils/mcelog/files/patch-server.c @@ -0,0 +1,79 @@ +--- ./server.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./server.c 2011-10-14 22:36:47.000000000 -0400 +@@ -101,7 +101,9 @@ + + static void dispatch_pages(FILE *fh) + { ++#ifdef __Linux__ + dump_page_errors(fh); ++#endif + fprintf(fh, "done\n"); + } + +@@ -137,6 +139,7 @@ + Enomem(); + } + ++#ifdef __Linux__ + /* check if client is allowed to access */ + static int access_check(int fd, struct msghdr *msg) + { +@@ -162,6 +165,35 @@ + sendstring(fd, "permission denied\n"); + return -1; + } ++#endif ++ ++#ifdef __FreeBSD__ ++/* check if client is allowed to access */ ++static int access_check(int fd, struct msghdr *msg) ++{ ++ struct cmsghdr *cmsg; ++ struct cmsgcred *cr; ++ ++ /* check credentials */ ++ cmsg = CMSG_FIRSTHDR(msg); ++ if (cmsg == NULL || ++ cmsg->cmsg_level != SOL_SOCKET || ++ cmsg->cmsg_type != SCM_CREDS) { ++ Eprintf("Did not receive credentials over client unix socket %p\n", ++ cmsg); ++ return -1; ++ } ++ cr = (struct cmsgcred *)CMSG_DATA(cmsg); ++ if (cr->cmcred_uid == 0 || ++ (acc.uid != -1U && cr->cmcred_uid == acc.uid) || ++ (acc.gid != -1U && cr->cmcred_gid == acc.gid)) ++ return 0; ++ Eprintf("rejected client access from pid:%u uid:%u gid:%u\n", ++ cr->cmcred_pid, cr->cmcred_uid, cr->cmcred_gid); ++ sendstring(fd, "permission denied\n"); ++ return -1; ++} ++#endif + + /* retrieve commands from client */ + static int client_input(int fd, struct clientcon *cc) +@@ -242,18 +274,22 @@ + { + struct clientcon *cc = NULL; + int nfd = accept(pfd->fd, NULL, 0); ++#ifdef __Linux__ + int on; ++#endif + + if (nfd < 0) { + SYSERRprintf("accept failed on client socket"); + return; + } + ++#ifdef __Linux__ + on = 1; + if (setsockopt(nfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { + SYSERRprintf("Cannot enable credentials passing on client socket"); + goto cleanup; + } ++#endif + + cc = xalloc(sizeof(struct clientcon)); + if (register_pollcb(nfd, POLLIN, client_event, cc) < 0) { diff --git a/sysutils/mcelog/files/patch-tsc.c b/sysutils/mcelog/files/patch-tsc.c new file mode 100644 index 000000000000..c035eb9850db --- /dev/null +++ b/sysutils/mcelog/files/patch-tsc.c @@ -0,0 +1,95 @@ +--- ./tsc.c.orig 2009-12-15 07:18:40.000000000 -0500 ++++ ./tsc.c 2011-10-14 22:36:47.000000000 -0400 +@@ -15,6 +15,12 @@ + on your Linux system; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #define _GNU_SOURCE 1 ++#ifdef __FreeBSD__ ++#include <sys/types.h> ++#include <sys/sysctl.h> ++#include <machine/cpufunc.h> ++#include <machine/specialreg.h> ++#endif + #include <string.h> + #include <stdio.h> + #include <stdlib.h> +@@ -46,6 +52,7 @@ + return 0; + } + ++#ifdef __Linux__ + static double cpufreq_mhz(int cpu, double infomhz) + { + double mhz; +@@ -68,12 +75,29 @@ + fclose(f); + return mhz; + } ++#endif ++ ++#ifdef __FreeBSD__ ++static double cpufreq_mhz(int cpu, double infomhz) ++{ ++ double mhz; ++ uint64_t freq; ++ size_t len; ++ ++ len = sizeof(freq); ++ if (sysctlbyname("machdep.tsc_freq", &freq, &len, NULL, 0) < 0) ++ return infomhz; ++ mhz = freq / 1000000.0; ++ return mhz; ++} ++#endif + + int decode_tsc_forced(char **buf, double mhz, u64 tsc) + { + return fmt_tsc(buf, tsc, mhz); + } + ++#ifdef __Linux__ + static int deep_sleep_states(int cpu) + { + int ret; +@@ -132,6 +156,41 @@ + return 0; + return 1; + } ++#endif ++ ++#ifdef __FreeBSD__ ++/* Try to figure out if this CPU has a somewhat reliable TSC clock */ ++static int tsc_reliable(int cputype, int cpunum) ++{ ++ u_int regs[4]; ++ u_int cpu_id, amd_pminfo; ++ ++ if (cputype != CPU_K8 && !is_intel_cpu(cputype)) ++ return 0; ++ ++ do_cpuid(0, regs); ++ cpu_id = regs[1]; ++ do_cpuid(0x80000000, regs); ++ if (regs[0] >= 0x80000007) { ++ do_cpuid(0x80000007, regs); ++ amd_pminfo = regs[3]; ++ } else ++ amd_pminfo = 0; ++ ++ if (amd_pminfo & AMDPM_TSC_INVARIANT) ++ return 1; ++ if (is_intel_cpu(cputype)) { ++ if (CPUID_TO_FAMILY(cpu_id) >= 0x10 || ++ cpu_id == 0x60fb2) ++ return 1; ++ } else if ((CPUID_TO_FAMILY(cpu_id) == 0x6 && ++ CPUID_TO_MODEL(cpu_id) >= 0xe) || ++ (CPUID_TO_FAMILY(cpu_id) == 0xf && CPUID_TO_MODEL(cpu_id) >= 0x3)) ++ return 1; ++ ++ return 0; ++} ++#endif + + int decode_tsc_current(char **buf, int cpunum, enum cputype cputype, double mhz, + unsigned long long tsc) diff --git a/sysutils/mcelog/pkg-descr b/sysutils/mcelog/pkg-descr new file mode 100644 index 000000000000..53655b16adb8 --- /dev/null +++ b/sysutils/mcelog/pkg-descr @@ -0,0 +1,15 @@ +mcelog processes machine checks (in particular memory and CPU +hardware errors) on modern x86-based unix systems and +produces human-readable output. + +This software is heavily patched to work on FreeBSD systems, +and thus provides an extremely limited subset of features as +of this writing. The primary purpose is to provide a way to +decode MCE output from the FreeBSD kernel into something more +human-readable using the command 'mcelog --no-dmi --ascii' + +FreeBSD conversion patches were originally written by John +Baldwin <jhb@freebsd.org> and later incorporated into this +port. + +WWW: http://mcelog.org/ diff --git a/sysutils/mcelog/pkg-message b/sysutils/mcelog/pkg-message new file mode 100644 index 000000000000..80710e89cfd8 --- /dev/null +++ b/sysutils/mcelog/pkg-message @@ -0,0 +1,7 @@ +================================================= +You can decode MCE output from the FreeBSD kernel +by using the following command: + +mcelog --no-dmi --ascii --file /path/to/log + +================================================= |