mcelog-100/0000775000175000017500000000000012247144140012243 5ustar vorlonvorlonmcelog-100/nehalem.c0000664000175000017500000001302112247142742014023 0ustar vorlonvorlon/* Copyright (C) 2008 Intel Corporation Decode Intel Nehalem specific machine check errors. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author: Andi Kleen */ #include #include #include "mcelog.h" #include "nehalem.h" #include "bitfield.h" #include "memdb.h" #include "xeon75xx.h" /* See IA32 SDM Vol3B Appendix E.3.2 ff */ /* MC1_STATUS error */ static struct field qpi_status[] = { SBITFIELD(16, "QPI header had bad parity"), SBITFIELD(17, "QPI Data packet had bad parity"), SBITFIELD(18, "Number of QPI retries exceeded"), SBITFIELD(19, "Received QPI data packet that was poisoned by sender"), SBITFIELD(20, "QPI reserved 20"), SBITFIELD(21, "QPI reserved 21"), SBITFIELD(22, "QPI received unsupported message encoding"), SBITFIELD(23, "QPI credit type is not supported"), SBITFIELD(24, "Sender sent too many QPI flits to the receiver"), SBITFIELD(25, "QPI Sender sent a failed response to receiver"), SBITFIELD(26, "Clock jitter detected in internal QPI clocking"), {} }; static struct field qpi_misc[] = { SBITFIELD(14, "QPI misc reserved 14"), SBITFIELD(15, "QPI misc reserved 15"), SBITFIELD(24, "QPI Interleave/Head Indication Bit (IIB)"), {} }; static struct numfield qpi_numbers[] = { HEXNUMBER(0, 7, "QPI class and opcode of packet with error"), HEXNUMBER(8, 13, "QPI Request Transaction ID"), NUMBERFORCE(16, 18, "QPI Requestor/Home Node ID (RHNID)"), HEXNUMBER(19, 23, "QPI miscreserved 19-23"), {}, }; static struct field nhm_memory_status[] = { SBITFIELD(16, "Memory read ECC error"), SBITFIELD(17, "Memory ECC error occurred during scrub"), SBITFIELD(18, "Memory write parity error"), SBITFIELD(19, "Memory error in half of redundant memory"), SBITFIELD(20, "Memory reserved 20"), SBITFIELD(21, "Memory access out of range"), SBITFIELD(22, "Memory internal RTID invalid"), SBITFIELD(23, "Memory address parity error"), SBITFIELD(24, "Memory byte enable parity error"), {} }; static struct numfield nhm_memory_status_numbers[] = { HEXNUMBER(25, 37, "Memory MISC reserved 25..37"), NUMBERFORCE(38, 52, "Memory corrected error count (CORE_ERR_CNT)"), HEXNUMBER(53, 56, "Memory MISC reserved 53..56"), {} }; static struct numfield nhm_memory_misc_numbers[] = { HEXNUMBERFORCE(0, 7, "Memory transaction Tracker ID (RTId)"), NUMBERFORCE(16, 17, "Memory DIMM ID of error"), NUMBERFORCE(18, 19, "Memory channel ID of error"), HEXNUMBERFORCE(32, 63, "Memory ECC syndrome"), {} }; static char *internal_errors[] = { [0x0] = "No Error", [0x3] = "Reset firmware did not complete", [0x8] = "Received an invalid CMPD", [0xa] = "Invalid Power Management Request", [0xd] = "Invalid S-state transition", [0x11] = "VID controller does not match POC controller selected", [0x1a] = "MSID from POC does not match CPU MSID", }; static struct field internal_error_status[] = { FIELD(24, internal_errors), {} }; static struct numfield internal_error_numbers[] = { HEXNUMBER(16, 23, "Internal machine check status reserved 16..23"), HEXNUMBER(32, 56, "Internal machine check status reserved 32..56"), {}, }; /* Generic architectural memory controller encoding */ static char *mmm_mnemonic[] = { "GEN", "RD", "WR", "AC", "MS", "RES5", "RES6", "RES7" }; static char *mmm_desc[] = { "Generic undefined request", "Memory read error", "Memory write error", "Address/Command error", "Memory scrubbing error", "Reserved 5", "Reserved 6", "Reserved 7" }; void decode_memory_controller(u32 status) { char channel[30]; if ((status & 0xf) == 0xf) strcpy(channel, "unspecified"); else sprintf(channel, "%u", status & 0xf); Wprintf("MEMORY CONTROLLER %s_CHANNEL%s_ERR\n", mmm_mnemonic[(status >> 4) & 7], channel); Wprintf("Transaction: %s\n", mmm_desc[(status >> 4) & 7]); } void nehalem_decode_model(u64 status, u64 misc) { u32 mca = status & 0xffff; if ((mca >> 11) == 1) { /* bus and interconnect QPI */ decode_bitfield(status, qpi_status); if (status & MCI_STATUS_MISCV) { decode_numfield(misc, qpi_numbers); decode_bitfield(misc, qpi_misc); } } else if (mca == 0x0001) { /* internal unspecified */ decode_bitfield(status, internal_error_status); decode_numfield(status, internal_error_numbers); } else if ((mca >> 7) == 1) { /* memory controller */ decode_bitfield(status, nhm_memory_status); decode_numfield(status, nhm_memory_status_numbers); if (status & MCI_STATUS_MISCV) decode_numfield(misc, nhm_memory_misc_numbers); } } /* Only core errors supported. Same as Nehalem */ void xeon75xx_decode_model(struct mce *m, unsigned msize) { u64 status = m->status; u32 mca = status & 0xffff; if (mca == 0x0001) { /* internal unspecified */ decode_bitfield(status, internal_error_status); decode_numfield(status, internal_error_numbers); } xeon75xx_decode_dimm(m, msize); } /* Nehalem-EP specific DIMM decoding */ void nehalem_memerr_misc(struct mce *m, int *channel, int *dimm) { if (m->status & MCI_STATUS_MISCV) { *channel = EXTRACT(m->misc, 18, 19); *dimm = EXTRACT(m->misc, 16, 17); } } mcelog-100/msg.h0000664000175000017500000000013712247142742013211 0ustar vorlonvorlonint need_stdout(void); void flushlog(void); void reopenlog(void); /* others are in mcelog.h */ mcelog-100/README.releases0000664000175000017500000000055712247142742014742 0ustar vorlonvorlon mcelog used to do released, but now switched to a rolling release scheme. That means the git tree is always kept stable and can be used directly in production. To simplify package management which likes to have increasing version numbers the commits are regularly tagged with a number. The number starts (arbitarily) with 100. The tags are named vXXX (e.g. v100) mcelog-100/tsc.h0000664000175000017500000000027612247142742013220 0ustar vorlonvorlonenum cputype; int decode_tsc_current(char **buf, int cpunum, enum cputype cputype, double mhz, unsigned long long tsc); int decode_tsc_forced(char **buf, double mhz, __u64 tsc); mcelog-100/TODO0000664000175000017500000000016312247142742012741 0ustar vorlonvorlon - unified error output for memory errors - support replacement DIMM table - decode syndromes on K8? (from EDAC) mcelog-100/db.c0000664000175000017500000002742312247142742013012 0ustar vorlonvorlon/* Copyright (C) 2006 Andi Kleen, SuSE Labs. Dumb database manager. not suitable for large datasets, but human readable files and simple. assumes groups and entries-per-group are max low double digits. the in memory presentation could be easily optimized with a few hashes, but that shouldn't be needed for now. Note: obsolete, new design uses in memory databases only mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* TBD: add lock file to protect final rename timeout for locks */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include "db.h" #include "memutil.h" /* file format # comment [group1] entry1: value entry2: value # comment # comment2 [group2] entry: value value is anything before new line, but first will be skipped spaces are allowed in entry names or groups comments are preserved, but moved in front of the group blank lines allowed. code doesnt check for unique records/entries right now. first wins. */ struct entry { char *name; char *val; }; struct group { struct group *next; char *name; struct entry *entries; char *comment; int numentries; }; #define ENTRY_CHUNK (128 / sizeof(struct entry)) struct database { struct group *groups; FILE *fh; char *fn; int dirty; }; static int read_db(struct database *db); static FILE *open_file(char *fn, int wr); static void free_group(struct group *g); static void DBerror(char *fmt, ...) { va_list ap; va_start(ap,fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(1); } #define DB_NEW(p) ((p) = xalloc(sizeof(*(p)))) static struct group *alloc_group(char *name) { struct group *g; DB_NEW(g); g->entries = xalloc(ENTRY_CHUNK * sizeof(struct entry)); g->name = name; return g; } static char *cleanline(char *s) { char *p; while (isspace(*s)) s++; if (*s == 0) return NULL; p = strchr(s, '\n'); if (p) *p = 0; return s; } struct database *open_db(char *fn, int wr) { struct database *db; DB_NEW(db); db->fh = open_file(fn, wr); if (!db->fh) { DBerror("Cannot open database %s\n", fn); free(db); return NULL; } db->fn = xstrdup(fn); if (read_db(db) < 0) { free(db->fn); free(db); return NULL; } return db; } static int read_db(struct database *db) { char *line = NULL; size_t linesz = 0; struct group *group = NULL, **pgroup = &db->groups; int linenr = 0; while (getline(&line, &linesz, db->fh) > 0) { char *s; s = strchr(line, '#'); if (s) { struct group *cmt; DB_NEW(cmt); *pgroup = cmt; pgroup = &cmt->next; cmt->comment = xstrdup(s + 1); *s = 0; } s = cleanline(line); linenr++; if (!s) continue; if (*s == '[') { int n; char *name; ++s; n = strcspn(s, "]"); if (s[n] == 0) goto parse_error; name = xalloc(n + 1); memcpy(name, s, n); group = alloc_group(name); *pgroup = group; pgroup = &group->next; } else { char *p; if (!group) goto parse_error; p = s + strcspn(s, ":"); if (*p != ':') goto parse_error; *p++ = 0; if (*p == ' ') p++; else goto parse_error; change_entry(db, group, line, p); } } if (ferror(db->fh)) { DBerror("IO error while reading database %s: %s\n", db->fn, strerror(errno)); goto error; } free(line); return 0; parse_error: DBerror("Parse error in database %s at line %d\n", db->fn, linenr); error: free(line); return -1; } /* Crash safety strategy: While the database is opened hold a exclusive flock on the file When writing write to a temporary file (.out). Only when the file is written rename to another temporary file (.complete). Then sync and swap tmp file with main file, then sync directory (later is linux specific) During open if the main file doesn't exist and a .complete file does rename the .complete file to main first; or open the .complete file if the file system is read only. */ /* Flush directory. Useful on ext2, on journaling file systems the later fsync would usually force earlier transactions on the metadata too. */ static int flush_dir(char *fn) { int err, fd; char *p; char dir[strlen(fn) + 1]; strcpy(dir, fn); p = strrchr(dir, '/'); if (p) *p = 0; else strcpy(dir, "."); fd = open(dir, O_DIRECTORY|O_RDONLY); if (fd < 0) return -1; err = 0; if (fsync(fd) < 0) err = -1; if (close(fd) < 0) err = -1; return err; } static int force_rename(char *a, char *b) { unlink(b); /* ignore error */ return rename(a, b); } static int rewrite_db(struct database *db) { FILE *fhtmp; int err; int tmplen = strlen(db->fn) + 10; char fn_complete[tmplen], fn_old[tmplen], fn_out[tmplen]; sprintf(fn_complete, "%s.complete", db->fn); sprintf(fn_old, "%s~", db->fn); sprintf(fn_out, "%s.out", db->fn); fhtmp = fopen(fn_out, "w"); if (!fhtmp) { DBerror("Cannot open `%s' output file: %s\n", fn_out, strerror(errno)); return -1; } dump_database(db, fhtmp); err = 0; /* Finish the output file */ if (ferror(fhtmp) || fflush(fhtmp) != 0 || fsync(fileno(fhtmp)) != 0 || fclose(fhtmp)) err = -1; /* Rename to .complete */ else if (force_rename(fn_out, fn_complete)) err = -1; /* RED-PEN: need to do retry for race */ /* Move to final name */ else if (force_rename(db->fn, fn_old) || rename(fn_complete, db->fn)) err = -1; /* Hit disk */ else if (flush_dir(db->fn)) err = -1; if (err) { DBerror("Error writing to database %s: %s\n", db->fn, strerror(errno)); } return err; } int sync_db(struct database *db) { if (!db->dirty) return 0; /* RED-PEN window without lock */ if (rewrite_db(db)) return -1; fclose(db->fh); db->dirty = 0; db->fh = open_file(db->fn, 1); if (!db->fh) return -1; return 0; } static void free_group(struct group *g) { free(g->entries); free(g->name); free(g->comment); free(g); } static void free_data(struct database *db) { struct group *g, *gnext; for (g = db->groups; g; g = gnext) { gnext = g->next; free_group(g); } } int close_db(struct database *db) { if (db->dirty && rewrite_db(db)) return -1; if (fclose(db->fh)) return -1; free_data(db); free(db->fn); free(db); return 0; } static FILE *open_file(char *fn, int wr) { char tmp[strlen(fn) + 10]; FILE *fh; if (access(fn, wr ? (R_OK|W_OK) : R_OK)) { switch (errno) { case EROFS: wr = 0; break; case ENOENT: /* No main DB file */ sprintf(tmp, "%s.complete", fn); /* Handle race */ if (!access(tmp, R_OK)) { if (rename(tmp, fn) < 0 && errno == EEXIST) return open_file(fn, wr); } else creat(fn, 0644); break; } } fh = fopen(fn, wr ? "r+" : "r"); if (fh) { if (flock(fileno(fh), wr ? LOCK_EX : LOCK_SH) < 0) { fclose(fh); return NULL; } } return fh; } void dump_group(struct group *g, FILE *out) { struct entry *e; fprintf(out, "[%s]\n", g->name); for (e = &g->entries[0]; e->name && !ferror(out); e++) fprintf(out, "%s: %s\n", e->name, e->val); } void dump_database(struct database *db, FILE *out) { struct group *g; for (g = db->groups; g && !ferror(out); g = g->next) { if (g->comment) { fprintf(out, "#%s", g->comment); continue; } dump_group(g, out); } } struct group *find_group(struct database *db, char *name) { struct group *g; for (g = db->groups; g; g = g->next) if (g->name && !strcmp(g->name, name)) return g; return NULL; } int delete_group(struct database *db, struct group *group) { struct group *g, **gprev; gprev = &db->groups; for (g = *gprev; g; gprev = &g->next, g = g->next) { if (g == group) { *gprev = g->next; free_group(g); return 0; } } db->dirty = 1; return -1; } char *entry_val(struct group *g, char *entry) { struct entry *e; for (e = &g->entries[0]; e->name; e++) if (!strcmp(e->name, entry)) return e->val; return NULL; } struct group *add_group(struct database *db, char *name, int *existed) { struct group *g, **gprev = &db->groups; for (g = *gprev; g; gprev = &g->next, g = g->next) if (g->name && !strcmp(g->name, name)) break; if (existed) *existed = (g != NULL); if (!g) { g = alloc_group(xstrdup(name)); g->next = *gprev; *gprev = g; } db->dirty = 1; return g; } void change_entry(struct database *db, struct group *g, char *entry, char *newval) { int i; struct entry *e, *entries; db->dirty = 1; entries = &g->entries[0]; for (e = entries; e->name; e++) { if (!strcmp(e->name, entry)) { free(e->val); e->val = xstrdup(newval); return; } } i = e - entries; assert(i == g->numentries); if (i > 0 && (i % ENTRY_CHUNK) == 0) { int new = (i + ENTRY_CHUNK) * sizeof(struct entry); g->entries = xrealloc(g->entries, new); } entries = &g->entries[0]; e = &entries[i]; e->name = xstrdup(entry); e->val = xstrdup(newval); g->numentries++; } void delete_entry(struct database *db, struct group *g, char *entry) { struct entry *e; for (e = &g->entries[0]; e->name; e++) if (!strcmp(e->name, entry)) break; if (e->name == NULL) return; while ((++e)->name) e[-1] = e[0]; g->numentries--; } struct group * clone_group(struct database *db, struct group *gold, char *newname) { struct entry *e; struct group *gnew = add_group(db, newname, NULL); for (e = &gold->entries[0]; e->name; e++) change_entry(db, gnew, e->name, e->val); return gnew; } static char *save_comment(char *c) { int len = strlen(c); char *s = xalloc(len + 2); strcpy(s, c); if (len == 0 || c[len - 1] != '\n') s[len] = '\n'; return s; } void add_comment(struct database *db, struct group *group, char *comment) { struct group *g; struct group **gprev = &db->groups; for (g = *gprev; g; gprev = &g->next, g = g->next) { if ((group && g == group) || (!group && g->comment == NULL)) break; } DB_NEW(g); g->comment = save_comment(comment); g->next = *gprev; *gprev = g; db->dirty = 1; } struct group *first_group(struct database *db) { return next_group(db->groups); } struct group *next_group(struct group *g) { struct group *n; if (!g) return NULL; n = g->next; while (n && n->comment) n = n->next; return n; } char *group_name(struct group *g) { return g->name; } struct group *find_entry(struct database *db, struct group *prev, char *entry, char *value) { int previ = 0; struct entry *e; struct group *g; if (prev) g = prev->next; else g = db->groups; for (; g; g = g->next) { if (g->comment) continue; /* Short cut when entry is at the same place as previous */ if (previ < g->numentries) { e = &g->entries[previ]; if (!strcmp(e->name, entry)) { if (!strcmp(e->val, value)) return g; continue; } } for (e = &g->entries[0]; e->name; e++) { if (strcmp(e->name, entry)) continue; if (!strcmp(e->val, value)) return g; previ = e - &g->entries[0]; break; } } return NULL; } void rename_group(struct database *db, struct group *g, char *newname) { free(g->name); g->name = xstrdup(newname); db->dirty = 1; } unsigned long entry_num(struct group *g, char *entry) { char *e = entry_val(g, entry); unsigned long val = 0; if (e) sscanf(e, "%lu", &val); return val; } void change_entry_num(struct database *db, struct group *g, char *entry, unsigned long val) { char buf[20]; sprintf(buf, "%lu", val); change_entry(db, g, entry, buf); } mcelog-100/dunnington.c0000664000175000017500000000643212247142742014605 0ustar vorlonvorlon/* Copyright (c) 2008 by Intel Corp. Decode Intel Xeon Processor 7400 Model (Dunnington) specific MCEs mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author: Andi Kleen */ #include #include "mcelog.h" #include "bitfield.h" #include "dunnington.h" /* Follows Intel IA32 SDM 3b Appendix E.2.1 ++ */ static struct field dunnington_bus_status[] = { SBITFIELD(16, "Parity error detected during FSB request phase"), FIELD(17, reserved_3bits), SBITFIELD(20, "Hard Failure response received for a local transaction"), SBITFIELD(21, "Parity error on FSB response field detected"), SBITFIELD(22, "Parity data error on inbound data detected"), FIELD(23, reserved_3bits), FIELD(25, reserved_3bits), FIELD(28, reserved_3bits), FIELD(31, reserved_1bit), {} }; static char *dnt_front_error[0xf] = { [0x1] = "Inclusion error from core 0", [0x2] = "Inclusion error from core 1", [0x3] = "Write Exclusive error from core 0", [0x4] = "Write Exclusive error from core 1", [0x5] = "Inclusion error from FSB", [0x6] = "SNP stall error from FSB", [0x7] = "Write stall error from FSB", [0x8] = "FSB Arbiter Timeout error", [0xA] = "Inclusion error from core 2", [0xB] = "Write exclusive error from core 2", }; static char *dnt_int_error[0xf] = { [0x2] = "Internal timeout error", [0x3] = "Internal timeout error", [0x4] = "Intel Cache Safe Technology Queue full error\n" "or disabled ways in a set overflow", [0x5] = "Quiet cycle timeout error (correctable)", }; struct field dnt_int_status[] = { FIELD(8, dnt_int_error), {} }; struct field dnt_front_status[] = { FIELD(0, dnt_front_error), {} }; struct field dnt_cecc[] = { SBITFIELD(1, "Correctable ECC event on outgoing core 0 data"), SBITFIELD(2, "Correctable ECC event on outgoing core 1 data"), SBITFIELD(3, "Correctable ECC event on outgoing core 2 data"), {} }; struct field dnt_uecc[] = { SBITFIELD(1, "Uncorrectable ECC event on outgoing core 0 data"), SBITFIELD(2, "Uncorrectable ECC event on outgoing core 1 data"), SBITFIELD(3, "Uncorrectable ECC event on outgoing core 2 data"), {} }; static void dunnington_decode_bus(u64 status) { decode_bitfield(status, dunnington_bus_status); } static void dunnington_decode_internal(u64 status) { u32 mca = (status >> 16) & 0xffff; if ((mca & 0xfff0) == 0) decode_bitfield(mca, dnt_front_status); else if ((mca & 0xf0ff) == 0) decode_bitfield(mca, dnt_int_status); else if ((mca & 0xfff0) == 0xc000) decode_bitfield(mca, dnt_cecc); else if ((mca & 0xfff0) == 0xe000) decode_bitfield(mca, dnt_uecc); } void dunnington_decode_model(u64 status) { if ((status & 0xffff) == 0xe0f) dunnington_decode_bus(status); else if ((status & 0xffff) == (1 << 10)) dunnington_decode_internal(status); } mcelog-100/p4.c0000664000175000017500000002275612247142742012754 0ustar vorlonvorlon/* Copyright (c) 2005 by Intel Corp. Decode Intel machine check (generic and P4 specific) mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Authors: Racing Guo Andi Kleen */ #include #include #include "mcelog.h" #include "p4.h" #include "core2.h" #include "nehalem.h" #include "dunnington.h" #include "tulsa.h" #include "intel.h" #include "yellow.h" #include "bitfield.h" #include "sandy-bridge.h" #include "ivy-bridge.h" /* decode mce for P4/Xeon and Core2 family */ static char* get_TT_str(__u8 t) { static char* TT[] = {"Instruction", "Data", "Generic", "Unknown"}; if (t >= NELE(TT)) { return "UNKNOWN"; } return TT[t]; } static char* get_LL_str(__u8 ll) { static char* LL[] = {"Level-0", "Level-1", "Level-2", "Level-3"}; if (ll > NELE(LL)) { return "UNKNOWN"; } return LL[ll]; } static char* get_RRRR_str(__u8 rrrr) { static struct { __u8 value; char* str; } RRRR [] = { {0, "Generic"}, {1, "Read"}, {2, "Write" }, {3, "Data-Read"}, {4, "Data-Write"}, {5, "Instruction-Fetch"}, {6, "Prefetch"}, {7, "Eviction"}, {8, "Snoop"} }; unsigned i; for (i = 0; i < (int)NELE(RRRR); i++) { if (RRRR[i].value == rrrr) { return RRRR[i].str; } } return "UNKNOWN"; } static char* get_PP_str(__u8 pp) { static char* PP[] = { "Local-CPU-originated-request", "Responed-to-request", "Observed-error-as-third-party", "Generic" }; if (pp >= NELE(PP)) { return "UNKNOWN"; } return PP[pp]; } static char* get_T_str(__u8 t) { static char* T[] = {"Request-did-not-timeout", "Request-timed-out"}; if (t >= NELE(T)) { return "UNKNOWN"; } return T[t]; } static char* get_II_str(__u8 i) { static char* II[] = {"Memory-access", "Reserved", "IO", "Other-transaction"}; if (i >= NELE(II)) { return "UNKNOWN"; } return II[i]; } static void decode_mca(__u32 mca, u64 track, int cpu, int *ismemerr, int socket) { #define TLB_LL_MASK 0x3 /*bit 0, bit 1*/ #define TLB_LL_SHIFT 0x0 #define TLB_TT_MASK 0xc /*bit 2, bit 3*/ #define TLB_TT_SHIFT 0x2 #define CACHE_LL_MASK 0x3 /*bit 0, bit 1*/ #define CACHE_LL_SHIFT 0x0 #define CACHE_TT_MASK 0xc /*bit 2, bit 3*/ #define CACHE_TT_SHIFT 0x2 #define CACHE_RRRR_MASK 0xF0 /*bit 4, bit 5, bit 6, bit 7 */ #define CACHE_RRRR_SHIFT 0x4 #define BUS_LL_MASK 0x3 /* bit 0, bit 1*/ #define BUS_LL_SHIFT 0x0 #define BUS_II_MASK 0xc /*bit 2, bit 3*/ #define BUS_II_SHIFT 0x2 #define BUS_RRRR_MASK 0xF0 /*bit 4, bit 5, bit 6, bit 7 */ #define BUS_RRRR_SHIFT 0x4 #define BUS_T_MASK 0x100 /*bit 8*/ #define BUS_T_SHIFT 0x8 #define BUS_PP_MASK 0x600 /*bit 9, bit 10*/ #define BUS_PP_SHIFT 0x9 static char *msg[] = { [0] = "No Error", [1] = "Unclassified", [2] = "Microcode ROM parity error", [3] = "External error", [4] = "FRC error", [5] = "Internal parity error", }; if (mca & (1UL << 12)) { Wprintf("corrected filtering (some unreported errors in same region)\n"); mca &= ~(1UL << 12); } if (mca < NELE(msg)) { Wprintf("%s\n", msg[mca]); return; } if ((mca >> 2) == 3) { Wprintf("%s Generic memory hierarchy error\n", get_LL_str(mca & 3)); } else if (test_prefix(4, mca)) { Wprintf("%s TLB %s Error\n", get_TT_str((mca & TLB_TT_MASK) >> TLB_TT_SHIFT), get_LL_str((mca & TLB_LL_MASK) >> TLB_LL_SHIFT)); } else if (test_prefix(8, mca)) { unsigned typenum = (mca & CACHE_TT_MASK) >> CACHE_TT_SHIFT; unsigned levelnum = (mca & CACHE_LL_MASK) >> CACHE_LL_SHIFT; char *type = get_TT_str(typenum); char *level = get_LL_str(levelnum); Wprintf("%s CACHE %s %s Error\n", type, level, get_RRRR_str((mca & CACHE_RRRR_MASK) >> CACHE_RRRR_SHIFT)); if (track == 2) run_yellow_trigger(cpu, typenum, levelnum, type, level, socket); } else if (test_prefix(10, mca)) { if (mca == 0x400) Wprintf("Internal Timer error\n"); else Wprintf("Internal unclassified error: %x\n", mca & 0xffff); } else if (test_prefix(11, mca)) { Wprintf("BUS %s %s %s %s %s Error\n", get_LL_str((mca & BUS_LL_MASK) >> BUS_LL_SHIFT), get_PP_str((mca & BUS_PP_MASK) >> BUS_PP_SHIFT), get_RRRR_str((mca & BUS_RRRR_MASK) >> BUS_RRRR_SHIFT), get_II_str((mca & BUS_II_MASK) >> BUS_II_SHIFT), get_T_str((mca & BUS_T_MASK) >> BUS_T_SHIFT)); } else if (test_prefix(7, mca)) { decode_memory_controller(mca); *ismemerr = 1; } else Wprintf("Unknown Error %x\n", mca); } static void p4_decode_model(__u32 model) { static struct { int value; char *str; }MD []= { {16, "FSB address parity"}, {17, "Response hard fail"}, {18, "Response parity"}, {19, "PIC and FSB data parity"}, {20, "Invalid PIC request(Signature=0xF04H)"}, {21, "Pad state machine"}, {22, "Pad strobe glitch"}, {23, "Pad address glitch"} }; unsigned i; Wprintf("Model:"); for (i = 0; i < NELE(MD); i++) { if (model & (1 << MD[i].value)) Wprintf("%s\n",MD[i].str); } Wprintf("\n"); } static void decode_tracking(u64 track) { static char *msg[] = { [1] = "green", [2] = "yellow\n" "Large number of corrected cache errors. System operating, but might lead\n" "to uncorrected errors soon", [3] ="res3" }; if (track) { Wprintf("Threshold based error status: %s\n", msg[track]); } } static const char *arstate[4] = { [0] = "UCNA", [1] = "AR", [2] = "SRAO", [3] = "SRAR" }; static void decode_mci(__u64 status, int cpu, unsigned mcgcap, int *ismemerr, int socket) { u64 track = 0; Wprintf("MCi status:\n"); if (!(status & MCI_STATUS_VAL)) Wprintf("Machine check not valid\n"); if (status & MCI_STATUS_OVER) Wprintf("Error overflow\n"); if (status & MCI_STATUS_UC) Wprintf("Uncorrected error\n"); else Wprintf("Corrected error\n"); if (status & MCI_STATUS_EN) Wprintf("Error enabled\n"); if (status & MCI_STATUS_MISCV) Wprintf("MCi_MISC register valid\n"); if (status & MCI_STATUS_ADDRV) Wprintf("MCi_ADDR register valid\n"); if (status & MCI_STATUS_PCC) Wprintf("Processor context corrupt\n"); if (status & (MCI_STATUS_S|MCI_STATUS_AR)) Wprintf("%s\n", arstate[(status >> 55) & 3]); if ((mcgcap == 0 || (mcgcap & MCG_TES_P)) && !(status & MCI_STATUS_UC)) { track = (status >> 53) & 3; decode_tracking(track); } Wprintf("MCA: "); decode_mca(status & 0xffffL, track, cpu, ismemerr, socket); } static void decode_mcg(__u64 mcgstatus) { Wprintf("MCG status:"); if (mcgstatus & MCG_STATUS_RIPV) Wprintf("RIPV "); if (mcgstatus & MCG_STATUS_EIPV) Wprintf("EIPV "); if (mcgstatus & MCG_STATUS_MCIP) Wprintf("MCIP "); Wprintf("\n"); } static void decode_thermal(struct mce *log, int cpu) { if (log->status & 1) { Gprintf( "Processor %d heated above trip temperature. Throttling enabled.\n", cpu); Gprintf( "Please check your system cooling. Performance will be impacted\n"); } else { Gprintf("Processor %d below trip temperature. Throttling disabled\n", cpu); } } void decode_intel_mc(struct mce *log, int cputype, int *ismemerr, unsigned size) { int socket = size > offsetof(struct mce, socketid) ? (int)log->socketid : -1; int cpu = log->extcpu ? log->extcpu : log->cpu; if (log->bank == MCE_THERMAL_BANK) { decode_thermal(log, cpu); return; } decode_mcg(log->mcgstatus); decode_mci(log->status, cpu, log->mcgcap, ismemerr, socket); if (test_prefix(11, (log->status & 0xffffL))) { switch (cputype) { case CPU_P6OLD: p6old_decode_model(log->status); break; case CPU_DUNNINGTON: case CPU_CORE2: core2_decode_model(log->status); break; case CPU_TULSA: case CPU_P4: p4_decode_model(log->status & 0xffff0000L); break; case CPU_NEHALEM: case CPU_XEON75XX: core2_decode_model(log->status); break; } } /* Model specific addon information */ switch (cputype) { case CPU_NEHALEM: nehalem_decode_model(log->status, log->misc); break; case CPU_DUNNINGTON: dunnington_decode_model(log->status); break; case CPU_TULSA: tulsa_decode_model(log->status, log->misc); break; case CPU_XEON75XX: xeon75xx_decode_model(log, size); break; case CPU_SANDY_BRIDGE: case CPU_SANDY_BRIDGE_EP: snb_decode_model(cputype, log->bank, log->status, log->misc); break; case CPU_IVY_BRIDGE_EPEX: ivb_decode_model(cputype, log->bank, log->status, log->misc); break; } /* IO MCA - reported as bus/interconnect with specific PP,T,RRRR,II,LL values * and MISCV set. MISC register points to root port that reported the error * need to cross check with AER logs for more details. * See: http://www.intel.com/content/www/us/en/architecture-and-technology/enhanced-mca-logging-xeon-paper.html */ if ((log->status & MCI_STATUS_MISCV) && (log->status & 0xefff) == 0x0e0b) { int seg, bus, dev, fn; seg = EXTRACT(log->misc, 32, 39); bus = EXTRACT(log->misc, 24, 31); dev = EXTRACT(log->misc, 19, 23); fn = EXTRACT(log->misc, 16, 18); Wprintf("IO MCA reported by root port %x:%02x:%02x.%x\n", seg, bus, dev, fn); } } char *intel_bank_name(int num) { static char bname[64]; sprintf(bname, "BANK %d", num); return bname; } mcelog-100/sandy-bridge.h0000664000175000017500000000021612247142742014771 0ustar vorlonvorlonvoid snb_decode_model(int cputype, int bank, u64 status, u64 misc); void sandy_bridge_ep_memerr_misc(struct mce *m, int *channel, int *dimm); mcelog-100/bitfield.h0000664000175000017500000000176012247142742014210 0ustar vorlonvorlon/* Generic bitfield decoder */ struct field { unsigned start_bit; char **str; unsigned stringlen; }; struct numfield { unsigned start, end; char *name; char *fmt; int force; }; #define FIELD(start_bit, name) { start_bit, name, NELE(name) } #define SBITFIELD(start_bit, string) { start_bit, ((char * [2]) { NULL, string }), 2 } #define NUMBER(start, end, name) { start, end, name, "%Lu", 0 } #define NUMBERFORCE(start, end, name) { start, end, name, "%Lu", 1 } #define HEXNUMBER(start, end, name) { start, end, name, "%Lx", 0 } #define HEXNUMBERFORCE(start, end, name) { start, end, name, "%Lx", 1 } void decode_bitfield(u64 status, struct field *fields); void decode_numfield(u64 status, struct numfield *fields); extern char *reserved_3bits[8]; extern char *reserved_1bit[2]; extern char *reserved_2bits[4]; #define MASK(x) ((1ULL << (1 + (x))) - 1) #define EXTRACT(v, a, b) (((v) >> (a)) & MASK((b)-(a))) static inline int test_prefix(int nr, __u32 value) { return ((value >> nr) == 1); } mcelog-100/tulsa.c0000664000175000017500000001026112247142742013545 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Decode Intel Xeon MP 7100 series (Tulsa) specific machine check errors. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author: Andi Kleen */ #include #include #include "mcelog.h" #include "bitfield.h" #include "tulsa.h" /* See IA32 SDM Vol3B Appendix E.4.1 ff */ static struct numfield corr_numbers[] = { NUMBER(32, 39, "Corrected events"), {} }; static struct numfield ecc_numbers[] = { HEXNUMBER(44, 51, "ECC syndrome"), {}, }; static struct field tls_bus_status[] = { SBITFIELD(16, "Parity error detected during FSB request phase"), SBITFIELD(17, "Partity error detected on Core 0 request's address field"), SBITFIELD(18, "Partity error detected on Core 1 request's address field"), FIELD(19, reserved_1bit), SBITFIELD(20, "Parity error on FSB response field detected"), SBITFIELD(21, "FSB data parity error on inbound date detected"), SBITFIELD(22, "Data parity error on data received from Core 0 detected"), SBITFIELD(23, "Data parity error on data received from Core 1 detected"), SBITFIELD(24, "Detected an Enhanced Defer parity error phase A or phase B"), SBITFIELD(25, "Data ECC event to error on inbound data correctable or uncorrectable"), SBITFIELD(26, "Pad logic detected a data strobe glitch or sequencing error"), SBITFIELD(27, "Pad logic detected a request strobe glitch or sequencing error"), FIELD(28, reserved_3bits), FIELD(31, reserved_1bit), {} }; static char *tls_front_error[0xf] = { [0x1] = "Inclusion error from core 0", [0x2] = "Inclusion error from core 1", [0x3] = "Write Exclusive error from core 0", [0x4] = "Write Exclusive error from core 1", [0x5] = "Inclusion error from FSB", [0x6] = "SNP stall error from FSB", [0x7] = "Write stall error from FSB", [0x8] = "FSB Arbiter Timeout error", [0x9] = "CBC OOD Queue Underflow/overflow", }; static char *tls_int_error[0xf] = { [0x1] = "Enhanced Intel SpeedStep Technology TM1-TM2 Error", [0x2] = "Internal timeout error", [0x3] = "Internal timeout error", [0x4] = "Intel Cache Safe Technology Queue full error\n" "or disabled ways in a set overflow", }; struct field tls_int_status[] = { FIELD(8, tls_int_error), {} }; struct field tls_front_status[] = { FIELD(0, tls_front_error), {} }; struct field tls_cecc[] = { SBITFIELD(0, "Correctable ECC event on outgoing FSB data"), SBITFIELD(1, "Correctable ECC event on outgoing core 0 data"), SBITFIELD(2, "Correctable ECC event on outgoing core 1 data"), {} }; struct field tls_uecc[] = { SBITFIELD(0, "Uncorrectable ECC event on outgoing FSB data"), SBITFIELD(1, "Uncorrectable ECC event on outgoing core 0 data"), SBITFIELD(2, "Uncorrectable ECC event on outgoing core 1 data"), {} }; static void tulsa_decode_bus(u64 status) { decode_bitfield(status, tls_bus_status); } static void tulsa_decode_internal(u64 status) { u32 mca = (status >> 16) & 0xffff; if ((mca & 0xfff0) == 0) decode_bitfield(mca, tls_front_status); else if ((mca & 0xf0ff) == 0) decode_bitfield(mca, tls_int_status); else if ((mca & 0xfff0) == 0xc000) decode_bitfield(mca, tls_cecc); else if ((mca & 0xfff0) == 0xe000) decode_bitfield(mca, tls_uecc); } void tulsa_decode_model(u64 status, u64 misc) { decode_numfield(status, corr_numbers); if (status & (1ULL << 52)) decode_numfield(status, ecc_numbers); /* MISC register not documented in the SDM. Let's just dump hex for now. */ if (status & MCI_STATUS_MISCV) Wprintf("MISC format %llx value %llx\n", (status >> 40) & 3, misc); if ((status & 0xffff) == 0xe0f) tulsa_decode_bus(status); else if ((status & 0xffff) == (1 << 10)) tulsa_decode_internal(status); } mcelog-100/cache.h0000664000175000017500000000014012247142742013460 0ustar vorlonvorlonint cache_to_cpus(int cpu, unsigned level, unsigned type, int *cpulen, unsigned **cpumap); mcelog-100/dimm.c0000664000175000017500000002433512247142742013352 0ustar vorlonvorlon/* Copyright (C) 2006 Andi Kleen, SuSE Labs. Manage dimm database. this is used to keep track of the error counts per DIMM so that we can take action when one starts to experience a unusual large number of them. Note: obsolete, not used anymore, new design is in memdb.c mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* TBD: Put error trigger information into database? */ #include #include #include #include #include #include #include #include "dmi.h" #include "mcelog.h" #include "db.h" #include "dimm.h" /* the algorithms are mostly brute force, only the generally small number of dimms saves us. advantage it is a quite simple and straight forward. */ struct database *dimm_db; struct key { char *name; size_t offset; enum { D_STR, D_BYTE, D_WORD, D_SIZE } type; int cmp; }; static unsigned key_sizes[] = { [D_STR] = 1, [D_BYTE] = 1, [D_WORD] = 2, [D_SIZE] = 2, }; #define O(x) offsetof(struct dmi_memdev, x) static struct key keys[] = { { "Locator", O(device_locator), D_STR, 0 }, { "Manufacturer", O(manufacturer), D_STR, 1}, { "Serial Number", O(serial_number),D_STR, 1}, { "Part Number", O(part_number), D_STR,10}, { "Asset Tag", O(asset_tag), D_STR, 0}, { "Speed", O(speed), D_WORD, 0}, { "Size", O(speed), D_SIZE, 1}, { "Form Factor", O(form_factor), D_STR, 0 }, { "Type Details", O(type_details), D_WORD, 1 }, { "Memory Type", O(memory_type), D_BYTE, 1 }, { "Total Width", O(total_width), D_WORD, 0 }, { "Data Width", O(data_width), D_WORD, 0 }, { "Device Set", O(device_set), D_BYTE, 0 }, { "Handle", O(header.handle), D_WORD, 0 }, { "Bank Locator", O(bank_locator), D_STR, 0 }, { "Array Handle", O(array_handle), D_WORD, 0 }, { }, }; static void fmt_size(struct dmi_memdev *a, char *buf) { char *unit; unit = buf + sprintf(buf, "%u ", a->size); dmi_dimm_size(a->size, unit); *++unit = 0; } static char *d_string(struct dmi_memdev *d, struct key *k, char *buf) { unsigned char *p; if (k->offset + key_sizes[k->type] > d->header.length) return NULL; p = (unsigned char *)d + k->offset; switch (k->type) { case D_BYTE: sprintf(buf, "%u", *p); break; case D_WORD: sprintf(buf, "%u", *(unsigned short *)p); break; case D_STR: return dmi_getstring(&d->header, *p); case D_SIZE: fmt_size(d, buf); break; default: abort(); } return buf; } static int cmp_dimm(struct dmi_memdev *a, struct group *b) { int i; for (i = 0; keys[i].name; i++) { char buf[100]; struct key *k = &keys[i]; if (!k->cmp) continue; char *s = d_string(a, k, buf); if (!s) continue; char *s2 = entry_val(b, k->name); if (!s2) continue; if (strcmp(s, s2)) return 0; } return 1; } static void d_to_group(struct dmi_memdev *de, struct group *g) { char buf[100]; int i; for (i = 0; keys[i].name; i++) { struct key *k = &keys[i]; char *s = d_string(de, k, buf); if (s) change_entry(dimm_db, g, k->name, s); } } /* TBD get this into syslog somehow without spamming? */ static void unique_warning(void) { static int warned; if (warned) return; warned = 1; Wprintf("Cannot uniquely identify your memory modules\n"); Wprintf("When changing them you should manage them using command line mcelog\n"); } static struct dmi_memdev *matching_dimm_group(struct group *g) { int i; struct dmi_memdev *match = NULL; int nmatch = 0; for (i = 0; dmi_dimms[i]; i++) { if (cmp_dimm(dmi_dimms[i], g)) { match = dmi_dimms[i]; nmatch++; } } if (nmatch > 1) { unique_warning(); return NULL; } return match; } static struct group *matching_dimm_dmi(struct dmi_memdev *d) { struct group *match = NULL, *g; int nmatch = 0; for (g = first_group(dimm_db); g; g = next_group(g)) { if (!cmp_dimm(d, g)) { match = g; nmatch++; } } if (nmatch > 1) { unique_warning(); return NULL; } return match; } void create_dimm_name(struct dmi_memdev *d, char *buf) { int i = 1; do { sprintf(buf, "dimm%d", i++); } while (find_group(dimm_db, buf)); } static char *timestamp(void) { static char buf[20]; time_t now; time(&now); sprintf(buf, "%lu", now); return buf; } static void remove_dimm(struct group *g) { char *loc = entry_val(g, "Locator"); Wprintf("Removing %s who was at %s\n", group_name(g), loc); change_entry(dimm_db, g, "old locator", loc); change_entry(dimm_db, g, "Locator", "removed"); change_entry(dimm_db, g, "removed at", timestamp()); } static void disable_leftover_dimms(void) { int i; struct group *g; /* Disable any left over dimms in the database. don't remove them because the information might be still useful later */ for (g = first_group(dimm_db); g; g = next_group(g)) { char *gloc = entry_val(g, "Locator"); if (!gloc || !strcmp(gloc, "removed")) continue; for (i = 0; dmi_dimms[i]; i++) { struct dmi_memdev *d = dmi_dimms[i]; char *loc = dmi_getstring(&d->header, d->device_locator); if (!strcmp(loc, gloc)) break; } if (dmi_dimms[i] == NULL) remove_dimm(g); } } void move_dimm(struct group *g, struct dmi_memdev *newpos, char *loc) { char *newloc = dmi_getstring(&newpos->header, newpos->device_locator); Wprintf("%s seems to have moved from %s to %s\n", group_name(g), loc, newloc); change_entry(dimm_db, g, "old locator", loc); change_entry(dimm_db, g, "Locator", newloc); delete_entry(dimm_db, g, "removed at"); change_entry(dimm_db, g, "moved at", timestamp()); } void new_dimm(struct dmi_memdev *d, char *loc) { struct group *g; char name[100]; create_dimm_name(d, name); g = add_group(dimm_db, name, NULL); d_to_group(d, g); change_entry(dimm_db, g, "added at", timestamp()); Wprintf("Found new %s at %s\n", name, loc); /* Run uniqueness check */ (void)matching_dimm_group(g); } /* check if reported dimms are at their places */ void check_dimm_positions(void) { int i; struct group *g; struct dmi_memdev *d; struct dmi_memdev *match; for (i = 0; (d = dmi_dimms[i]) != NULL; i++) { char *loc = dmi_getstring(&d->header, d->device_locator); g = find_entry(dimm_db, NULL, "Locator", loc); /* In the database, but somewhere else? */ if (g && !cmp_dimm(d, g)) { match = matching_dimm_group(g); if (match) move_dimm(g, match, loc); else remove_dimm(g); g = NULL; /* In DMI but somewhere else? */ } else if (!g) { g = matching_dimm_dmi(d); if (g) move_dimm(g, d, loc); } if (!g) new_dimm(d, loc); } } /* synchronize database with smbios */ int sync_dimms(void) { if (!dmi_dimms) return -1; check_dimm_positions(); disable_leftover_dimms(); sync_db(dimm_db); return 0; } void gc_dimms(void) { struct group *g; while ((g = find_entry(dimm_db, NULL, "Locator", "removed")) != NULL) { Wprintf("Purging removed %s which was at %s\n", group_name(g), entry_val(g, "Old Locator")); delete_group(dimm_db, g); } sync_db(dimm_db); } static unsigned long inc_val(struct group *g, char *entry) { unsigned long val = entry_num(g, entry) + 1; change_entry_num(dimm_db, g, entry, val); return val; } static void run_trigger(char *trigger, char *loc, unsigned long val, unsigned long max) { pid_t pid; Lprintf("Running error trigger because memory at %s had %lu errors\n", loc, max); close_dimm_db(); if ((pid = fork()) == 0) { char valbuf[20], maxbuf[20]; char *argv[] = { trigger, loc, valbuf, maxbuf, NULL }; char *env[] = { "PATH=/sbin:/usr/bin", NULL }; sprintf(valbuf, "%lu", val); sprintf(maxbuf, "%lu", max); execve(trigger, argv, env); _exit(1); } int status; if (waitpid(pid, &status, 0) || !WIFEXITED(status) || WEXITSTATUS(status) != 0) Eprintf("Cannot run error trigger %s for %s\n", trigger, loc); open_dimm_db(NULL); } void new_error(unsigned long addr, unsigned long max_error, char *trigger) { struct dmi_memdev **devs; int i; devs = dmi_find_addr(addr); if (devs[0] == NULL) { Wprintf("No memory found for address %lx\n", addr); exit(1); } for (i = 0; devs[i]; i++) { struct dmi_memdev *d = devs[i]; char *loc = dmi_getstring(&d->header, d->device_locator); struct group *g = find_entry(dimm_db, NULL, "Locator", loc); if (!g) { // shouldn't happen Eprintf("No record found for %lx\n", addr); return; } unsigned long val = inc_val(g, "corrected errors"); if (val == max_error) { Lprintf("Large number of corrected errors in memory at %s", loc); Lprintf("Consider replacing it"); if (trigger && trigger[0]) run_trigger(trigger, loc, val, max_error); } } free(devs); } void reset_dimm(char *locator) { struct group *g; if (locator) { g = find_entry(dimm_db, NULL, "Locator", locator); if (!g) { fprintf(stderr, "Locator %s not found\n", locator); exit(1); } change_entry(dimm_db, g, "corrected errors", "0"); } else { for (g = first_group(dimm_db); g; g = next_group(g)) change_entry(dimm_db, g, "corrected errors", "0"); } sync_db(dimm_db); } struct group *lookup_dimm(char *locator) { struct group *g = find_entry(dimm_db, NULL, "Locator", locator); return g; } void dump_all_dimms(void) { dump_database(dimm_db, stdout); } void dump_dimm(char *locator) { struct group *g = lookup_dimm(locator); if (g) dump_group(g, stdout); else fprintf(stderr, "%s not found\n", locator); } void close_dimm_db(void) { if (dimm_db) { close_db(dimm_db); dimm_db = NULL; } } int open_dimm_db(char *fn) { static char *old_db_name; if (dmi_dimms < 0) return -1; if (dimm_db) return 0; if (!fn) { fn = old_db_name; } else { old_db_name = strdup(fn); if (!old_db_name) exit(ENOMEM); atexit(close_dimm_db); } dimm_db = open_db(fn, 1); if (!dimm_db) { Eprintf("Cannot open dimm database %s: %s", fn, strerror(errno)); return -1; } if (sync_dimms() < 0) return -1; return 0; } mcelog-100/README0000664000175000017500000001027712247142742013140 0ustar vorlonvorlonmcelog is the user space backend for logging machine check errors reported by the hardware to the kernel. The kernel does the immediate actions (like killing processes etc.) and mcelog decodes the errors and manages various other advanced error responses like offlining memory, CPUs or triggering events. It primarily handles machine checks and thermal events, which are reported for errors detected by the CPU. It is recommended that mcelog runs on all x86 machines, both 64bit (since early 2.6) and 32bit (since 2.6.32) mcelog can run in several modi: cronjob, trigger, daemon cronjob is the old method. mcelog runs every 5 minutes from cron and checks for errors. Disadvantage of this is that it can delay error reporting significantly (upto 10 minutes) and does not allow mcelog to keep extended state. trigger is a newer method where the kernel runs mcelog on a error. This is configured with echo /usr/sbin/mcelog > /sys/devices/system/machinecheck/machinecheck0/trigger This is faster, but still doesn't allow mcelog to keep state, and has relatively high overhead for each error because a program has to be initialized from scratch. In daemon mode mcelog runs continuously as a daemon in the background and wait for errors. It is enabled by running mcelog --daemon & from a init script. This is the fastest and most feature-ful. The recommended mode is daemon, because several new functions (like page error predictive failure analysis) require a continuously running daemon. Documentation: The primary reference documentation are the man pages. lk10-mcelog.pdf has a overview over the errors mcelog handles (originally from Linux Kongress 2010) mce.pdf is a very old paper describing the first releases of mcelog (some parts are obsolete) For distributors: Please install a init script by default that runs mcelog in daemon mode. The mcelog.init script is a good starting point. Also install a logrotated file (mcelog.logrotate) or equivalent when mcelog is running in daemon mode. These two are not in make install. The installation also requires a config file (/etc/mcelog.conf) and the default triggers. These are all installed by "make install" /dev/mcelog is needed for mcelog operation If it's not there it can be created with mknod /dev/mcelog c 10 227 Normally it should be created automatically in udev. Security: mcelog needs to run as root because it might trigger actions like page-offlining, which require CAP_SYS_ADMIN. Also it opens /dev/mcelog and a unix socket for client support. It also opens /dev/mem to parse the BIOS DMI tables. It is careful to close the file descriptor and unmap any mappings after using them. There is support for changing the user in daemon mode after opening the device and the sockets, but that would stop triggers from doing corrective action that require root. In principle it would be possible to only keep CAP_SYS_ADMIN for page-offling, but that would prevent triggers from doing root only actions not covered by it (and CAP_SYS_ADMIN is not that different from full root) In daemon mode mcelog listens to a unix socket and processes requests from mcelog --client. This can be disabled in the configuration file. The uid/gid of the requestor is checked on access and is configurable (default 0/0 only). The command parsing code is very straight forward (server.c) The client parsing/reply is currently done with full privileges of the daemon. Testing: There is a simple test suite in tests/. The test suite requires root to run and access to mce-inject and a kernel with MCE injection support (CONFIG_X86_MCE_INJECT). It will kill any running mcelog daemon. Run it with "make test" The test suite requires the mce-inject tool, available from git://git.kernel.org/pub/utils/cpu/mce/mce-inject.git The mce-inject executable must be either in $PATH or in the ../mce-inject directory. You can also test under valgrind with "make valgrind-test". For this valgrind needs to be installed of course. Advanced valgrind options can be specified with make VALGRIND="valgrind --option" valgrind-test Other checks: make iccverify and make clangverify run the static verifiers in clang and icc respectively. License: This program is licensed under the subject of the GNU Public General License, v.2 mcelog-100/mcelog.c0000664000175000017500000007551012247143512013667 0ustar vorlonvorlon/* Copyright (C) 2004,2005,2006 Andi Kleen, SuSE Labs. Copyright (C) 2008 Intel Corporation Authors: Andi Kleen, Ying Huang Decode IA32/x86-64 machine check events in /dev/mcelog. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mcelog.h" #include "paths.h" #include "k8.h" #include "intel.h" #include "p4.h" #include "dmi.h" #include "dimm.h" #include "tsc.h" #include "version.h" #include "config.h" #include "diskdb.h" #include "memutil.h" #include "eventloop.h" #include "memdb.h" #include "server.h" #include "trigger.h" #include "client.h" #include "msg.h" #include "yellow.h" #include "page.h" enum cputype cputype = CPU_GENERIC; char *logfn = LOG_DEV_FILENAME; int ignore_nodev; int filter_bogus = 1; int cpu_forced; static double cpumhz; static int cpumhz_forced; int ascii_mode; int dump_raw_ascii; int daemon_mode; static char *inputfile; char *processor_flags; static int foreground; int filter_memory_errors; static struct config_cred runcred = { .uid = -1U, .gid = -1U }; static int numerrors; static char pidfile_default[] = PID_FILE; static char logfile_default[] = LOG_FILE; static char *pidfile = pidfile_default; static char *logfile; static int debug_numerrors; int imc_log = -1; static int is_cpu_supported(void); static void disclaimer(void) { Wprintf("Hardware event. This is not a software error.\n"); } static char *extended_bankname(unsigned bank) { static char buf[64]; switch (bank) { case MCE_THERMAL_BANK: return "THERMAL EVENT"; case MCE_TIMEOUT_BANK: return "Timeout waiting for exception on other CPUs"; case K8_MCE_THRESHOLD_BASE ... K8_MCE_THRESHOLD_TOP: return k8_bank_name(bank); /* add more extended banks here */ default: sprintf(buf, "Undecoded extended event %x", bank); return buf; } } static char *bankname(unsigned bank) { static char numeric[64]; if (bank >= MCE_EXTENDED_BANK) return extended_bankname(bank); switch (cputype) { case CPU_K8: return k8_bank_name(bank); CASE_INTEL_CPUS: return intel_bank_name(bank); /* add banks of other cpu types here */ default: sprintf(numeric, "BANK %d", bank); return numeric; } } static void resolveaddr(unsigned long addr) { if (addr && do_dmi && dmi_forced) dmi_decodeaddr(addr); /* Should check for PCI resources here too */ } static int mce_filter(struct mce *m, unsigned recordlen) { if (!filter_bogus) return 1; /* Filter out known broken MCEs */ switch (cputype) { case CPU_K8: return mce_filter_k8(m); /* add more buggy CPUs here */ CASE_INTEL_CPUS: return mce_filter_intel(m, recordlen); default: case CPU_GENERIC: return 1; } } static void print_tsc(int cpunum, __u64 tsc, unsigned long time) { int ret = -1; char *buf = NULL; if (cpumhz_forced) ret = decode_tsc_forced(&buf, cpumhz, tsc); else if (!time) ret = decode_tsc_current(&buf, cpunum, cputype, cpumhz, tsc); Wprintf("TSC %llx %s", tsc, ret >= 0 && buf ? buf : ""); free(buf); } struct cpuid1 { unsigned stepping : 4; unsigned model : 4; unsigned family : 4; unsigned type : 2; unsigned res1 : 2; unsigned ext_model : 4; unsigned ext_family : 8; unsigned res2 : 4; }; static void parse_cpuid(u32 cpuid, u32 *family, u32 *model) { union { struct cpuid1 c; u32 v; } c; /* Algorithm from IA32 SDM 2a 3-191 */ c.v = cpuid; *family = c.c.family; if (*family == 0xf) *family += c.c.ext_family; *model = c.c.model; if (*family == 6 || *family == 0xf) *model += c.c.ext_model << 4; } static u32 unparse_cpuid(unsigned family, unsigned model) { union { struct cpuid1 c; u32 v; } c; c.c.family = family; if (family >= 0xf) { c.c.family = 0xf; c.c.ext_family = family - 0xf; } c.c.model = model & 0xf; if (family == 6 || family == 0xf) c.c.ext_model = model >> 4; return c.v; } static char *cputype_name[] = { [CPU_GENERIC] = "generic CPU", [CPU_P6OLD] = "Intel PPro/P2/P3/old Xeon", [CPU_CORE2] = "Intel Core", /* 65nm and 45nm */ [CPU_K8] = "AMD K8 and derivates", [CPU_P4] = "Intel P4", [CPU_NEHALEM] = "Intel Xeon 5500 series / Core i3/5/7 (\"Nehalem/Westmere\")", [CPU_DUNNINGTON] = "Intel Xeon 7400 series", [CPU_TULSA] = "Intel Xeon 7100 series", [CPU_INTEL] = "Intel generic architectural MCA", [CPU_XEON75XX] = "Intel Xeon 7500 series", [CPU_SANDY_BRIDGE] = "Sandy Bridge", /* Fill in better name */ [CPU_SANDY_BRIDGE_EP] = "Sandy Bridge EP", /* Fill in better name */ [CPU_IVY_BRIDGE] = "Ivy Bridge", /* Fill in better name */ [CPU_IVY_BRIDGE_EPEX] = "Ivy Bridge EP/EX", /* Fill in better name */ [CPU_HASWELL] = "Haswell", /* Fill in better name */ }; static struct config_choice cpu_choices[] = { { "generic", CPU_GENERIC }, { "p6old", CPU_P6OLD }, { "core2", CPU_CORE2 }, { "k8", CPU_K8 }, { "p4", CPU_P4 }, { "dunnington", CPU_DUNNINGTON }, { "xeon74xx", CPU_DUNNINGTON }, { "xeon7400", CPU_DUNNINGTON }, { "xeon5500", CPU_NEHALEM }, { "xeon5200", CPU_CORE2 }, { "xeon5000", CPU_P4 }, { "xeon5100", CPU_CORE2 }, { "xeon3100", CPU_CORE2 }, { "xeon3200", CPU_CORE2 }, { "core_i7", CPU_NEHALEM }, { "core_i5", CPU_NEHALEM }, { "core_i3", CPU_NEHALEM }, { "nehalem", CPU_NEHALEM }, { "westmere", CPU_NEHALEM }, { "xeon71xx", CPU_TULSA }, { "xeon7100", CPU_TULSA }, { "tulsa", CPU_TULSA }, { "intel", CPU_INTEL }, { "xeon75xx", CPU_XEON75XX }, { "xeon7500", CPU_XEON75XX }, { "xeon7200", CPU_CORE2 }, { "xeon7100", CPU_P4 }, { "sandybridge", CPU_SANDY_BRIDGE }, /* Fill in better name */ { "sandybridge-ep", CPU_SANDY_BRIDGE_EP }, /* Fill in better name */ { "ivybridge", CPU_IVY_BRIDGE }, /* Fill in better name */ { "ivybridge-ep", CPU_IVY_BRIDGE_EPEX }, /* Fill in better name */ { "ivybridge-ex", CPU_IVY_BRIDGE_EPEX }, /* Fill in better name */ { "haswell", CPU_HASWELL }, /* Fill in better name */ {} }; static void print_cputypes(void) { struct config_choice *c; fprintf(stderr, "Valid CPUs:"); for (c = cpu_choices; c->name; c++) fprintf(stderr, " %s", c->name); fputc('\n', stderr); } static enum cputype lookup_cputype(char *name) { struct config_choice *c; for (c = cpu_choices; c->name; c++) { if (!strcasecmp(name, c->name)) return c->val; } fprintf(stderr, "Unknown CPU type `%s' specified\n", name); print_cputypes(); exit(1); } static char *vendor[] = { [0] = "Intel", [1] = "Cyrix", [2] = "AMD", [3] = "UMC", [4] = "vendor 4", [5] = "Centaur", [6] = "vendor 6", [7] = "Transmeta", [8] = "NSC" }; static unsigned cpuvendor_to_num(char *name) { unsigned i; unsigned v; char *end; v = strtoul(name, &end, 0); if (end > name) return v; for (i = 0; i < NELE(vendor); i++) if (!strcmp(name, vendor[i])) return i; return 0; } static char *cpuvendor_name(u32 cpuvendor) { return (cpuvendor < NELE(vendor)) ? vendor[cpuvendor] : "Unknown vendor"; } static enum cputype setup_cpuid(u32 cpuvendor, u32 cpuid) { u32 family, model; parse_cpuid(cpuid, &family, &model); switch (cpuvendor) { case X86_VENDOR_INTEL: return select_intel_cputype(family, model); case X86_VENDOR_AMD: if (family >= 15 && family <= 17) return CPU_K8; /* FALL THROUGH */ default: Eprintf("Unknown CPU type vendor %u family %x model %x", cpuvendor, family, model); return CPU_GENERIC; } } static void mce_cpuid(struct mce *m) { static int warned; if (m->cpuid) { enum cputype t = setup_cpuid(m->cpuvendor, m->cpuid); if (!cpu_forced) cputype = t; else if (t != cputype && t != CPU_GENERIC && !warned) { Eprintf("Forced cputype %s does not match cpu type %s from mcelog\n", cputype_name[cputype], cputype_name[t]); warned = 1; } } else if (cputype == CPU_GENERIC && !cpu_forced) { is_cpu_supported(); } } static void mce_prepare(struct mce *m) { mce_cpuid(m); if (!m->time) m->time = time(NULL); } static void dump_mce(struct mce *m, unsigned recordlen) { int n; int ismemerr = 0; unsigned cpu = m->extcpu ? m->extcpu : m->cpu; /* should not happen */ if (!m->finished) Wprintf("not finished?\n"); Wprintf("CPU %d %s ", cpu, bankname(m->bank)); if (m->tsc) print_tsc(cpu, m->tsc, m->time); Wprintf("\n"); if (m->ip) Wprintf("RIP%s %02x:%llx\n", !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "", m->cs, m->ip); n = 0; if (m->status & MCI_STATUS_MISCV) n += Wprintf("MISC %llx ", m->misc); if (m->status & MCI_STATUS_ADDRV) n += Wprintf("ADDR %llx ", m->addr); if (n > 0) Wprintf("\n"); if (m->time) { time_t t = m->time; Wprintf("TIME %llu %s", m->time, ctime(&t)); } switch (cputype) { case CPU_K8: decode_k8_mc(m, &ismemerr); break; CASE_INTEL_CPUS: decode_intel_mc(m, cputype, &ismemerr, recordlen); break; /* add handlers for other CPUs here */ default: break; } /* decode all status bits here */ Wprintf("STATUS %llx MCGSTATUS %llx\n", m->status, m->mcgstatus); n = 0; if (recordlen >= offsetof(struct mce, cpuid) && m->mcgcap) n += Wprintf("MCGCAP %llx ", m->mcgcap); if (recordlen >= offsetof(struct mce, apicid)) n += Wprintf("APICID %x ", m->apicid); if (recordlen >= offsetof(struct mce, socketid)) n += Wprintf("SOCKETID %x ", m->socketid); if (n > 0) Wprintf("\n"); if (recordlen >= offsetof(struct mce, cpuid) && m->cpuid) { u32 fam, mod; parse_cpuid(m->cpuid, &fam, &mod); Wprintf("CPUID Vendor %s Family %u Model %u\n", cpuvendor_name(m->cpuvendor), fam, mod); } if (cputype != CPU_SANDY_BRIDGE_EP && cputype != CPU_IVY_BRIDGE_EPEX) resolveaddr(m->addr); if (!ascii_mode && ismemerr && (m->status & MCI_STATUS_ADDRV)) { diskdb_resolve_addr(m->addr); } } static void dump_mce_raw_ascii(struct mce *m, unsigned recordlen) { /* should not happen */ if (!m->finished) Wprintf("not finished?\n"); Wprintf("CPU %u\n", m->extcpu ? m->extcpu : m->cpu); Wprintf("BANK %d\n", m->bank); Wprintf("TSC %#llx\n", m->tsc); Wprintf("RIP %#02x:%#llx\n", m->cs, m->ip); Wprintf("MISC %#llx\n", m->misc); Wprintf("ADDR %#llx\n", m->addr); Wprintf("STATUS %#llx\n", m->status); Wprintf("MCGSTATUS %#llx\n", m->mcgstatus); if (recordlen >= offsetof(struct mce, cpuid)) Wprintf("PROCESSOR %u:%#x\n", m->cpuvendor, m->cpuid); #define CPRINT(str, field) \ if (recordlen >= offsetof(struct mce, field)) \ Wprintf(str "\n", m->field) CPRINT("TIME %llu", time); CPRINT("SOCKETID %u", socketid); CPRINT("APICID %u", apicid); CPRINT("MCGCAP %#llx", mcgcap); #undef CPRINT Wprintf("\n"); } int is_cpu_supported(void) { enum { VENDOR = 1, FAMILY = 2, MODEL = 4, MHZ = 8, FLAGS = 16, ALL = 0x1f } seen = 0; FILE *f; static int checked; if (checked) return 1; checked = 1; f = fopen("/proc/cpuinfo","r"); if (f != NULL) { int family = 0; int model = 0; char vendor[64] = { 0 }; char *line = NULL; size_t linelen = 0; double mhz; while (getdelim(&line, &linelen, '\n', f) > 0 && seen != ALL) { if (sscanf(line, "vendor_id : %63[^\n]", vendor) == 1) seen |= VENDOR; if (sscanf(line, "cpu family : %d", &family) == 1) seen |= FAMILY; if (sscanf(line, "model : %d", &model) == 1) seen |= MODEL; /* We use only Mhz of the first CPU, assuming they are the same (there are more sanity checks later to make this not as wrong as it sounds) */ if (sscanf(line, "cpu MHz : %lf", &mhz) == 1) { if (!cpumhz_forced) cpumhz = mhz; seen |= MHZ; } if (!strncmp(line, "flags", 5) && isspace(line[6])) { processor_flags = line; line = NULL; linelen = 0; seen |= FLAGS; } } if (seen == ALL) { if (!strcmp(vendor,"AuthenticAMD")) { if (family == 15) cputype = CPU_K8; if (family >= 15) SYSERRprintf("AMD Processor family %d: Please load edac_mce_amd module.\n", family); return 0; } else if (!strcmp(vendor,"GenuineIntel")) cputype = select_intel_cputype(family, model); /* Add checks for other CPUs here */ } else { Eprintf("warning: Cannot parse /proc/cpuinfo\n"); } fclose(f); free(line); } else Eprintf("warning: Cannot open /proc/cpuinfo\n"); return 1; } static char *skipspace(char *s) { while (isspace(*s)) ++s; return s; } static char *skip_syslog(char *s) { char *p; /* Handle syslog output */ p = strstr(s, "mcelog: "); if (p) return p + sizeof("mcelog: ") - 1; return s; } static char *skipgunk(char *s) { s = skip_syslog(s); s = skipspace(s); if (*s == '<') { s += strcspn(s, ">"); if (*s == '>') ++s; } s = skipspace(s); if (*s == '[') { s += strcspn(s, "]"); if (*s == ']') ++s; } return skipspace(s); } static inline int urange(unsigned val, unsigned lo, unsigned hi) { return val >= lo && val <= hi; } static int is_short(char *name) { return strlen(name) == 3 && isupper(name[0]) && islower(name[1]) && islower(name[2]); } static unsigned skip_date(char *s) { unsigned day, hour, min, year, sec; char dayname[11]; char month[11]; unsigned next; if (sscanf(s, "%10s %10s %u %u:%u:%u %u%n", dayname, month, &day, &hour, &min, &sec, &year, &next) != 7) return 0; if (!is_short(dayname) || !is_short(month) || !urange(day, 1, 31) || !urange(hour, 0, 24) || !urange(min, 0, 59) || !urange(sec, 0, 59) || year < 1900) return 0; return next; } static void dump_mce_final(struct mce *m, char *symbol, int missing, int recordlen, int dseen) { m->finished = 1; if (m->cpuid) mce_cpuid(m); if (!dump_raw_ascii) { if (!dseen) disclaimer(); dump_mce(m, recordlen); if (symbol[0]) Wprintf("RIP: %s\n", symbol); if (missing) Wprintf("(Fields were incomplete)\n"); } else dump_mce_raw_ascii(m, recordlen); flushlog(); } static char *skip_patterns[] = { "MCA:*", "MCi_MISC register valid*", "MC? status*", "Unsupported new Family*", "Kernel does not support page offline interface", NULL }; static int match_patterns(char *s, char **pat) { for (; *pat; pat++) if (!fnmatch(*pat, s, 0)) return 0; return 1; } #define FIELD(f) \ if (recordlen < endof_field(struct mce, f)) \ recordlen = endof_field(struct mce, f) /* Decode ASCII input for fatal messages */ static void decodefatal(FILE *inf) { struct mce m; char *line = NULL; size_t linelen = 0; int missing; char symbol[100]; int data; int next; char *s = NULL; unsigned cpuvendor; unsigned recordlen; int disclaimer_seen; ascii_mode = 1; if (do_dmi && dmi_forced) Wprintf( "WARNING: with --dmi mcelog --ascii must run on the same machine with the\n" " same BIOS/memory configuration as where the machine check occurred.\n"); restart: missing = 0; data = 0; next = 0; disclaimer_seen = 0; recordlen = 0; memset(&m, 0, sizeof(struct mce)); symbol[0] = '\0'; while (next > 0 || getdelim(&line, &linelen, '\n', inf) > 0) { int n = 0; char *start; s = next > 0 ? s + next : line; s = skipgunk(s); start = s; next = 0; if (!strncmp(s, "CPU ", 4)) { unsigned cpu = 0, bank = 0; n = sscanf(s, "CPU %u: Machine Check Exception: %16Lx Bank %d: %016Lx%n", &cpu, &m.mcgstatus, &bank, &m.status, &next); if (n == 1) { n = sscanf(s, "CPU %u BANK %u%n", &cpu, &bank, &next); if (n != 2) n = sscanf(s, "CPU %u %u%n", &cpu, &bank, &next); m.cpu = cpu; if (n < 2) missing++; else { m.bank = bank; FIELD(bank); } } else if (n <= 0) { missing++; } else if (n > 1) { FIELD(mcgstatus); m.cpu = cpu; if (n > 2) { m.bank = bank; FIELD(bank); } else if (n > 3) FIELD(status); if (n < 4) missing++; } } else if (!strncmp(s, "STATUS", 6)) { if ((n = sscanf(s,"STATUS %llx%n", &m.status, &next)) < 1) missing++; else FIELD(status); } else if (!strncmp(s, "MCGSTATUS", 6)) { if ((n = sscanf(s,"MCGSTATUS %llx%n", &m.mcgstatus, &next)) < 1) missing++; else FIELD(mcgstatus); } else if (!strncmp(s, "RIP", 3)) { unsigned cs = 0; if (!strncmp(s, "RIP !INEXACT!", 13)) s += 13; else s += 3; n = sscanf(s, "%02x:<%016Lx> {%100s}%n", &cs, &m.ip, symbol, &next); m.cs = cs; if (n < 2) missing++; else FIELD(ip); } else if (!strncmp(s, "TSC",3)) { if ((n = sscanf(s, "TSC %llx%n", &m.tsc, &next)) < 1) missing++; else FIELD(tsc); } else if (!strncmp(s, "ADDR",4)) { if ((n = sscanf(s, "ADDR %llx%n", &m.addr, &next)) < 1) missing++; else FIELD(addr); } else if (!strncmp(s, "MISC",4)) { if ((n = sscanf(s, "MISC %llx%n", &m.misc, &next)) < 1) missing++; else FIELD(misc); } else if (!strncmp(s, "PROCESSOR", 9)) { if ((n = sscanf(s, "PROCESSOR %u:%x%n", &cpuvendor, &m.cpuid, &next)) < 2) missing++; else { m.cpuvendor = cpuvendor; FIELD(cpuid); FIELD(cpuvendor); } } else if (!strncmp(s, "TIME", 4)) { if ((n = sscanf(s, "TIME %llu%n", &m.time, &next)) < 1) missing++; else FIELD(time); next += skip_date(s + next); } else if (!strncmp(s, "MCGCAP", 6)) { if ((n = sscanf(s, "MCGCAP %llx%n", &m.mcgcap, &next)) != 1) missing++; else FIELD(mcgcap); } else if (!strncmp(s, "APICID", 6)) { if ((n = sscanf(s, "APICID %x%n", &m.apicid, &next)) != 1) missing++; else FIELD(apicid); } else if (!strncmp(s, "SOCKETID", 8)) { if ((n = sscanf(s, "SOCKETID %u%n", &m.socketid, &next)) != 1) missing++; else FIELD(socketid); } else if (!strncmp(s, "CPUID", 5)) { unsigned fam, mod; char vendor[31]; if ((n = sscanf(s, "CPUID Vendor %30s Family %u Model %u\n", vendor, &fam, &mod)) < 3) missing++; else { m.cpuvendor = cpuvendor_to_num(vendor); m.cpuid = unparse_cpuid(fam, mod); FIELD(cpuid); FIELD(cpuvendor); } } else if (strstr(s, "HARDWARE ERROR")) disclaimer_seen = 1; else if (!strncmp(s, "(XEN)", 5)) { char *w; unsigned bank, cpu; if (strstr(s, "The hardware reports a non fatal, correctable incident occurred")) { w = strstr(s, "CPU"); if (w && sscanf(w, "CPU %d", &cpu)) { m.cpu = cpu; FIELD(cpu); } } else if ((n = sscanf(s, "(XEN) Bank %d: %llx at %llx", &bank, &m.status, &m.addr) >= 1)) { m.bank = bank; FIELD(bank); if (n >= 2) FIELD(status); if (n >= 3) FIELD(addr); } } else if (!match_patterns(s, skip_patterns)) n = 0; else { s = skipspace(s); if (*s && data) dump_mce_final(&m, symbol, missing, recordlen, disclaimer_seen); if (!dump_raw_ascii) Wprintf("%s", start); if (*s && data) goto restart; } if (n > 0) data = 1; } free(line); if (data) dump_mce_final(&m, symbol, missing, recordlen, disclaimer_seen); } static void remove_pidfile(void) { unlink(pidfile); if (pidfile != pidfile_default) free(pidfile); } static void signal_exit(int sig) { remove_pidfile(); _exit(sig); } static void setup_pidfile(char *s) { char cwd[PATH_MAX]; char *c; if (*s != '/') { c = getcwd(cwd, PATH_MAX); if (!c) return; asprintf(&pidfile, "%s/%s", cwd, s); } else { asprintf(&pidfile, "%s", s); } return; } static void write_pidfile(void) { FILE *f; atexit(remove_pidfile); signal(SIGTERM, signal_exit); signal(SIGINT, signal_exit); signal(SIGQUIT, signal_exit); f = fopen(pidfile, "w"); if (!f) { Eprintf("Cannot open pidfile `%s'", pidfile); return; } fprintf(f, "%u", getpid()); fclose(f); } void usage(void) { fprintf(stderr, "Usage:\n" " mcelog [options] [mcelogdevice]\n" "Decode machine check error records from current kernel.\n" " mcelog [options] --daemon\n" "Run mcelog in daemon mode, waiting for errors from the kernel.\n" " mcelog [options] --client\n" "Query a currently running mcelog daemon for errors\n" " mcelog [options] --ascii < log\n" " mcelog [options] --ascii --file log\n" "Decode machine check ASCII output from kernel logs\n" "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" "--raw (with --ascii) Dump in raw ASCII format for machine processing\n" "--daemon Run in background waiting for events (needs newer kernel)\n" "--ignorenodev Exit silently when the device cannot be opened\n" "--file filename With --ascii read machine check log from filename instead of stdin\n" "--syslog Log decoded machine checks in syslog (default stdout or syslog for daemon)\n" "--syslog-error Log decoded machine checks in syslog with error level\n" "--no-syslog Never log anything to syslog\n" "--logfile filename Append log output to logfile instead of stdout\n" "--dmi Use SMBIOS information to decode DIMMs (needs root)\n" "--no-dmi Don't use SMBIOS information\n" "--dmi-verbose Dump SMBIOS information (for debugging)\n" "--filter Inhibit known bogus events (default on)\n" "--no-filter Don't inhibit known broken events\n" "--config-file filename Read config information from config file instead of " CONFIG_FILENAME "\n" "--foreground Keep in foreground (for debugging)\n" "--num-errors N Only process N errors (for testing)\n" "--pidfile file Write pid of daemon into file\n" "--no-imc-log Disable extended iMC logging\n" ); diskdb_usage(); print_cputypes(); exit(1); } enum options { O_LOGFILE = O_COMMON, O_K8, O_P4, O_GENERIC, O_CORE2, O_INTEL_CPU, O_FILTER, O_DMI, O_NO_DMI, O_DMI_VERBOSE, O_SYSLOG, O_NO_SYSLOG, O_CPUMHZ, O_SYSLOG_ERROR, O_RAW, O_DAEMON, O_ASCII, O_CLIENT, O_VERSION, O_CONFIG_FILE, O_CPU, O_FILE, O_FOREGROUND, O_NUMERRORS, O_PIDFILE, O_DEBUG_NUMERRORS, O_NO_IMC_LOG, }; static struct option options[] = { { "logfile", 1, NULL, O_LOGFILE }, { "k8", 0, NULL, O_K8 }, { "p4", 0, NULL, O_P4 }, { "generic", 0, NULL, O_GENERIC }, { "core2", 0, NULL, O_CORE2 }, { "intel-cpu", 1, NULL, O_INTEL_CPU }, { "ignorenodev", 0, &ignore_nodev, 1 }, { "filter", 0, &filter_bogus, 1 }, { "no-filter", 0, &filter_bogus, 0 }, { "dmi", 0, NULL, O_DMI }, { "no-dmi", 0, NULL, O_NO_DMI }, { "dmi-verbose", 1, NULL, O_DMI_VERBOSE }, { "syslog", 0, NULL, O_SYSLOG }, { "cpumhz", 1, NULL, O_CPUMHZ }, { "syslog-error", 0, NULL, O_SYSLOG_ERROR }, { "dump-raw-ascii", 0, &dump_raw_ascii, 1 }, { "raw", 0, &dump_raw_ascii, 1 }, { "no-syslog", 0, NULL, O_NO_SYSLOG }, { "daemon", 0, NULL, O_DAEMON }, { "ascii", 0, NULL, O_ASCII }, { "file", 1, NULL, O_FILE }, { "version", 0, NULL, O_VERSION }, { "config-file", 1, NULL, O_CONFIG_FILE }, { "cpu", 1, NULL, O_CPU }, { "foreground", 0, NULL, O_FOREGROUND }, { "client", 0, NULL, O_CLIENT }, { "num-errors", 1, NULL, O_NUMERRORS }, { "pidfile", 1, NULL, O_PIDFILE }, { "debug-numerrors", 0, NULL, O_DEBUG_NUMERRORS }, /* undocumented: for testing */ { "no-imc-log", 0, NULL, O_NO_IMC_LOG }, DISKDB_OPTIONS {} }; static int modifier(int opt) { int v; switch (opt) { case O_LOGFILE: logfile = optarg; break; case O_K8: cputype = CPU_K8; cpu_forced = 1; break; case O_P4: cputype = CPU_P4; cpu_forced = 1; break; case O_GENERIC: cputype = CPU_GENERIC; cpu_forced = 1; break; case O_CORE2: cputype = CPU_CORE2; cpu_forced = 1; break; case O_INTEL_CPU: { unsigned fam, mod; if (sscanf(optarg, "%i,%i", &fam, &mod) != 2) usage(); cputype = select_intel_cputype(fam, mod); if (cputype == CPU_GENERIC) { fprintf(stderr, "Unknown Intel CPU\n"); usage(); } cpu_forced = 1; break; } case O_CPU: cputype = lookup_cputype(optarg); cpu_forced = 1; intel_cpu_init(cputype); break; case O_DMI: do_dmi = 1; dmi_forced = 1; break; case O_NO_DMI: dmi_forced = 1; do_dmi = 0; break; case O_DMI_VERBOSE: if (sscanf(optarg, "%i", &v) != 1) usage(); dmi_set_verbosity(v); break; case O_SYSLOG: openlog("mcelog", 0, LOG_DAEMON); syslog_opt = SYSLOG_ALL|SYSLOG_FORCE; break; case O_NO_SYSLOG: syslog_opt = SYSLOG_FORCE; break; case O_CPUMHZ: cpumhz_forced = 1; if (sscanf(optarg, "%lf", &cpumhz) != 1) usage(); break; case O_SYSLOG_ERROR: syslog_level = LOG_ERR; syslog_opt = SYSLOG_ALL|SYSLOG_FORCE; break; case O_DAEMON: daemon_mode = 1; if (!logfile && !foreground) logfile = logfile_default; if (!(syslog_opt & SYSLOG_FORCE)) syslog_opt = SYSLOG_REMARK|SYSLOG_ERROR; break; case O_FILE: inputfile = optarg; break; case O_FOREGROUND: foreground = 1; if (!(syslog_opt & SYSLOG_FORCE)) syslog_opt = SYSLOG_FORCE; if (logfile == logfile_default) logfile = NULL; break; case O_NUMERRORS: numerrors = atoi(optarg); break; case O_PIDFILE: setup_pidfile(optarg); break; case O_CONFIG_FILE: /* parsed in config.c */ break; case O_DEBUG_NUMERRORS: debug_numerrors = 1; break; case O_NO_IMC_LOG: imc_log = 0; break; case 0: break; default: return 0; } return 1; } static void modifier_finish(void) { if (logfile) { if (open_logfile(logfile) < 0) { if (daemon_mode && !(syslog_opt & SYSLOG_FORCE)) syslog_opt = SYSLOG_ALL; SYSERRprintf("Cannot open logfile %s", logfile); if (!daemon_mode) exit(1); } } } void argsleft(int ac, char **av) { int opt; while ((opt = getopt_long(ac, av, "", options, NULL)) != -1) { if (modifier(opt) != 1) usage(); } } void no_syslog(void) { if (!(syslog_opt & SYSLOG_FORCE)) syslog_opt = 0; } static int combined_modifier(int opt) { int r = modifier(opt); if (r == 0) r = diskdb_modifier(opt); return r; } static void general_setup(void) { trigger_setup(); yellow_setup(); config_cred("global", "run-credentials", &runcred); if (config_bool("global", "filter-memory-errors") == 1) filter_memory_errors = 1; } static void drop_cred(void) { if (runcred.uid != -1U && runcred.gid == -1U) { struct passwd *pw = getpwuid(runcred.uid); if (pw) runcred.gid = pw->pw_gid; } if (runcred.gid != -1U) { if (setgid(runcred.gid) < 0) SYSERRprintf("Cannot change group to %d", runcred.gid); } if (runcred.uid != -1U) { if (setuid(runcred.uid) < 0) SYSERRprintf("Cannot change user to %d", runcred.uid); } } static void process(int fd, unsigned recordlen, unsigned loglen, char *buf) { int i; int len; int finish = 0; if (recordlen == 0) { Wprintf("no data in mce record\n"); return; } len = read(fd, buf, recordlen * loglen); if (len < 0) { SYSERRprintf("mcelog read"); return; } for (i = 0; (i < len / (int)recordlen) && !finish; i++) { struct mce *mce = (struct mce *)(buf + i*recordlen); mce_prepare(mce); if (numerrors > 0 && --numerrors == 0) finish = 1; if (!mce_filter(mce, recordlen)) continue; if (!dump_raw_ascii) { disclaimer(); Wprintf("MCE %d\n", i); dump_mce(mce, recordlen); } else dump_mce_raw_ascii(mce, recordlen); flushlog(); } if (debug_numerrors && numerrors <= 0) finish = 1; if (recordlen > sizeof(struct mce)) { Eprintf("warning: %lu bytes ignored in each record\n", (unsigned long)recordlen - sizeof(struct mce)); Eprintf("consider an update\n"); } if (finish) exit(0); } static void noargs(int ac, char **av) { if (getopt_long(ac, av, "", options, NULL) != -1) usage(); } static void parse_config(char **av) { static const char config_fn[] = CONFIG_FILENAME; const char *fn = config_file(av, config_fn); if (!fn) usage(); if (parse_config_file(fn) < 0) { /* If it's the default file don't complain if it isn't there */ if (fn != config_fn) { fprintf(stderr, "Cannot open config file %s\n", fn); exit(1); } return; } config_options(options, combined_modifier); } static void ascii_command(int ac, char **av) { FILE *f = stdin; argsleft(ac, av); if (inputfile) { f = fopen(inputfile, "r"); if (!f) { fprintf(stderr, "Cannot open input file `%s': %s\n", inputfile, strerror(errno)); exit(1); } /* f closed by exit */ } no_syslog(); checkdmi(); decodefatal(f); } static void client_command(int ac, char **av) { argsleft(ac, av); no_syslog(); // XXX modifiers ask_server("dump all bios\n"); ask_server("pages\n"); } struct mcefd_data { unsigned loglen; unsigned recordlen; char *buf; }; 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); } static void handle_sigusr1(int sig) { reopenlog(); } int main(int ac, char **av) { struct mcefd_data d = {}; int opt; int fd; parse_config(av); while ((opt = getopt_long(ac, av, "", options, NULL)) != -1) { if (opt == '?') { usage(); } else if (combined_modifier(opt) > 0) { continue; } else if (opt == O_ASCII) { ascii_command(ac, av); exit(0); } else if (opt == O_CLIENT) { client_command(ac, av); exit(0); } else if (opt == O_VERSION) { noargs(ac, av); fprintf(stderr, "mcelog %s\n", MCELOG_VERSION); exit(0); } else if (diskdb_cmd(opt, ac, av)) { exit(0); } else if (opt == 0) break; } /* before doing anything else let's see if the CPUs are supported */ if (!cpu_forced && !is_cpu_supported()) { fprintf(stderr, "CPU is unsupported\n"); exit(1); } /* If the user didn't tell us not to use iMC logging, check if CPU supports it */ if (imc_log == -1) { switch (cputype) { case CPU_SANDY_BRIDGE_EP: case CPU_IVY_BRIDGE_EPEX: imc_log = 1; break; default: imc_log = 0; break; } } modifier_finish(); if (av[optind]) logfn = av[optind++]; if (av[optind]) usage(); checkdmi(); general_setup(); fd = open(logfn, O_RDONLY); if (fd < 0) { if (ignore_nodev) exit(0); SYSERRprintf("Cannot open `%s'", logfn); exit(1); } if (ioctl(fd, MCE_GET_RECORD_LEN, &d.recordlen) < 0) err("MCE_GET_RECORD_LEN"); if (ioctl(fd, MCE_GET_LOG_LEN, &d.loglen) < 0) err("MCE_GET_LOG_LEN"); d.buf = xalloc(d.recordlen * d.loglen); if (daemon_mode) { prefill_memdb(); if (!do_dmi) closedmi(); server_setup(); page_setup(); if (imc_log) set_imc_log(cputype); drop_cred(); register_pollcb(fd, POLLIN, process_mcefd, &d); if (!foreground && daemon(0, need_stdout()) < 0) err("daemon"); if (pidfile) write_pidfile(); signal(SIGUSR1, handle_sigusr1); event_signal(SIGUSR1); eventloop(); } else { process(fd, d.recordlen, d.loglen, d.buf); } trigger_wait(); exit(0); } mcelog-100/config.c0000664000175000017500000002034512247142742013666 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Simple config file parser mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author: Andi Kleen */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include "memutil.h" #include "mcelog.h" #include "config.h" #include "leaky-bucket.h" #include "trigger.h" #ifdef TEST #define Eprintf printf #define Wprintf printf #define xalloc(x) calloc(x,1) #endif /* ISSUES: doesn't detect misspelled options (this would require a major revamp!) doesn't merge/detect duplicated headers */ #define SHASH 11 struct opt { struct opt *next; char *name; char *val; }; struct header { struct header *next; char *name; struct opt *opts[SHASH]; struct opt *optslast[SHASH]; }; static struct header *hlist; /* djb hash */ static unsigned hash(const char *str) { const unsigned char *s; unsigned hash = 5381; for (s = (const unsigned char *)str; *s; s++) hash = (hash * 32) + hash + *s; return hash % SHASH; } static struct header *new_header(struct header *prevh, char *name) { struct header *h = xalloc(sizeof(struct header)); h->name = xstrdup(name); if (prevh) prevh->next = h; else hlist = h; return h; } static int empty(char *s) { while (isspace(*s)) ++s; return *s == 0; } static void noreturn parse_error(int line, char *msg) { Eprintf("config file line %d: %s\n", line, msg); exit(1); } static void nothing(char *s, int line) { if (!empty(s) != 0) parse_error(line, "left over characters at end of line"); } static void unparseable(char *desc, const char *header, const char *name) { char *field; if (!strcmp(header, "global")) { asprintf(&field, "%s", name); } else { asprintf(&field, "[%s] %s", header, name); } Eprintf("%s config option `%s' unparseable\n", desc, field); free(field); exit(1); } /* Remove leading/trailing white space */ static char *strstrip(char *s) { char *p; while (isspace(*s)) s++; p = s + strlen(s) - 1; if (p <= s) return s; while (isspace(*p) && p >= s) *p-- = 0; return s; } int parse_config_file(const char *fn) { FILE *f; char *line = NULL; size_t linelen = 0; char *name; char *val; struct opt *opt; struct header *hdr; int lineno = 1; unsigned h; f = fopen(fn, "r"); if (!f) return -1; hdr = NULL; while (getline(&line, &linelen, f) > 0) { char *s = strchr(line, '#'); if (s) *s = 0; s = strstrip(line); if (*s == '[') { char *p = strchr(s, ']'); if (p == NULL) parse_error(lineno, "Header without ending ]"); nothing(p + 1, lineno); *p = 0; hdr = new_header(hdr, s + 1); } else if ((val = strchr(line, '=')) != NULL) { *val++ = 0; name = strstrip(s); val = strstrip(val); opt = xalloc(sizeof(struct opt)); opt->name = xstrdup(name); opt->val = xstrdup(val); h = hash(name); if (!hdr) hdr = new_header(hdr, "global"); //printf("[%s] \"%s\" = \"%s\"\n", hdr->name, name, val); if (hdr->optslast[h] == NULL) hdr->opts[h] = opt; else hdr->optslast[h]->next = opt; hdr->optslast[h] = opt; } else if (!empty(s)) { parse_error(lineno, "config file line not field nor header"); } lineno++; } fclose(f); free(line); return 0; } char *config_string(const char *header, const char *name) { struct header *hdr; unsigned h = hash(name); for (hdr = hlist; hdr; hdr = hdr->next) { if (!strcmp(hdr->name, header)) { struct opt *o; for (o = hdr->opts[h]; o; o = o->next) { if (!strcmp(o->name, name)) return o->val; } } } if (strcmp(header, "global")) return config_string("global", name); return NULL; } int config_number(const char *header, const char *name, char *fmt, void *val) { char *str = config_string(header, name); if (str == NULL) return -1; if (sscanf(str, fmt, val) != 1) { unparseable("numerical", header, name); return -1; } return 0; } int config_choice(const char *header, const char *name, const struct config_choice *c) { char *str = config_string(header, name); if (!str) return -1; for (; c->name; c++) { if (!strcasecmp(str, c->name)) return c->val; } unparseable("choice", header, name); return -1; } int config_bool(const char *header, const char *name) { static const struct config_choice bool_choices[] = { { "yes", 1 }, { "true", 1 }, { "1", 1 }, { "on", 1 }, { "no", 0 }, { "false", 0 }, { "0", 0 }, { "off", 0 }, {} }; return config_choice(header, name, bool_choices); } static char *match_arg(char **av, char *arg) { int len = strlen(arg); if (!strncmp(*av, arg, len)) { if ((*av)[len] == '=') { return len + 1 + *av; } else { if (av[1] == NULL) usage(); return av[1]; } } return NULL; } /* Look for the config file argument before parsing the other options because we want to read the config file first so that command line options can conveniently override it. */ const char *config_file(char **av, const char *deffn) { char *arg; while (*++av) { if (!strcmp(*av, "--")) break; if ((arg = match_arg(av, "--conf")) != NULL) return arg; } return deffn; } /* Use getopt_long struct option array to process config file */ void config_options(struct option *opts, int (*func)(int)) { for (; opts->name; opts++) { if (!opts->has_arg) { if (config_bool("global", opts->name) != 1) continue; if (opts->flag) { *(opts->flag) = opts->val; continue; } } else { char *s = config_string("global", opts->name); if (s == NULL) continue; optarg = s; } func(opts->val); } } int config_trigger(const char *header, const char *base, struct bucket_conf *bc) { char *s; char *name; int n; asprintf(&name, "%s-threshold", base); s = config_string(header, name); if (s) { if (bucket_conf_init(bc, s) < 0) { unparseable("trigger", header, name); return -1; } } free(name); asprintf(&name, "%s-trigger", base); s = config_string(header, name); if (s) { /* no $PATH */ if (trigger_check(s) != 0) { SYSERRprintf("Trigger `%s' not executable\n", s); exit(1); } bc->trigger = s; } free(name); bc->log = 0; asprintf(&name, "%s-log", base); n = config_bool(header, name); if (n >= 0) bc->log = n; free(name); return 0; } void config_cred(char *header, char *base, struct config_cred *cred) { char *s; char *name; asprintf(&name, "%s-user", base); if ((s = config_string(header, name)) != NULL) { struct passwd *pw; if (!strcmp(s, "*")) cred->uid = -1U; else if ((pw = getpwnam(s)) == NULL) Eprintf("Unknown user `%s' in %s:%s config entry\n", s, header, name); else cred->uid = pw->pw_uid; } free(name); asprintf(&name, "%s-group", base); if ((s = config_string(header, name)) != NULL) { struct group *gr; if (!strcmp(s, "*")) cred->gid = -1U; else if ((gr = getgrnam(s)) == NULL) Eprintf("Unknown group `%s' in %s:%s config entry\n", header, name, s); else cred->gid = gr->gr_gid; } free(name); } #ifdef TEST int main(int ac, char **av) { if (!av[1]) printf("need config file\n"), exit(1); if (parse_config_file(av[1]) < 0) printf("cannot parse config file\n"), exit(1); char *type; char *header; char *name; int n; while (scanf("%as %as %as", &type, &header, &name) == 3) { switch (type[0]) { case 'n': if (config_number(header, name, "%d", &n) < 0) printf("Cannot parse number %s %s\n", header, name); else printf("res %d\n", n); break; case 's': printf("res %s\n", config_string(header, name)); break; case 'b': printf("res %d\n", config_bool(header, name)); break; default: printf("unknown type %s\n", type); break; } free(type); free(header); free(name); } return 0; } #endif mcelog-100/mcelog.logrotate0000664000175000017500000000041312247142742015437 0ustar vorlonvorlon/var/log/mcelog { compress dateext maxage 365 rotate 99 size=+2048k notifempty missingok copytruncate postrotate chmod 644 /var/log/mcelog [ -r /var/run/mcelog.pid ] && kill -USR1 `cat /var/run/mcelog.pid` endscript } mcelog-100/dmi.h0000664000175000017500000000374412247142742013203 0ustar vorlonvorlon struct dmi_entry { unsigned char type; unsigned char length; unsigned short handle; }; enum { DMI_MEMORY_ARRAY = 16, DMI_MEMORY_DEVICE = 17, DMI_MEMORY_ARRAY_ADDR = 19, DMI_MEMORY_MAPPED_ADDR = 20, }; struct dmi_memdev_addr { struct dmi_entry header; unsigned start_addr; unsigned end_addr; unsigned short dev_handle; unsigned short memarray_handle; unsigned char row; unsigned char interleave_pos; unsigned char interleave_depth; } __attribute__((packed)); struct dmi_memdev { struct dmi_entry header; unsigned short array_handle; unsigned short memerr_handle; unsigned short total_width; unsigned short data_width; unsigned short size; unsigned char form_factor; unsigned char device_set; unsigned char device_locator; unsigned char bank_locator; unsigned char memory_type; unsigned short type_details; unsigned short speed; unsigned char manufacturer; unsigned char serial_number; unsigned char asset_tag; unsigned char part_number; } __attribute__((packed)); struct dmi_memarray { struct dmi_entry header; unsigned char location; unsigned char use; unsigned char error_correction; unsigned int maximum_capacity; unsigned short error_handle; short num_devices; } __attribute__((packed)); struct dmi_memarray_addr { struct dmi_entry header; unsigned int start_addr; unsigned int end_addr; unsigned short array_handle; unsigned partition_width; } __attribute__((packed)); int opendmi(void); void dmi_decodeaddr(unsigned long addr); int dmi_sanity_check(void); unsigned dmi_dimm_size(unsigned short size, char *unit); struct dmi_memdev **dmi_find_addr(unsigned long addr); void dmi_set_verbosity(int v); char *dmi_getstring(struct dmi_entry *e, unsigned number); extern void checkdmi(void); void closedmi(void); /* valid after opendmi: */ extern struct dmi_memdev **dmi_dimms; extern struct dmi_memdev_addr **dmi_ranges; extern struct dmi_memarray **dmi_arrays; extern struct dmi_memarray_addr **dmi_array_ranges; extern int dmi_forced; extern int do_dmi; mcelog-100/eventloop.c0000664000175000017500000000653412247142742014440 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Event loop for mcelog daemon mode. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include #include "mcelog.h" #include "eventloop.h" #define MAX_POLLFD 10 static int max_pollfd; struct pollcb { poll_cb_t cb; int fd; void *data; }; static struct pollfd pollfds[MAX_POLLFD]; static struct pollcb pollcbs[MAX_POLLFD]; static sigset_t event_sigs; static int closeonexec(int fd) { int flags = fcntl(fd, F_GETFD); if (flags < 0 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { SYSERRprintf("Cannot set FD_CLOEXEC flag on fd"); return -1; } return 0; } int register_pollcb(int fd, int events, poll_cb_t cb, void *data) { int i = max_pollfd; if (closeonexec(fd) < 0) return -1; if (i >= MAX_POLLFD) { Eprintf("poll table overflow"); return -1; } max_pollfd++; pollfds[i].fd = fd; pollfds[i].events = events; pollcbs[i].cb = cb; pollcbs[i].data = data; return 0; } /* Could mark free and put into a free list */ void unregister_pollcb(struct pollfd *pfd) { int i = pfd - pollfds; assert(i >= 0 && i < max_pollfd); memmove(pollfds + i, pollfds + i + 1, (max_pollfd - i - 1) * sizeof(struct pollfd)); memmove(pollcbs + i, pollcbs + i + 1, (max_pollfd - i - 1) * sizeof(struct pollcb)); max_pollfd--; } static void poll_callbacks(int n) { int k; for (k = 0; k < max_pollfd && n > 0; k++) { struct pollfd *f = pollfds + k; if (f->revents) { struct pollcb *c = pollcbs + k; c->cb(f, c->data); n--; } } } /* Run signal handler only directly after event loop */ int event_signal(int sig) { static int first = 1; sigset_t mask; if (first && sigprocmask(SIG_BLOCK, NULL, &event_sigs) < 0) return -1; first = 0; if (sigprocmask(SIG_BLOCK, NULL, &mask) < 0) return -1; sigaddset(&mask, sig); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) return -1; return 0; } /* Handle old glibc without ppoll. */ static int ppoll_fallback(struct pollfd *pfd, nfds_t nfds, const struct timespec *ts, const sigset_t *ss) { sigset_t origmask; int ready; sigprocmask(SIG_SETMASK, ss, &origmask); ready = poll(pfd, nfds, ts ? ts->tv_sec : -1); sigprocmask(SIG_SETMASK, &origmask, NULL); return ready; } static int (*ppoll_vec)(struct pollfd *, nfds_t, const struct timespec *, const sigset_t *); void eventloop(void) { #if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 5 || __GLIBC__ > 2 ppoll_vec = ppoll; #endif if (!ppoll_vec) ppoll_vec = ppoll_fallback; for (;;) { int n = ppoll_vec(pollfds, max_pollfd, NULL, &event_sigs); if (n <= 0) { if (n < 0 && errno != EINTR) SYSERRprintf("poll error"); continue; } poll_callbacks(n); } } mcelog-100/triggers/0000775000175000017500000000000012247142742014077 5ustar vorlonvorlonmcelog-100/triggers/dimm-error-trigger0000775000175000017500000000227512247142742017551 0ustar vorlonvorlon#!/bin/sh # This shell script can be executed by mcelog in daemon mode when a DIMM # exceeds a pre-configured error threshold # # environment: # THRESHOLD human readable threshold status # MESSAGE Human readable consolidated error message # TOTALCOUNT total count of errors for current DIMM of CE/UC depending on # what triggered the event # LOCATION Consolidated location as a single string # DMI_LOCATION DIMM location from DMI/SMBIOS if available # DMI_NAME DIMM identifier from DMI/SMBIOS if available # DIMM DIMM number reported by hardware # CHANNEL Channel number reported by hardware # SOCKETID Socket ID of CPU that includes the memory controller with the DIMM # CECOUNT Total corrected error count for DIMM # UCCOUNT Total uncorrected error count for DIMM # LASTEVENT Time stamp of event that triggered threshold (in time_t format, seconds) # THRESHOLD_COUNT Total umber of events in current threshold time period of specific type # # note: will run as mcelog configured user # this can be changed in mcelog.conf logger -s -p daemon.err -t mcelog "$MESSAGE" logger -s -p daemon.err -t mcelog "Location: $LOCATION" [ -x ./dimm-error-trigger.local ] && . ./dimm-error-trigger.local exit 0 mcelog-100/triggers/socket-memory-error-trigger0000775000175000017500000000204112247142742021410 0ustar vorlonvorlon#!/bin/sh # This shell script can be executed by mcelog in daemon mode when a sockets # exceeds a pre-configured error threshold for memory errors # # environment: # THRESHOLD human readable threshold status # MESSAGE Human readable consolidated error message # TOTALCOUNT total count of errors for current socket of CE/UC depending on # what triggered the event # LOCATION Consolidated location as a single string # SOCKETID Socket ID of CPU that includes the memory controller with the DIMM # CECOUNT Total corrected error count for socket # UCCOUNT Total uncorrected error count for socket # LASTEVENT Time stamp of event that triggered threshold (in time_t format, seconds) # THRESHOLD_COUNT Total umber of events in current threshold time period of specific type # # note: will run as mcelog configured user # this can be changed in mcelog.conf logger -s -p daemon.err -t mcelog "$MESSAGE" logger -s -p daemon.err -t mcelog "Location: $LOCATION" [ -x ./socket-memory-error-trigger.local ] && . ./socket-memory-error-trigger.local exit 0 mcelog-100/triggers/page-error-trigger0000775000175000017500000000243412247142742017534 0ustar vorlonvorlon#!/bin/sh # This shell script can be executed by mcelog in daemon mode when a page # in memory exceeds a pre-configured corrected error threshold. # mcelog internally also supports offlining the page through the kernel. # # environment: # THRESHOLD human readable threshold status # MESSAGE Human readable consolidated error message # TOTALCOUNT total count of errors for current DIMM of CE/UC depending on # what triggered the event # LOCATION Consolidated location as a single string # DMI_LOCATION DIMM location from DMI/SMBIOS if available # DMI_NAME DIMM identifier from DMI/SMBIOS if available # DIMM DIMM number reported by hardware # CHANNEL Channel number reported by hardware # SOCKETID Socket ID of CPU that includes the memory controller with the DIMM # CECOUNT Total corrected error count for DIMM # UCCOUNT Total uncorrected error count for DIMM # LASTEVENT Time stamp of event that triggered threshold (in time_t format, seconds) # THRESHOLD_COUNT Total umber of events in current threshold time period of specific type # # note: will run as mcelog configured user # this can be changed in mcelog.conf logger -s -p daemon.err -t mcelog "$MESSAGE" logger -s -p daemon.err -t mcelog "Location: $LOCATION" [ -x ./page-error-trigger.local ] && . ./page-error-trigger.local exit 0 mcelog-100/triggers/cache-error-trigger0000775000175000017500000000220512247142742017657 0ustar vorlonvorlon#!/bin/bash # cache error trigger. This shell script is executed by mcelog in daemon mode # when a CPU reports excessive corrected cache errors. This could be a indication # for future uncorrected errors. # # environment: # MESSAGE Human readable error message # CPU Linux CPU number that triggered the error # LEVEL Cache level affected by error # TYPE Cache type affected by error (Data,Instruction,Generic) # AFFECTED_CPUS List of CPUs sharing the affected cache # SOCKETID Socket ID of affected CPU # # note: will run as mcelog configured user # this can be changed in mcelog.conf # # offline the CPUs (except CPU #0) sharing the affected cache # EXIT=0 for i in $AFFECTED_CPUS ; do if [ $i = 0 ] ; then logger -s -p daemon.warn -t mcelog "Not offlining CPU 0" EXIT=1 continue fi logger -s -p daemon.crit -t mcelog "Offlining CPU $i due to cache error threshold" F=$(printf "/sys/devices/system/cpu/cpu%d/online" $i) echo 0 > $F if [ "$(< $F)" != "0" ] ; then logger -s -p daemon.warn -t mcelog "Offlining CPU $i failed" EXIT=1 fi done [ -x ./cache-error-trigger.local ] && . ./cache-error-trigger.local exit $EXIT mcelog-100/sysfs.c0000664000175000017500000000467112247142742013574 0ustar vorlonvorlon/* Copyright (C) 2008 Intel Corporation Author: Andi Kleen Read/Write sysfs values mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include #include #include "mcelog.h" #include "sysfs.h" #include "memutil.h" char *read_field(char *base, char *name) { char *fn, *val; int n, fd; struct stat st; char *s; char *buf = NULL; asprintf(&fn, "%s/%s", base, name); fd = open(fn, O_RDONLY); if (fstat(fd, &st) < 0) goto bad; buf = xalloc(st.st_size); free(fn); if (fd < 0) goto bad; n = read(fd, buf, st.st_size); close(fd); if (n < 0) goto bad; val = xalloc(n); memcpy(val, buf, n); free(buf); s = strchr(val, '\n'); if (s) *s = 0; return val; bad: SYSERRprintf("Cannot read sysfs field %s/%s", base, name); return xstrdup(""); } unsigned read_field_num(char *base, char *name) { unsigned num; char *val = read_field(base, name); int n = sscanf(val, "%u", &num); free(val); if (n != 1) { Eprintf("Cannot parse number in sysfs field %s/%s\n", base,name); return 0; } return num; } unsigned read_field_map(char *base, char *name, struct map *map) { char *val = read_field(base, name); for (; map->name; map++) { if (!strcmp(val, map->name)) break; } free(val); if (map->name) return map->value; Eprintf("sysfs field %s/%s has unknown string value `%s'\n", base, name, val); return -1; } int sysfs_write(const char *name, const char *fmt, ...) { int e; int n; char *buf; va_list ap; int fd = open(name, O_WRONLY); if (fd < 0) return -1; va_start(ap, fmt); n = vasprintf(&buf, fmt, ap); va_end(ap); n = write(fd, buf, n); e = errno; close(fd); free(buf); errno = e; return n; } int sysfs_available(const char *name, int flags) { return access(name, flags) == 0; } mcelog-100/leaky-bucket.h0000664000175000017500000000135412247142742015005 0ustar vorlonvorlon#ifndef LEAKY_BUCKET_H #define LEAKY_BUCKET_H 1 #include /* Leaky bucket algorithm for triggers */ struct bucket_conf { unsigned capacity; unsigned agetime; unsigned char tunit; /* 'd','h','m','s' */ unsigned char log; char *trigger; }; struct leaky_bucket { unsigned count; unsigned excess; time_t tstamp; }; int bucket_account(const struct bucket_conf *c, struct leaky_bucket *b, unsigned inc); int __bucket_account(const struct bucket_conf *c, struct leaky_bucket *b, unsigned inc, time_t time); char *bucket_output(const struct bucket_conf *c, struct leaky_bucket *b); int bucket_conf_init(struct bucket_conf *c, const char *rate); void bucket_init(struct leaky_bucket *b); time_t bucket_time(void); #endif mcelog-100/bitfield.c0000664000175000017500000000235112247142742014200 0ustar vorlonvorlon#include #include #include "mcelog.h" #include "bitfield.h" char *reserved_3bits[8]; char *reserved_1bit[2]; char *reserved_2bits[4]; static u64 bitmask(u64 i) { u64 mask = 1; while (mask < i) mask = (mask << 1) | 1; return mask; } void decode_bitfield(u64 status, struct field *fields) { struct field *f; int linelen = 0; char *delim = ""; char buf[60]; int len; for (f = fields; f->str; f++) { u64 v = (status >> f->start_bit) & bitmask(f->stringlen - 1); char *s = NULL; if (v < f->stringlen) s = f->str[v]; if (!s) { if (v == 0) continue; s = buf; buf[(sizeof buf)-1] = 0; snprintf(buf, (sizeof buf) - 1, "<%u:%llx>", f->start_bit, v); } len = strlen(s); if (linelen + len > 75) { delim = "\n"; linelen = 0; } Wprintf("%s%s", delim, s); delim = " "; linelen += len + 1; } if (linelen > 0) Wprintf("\n"); } void decode_numfield(u64 status, struct numfield *fields) { struct numfield *f; for (f = fields; f->name; f++) { u64 mask = (1ULL << (f->end - f->start + 1)) - 1; u64 v = (status >> f->start) & mask; if (v > 0 || f->force) { char fmt[30]; snprintf(fmt, 30, "%%s: %s\n", f->fmt ? f->fmt : "%Lu"); Wprintf(fmt, f->name, v); } } } mcelog-100/client.c0000664000175000017500000000342012247142742013672 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Client code to talk to the mcelog server. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "mcelog.h" #include "client.h" #include "paths.h" #include "config.h" /* Send a command to the mcelog server and dump output */ void ask_server(char *command) { struct sockaddr_un sun; int fd; int n; char buf[1024]; int done; char *path = config_string("server", "socket-path"); if (!path) path = SOCKET_PATH; fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) SYSERRprintf("client socket"); sun.sun_family = AF_UNIX; sun.sun_path[sizeof(sun.sun_path)-1] = 0; strncpy(sun.sun_path, path, sizeof(sun.sun_path)-1); if (connect(fd, (struct sockaddr *)&sun, sizeof(struct sockaddr_un)) < 0) SYSERRprintf("client connect"); n = strlen(command); if (write(fd, command, n) != n) SYSERRprintf("client command write"); done = 0; while (!done && (n = read(fd, buf, sizeof buf)) > 0) { if (n >= 5 && !memcmp(buf + n - 5, "done\n", 5)) { n -= 5; done = 1; } write(1, buf, n); } if (n < 0) SYSERRprintf("client read"); } mcelog-100/trigger.c0000664000175000017500000000732512247142742014067 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Manage trigger commands running as separate processes. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include #include "trigger.h" #include "eventloop.h" #include "list.h" #include "mcelog.h" #include "memutil.h" #include "config.h" struct child { struct list_head nd; pid_t child; const char *name; }; static LIST_HEAD(childlist); static int num_children; static int children_max = 4; static char *trigger_dir; pid_t mcelog_fork(const char *name) { pid_t child; struct child *c; child = fork(); if (child <= 0) return child; num_children++; c = xalloc(sizeof(struct child)); c->name = name; c->child = child; list_add_tail(&c->nd, &childlist); return child; } // note: trigger must be allocated, e.g. from config void run_trigger(char *trigger, char *argv[], char **env) { pid_t child; char *fallback_argv[] = { trigger, NULL, }; if (!argv) argv = fallback_argv; Lprintf("Running trigger `%s'\n", trigger); if (children_max > 0 && num_children >= children_max) { Eprintf("Too many trigger children running already\n"); return; } child = mcelog_fork(trigger); if (child < 0) { SYSERRprintf("Cannot create process for trigger"); return; } if (child == 0) { if (trigger_dir) chdir(trigger_dir); execve(trigger, argv, env); _exit(127); } } /* Clean up child on SIGCHLD */ static void finish_child(pid_t child, int status) { struct child *c, *tmpc; list_for_each_entry_safe (c, tmpc, &childlist, nd) { if (c->child == child) { if (WIFEXITED(status) && WEXITSTATUS(status)) { Eprintf("Trigger `%s' exited with status %d\n", c->name, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { Eprintf("Trigger `%s' died with signal %s\n", c->name, strsignal(WTERMSIG(status))); } list_del(&c->nd); free(c); num_children--; return; } } abort(); } /* Runs only directly after ppoll */ static void child_handler(int sig, siginfo_t *si, void *ctx) { int status; if (waitpid(si->si_pid, &status, WNOHANG) < 0) { SYSERRprintf("Cannot collect child %d", si->si_pid); return; } finish_child(si->si_pid, status); } void trigger_setup(void) { char *s; struct sigaction sa = { .sa_sigaction = child_handler, .sa_flags = SA_SIGINFO|SA_NOCLDSTOP|SA_RESTART, }; sigaction(SIGCHLD, &sa, NULL); event_signal(SIGCHLD); config_number("trigger", "children-max", "%d", &children_max); s = config_string("trigger", "directory"); if (s) { if (access(s, R_OK|X_OK) < 0) SYSERRprintf("Cannot access trigger directory `%s'", s); trigger_dir = s; } } void trigger_wait(void) { int sig; sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); while (num_children > 0) { if (sigwait(&mask, &sig) < 0) SYSERRprintf("sigwait waiting for children"); } } int trigger_check(char *s) { char *name; int rc; if (trigger_dir) asprintf(&name, "%s/%s", trigger_dir, s); else name = s; rc = access(name, R_OK|X_OK); if (trigger_dir) free(name); return rc; } mcelog-100/rbtree.c0000664000175000017500000002051612247142742013704 0ustar vorlonvorlon/* Red Black Trees (C) 1999 Andrea Arcangeli (C) 2002 David Woodhouse Taken from the Linux 2.6.30 source with some minor modificatons. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA linux/lib/rbtree.c */ #include "rbtree.h" static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) { struct rb_node *right = node->rb_right; struct rb_node *parent = rb_parent(node); if ((node->rb_right = right->rb_left)) rb_set_parent(right->rb_left, node); right->rb_left = node; rb_set_parent(right, parent); if (parent) { if (node == parent->rb_left) parent->rb_left = right; else parent->rb_right = right; } else root->rb_node = right; rb_set_parent(node, right); } static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) { struct rb_node *left = node->rb_left; struct rb_node *parent = rb_parent(node); if ((node->rb_left = left->rb_right)) rb_set_parent(left->rb_right, node); left->rb_right = node; rb_set_parent(left, parent); if (parent) { if (node == parent->rb_right) parent->rb_right = left; else parent->rb_left = left; } else root->rb_node = left; rb_set_parent(node, left); } void rb_insert_color(struct rb_node *node, struct rb_root *root) { struct rb_node *parent, *gparent; while ((parent = rb_parent(node)) && rb_is_red(parent)) { gparent = rb_parent(parent); if (parent == gparent->rb_left) { { register struct rb_node *uncle = gparent->rb_right; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } if (parent->rb_right == node) { struct rb_node *tmp; __rb_rotate_left(parent, root); tmp = parent; parent = node; node = tmp; } rb_set_black(parent); rb_set_red(gparent); __rb_rotate_right(gparent, root); } else { { struct rb_node *uncle = gparent->rb_left; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } if (parent->rb_left == node) { struct rb_node *tmp; __rb_rotate_right(parent, root); tmp = parent; parent = node; node = tmp; } rb_set_black(parent); rb_set_red(gparent); __rb_rotate_left(gparent, root); } } rb_set_black(root->rb_node); } static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, struct rb_root *root) { struct rb_node *other; while ((!node || rb_is_black(node)) && node != root->rb_node) { if (parent->rb_left == node) { other = parent->rb_right; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_left(parent, root); other = parent->rb_right; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_right || rb_is_black(other->rb_right)) { rb_set_black(other->rb_left); rb_set_red(other); __rb_rotate_right(other, root); other = parent->rb_right; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_right); __rb_rotate_left(parent, root); node = root->rb_node; break; } } else { other = parent->rb_left; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_right(parent, root); other = parent->rb_left; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_left || rb_is_black(other->rb_left)) { rb_set_black(other->rb_right); rb_set_red(other); __rb_rotate_left(other, root); other = parent->rb_left; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_left); __rb_rotate_right(parent, root); node = root->rb_node; break; } } } if (node) rb_set_black(node); } void rb_erase(struct rb_node *node, struct rb_root *root) { struct rb_node *child, *parent; int color; if (!node->rb_left) child = node->rb_right; else if (!node->rb_right) child = node->rb_left; else { struct rb_node *old = node, *left; node = node->rb_right; while ((left = node->rb_left) != NULL) node = left; child = node->rb_right; parent = rb_parent(node); color = rb_color(node); if (child) rb_set_parent(child, parent); if (parent == old) { parent->rb_right = child; parent = node; } else parent->rb_left = child; node->rb_parent_color = old->rb_parent_color; node->rb_right = old->rb_right; node->rb_left = old->rb_left; if (rb_parent(old)) { if (rb_parent(old)->rb_left == old) rb_parent(old)->rb_left = node; else rb_parent(old)->rb_right = node; } else root->rb_node = node; rb_set_parent(old->rb_left, node); if (old->rb_right) rb_set_parent(old->rb_right, node); goto color; } parent = rb_parent(node); color = rb_color(node); if (child) rb_set_parent(child, parent); if (parent) { if (parent->rb_left == node) parent->rb_left = child; else parent->rb_right = child; } else root->rb_node = child; color: if (color == RB_BLACK) __rb_erase_color(child, parent, root); } /* * This function returns the first node (in sort order) of the tree. */ struct rb_node *rb_first(const struct rb_root *root) { struct rb_node *n; n = root->rb_node; if (!n) return NULL; while (n->rb_left) n = n->rb_left; return n; } struct rb_node *rb_last(const struct rb_root *root) { struct rb_node *n; n = root->rb_node; if (!n) return NULL; while (n->rb_right) n = n->rb_right; return n; } struct rb_node *rb_next(const struct rb_node *node) { struct rb_node *parent; if (rb_parent(node) == node) return NULL; /* If we have a right-hand child, go down and then left as far as we can. */ if (node->rb_right) { node = node->rb_right; while (node->rb_left) node=node->rb_left; return (struct rb_node *)node; } /* No right-hand children. Everything down and left is smaller than us, so any 'next' node must be in the general direction of our parent. Go up the tree; any time the ancestor is a right-hand child of its parent, keep going up. First time it's a left-hand child of its parent, said parent is our 'next' node. */ while ((parent = rb_parent(node)) && node == parent->rb_right) node = parent; return parent; } struct rb_node *rb_prev(const struct rb_node *node) { struct rb_node *parent; if (rb_parent(node) == node) return NULL; /* If we have a left-hand child, go down and then right as far as we can. */ if (node->rb_left) { node = node->rb_left; while (node->rb_right) node=node->rb_right; return (struct rb_node *)node; } /* No left-hand children. Go up till we find an ancestor which is a right-hand child of its parent */ while ((parent = rb_parent(node)) && node == parent->rb_left) node = parent; return parent; } void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { struct rb_node *parent = rb_parent(victim); /* Set the surrounding nodes to point to the replacement */ if (parent) { if (victim == parent->rb_left) parent->rb_left = new; else parent->rb_right = new; } else { root->rb_node = new; } if (victim->rb_left) rb_set_parent(victim->rb_left, new); if (victim->rb_right) rb_set_parent(victim->rb_right, new); /* Copy the pointers/colour from the victim to the replacement */ *new = *victim; } mcelog-100/mcelog.cron0000775000175000017500000000010712247142742014403 0ustar vorlonvorlon#!/bin/bash /usr/sbin/mcelog --ignorenodev --filter >> /var/log/mcelog mcelog-100/page.c0000664000175000017500000001504712247142742013340 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Memory error accounting per page mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* NB investigate other data structures. Primary consideration would be space efficiency. rbtree nodes are rather large. Do we need aging? Right now the only way to get rid of old nodes is to restart. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include "memutil.h" #include "mcelog.h" #include "rbtree.h" #include "leaky-bucket.h" #include "page.h" #include "config.h" #include "memdb.h" #include "sysfs.h" #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) enum { PAGE_ONLINE = 0, PAGE_OFFLINE = 1, PAGE_OFFLINE_FAILED = 2 }; struct mempage { struct rb_node nd; u64 addr; struct err_type ce; char offlined; char triggered; // 2(32bit)-6(64bit) bytes of padding to play with here }; static struct rb_root mempage_root; static struct bucket_conf page_trigger_conf; static const char *page_state[] = { [PAGE_ONLINE] = "online", [PAGE_OFFLINE] = "offline", [PAGE_OFFLINE_FAILED] = "offline-failed", }; static struct mempage *mempage_lookup(u64 addr) { struct rb_node *n = mempage_root.rb_node; while (n) { struct mempage *mp = rb_entry(n, struct mempage, nd); if (addr < mp->addr) n = n->rb_left; else if (addr > mp->addr) n = n->rb_right; else return mp; } return NULL; } static struct mempage * mempage_insert_lookup(u64 addr, struct rb_node * node) { struct rb_node **p = &mempage_root.rb_node; struct rb_node *parent = NULL; struct mempage *mp; while (*p) { parent = *p; mp = rb_entry(parent, struct mempage, nd); if (addr < mp->addr) p = &(*p)->rb_left; else if (addr > mp->addr) p = &(*p)->rb_right; else return mp; } rb_link_node(node, parent, p); return NULL; } static struct mempage *mempage_insert(u64 addr, struct mempage *mp) { mp->addr = addr; mp = mempage_insert_lookup(addr, &mp->nd); if (mp != NULL) rb_insert_color(&mp->nd, &mempage_root); return mp; } /* Following arrays need to be all kept in sync with the enum */ enum otype { OFFLINE_OFF, OFFLINE_ACCOUNT, OFFLINE_SOFT, OFFLINE_HARD, OFFLINE_SOFT_THEN_HARD }; static const char *kernel_offline[] = { [OFFLINE_SOFT] = "/sys/devices/system/memory/soft_offline_page", [OFFLINE_HARD] = "/sys/devices/system/memory/hard_offline_page", [OFFLINE_SOFT_THEN_HARD] = "/sys/devices/system/memory/soft_offline_page" }; static struct config_choice offline_choice[] = { { "off", OFFLINE_OFF }, { "account", OFFLINE_ACCOUNT }, { "soft", OFFLINE_SOFT }, { "hard", OFFLINE_HARD }, { "soft-then-hard", OFFLINE_SOFT_THEN_HARD }, {} }; static enum otype offline = OFFLINE_OFF; static int do_memory_offline(u64 addr, enum otype type) { return sysfs_write(kernel_offline[type], "%#llx", addr); } static int memory_offline(u64 addr) { if (offline == OFFLINE_SOFT_THEN_HARD) { if (do_memory_offline(addr, OFFLINE_SOFT) < 0) { Lprintf("Soft offlining of page %llx failed, trying hard offlining\n", addr); return do_memory_offline(addr, OFFLINE_HARD); } return 0; } return do_memory_offline(addr, offline); } static void offline_action(struct mempage *mp, u64 addr) { if (offline <= OFFLINE_ACCOUNT) return; Lprintf("Offlining page %llx\n", addr); if (memory_offline(addr) < 0) { Lprintf("Offlining page %llx failed: %s\n", addr, strerror(errno)); mp->offlined = PAGE_OFFLINE_FAILED; } else mp->offlined = PAGE_OFFLINE; } void account_page_error(struct mce *m, int channel, int dimm) { u64 addr = m->addr; struct mempage *mp; time_t t; unsigned cpu = m->extcpu ? m->extcpu : m->cpu; if (offline == OFFLINE_OFF) return; if (!(m->status & MCI_STATUS_ADDRV) || (m->status & MCI_STATUS_UC)) return; switch (cputype) { case CPU_SANDY_BRIDGE_EP: /* * On SNB-EP platform we see corrected errors reported with * address in Bank 5 from hardware (depending on BIOS setting), * in the meanwhile, a duplicate record constructed from * information found by "firmware first" APEI code. Ignore the * duplicate information so that we don't double count errors. * * NOTE: the record from APEI fake this error from CPU 0 BANK 1. */ if (m->bank == 1 && cpu == 0) return; default: break; } t = m->time; addr &= ~((u64)PAGE_SIZE - 1); mp = mempage_lookup(addr); if (!mp) { mp = xalloc(sizeof(struct mempage)); bucket_init(&mp->ce.bucket); mempage_insert(addr, mp); } ++mp->ce.count; if (__bucket_account(&page_trigger_conf, &mp->ce.bucket, 1, t)) { struct memdimm *md; char *msg; char *thresh; if (mp->offlined != PAGE_ONLINE) return; /* Only do triggers and messages for online pages */ thresh = bucket_output(&page_trigger_conf, &mp->ce.bucket); md = get_memdimm(m->socketid, channel, dimm, 1); asprintf(&msg, "Corrected memory errors on page %llx exceed threshold %s", addr, thresh); free(thresh); memdb_trigger(msg, md, t, &mp->ce, &page_trigger_conf); free(msg); mp->triggered = 1; offline_action(mp, addr); } } void dump_page_errors(FILE *f) { char *msg; struct rb_node *r; long k; k = 0; for (r = rb_first(&mempage_root); r; r = rb_next(r)) { struct mempage *p = rb_entry(r, struct mempage, nd); if (k++ == 0) fprintf(f, "Per page corrected memory statistics:\n"); msg = bucket_output(&page_trigger_conf, &p->ce.bucket); fprintf(f, "%llx: total %lu seen \"%s\" %s%s\n", p->addr, p->ce.count, msg, page_state[(unsigned)p->offlined], p->triggered ? " triggered" : ""); free(msg); fputc('\n', f); } } void page_setup(void) { int n; config_trigger("page", "memory-ce", &page_trigger_conf); n = config_choice("page", "memory-ce-action", offline_choice); if (n >= 0) offline = n; if (offline > OFFLINE_ACCOUNT && !sysfs_available(kernel_offline[offline], W_OK)) { Lprintf("Kernel does not support page offline interface\n"); offline = OFFLINE_ACCOUNT; } } mcelog-100/sysfs.h0000664000175000017500000000053512247142742013574 0ustar vorlonvorlon struct map { char *name; int value; }; char *read_field(char *base, char *name); unsigned read_field_num(char *base, char *name); unsigned read_field_map(char *base, char *name, struct map *map); int sysfs_write(const char *name, const char *format, ...) __attribute__((format(printf,2,3))); int sysfs_available(const char *name, int flags); mcelog-100/rbtree.h0000664000175000017500000001175612247142742013717 0ustar vorlonvorlon/* Red Black Trees (C) 1999 Andrea Arcangeli Taken from the Linux 2.6.30 source. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA linux/include/linux/rbtree.h To use rbtrees you'll have to implement your own insert and search cores. This will avoid us to use callbacks and to drop drammatically performances. I know it's not the cleaner way, but in C (not in C++) to get performances and genericity... Some example of insert and search follows here. The search is a plain normal search over an ordered tree. The insert instead must be implemented int two steps: as first thing the code must insert the element in order as a red leaf in the tree, then the support library function rb_insert_color() must be called. Such function will do the not trivial work to rebalance the rbtree if necessary. ----------------------------------------------------------------------- static inline struct page * rb_search_page_cache(struct inode * inode, unsigned long offset) { struct rb_node * n = inode->i_rb_page_cache.rb_node; struct page * page; while (n) { page = rb_entry(n, struct page, rb_page_cache); if (offset < page->offset) n = n->rb_left; else if (offset > page->offset) n = n->rb_right; else return page; } return NULL; } static inline struct page * __rb_insert_page_cache(struct inode * inode, unsigned long offset, struct rb_node * node) { struct rb_node ** p = &inode->i_rb_page_cache.rb_node; struct rb_node * parent = NULL; struct page * page; while (*p) { parent = *p; page = rb_entry(parent, struct page, rb_page_cache); if (offset < page->offset) p = &(*p)->rb_left; else if (offset > page->offset) p = &(*p)->rb_right; else return page; } rb_link_node(node, parent, p); return NULL; } static inline struct page * rb_insert_page_cache(struct inode * inode, unsigned long offset, struct rb_node * node) { struct page * ret; if ((ret = __rb_insert_page_cache(inode, offset, node))) goto out; rb_insert_color(node, &inode->i_rb_page_cache); out: return ret; } ----------------------------------------------------------------------- */ #ifndef _LINUX_RBTREE_H #define _LINUX_RBTREE_H #include #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct rb_node { unsigned long rb_parent_color; #define RB_RED 0 #define RB_BLACK 1 struct rb_node *rb_right; struct rb_node *rb_left; } __attribute__((aligned(sizeof(long)))); /* The alignment might seem pointless, but allegedly CRIS needs it */ struct rb_root { struct rb_node *rb_node; }; #define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) #define rb_color(r) ((r)->rb_parent_color & 1) #define rb_is_red(r) (!rb_color(r)) #define rb_is_black(r) rb_color(r) #define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) #define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) { rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; } static inline void rb_set_color(struct rb_node *rb, int color) { rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; } #define RB_ROOT (struct rb_root) { NULL, } #define rb_entry(ptr, type, member) container_of(ptr, type, member) #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) #define RB_EMPTY_NODE(node) (rb_parent(node) == node) #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ extern struct rb_node *rb_next(const struct rb_node *); extern struct rb_node *rb_prev(const struct rb_node *); extern struct rb_node *rb_first(const struct rb_root *); extern struct rb_node *rb_last(const struct rb_root *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link) { node->rb_parent_color = (unsigned long )parent; node->rb_left = node->rb_right = NULL; *rb_link = node; } #endif /* _LINUX_RBTREE_H */ mcelog-100/CHANGES0000664000175000017500000001137312247142742013251 0ustar vorlonvorlon Add Linux Kongress 2010 paper Add Sandy Bridge Support Write pid file by default in daemon mode Reopen log files on SIGUSR1 in daemon mode Default --daemon mode to logging to /var/log/mcelog Add Core i3/5 and more Westmere support Add Xeon75xx support Add Intel architectural mca fallback for new family 6 Intel CPUs Add --pidfile option to write daemon pid into file. Enable DIMM, socket, cache tracking and per page tracking by default Support changing mcelog daemon to other user Disabled by default because this breaks the advanced triggers which need root rights. Allow to disable memory error logging Add per socket memory accounting/trigger support Add per page memory error accounting and predictive page offlining Support for accounting errors per memory page and offlining pages and executing shell script triggers when error thresholds are exceeded. Offlining requires a new kernel that supports page offlining. Works with specific Intel systems with integrated memory controller Add cache error ``yellow bit trigger'' support An cache error threshold indication on Intel CPUs can execute a special trigger program to offline affected CPUs. Add in memory dimm database in daemon mode Errors are accounted per DIMM and can execute shell scripts when error thresholds are exceeded. This can be all configured using the configuration file. Add mcelog --client to query a running mcelog daemon for errors Works only on specific Intel systems for now with integrated memory controllers that report DIMM errors. Keep stdout open in daemon mode when no syslog Add --foreground option Avoid %a scanf extension that broke old glibc version Fixes to Tulsa and Dunngton decoding (Youkang Song) Fixes to Nehalem decoding and (Ted Barragy, Hidetoshi Seto) Fix parallel logging in daemon mode Fixes to the manpage (Thomas Renninger) More consistent number printing (Huang Ying) mcelog builds now with more gcc warning options enabled Improved man page; now actually describing what a machine check is Fix and finally document TSC decoding (still doesn't work everywhere and is obsolete now) Add support for --ascii --file option to specify input file Add support for /etc/mcelog.conf config file. This can set all options on the command line and some more. Add new --cpu=cputype option and deprecate old k8/core2/generic/intel-cpu options Use official Nehalem names Support re-parsing mcelog output with --ascii Add Intel Tulsa (Xeon 71xx) decoding support Add Dunnington (Xeon 7400) decoding support Add Nehalem decoding support Add --no-syslog option Various bug fixes Add --logfile option Implement daemon mode Various bug fixes to the decoder Add --intel-family=... argument Better decoding of core2 events and explicit decoding of pre Core P6 cores Add decoding of broadcast timeouts Add --version argument Fix parsing of multiple fields on a line in --ascii Add --raw mode for easier machine decoding (Ying Huang) Skip printk timestamp headers in --ascii (Ying Huang) Support more than 255 CPUs (requires updated kernel) Update for upcoming kernel interface "struct mce" extensions Support AMD Fam10h/11h CPUs (Joachim Deguara) Add switch to use LOG_ERR for syslog messages Various misc. cleanups mcelog now logs summaries of some serious events into syslog by default Fix syslog multiline logging Separate decoding output and error messages Automatic dependency generation in Makefile Many cleanups in DMI decoding Add DIMM database and error triggers Automatic sanity check for DMI information and enable by default Add support for decoding Intel Core2 machine checks Simple decoding of the TSC value into uptime for Intel CPUs Add the MCE design paper. Decode Intel thermal events properly Add some "RAMs" to K8 ECC strings in futile hope that users will get the hint Allow modifier command line options after --ascii Don't print decoded address twice for --ascii Fix SMBIOS anchor scan to work on more machines and don't crash when no anchor found. Fix --ascii reparsing of mcelog output. Add --filter and filter out known broken K8 GART errors Add --ignorenodev argument and use in cron script (avoids cron errors in Xen guest kernels) Add new --dmi argument to look up machine check addresses in SMBIOS (warning unreliable due to wide spread bios bugs) Fix argument decoding (support --, allow arguments in any order) Clarify --ascii in the manpage Support for AMD K8 Revision F machine check DRAM error thresholding from Jacob Shin Add P4 decoder contributed by "Guo, Racing" for Intel P4 and Xeon. Add K8 decoder from 2.4 kernel code to decode Opteron/Athlon64 logs. (code mostly from Eric Morton and Andi Kleen) Add --ascii function to decode fatal kernel output. Improve manpage Fix 32bit bugs Fix uninitialized variable in check_cpu Minor cleanups mcelog-100/config.h0000664000175000017500000000135212247142742013670 0ustar vorlonvorlon#include struct config_choice { char *name; int val; }; int config_choice(const char *header, const char *name, const struct config_choice *c); char *config_string(const char *header, const char *name); int config_number(const char *header, const char *name, char *fmt, void *val); int config_bool(const char *header, const char *name); int parse_config_file(const char *fn); const char *config_file(char **av, const char *deffn); struct option; void config_options(struct option *opts, int (*func)(int)); struct bucket_conf; int config_trigger(const char *header, const char *name, struct bucket_conf *bc); struct config_cred { uid_t uid; gid_t gid; }; void config_cred(char *header, char *name, struct config_cred *cred); mcelog-100/p4.h0000664000175000017500000000015712247142742012750 0ustar vorlonvorlonchar *intel_bank_name(int num); void decode_intel_mc(struct mce *log, int cpu, int *ismemerr, unsigned len); mcelog-100/Makefile0000664000175000017500000000624612247142742013721 0ustar vorlonvorlonCFLAGS := -g -Os prefix := /usr etcprefix := # Define appropiately for your distribution # DOCDIR := /usr/share/doc/packages/mcelog # Note when changing prefix: some of the non-critical files like # the manpage or the init script have hardcoded prefixes # Warning flags added implicitely to CFLAGS in the default rule # this is done so that even when CFLAGS are overriden we still get # the additional warnings # Some warnings require the global optimizer and are only output with # -O2/-Os, so that should be tested occasionally WARNINGS := -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter \ -Wstrict-prototypes -Wformat-security -Wmissing-declarations \ -Wdeclaration-after-statement # The on disk database has still many problems (partly in this code and partly # due to missing support from BIOS), so it's disabled by default. You can # enable it here by uncommenting the following line # CONFIG_DISKDB = 1 TRIGGERS=cache-error-trigger dimm-error-trigger page-error-trigger \ socket-memory-error-trigger all: mcelog .PHONY: install clean depend 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 \ xeon75xx.o sandy-bridge.o ivy-bridge.o msr.o DISKDB_OBJ := diskdb.o dimm.o db.o CLEAN := mcelog dmi tsc dbquery .depend .depend.X dbquery.o ${DISKDB_OBJ} DOC := mce.pdf ADD_DEFINES := ifdef CONFIG_DISKDB ADD_DEFINES := -DCONFIG_DISKDB=1 OBJ += ${DISKDB_OBJ} all: dbquery endif SRC := $(OBJ:.o=.c) mcelog: ${OBJ} # dbquery intentionally not installed by default install: mcelog mkdir -p $(DESTDIR)${etcprefix}/etc/mcelog $(DESTDIR)${prefix}/sbin $(DESTDIR)${prefix}/share/man/man8 install -m 755 -p mcelog $(DESTDIR)${prefix}/sbin/mcelog install -m 644 -p mcelog.8 $(DESTDIR)${prefix}/share/man/man8 install -m 644 -p -b mcelog.conf $(DESTDIR)${etcprefix}/etc/mcelog/mcelog.conf for i in ${TRIGGERS} ; do \ install -m 755 -p -b triggers/$$i $(DESTDIR)${etcprefix}/etc/mcelog ; \ done ifdef DOCDIR install -m 644 -p ${DOC} $(DESTDIR)${DOCDIR} else echo echo "Consider defining DOCDIR to install additional documentation" endif clean: test-clean rm -f ${CLEAN} ${OBJ} tsc: tsc.c gcc -o tsc ${CFLAGS} -DSTANDALONE tsc.c ${LDFLAGS} dbquery: db.o dbquery.o memutil.o depend: .depend %.o: %.c $(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(ADD_DEFINES) -o $@ $< .depend: ${SRC} ${CC} -MM -I. ${SRC} > .depend.X && mv .depend.X .depend include .depend Makefile: .depend .PHONY: iccverify src test # run the icc static verifier over sources. you need the intel compiler installed for this DISABLED_DIAGS := -diag-disable 188,271,869,2259,981,12072,181,12331,1572 iccverify: icc -Wall -diag-enable sv3 $(DISABLED_DIAGS) $(ADD_DEFINES) $(SRC) clangverify: clang --analyze $(ADD_DEFINES) $(SRC) src: echo $(SRC) config-test: config.c gcc -DTEST=1 config.c -o config-test test: $(MAKE) -C tests test DEBUG="" VALGRIND=valgrind --leak-check=full valgrind-test: $(MAKE) -C tests test DEBUG="${VALGRIND}" test-clean: $(MAKE) -C tests clean mcelog-100/intel.c0000664000175000017500000000760412247142742013537 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Common Intel CPU code. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "mcelog.h" #include "intel.h" #include "bitfield.h" #include "nehalem.h" #include "memdb.h" #include "page.h" #include "sandy-bridge.h" #include "ivy-bridge.h" #include "xeon75xx.h" int memory_error_support; void intel_cpu_init(enum cputype cpu) { if (cpu == CPU_NEHALEM || cpu == CPU_XEON75XX || cpu == CPU_INTEL || cpu == CPU_SANDY_BRIDGE || cpu == CPU_SANDY_BRIDGE_EP || cpu == CPU_IVY_BRIDGE || cpu == CPU_IVY_BRIDGE_EPEX || cpu == CPU_HASWELL) memory_error_support = 1; } enum cputype select_intel_cputype(int family, int model) { if (family == 15) { if (model == 6) return CPU_TULSA; return CPU_P4; } if (family == 6) { if (model >= 0x1a && model != 28) memory_error_support = 1; if (model < 0xf) return CPU_P6OLD; else if (model == 0xf || model == 0x17) /* Merom/Penryn */ return CPU_CORE2; else if (model == 0x1d) return CPU_DUNNINGTON; else if (model == 0x1a || model == 0x2c || model == 0x1e || model == 0x25) return CPU_NEHALEM; else if (model == 0x2e || model == 0x2f) return CPU_XEON75XX; else if (model == 0x2a) return CPU_SANDY_BRIDGE; else if (model == 0x2d) return CPU_SANDY_BRIDGE_EP; else if (model == 0x3a) return CPU_IVY_BRIDGE; else if (model == 0x3e) return CPU_IVY_BRIDGE_EPEX; else if (model == 0x3c || model == 0x45 || model == 0x46) return CPU_HASWELL; if (model > 0x1a) { Eprintf("Family 6 Model %x CPU: only decoding architectural errors\n", model); return CPU_INTEL; } } if (family > 6) { Eprintf("Family %u Model %x CPU: only decoding architectural errors\n", family, model); return CPU_INTEL; } Eprintf("Unknown Intel CPU type family %x model %x\n", family, model); return family == 6 ? CPU_P6OLD : CPU_GENERIC; } int is_intel_cpu(int cpu) { switch (cpu) { CASE_INTEL_CPUS: return 1; } return 0; } static int intel_memory_error(struct mce *m, unsigned recordlen) { u32 mca = m->status & 0xffff; if ((mca >> 7) == 1) { unsigned corr_err_cnt = 0; int channel[2] = { (mca & 0xf) == 0xf ? -1 : (int)(mca & 0xf), -1 }; int dimm[2] = { -1, -1 }; switch (cputype) { case CPU_NEHALEM: nehalem_memerr_misc(m, channel, dimm); break; case CPU_XEON75XX: xeon75xx_memory_error(m, recordlen, channel, dimm); break; case CPU_SANDY_BRIDGE_EP: sandy_bridge_ep_memerr_misc(m, channel, dimm); break; case CPU_IVY_BRIDGE_EPEX: ivy_bridge_ep_memerr_misc(m, channel, dimm); break; default: break; } if (recordlen > offsetof(struct mce, mcgcap) && m->mcgcap & MCG_CMCI_P) corr_err_cnt = EXTRACT(m->status, 38, 52); memory_error(m, channel[0], dimm[0], corr_err_cnt, recordlen); account_page_error(m, channel[0], dimm[0]); /* * When both DIMMs have a error account the error twice to the page. */ if (channel[1] != -1) { memory_error(m, channel[1], dimm[1], corr_err_cnt, recordlen); account_page_error(m, channel[1], dimm[1]); } return 1; } return 0; } /* No bugs known, but filter out memory errors if the user asked for it */ int mce_filter_intel(struct mce *m, unsigned recordlen) { if (intel_memory_error(m, recordlen) == 1) return !filter_memory_errors; return 1; } mcelog-100/eventloop.h0000664000175000017500000000035712247142742014442 0ustar vorlonvorlon#include typedef void (*poll_cb_t)(struct pollfd *pfd, void *data); int register_pollcb(int fd, int events, poll_cb_t cb, void *data); void unregister_pollcb(struct pollfd *pfd); void eventloop(void); int event_signal(int sig); mcelog-100/msr.c0000664000175000017500000000253512247142742013223 0ustar vorlonvorlon#include "mcelog.h" #include #include #include #include #include static void domsr(int cpu, int msr, int bit) { char fpath[32]; unsigned long long data; int fd; sprintf(fpath, "/dev/cpu/%d/msr", cpu); fd = open(fpath, O_RDWR); if (fd == -1) { switch (errno) { case ENOENT: SYSERRprintf("Warning: cpu %d offline?, imc_log not set\n", cpu); return; default: SYSERRprintf("Cannot open %s to set imc_log\n", fpath); exit(1); } } if (pread(fd, &data, sizeof data, msr) != sizeof data) { SYSERRprintf("Cannot read MSR_ERROR_CONTROL from %s\n", fpath); exit(1); } data |= bit; if (pwrite(fd, &data, sizeof data, msr) != sizeof data) { SYSERRprintf("Cannot write MSR_ERROR_CONTROL to %s\n", fpath); exit(1); } if (pread(fd, &data, sizeof data, msr) != sizeof data) { SYSERRprintf("Cannot re-read MSR_ERROR_CONTROL from %s\n", fpath); exit(1); } if ((data & bit) == 0) { SYSERRprintf("Failed to set imc_log on cpu %d\n", cpu); exit(1); } close(fd); } void set_imc_log(int cputype) { int cpu, ncpus = sysconf(_SC_NPROCESSORS_CONF); int msr, bit; switch (cputype) { case CPU_SANDY_BRIDGE_EP: case CPU_IVY_BRIDGE_EPEX: msr = 0x17f; /* MSR_ERROR_CONTROL */ bit = 0x2; /* MemError Log Enable */ break; } for (cpu = 0; cpu < ncpus; cpu++) domsr(cpu, msr, bit); } mcelog-100/memutil.h0000664000175000017500000000025012247142742014073 0ustar vorlonvorlon#include void *xalloc(size_t size); void *xalloc_nonzero(size_t size); void *xrealloc(void *old, size_t size); char *xstrdup(char *str); void Enomem(void); mcelog-100/tulsa.h0000664000175000017500000000005712247142742013554 0ustar vorlonvorlonvoid tulsa_decode_model(u64 status, u64 misc); mcelog-100/mcelog.conf0000664000175000017500000001365612247142742014401 0ustar vorlonvorlon# # Example config file for mcelog # mcelog is the user space backend that decodes and process machine check events # (cpu hardware errors) reported by the CPU to the kernel # # general format #optionname = value # white space is not allowed in value currently, except at the end where it is dropped # # in general all command line options that are not commands work here # see man mcelog or mcelog --help for a list # e.g. to enable the --no-syslog option use #no-syslog = yes (or no to disable) # when the option has a argument #logfile = /tmp/logfile # below are the options which are not command line options # Set CPU type for which mcelog decodes events: #cpu = type # for valid values for type please see mcelog --help # If this value is set incorrectly the decoded output will be likely incorrect. # by default when this parameter is not set mcelog uses the CPU it is running on # on very new kernels the mcelog events reported by the kernel also carry # the CPU type which is used too when available and not overriden. # Enable daemon mode: #daemon = yes # By default mcelog just processes the currently pending events and exits. # in daemon mode it will keep running as a daemon in the background and poll # the kernel for events and then decode them. # Filter out known broken events by default filter = yes # don't log memory errors individually # they still get accounted if that is enabled #filter-memory-errors = yes # output in undecoded raw format to be easier machine readable # (default is decoded) #raw = yes # Set CPU Mhz to decode uptime from time stamp counter (output # unreliable, not needed on new kernels which report the event time # directly. A lot of systems don't have a linear time stamp clock # and the output is wrong then. # Normally mcelog tries to figure out if it the TSC is reliable # and only uses the current frequency then. # Setting a frequency forces timestamp decoding. # This setting is obsolete with modern kernels which report the time # directly. #cpumhz = 1800.00 # log output options # Log decoded machine checks in syslog (default stdout or syslog for daemon) #syslog = yes # Log decoded machine checks in syslog with error level #syslog-error = yes # Never log anything to syslog #no-syslog = yes # Append log output to logfile instead of stdout. Only when no syslog logging is active #logfile = filename # Use SMBIOS information to decode DIMMs (needs root) # This function is not recommended to use right now and generally not needed # The exception is memdb prepopulation, which is configured separately below. #dmi = no # when in daemon mode run as this user after set up # note that the triggers will run as this user too # setting this to non root will mean that triggers cannot take some corrective # action, like offlining objects #run-credentials-user = root # group to run as daemon with # default to the group of the run-credentials-user #run-credentials-group = nobody [server] # user allowed to access client socket. # when set to * match any # root is always allowed to access # default: root only client-user = root # group allowed to access mcelog # when no group is configured any group matches (but still user checking) # when set to * match any #client-group = root # path to the unix socket for client<->server communication # when no socket-path is configured the server will not start #socket-path = /var/run/mcelog-client # when mcelog starts it checks if a server is already running. timeout # for this check. #initial-ping-timeout = 2 # [dimm] # Is the in memory DIMM error tracking enabled? # Only works on systems with integrated memory controller and # which are supported # Only takes effect in daemon mode dimm-tracking-enabled = yes # Use DMI information from the BIOS to prepopulate DIMM database # Note this might not work with all BIOS and requires mcelog to run as root. # Alternative is to let mcelog create DIMM objects on demand. dmi-prepopulate = yes # # execute these triggers when the rate of corrected or uncorrected # errors per DIMM exceeds the threshold # Note when the hardware does not report DIMMs this might also # be per channel # The default of 10/24h is reasonable for server quality # DDR3 DIMMs as of 2009/10 #uc-error-trigger = dimm-error-trigger uc-error-threshold = 1 / 24h #ce-error-trigger = dimm-error-trigger ce-error-threshold = 10 / 24h [socket] # Memory error accounting per socket socket-tracking-enabled = yes # Threshold and trigger for uncorrected memory errors on a socket # mem-uc-error-trigger = socket-memory-error-trigger mem-uc-error-threshold = 100 / 24h # Threshold and trigger for corrected memory errors on a socket mem-ce-error-trigger = socket-memory-error-trigger mem-ce-error-threshold = 100 / 24h # Log socket error threshold explicitely? mem-ce-error-log = yes [cache] # Processing of cache error thresholds reported by Intel CPUs cache-threshold-trigger = cache-error-trigger # Should cache threshold events be logged explicitely? cache-threshold-log = yes [page] # Memory error accouting per 4K memory page # Threshold for the correct memory errors trigger script memory-ce-threshold = 10 / 24h # Trigger script for corrected errors # memory-ce-trigger = page-error-trigger # Should page threshold events be logged explicitely? memory-ce-log = yes # specify the internal action in mcelog to exceeding a page error threshold # this is done in addition to executing the trigger script if available # off no action # account only account errors # soft try to soft-offline page without killing any processes # This requires an uptodate kernel. Might not be successfull. # hard try to hard-offline page by killing processes # Requires an uptodate kernel. Might not be successfull. # soft-then-hard First try to soft offline, then try hard offlining #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = soft [trigger] # Maximum number of running triggers children-max = 2 # execute triggers in this directory directory = /etc/mcelog mcelog-100/dimm.h0000664000175000017500000000036212247142742013351 0ustar vorlonvorlonvoid close_dimm_db(void); int open_dimm_db(char *fn); void new_error(unsigned long addr, unsigned long max_error, char *trigger); void reset_dimm(char *locator); void gc_dimms(void); void dump_all_dimms(void); void dump_dimm(char *locator); mcelog-100/server.h0000664000175000017500000000003112247142742013722 0ustar vorlonvorlonvoid server_setup(void); mcelog-100/diskdb.h0000664000175000017500000000144312247142742013664 0ustar vorlonvorlon #ifdef CONFIG_DISKDB enum diskdb_options { O_DATABASE = O_DISKDB, O_ERROR_TRIGGER, O_DUMP_MEMORY, O_RESET_MEMORY, O_DROP_OLD_MEMORY, }; void diskdb_resolve_addr(u64 addr); int diskdb_modifier(int opt); int diskdb_cmd(int opt, int ac, char **av); void diskdb_usage(void); #define DISKDB_OPTIONS \ { "database", 1, NULL, O_DATABASE }, \ { "error-trigger", 1, NULL, O_ERROR_TRIGGER }, \ { "dump-memory", 2, NULL, O_DUMP_MEMORY }, \ { "reset-memory", 2, NULL, O_RESET_MEMORY }, \ { "drop-old-memory", 0, NULL, O_DROP_OLD_MEMORY }, #else static inline void diskdb_resolve_addr(u64 addr) {} static inline int diskdb_modifier(int opt) { return 0; } static inline int diskdb_cmd(int opt, int ac, char **av) { return 0; } static inline void diskdb_usage(void) {} #define DISKDB_OPTIONS #endif mcelog-100/xeon75xx.c0000664000175000017500000000230212247142742014117 0ustar vorlonvorlon/* Copyright (C) 2009/2010 Intel Corporation Decode Intel Xeon75xx memory errors. Requires the mce-75xx.ko driver load. The core errors are the same as Nehalem. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author: Andi Kleen */ #include #include #include "mcelog.h" #include "xeon75xx.h" /* This used to decode the old xeon 75xx memory error aux format. But that has never been merged into mainline kernels, so removed it again. */ void xeon75xx_memory_error(struct mce *m, unsigned msize, int *channel, int *dimm) { } void xeon75xx_decode_dimm(struct mce *m, unsigned msize) { } mcelog-100/ivy-bridge.c0000664000175000017500000001040212247142742014453 0ustar vorlonvorlon/* Copyright (C) 2013 Intel Corporation Decode Intel Ivy Bridge specific machine check errors. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author: Tony Luck */ #include "mcelog.h" #include "bitfield.h" #include "ivy-bridge.h" #include "memdb.h" /* See IA32 SDM Vol3B Table 16-17 */ static char *pcu_1[] = { [0] = "No error", [1] = "Non_IMem_Sel", [2] = "I_Parity_Error", [3] = "Bad_OpCode", [4] = "I_Stack_Underflow", [5] = "I_Stack_Overflow", [6] = "D_Stack_Underflow", [7] = "D_Stack_Overflow", [8] = "Non-DMem_Sel", [9] = "D_Parity_Error" }; static char *pcu_2[] = { [0x00] = "No Error", [0x0D] = "MC_IMC_FORCE_SR_S3_TIMEOUT", [0x0E] = "MC_MC_CPD_UNCPD_ST_TIMEOUT", [0x0F] = "MC_PKGS_SAFE_WP_TIMEOUT", [0x43] = "MC_PECI_MAILBOX_QUIESCE_TIMEOUT", [0x44] = "MC_CRITICAL_VR_FAILED", [0x45] = "MC_ICC_MAX-NOTSUPPORTED", [0x5C] = "MC_MORE_THAN_ONE_LT_AGENT", [0x60] = "MC_INVALID_PKGS_REQ_PCH", [0x61] = "MC_INVALID_PKGS_REQ_QPI", [0x62] = "MC_INVALID_PKGS_RES_QPI", [0x63] = "MC_INVALID_PKGC_RES_PCH", [0x64] = "MC_INVALID_PKG_STATE_CONFIG", [0x70] = "MC_WATCHDG_TIMEOUT_PKGC_SLAVE", [0x71] = "MC_WATCHDG_TIMEOUT_PKGC_MASTER", [0x72] = "MC_WATCHDG_TIMEOUT_PKGS_MASTER", [0x7A] = "MC_HA_FAILSTS_CHANGE_DETECTED", [0x7B] = "MC_PCIE_R2PCIE-RW_BLOCK_ACK_TIMEOUT", [0x81] = "MC_RECOVERABLE_DIE_THERMAL_TOO_HOT", }; static struct field pcu_mc4[] = { FIELD(16, pcu_1), FIELD(24, pcu_2), {} }; /* See IA32 SDM Vol3B Table 16-18 */ static char *memctrl_1[] = { [0x001] = "Address parity error", [0x002] = "HA Wrt buffer Data parity error", [0x004] = "HA Wrt byte enable parity error", [0x008] = "Corrected patrol scrub error", [0x010] = "Uncorrected patrol scrub error", [0x020] = "Corrected spare error", [0x040] = "Uncorrected spare error", [0x080] = "Corrected memory read error", [0x100] = "iMC, WDB, parity errors", }; static struct field memctrl_mc9[] = { FIELD(16, memctrl_1), {} }; void ivb_decode_model(int cputype, int bank, u64 status, u64 misc) { switch (bank) { case 4: Wprintf("PCU: "); decode_bitfield(status, pcu_mc4); Wprintf("\n"); break; case 5: if (cputype == CPU_IVY_BRIDGE_EPEX) { /* MCACOD already decoded */ Wprintf("QPI\n"); } break; case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: Wprintf("MemCtrl: "); decode_bitfield(status, memctrl_mc9); Wprintf("\n"); break; } } /* * Ivy Bridge EP and EX processors (family 6, model 62) support additional * logging for corrected errors in the integrated memory controller (IMC) * banks. The mode is off by default, but can be enabled by setting the * "MemError Log Enable" * bit in MSR_ERROR_CONTROL (MSR 0x17f). * The SDM leaves it as an exercise for the reader to convert the * faling rank to a DIMM slot. */ static int failrank2dimm(unsigned failrank, int socket, int channel) { switch (failrank) { case 0: case 1: case 2: case 3: return 0; case 4: case 5: return 1; case 6: case 7: if (get_memdimm(socket, channel, 2, 0)) return 2; else return 1; } return -1; } void ivy_bridge_ep_memerr_misc(struct mce *m, int *channel, int *dimm) { u64 status = m->status; unsigned failrank, chan; /* Ignore unless this is an corrected extended error from an iMC bank */ if (!imc_log || m->bank < 9 || m->bank > 16 || (status & MCI_STATUS_UC) || !test_prefix(7, status & 0xefff)) return; chan = EXTRACT(status, 0, 3); if (chan == 0xf) return; if (EXTRACT(m->misc, 62, 62)) { failrank = EXTRACT(m->misc, 46, 50); dimm[0] = failrank2dimm(failrank, m->socketid, chan); channel[0] = chan; } if (EXTRACT(m->misc, 63, 63)) { failrank = EXTRACT(m->misc, 51, 55); dimm[1] = failrank2dimm(failrank, m->socketid, chan); channel[1] = chan; } } mcelog-100/memdb.h0000664000175000017500000000113712247142742013510 0ustar vorlonvorlon#include #include "leaky-bucket.h" struct err_type { struct leaky_bucket bucket; unsigned long count; }; enum printflags { DUMP_ALL = (1 << 0), DUMP_BIOS = (1 << 1), }; void prefill_memdb(void); void memdb_config(void); void dump_memory_errors(FILE *f, enum printflags flags); void memory_error(struct mce *m, int channel, int dimm, unsigned corr_err_cnt, unsigned recordlen); struct memdimm; void memdb_trigger(char *msg, struct memdimm *md, time_t t, struct err_type *et, struct bucket_conf *bc); struct memdimm *get_memdimm(int socketid, int channel, int dimm, int insert); mcelog-100/mce.pdf0000664000175000017500000032753512247142742013527 0ustar vorlonvorlon%PDF-1.3 3 0 obj << /Length 2451 /Filter /FlateDecode >> stream xڍr}oCU!yIS9ƕOC6,B_RMFwO(_t0K8ov?h Ka 3m}+xPUUN& ixAPT p&rKgEXD:n_"`cF q g% IX!~*-40^qSe VFiv^4gp܄yL$,ݤ`vv9% x۫m(E%|@q"@UOnkaxڳmM_3D5Â*X W'jJh& Ж7kՇ('&==!}lQQ4Q[pֲYY Vj栭z%h1Jy@m5Ē-.qDU=hQ[ %IL?|ӀAA^[M9]7nk2.G)Þ˜(N@/7n&]ʁ0C c+3qc+3:xNGvt-4d'!F;B.O7$(lBebg\yF!Z7Z[gHבX"chQ0RTfP{PdmWbTi u${)3{-ܓuŞ@; dznrvp#Q Q:=c@@pz'- &3c8$8S+R69O]j g<eÑرϔZ.ܡ2r I $A׳@p:Ç7>({nZW%Cꜷ 6I)ozN ?H@N>\.\|\nᠱ.Ay/&J>]Y!b";?|τkw`. quG(!7bbD)6y0&Sގm'EM>-X ̃o3@*@foloVke `%=2U(Ў_IE+^\pvȯ,x0'`IB6IռgSEQڽkV=uf0ݍ2Y"mt\$?|Ԕ3ľ)æM:1~̼qK`a| ];C{`~gB*cHQz0?3`)qohq{څ+4X;.{ |^AWE?.̸H^- ܽ{C5/cswP' d.>Mz|]w~z$a^2xʎ.rsL9DeN jg(~po;X+KC8UKF6 ]{? K\r!6endstream endobj 2 0 obj << /Type /Page /Contents 3 0 R /Resources 1 0 R /MediaBox [0 0 595.276 841.89] /Parent 19 0 R >> endobj 1 0 obj << /Font << /F15 6 0 R /F16 9 0 R /F25 12 0 R /F8 15 0 R /F26 18 0 R >> /ProcSet [ /PDF /Text ] >> endobj 22 0 obj << /Length 3637 /Filter /FlateDecode >> stream xڕZKs6WT-Mx3N0-ˤx~kDfU&4n_7. ai,QYض7n~y<OBP~7~L}Xn;~C-h@*zrHC$$RFSg%n֭SGd- = d)ky?3п/U9I C?OSɔmG|KU3ފC/I: Vϓ:HJwwþ1t-YYryUx {JI[b'aov7yvs vI[>ÇØhR֍;;=sGsfg5 Lռ;L☄^ԁ w.^MxP>; _5n]xVYÞLym+ӑleL%iwᅹfӫ礔@;ZD񋸯xphq:WJ,A>W^` pH|6[4=EU!,=S!wknSTug=3i:ֹeT(㭎iV ",V QSJo1OXtRF6$o8- JeQGAN8@-Ѻn/\ΊB#Ibആ;}ƚt.AR gUB1A@zzKг /wk [K N2*F>{)p4#nI-l"Przo_ pnPSbvjLx1wF{YG3enׂ@]<^kM4C{P3@ޢNނ]o.bD] ̜8@6HdپVLs'".A=3WL*l>4Mᔜ>' [`΁`W*"m LўK-)LڣK> 4rohrm-5GEjE3a9'C9ŅK1ӮVqn%J=Gn R~@E PCY#h2^ 73Lh:?]׉d}1k0s NۋEL]X^ Ѻ7EE;q~Qr-Z[ݍJkdBFJ֣rW E0_層# ͙Pi zeI|J;g=c8J=)_P1iW h5nlOFIp1~tW `K*%%Z*>9J5V2V: llܵ-CN]I\2:G80&-s0xHt$a^X8O(8T>˴Bzz|*ũ c+~gy8Z}E,s .F:CWh|kӂDDr+3;h{.j#E(0/P̔So-?t íBXj)CUClPpY/"L7aO{X(]+>E)JiG`}cGO2wORr(vƻ%%.<@L3)<f7='̋endstream endobj 21 0 obj << /Type /Page /Contents 22 0 R /Resources 20 0 R /MediaBox [0 0 595.276 841.89] /Parent 19 0 R >> endobj 20 0 obj << /Font << /F8 15 0 R /F7 25 0 R /F26 18 0 R /F18 28 0 R /F17 31 0 R >> /ProcSet [ /PDF /Text ] >> endobj 34 0 obj << /Length 3362 /Filter /FlateDecode >> stream xڍZYoF~FH6)r 6Xdvǃ<$yhDҤv~ͦ$0`Y}TWG+Z/ZDQfM,]lۻ>_a% {, LaJ$2 .(W\W]e-WZ]ƛN@K*xƦa(00ԮЕ퓧8pA[;iWGwσ%?YVp"MLC0>.V7ԛ&F*,~<8u.pSYQ[MPte0 5ux!paw2ÄGD=T㡖ήNX6whW`KNNT tcI{+p2pi-j",y m =A,n7R>}xB/3{`N[LaіЙ>qid EqrX GŃ6%pQJ`Ve;5%@zW|g0K38 8=НUvIK%J`LI3QG2W[mT4+qT=iTYI% Nbpu;!ρ'lo4pKksFpK9h\ǐ78cgm< j.t qG:CִU1(@s+4vi ڳ0hzLcF1Zi&7tԝ(ugukhkPpiBqKNXCP⏓9zGzg_t+ !RBu9]#ܐnLj%r}ptn7enbp kf-B'SteM+*iف-nSkXٳ4:Al99s.OkVKMH:_p=hJn1Ft ˆ&>pt]9(̃ j g䆛2Dx#2R;/ٺ5X3lq"9W(yJs$tjm0]ҭm [-*cޮH8ÓItjjͻ鸼##be'\[ C F[MP c~BN WΜq:n}UHhZye}q~koB!+nS7*ۨ=g(olx.$$"x^89^ ~YyQIb@b5 `t+d i4!fZZ5l+ }1?F,I?OZ}ph GӧyOߊPXut;ْ[L񟅾iZ^7Q_7fnU&a5W]F|Y1+ՁûKHnaEq ``i7=56g)B9b${ KL*rQ 9Vt;Hn(~#(1 Y˼iqyR B,qݾdu+Qd1fΧ5kôLمL=<{%8*a72ċxcblYKQG9ذQXkcM<6(iBy @yr|嵏=/uǡPL'#?^0 Kjlfjʏb)7Cjʣ@'QD O8h^2}|G|=6HT;',58y wQ&=j9:#˶ۓ)5Lrku>+V7z֌nn4ʂU3y??bQ& u!_/(1 :S#U^R * OA"Y8+k^.5 ?I،X-J/$-g?V%!5r%`!H;6tÉ/ &AUD{c"CS*T҃fe7͔ŧU`:K&сej*D"Gܣ4`(!i*O>ɕ :qsiLq~OM߶Ծ 18Q58ɚqҊzdLœz?:{& 7e^?reƊ񄴈}hI-VE2#`wFw+%dendstream endobj 33 0 obj << /Type /Page /Contents 34 0 R /Resources 32 0 R /MediaBox [0 0 595.276 841.89] /Parent 19 0 R >> endobj 32 0 obj << /Font << /F8 15 0 R /F26 18 0 R /F7 25 0 R /F18 28 0 R /F17 31 0 R >> /ProcSet [ /PDF /Text ] >> endobj 37 0 obj << /Length 3790 /Filter /FlateDecode >> stream xڍZY6~_Xxd'@&dҲq{VWg b,릂O"S/EF&LJ?+h~˯*Xv: LѲf5vF?a3P2E$=C]_T eۺY{bdCH+ۜsҐ@ҷ;sa;ߗ/5'EEXXyD$b5U]LnK7eR{<h!]yP{ ؜z/R*D!j]TxHM.77rLmX}XƖN8_FdI)ߣ6tKfAta9!}|'~'6E6NF)a5gj T]HUcl;z1Bʱ4cTzԈ*bǛMXZ%=~g3U=R/'(۱0+HAOu375fHAVX@::ex.V+n!ܢh{zvɺ;s9 zG3iIguZ 0cJެkdb%AȢ/Վkx砛^0ٰ}S(NbQBzO 9+SNcvnyLF"uxy2}A"Q ׼,GJS=C+ȩtAC^Up=#:,S&Y%x ti ipő(&1[sģI^ hZi7 c cVJzPV{0B缚%yQ% +vvjt w6L3ޗ-Rb0[al3NQeS _̄D[tzjՓ*nl%|δhƱ@l8KP;[QVYlr%m3F-jR 4ܑUZa|AAc<# d{~A}Hb*ds #yjb*ȩm$72pwRz֋wLGԥm-1BGQ{Pp}w}YԺ<a~nb1P>ExK0M=4KeODtI8HvT9:˖NwB\h;TE>q6G}lJ pӯ) V`~nw3F/IPgݦ'Gy$+-Ć094? r6/ *,IE,\$lP;,{kp@/(ډChĞ B(wXnO.E|MeIB2ȧDpnMMWӶ6llMH#(ZJc#$["(@ͩ: qW_R()c#AS`1N*:[dj =>^&|4BUϤ|dBL%k3y?'D\-%SgK|`0ׇ0ԴagaId8%.H~B鰆U7[Ȭ hYƹAq=#47n)hazV ׂ^kfu3Iݎ3Gf|Ms* (S lTbJha&76nr< *ɭyaNDQ"v*&:7 8gܯaR{5(6ёXUfk80amA6 /H˜щ+#3iv2^X%i_LKAn]I ΀y<1z!U bpoUivO>`[iq=ʲoٝA+ xV̱[g0{MZ:$b4 lyO H9*)xgS͜2GT_"< we͉s.ѕ isn\|OǍ}TJ_zFQo&ׇA3 =W2K#,ϥ)p:ljXRMysk!^_Me'j#E3K/ L8G80O Rf7eݺZyCM M_.RD-]Dqz]XC>>͙$O(<|/EE8QX8 >/q/:8߯R,w?`7 [d4 RjI(wK )k"w)qŋ`B 7j:wykPN8'yEs"5Kct8~Bb7r`b|d|q>v;/4i 3_*s f¬"ODT]4_W:}'O)D&9$Z1gKO㏯%wm ď~W:"|J<8v# '|P9KۺD:(f`TՖ} 9癯|*endstream endobj 36 0 obj << /Type /Page /Contents 37 0 R /Resources 35 0 R /MediaBox [0 0 595.276 841.89] /Parent 19 0 R >> endobj 35 0 obj << /Font << /F8 15 0 R /F26 18 0 R /F7 25 0 R /F18 28 0 R /F17 31 0 R >> /ProcSet [ /PDF /Text ] >> endobj 40 0 obj << /Length 2448 /Filter /FlateDecode >> stream xڭY]BTqyJlj61.9C%֪E{kFJYo°97}2?2 C?[QYwf7Y}ɻfN{ßSz堪~zCoVk ISB5aǿ 0 "/cMi/uӣQ ̐zuEgp[~ҠlIw{zYspT R_93eoGe $0 zn([~grl~ԍDyjWΐ.W gtx3_F8NdA'QݫFڠi1|^Y$T=:XZίzrl7_ asKao($ez X*[qgCxŹ_G|(%Bo$dl&~d.qoVD-=&*4 lظFu7TdpFM r1bD0Ѿ8~{@6xTAE=w@DN4NɵŀpmrZdrݨ79fEYSYAaKx]M~єSXrقyC9wxtǔeo GƇ([~2ape0M*bDӣZUSqkw:Lf$b/Tx2=z U87a(,\<~A{2]x^e08*w^޵ A^PCp  IGG$K2W:cϔ̈[f=!8!οEXHa9E@oaՁҁ5/Nv@].+QkҸ#4C!| 2rߖժ$xts@y="`0ϓ)$UVu"- G0/N=e=2gng[骟f!xKv =ATSA^xOJ>p&"Aw%?-mSWU7SH`u /ZILydh dL#GYvcXo O|4/trڒ;!$눶;mL N4܃rN dTA+Vpm7}7yZ8T>[ȱc:f7/FBFwM1?0<J޼q$[F.vkX^ŕC [ʨvm?^zL c?n8q3q͟eu ~Fst⧯Lh6QGOlNx_A|ˆ:57vbpHT{s63%@1fpkĸp(f|Ip_L.<.G0>SXVI]M|\V[5a87;- 4PqYm݆ꉝ@;~ʿa%^f:Ɂz4//)q~X%I5ܰk =K1x'ațFWZ,G1+T[J׊swM#fU\J|9 ʨv(Y{=/˕˃z=2uyɟV(YR&dܶ+ڠ)x >nrQԏE_Aos1 %Fƅ1NWSa4zSS_d2eq3r+n: i\GA+Z0 j7BW>,跅blr"HcJ>*&{3ڕ4aK0oI> endobj 38 0 obj << /Font << /F29 43 0 R /F26 18 0 R /F8 15 0 R /F30 46 0 R /F7 25 0 R /F14 49 0 R /F18 28 0 R /F17 31 0 R >> /ProcSet [ /PDF /Text ] >> endobj 52 0 obj << /Length 3785 /Filter /FlateDecode >> stream xڥZKs6WHUE AI*Nv2gw`C!_ʲ*h4nH"S+T,]ګo﮾!_aݕ0zi`ʵ U -:΃/ui;KRwMP1G tݕܤy#'^G*OWQ|(TEj 5W&xk3Pq"Vc~mf2LA ݄wڨL1)iJkO=/Ȫ_`zP#w+ArKƒLYc>=̱#jغ}dY굣ajJv Ah_Q f {,k&n UM^$i~E,C:]XS&m[~T2㝸b O_n@d)ۿ8AHSIE$SkvfᢢtY^o٠%:P_xv2;}5!kvEp#Cv=`5 g}kV"7&e{pGzCyӪyF3$!K?lWYXlmaxRLY9f&7qMXfT˂[{~hW;=eUUcdHN+^q2G2d{1k8R-n0blt?% "3X%5ѽU<-s*r#.{|jxd~ظ#4#/(+e潾T$ab1Lh`t8UPOx$w7} ->`Q8=}kpBlf5㞶qGϴ{le! XM3S6Q&;5ɂCevo]ָuF(qLI$ R(%Tra&Å>{.vz=2Llz#Ĵ#*?\05[2fߏ7k"F<_--Χ60ؘ]l;2h 4xˤ! 2)|oƏ\:KxK>:|>tjO',r H}u|-, OI\ |1| 4`HGna ngKBbF'Ȍ|UwA}IDΜzN@spBn7VKEU]Q %aTg^bq F^oiʦܞgMU;q;/ߡQRPz#< 76P^x_<S"B~xs)'SDu` OvX)/$>9Ilma?cRD6̹,hAJ=;lΠ^.hx@HG>dӍO,}/eȶ:X}wXh$~a@%m4DZƞٯad.Bȗ9|6 s`RJ&MX6YS0aw #hXx\ܕj썇MRºA ~uЋ_{}pFS5x@TK "xC,`@Qn>}DkvN"-S y͚ QRsb"?k s8:S~YYE%trGx8͛ԑaԋh?%O=ym6}`ko Gc`ƃz +g?T' D+uچ6K=>sx5^]8'kVBL2WHb1%>'N|@ Y'q鼼!i|9iBשּW*E98Must}ƗKϸIǾu*^!O鮜W@ Ek:'^TedF>3N@!%ؔJ3ڛ~\F[y%njd7:3)jǓMȢB1!&+*>Z:of7p`hHوh> endobj 50 0 obj << /Font << /F8 15 0 R /F7 25 0 R /F26 18 0 R /F18 28 0 R /F17 31 0 R >> /ProcSet [ /PDF /Text ] >> endobj 55 0 obj << /Length 1741 /Filter /FlateDecode >> stream xڭXYs6~ׯ[M1o򑌓zFvn 8桂%R$N],v{ q(AzI2>Cߖ>ąFEM_}>e/V(Z>Hʫ6H_K>JYCG Ai<|2lƈLjdbO/9X;H93V#x6;q&.TLјA&#K䔠: uC;9On-,Y7`@S8DD4c/ h)>spzE!䖧p=h?DS~KkVh7jűV ?J˗LC%Χ3 :,xA뽢&!¢?bECq?p&ދM~>_.lWo0bB:#MIEwN+ 89ԽK k-C7 <7J3P<|STT&.$T?厶8ŰE Ina`6w"`C@r#bE2{Sԓ( \*neZL/h5vYv(0xTka9S5 0ە:74uv:Mei?v;\Tͱ\&#O PG(;ܹH#.oZdU3w5{MoXZvA<\ép3Yo/ utC:U?wMtY;}{?O9$>t(~'Knuΐk׶_}B"5Á`FpxvO4찑zyx -xbYuS|oiutaj]ߏxl G_q1F ƞgٸQ8-F ,{)$)|ZrGI↞ h%6w7TYk ׇ;7.@~wʵKu[R`endstream endobj 54 0 obj << /Type /Page /Contents 55 0 R /Resources 53 0 R /MediaBox [0 0 595.276 841.89] /Parent 56 0 R >> endobj 53 0 obj << /Font << /F8 15 0 R /F26 18 0 R /F30 46 0 R >> /ProcSet [ /PDF /Text ] >> endobj 48 0 obj << /Length1 772 /Length2 576 /Length3 532 /Length 1127 /Filter /FlateDecode >> stream xSU uLOJu+53Rp 44P03RUu.JM,sI,IR04Tp,MW04U002225RUp/,L(Qp)2WpM-LNSM,HZRQZZTeh\ǥrg^Z9D8&UZT tБ @'T*qJB7ܭ4'/1d<80s3s**s JKR|SRЕB盚Y.Y옗khg`l ,vˬHM ,IPHK)N楠;|`{DzDhC,WRY`P "P*ʬP6300*B+2׼̼t#S3ĢJ.` L 2RR+R+./jQMBZ~(ZI? % q.L89WTY*Z 644S077EUYX`j```aYriQQj^ 8 OljjEj2k-Yӷs]|a>k_d?nvfJm@%>wX,iG /vRfǝ%ش9QDod;U? ^VNvK{~/t-798 mi"6=!y:I_K-,1{).o *[mF%s_-j(ls~gغ|K~#ﵾӷ&g]p_!GrnM`v^Dl>Z`.xYh.#8w6O~5{ބU7807k4鹇W5ùf՟VRŗm_AXOjW;[(ϴu,n q(ﻰƏA+̻3z^"_ٓO:~UIH$PkR-y'y='sBˉcWdtD?:`w[K_ȀB5j0 9'5$?7( >xendstream endobj 49 0 obj << /Type /Font /Subtype /Type1 /Encoding 57 0 R /FirstChar 15 /LastChar 15 /Widths 58 0 R /BaseFont /HYMHYD+CMSY10 /FontDescriptor 47 0 R >> endobj 47 0 obj << /Ascent 750 /CapHeight 683 /Descent -194 /FontName /HYMHYD+CMSY10 /ItalicAngle -14 /StemV 85 /XHeight 431 /FontBBox [-29 -960 1116 775] /Flags 4 /CharSet (/bullet) /FontFile 48 0 R >> endobj 58 0 obj [500 ] endobj 57 0 obj << /Type /Encoding /Differences [ 0 /.notdef 15/bullet 16/.notdef] >> endobj 45 0 obj << /Length1 1519 /Length2 11163 /Length3 532 /Length 12070 /Filter /FlateDecode >> stream xUT֠ f`S 0vp0vG~Df 7՘ qz=:o9_+jn?`w w jng  qX^ fB<VGcG?^/'/!ßka_kBl T_DЫzuCJ׋7IW1?`Ck?AvP7vPC5Ыzoz}Xk?d{ma^/lWuЖUZ᫅_a_7;+/|Հvk__:U/|p _5\ _͓`fݤR>n>5Sle xllDM@`fs2E^ }Jj .^ GHR4>Eh]CӤ:3'2 V3sMFQAO5ϋXh89jgGzCYaKd;vMg˔7.pU@\“pؒ+k%d3Dd6ύчciHd`_/ewmz-/"Kf.E 820͚lI;Owu>9˰˼rʢ"OQ}&ɧOp<Ǜe,#gYKa>w?9v!eּ٤F,gSlΨQ2 Bss1ld9Sx1V΃ X)do<(X,<yr,BKUQ?}[p>QM+'1;K9xtY&2A^Qd1PW )0oz4'cT@Guuju@&к>u TA5pG&NZL.sN0.*}"2kƋ '-%Cp9L~55,eW^ytA]6C N*Wwy#]"xV6Ⱦ ~I k7 VdfG {OSX DfGՋtb'?/WϺxOG6T"?C-jݩ# w9y3Kf7}^"ƀ9qQ|B9 s9Q۶t0e><~X+Ke^te6 @ZqDY5lT [[^p1D!Vؙ"Cӕ kIJUUUXȖV\to#.vƜCPaÆ'Km_N \+J2˼ꑌ3/&U*Ł^^+8?7eç4&$;5T@Hoߖ̑AQH6I}UDgwu лPCDd h够9.m6w_7If;m훮AR|ʘ:fذY 2|t-ƛ\e\AMz}~-0Ă\~ [v꫼[9[TɟʸI*~1WtZSsH[/K /:.hϨ0TD軫o"_-ۓD?,C6 63Ҝq@]1 KY:4J;o_@A,4}i)fHĩtyN{䋳5eÝC 5mV TDK gGhsIHYnltIG*$NlhL:KG64w!6)YV.֞FQoܒ-wslpqX>膛l25U\ \ uq[t8}3N@^a' pO Lj {Y*l1E Sh,2E)0_@(m3*UbaU:ՠbs%|%TjC?f]Ha U ބ_px ʘOn]+Ģ*/8 u  Q>Їݽ3: {$! p 9g0&zl0i3 rAGAN@U/ T>.Ӿ*{!C f8[C0, so`u3pYM3BJf/yc"X%|xLWKؠ?Bށ-BmurګIx|@-| 뽃U85⽪HaQ?4b1MrDq4m)ZDs>._fUb#,Kb۰ H8`mּcu ?[zt`7x"Iw/T݌SG1.?^Bef?+Frl6Wʢz 'T>&$ @d,:'w)Xuyd{ lL%7"]y|-2I 6eQW@M1@:O{!Gn#XzEi1VG^\e0fc,9u>dEGVqsIqV%:AhH9gt\@߭F qџ,VM'L:p--(~"& C*B=7bFCcbDfsͿY|*'!kKkF UvK *4HxGi 6}j+SxMV^QP ulwk3Ewak>'N.,o/3\[=+D{+).چGf[87{p!cW tW~Q&4GMXHhDb\fC[ɫU $YH_( 6 s?>!pRc\WGRYho抒w y{XExz.iMI˒ (J$EY!r_]9Wj |sOj&%&[Kꉨ[`euuG:u7 #"sg ]_.?|_ ҃r P*hg;zscLk&\8oпlmv)T|wi# iRiWPP+&A:XK1 䪅Xݵ`5*3߫{G1wD.MwpPw%T=z'8O̞F.cҢAn! ګWmxEL[MlSP=KnΛD53Msatlݽ8@OÁEoT<ً(TdI~FQ.Q/5yBr$&`;Ŋ7j4 uqjG X>YTO{kbB VC[3߰H'N;}PkL?O஌L/vɢ\m=FJj EGgq lSm>f,"bG TQ?|y( ROb\Is\[UށwED*PN6v7nLN h]3$^^ِ4Ńpp5Uo 5@%Vu#>Z@o gcC8 6ҳK[4ea>βz.j p u..4.bvZ|_϶H KtD&ran"(;GT7JDS#VBʡOoV0Qfoxz:(XN")Ƙt/}Qio~/M׼h"U,BBn}T޺1zu7tLM&M.by(YQʭd+BӾx?<'B&F]6Ǥ 9wʮܷS ޼Xcs]5Αk~'ئر3^/d{xQT#YCŰ e'p\{Y]/ 0g z c':-8o}I}~3b {$LjdgNv7tj@G-/$QլTt6xSB#|Kaui&H{YǪK@C+6~Qm |O9>wFLŜn'w7v3~܏;N>N3)=>HLJxueiAű&R##eW =A4􈃠4.!]EK6<${Fo@̶3Y_u]2wWn{͌= Q$11v10Ȟ/_XvxMd U?U5ϊ`%`oV lЖ n K iLt:5ER(L$׽ZfN8"rc^>$%ޑ%c*mwlj=e90KB$+|ĸ1nV$dڸcewX Ҡ}O+9;8^x }{2yK"9!GiXMĬ(d&KS]-K+"H{2:Bs",mNTw-ܔ?]) R=,ҙxxP [|ql aT"a{ CgFmBUa!d^Ho+ac$:I^Snudži)zcPgp8y}|JC2mkvK5uw.$əsk=;Jd[恚D7CLp)?MwoNh>cY4Ka1&` g.rymwx?)[`Et l@&!^u 1!K}׫K?NCd_|>1q?"~srދE`y'VyT8 >ޚhQ\wޓn*_[&ΟŝyKAlUasT(哬տաMLYaPlnڞY_FVp)+U? Zg~oA,3'7;j& :I YT{pNTWЕrt8FO\/wgO(||?_ct4}##|2$;sK֬ K9\tj5X 5a5͍bHY :(lÇ/7<3 &4Tܢ܁(߾Z>fJ Kk-4t63`%GsxKWk>RRPX`^ZhU-t<,z-@FNJ*m/ƖBAC/)JRfhLn G>mx6U*X)=YObԳCA'y2°?LёEp=SDսq졑3KlB7t`Hiwc T9,CN[Gf-BY p1$"ծe3f휴P:]?NTP]-\}hQMJU™H#>R.9L42CuԀ2v:26g^Nk݉NUL 0|޹ğm>/GBChtS]R5y[j `^ ڤ$^$W/c1ӞWT*ՈF,xA$Լr*=jVw:Xuf7?ߡnW;.[fz&iCBœ* #\=T\VkIvW^*[N'lHcrϵzfQlnEV9t=\aQCS9Q&x:5 2ΰŷ_yUv"PGtQۦKݔy/dp48>4+HLO^#Nyo9C~H^F rkl3޳=18zQJJ+PI/ Y]Hdh5늢nOx,FP5Ab' o;&׷w:Ǖq,i#-JBuG=BjEA?~7{wZr@e(br:!m-p૿Cz7+2p_!<& W_j_FMv͆\U@P*5̈́tExUZlnO}g;.bo\|YBût jUۭrmB>>r-(C8` Jr:j`WDtR*b瞫t滩u}4+_}MeBt1Ǔmb Whx;ݲac0kʫ+9UVbѲ@49#.R<`ϵfpIQbs$ r!Q#PiONJH1X>l̖}fbjeA8\;p=nZkGs=/&hsc.9!7!ۧ\c s# 7+ 6}*dԠߡ,էH96yd(52Vtԙϣggɲ`uaDDdlR=^T8g=Q}<-*kN*8 7xH lӚ:ӻJ9:R; UVå ڎy(7e]yB1&RȎTx}ѱFxr.xgG {h1a;jː<=8"ۗ!:"Gès 6iy}"lxˆ=YiKoQg+v-x80p]>+&?n:wҿ,η89 n̷ϬT;FOD<' 7^ǰޝh}tJlC'"rQ77]Jqn7M jUg=;z?fl:(j-koAR+cagܔʇꢼ@sR- D'HQ Y R͒kb ,dbg`#3&5"'3&=$hѠiGl A#bLJKI`ݷeDvC=23 `0Igmv_+_-$`U:QVO\ V/.:|yVt~EvPiVp)hP熹&<'KE˼u$ńzJ*aM"UCݐrnվm* əM6ydz=2uBϰ7_d;zd_%%g" W?*S~A 9'ue1+e7vj:yCɩK1- b&~$:8?!Im,q:PUC)FSWQCL/ga w[ne9Kxuh [} A[wD>}coB4* tYPLur԰ք@䃭Ohw4O_{-X_B]5Rp9`loGL+P(o9bޚjTKW#5(qE\V7! G:Ej}dc֙k1(L J|Uڤ?g^)r{s{(}~I0=c8犮7_a tDa K,;U ]\_`agUb(/,0Sz|[D٭'6m( X}sk(Sl W+k? )~,Ц!">}-qV ~0x7pOr^? $JRo Ic4F4tZoARBP蛬dYz92%2XZ[D/{2@xP9"F=w[XuftSvk/SY Oksi5V赘V=ц8-ob)DțGhdZx%Tw;ܬ!E7;,n?s9[P, U.nKmdF`%=Ĝ_b멹};%CNRyUwXm6@N}Phy9X5&WODX&`@fBZ = (usrd3L]S1)铉C/+7F~Ԣ%>]_¿x֌95?FI&^pи\9Ju`ֳRiցG(\RF^If iF񃟴yn7|  ):3qߕ|$Z"M i A ?oA@慲][e0mQ1Nw7E}1#̬6iw y[͏1l/_6 c'5endstream endobj 46 0 obj << /Type /Font /Subtype /Type1 /Encoding 59 0 R /FirstChar 12 /LastChar 121 /Widths 60 0 R /BaseFont /HUVNAC+CMTI10 /FontDescriptor 44 0 R >> endobj 44 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /HUVNAC+CMTI10 /ItalicAngle -14 /StemV 68 /XHeight 431 /FontBBox [-163 -250 1146 969] /Flags 4 /CharSet (/fi/quoteright/hyphen/period/slash/zero/two/three/four/six/colon/A/B/C/D/E/F/G/I/K/M/N/O/P/S/V/W/a/b/c/d/e/f/g/h/i/k/l/m/n/o/p/r/s/t/u/v/w/x/y) /FontFile 45 0 R >> endobj 60 0 obj [562 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 307 0 0 0 0 0 358 307 511 511 0 511 511 511 0 511 0 0 0 307 0 0 0 0 0 0 743 704 716 755 678 653 774 0 386 0 769 0 897 743 767 678 0 0 562 0 0 743 999 0 0 0 0 0 0 0 0 0 511 460 460 511 460 307 460 511 307 0 460 256 818 562 511 511 0 422 409 332 537 460 664 464 486 ] endobj 59 0 obj << /Type /Encoding /Differences [ 0 /.notdef 12/fi 13/.notdef 39/quoteright 40/.notdef 45/hyphen/period/slash/zero 49/.notdef 50/two/three/four 53/.notdef 54/six 55/.notdef 58/colon 59/.notdef 65/A/B/C/D/E/F/G 72/.notdef 73/I 74/.notdef 75/K 76/.notdef 77/M/N/O/P 81/.notdef 83/S 84/.notdef 86/V/W 88/.notdef 97/a/b/c/d/e/f/g/h/i 106/.notdef 107/k/l/m/n/o/p 113/.notdef 114/r/s/t/u/v/w/x/y 122/.notdef] >> endobj 42 0 obj << /Length1 1459 /Length2 7907 /Length3 532 /Length 8792 /Filter /FlateDecode >> stream xU\+V%Cp-Z A]Z/R;ݵUޫsy$y9s|%H Ȩrܜ@46 vZA"naa G A2N.`[;8Y AleY@VHA v  d͉ [ [0/OJPGdkW,\`Sm&O5KQ 'w@-Jwu 0Np @Pп̩/ {5p+; ZDv&K{Q z:这7#F"g_V`-_`bቍ`5@:[l]Q> l-KGpY^$A,`v! ra~u~ DqGW K0GVĴGpI&WHAj”?$ /XsY&A-€? Dgp3?ap6~#b †"?s"laDǷt{2OZÛF\$Aή %YB<V.CFۦvE5h ˟:ǧLooz5Pyn9~>ojZP8+.g?}Qg6^;-Vc e<$ (j雎lʮ+ٍ"O_`$lyvi$znZD'f h}6fiSJz!9fSOqLyVE.eߛ|Ǚ0 䕸r]GnR`RK$u.8{(:j6ĒCy~‚exX-"9`v1vzg#P㓧0jsZsxU 2Yz_M©cT0ħ۸$k\ 3w yv+- ů]; ^QW3 Uqb+ewFG+fݙMRr R>3ϭLd[W*nt-;Y^kS{ADPLw(]gN҃gk[ĔUH.,ffKDi)rh:Χ/t&MHԻ} e`-;4{7ͽBOZ|y U.Vc ^^gltN:D'sK <z 5@a: E痴đ:C/zC.vvLD} ڵ -"t|kSau-- t< .b6MVwIB#$wϐ?r}3X_C[ha}?̯bI5mrĐݩ.=DdIQROwQuAE,ƀ+8K;|9}ЋB$HR+[ ^؟\7>6}}H  Nq$ɂ1cFB5ZP&jVɐfK=IنK^8WE]GFn\b!Iݕ& |7&7kК\x56Tb?\diC*~Zp%ӼJ"oBϻ=U7F(\ּ`LGy87pӃ XzԶ%(=GVd=[އ20:kX1̗zAL)V>eGukĞ'xvZYκ[̓㖇s?ZKc;w )T:P[&@P#qHyjبb)_Ś'h9 76>8h38cw#`2LUz{,~SFOܚxI?_ح7RyJvd>tTb8DxcgX&<~T243Ov0$F`J[}1Y3z<F@Ov]'H(:@{C$x_l! ذAڢV!iʇ\b*_A1bN qS8 e~ʟ&h:sJ|[s^#1$166o1:4DS&_%2M[&ќN8:{㰧zbLKG?93 DOqՀsAzR>ӧic|uu,c%;aH]t_Ct 0@ "PEq/R÷K)_] ف ݛg5_di*]˥qu <6bMitw R!W Kns*(jv@%mHGU!n nrΒT#?ԜAD-&uHꇜ|S}=sZU~8Π8'qI,8l O/R5J˟/S ۷w%=S;݄Ĺ cTc)y hbu"GYv,kDuPITTG̯UW)|?zqx] /"htɱh& azfáXGWQP5*LG/ݔ7]*;O{8\}ᠡ2=.Ke,RڽU{ĸv>.j6g2i|7_)rK% xZM93 -j3+tͨO4e9֥!-E!kx20mQO;,?N$lƆ~x]7f,K̭6o~] Z%5"$\HPTZUU}pIwφC9>R_῟'Q"YSm6A)g>'DIk1}>N%'cܛ>T[E'dy?;57ZvO2ɑO.;z "Z8Q7]5^ -~99*P&K.H܄3sB{J=FӫCE$_BȒ;~DZzPeмײyyfMGIJn ^8IhwgዤO 9)yY6ГOB_#?( ރh"[Y,$ n!$wCa>kؖΠA۠tL}$xJ?[C.:{aKw`x:Đɱf2ڦ'DɌwĉnK'*_^$d}ZF3HXvy=={8(^94Sn^Mr ,nG EB*-s|sA%y[u R_i5)uze aCe_hH7R7Nx#{Drk, l:MMvfA024 GS3=+WCS{z_o"COmk *k)|i@=|KQ}Q+YN-Q\(& HxO"Z5v1u[%rV>SN?RJ+,f_>c lX9Ο@eS;4Y-I2!~$PT|t|u<'nHYcȽ6 kqlhݡ)HE}YI+WےM+K =~Ql oTRmGv|ƕ]e_2>=@27x(mz 7xcdG~5LWl?T , 22'ߪYYpQI}o;Z`:5ư4ƞpnE.[DOpi^,4YEu6ggVx.7B 8 n暥A/9XT$YM:F&[)`J`z~մ<ef$O>1qXђV߸>_!1qC Xr?Cޭj$t"']u3n Hw!5-;5hɛ"7z⍆l]GS4j} ԪN6R^"!`* *шk-<8KVkpL $|0~12_}9=N;@I4FnOϣO=*f!c!d <> 0d*UT1wwN wVwnWU-(Z-S=8z2#)0E*w ܧ w"oCM:z,3W H5̵7QoQ2HHwzOzԉBF7%ʆvD C/_z3Z FL$ Hf:N$;%[boܙhNu8 >LtE } ?JgVpӐPު0lFE9ɔi5Ș97hDCG|ozA8KIqS񏝂\HnjVaﳥ;}} wPřO+z U=զ` c˞A|'pOŐm"T6v|?`M=ﰑɈYA3H1? &~NsWn-9 m(^Ʉ5VEI{/hѫ 0_=yÏpL6s^6v&MkV,i}DѵMBVA|8残'g+GuٴQCsيJ?H.sINRw* G}A'|_Bm~a{P1I49F04w}QVbJoy3a BP{JVrʹ-ǡ{"Z5& % ع{ҲӼCٿb)ʳn1fCT$mú)I #ްx2^;ƔD(O_)\UB< ` z&7XPla b.Ou(#X3R2obzP6Ԗ}̎/;dwO ȶz4MI`5rT4޲厸EӣRo[c`GK[]^h1/56%b"d4uXg{mHWpzӑH=[MQ'XSo"!Tzo4uX 3Ebv ɷ;ݴfd3,黽blNa/x}e]dX{m*Uo|nZgB0_Cb@?ꤤe\[A뎩_xK^l%MoGn4E ꘝCB60hmqe){ c/#'*tXGk}:cO` 0)@5΢@V7o\d[:_R +ڤ*޾+p>x*_d̺qYYD;٢D(6͡Yphp)Xg.ne҈q5~FqTݫ}$`be]ߚpЭBiS"A t7Arγ!JR2$[ a4]q軎^T56_ui['(kw>?Eevמ3",#K94 3oARZ+;{}6N)5crtvE}Œ䜁\2K7NR [J-ẇwK&QF.*9,_m@v$LBmƧx`+#/-o. %!".Z,~Q /uFÅagJb΋O"+J1ǵ2'|D]?lbh˻LOd/n1Xcu,[: /m> k:ڨ? wOfzDl<Cel<β_:hIhu*Qfs*;1UWlߔ~`{ƺ(qκ}agwh!$*mSDJGG]Q!ŞӐ4,>ʟ"y`kzvcXGAf)#Z1O%o]uUiñZ@j؀2fVkhwjpΗ+mƒLZ|xU%R{0x{ ;)\HɚZ7ZBˈ`!}²V;KLSxVtgA`;mOꊶ_4q@GC/TUxq*XT tkMƱfɇ2Khcsu#axaiJTkݷeL3KELnIp#"M!788b S\AۙWjF u7`{nx$ WNo2N"!*8'E-i';ZY!g vӴ&qQ .UMo>(3X%쥺0YL1%#4I529=?)Λ1e ?&K̀IZE-rD6&wrV=3$ޢf͗Q_̨M9ɑEr \udcS m1YlÂJ v Uɮ"۔=ZY pN]yK6}˽=ncO|e\u6s~ 4ƌSa\˩wh^l">U?'Bu{`9eXbՉ[j^2RKuL=-|C5 .mR{kiIM l 5ŷr_V>㷴,$27h;]Y{t»ɴ,_3߉ ]R N|V&TP;"r YI˳çn\\B- WzL 'oEa *9P5}+9j7sIvv>}>Yvh=bugmET,'k_!ޅe&+^4Dt]ɭ얼& Ƣ3B_ 8#BsܜڢԥՍKHۤDL -DӂM'+s7/2pR S;usJ#UsR6h2RAb]8’ > endobj 41 0 obj << /Ascent 611 /CapHeight 611 /Descent -222 /FontName /SSMDNO+CMTT10 /ItalicAngle 0 /StemV 69 /XHeight 431 /FontBBox [-4 -235 731 800] /Flags 4 /CharSet (/parenleft/parenright/asterisk/period/slash/zero/one/two/three/four/six/eight/semicolon/A/C/M/P/underscore/a/b/c/d/e/f/g/h/i/k/l/m/n/o/p/r/s/t/u/v/w/x/y/braceleft/braceright) /FontFile 42 0 R >> endobj 62 0 obj [525 525 525 0 0 0 525 525 525 525 525 525 525 0 525 0 525 0 0 525 0 0 0 0 0 525 0 525 0 0 0 0 0 0 0 0 0 525 0 0 525 0 0 0 0 0 0 0 0 0 0 0 0 0 0 525 0 525 525 525 525 525 525 525 525 525 0 525 525 525 525 525 525 0 525 525 525 525 525 525 525 525 0 525 0 525 ] endobj 61 0 obj << /Type /Encoding /Differences [ 0 /.notdef 40/parenleft/parenright/asterisk 43/.notdef 46/period/slash/zero/one/two/three/four 53/.notdef 54/six 55/.notdef 56/eight 57/.notdef 59/semicolon 60/.notdef 65/A 66/.notdef 67/C 68/.notdef 77/M 78/.notdef 80/P 81/.notdef 95/underscore 96/.notdef 97/a/b/c/d/e/f/g/h/i 106/.notdef 107/k/l/m/n/o/p 113/.notdef 114/r/s/t/u/v/w/x/y 122/.notdef 123/braceleft 124/.notdef 125/braceright 126/.notdef] >> endobj 30 0 obj << /Length1 1472 /Length2 8383 /Length3 532 /Length 9259 /Filter /FlateDecode >> stream xUX\˶!n h4>t?jƬ_Sg3v, %5>; 25#4q b.6;'d@lrI5 f tLf&6`3ك fcPk@ tt AfS%/9; 0_as R:$=h-Y߁!R\杉_!=o& ;wq:@Gbr&6 31;K _!4hr6X8SҶXd~=Iܿ7Czr豱C&Be ;q8:x C^9t؁!K,=Lv`upCzwkmmM~GVV@!=d!w tf:8@*9AC o&^olF⅔MVɢ Y~d~$?َo| ;QMkrXR7A*Cl ;_! D8@ć?baB4l#;Dh@p! DX+?b!@V?b7(.vb0s@!dl^n6:QS66^H3GG?U7[ / h86 N)*(cl}W} %p!ڦxHaZ<}n)[uTCl^gEǮ=M1FKnh婵Dެ<:I}rN=Ό ^mtjNr5T0U7x@_PFsp }bn³[=Yy9[+HYF.Ȃ茦LV zTF١MB,_<7 K=ls21qdY2RdɎqy[sPGz\+,0ɞ=F,EOH/'=Ѝjnȏ5xI% tG QpvT ެrbфdh|S6a ɁVȒoZRp;wgb'ܙcMqQ ^^\BK٧}i۲x j>Ӯ0]4/TaJkN3 $iV%G]ȮsL0^t&[;󝊞3پt~cp_sp7KQr_u|a{Ӯf[hG;x8~+ '4qC8ʓ0: TD4\wHMA>,y4Uߏbƕ,P 5 )a 07BaW:[)Iqd'UӸ0&s#Xc1#ꂈ2{}c jqD?S&.DՊ1EIT ;G׺ډ ^o38S0 UcO3ٶZ~2R= sN 7^(5#E#?3Nz97[ .ﻗvj qGﻏȄfyM&L4$"ڝ=8:Hp 8.v]Lh ;ܵ藫Tm@{V\ȱ9K:QI9PĕuG1yp@AvN66"`1u2M3IKAWcz3Ƭt[6$WŻX= {=O<j+g-$Ϸ m,% ̄S.-ܦ(jGrUҝhpV&9ُ7. b(aD$*GP#S-] ab+;b.{tp(4] J37s/t?~1r@DVi٢.")[MuNe7XVtS*`L9WN#EGt{^}zPˈ`ݗ"'B}̤rºɦ?y.Cre(Lvd\PdiftĺKAy;X_6Zb@nsZgBdwn}MX6JϨ?PdzFM|]ZeJceTeR N`ҙ/Q];rh7;/kw"]~_%H=fJG=5c \Ka1vUYC>HS:17F9PVR$΅g*Kͧ\>Ri4(I3f.#/$z>Gj@ N:`C7KD<^Mь\ݴ<2J""oHܖ+ gqy2<(7QYX{?T  DtRA~5:@joO<[d2~%VK݇{⸨a)~}tŬ+DƘvQCdzqemBk{WgYxD^odESU(*4pÏX?E Eڄ7{ԒaUV q{"ت6X5~qy#T]ߤD) +mS4JQ4f ym\O&̔b7yβפfrɽD?%Wv[,<aRk|c'gs}5a4(?.ۘl2&GapI `RX.;L#Ts뇱 Cy)21 6kWi[sz i oK⃭AZ?կ"R R%,-mG (;Q]LjwZk'ڌ11@L+ v>`P2ƦtM1OiO*tߣXxdKpK;8s^n#:HGcɸ8nykRD٫w+xjQu02tC k\BWf8R<-S7S"yLEC:8e%&L.ok5߲߯/XraXhw&NJ֌mٽ i(vBd,`JDFloVqFoɌެ%!1{hmӤ8?Ǘ2l&ev=9* H YQdܷI]05oM}Dگ5rYȀc! [^v,]1vbܺǰS{4v<ѲkxiV)g-TYcα| ڮ^xbZ'vxU٥/V#/2Pwr "sA|"wy<9xՏ*CArPz'S܂kPgSߠBu" =-E%0[nwGm:B&# %^1L ⢲Ş0uk,B;gga&wRst<> ON:.,訦9Rxt6s(uq>_Sr+O3IA19ibOik)(0HN0v-3^ 9  F9ve.C5v~X=pPO]!,OW|jB?=m!t8{:O ƅb f9@Ij7zl4 %"J4%+,|5Ϻ{qDmCbh'!DAdv?VAӗVnˏ8V1iHFrݵwUyWGԼɂgVK()FE*xn2%|Wb[065 XB3ء6~(ۘ;iObceL|Ћ.h1{{1jr tiJU.|ӄ9^j0E%m4LSj˨~ X^I2|Jb/NزCJ~p"wL+.h4c(K&+Ϭ ?Cc.aS_$f( aBZA LW"!jgq =~+Or;iN O&s.F$礫7*$,( Ƕ 醴kD<&);f6=+ "Z;U0yKz#1zG= d/$Yeئ\MM%M&ˊ&Xp1u VBtx VM2_WerK$u=HJ@a~A^pPТη?Ԯw,W_uQ 3U>E^GF$]({2ԺY˞ΕZ9Ĺ (*ln0xӈI6K֚ kYB8 xvZ7i/>PV=+rL  ̝ _"{.V.GNpg1C /i$`PĺGM< )d7Gh)I޹L$¯\qnѾ5#ޛ> v \ ϥ'$l7E_vQX*sÔZoX6^4K5uYD>4!T+|( a /OM"6 &fp4gӰs|mhed#JH·I_ECoH󢥕*7_T7΀U(C> c~ej9ã5Vd8 w5!|մ%`AVvg1MbWW!ަ#p/k4Knq7jXdt!X:;;P!~XZ= K$ҋ(.]?OCPCvHeDēr珔אps #:I'Î#is[O;^_QSБ1EHwsGۂJ'Mu\>^[i6zd,m!a, bT2n|(&v9 ~NRq !.?F1mM컅NBd*1Tc Z;#~>䘈|+UeEn8jlaЮNE27h XL; ">-h-g**+1 [TFZj7WFlyjKe궼 iXFApO_XV#Dc_*5?`)웎^d|p\p. MsIr Gr(D*0vAǖV޸U;d]FTN@;^V]11KײCX~8;D  jd`ÃU,3۝0h) HtjL1*?eZL?ϻ`VÏ!8S{вm\.y`=x,H*.[1Jsa#GaeTW6H; Xޒ)V|&e$yP0&WK 8i1c:aT| .a+k`3T kK{6V Jg꜅Q^NW8>~9Wx)0O{_w!ǁ]4VI[4[EA53:sqnx;Ft*L64ԺUXDB$4QOUdQgQXi'(в?!} ˬP^HT>R{PT;qp@~is+$5 ~J1ZP[TnuS jލs~zw0CI8R*!~Gm}pJ|$-2zyeH)E'Wm!jh^eޠ:Qs%2uK$,(XV~m,Y"^Vʳx^=I;nma*hn$9;5)?PGD]L|z_2ʬd˼PP7A^R Kyy V³hqP@.OB>g ^)m&7wy}(&4c硡1I:eV9Y h&kS VD]D ȅK-GS˜d`nfX_=A_[s 6fMM?ꄝfy?*»m:Z+p Y®6Gg*Ɵe* x~T]5ww $}aglb_B>M"NArg鵖\']sV}q9oPx]v+i 5й[T8`A̵A>P>?Rp 솾TuegInD"9(;!l<́2p&rhnKyh틣^I:,_v8W#א3܋wF5BM 0~\<_D1voGʰ2ݩ ~бWU&ֺ?Xqk"ݒRS|a*M-'~tԓU]vMӅ/k)IS(?^D :_z^b7ZTgBZ1 +" ':NuKz Y ̵Hk5ưh nCMЧ&C̽QG0W<ޙ8_)Qa5DG>+lM?" lkendstream endobj 31 0 obj << /Type /Font /Subtype /Type1 /Encoding 63 0 R /FirstChar 12 /LastChar 121 /Widths 64 0 R /BaseFont /TKHKFM+CMR8 /FontDescriptor 29 0 R >> endobj 29 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /TKHKFM+CMR8 /ItalicAngle 0 /StemV 76 /XHeight 431 /FontBBox [-36 -250 1070 750] /Flags 4 /CharSet (/fi/quoteright/comma/hyphen/period/zero/two/four/six/eight/A/B/C/E/H/I/L/M/N/O/P/S/T/U/a/b/c/d/e/f/g/h/i/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y) /FontFile 30 0 R >> endobj 64 0 obj [590 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 295 0 0 0 0 295 354 295 0 531 0 531 0 531 0 531 0 531 0 0 0 0 0 0 0 0 796 752 767 0 723 0 0 796 383 0 0 664 973 796 826 723 0 0 590 767 796 0 0 0 0 0 0 0 0 0 0 0 531 590 472 590 472 325 531 590 295 0 561 295 885 590 531 590 561 414 419 413 590 561 767 561 561 ] endobj 63 0 obj << /Type /Encoding /Differences [ 0 /.notdef 12/fi 13/.notdef 39/quoteright 40/.notdef 44/comma/hyphen/period 47/.notdef 48/zero 49/.notdef 50/two 51/.notdef 52/four 53/.notdef 54/six 55/.notdef 56/eight 57/.notdef 65/A/B/C 68/.notdef 69/E 70/.notdef 72/H/I 74/.notdef 76/L/M/N/O/P 81/.notdef 83/S/T/U 86/.notdef 97/a/b/c/d/e/f/g/h/i 106/.notdef 107/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y 122/.notdef] >> endobj 27 0 obj << /Length1 911 /Length2 2612 /Length3 532 /Length 3256 /Filter /FlateDecode >> stream xSi<"FY)3WJv:*K̃cf̂^.D0f+dϒ}}Q({N9<ϗ}ߏqS -20Ycd`PDx. 0UUEq H:xvr&:-WF"p 2<,`GA2Ub+H9H J(4 8NhD{K8G#>LE@ $⿳? U@JP@W] D#BP`hR(r$yJ 8 s2ʀ ]mmM/ #H*JP^(%]!Pe,B$8̘͘3zHevH1i;Vr1a\v)Q92#%D6k_wMߙqIz!tu%Ɉ:M8eiߗߕLzFZe5?5~H7ԵZkApI+'s'){p'7 e{.n_AdK߱2spJ?y댈((y+Eƣs] 'cq})s-O龲[}w혲.{}"MXԳ'l}u;Yj~QŭqXVGA4\6[v!znzO^pCBE|._׉By6ė~`kٷ!&6&|5͘|?rYHr첡dʂi%q]{WZs4'mhڧh\GOzm pޚٚP+,IEA9%gXm:veӲJY}f7;i=Rx2ELeyV |-(Jfm;ҧ"W?%Sn bZ|{Hd̈́-M? α+RZv:Vhnځ:me۳oˏ8R=$I, X7f^C`ML\h6zb].z%e> x5Ʀsz./2yj]thаz&c7#o"ѾB1&՗ MG iE {?ؽ)5Ľu`sG/ٚhR+·%Xt/ϧ#s{KΓ7=x[s?QqR/ڻ:hQU[MM?oI_Gnji@fFJ.&S/2n,t3<^c]g+=Lj.8\QT#_-B+~ UI8xJP(!X^^ 殷;94  ٛV}AE%X/A&#NZƬP$șGY;_CC1[M!>慆=XhG+}B%'x/y69#ls*>bq'f|*Mֺ1|'NYC#/,21 zI@|r$9q^xdr̍)0doj~; K'!]=T+xY\kNB)C߼3z .OlI;迨 zYoWr8[礂Vkwv"ZR7XOfyʃQt?ȩes>1ȿF_}lh{EM~x<=;klyoOÜZ=s]H}S]8L BcwUF\̭c$lMJ{Ͼ3YJp~*6C52Tsd/FoOJ HqK l7ɔp':è>#Lݎ.^<mt W쨰Zh@yrK.{[,ni,cO4i˻vI vK4_Mj5Y~󡹙JW fƻ-2'5։P8]?/yȧsS Wu]#te|_|.G\ JgX\'ZvkQ\ Xt= R.{HmSL䉛{l:LT3U'5BuI$PĤNȮՃJl_RtBG30LK'xka&M#jټH`gբۤSn,NL?߭ JKͺ$lAC $DxWwendstream endobj 28 0 obj << /Type /Font /Subtype /Type1 /Encoding 65 0 R /FirstChar 48 /LastChar 57 /Widths 66 0 R /BaseFont /QPRZLJ+CMR6 /FontDescriptor 26 0 R >> endobj 26 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /QPRZLJ+CMR6 /ItalicAngle 0 /StemV 83 /XHeight 431 /FontBBox [-20 -250 1193 750] /Flags 4 /CharSet (/zero/one/two/three/four/five/six/seven/eight/nine) /FontFile 27 0 R >> endobj 66 0 obj [611 611 611 611 611 611 611 611 611 611 ] endobj 65 0 obj << /Type /Encoding /Differences [ 0 /.notdef 48/zero/one/two/three/four/five/six/seven/eight/nine 58/.notdef] >> endobj 24 0 obj << /Length1 911 /Length2 2610 /Length3 532 /Length 3256 /Filter /FlateDecode >> stream xRiًߘl47ǐpx9/+ZeМ6/'f嬴/ܲ5IfWzMhUg,C\L %I[k~w dE8kV)Op]Eov~%@)/x^1˼_ϛ?sܷ)>H\HIń~ZXFٽߗk]xiD.t' jHƐĉ#"!Syn0 a6*vZE`_YI=-;PC`ٯ;h$eoa+]"`9KeENN}#n8::y!Ѵ#qBJeKOB]B)mM;J/K[Nq'}欳`q83ؑ8vFI+5vMgH0W)*Ν)scizJMUQx`X-Iֽp8tV ~Y6Y +Ao%= h8`V6Urf]($=2ܧV:yD}oye‚Mk+ZM;2*O>/pO@h۞1lE0w`Ձm;0 3,qr=gW7A źq:LT.«ö Jm=HH)uߴGf](07xgc2B vHyQw1,_dfY7/~aE:+^فqwbFJW k̻7, (V""y2u;ڭO<źAg,(gږFfpEeֲQ( '÷,þF- o <~y0T9LԃM vrh@t]N; l›M( tۮezSj8ˊWCEա cp226 tWפK %Wzԧ ~;0uB);}̳N4~uc[ĿsVbG '|0V!$7Z Il wJ+wDݽsҦcaI.bSڂ60M_Yio ؟WvMC3^3/Io\oZBb+,nxxw+sT؞o_*hM3y07~9Q;'9_ t+3ݜ2)g== T C`V43!RpۈCc}wwo^wuh2TJp\v{YQe-`N@^$j4fKrmt)zPsys7+$]Pm\-ckaJJ射$xu{]"Q /0Ԧڇ0xGT3խ^6yخVzw"geW>].RpJ/AnB2>s5y|P', Q&X7cEY~jw4hيNQem6vDXoeQYoU Su%7ݻ ώ%.~$^l|hC§S J$#]O R#o%fāZq , Pi endstream endobj 25 0 obj << /Type /Font /Subtype /Type1 /Encoding 67 0 R /FirstChar 48 /LastChar 57 /Widths 68 0 R /BaseFont /AYYUBR+CMR7 /FontDescriptor 23 0 R >> endobj 23 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /AYYUBR+CMR7 /ItalicAngle 0 /StemV 79 /XHeight 431 /FontBBox [-27 -250 1122 750] /Flags 4 /CharSet (/zero/one/two/three/four/five/six/seven/eight/nine) /FontFile 24 0 R >> endobj 68 0 obj [569 569 569 569 569 569 569 569 569 569 ] endobj 67 0 obj << /Type /Encoding /Differences [ 0 /.notdef 48/zero/one/two/three/four/five/six/seven/eight/nine 58/.notdef] >> endobj 17 0 obj << /Length1 1531 /Length2 8655 /Length3 532 /Length 9554 /Filter /FlateDecode >> stream xUX\` kp P[pwKp݂@@9'{몗s=s&W`d,Ie ]v; 53jvq7+GK  vrvI 9ۘ@Wk=96zPA,H sW/#yK0_a 7_r9@tIzD`Y"!@RY\NhW_ׁ6v^wts9 gLKNl_m]v6Vv ۿB6.26 5Wsk%w` -,)!?E5?e3d:6666vH"_FLla<3 3 a8X< O1+r 2?%hZ^ !^w rX :7;Ad;\3be :)15ہrM*/Yd~[7A&oTQM? ?yX R7AjCl d@_!aBFoBcB$ ?a! DB4h8m?2m?bB@o oXB ٝ] QrZ6=%/ll<GݜA3li9Q@ O9\06%_x5+ .䐕Wv6t*^y<Dw9 rJX|s7n#ֽRu 9BhYyJunsШ,zǭ@9S/`,26{0KB^*!Wl/Mt{W T}B-MtFQ DKsVN~Vzg -JQ,AleDnY<,XYO;-n!\tF]Zɢl, 9ED&BїR;3A=*{K]2N< 68iDߝِu-ϗQͷŸS/ZfxdxYU`'c~fd%toPDSs#⻃6z=lK Wzliff(I9l!ϖ^{: Akz̹9+͍ۉ BFZq(4'lRJN_}zG^sTgc{1G!P 4Pa`gY{b]棢 c궠=YzзE7ԇUq2lu/K^ǴUa*o1([LO#cyJ3]$ Dp*ME{е_=˭ӨgfcWWJ>.Qmj{2IB~%T׶hR"eΪ(W闁ُ'5H76 .EO d9*v ZTlHlGfe-CvR_Қ/A)!k( D N CrĈ30/{b<#`KfjEVp-@$ "8bS*O|\ʺU3Zc J{&=n^ʗw{9<@nX ax$ę^tF"r:uM?Lq riZ^?դ7"ޒZxjpESFHJ\-\6Xo*FX!?d 44\Smjx^9s,`s͗HJ>{ ,Cn'~<"[%áMPjT&QQFf5% jJ(Ht@ Z] 'r-u[-Mns0UT$"2-P*RbXtqևH_qsb#Q*X  ]EsaRD7f;o׸ĶH V yCL$F&LeTk҇sHy׾4aCha ?7GCj59DZ]¸rT2O!J 5""z.N>7K9=kCž؈HRl|XToV~,F C4j9MD+~煜L1jTEFU+sp,Iaߏ"W>_CÏPi'am۬KMJ7;DҐ*,06k+ PԘ O*eHO:֋Uu4ZF1CA+^w"[5}tkkZBLÓ]нO${brԾ1PçpS r[oII\pAH V4;<6IxtrbX0fDKȞ?|RU\yvoQK韒i1U07{8.8X^LgGXd̟I3WN[PJ 2bJ]gQrO}(LOg6V1:G5b^OfFv'1g5hQjGwֳ[+ڌJ8t5PvҌL\0f_MIpd`iidZ=b?b#?R$>P1]$q*IԳ-2|:+]5U/)^S>\! qtif+KL,I"Oّu4# |2 KXqXk qVyRoD\z5hh{k|M<iudmR>`V ]4mZme4,sj{9[/MF*T.̪nx=2 \> 5ђAS 5&, <8EvrvFM G##qi^+jW{&i{>`מ$\QW`lu5Z_Bz! $Ezk g?j#"(${kUdnGcG/ԚɃY)cyv){vkjLhI09ks256>O3(N~ Zr Es,Cnϼ1]BھG}4ϛL u[TJTFw]I!/U0)~-;A?x\6i5yH&GHcU;ھpN'ўn[!6m+o6;d1\; ^%x% NW[D]Ԫѣe8$PqI`hh߳77PzT`.^K~41"MR2SQl) +cO0{D/mS7G EDRuF*Z4 +8%Ę7tk^!${?R""yS4j;H8Qf^6A1E9P]`0/&6n&8(Y+ qw(ѕ:JX!G/Xlﶈw3rEzJ4%2[̋DN|n0>ڼgCR͕nӌ;U1v *:~YR'|ft晭vԚwTi\1|^q#IKzetӒ#&uυm_x??xp0-/Ʒ x]x*:(%x(7%x6i~Xp/PZX@f(P ,+G(d-C(#6r@/s &X`aQ`!3̪ym=Vڌ8D쪹ku(`G`1@^/ӋeJ¾Tv no7m*?H ^Pc1-m#~s.,Pzx5ÌkhAEM8(zKu"qb4Ft{x<6 @,WWWi}taV4,%:jƝ,2JgP}o2$qh4x,eLdx]м!&EVY!y R 6ѲB5?qklӁһTW{Ab묃(@dmԘO1tC%[rܧtzUKN,C->^հ3]a*/,<g&WceWn V5[!r QXeܧ75(yOYP|wpI̤ɔhb. V N8w8O DoYvNGsQw^k6?Jh"tQ霨ig>| 8}.IB띷x D!Ss> c$(O%3q>ɉYsЗG˞v3U[&.@gKH zb"gEgn;nyQ䕻 )ڡV vuk)܍y6|VUxjehq;|БE,U#R>/yv\cNgg&(!A %ؠ~O^ft >K7f^ 6e ftOȑ492ݏ>k}Kː~k?8*QOOdlp_*=42ռhf ]7hPeUt~Ѻ=ڷB! յSPhX4/ muk^g$r>qgN7Ǚ0a./V eYQygm݄YE2nWCd exI/>~D;OGHKdE|#ꍑ vFU@fLeI7 i*_Ԣ-HKw`sLx5[m)) Zpe(F64BeXbcu>|h ء%W\2^wK4#FCkP/7OfZv:䢆E9Sqr,d^E0P$Ck}aa{2 \B[7AUQݏ7"4jFP1wX9qġ>'%sXb`- NbKXB>5(~Q~H9Dm&@ ECĞC~27N4{ ,WS[{Q}M߽̜ vHmۊ_lpO3 SM ^O p8AtfF^vW6Iɽ.mE❜>4xy>Zl}nS*(C"$*.D>it!U 神xaF'7in }r<3:4և$L)=∹YyqI.bCt#tc^+Q;bZ~T.Q%@`b:!uxk$~6iZTC6MbR-+ ?z>uIJbӫ-sWđ>lJ.8F >R(zzMQFwAS"^k@?\ PI̻l8_*=_.m.tHDxgTtr x}.n|ѽbnCB' OLF<*υ>r:>Eľ ;ecg6@{PPvB.F\:CRrWW|vs8Cj@>ϛ׹BhtO oWc*$}JJlo\( (?Zx~T3tLt0+̥8kM~V;} k?(>1?‘ˍܭ1 p2~$=oF;8j°,ަ!1dՖ'θ6 c͗:%D!c7it $9c{ñA&ٳ%oeêdv8̖/vёuK+-u!>ê zQ%-0&!poER/4hëIo<3?!-}Ba|+F}¢~Br<+xiQU7^Cwk}O_C/Sr7TtuΓ|B!T?iMjsXfVష>-K/:g)' S-bx6K`G^tIXYLgѳ3MϏQz,H*Ѽo̔@&&Ԕft_yqv^X L1Q`:=Bi\bۼ8 *Y=|Cvc-v9!'_g*WJ.Tu@ $e| =*ޗ`#[CeJ .!$( ^Vh h؄_=~Q>7<2[+3 \EMn@>FrzG(62ƂAOsXA Z`D>[[<#{{#+¶Gbu @8p*&UZpC)KiNG`Q=rLG0 P`w/DNb]]j@ݎf-mg Q\+p_W@J?8w>bD KH7fx;#1 B=5f-QpO5|1e #HilAsLE,0v_ ?@Ci CdSȩݥ͡ky $B+%`OyPS$L9obdo``&SHj|WWl_v +0zendstream endobj 18 0 obj << /Type /Font /Subtype /Type1 /Encoding 69 0 R /FirstChar 12 /LastChar 121 /Widths 70 0 R /BaseFont /MCBIYN+CMBX12 /FontDescriptor 16 0 R >> endobj 16 0 obj << /Ascent 694 /CapHeight 686 /Descent -194 /FontName /MCBIYN+CMBX12 /ItalicAngle 0 /StemV 109 /XHeight 444 /FontBBox [-53 -251 1139 750] /Flags 4 /CharSet (/fi/hyphen/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/question/A/C/F/H/I/L/M/N/O/P/R/W/a/b/c/d/e/f/g/h/i/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y) /FontFile 17 0 R >> endobj 70 0 obj [625 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 375 0 563 563 563 563 563 563 563 563 563 563 563 313 0 0 0 0 531 0 850 0 813 0 0 707 0 880 419 0 0 676 1067 880 845 769 0 839 0 0 0 0 1162 0 0 0 0 0 0 0 0 0 547 625 500 625 513 344 563 625 313 0 594 313 938 625 563 625 594 459 444 438 625 594 813 594 594 ] endobj 69 0 obj << /Type /Encoding /Differences [ 0 /.notdef 12/fi 13/.notdef 45/hyphen 46/.notdef 47/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon 59/.notdef 63/question 64/.notdef 65/A 66/.notdef 67/C 68/.notdef 70/F 71/.notdef 72/H/I 74/.notdef 76/L/M/N/O/P 81/.notdef 82/R 83/.notdef 87/W 88/.notdef 97/a/b/c/d/e/f/g/h/i 106/.notdef 107/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y 122/.notdef] >> endobj 14 0 obj << /Length1 1958 /Length2 14287 /Length3 532 /Length 15371 /Filter /FlateDecode >> stream xUTݶh(=;[.]v_k}}R4Z17Gӊ$UVc17J۹02TYYL,,bN@#K{;q# / 4r|`C;x:Y[hh:Y\,1Llj&@O& @'@gД `ji0[!0$cgfwΊ܀N )Ϳ4i IS{;O) Y rC.jchdLldki_m\]N{S ʸXؙ,Y:KZzM-]L,fF6Łv[4sR`WWME?_AdaM/+#W. ;{SK;s'{@fXڙ=@03 hN|fN,(h͙7~fv;ڻMmi`v0rWunbokk'`t qe70;9[pN ={;db?c'X Z0 ? ? gr]L<'hl?qA!PbRI@EK!PR?*WjC ?rC ?rQC ?rQq\(eWCeC]?ʧ@;X]aOnMMg[6CLP&ӿ V!(_/dUkB_'deA_ AV!/Y9 +d\B_rYy +Ͽd/o[QQ{oF6йagy<<';KGW8輳pVqG*o6]@ayބ/*)O`;4ysb}lbRM#FSy:&>> sdʸyc|۾~W+U/cMs7'J3eɟ .i`Hztz29$m>Q9 [wGKNZ k_owx*MzӽǎC7i'aWݡR=:)- CC}9WTGd1C$ /p'$Z۵H_)96 , eX. +bގ_x)}P\b4tiI[q3XQn2?!9%{ ;#X=YҾ)#+ff7:1 _nz2Uoj+F+(CtQbClT5&{Op` e#Ѕ"y} udد)EQ@qs@NQ.X3Vt3lSޟj [f\3i:Qh g,:gudؐ3)"xc^|z)XR3PfMzvRReW% ʦ+[>kA6wA*]y9L.,ǖ՛嶣V2cIQhM@CϫaXX04}xubH[gG`{&/|K`:@Tf5[H wE []nS1jg_mZ,#)جfx)+;O˞ w~fUԓKԕɰJ_F5C#l j®w28$ͳU$ޮ:ObcJ {6NfXɋ,ZWڄ83R<n_UŨkw-JD17SZ⊾ 1G|:% o_֍T3l13j-[h(3 5d%\bjsMX֯hǧXr2f=UߎXKH{r,R/4Jˮ}șrM{L+}㜚@+JF"h.l&&AkG;5PhE&#+3sor["BOnM:VK}QeYȕ>ӏs@z Qa.wyTs{b%ȹ[|ʧn( 8Ȁ ۔V (_~ ֝@PƔiK9eWfr*_<ݻ9K fDboݺRB|YqWA=.ZP7Cr[C#ah+f*Gq>%-nrL#1X{MٕժmB+Xgg ;P&S'bw$y~xB8=aŻ p)vjqĤkI/@n}nKtZab 5P~gBtὒvؔX=7i΢D99wc]IlSzϠQ$ Nq\e8U eec ~1mz6Vq7UEoz WؕR;_䏼|U( ՗ʕlUV9?ơʥn* )} V58`Y6Ԕqr7!or 1"i3 せDա6,v~HleOQ RX7 J*˖/?MhqKD;/EWQODÿupf >g6j0m\*ȉZ̅{Cx4"S)ė0L7_ԭ*M}n8sQamٛ 䚧߼bUɫj\`(|zLN~n oB&@l ù &A&%YwQAݒVkBkn&b*ӑ.ݣJDe6ːߝCS79&#Z*⺴d 붝J a~ܔnZo|3e B*apaAk(Z#QaXJnznwGη!"4yTGO<{z@ `QM4k1UY]b/zo~Dy ZQY6Uߍ7sJJkDnw$I 4^D$ji"W}Ⱦh; ew=ZM*?\Ӧm~zITOF-?1L2\4SګگN1jNpr"T [VYy7 W|1h"r\P)+?Fir}>RQԜ1!7}⾌|QWu!2NfL&;sP0f~=Cl%gz|/E݋Edjn$p#j )C5REaAPsK"{DђC {v`X'65a9\>4`l^PS]1CFZ?>A4@ͰekIz,ķ;mV4 0tʸ~),*|V 7:Z#MZ2ESs!AA=qi}S獙Ke[ĠNe_ҿ:U5HC_-q, tHԠUza1##f+a' B(YPbM5!<&`ښU7D&l> }e${m9 )<с WN7:!sz?{fcK8j[͇cP;9|pu G>fwݻI q(nK;2>2*PЇY۴Xw$W O&%w|fthH-^7tJF-7g8x귚bN3޴vqxX c6dlEܦ55 O=>1=sT+Щ7AH sD7$uλvȯWg™I,ԯ,?ʱoJ/mEv kK u]b @ vקf2Yn2_Ke' Z4w[)kS#+JS.$tX!fbnS:Yy u [/Ǫ}Mx3jS[%1Dy&gƷB&I?˜(W]cqg~~1Up2`щ^oN<>LFӪ-U@ >@ps>(|f_5zɪ`2(z^o2=rHtuRlWaC[AM=qPo>CĞ\~bj_ _#CmUЯj],}n0{։P`4g+6xy;.M*3DPONwUdP8h`쐏/\]^3QwBT5ZCLa-¡]Ѱgnt k47hj9t/z ijK)S_pfxS:sO&]O8!EǚHJ^ˠ'>l`l9n9K~=ʸ&eSn5o`dn!(59G#*uV3> 1`څA֛t}m;wߘܿ0r?wwCxhR#b Hxۏ'?fqG1/F}vaIbSR{ٞ,`b^qCՂ6\v](-GDMEͬGIݫ4ˠ aGG+8wF4atCWdO= gud5P&t`|FPr*'U-f Qu]:4b$ x%:yU(W]M˿gA`dMo 1~h+}~,!TʹT '$f}e^"P]$VɤC*JXG5`[JӗlGT\.{y°/ӭL$ITb>{ ߂e"&PB!i ";4a`4G/Midǯ+J R⭑t|`#ӬeQ52ɘZyR W,z28kؽ鼭ڰ2] =-19qZ s]eJȎ_X ḗSS:OPui~~>V wg=]>3 3w3y8ldɪUpkΎ[6x7~×@7= *VWeV82Bv8b׾}+2ɠb7Ո*//Q/rL0A>s_` 㛰 c, YsoHArkzdP>_YdAlR8!/ѼMe#0'qa4Mv"RF[Bww@[`EՔ9D%Y6DʖwjH8Ծ@8xFz0?c{#"2,/a>+>r}=gL4Qj }٠!s= yqV:E⮯Gl*Z롪G{G7e cAطNkAG췱ZZ]U~DuL 8g˙*̜lCɯsm=ײҪ0+{U4`1JLf?]ONIQ#8앶Q+gӆY]Q{~\㤩 Qҕja9-zZгw|wjy9W ]l$f5m  ?ґmZzraL^Zlf(5X%?9/E4691$ "Cm_4/rcEx?̰3t7U*M9Xvij}zk:Ӂft8c54в+ʨ8ϋ*o{%=7O#%a}\7r6`22taS~> #1h}ӟE{*¡$h 譅ޱj897 FsX)\̓heZ'}y)F NzdٌO ;`XƗǐyj;M>ͳW3hu+|qIRF }x/ˣ$XtrC_<)wWKňhdNfE%O4劗=>$y2j~>3d"0I_b/;dsK/ lD9h{ I*X_Kf+_~ Gx8FPIGRZUSW-Ig.@xyufB\j]2;gi}5ޢщsy  v[e ߵU :a`S.gй.1FR>ӭs(!RB=7=3*\ZG˯Cl#zhd}KĦ Ěv]p7\~" 3=agCIh%JާMH$훨_KKtN繲 =EE}W⤄u vlN]sqqUwF 'ggy|q0BDc'|:fٿVVEuwE8b']|TDLfBlP`G^-&/g%}9"gqL0#7' K|&_ Q}&}Ŧ0q=Eg&-z%(X6K@OT~{M,; ݰf_5bo7VȨ9O}>`BŞq2|FPf:}yї!i1s3wD"Z($gLNl\ _D˞lTAA2^;C'QdB'o΄gQFLB^wA(<8 GƬMXJ6^y(V3ؼόv0Ks}C' :TLefm=7s,pT@J(C.'/#qoUz̾4\H%d{d%5pEnTV?|ٍ2$~P-8T 5`H,;g7u\lpr3BbΟ'm: }ʀ!O[ij. c} ^^JhhO}VK OdƃYqAvLn#b<4d,(42eM|ku(zj7y2d/,Kq^. `dbѢk9}<`ro4h (s<`uQ;2 Y]Ԇ(Ekw(U¸)hH<٧X R3А}֥0hon4eEIHgf)jW}Ga(=tG@l{~4[!S ee_f _75&Jf1Q.z1.>M`NuY˅ǘ"MTb %ues1`ĒJ9Ȗ8u͠ ҈rL-Fg)ܴqafK9{Ϩ$:̺]U͛^jU{w/>n ŜZȴp,549RM~9TTn.޵#Ze݁Ҽ8)YTWn]^3v؛w,z$nJHZe@fb"l+ coi/P{%;ef{]Ɂ6aJLL#ֿvmOmd #QeZY̔M87$lLad< x̭È,`rbp{|O !LS*ُ$oy"+#"4a~ h!ѨS]QMO5YK~U㑫<\pkm[Ŷ!2u G+ZHoXN%+ UolhIe{BwIF5aWRDrͺ.tf֋a'L]LajP뙯hD5P1kCDӶ@&&\$U[E6"CQWn1b&!)ѫ{ 77$%gjN6|H܇@8Sdreom959 b'#x)6iyɽAk&&yđ<4Y$0.M7#Yi; y;6_ۮ 78,n=0%U[%ߛ(k)OZK U~t=LMNEsphqsn_V2Ka"1 s6WUD!bjt}3D]cډSok]R}y٧>PDY4q% p<)X:mY>Z1>]m]mb8c|YJy1˪ԓ= د5TKvH)jo}e=>U9;ϒzs_E(mJpF)ʼIT+z-mUD )pwHCZDkG(㷈|Ǯcln\;oa\NAL}NuKyV;v^(B9l4J4F}I$ tb=!A=؂[_ސ H)eots<4_DY؀R5&D 1^d긻…:bcis:jgd u6 k]3RfWƋ#e'a14D.^x=\VZER$dc#B9$)w{~=q }'ԛ¤Jna?QdFkxyȶ},۶d,oTd7; d=$v<J?@Ss{[zxa|w>*z\u4GAyΗe02IQ* ofvY/ UVHeAjCҼeC4Y_2q0Z5%V*nxɧٵ)S'WSr<[_&஛[}5.Z/NJBLsmZ%?^5ʈd V[J` x\L MTNHPm Hnfyd]kQMb3%n燖Qb6AǤWϱH9?3 u/UIk7H!naoqK1瓄w&Ek8㰨[!}' %1aa!a* zjBDv[h E8o}i&:C(K`XsÃ܊ ~;OS4$ g2 mg:D KGL35Jv凓[WS\8eE/S!5!Ezs nuџ^X2Aa ` 뽑daKȝn?BJm//$Fѩ3W,nU~f]J^.:;7LHk H>ǁxT`l {@vՊ%?y)5~2;S'nZ\xI?r=|@HB S]j9Yˌ--gb󷁇WHG7lrnB2Kg)"Aث?\GGj睳Qr7~ @ oe=Hvӟ{ 0D\$pZN. JS '"d8)u :GlWn)f~ą1l֕B=LoV nT`Uc 0="ǫ W]l=K~9A2 \*NkΒʭ}Y9 ZdU"m_z3_۴uŢ;1:Z>i;tlquu.[P6i?(<%#vW!n*{#jb0 z]oQsEXufYT{L+~.ВT- \Qp/`cr#q[ !r {56$xpdQxw sBYOF۟'R;GFɗ)X2_ÆB_C^w}Ly"Fqq?cD9Q?4~φD9zcܘYa̙VISeْ0g 9#ZR '3/Vãoq&zb26c8Mzɡ )PYx:>pnP !y a},gfk|^x y\ 2+-3α"/hr|TW*HпyiK4FɎ] D:Ud^qϭrb./{(BHG^y;z0(aķmԮȲJ/=,= 8" y1JҒ3',L?<["Se~;-cezeV@#t #*PSO~xԊRuy% Y>/(\^>8KV"*L4~WQӯ(97D!)=lUq ?Πk9$ڦ0uܫ ;`p+W#$ՆMD@dceo!˵ aI+?Y`Pc]^~X`bg !FsYx\e(ψ*)[=È}*ṶA[{gw )`vB>6%NR:`ŏ>{[{W}Uze T-Rʘ7Т8Q!ƠB &ePjFkb`iz9Cbx&^mMbSM &&{!^YyuO˙|oDºPQ;sP64sD `H#XD$:mrzh$8-grڠ<ߝw)O8[#T݄ul.S/ +ȅYlp雈JQn)_i{z?*&6Ezڤk2瑌&pU39 S'骽i0Y:U}1ZEJ ӑLZHdvnTM+JFMfpZ ,Ov<.̱d;Rkr̫ViꉶiE= ZHZCfEܦ=BҬ_7TAxTɪ,D7u5lj  P#8 暚(O䘞35@K:Yqrkblp@oV+4a"-[ҵoxG=[)Ծ2p6W! 4N*S9Gf:?2-lv(zSSW oEuwT:O9u<ȑҴUw8`܇F+я>+X :} o(xFqBKq-nOcaUC+fE@%jL '3J\1o1 _#F,w$^V#UOjAU~Mמ(a:Z>ݦx3|!=`7s61pz}[JwXʼZјxTY 2_)"2.Wew^))$1F$04yp1Rȍ 4NWD`;Q/"dv1OQ<1 4 `!y\ a WqCbD!%u1(n~ٍٕ_fAM0h3j6fi);Z]DXwā]ucV3~'y}l9O"\ |!<fkowZ4'G`鮲PsN ŽP7GXaDؗCkxn!Yc^:i)]#_^ YGpQaxB{hiY3nA 7S 6j /jm)[H՗Qs0U@kEE2)B>J~VWn9\PӝM* Iqd>X4UߠsQK3<[QqwD] [>(!G{eB0B/Hm{oBŭ=Kas:p8Ϳ23 .<: q+$ThW@쮚+ OTmÃ95FԥXfO69*}ܘkC,v vVWqD,TeqRv>օU|j;%]dvXLEMɀEe*M%eкƺ_鏔XO/MzEG]"GF3>uZUI%ژI7i)h٘;yYw|7@4WYU9UUKh(l(*|_=فt! L?ޒEA"}-xZzX2U:>R]^4n܀K-N5I2z9Nf-lܽ%# }҈(^]w̷q^H4u?2( -㬶xDɎ7^exaFl u=eFָ w[o*mhҷ!(Hb`-Wv?*v]u ʌﱆb•H'ڋf9*{on'9Qb*Yfj9&e[UJ&KfY3+ :K;Ig~:UaّWҷ S#U8lhT2o]dИs'ڂׁ]15–'y I0g¤1룐S!C#W՝5?[Y'Vbr}69?%1}Xxv?^CW4Ш3yp/NU( )2LCȩx|~0~s"TIi T͑carFTp A8z"Ӏ/zZMa5?uOPfKi.xн kw$H3iٝF_Zjq>a zʇnzQט8ao9es$\Η"RY濷SgZ+9Y!ϞkoCxAp-l-lr?A&6@#'{[#'kTpendstream endobj 15 0 obj << /Type /Font /Subtype /Type1 /Encoding 71 0 R /FirstChar 11 /LastChar 122 /Widths 72 0 R /BaseFont /IIDOLT+CMR10 /FontDescriptor 13 0 R >> endobj 13 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /IIDOLT+CMR10 /ItalicAngle 0 /StemV 69 /XHeight 431 /FontBBox [-251 -250 1009 969] /Flags 4 /CharSet (/ff/fi/fl/ffi/quotedblright/quoteright/parenleft/parenright/comma/hyphen/period/slash/zero/one/two/three/four/five/six/eight/colon/semicolon/equal/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/R/S/T/U/V/W/X/Z/bracketleft/bracketright/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z) /FontFile 14 0 R >> endobj 72 0 obj [583 556 556 833 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0 0 0 0 278 389 389 0 0 278 333 278 500 500 500 500 500 500 500 500 0 500 0 278 278 0 778 0 0 778 750 708 722 764 681 653 785 750 361 514 778 625 917 750 778 681 0 736 556 722 750 750 1028 750 0 611 278 0 278 0 0 0 500 556 444 556 444 306 500 556 278 306 528 278 833 556 500 556 528 392 394 389 556 528 722 528 528 444 ] endobj 71 0 obj << /Type /Encoding /Differences [ 0 /.notdef 11/ff/fi/fl/ffi 15/.notdef 34/quotedblright 35/.notdef 39/quoteright/parenleft/parenright 42/.notdef 44/comma/hyphen/period/slash/zero/one/two/three/four/five/six 55/.notdef 56/eight 57/.notdef 58/colon/semicolon 60/.notdef 61/equal 62/.notdef 64/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P 81/.notdef 82/R/S/T/U/V/W/X 89/.notdef 90/Z/bracketleft 92/.notdef 93/bracketright 94/.notdef 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z 123/.notdef] >> endobj 11 0 obj << /Length1 851 /Length2 2352 /Length3 532 /Length 2962 /Filter /FlateDecode >> stream xRi8}%,5&rKbkh"bf3fdWʾ$[(RRIlSٲ QIxx'||OQ)n>LW@Qꀎ(#!RR:4K'PȺX:PT(u:Z"P4'Ց!R0$Faɀ 8, =`D _i~ A<G@ȈNT _#?ȲCx Awœ¾ d'o\A$cI?7[K"TPHTfE"|(W\ 5:.+ϟ>w;dwV؃y=99C-;8=ZǷB wq ӧqg̪w?nK\939S{#Jeg5ҐCAF#y8 EƗFi d\s1! /(FnfZ(9:k2 }CPYN1 7t5nYcl%=`ɲ҅zboz_E$ybJևB}ώ^1Y!9uhɮsaa){:o2=76g`5rФk/5^ޢbI:}';== r͢J-`zme ʴ N/|9yFr/|@.zj*pi;ZN9/KBN;8ILz͋}4R @{,ik )hdq~lfޔ4ĭiPqQGΝJR՟xʜܓ3\?-n]DnccqO}.5u mL`ڍ,AG^E}*ZRFX~5uei߅ 3˿J3}59}݆ŏ- YrSv: H_t.fS˔|UWR;{(VMlvPEhZj~q$?57,um=BLabT)pO]a.ǝ?^𿒋",ߺnj:&6oR)0;,"b>*E874`U.ΨoG^N@J*Iȫ=T=s*af")mLA%hB#5Hu5$G6}]dsF>q&D<. PiВ%`5Bgwi3t~SfDtto}bə~6 O<:|yxJS7esn!5U\/}hm}➂_4tQy[傋6 W-n[,NSMXԴNI ~lZo(}miTՕ|zE)B X%%vwʻ)e/olZH6OIhB̖!.$4#xXS2X =HY9V'S#s(1~q  ' pDKSHX7_endstream endobj 12 0 obj << /Type /Font /Subtype /Type1 /Encoding 73 0 R /FirstChar 69 /LastChar 115 /Widths 74 0 R /BaseFont /OKONFQ+CMTI12 /FontDescriptor 10 0 R >> endobj 10 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /OKONFQ+CMTI12 /ItalicAngle -14 /StemV 63 /XHeight 431 /FontBBox [-36 -251 1103 750] /Flags 4 /CharSet (/E/L/S/U/a/b/s) /FontFile 11 0 R >> endobj 74 0 obj [663 0 0 0 0 0 0 613 0 0 0 0 0 0 550 0 727 0 0 0 0 0 0 0 0 0 0 0 500 450 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 400 ] endobj 73 0 obj << /Type /Encoding /Differences [ 0 /.notdef 69/E 70/.notdef 76/L 77/.notdef 83/S 84/.notdef 85/U 86/.notdef 97/a/b 99/.notdef 115/s 116/.notdef] >> endobj 8 0 obj << /Length1 926 /Length2 2855 /Length3 532 /Length 3496 /Filter /FlateDecode >> stream xyI4% 8 7(L2A@m <e8PVԆAcрǾiDGH l 4"  [p @!iyO aɀ_a)BeM1d`@,fKfoY̏@EQD<_d 6d H!\z˛ ZP<ڐI_!< O1vx`Q*=0?`=g“h TgĿ= p8]~r,SBM@Q((}3ؤ\@x m%i=`K@SU~`e%Q 0HMV*d@¿";笍 J*dۂ:'0a =P@foKA: :^kJBL r+py>Hl~pϥ$BQDvٍBss2;R nwa_r^axsxҢpC͋%5dDgI˭'\̳;j8;.Ϳ%3B]):∊HoIj4+@$%y7RW4_F?k_~_}o?k@ݝ30$px2W3wfIL'˺R7K蘖 ̵PVgS%l]Zц iv9lD:uAnQ! $3mLL3F{F-r)FE+8Ѭ ضiErbUB)oy*ϕGKR5UR $.m͸!=5՗Zb>3NN-$F'sk}*ekԠ4Xѓ!wS8KblȬ{{v=L]ZpfZw/u`cuK58XBs#8ѫN}Cv6=:ix*3w\]pMtt@r BࣵƧKVLDB}F0G.:\R#>t6*s:\WN\x.R;_v֋>T=;쀵;G ;ޛA <9-n[]֯a|άywV[X%.#ɴ_JvVډ"Pr|GѶ#=OCcBg_HMBI xw|bD:[hWDIL{LTIt̼ hv}g8d|~ubυ4؝2;+'bw)k%.%/6&R,,D|U$8>Y ukN9-(̰E`ʇkIgy1?UgU nzHmq d:,m$BлvOdSMoQIFwT&Ȱo&褬ֹ{"E0(+ KS5ސL:o/rɜ(EOԷ) }w~q<,70q`Ղ|zfYZq[юim˖5\x)*}}yLpLy/}IE_d $>]t\K^EǝBɓ4CJCƥW0u]ud'EG5m8ާ͹½zf<G^'ib K9w4>e|fz&Y.K9rk}q1]~Ӆ˓&ie_05qR~Gl:3t )e4?U!ƭaط=zaM^ {hVHgrG4|R\g*v oi }9˒ jwm'Sl;ea8j=Gˬ|(fD_ԓkenqSy,TyСná;"JyxY9Ӷ%%F$v嶰b3VX \4:j7yz=6 䝐,Ϥu92R~Ƨ!lOoA% :fk3W[Ϩs=^K;1y XsTӡЊC\>_B=S%ēqZY>ԯBn +u ø?!&( LDQ!@!endstream endobj 9 0 obj << /Type /Font /Subtype /Type1 /Encoding 75 0 R /FirstChar 48 /LastChar 117 /Widths 76 0 R /BaseFont /CHAULQ+CMR12 /FontDescriptor 7 0 R >> endobj 7 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /CHAULQ+CMR12 /ItalicAngle 0 /StemV 65 /XHeight 431 /FontBBox [-34 -251 988 750] /Flags 4 /CharSet (/zero/two/four/A/K/d/e/g/i/l/n/u) /FontFile 8 0 R >> endobj 76 0 obj [490 0 490 0 490 0 0 0 0 0 0 0 0 0 0 0 0 734 0 0 0 0 0 0 0 0 0 761 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 544 435 0 490 0 272 0 0 272 0 544 0 0 0 0 0 0 544 ] endobj 75 0 obj << /Type /Encoding /Differences [ 0 /.notdef 48/zero 49/.notdef 50/two 51/.notdef 52/four 53/.notdef 65/A 66/.notdef 75/K 76/.notdef 100/d/e 102/.notdef 103/g 104/.notdef 105/i 106/.notdef 108/l 109/.notdef 110/n 111/.notdef 117/u 118/.notdef] >> endobj 5 0 obj << /Length1 964 /Length2 3345 /Length3 532 /Length 4006 /Filter /FlateDecode >> stream xyqbHK9'rycjyvII2E (*V @T]!zqnT@ZGg @xa84X/z 4 X8,5@@ ``ɾX<;`ph*u?\3O/L"eD 0XWv KW!}x3?on?D/o* 1X2១h3bp>^QQxApc?L8>Ahw`ٱ?EK\!>(jW՟7ӧC z ^z4#/ "QA'e ? Tz @I0J$\'Li`/ A07`o~C%*`oH`7"Fߐ7_FArTY+)0%x&H>X#]@YAAWeEX/OvѯE"OGyܨ\W>mDWO8"owNWna>tG9zwy&3jiEo*IPؚm1)k5 chnɊRg⭣iRb<餒1?d汾hzّcޭּ݋ |K,r 2Ue8h&P)$'\YY#3K}Q֤͗Πh&w< ׯ1bGfBeGz=LݻGik/̋2a L= _D%oj-;Vu@{JiGY,hbI #Jeeqzg^_x ˼*$u`#dy-Sctҁ[!4~ ݯ}Ib)j5x:;GkcB LIL3Li-6w МV8J+yZRT1|fHȭwqA͆ {„B W"#iW" [?W'&:9$Ħ@xajtZJ" O1@$o8w> cxjs* 7`~-?\*![yAߞZT#aBEZ>c.#;RKڹ.5>yϢL n6Z 'l˞s Kw|))5fP|Q`g!tkng7lf2'ˤ) pu/baO0G_ ?p|z\k4] .J=ރZ!9KPy[[pf/wkl] :b'n䟷:/ u^e49e kbz!!261 Hv93^>bdڡ+8s9Uۻ1m'dS/2jSmo5fۇH1r\flϟNa"tb\Cx+ڍK[5֨:SФSdm7u3`#*}U#ޮ2K/x^`*Gyr*l v{8w\vZw{ĘxH~{^6f)) #define endof_field(t, f) (sizeof(((t *)0)->f) + offsetof(t, f)) #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1)) #define round_down(x,y) ((x) & ~((y)-1)) #define BITS_PER_INT (sizeof(unsigned) * 8) #define BITS_PER_LONG (sizeof(unsigned long) * 8) #ifdef __GNUC__ #define PRINTFLIKE __attribute__((format(printf,1,2))) #define noreturn __attribute__((noreturn)) #else #define PRINTFLIKE #define noreturn #endif int Wprintf(char *fmt, ...) PRINTFLIKE; void Eprintf(char *fmt, ...) PRINTFLIKE; void SYSERRprintf(char *fmt, ...) PRINTFLIKE; void Lprintf(char *fmt, ...) PRINTFLIKE; void Gprintf(char *fmt, ...) PRINTFLIKE; extern int open_logfile(char *fn); /* Don't forget to update mcelog.c:cputype_name[] too */ enum cputype { CPU_GENERIC, CPU_P6OLD, CPU_CORE2, /* 65nm and 45nm */ CPU_K8, CPU_P4, CPU_NEHALEM, CPU_DUNNINGTON, CPU_TULSA, CPU_INTEL, /* Intel architectural errors */ CPU_XEON75XX, CPU_SANDY_BRIDGE, CPU_SANDY_BRIDGE_EP, CPU_IVY_BRIDGE, CPU_IVY_BRIDGE_EPEX, CPU_HASWELL, }; enum option_ranges { O_COMMON = 500, O_DISKDB = 1000, }; enum syslog_opt { SYSLOG_LOG = (1 << 0), /* normal decoding output to syslog */ SYSLOG_REMARK = (1 << 1), /* special warnings to syslog */ SYSLOG_ERROR = (1 << 2), /* errors during operation to syslog */ SYSLOG_ALL = SYSLOG_LOG|SYSLOG_REMARK|SYSLOG_ERROR, SYSLOG_FORCE = (1 << 3), }; extern void usage(void); extern void no_syslog(void); extern void argsleft(int ac, char **av); extern char *processor_flags; extern int force_tsc; extern enum syslog_opt syslog_opt; extern int syslog_level; extern enum cputype cputype; extern int filter_memory_errors; extern int imc_log; extern void set_imc_log(int cputype); mcelog-100/core2.h0000664000175000017500000000011212247142742013426 0ustar vorlonvorlonvoid core2_decode_model(u64 status); void p6old_decode_model(u64 status); mcelog-100/dmi.c0000664000175000017500000003537112247142742013177 0ustar vorlonvorlon/* Copyright (C) 2006 Andi Kleen, SuSE Labs. Use SMBIOS/DMI to map address to DIMM description. For reference see the SMBIOS specification 2.4 dmi 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; version 2. dmi 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Notebook add an option to dump existing errors in SMBIOS? implement code to look up PCI resources too. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include "mcelog.h" #include "dmi.h" #include "memutil.h" static int verbose = 0; int dmi_forced; int do_dmi; struct anchor { char str[4]; /* _SM_ */ char csum; char entry_length; char major; char minor; short maxlength; char rev; char fmt[5]; char str2[5]; /* _DMI_ */ char csum2; unsigned short length; unsigned table; unsigned short numentries; char bcdrev; } __attribute__((packed)); static struct dmi_entry *entries; static int entrieslen; static int numentries; static int dmi_length; static struct dmi_entry **handle_to_entry; struct dmi_memdev **dmi_dimms; struct dmi_memarray **dmi_arrays; struct dmi_memdev_addr **dmi_ranges; struct dmi_memarray_addr **dmi_array_ranges; static void collect_dmi_dimms(void); static struct dmi_entry **dmi_collect(int type, int minsize, int *len); static void dump_ranges(struct dmi_memdev_addr **, struct dmi_memdev **); static unsigned checksum(unsigned char *s, int len) { unsigned char csum = 0; int i; for (i = 0; i < len; i++) csum += s[i]; return csum; } /* Check if entry is valid */ static int check_entry(struct dmi_entry *e, struct dmi_entry **next) { char *end = (char *)entries + dmi_length; char *s; if (!e) return 0; s = (char *)e + e->length; if (verbose > 3) printf("length %d handle %x\n", e->length, e->handle); do { if (verbose > 3) printf("string %s\n", s); while (s < end-1 && *s) s++; if (s >= end-1) { if (verbose > 0) printf("handle %x length %d truncated\n", e->handle, e->length); return 0; } s++; } while (*s); if (s >= end) *next = NULL; else *next = (struct dmi_entry *)(s + 1); return 1; } /* Relies on sanity checks in check_entry */ char *dmi_getstring(struct dmi_entry *e, unsigned number) { char *s = (char *)e + e->length; if (number == 0) return ""; do { if (--number == 0) return s; while (*s) s++; s++; } while (*s); return NULL; } static void fill_handles(void) { int i; struct dmi_entry *e, *next; e = entries; handle_to_entry = xalloc(sizeof(void *) * 0xffff); for (i = 0; i < numentries; i++, e = next) { if (!check_entry(e, &next)) break; handle_to_entry[e->handle] = e; } } static int get_efi_base_addr(size_t *address) { FILE *efi_systab; const char *filename; char linebuf[64]; int ret = 0; *address = 0; /* Prevent compiler warning */ /* Linux 2.6.7 and up: /sys/firmware/efi/systab */ filename = "/sys/firmware/efi/systab"; if ((efi_systab = fopen(filename, "r")) != NULL) goto check_symbol; /* Linux up to 2.6.6: /proc/efi/systab */ filename = "/proc/efi/systab"; if ((efi_systab = fopen(filename, "r")) != NULL) goto check_symbol; /* Failed to open EFI interfaces */ return ret; check_symbol: while ((fgets(linebuf, sizeof(linebuf) - 1, efi_systab)) != NULL) { char *addrp = strchr(linebuf, '='); *(addrp++) = '\0'; if (strcmp(linebuf, "SMBIOS") == 0) { *address = strtoul(addrp, NULL, 0); ret = 1; break; } } if (fclose(efi_systab) != 0) perror(filename); if (!ret) Eprintf("%s: SMBIOS entry point missing", filename); if (verbose) printf("%s: SMBIOS entry point at 0x%08lx\n", filename, (unsigned long)*address); return ret; } int opendmi(void) { struct anchor *a, *abase; void *p, *q; int pagesize = getpagesize(); int memfd; unsigned corr; int err = -1; const int segsize = 0x10000; size_t entry_point_addr = 0; size_t length = 0; if (entries) return 0; memfd = open("/dev/mem", O_RDONLY); if (memfd < 0) { Eprintf("Cannot open /dev/mem for DMI decoding: %s", strerror(errno)); return -1; } /* * On EFI-based systems, the SMBIOS Entry Point structure can be * located by looking in the EFI Configuration Table. */ if (get_efi_base_addr(&entry_point_addr)) { size_t addr_start = round_down(entry_point_addr, pagesize); size_t addr_end = round_up(entry_point_addr + 0x20, pagesize); length = addr_end - addr_start; /* mmap() the address of SMBIOS structure table entry point. */ abase = mmap(NULL, length, PROT_READ, MAP_SHARED, memfd, addr_start); if (abase == (struct anchor *)-1) { Eprintf("Cannot mmap 0x%lx for efi mode: %s", (unsigned long)entry_point_addr, strerror(errno)); goto legacy; } a = (struct anchor*)((char*)abase + (entry_point_addr - addr_start)); goto fill_entries; } legacy: /* * On non-EFI systems, the SMBIOS Entry Point structure can be located * by searching for the anchor-string on paragraph (16-byte) boundaries * within the physical memory address range 000F0000h to 000FFFFFh */ length = segsize - 1; abase = mmap(NULL, length, PROT_READ, MAP_SHARED, memfd, 0xf0000); if (abase == (struct anchor *)-1) { Eprintf("Cannot mmap 0xf0000 for legacy mode: %s", strerror(errno)); goto out; } for (p = abase, q = p + segsize; p < q; p += 0x10) { if (!memcmp(p, "_SM_", 4) && (checksum(p, ((struct anchor *)p)->entry_length) == 0)) break; } if (p >= q) { Eprintf("Cannot find SMBIOS DMI tables"); goto out; } a = p; fill_entries: if (verbose) printf("DMI tables at %x, %u bytes, %u entries\n", a->table, a->length, a->numentries); corr = a->table - round_down(a->table, pagesize); entrieslen = round_up(a->table + a->length, pagesize) - round_down(a->table, pagesize); entries = mmap(NULL, entrieslen, PROT_READ, MAP_SHARED, memfd, round_down(a->table, pagesize)); if (entries == (struct dmi_entry *)-1) { Eprintf("Cannot mmap SMBIOS tables at %x", a->table); goto out_mmap; } entries = (struct dmi_entry *)(((char *)entries) + corr); numentries = a->numentries; dmi_length = a->length; fill_handles(); collect_dmi_dimms(); err = 0; out_mmap: munmap(abase, length); out: close(memfd); return err; } unsigned dmi_dimm_size(unsigned short size, char *unit) { unsigned mbflag = !(size & (1<<15)); size &= ~(1<<15); strcpy(unit, "KB"); if (mbflag) { unit[0] = 'M'; if (size >= 1024) { unit[0] = 'G'; size /= 1024; } } return size; } static char *form_factors[] = { "?", "Other", "Unknown", "SIMM", "SIP", "Chip", "DIP", "ZIP", "Proprietary Card", "DIMM", "TSOP", "Row of chips", "RIMM", "SODIMM", "SRIMM" }; static char *memory_types[] = { "?", "Other", "Unknown", "DRAM", "EDRAM", "VRAM", "SRAM", "RAM", "ROM", "FLASH", "EEPROM", "FEPROM", "EPROM", "CDRAM", "3DRAM", "SDRAM", "SGRAM", "RDRAM", "DDR", "DDR2" }; #define LOOKUP(array, val, buf) \ ((val) >= NELE(array) ? \ (sprintf(buf,"<%u>",(val)), (buf)) : \ (array)[val]) static char *type_details[16] = { "Reserved", "Other", "Unknown", "Fast-paged", "Static Column", "Pseudo static", "RAMBUS", "Synchronous", "CMOS", "EDO", "Window DRAM", "Cache DRAM", "Non-volatile", "Res13", "Res14", "Res15" }; static void dump_type_details(unsigned short td) { int i; if (!td) return; for (i = 0; i < 16; i++) if (td & (1<header.length < offsetof(struct dmi_memdev, manufacturer)) { if (verbose > 0) printf("Memory device for address %lx too short %u\n", addr, md->header.length); return; } Wprintf("%s ", LOOKUP(memory_types, md->memory_type, tmp)); if (md->form_factor >= 3) Wprintf("%s ", LOOKUP(form_factors, md->form_factor, tmp)); if (md->speed != 0) Wprintf("%hu Mhz ", md->speed); dump_type_details(md->type_details); Wprintf("Width %hu Data Width %hu Size %u %s\n", md->total_width, md->data_width, dmi_dimm_size(md->size, unit), unit); #define DUMPSTR(n,x) \ if (md->x) { \ s = dmi_getstring(&md->header, md->x); \ if (s && *s && strcmp(s,"None")) \ Wprintf(n ": %s\n", s); \ } DUMPSTR("Device Locator", device_locator); DUMPSTR("Bank Locator", bank_locator); if (md->header.length < offsetof(struct dmi_memdev, manufacturer)) return; DUMPSTR("Manufacturer", manufacturer); DUMPSTR("Serial Number", serial_number); DUMPSTR("Asset Tag", asset_tag); DUMPSTR("Part Number", part_number); } static void warnuser(void) { static int warned; if (warned) return; warned = 1; Wprintf("WARNING: " "SMBIOS data is often unreliable. Take with a grain of salt!\n"); } static int cmp_range(const void *a, const void *b) { struct dmi_memdev_addr *ap = *(struct dmi_memdev_addr **)a; struct dmi_memdev_addr *bp = *(struct dmi_memdev_addr **)b; return (int)ap->start_addr - (int)bp->end_addr; } static int cmp_arr_range(const void *a, const void *b) { struct dmi_memarray_addr *ap = *(struct dmi_memarray_addr **)a; struct dmi_memarray_addr *bp = *(struct dmi_memarray_addr **)b; return (int)ap->start_addr - (int)bp->end_addr; } #define COLLECT(var, id, ele) { \ typedef typeof (**(var)) T; \ var = (T **)dmi_collect(id, \ offsetof(T, ele) + sizeof_field(T, ele), \ &len); \ } static void collect_dmi_dimms(void) { int len; COLLECT(dmi_ranges, DMI_MEMORY_MAPPED_ADDR, dev_handle); qsort(dmi_ranges, len, sizeof(struct dmi_entry *), cmp_range); COLLECT(dmi_dimms, DMI_MEMORY_DEVICE, device_locator); if (verbose > 1) dump_ranges(dmi_ranges, dmi_dimms); COLLECT(dmi_arrays, DMI_MEMORY_ARRAY, location); COLLECT(dmi_array_ranges, DMI_MEMORY_ARRAY_ADDR, array_handle); qsort(dmi_array_ranges, len, sizeof(struct dmi_entry *),cmp_arr_range); } #undef COLLECT static struct dmi_entry ** dmi_collect(int type, int minsize, int *len) { struct dmi_entry **r; struct dmi_entry *e, *next; int i, k; r = xalloc(sizeof(struct dmi_entry *) * (numentries + 1)); k = 0; e = entries; next = NULL; for (i = 0; i < numentries; i++, e = next) { if (!check_entry(e, &next)) break; if (e->type != type) continue; if (e->length < minsize) { if (verbose > 0) printf("hnd %x size %d expected %d\n", e->handle, e->length, minsize); continue; } if (type == DMI_MEMORY_DEVICE && ((struct dmi_memdev *)e)->size == 0) { if (verbose > 0) printf("entry %x disabled\n", e->handle); continue; } r[k++] = e; } *len = k; return r; } #define FAILED " SMBIOS DIMM sanity check failed\n" int dmi_sanity_check(void) { int i, k; int numdmi_dimms = 0; int numranges = 0; if (dmi_ranges[0] == NULL) return 0; for (k = 0; dmi_dimms[k]; k++) numdmi_dimms++; /* Do we have multiple ranges? */ for (k = 1; dmi_ranges[k]; k++) { if (dmi_ranges[k]->start_addr <= dmi_ranges[k-1]->end_addr) { return 0; } if (dmi_ranges[k]->start_addr >= dmi_ranges[k-1]->end_addr) numranges++; } if (numranges == 1 && numdmi_dimms > 2) { if (verbose > 0) printf("Not enough unique address ranges." FAILED); return 0; } /* Unique locators? */ for (k = 0; dmi_dimms[k]; k++) { char *loc; loc = dmi_getstring(&dmi_dimms[k]->header, dmi_dimms[k]->device_locator); if (!loc) { if (verbose > 0) printf("Missing locator." FAILED); return 0; } for (i = 0; i < k; i++) { char *b = dmi_getstring(&dmi_dimms[i]->header, dmi_dimms[i]->device_locator); if (!strcmp(b, loc)) { if (verbose > 0) printf("Ambigious locators `%s'<->`%s'." FAILED, b, loc); return 0; } } } return 1; } #define DMIGET(p, member) \ (offsetof(typeof(*(p)), member) + sizeof((p)->member) <= (p)->header.length ? \ (p)->member : 0) static void dump_ranges(struct dmi_memdev_addr **ranges, struct dmi_memdev **dmi_dimms) { int i; printf("RANGES\n"); for (i = 0; ranges[i]; i++) printf("range %x-%x h %x a %x row %u ilpos %u ildepth %u\n", ranges[i]->start_addr, ranges[i]->end_addr, ranges[i]->dev_handle, DMIGET(ranges[i], memarray_handle), DMIGET(ranges[i], row), DMIGET(ranges[i], interleave_pos), DMIGET(ranges[i], interleave_depth)); printf("DMI_DIMMS\n"); for (i = 0; dmi_dimms[i]; i++) printf("dimm h %x width %u datawidth %u size %u set %u\n", dmi_dimms[i]->header.handle, dmi_dimms[i]->total_width, DMIGET(dmi_dimms[i],data_width), DMIGET(dmi_dimms[i],size), DMIGET(dmi_dimms[i],device_set)); } struct dmi_memdev **dmi_find_addr(unsigned long addr) { struct dmi_memdev **devs; int i, k; devs = xalloc(sizeof(void *) * (numentries+1)); k = 0; for (i = 0; dmi_ranges[i]; i++) { struct dmi_memdev_addr *da = dmi_ranges[i]; if (addr < ((unsigned long long)da->start_addr)*1024 || addr >= ((unsigned long long)da->end_addr)*1024) continue; devs[k] = (struct dmi_memdev *)handle_to_entry[da->dev_handle]; if (devs[k]) k++; } #if 0 /* Need to implement proper decoding of interleaving sets before enabling this. */ int j, w; for (i = 0; dmi_array_ranges[i]; i++) { struct dmi_memarray_addr *d = dmi_array_ranges[i]; if (addr < ((unsigned long long)d->start_addr)*1024 || addr >= ((unsigned long long)d->end_addr)*1024) continue; for (w = 0; dmi_dimms[w]; w++) { struct dmi_memdev *m = dmi_dimms[w]; if (m->array_handle == d->array_handle) { for (j = 0; j < k; j++) { if (devs[j] == m) break; } if (j == k) devs[k++] = m; } } } #endif devs[k] = NULL; return devs; } void dmi_decodeaddr(unsigned long addr) { struct dmi_memdev **devs = dmi_find_addr(addr); if (devs[0]) { int i; warnuser(); for (i = 0; devs[i]; i++) dump_memdev(devs[i], addr); } else { Wprintf("No DIMM found for %lx in SMBIOS\n", addr); } free(devs); } void dmi_set_verbosity(int v) { verbose = v; } void checkdmi(void) { static int dmi_checked; if (dmi_checked) return; dmi_checked = 1; if (dmi_forced && !do_dmi) return; if (opendmi() < 0) { if (dmi_forced) exit(1); do_dmi = 0; return; } if (!dmi_forced) do_dmi = dmi_sanity_check(); } #define FREE(x) free(x), (x) = NULL void closedmi(void) { if (!entries) return; munmap(entries, entrieslen); entries = NULL; FREE(dmi_dimms); FREE(dmi_arrays); FREE(dmi_ranges); FREE(dmi_array_ranges); FREE(handle_to_entry); } mcelog-100/k8.h0000664000175000017500000000072212247142742012745 0ustar vorlonvorlonchar *k8_bank_name(unsigned num); void decode_k8_mc(struct mce *mce, int *ismemerr); int mce_filter_k8(struct mce *m); #define K8_MCE_THRESHOLD_BASE (MCE_EXTENDED_BANK + 1) /* MCE_AMD */ #define K8_MCE_THRESHOLD_TOP (K8_MCE_THRESHOLD_BASE + 6 * 9) #define K8_MCELOG_THRESHOLD_DRAM_ECC (4 * 9 + 0) #define K8_MCELOG_THRESHOLD_LINK (4 * 9 + 1) #define K8_MCELOG_THRESHOLD_L3_CACHE (4 * 9 + 2) #define K8_MCELOG_THRESHOLD_FBDIMM (4 * 9 + 3) mcelog-100/yellow.h0000664000175000017500000000015712247142742013740 0ustar vorlonvorlonvoid yellow_setup(void); void run_yellow_trigger(int cpu, int tnum, int lnum, char *ts, char *ls, int socket); mcelog-100/paths.h0000664000175000017500000000044612247142742013545 0ustar vorlonvorlon#define PREFIX "" #define LOG_DEV_FILENAME "/dev/mcelog" #define DIMM_DB_FILENAME PREFIX "/var/lib/memory-errors" #define CONFIG_FILENAME PREFIX "/etc/mcelog/mcelog.conf" #define SOCKET_PATH "/var/run/mcelog-client" #define LOG_FILE "/var/log/mcelog" #define PID_FILE "/var/run/mcelog.pid" mcelog-100/db.h0000664000175000017500000000236412247142742013014 0ustar vorlonvorlon#include struct database; struct group; struct database *open_db(char *fn, int wr); int sync_db(struct database *db); int close_db(struct database *db); struct group *find_group(struct database *db, char *name); char *entry_val(struct group *g, char *entry); struct group *add_group(struct database *db, char *name, int *existed); int delete_group(struct database *db, struct group *g); void change_entry(struct database *db, struct group *g, char *entry, char *newval); void add_comment(struct database *db, struct group *group, char *comment); struct group *first_group(struct database *db); struct group *next_group(struct group *g); void dump_group(struct group *g, FILE *out); void dump_database(struct database *db, FILE *out); struct group *find_entry(struct database *db, struct group *prev, char *entry, char *value); void rename_group(struct database *db, struct group *group, char *newname); char *group_name(struct group *g); unsigned long entry_num(struct group *g, char *entry); void change_entry_num(struct database *db, struct group *g, char *entry, unsigned long val); void delete_entry(struct database *db, struct group *g, char *entry); struct group * clone_group(struct database *db, struct group *gold, char *newname); mcelog-100/mcelog.init0000775000175000017500000000400112247142742014402 0ustar vorlonvorlon#!/bin/sh # # Startup script for mcelog # # This should be customized for distribution standards # (using rc_status etc.) # The paths are hardcoded and are not automatically adjusted # for different prefix # ### BEGIN INIT INFO # Provides: mcelog # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: mcelog hardware error logging # Description: Start the mcelog hardware error logging. # This logs and handles CPU hardware errors on x86 systems. ### END INIT INFO # put this is sysconfig # mcelog mode # valid values: daemon, trigger, cron # Recommended value daemon MCELOG_MODE=daemon # additional options to pass to the daemon # this only works in daemon mode # see the manpage for details. settings can be also # set in /etc/mcelog.conf MCELOG_OPTIONS="" # private settings MCELOG=${MCELOG:-/usr/sbin/mcelog} TRIGGER=/sys/devices/system/machinecheck/machinecheck0/trigger [ ! -x $MCELOG ] && ( echo "mcelog not found" ; exit 1 ) [ ! -r /dev/mcelog ] && ( echo "/dev/mcelog not active" ; exit 0 ) case "$MCELOG_MODE" in daemon) ;; trigger) ;; cron) echo "mcelog not started" exit 0 ;; *) echo "Unknown mcelog mode $MCELOG_MODE. Valid daemon/trigger/cron" exit 1 esac case "$1" in start) if [ "$MCELOG_MODE" = "daemon" ] ; then echo "Starting mcelog daemon" startproc $MCELOG --daemon $MCELOG_OPTIONS elif [ -f "$TRIGGER" ] ; then echo $MCELOG > "$TRIGGER" else echo No machine check capability fi ;; stop) if [ "$MCELOG_MODE" = "daemon" ] ; then echo "Stopping mcelog" killproc -TERM $MCELOG elif [ "$MCELOG_MODE" = "trigger" -a -f "$TRIGGER" ]; then echo "" > "$TRIGGER" else echo mcelog not running fi ;; try-restart) $0 status > /dev/null && $0 restart ;; restart) $0 stop $0 start ;; reload) $0 try-restart ;; force-reload) $0 try-restart ;; status) if [ "$MCELOG_MODE" = "daemon" ] ; then echo "Checking for mcelog" checkproc $MCELOG fi ;; *) echo "Usage: $0 {start|stop|try-restart|restart|status|force-reload|reload}" exit 1 esac mcelog-100/yellow.c0000664000175000017500000000575512247142742013744 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Handle 'yellow bit' cache error threshold indications. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include "memutil.h" #include "mcelog.h" #include "config.h" #include "trigger.h" #include "yellow.h" #include "cache.h" #define BITS_PER_U (sizeof(unsigned) * 8) #define test_bit(i, a) (((unsigned *)(a))[(i) / BITS_PER_U] & (1U << ((i) % BITS_PER_U))) static char *yellow_trigger; static int yellow_log = 1; enum { MAX_ENV = 10, }; static char *cpulist(char *prefix, unsigned *cpumask, unsigned cpumasklen) { unsigned i, k; char *buf = NULL; size_t size = 0; FILE *f = open_memstream(&buf, &size); if (!f) Enomem(); fprintf(f, "%s", prefix); k = 0; for (i = 0; i < cpumasklen * 8; i++) { if (test_bit(i, cpumask)) { fprintf(f, "%s%u", k > 0 ? " " : "", i); k++; } } fclose(f); return buf; } void run_yellow_trigger(int cpu, int tnum, int lnum, char *ts, char *ls, int socket) { int ei = 0; char *env[MAX_ENV]; unsigned *cpumask; int cpumasklen; int i; char *msg; char *location; if (socket >= 0) asprintf(&location, "CPU %d on socket %d", cpu, socket); else asprintf(&location, "CPU %d", cpu); asprintf(&msg, "%s has large number of corrected cache errors in %s %s", location, ls, ts); free(location); if (yellow_log) { Lprintf("%s\n", msg); Lprintf("System operating correctly, but might lead to uncorrected cache errors soon\n"); } if (!yellow_trigger) goto out; if (socket >= 0) asprintf(&env[ei++], "SOCKETID=%d", socket); asprintf(&env[ei++], "MESSAGE=%s", msg); asprintf(&env[ei++], "CPU=%d", cpu); asprintf(&env[ei++], "LEVEL=%d", lnum); asprintf(&env[ei++], "TYPE=%s", ts); if (cache_to_cpus(cpu, lnum, tnum, &cpumasklen, &cpumask) >= 0) env[ei++] = cpulist("AFFECTED_CPUS=", cpumask, cpumasklen); env[ei] = NULL; assert(ei < MAX_ENV); run_trigger(yellow_trigger, NULL, env); for (i = 0; i < ei; i++) free(env[i]); out: free(msg); } void yellow_setup(void) { int n; yellow_trigger = config_string("cache", "cache-threshold-trigger"); if (yellow_trigger && trigger_check(yellow_trigger) < 0) { SYSERRprintf("Cannot access cache threshold trigger `%s'", yellow_trigger); exit(1); } n = config_bool("cache", "cache-threshold-log"); if (n >= 0) yellow_log = n; } mcelog-100/cache.c0000664000175000017500000001054612247142742013466 0ustar vorlonvorlon/* Copyright (C) 2008 Intel Corporation Author: Andi Kleen Parse sysfs exported CPU cache topology mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include #include "mcelog.h" #include "memutil.h" #include "sysfs.h" #include "cache.h" struct cache { unsigned level; /* Numerical values must match MCACOD */ enum { INSTR, DATA, UNIFIED } type; unsigned *cpumap; unsigned cpumaplen; }; struct cache **caches; static unsigned cachelen; #define PREFIX "/sys/devices/system/cpu" #define MIN_CPUS 8 #define MIN_INDEX 4 static struct map type_map[] = { { "Instruction", INSTR }, { "Data", DATA }, { "Unified", UNIFIED }, { }, }; static void more_cpus(int cpu) { int old = cachelen; if (!cachelen) cachelen = MIN_CPUS/2; cachelen *= 2; caches = xrealloc(caches, cachelen * sizeof(struct cache *)); memset(caches + old, 0, (cachelen - old) * sizeof(struct cache *)); } static unsigned cpumap_len(char *s) { unsigned len = 0, width = 0; do { if (isxdigit(*s)) width++; else { len += round_up(width * 4, BITS_PER_INT) / 8; width = 0; } } while (*s++); return len; } static void parse_cpumap(char *map, unsigned *buf, unsigned len) { char *s; int c; c = 0; s = map + strlen(map); for (;;) { s = memrchr(map, ',', s - map); if (!s) s = map; else s++; buf[c++] = strtoul(s, NULL, 16); if (s == map) break; s--; } assert(len == c * sizeof(unsigned)); } static void read_cpu_map(struct cache *c, char *cfn) { char *map = read_field(cfn, "shared_cpu_map"); c->cpumaplen = cpumap_len(map); c->cpumap = xalloc(c->cpumaplen); parse_cpumap(map, c->cpumap, c->cpumaplen); free(map); } static int read_caches(void) { DIR *cpus = opendir(PREFIX); struct dirent *de; if (!cpus) { Wprintf("Cannot read cache topology from %s", PREFIX); return -1; } while ((de = readdir(cpus)) != NULL) { unsigned cpu; if (sscanf(de->d_name, "cpu%u", &cpu) == 1) { struct stat st; char *fn; int i; int numindex; asprintf(&fn, "%s/%s/cache", PREFIX, de->d_name); if (!stat(fn, &st)) { numindex = st.st_nlink - 2; if (numindex < 0) numindex = MIN_INDEX; if (cachelen <= cpu) more_cpus(cpu); caches[cpu] = xalloc(sizeof(struct cache) * (numindex+1)); for (i = 0; i < numindex; i++) { char *cfn; struct cache *c = caches[cpu] + i; asprintf(&cfn, "%s/index%d", fn, i); c->type = read_field_map(cfn, "type", type_map); c->level = read_field_num(cfn, "level"); read_cpu_map(c, cfn); free(cfn); } } free(fn); } } closedir(cpus); return 0; } int cache_to_cpus(int cpu, unsigned level, unsigned type, int *cpulen, unsigned **cpumap) { struct cache *c; if (!caches) { if (read_caches() < 0) return -1; if (!caches) { Wprintf("No caches found in sysfs"); return -1; } } for (c = caches[cpu]; c && c->cpumap; c++) { //printf("%d level %d type %d\n", cpu, c->level, c->type); if (c->level == level && c->type == type) { *cpumap = c->cpumap; *cpulen = c->cpumaplen; return 0; } } Wprintf("Cannot find sysfs cache for CPU %d", cpu); return -1; } #ifdef TEST main() { int cpulen; unsigned *cpumap; cache_to_cpus(1, 1, INSTR, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]); cache_to_cpus(1, 1, DATA, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]); cache_to_cpus(1, 2, UNIFIED, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]); cache_to_cpus(0, 1, INSTR, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]); cache_to_cpus(0, 1, DATA, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]); cache_to_cpus(0, 2, UNIFIED, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]); } #endif mcelog-100/dbquery.c0000664000175000017500000000475212247142742014100 0ustar vorlonvorlon/* Access db files. This is for testing and debugging only. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include "db.h" #define C(x) if (x) printf(#x " failed: %s\n", strerror(errno)) #define NEEDGROUP if (!group) { printf("need group first\n"); break; } void Eprintf(char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } void usage(void) { printf( "s sync\n" "q close/quit\n" "ggroup find group\n" "G delete group\n" "agroup add group\n" "ventry dump entry\n" "centry,val change entry to val\n" "fentry,val find entry with value and dump its group\n" "Ccomment add comment\n" "Lnewname clone group to newname\n" "d dump group\n" "D dump database\n"); } int main(int ac, char **av) { struct database *db; struct group *group = NULL; char *line = NULL; size_t linesz = 0; if (!av[1]) { printf("%s database\n", av[0]); exit(1); } printf("dbtest\n"); db = open_db(av[1], 1); while (printf("> "), fflush(stdout), getline(&line, &linesz, stdin) > 0) { char *p = line + strlen(line) - 1; while (p >= line && isspace(*p)) *p-- = 0; switch (line[0]) { case 's': C(sync_db(db)); break; case 'q': C(close_db(db)); exit(0); case 'g': group = find_group(db, line + 1); if (group) printf("found\n"); break; case 'G': NEEDGROUP; C(delete_group(db, group)); group = NULL; break; case 'a': { int existed = 0; group = add_group(db, line + 1, &existed); if (existed) printf("existed\n"); break; } case 'v': NEEDGROUP; printf("%s\n", entry_val(group, line + 1)); break; case 'c': { p = line + 1; char *entry = strsep(&p, ","); NEEDGROUP; change_entry(db, group, entry, strsep(&p, "")); break; } case 'L': NEEDGROUP; clone_group(db, group, line + 1); break; case 'f': { struct group *g; p = line + 1; char *entry = strsep(&p, ","); char *val = strsep(&p, ""); g = NULL; int nr = 0; while ((g = find_entry(db, g, entry, val)) != NULL) { if (nr == 0) group = g; nr++; dump_group(group, stdout); } if (nr == 0) printf("not found\n"); break; } case 'C': NEEDGROUP; add_comment(db, group, line + 1); break; case 'd': NEEDGROUP; dump_group(group, stdout); break; case 'D': dump_database(db, stdout); break; default: usage(); break; } } return 0; } mcelog-100/sandy-bridge.c0000664000175000017500000001103712247142742014767 0ustar vorlonvorlon/* Copyright (C) 2010 Intel Corporation Decode Intel Sandy Bridge specific machine check errors. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author: Andi Kleen */ #include "mcelog.h" #include "bitfield.h" #include "sandy-bridge.h" #include "memdb.h" /* See IA32 SDM Vol3B Table 16.4.1 */ static char *pcu_1[] = { [0] = "No error", [1] = "Non_IMem_Sel", [2] = "I_Parity_Error", [3] = "Bad_OpCode", [4] = "I_Stack_Underflow", [5] = "I_Stack_Overflow", [6] = "D_Stack_Underflow", [7] = "D_Stack_Overflow", [8] = "Non-DMem_Sel", [9] = "D_Parity_Error" }; static char *pcu_2[] = { [0x00] = "No Error", [0x0D] = "MC_IMC_FORCE_SR_S3_TIMEOUT", [0x0E] = "MC_MC_CPD_UNCPD_ST_TIMEOUT", [0x0F] = "MC_PKGS_SAFE_WP_TIMEOUT", [0x43] = "MC_PECI_MAILBOX_QUIESCE_TIMEOUT", [0x5C] = "MC_MORE_THAN_ONE_LT_AGENT", [0x60] = "MC_INVALID_PKGS_REQ_PCH", [0x61] = "MC_INVALID_PKGS_REQ_QPI", [0x62] = "MC_INVALID_PKGS_RES_QPI", [0x63] = "MC_INVALID_PKGC_RES_PCH", [0x64] = "MC_INVALID_PKG_STATE_CONFIG", [0x70] = "MC_WATCHDG_TIMEOUT_PKGC_SLAVE", [0x71] = "MC_WATCHDG_TIMEOUT_PKGC_MASTER", [0x72] = "MC_WATCHDG_TIMEOUT_PKGS_MASTER", [0x7A] = "MC_HA_FAILSTS_CHANGE_DETECTED", [0x81] = "MC_RECOVERABLE_DIE_THERMAL_TOO_HOT", }; static struct field pcu_mc4[] = { FIELD(16, pcu_1), FIELD(24, pcu_2), {} }; static char *memctrl_1[] = { [0x001] = "Address parity error", [0x002] = "HA Wrt buffer Data parity error", [0x004] = "HA Wrt byte enable parity error", [0x008] = "Corrected patrol scrub error", [0x010] = "Uncorrected patrol scrub error", [0x020] = "Corrected spare error", [0x040] = "Uncorrected spare error", }; static struct field memctrl_mc8[] = { FIELD(16, memctrl_1), {} }; void snb_decode_model(int cputype, int bank, u64 status, u64 misc) { switch (bank) { case 4: Wprintf("PCU: "); decode_bitfield(status, pcu_mc4); Wprintf("\n"); break; case 6: case 7: if (cputype == CPU_SANDY_BRIDGE_EP) { /* MCACOD already decoded */ Wprintf("QPI\n"); } break; case 8: case 9: case 10: case 11: Wprintf("MemCtrl: "); decode_bitfield(status, memctrl_mc8); Wprintf("\n"); break; } } /* * Sandy Bridge EP and EP4S processors (family 6, model 45) support additional * logging for corrected errors in the integrated memory controller (IMC) * banks. The mode is off by default, but can be enabled by setting the * "MemError Log Enable" * bit in MSR_ERROR_CONTROL (MSR 0x17f). * The documentation in the August 2012 edition of Intel's Software developer * manual has some minor errors because the worng version of table 16-16 * "Intel IMC MC Error Codes for IA32_MCi_MISC (i= 8, 11)" was included. * Corrections are: * Bit 62 is the "VALID" bit for the "first-device" bits in MISC and STATUS * Bit 63 is the "VALID" bit for the "second-device" bits in MISC * Bits 58:56 and 61:59 should be marked as "reserved". * There should also be a footnote explaining how the "failing rank" fields * can be converted to a DIMM number within a channel for systems with either * two or three DIMMs per channel. */ static int failrank2dimm(unsigned failrank, int socket, int channel) { switch (failrank) { case 0: case 1: case 2: case 3: return 0; case 4: case 5: return 1; case 6: case 7: if (get_memdimm(socket, channel, 2, 0)) return 2; else return 1; } return -1; } void sandy_bridge_ep_memerr_misc(struct mce *m, int *channel, int *dimm) { u64 status = m->status; unsigned failrank, chan; /* Ignore unless this is an corrected extended error from an iMC bank */ if (!imc_log || m->bank < 8 || m->bank > 11 || (status & MCI_STATUS_UC) || !test_prefix(7, status & 0xefff)) return; chan = EXTRACT(status, 0, 3); if (chan == 0xf) return; if (EXTRACT(m->misc, 62, 62)) { failrank = EXTRACT(m->misc, 46, 50); dimm[0] = failrank2dimm(failrank, m->socketid, chan); channel[0] = chan; } if (EXTRACT(m->misc, 63, 63)) { failrank = EXTRACT(m->misc, 51, 55); dimm[1] = failrank2dimm(failrank, m->socketid, chan); channel[1] = chan; } } mcelog-100/client.h0000664000175000017500000000004012247142742013672 0ustar vorlonvorlonvoid ask_server(char *command); mcelog-100/xeon75xx.h0000664000175000017500000000021612247142742014126 0ustar vorlonvorlonvoid xeon75xx_memory_error(struct mce *m, unsigned msize, int *channel, int *dimm); void xeon75xx_decode_dimm(struct mce *m, unsigned msize); mcelog-100/tests/0000775000175000017500000000000012247142742013413 5ustar vorlonvorlonmcelog-100/tests/server/0000775000175000017500000000000012247142742014721 5ustar vorlonvorlonmcelog-100/tests/server/inject0000664000175000017500000000031312247142742016115 0ustar vorlonvorlon#!/bin/sh PATH=$PATH:$(pwd)/../../../mce-inject ../../input/GENCACHE 1 1 data green | mce-inject ../../input/GENCACHE 1 1 data yellow | mce-inject ../../input/GENCACHE 1 2 generic yellow | mce-inject mcelog-100/tests/server/server.conf0000664000175000017500000000023612247142742017077 0ustar vorlonvorlon# trigger: 2 num-errors = 3 [cache] cache-threshold-trigger = ../trigger [server] socket-path = /tmp/mcelog-client [trigger] directory = . children-max = 3 mcelog-100/tests/pfa/0000775000175000017500000000000012247142742014161 5ustar vorlonvorlonmcelog-100/tests/pfa/page-soft.conf0000664000175000017500000000031412247142742016713 0ustar vorlonvorlon# trigger: 1 #num-errors = 3 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = soft [trigger] directory = . mcelog-100/tests/pfa/PFA_test_howto0000664000175000017500000001577712247142742017012 0ustar vorlonvorlonThis README file describes the steps of testing PFA (Predictive Failure Analysis) functionality of mcelog under Linux which is facilitated by using mce-inject. PFA is a RAS Feature. PFA capable system can monitor corrected hardware errors and take corrective action in advance before uncorrected error happen. For example, PFA should offline a memory page if more than 10 errors per hour on a memory page are found. It mostly focuses on memory errors. 0. Preparation work ******************************* - Install the Linux kernel with full MCE injection support Make sure following configuration options are enabled: CONFIG_X86_MCE=y CONFIG_X86_MCE_INTEL=y CONFIG_X86_MCE_INJECT=y or CONFIG_X86_MCE_INJECT=m - Build mcelog and install in /usr/bin (or rather first in your $PATH) # cd $HOME/mcelog # make # make install - Get mce-inject git version from git://git.kernel.org/pub/scm/utils/cpu/mce/mce-inject.git and install in /usr/bin (or rather first in your $PATH) # cd $HOME # git clone git://git.kernel.org/pub/scm/utils/cpu/mce/mce-inject.git # cd mce-inject # make # make install - Install page-types tool, which is accompanied with Linux kernel source (2.6.32 or newer). # cd $KERNEL_SRC/Documentation/vm/ # gcc -o page-types page-types.c # cp page-types /usr/bin/ 1. Start PFA test ******************************* The PFA test cases in mcelog are in the following directories: - mcelog/tests/pfa #page level pfa test cases You can run all PFA test cases simply just by typing: # cd mcelog/tests # ./test pfa all the test cases in the specified subdirectory will be ran and the test results will be saved in files: mcelog/tests/pfa/results When you examine the content of the file, you will find such results: - if one case passed: "*.conf: triggers trigger as expected" - if one case failed "*.conf: triggers did not trigger as expected: $expected_num != $actual_got_num" you can refer to the "*.log" file in the specific subdirectory for the log saved by mcelog. 2. Modify or add new test cases ******************************* If you want to modify the existing test cases or add your own case, the following description will have a more detailed look which might help: - To add or run a page level PFA test, you need first get a configure file in mcelog/tests/pfa/ directory defining mainly the threshold and trigger actions you want, then the number of trigger events you expect to happen. - A typical configure file is as following: mcelog/tests/pfa/page-account.conf ---------------------------------------------------------- # trigger: 5 # num-errors = 3 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = account [trigger] directory = . ------------------------------------------------------------ - “# trigger: 5” Specify the count number of triggers you expect to get based on the threshold defined in "memory-ce-threshold" described below. mcelog/tests/test harness in the end will compare this count number with the number of actual trigger events got from the log to verify the test results. please note the "#" is needed for mcelog/tests/test harness to read here. - "# num-errors = 3" "num-errors" is a mcelog configure option. if uncomment, it is used by mcelog to stop processing the stored machine check records in mcelog buffer read from /dev/mcelog and return(for debug purpose) when the number is reached even there might be: - still some unprocessed records left in the buffer which will be ignored - or there are not enough records in /dev/mcelog the program will not return. if not set as in this example, mcelog will return until finish processing all the records. When you are not sure what should be the correct num-errors number, it is not recommended to set this option. - "memory-ce-threshold = 2 / 1h" Define the threshold for memory corrected errors per page. Here means if there are 2 corrected errors detected in one page within 1 hour, the trigger defined in “memory-ce-trigger” described below will be called. - "memory-ce-trigger = ../trigger" Specify the trigger you want when exceeding the threshold. Here mcelog/tests/trigger will be called which simply print some text for testing. - "memory-ce-action = account" specify the internal action in mcelog to exceeding a memory corrected error threshold. This is done in addition to executing the trigger script if available. - off: No action - account: only account errors - soft: try to soft-offline page without killing any processes This requires an update kernel. Might not be successful - hard: try to hard-offline page without killing any processes This requires an update kernel. Might not be successful - soft-then-hard: First try to soft offline, then try hard offlining The offline action is based on the sysfs_wirte action of: /sys/devices/system/memory/soft_offline_page or /sys/devices/system/memory/hard_offline_page Please note that offlining does not work for all pages, but only for pages in the Linux page cache or free pages. And if offline action(soft, hard, or soft-then-hard)are chosen in "memory-ce-action", there will trigger only once for each page,no matter the offline action taken was successful or failed. 3. Influencing factors of the trigger results ******************************* The correct expectation of triggers depends on 4 factors: - The count number of trigger expectation defined in "pfa/*.conf" file As described above, in our example the trigger expectation are defined to be 5 times which means the "mcelog/tests/pfa/inject" script will randomly chosen 5 free pages to inject in turn and do the MCE injection on each page for $memory-ce-threshold times. - The threshold defined in “memory-ce-trigger” of "pfa/*.conf" file As described above, for “memory-ce-threshold = 2 / 1h” in our example, "mcelog/tests/pfa/inject" script will do the MCE injection 2 times continuously for each chosen page to make the trigger happen. - The “memory-ce-action” defined in "pfa/*.conf" file As described above. if the “memory-ce-action” is soft/hard/soft-then-hard, no matter offlining action succeed or not, triggers_per_page calculation will changed to be: triggers_per_page = INT(injections_per_page / memory-ce-threshold) >= 1? 1:0 - The actual number of records read out if "num-errors" defined in "pfa/*.conf" file As described above. mcelog will just read out $num-errors records, that means: readout_total_injections = MIN(num-errors, injection_per_page * actual-inject_pages) this might affect the trigger counts for some last injected pages since not all the machine check records from /dev/mcelog are processed and counted. mcelog-100/tests/pfa/inject0000775000175000017500000000336212247142742015367 0ustar vorlonvorlon#!/bin/sh PATH=$PATH:$(pwd)/../../../mce-inject page_type="slab buddy mmap anonymous nopage huge" function get_free_page() { local rand=0 cnt=`page-types -Nl -b $1 | tee page_$1 | wc -l` if [ $cnt -gt 1 ]; then rand=$(expr $RANDOM % $cnt + 1) if [ ${rand} -eq 1 ]; then # skip the title line of output ((rand++)) fi page=`awk -v line=${rand} 'NR == line {print $1}' page_$1` echo 0x${page} else echo 0 fi rm -f page_$1 } if [ "$1" = "" ]; then echo "usage $0 conf_file" exit 1 fi if [ ! -f $1 ]; then echo "configure file not exists: $1" exit 1 fi which page-types > /dev/null 2>&1 if [ $? -ne 0 ];then echo "please install page-types tool first" exit 1 fi echo "+++ start the injection for $1 +++" NUMT="$(awk '/# trigger: / { print $3}' $1)" THRESHOLD="$(awk '/memory-ce-threshold = / { print $3}' $1)" if [ "$NUMT" -eq 0 ]; then echo "No injection will be done!" exit 0 fi if [ "$THRESHOLD" -eq 0 ]; then echo "Threshold should not be 0!" exit 1 fi trigger_cnt=0 while [ "$trigger_cnt" -lt "$NUMT" ]; do for i in ${page_type}; do P=$(get_free_page $i) if [ "$P" = "0" ]; then continue fi if [ "$trigger_cnt" -ge "$NUMT" ]; then exit 0; fi inject_cnt=0 while [ "$inject_cnt" -lt "$THRESHOLD" ]; do echo "inject for page type $i at physical address ${P}000 [ NO. $inject_cnt ]" ../../input/GENPAGE $P | mce-inject inject_cnt=$(($inject_cnt+1)) done if [ "$inject_cnt" -eq "$THRESHOLD" ]; then trigger_cnt=$(($trigger_cnt+1)) fi done if [ "$trigger_cnt" -eq 0 ]; then echo "None available free pages found!" exit 1 fi done mcelog-100/tests/pfa/page-hard.conf0000664000175000017500000000031412247142742016656 0ustar vorlonvorlon# trigger: 1 #num-errors = 3 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = hard [trigger] directory = . mcelog-100/tests/pfa/page-account.conf0000664000175000017500000000031712247142742017377 0ustar vorlonvorlon# trigger: 1 #num-errors = 3 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = account [trigger] directory = . mcelog-100/tests/pfa/page-soft-then-hard.conf0000664000175000017500000000032612247142742020566 0ustar vorlonvorlon# trigger: 1 #num-errors = 3 [page] memory-ce-threshold = 1 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = soft-then-hard [trigger] directory = . mcelog-100/tests/cache/0000775000175000017500000000000012247142742014456 5ustar vorlonvorlonmcelog-100/tests/cache/inject0000775000175000017500000000031312247142742015655 0ustar vorlonvorlon#!/bin/sh PATH=$PATH:$(pwd)/../../../mce-inject ../../input/GENCACHE 1 1 data green | mce-inject ../../input/GENCACHE 1 1 data yellow | mce-inject ../../input/GENCACHE 1 2 generic yellow | mce-inject mcelog-100/tests/cache/cache.conf0000664000175000017500000000016512247142742016372 0ustar vorlonvorlon# trigger: 2 num-errors = 3 [cache] cache-threshold-trigger = ../trigger [trigger] directory = . children-max = 3 mcelog-100/tests/Makefile0000664000175000017500000000030612247142742015052 0ustar vorlonvorlon.PHONY: test clean DEBUG = test: ./test cache "${DEBUG}" ./test page "${DEBUG}" ./test memdb "${DEBUG}" ./test socket "${DEBUG}" ./test pfa "${DEBUG}" clean: rm -f */*log rm -f */results* mcelog-100/tests/trigger0000775000175000017500000000005612247142742015005 0ustar vorlonvorlon#!/bin/sh echo trigger env | egrep -v 'PATH' mcelog-100/tests/page/0000775000175000017500000000000012247142742014327 5ustar vorlonvorlonmcelog-100/tests/page/page-soft.conf0000664000175000017500000000031312247142742017060 0ustar vorlonvorlon# trigger: 1 num-errors = 6 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = soft [trigger] directory = . mcelog-100/tests/page/page-off.conf0000664000175000017500000000031212247142742016656 0ustar vorlonvorlon# trigger: 0 num-errors = 6 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = off [trigger] directory = . mcelog-100/tests/page/inject0000775000175000017500000000042212247142742015527 0ustar vorlonvorlon#!/bin/sh PATH=$PATH:$(pwd)/../../../mce-inject P=$RANDOM ../../input/GENPAGE $P | mce-inject ../../input/GENPAGE $P 0 1 1 5 | mce-inject ../../input/GENPAGE $P | mce-inject ../../input/GENPAGE | mce-inject ../../input/GENPAGE | mce-inject ../../input/GENPAGE | mce-inject mcelog-100/tests/page/page-hard.conf0000664000175000017500000000031312247142742017023 0ustar vorlonvorlon# trigger: 1 num-errors = 6 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = hard [trigger] directory = . mcelog-100/tests/page/page-memdb.conf0000664000175000017500000000057712247142742017205 0ustar vorlonvorlon# trigger: 7 cpu = nehalem dmi = yes num-errors = 6 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = soft [dimm] dimm-tracking-enabled = yes ce-error-trigger = ../trigger ce-error-threshold = 1 / 1min uc-error-trigger = ../trigger uc-error-threshold = 1 / 1min [trigger] directory = . mcelog-100/tests/page/page-account.conf0000664000175000017500000000031612247142742017544 0ustar vorlonvorlon# trigger: 1 num-errors = 6 [page] memory-ce-threshold = 2 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = account [trigger] directory = . mcelog-100/tests/page/page-soft-then-hard.conf0000664000175000017500000000032512247142742020733 0ustar vorlonvorlon# trigger: 4 num-errors = 6 [page] memory-ce-threshold = 1 / 1h memory-ce-trigger = ../trigger #memory-ce-action = off|account|soft|hard|soft-then-hard memory-ce-action = soft-then-hard [trigger] directory = . mcelog-100/tests/test0000775000175000017500000000275012247142742014324 0ustar vorlonvorlon#!/bin/sh # simple test harness for mcelog daemon trigger test cases # ./test subdir [debugger] # run mcelog test in specific sub directory # requires root rights and a built mce-inject in ../../mce-inject or $PATH # warning: this kills any other running mcelogs D=${2:-} if [ "$1" = "" ] ; then echo "usage $0 testdir" exit 1 fi if [ "$(whoami)" != "root" ] ; then echo "Must run as root" exit 1 fi echo "++++++++++++ running $1 test +++++++++++++++++++" # disable trigger echo -n "" > /sys/devices/system/machinecheck/machinecheck0/trigger killall mcelog || true #killwatchdog() { # kill %1 || true #} # #watchdog() { # sleep 10 # echo timeout waiting for mcelog # killall mcelog #} cd $1 #trap killwatchdog 0 #watchdog & rm -f *.log rm -f results for conf in `ls *.conf` do log=`echo $conf | sed "s/conf/log/g"` ./inject $conf $D ../../mcelog --foreground --daemon --debug-numerrors --config $conf --logfile $log >> result # let triggers finish sleep 1 NUMT="$(awk '/# trigger: / { print $3 }' $conf)" NUMC="$(grep -c 'Running trigger' $log || true)" if [ "$NUMT" != 0 ] ; then if [ "$NUMC" = 0 ] ; then echo "$conf: no triggers at all" >> results fi fi if [ "$NUMT" != "" ] ; then if [ "$NUMC" != "$NUMT" ] ; then echo "$conf: triggers did not trigger as expected: $NUMT != $NUMC" >> results else echo "$conf: triggers trigger as expected" >> results fi else echo "$conf: did not declare number of triggers" >> results fi done #trap "" 0 #killwatchdog mcelog-100/tests/memdb/0000775000175000017500000000000012247142742014477 5ustar vorlonvorlonmcelog-100/tests/memdb/inject0000775000175000017500000000032212247142742015676 0ustar vorlonvorlon#!/bin/sh B=$(pwd)/../.. PATH=$PATH:$B/../mce-inject $B/input/GENMEM 0 1 0 2 | mce-inject #$B/input/GENMEM 0 0 0 0 1 | mce-inject #$B/input/GENMEM 0 1 0 0 1 | mce-inject $B/input/GENMEM 0 2 0 3 | mce-inject mcelog-100/tests/memdb/memdb-1.conf0000664000175000017500000000040512247142742016567 0ustar vorlonvorlon# trigger: 2 cpu = nehalem dmi = yes filter-memory-errors = yes num-errors = 2 [dimm] dimm-tracking-enabled = yes ce-error-trigger = ../trigger ce-error-threshold = 1 / 1min uc-error-trigger = ../trigger uc-error-threshold = 1 / 1min [trigger] directory = . mcelog-100/tests/memdb/memdb-2.conf0000664000175000017500000000040712247142742016572 0ustar vorlonvorlon# trigger: 0 cpu = nehalem dmi = yes #filter-memory-errors = yes num-errors = 2 [dimm] dimm-tracking-enabled = yes ce-error-trigger = ../trigger ce-error-threshold = 2 / 1min uc-error-trigger = ../trigger uc-error-threshold = 1 / 1min [trigger] directory = . mcelog-100/tests/socket/0000775000175000017500000000000012247142742014703 5ustar vorlonvorlonmcelog-100/tests/socket/socket-memdb.conf0000664000175000017500000000067512247142742020134 0ustar vorlonvorlon# trigger: 4 cpu = nehalem dmi = yes num-errors = 2 [dimm] dimm-tracking-enabled = yes [socket] socket-tracking-enabled = yes mem-ce-error-trigger = ../trigger mem-ce-error-threshold = 1 / 1min mem-uc-error-trigger = ../trigger mem-uc-error-threshold = 1 / 1min [dimm] dimm-tracking-enabled = yes ce-error-trigger = ../trigger ce-error-threshold = 1 / 1min uc-error-trigger = ../trigger uc-error-threshold = 1 / 1min [trigger] directory = . mcelog-100/tests/socket/inject0000775000175000017500000000032212247142742016102 0ustar vorlonvorlon#!/bin/sh B=$(pwd)/../.. PATH=$PATH:$B/../mce-inject $B/input/GENMEM 0 1 0 2 | mce-inject #$B/input/GENMEM 0 0 0 0 1 | mce-inject #$B/input/GENMEM 0 1 0 0 1 | mce-inject $B/input/GENMEM 0 2 0 3 | mce-inject mcelog-100/tests/socket/socket-1.conf0000664000175000017500000000044212247142742017200 0ustar vorlonvorlon# trigger: 2 cpu = nehalem dmi = yes num-errors = 2 [dimm] dimm-tracking-enabled = yes [socket] socket-tracking-enabled = yes mem-ce-error-trigger = ../trigger mem-ce-error-threshold = 1 / 1min mem-uc-error-trigger = ../trigger mem-uc-error-threshold = 1 / 1min [trigger] directory = . mcelog-100/tests/socket/socket-2.conf0000664000175000017500000000044212247142742017201 0ustar vorlonvorlon# trigger: 1 cpu = nehalem dmi = yes num-errors = 2 [dimm] dimm-tracking-enabled = yes [socket] socket-tracking-enabled = yes mem-ce-error-trigger = ../trigger mem-ce-error-threshold = 2 / 1min mem-uc-error-trigger = ../trigger mem-uc-error-threshold = 1 / 1min [trigger] directory = . mcelog-100/mcelog.80000664000175000017500000002257012247142742013616 0ustar vorlonvorlon.\" disk db commented out for now because it's not usable enough .TH MCELOG 8 "May 2009" "" "Linux's Administrator's Manual" .SH NAME mcelog \- Decode kernel machine check log on x86 machines .SH SYNOPSIS mcelog [options] [device] .br mcelog [options] \-\-daemon .br mcelog [options] \-\-client .br mcelog [options] \-\-ascii .br .\"mcelog [options] \-\-drop-old-memory .\".br .\"mcelog [options] \-\-reset-memory locator .\".br .\"mcelog [options] \-\-dump-memory[=locator] .br mcelog \-\-version .SH DESCRIPTION X86 CPUs report errors detected by the CPU as .I machine check events (MCEs). These can be data corruption detected in the CPU caches, in main memory by an integrated memory controller, data transfer errors on the front side bus or CPU interconnect or other internal errors. Possible causes can be cosmic radiation, instable power supplies, cooling problems, broken hardware, or bad luck. Most errors can be corrected by the CPU by internal error correction mechanisms. Uncorrected errors cause machine check exceptions which may panic the machine. When a corrected error happens the x86 kernel writes a record describing the MCE into a internal ring buffer available through the .I /dev/mcelog device .I mcelog retrieves errors from .I /dev/mcelog, decodes them into a human readable format and prints them on the standard output or optionally into the system log. Optionally it can also take more options like keeping statistics or triggering shell scripts on specific events. The normal operating modi for mcelog are running as a regular cron job (traditional way, deprecated), running as a trigger directly executed by the kernel, or running as a daemon with the .I \-\-daemon option. When an uncorrected machine check error happens that the kernel cannot recover from then it will usually panic the system. In this case when there was a warm reset after the panic mcelog should pick up the machine check errors after reboot. This is not possible after a cold reset. In addition mcelog can be used on the command line to decode the kernel output for a fatal machine check panic in text format using the .I \-\-ascii option. This is typically used to decode the panic console output of a fatal machine check, if the system was power cycled or mcelog didn't run immediately after reboot. When the panic triggers a kdump kexec crash kernel the crash kernel boot up script should log the machine checks to disk, otherwise they might be lost. Note that after mcelog retrieves an error the kernel doesn't store it anymore (different from .I dmesg(1)), so the output should be always saved somewhere and mcelog not run in uncontrolled ways. .SH OPTIONS When the .B \-\-syslog option is specified redirect output to system log. The .B \-\-syslog-error option causes the normal machine checks to be logged as .I LOG_ERR (implies .I \-\-syslog ). Normally only fatal errors or high level remarks are logged with error level. High level one line summaries of specific errors are also logged to the syslog by default unless mcelog operates in .I \-\-ascii mode. When the .B \-\-logfile=file option is specified append log output to the specified file. With the .B \-\-no-syslog option mcelog will never log anything to the syslog. When the .B \-\-cpu=cputype option is specified set the to be decoded CPU to .I cputype. See .I mcelog \-\-help for a list of valid CPUs. Note that specifying an incorrect CPU can lead to incorrect decoding output. Default is either the CPU of the machine that reported the machine check (needs a newer kernel version) or the CPU of the machine mcelog is running on, so normally this option doesn't have to be used. Older versions of mcelog had separate options for different CPU types. These are still implemented, but deprecated and undocumented now. With the .B \-\-dmi option mcelog will look up the addresses reported in machine checks in the .I SMBIOS/DMI tables of the BIOS. This can sometimes tell you which DIMM or memory controller has developed a problem. More often the information reported by the BIOS is either subtly or obviously wrong or useless. This option requires that mcelog has read access to /dev/mem (normally requires root) and runs on the same machine in the same hardware configuration as when the machine check event happened. When .B \-\-ignorenodev is specified then mcelog will exit silently when the device cannot be opened. This is useful in virtualized environment with limited devices. When .B \-\-filter is specified .I mcelog will filter out known broken machine check events (default on). When the .B \-\-no-filter option is specified mcelog does not filter events. When .B \-\-raw is specified .I mcelog will not decode, but just dump the mcelog in a raw hex format. This can be useful for automatic post processing. When a device is specified the machine check logs are read from device instead of the default .I /dev/mcelog. With the .B \-\-ascii option mcelog decodes a fatal machine check panic generated by the kernel ("CPU n: Machine Check Exception ...") in ASCII from standard input and exits afterwards. Note that when the panic comes from a different machine than where mcelog is running on you might need to specify the correct cputype on older kernels. On newer kernels which output the .I PROCESSOR field this is not needed anymore. When the .B \-\-file filename option is specified .I mcelog \-\-ascii will read the ASCII machine check record from input file .I filename instead of standard input. With the .B \-\-config-file file option mcelog reads the specified config file. Default is .I /etc/mcelog/mcelog.conf See also .I CONFIG FILE below. With the .B \-\-daemon option mcelog will run in the background. This gives the fastest reaction time and is the recommended operating mode. This option implies .I \-\-logfile=/var/log/mcelog. Important messages will be logged as one-liner summaries to syslog unless .I \-\-no-syslog is given. The option .I \-\-foreground will prevent mcelog from giving up the terminal in daemon mode. This is intended for debugging. With the .B \-\-client option mcelog will query a running daemon for accumulated errors. With the .B \-\-cpumhz=mhz option assume the CPU has .I mhz frequency for decoding the time of the event using the CPU time stamp counter. This also forces decoding. Note this can be unreliable. on some systems with CPU frequency scaling or deep C states, where the CPU time stamp counter does not increase linearly. By default the frequency of the current CPU is used when mcelog determines it is safe to use. Newer kernels report the time directly in the event and don't need this anymore. The .B \-\-pidfile file option writes the process id of the daemon into file .I file. Only valid in daemon mode. Mcelog will enable extended error reporting from the memory controller on processors that support it unless you tell it not to with the .B \-\-no-imc-log option. You might need this option when decoding old logs from a system where this mode was not enabled. .\".B \-\-database filename .\"specifies the memory module error database file. Default is .\"/var/lib/memory-errors. It is only used together with DMI decoding. .\" .\" .\".B \-\-error\-trigger=cmd,thresh .\"When a memory module accumulates .\".I thresh .\"errors in the err database run command .\".I cmd. .\" .\".B \-\-drop-old-memory .\"Drop old DIMMs in the memory module database that are not plugged in .\"anymore. .\" .\".B \-\-reset\-memory=locator .\"When the DIMMs have suitable unique serial numbers mcelog .\"will automatically detect changed DIMMs. When the DIMMs don't .\"have those the user will have to use this option when changing .\"a DIMM to reset the error count in the error database. .\".I Locator .\"is the memory slot identifier printed on the motherboard. .\" .\".B \-\-dump-memory[=locator] .\"Dump error database information for memory module located .\"at .\".I locator. .\"When no locator is specified dump all. .B \-\-version displays the version of mcelog and exits. .SH CONFIG FILE mcelog supports a config file to set defaults. Command line options override the config file. By default the config file is read from .I /etc/mcelog/mcelog.conf unless overridden with the .I --config-file option. The general format is .I optionname = value White space is not allowed in value currently, except at the end where it is dropped Comments start with #. All command line options that are not commands can be specified in the config file. For example t to enable the .I --no-syslog option use .I no-syslog = yes (or no to disable). When the option has a argument use .I logfile = /tmp/logfile .SH NOTES The kernel prefers old messages over new. If the log buffer overflows only old ones will be kept. The exact output in the log file depends on the CPU, unless the --raw option is used. mcelog will report serious errors to the syslog during decoding. .SH SIGNALS When .I mcelog runs in daemon mode and receives a .I SIGUSR1 it will close and reopen the log files. This can be used to rotate logs without restarting the daemon. .SH FILES /dev/mcelog (char 10, minor 227) /etc/mcelog/mcelog.conf /var/log/mcelog /var/run/mcelog.pid .\"/var/lib/memory-errors .SH SEE ALSO AMD x86-64 architecture programmer's manual, Volume 2, System programming Intel 64 and IA32 Architectures Software Developer's manual, Volume 3, System programming guide Parts 1 and 2. Machine checks are described in Chapter 14 in Part1 and in Appendix E in Part2. Datasheet of your CPU. mcelog-100/k8.c0000664000175000017500000001571512247142742012750 0ustar vorlonvorlon/* Based on K8 decoding code written for the 2.4 kernel by Andi Kleen and * Eric Morton. Hacked and extended for mcelog by AK. * * Original copyright: * K8 parts Copyright 2002,2003 Andi Kleen, SuSE Labs. * Additional K8 decoding and simplification Copyright 2003 Eric Morton, Newisys Inc * K8 threshold counters decoding Copyright 2005,2006 Jacob Shin, AMD Inc. * * Subject to the GNU General Public License */ #include #include "mcelog.h" #include "k8.h" static char *k8bank[] = { "data cache", "instruction cache", "bus unit", "load/store unit", "northbridge", "fixed-issue reoder" }; static char *transaction[] = { "instruction", "data", "generic", "reserved" }; static char *cachelevel[] = { "0", "1", "2", "generic" }; static char *memtrans[] = { "generic error", "generic read", "generic write", "data read", "data write", "instruction fetch", "prefetch", "evict", "snoop", "?", "?", "?", "?", "?", "?", "?" }; static char *partproc[] = { "local node origin", "local node response", "local node observed", "generic participation" }; static char *timeout[] = { "request didn't time out", "request timed out" }; static char *memoryio[] = { "memory", "res.", "i/o", "generic" }; static char *nbextendederr[] = { "RAM ECC error", "CRC error", "Sync error", "Master abort", "Target abort", "GART error", "RMW error", "Watchdog error", "RAM Chipkill ECC error", "DEV Error", "Link Data Error", "Link Protocol Error", "NB Array Error", "DRAM Parity Error", "Link Retry", "Tablew Walk Data Error", "L3 Cache Data Error", "L3 Cache Tag Error", "L3 Cache LRU Error" }; static char *highbits[32] = { [31] = "valid", [30] = "error overflow (multiple errors)", [29] = "error uncorrected", [28] = "error enable", [27] = "misc error valid", [26] = "error address valid", [25] = "processor context corrupt", [24] = "res24", [23] = "res23", /* 22-15 ecc syndrome bits */ [14] = "corrected ecc error", [13] = "uncorrected ecc error", [12] = "res12", [11] = "L3 subcache in error bit 1", [10] = "L3 subcache in error bit 0", [9] = "sublink or DRAM channel", [8] = "error found by scrub", /* 7-4 ht link number of error */ [3] = "err cpu3", [2] = "err cpu2", [1] = "err cpu1", [0] = "err cpu0", }; static char *k8threshold[] = { [0 ... K8_MCELOG_THRESHOLD_DRAM_ECC - 1] = "Unknow threshold counter", [K8_MCELOG_THRESHOLD_DRAM_ECC] = "MC4_MISC0 DRAM threshold", [K8_MCELOG_THRESHOLD_LINK] = "MC4_MISC1 Link threshold", [K8_MCELOG_THRESHOLD_L3_CACHE] = "MC4_MISC2 L3 Cache threshold", [K8_MCELOG_THRESHOLD_FBDIMM] = "MC4_MISC3 FBDIMM threshold", [K8_MCELOG_THRESHOLD_FBDIMM + 1 ... K8_MCE_THRESHOLD_TOP - K8_MCE_THRESHOLD_BASE - 1] = "Unknown threshold counter", }; static void decode_k8_generic_errcode(u64 status) { unsigned short errcode = status & 0xffff; int i; for (i=0; i<32; i++) { if (i==31 || i==28 || i==26) continue; if (highbits[i] && (status & (1ULL<<(i+32)))) { Wprintf( " bit%d = %s\n", i+32, highbits[i]); } } if ((errcode & 0xFFF0) == 0x0010) { Wprintf( " TLB error '%s transaction, level %s'\n", transaction[(errcode >> 2) & 3], cachelevel[errcode & 3]); } else if ((errcode & 0xFF00) == 0x0100) { Wprintf( " memory/cache error '%s mem transaction, %s transaction, level %s'\n", memtrans[(errcode >> 4) & 0xf], transaction[(errcode >> 2) & 3], cachelevel[errcode & 3]); } else if ((errcode & 0xF800) == 0x0800) { Wprintf( " bus error '%s, %s\n %s mem transaction\n %s access, level %s'\n", partproc[(errcode >> 9) & 0x3], timeout[(errcode >> 8) & 1], memtrans[(errcode >> 4) & 0xf], memoryio[(errcode >> 2) & 0x3], cachelevel[(errcode & 0x3)]); } } static void decode_k8_dc_mc(u64 status, int *err) { unsigned short exterrcode = (status >> 16) & 0x0f; unsigned short errcode = status & 0xffff; if(status&(3ULL<<45)) { Wprintf( " Data cache ECC error (syndrome %x)", (u32) (status >> 47) & 0xff); if(status&(1ULL<<40)) { Wprintf(" found by scrubber"); } Wprintf("\n"); } if ((errcode & 0xFFF0) == 0x0010) { Wprintf( " TLB parity error in %s array\n", (exterrcode == 0) ? "physical" : "virtual"); } decode_k8_generic_errcode(status); } static void decode_k8_ic_mc(u64 status, int *err) { unsigned short exterrcode = (status >> 16) & 0x0f; unsigned short errcode = status & 0xffff; if(status&(3ULL<<45)) { Wprintf(" Instruction cache ECC error\n"); } if ((errcode & 0xFFF0) == 0x0010) { Wprintf(" TLB parity error in %s array\n", (exterrcode == 0) ? "physical" : "virtual"); } decode_k8_generic_errcode(status); } static void decode_k8_bu_mc(u64 status, int *err) { unsigned short exterrcode = (status >> 16) & 0x0f; if(status&(3ULL<<45)) { Wprintf(" L2 cache ECC error\n"); } Wprintf(" %s array error\n", (exterrcode == 0) ? "Bus or cache" : "Cache tag"); decode_k8_generic_errcode(status); } static void decode_k8_ls_mc(u64 status, int *err) { decode_k8_generic_errcode(status); } static void decode_k8_nb_mc(u64 status, int *memerr) { unsigned short exterrcode = (status >> 16) & 0x0f; Wprintf(" Northbridge %s\n", nbextendederr[exterrcode]); switch (exterrcode) { case 0: *memerr = 1; Wprintf(" ECC syndrome = %x\n", (u32) (status >> 47) & 0xff); break; case 8: *memerr = 1; Wprintf(" Chipkill ECC syndrome = %x\n", (u32) ((((status >> 24) & 0xff) << 8) | ((status >> 47) & 0xff))); break; case 1: case 2: case 3: case 4: case 6: Wprintf(" link number = %x\n", (u32) (status >> 36) & 0xf); break; } decode_k8_generic_errcode(status); } static void decode_k8_fr_mc(u64 status, int *err) { decode_k8_generic_errcode(status); } static void decode_k8_threshold(u64 misc) { if (misc & MCI_THRESHOLD_OVER) Wprintf(" Threshold error count overflow\n"); } typedef void (*decoder_t)(u64, int *ismemerr); static decoder_t decoders[] = { [0] = decode_k8_dc_mc, [1] = decode_k8_ic_mc, [2] = decode_k8_bu_mc, [3] = decode_k8_ls_mc, [4] = decode_k8_nb_mc, [5] = decode_k8_fr_mc, }; void decode_k8_mc(struct mce *mce, int *ismemerr) { if (mce->bank < NELE(decoders)) decoders[mce->bank](mce->status, ismemerr); else if (mce->bank >= K8_MCE_THRESHOLD_BASE && mce->bank < K8_MCE_THRESHOLD_TOP) decode_k8_threshold(mce->misc); else Wprintf(" no decoder for unknown bank %u\n", mce->bank); } char *k8_bank_name(unsigned num) { static char buf[64]; char *s = "unknown"; if (num < NELE(k8bank)) s = k8bank[num]; else if (num >= K8_MCE_THRESHOLD_BASE && num < K8_MCE_THRESHOLD_TOP) s = k8threshold[num - K8_MCE_THRESHOLD_BASE]; buf[sizeof(buf)-1] = 0; snprintf(buf, sizeof(buf) - 1, "%u %s", num, s); return buf; } int mce_filter_k8(struct mce *m) { /* Filter out GART errors */ if (m->bank == 4) { unsigned short exterrcode = (m->status >> 16) & 0x0f; if (exterrcode == 5 && (m->status & (1ULL<<61))) return 0; } return 1; } mcelog-100/ivy-bridge.h0000664000175000017500000000021412247142742014460 0ustar vorlonvorlonvoid ivb_decode_model(int cputype, int bank, u64 status, u64 misc); void ivy_bridge_ep_memerr_misc(struct mce *m, int *channel, int *dimm); mcelog-100/core2.c0000664000175000017500000000571012247142742013432 0ustar vorlonvorlon#include #include #include #include "mcelog.h" #include "core2.h" #include "bitfield.h" /* Decode P6 family (Core2) model specific errors. The generic errors are decoded in p4.c */ /* [19..24] */ static char *bus_queue_req_type[] = { [0] = "BQ_DCU_READ_TYPE", [2] = "BQ_IFU_DEMAND_TYPE", [3] = "BQ_IFU_DEMAND_NC_TYPE", [4] = "BQ_DCU_RFO_TYPE", [5] = "BQ_DCU_RFO_LOCK_TYPE", [6] = "BQ_DCU_ITOM_TYPE", [8] = "BQ_DCU_WB_TYPE", [10] = "BC_DCU_WCEVICT_TYPE", [11] = "BQ_DCU_WCLINE_TYPE", [12] = "BQ_DCU_BTM_TYPE", [13] = "BQ_DCU_INTACK_TYPE", [14] = "BQ_DCU_INVALL2_TYPE", [15] = "BQ_DCU_FLUSHL2_TYPE", [16] = "BQ_DCU_PART_RD_TYPE", [18] = "BQ_DCU_PART_WR_TYPE", [20] = "BQ_DCU_SPEC_CYC_TYPE", [24] = "BQ_DCU_IO_RD_TYPE", [25] = "BQ_DCU_IO_WR_TYPE", [28] = "BQ_DCU_LOCK_RD_TYPE", [30] = "BQ_DCU_SPLOCK_RD_TYPE", [29] = "BQ_DCU_LOCK_WR_TYPE", }; /* [25..27] */ static char *bus_queue_error_type[] = { [0] = "BQ_ERR_HARD_TYPE", [1] = "BQ_ERR_DOUBLE_TYPE", [2] = "BQ_ERR_AERR2_TYPE", [4] = "BQ_ERR_SINGLE_TYPE", [5] = "BQ_ERR_AERR1_TYPE", }; static struct field p6_shared_status[] = { FIELD(16, reserved_3bits), FIELD(19, bus_queue_req_type), FIELD(25, bus_queue_error_type), FIELD(25, bus_queue_error_type), SBITFIELD(30, "internal BINIT"), SBITFIELD(36, "received parity error on response transaction"), SBITFIELD(38, "timeout BINIT (ROB timeout)." " No micro-instruction retired for some time"), FIELD(39, reserved_3bits), SBITFIELD(42, "bus transaction received hard error response"), SBITFIELD(43, "failure that caused IERR"), /* The following are reserved for Core in the SDM. Let's keep them here anyways*/ SBITFIELD(44, "two failing bus transactions with address parity error (AERR)"), SBITFIELD(45, "uncorrectable ECC error"), SBITFIELD(46, "correctable ECC error"), /* [47..54]: ECC syndrome */ FIELD(55, reserved_2bits), {}, }; static struct field p6old_status[] = { SBITFIELD(28, "FRC error"), SBITFIELD(29, "BERR on this CPU"), FIELD(31, reserved_1bit), FIELD(32, reserved_3bits), SBITFIELD(35, "BINIT received from external bus"), SBITFIELD(37, "Received hard error reponse on split transaction (Bus BINIT)"), {} }; static struct field core2_status[] = { SBITFIELD(28, "MCE driven"), SBITFIELD(29, "MCE is observed"), SBITFIELD(31, "BINIT observed"), FIELD(32, reserved_2bits), SBITFIELD(34, "PIC or FSB data parity error"), FIELD(35, reserved_1bit), SBITFIELD(37, "FSB address parity error detected"), {} }; static struct numfield p6old_status_numbers[] = { HEXNUMBER(47, 54, "ECC syndrome"), {} }; void core2_decode_model(u64 status) { decode_bitfield(status, p6_shared_status); decode_bitfield(status, core2_status); /* Normally reserved, but let's parse anyways: */ decode_numfield(status, p6old_status_numbers); } void p6old_decode_model(u64 status) { decode_bitfield(status, p6_shared_status); decode_bitfield(status, p6old_status); decode_numfield(status, p6old_status_numbers); } mcelog-100/version.h0000664000175000017500000000004112247142742014102 0ustar vorlonvorlon#define MCELOG_VERSION "1.0pre" mcelog-100/memdb.c0000664000175000017500000002535212247142742013510 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Simple in memory error database for mcelog running in daemon mode mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include #include "mcelog.h" #include "memutil.h" #include "config.h" #include "dmi.h" #include "memdb.h" #include "leaky-bucket.h" #include "trigger.h" #include "intel.h" #include "page.h" struct memdimm { struct memdimm *next; int channel; /* -1: unknown */ int dimm; /* -1: unknown */ int socketid; struct err_type ce; struct err_type uc; char *name; char *location; struct dmi_memdev *memdev; }; struct err_triggers { struct bucket_conf ce_bucket_conf; struct bucket_conf uc_bucket_conf; char *type; }; #define SHASH 17 static int md_numdimms; static struct memdimm *md_dimms[SHASH]; static struct err_triggers dimms = { .type = "DIMM" }; static struct err_triggers sockets = { .type = "Socket" }; static int memdb_enabled; static int sockdb_enabled; #define FNV32_OFFSET 2166136261U #define FNV32_PRIME 0x01000193 #define O(x) ((x) & 0xff) /* FNV 1a 32bit, max 16k sockets, 8bit dimm/channel */ static unsigned dimmhash(unsigned socket, int dimm, unsigned ch) { unsigned hash = FNV32_OFFSET; hash = (hash ^ O(socket)) * FNV32_PRIME; hash = (hash ^ O(socket >> 8)) * FNV32_PRIME; hash = (hash ^ O(dimm)) * FNV32_PRIME; hash = (hash ^ O(ch)) * FNV32_PRIME; return hash % SHASH; } /* Search DIMM in hash table */ struct memdimm *get_memdimm(int socketid, int channel, int dimm, int insert) { struct memdimm *md; unsigned h; h = dimmhash(socketid, dimm, channel); for (md = md_dimms[h]; md; md = md->next) { if (md->socketid == socketid && md->channel == channel && md->dimm == dimm) break; } if (md || !insert) return md; md = xalloc(sizeof(struct memdimm)); md->next = md_dimms[h]; md_dimms[h] = md; md->socketid = socketid; md->channel = channel; md->dimm = dimm; md_numdimms++; bucket_init(&md->ce.bucket); bucket_init(&md->uc.bucket); return md; } enum { NUMLEN = 30, MAX_ENV = 20, }; static char *number(char *buf, long num) { snprintf(buf, NUMLEN, "%ld", num); return buf; } static char *format_location(struct memdimm *md) { char numbuf[NUMLEN], numbuf2[NUMLEN]; char *location; asprintf(&location, "SOCKET:%d CHANNEL:%s DIMM:%s [%s%s%s]", md->socketid, md->channel == -1 ? "?" : number(numbuf, md->channel), md->dimm == -1 ? "?" : number(numbuf2, md->dimm), md->location ? md->location : "", md->location && md->name ? " " : "", md->name ? md->name : ""); return location; } /* Run a user defined trigger when a error threshold is crossed. */ void memdb_trigger(char *msg, struct memdimm *md, time_t t, struct err_type *et, struct bucket_conf *bc) { struct leaky_bucket *bucket = &et->bucket; char *env[MAX_ENV]; int ei = 0; int i; char *location = format_location(md); char *thresh = bucket_output(bc, bucket); char *out; asprintf(&out, "%s: %s", msg, thresh); if (bc->log) { Gprintf("%s\n", out); Gprintf("Location %s\n", location); } if (bc->trigger == NULL) goto out; asprintf(&env[ei++], "PATH=%s", getenv("PATH") ?: "/sbin:/usr/sbin:/bin:/usr/bin"); asprintf(&env[ei++], "THRESHOLD=%s", thresh); asprintf(&env[ei++], "TOTALCOUNT=%lu", et->count); asprintf(&env[ei++], "LOCATION=%s", location); if (md->location) asprintf(&env[ei++], "DMI_LOCATION=%s", md->location); if (md->name) asprintf(&env[ei++], "DMI_NAME=%s", md->name); if (md->dimm != -1) asprintf(&env[ei++], "DIMM=%d", md->dimm); if (md->channel != -1) asprintf(&env[ei++], "CHANNEL=%d", md->channel); asprintf(&env[ei++], "SOCKETID=%d", md->socketid); asprintf(&env[ei++], "CECOUNT=%lu", md->ce.count); asprintf(&env[ei++], "UCCOUNT=%lu", md->uc.count); if (t) asprintf(&env[ei++], "LASTEVENT=%lu", t); asprintf(&env[ei++], "AGETIME=%u", bc->agetime); // XXX human readable version of agetime asprintf(&env[ei++], "MESSAGE=%s", out); asprintf(&env[ei++], "THRESHOLD_COUNT=%d", bucket->count); env[ei] = NULL; assert(ei < MAX_ENV); run_trigger(bc->trigger, NULL, env); for (i = 0; i < ei; i++) free(env[i]); out: free(location); free(out); free(thresh); } /* * Lost some errors. Assume they were CE. Only works for the sockets because * we have no clues where they are. */ static void account_over(struct err_triggers *t, struct memdimm *md, struct mce *m, unsigned corr_err_cnt) { if (corr_err_cnt && --corr_err_cnt > 0) { md->ce.count += corr_err_cnt; if (__bucket_account(&t->ce_bucket_conf, &md->ce.bucket, corr_err_cnt, m->time)) { char *msg; asprintf(&msg, "Fallback %s memory error count %d exceeded threshold", t->type, corr_err_cnt); memdb_trigger(msg, md, 0, &md->ce, &t->ce_bucket_conf); free(msg); } } } static void account_memdb(struct err_triggers *t, struct memdimm *md, struct mce *m) { char *msg; asprintf(&msg, "%scorrected %s memory error count exceeded threshold", (m->status & MCI_STATUS_UC) ? "Un" : "", t->type); if (m->status & MCI_STATUS_UC) { md->uc.count++; if (__bucket_account(&t->uc_bucket_conf, &md->uc.bucket, 1, m->time)) memdb_trigger(msg, md, m->time, &md->uc, &t->uc_bucket_conf); } else { md->ce.count++; if (__bucket_account(&t->ce_bucket_conf, &md->ce.bucket, 1, m->time)) memdb_trigger(msg, md, m->time, &md->ce, &t->ce_bucket_conf); } free(msg); } /* * A memory error happened, record it in the memdb database and run * triggers if needed. * ch/dimm == -1: Unspecified DIMM on the channel */ void memory_error(struct mce *m, int ch, int dimm, unsigned corr_err_cnt, unsigned recordlen) { struct memdimm *md; if (recordlen < offsetof(struct mce, socketid)) { static int warned; if (!warned) { Eprintf("Cannot account memory errors because kernel does not report socketid"); warned = 1; } return; } if (memdb_enabled && (ch != -1 || dimm != -1)) { md = get_memdimm(m->socketid, ch, dimm, 1); account_memdb(&dimms, md, m); } if (sockdb_enabled) { md = get_memdimm(m->socketid, -1, -1, 1); account_over(&sockets, md, m, corr_err_cnt); account_memdb(&sockets, md, m); } } /* Compare two dimms for sorting. */ static int cmp_dimm(const void *a, const void *b) { const struct memdimm *ma = *(void **)a; const struct memdimm *mb = *(void **)b; if (ma->socketid != mb->socketid) return ma->socketid - mb->socketid; if (ma->channel != mb->channel) return ma->channel - mb->channel; return ma->dimm - mb->dimm; } /* Dump CE or UC errors */ static void dump_errtype(char *name, struct err_type *e, FILE *f, enum printflags flags, struct bucket_conf *bc) { int all = (flags & DUMP_ALL); char *s; if (e->count || e->bucket.count || all) fprintf(f, "%s:\n", name); if (e->count || all) { fprintf(f, "\t%lu total\n", e->count); } if (bc->capacity && (e->bucket.count || all)) { s = bucket_output(bc, &e->bucket); fprintf(f, "\t%s\n", s); free(s); } } static void dump_bios(struct memdimm *md, FILE *f) { int n = 0; if (md->name) n += fprintf(f, "DMI_NAME \"%s\"", md->name); if (md->location) { if (n > 0) fputc(' ', f); n += fprintf(f, "DMI_LOCATION \"%s\"", md->location); } if (n > 0) fputc('\n', f); } static void dump_dimm(struct memdimm *md, FILE *f, enum printflags flags) { if (md->ce.count + md->uc.count > 0 || (flags & DUMP_ALL)) { fprintf(f, "SOCKET %u", md->socketid); if (md->channel == -1) fprintf(f, " CHANNEL any"); else fprintf(f, " CHANNEL %d", md->channel); if (md->dimm == -1) fprintf(f, " DIMM any"); else fprintf(f, " DIMM %d", md->dimm); fputc('\n', f); if (flags & DUMP_BIOS) dump_bios(md, f); dump_errtype("corrected memory errors", &md->ce, f, flags, &dimms.ce_bucket_conf); dump_errtype("uncorrected memory errors", &md->uc, f, flags, &dimms.uc_bucket_conf); } } /* Sort and dump DIMMs */ void dump_memory_errors(FILE *f, enum printflags flags) { int i, k; struct memdimm *md, **da; da = xalloc(sizeof(void *) * md_numdimms); k = 0; for (i = 0; i < SHASH; i++) { for (md = md_dimms[i]; md; md = md->next) da[k++] = md; } qsort(da, md_numdimms, sizeof(void *), cmp_dimm); for (i = 0; i < md_numdimms; i++) { if (i > 0) fputc('\n', f); else fprintf(f, "Memory errors\n"); dump_dimm(da[i], f, flags); } free(da); } void memdb_config(void) { int n; n = config_bool("dimm", "dimm-tracking-enabled"); if (n < 0) memdb_enabled = memory_error_support; else memdb_enabled = n; config_trigger("dimm", "ce-error", &dimms.ce_bucket_conf); config_trigger("dimm", "uc-error", &dimms.uc_bucket_conf); n = config_bool("socket", "socket-tracking-enabled"); if (n < 0) sockdb_enabled = memory_error_support; else sockdb_enabled = n; config_trigger("socket", "mem-ce-error", &sockets.ce_bucket_conf); config_trigger("socket", "mem-uc-error", &sockets.uc_bucket_conf); } static int parse_dimm_addr(char *bl, unsigned *socketid, unsigned *channel, unsigned *dimm) { if (!bl) return 0; if (sscanf(bl + strcspn(bl, "_"), "_Node%u_Channel%u_Dimm%u", socketid, channel, dimm) == 3) return 1; if (sscanf(bl, "NODE %u CHANNEL %u DIMM %u", socketid, channel, dimm) == 3) return 1; /* Add more DMI formats here */ return 0; } /* Prepopulate DIMM database from BIOS information */ void prefill_memdb(void) { static int initialized; int i; int missed = 0; unsigned socketid, channel, dimm; if (initialized) return; memdb_config(); if (!memdb_enabled) return; initialized = 1; if (config_bool("dimm", "dmi-prepopulate") == 0) return; if (opendmi() < 0) return; for (i = 0; dmi_dimms[i]; i++) { struct memdimm *md; struct dmi_memdev *d = dmi_dimms[i]; char *bl; bl = dmi_getstring(&d->header, d->bank_locator); if (!parse_dimm_addr(bl, &socketid, &channel, &dimm)) { missed++; continue; } md = get_memdimm(socketid, channel, dimm, 1); if (md->memdev) { /* dups -- likely parse error */ missed++; continue; } md->memdev = d; md->location = xstrdup(bl); md->name = xstrdup(dmi_getstring(&d->header, d->device_locator)); } if (missed) { static int warned; if (!warned) { Eprintf("failed to prefill DIMM database from DMI data"); warned = 1; } } } mcelog-100/list.h0000664000175000017500000001420712247142742013401 0ustar vorlonvorlon/* Stripped down version of the Linux 2.6.30 list.h */ #include /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) /** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } /** * list_is_last - tests whether @list is the last entry in list @head * @list: the entry to test * @head: the head of the list */ static inline int list_is_last(const struct list_head *list, const struct list_head *head) { return list->next == head; } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(const struct list_head *head) { return head->next == head; } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /** * list_first_entry - get the first element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. * * Note, that list is expected to be not empty. */ #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); \ pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_prev_safe(pos, n, head) \ for (pos = (head)->prev, n = pos->prev; \ pos != (head); \ pos = n, n = pos->prev) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.prev, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) mcelog-100/server.c0000664000175000017500000001752212247142742013732 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Simple event-driven unix network server for client access. Process commands and buffer output. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "mcelog.h" #include "server.h" #include "eventloop.h" #include "config.h" #include "memdb.h" #include "memutil.h" #include "paths.h" #include "page.h" #define PAIR(x) x, sizeof(x)-1 struct clientcon { char *inbuf; /* 0 terminated */ char *inptr; char *outbuf; size_t outcur; size_t outlen; }; static char *client_path = SOCKET_PATH; static int initial_ping_timeout = 2; static struct config_cred acc = { .uid = 0, .gid = -1U }; static void free_outbuf(struct clientcon *cc) { free(cc->outbuf); cc->outbuf = NULL; cc->outcur = cc->outlen = 0; } static void free_inbuf(struct clientcon *cc) { free(cc->inbuf); cc->inbuf = NULL; cc->inptr = NULL; } static void free_cc(struct clientcon *cc) { free(cc->outbuf); free(cc->inbuf); free(cc); } static void sendstring(int fd, char *str) { send(fd, str, strlen(str), MSG_DONTWAIT|MSG_NOSIGNAL); } static void dispatch_dump(FILE *fh, char *s) { char *p; enum printflags printflags = 0; while ((p = strsep(&s, " ")) != NULL) { if (!strcmp(p, "dump")) ; else if (!strcmp(p, "bios")) printflags |= DUMP_BIOS; else if (!strcmp(p, "all")) printflags |= DUMP_ALL; else fprintf(fh, "Unknown dump parameter\n"); } dump_memory_errors(fh, printflags); fprintf(fh, "done\n"); } static void dispatch_pages(FILE *fh) { dump_page_errors(fh); fprintf(fh, "done\n"); } static void dispatch_commands(char *line, FILE *fh) { char *s; while ((s = strsep(&line, "\n")) != NULL) { while (isspace(*s)) line++; if (!strncmp(s, "dump", 4)) dispatch_dump(fh, s); else if (!strncmp(s, "pages", 5)) dispatch_pages(fh); else if (!strcmp(s, "ping")) fprintf(fh, "pong\n"); else if (*s != 0) fprintf(fh, "Unknown command\n"); } } /* assumes commands don't cross records */ static void process_cmd(struct clientcon *cc) { FILE *fh; assert(cc->outbuf == NULL); fh = open_memstream(&cc->outbuf, &cc->outlen); if (!fh) Enomem(); cc->outcur = 0; dispatch_commands(cc->inbuf, fh); if (ferror(fh) || fclose(fh) != 0) Enomem(); } /* check if client is allowed to access */ static int access_check(int fd, struct msghdr *msg) { struct cmsghdr *cmsg; struct ucred *uc; /* check credentials */ cmsg = CMSG_FIRSTHDR(msg); if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDENTIALS) { Eprintf("Did not receive credentials over client unix socket %p\n", cmsg); return -1; } uc = (struct ucred *)CMSG_DATA(cmsg); if (uc->uid == 0 || (acc.uid != -1U && uc->uid == acc.uid) || (acc.gid != -1U && uc->gid == acc.gid)) return 0; Eprintf("rejected client access from pid:%u uid:%u gid:%u\n", uc->pid, uc->uid, uc->gid); sendstring(fd, "permission denied\n"); return -1; } /* retrieve commands from client */ static int client_input(int fd, struct clientcon *cc) { char ctlbuf[CMSG_SPACE(sizeof(struct ucred))]; struct iovec miov; struct msghdr msg = { .msg_iov = &miov, .msg_iovlen = 1, .msg_control = ctlbuf, .msg_controllen = sizeof(ctlbuf), }; int n, n2; assert(cc->inbuf == NULL); if (ioctl(fd, FIONREAD, &n) < 0) return -1; if (n == 0) return 0; cc->inbuf = xalloc_nonzero(n + 1); cc->inbuf[n] = 0; cc->inptr = cc->inbuf; miov.iov_base = cc->inbuf; miov.iov_len = n; n2 = recvmsg(fd, &msg, 0); if (n2 < n) return -1; return access_check(fd, &msg) == 0 ? n : -1; } /* process input/out on client socket */ static void client_event(struct pollfd *pfd, void *data) { int events = pfd->revents; struct clientcon *cc = (struct clientcon *)data; int n; if (events & ~(POLLIN|POLLOUT)) /* error/close */ goto error; if (events & POLLOUT) { if (cc->outcur < cc->outlen) { n = send(pfd->fd, cc->outbuf + cc->outcur, cc->outlen - cc->outcur, MSG_DONTWAIT|MSG_NOSIGNAL); if (n < 0) { /* EAGAIN here? but should not happen */ goto error; } cc->outcur += n; } if (cc->outcur == cc->outlen) free_outbuf(cc); } if (events & POLLIN) { n = client_input(pfd->fd, cc); if (n < 0) goto error; process_cmd(cc); free_inbuf(cc); } pfd->events = cc->outbuf ? POLLOUT : POLLIN; return; error: if (pfd->revents & POLLERR) SYSERRprintf("error while reading from client"); close(pfd->fd); unregister_pollcb(pfd); free_cc(cc); } /* accept a new client */ static void client_accept(struct pollfd *pfd, void *data) { struct clientcon *cc = NULL; int nfd = accept(pfd->fd, NULL, 0); int on; if (nfd < 0) { SYSERRprintf("accept failed on client socket"); return; } on = 1; if (setsockopt(nfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { SYSERRprintf("Cannot enable credentials passing on client socket"); goto cleanup; } cc = xalloc(sizeof(struct clientcon)); if (register_pollcb(nfd, POLLIN, client_event, cc) < 0) { sendstring(nfd, "mcelog server too busy\n"); goto cleanup; } return; cleanup: free(cc); close(nfd); } static void server_config(void) { char *s; long v; config_cred("server", "client", &acc); if ((s = config_string("server", "socket-path")) != NULL) client_path = s; if (config_number("server", "initial-ping-timeout", "%u", &v) == 0) initial_ping_timeout = v; } static sigjmp_buf ping_timeout_ctx; static void ping_timeout(int sig) { siglongjmp(ping_timeout_ctx, 1); } /* server still running? */ static int server_ping(struct sockaddr_un *un) { struct sigaction oldsa; struct sigaction sa = { .sa_handler = ping_timeout }; int ret = -1, n; char buf[10]; int fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) return 0; sigaction(SIGALRM, &sa, &oldsa); if (sigsetjmp(ping_timeout_ctx, 1) == 0) { alarm(initial_ping_timeout); if (connect(fd, un, sizeof(struct sockaddr_un)) < 0) goto cleanup; if (write(fd, PAIR("ping\n")) < 0) goto cleanup; if ((n = read(fd, buf, 10)) < 0) goto cleanup; if (n == 5 && !memcmp(buf, "pong\n", 5)) ret = 0; } cleanup: sigaction(SIGALRM, &oldsa, NULL); alarm(0); close(fd); return ret; } void server_setup(void) { int fd; struct sockaddr_un adr; server_config(); if (client_path[0] == 0) return; if (strlen(client_path) >= sizeof(adr.sun_path) - 1) { Eprintf("Client socket path `%s' too long for unix socket", client_path); return; } memset(&adr, 0, sizeof(struct sockaddr_un)); adr.sun_family = AF_UNIX; strncpy(adr.sun_path, client_path, sizeof(adr.sun_path) - 1); if (access(client_path, F_OK) == 0) { if (server_ping(&adr) == 0) { Eprintf("mcelog server already running\n"); exit(1); } unlink(client_path); } fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { SYSERRprintf("cannot open listening socket"); return; } if (bind(fd, (struct sockaddr *)&adr, sizeof(struct sockaddr_un)) < 0) { SYSERRprintf("Cannot bind to client unix socket `%s'", client_path); goto cleanup; } listen(fd, 10); register_pollcb(fd, POLLIN, client_accept, NULL); return; cleanup: close(fd); exit(1); } mcelog-100/tsc.c0000664000175000017500000001031212247142742013203 0ustar vorlonvorlon/* Copyright (C) 2006 Andi Kleen, SuSE Labs. Decode TSC value into human readable uptime mcelog 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; version 2. dmi 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include "mcelog.h" #include "tsc.h" #include "intel.h" static unsigned scale(u64 *tsc, unsigned unit, double mhz) { u64 v = (u64)(mhz * 1000000) * unit; unsigned u = *tsc / v; *tsc -= u * v; return u; } static int fmt_tsc(char **buf, u64 tsc, double mhz) { unsigned days, hours, mins, secs; if (mhz == 0.0) return -1; days = scale(&tsc, 3600 * 24, mhz); hours = scale(&tsc, 3600, mhz); mins = scale(&tsc, 60, mhz); secs = scale(&tsc, 1, mhz); asprintf(buf, "[at %.0f Mhz %u days %u:%u:%u uptime (unreliable)]", mhz, days, hours, mins, secs); return 0; } static double cpufreq_mhz(int cpu, double infomhz) { double mhz; FILE *f; char *fn; asprintf(&fn, "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu); f = fopen(fn, "r"); free(fn); if (!f) { /* /sys exists, but no cpufreq -- use value from cpuinfo */ if (access("/sys/devices", F_OK) == 0) return infomhz; /* /sys not mounted. We don't know if cpufreq is active or not, so must fallback */ return 0.0; } if (fscanf(f, "%lf", &mhz) != 1) mhz = 0.0; mhz /= 1000; fclose(f); return mhz; } int decode_tsc_forced(char **buf, double mhz, u64 tsc) { return fmt_tsc(buf, tsc, mhz); } static int deep_sleep_states(int cpu) { int ret; char *fn; FILE *f; char *line = NULL; size_t linelen = 0; /* When cpuidle is active assume there are deep sleep states */ asprintf(&fn, "/sys/devices/system/cpu/cpu%d/cpuidle", cpu); ret = access(fn, X_OK); free(fn); if (ret == 0) return 1; asprintf(&fn, "/proc/acpi/processor/CPU%d/power", cpu); f = fopen(fn, "r"); free(fn); if (!f) return 0; while ((getline(&line, &linelen, f)) > 0) { int n; if ((sscanf(line, " C%d:", &n)) == 1) { if (n > 1) { char *p = strstr(line, "usage"); if (p && sscanf(p, "usage[%d]", &n) == 1 && n > 0) return 1; } } } free(line); fclose(f); return 0; } /* Try to figure out if this CPU has a somewhat reliable TSC clock */ static int tsc_reliable(int cputype, int cpunum) { if (!processor_flags) return 0; /* Trust the kernel */ if (strstr(processor_flags, "nonstop_tsc")) return 1; /* TSC does not change frequency TBD: really old kernels don't set that */ if (!strstr(processor_flags, "constant_tsc")) return 0; /* We don't know the frequency on non Intel CPUs because the kernel doesn't report them (e.g. AMD GH TSC doesn't run at highest P-state). But then the kernel can just report the real time too. Also a lot of AMD and VIA CPUs have unreliable TSC, so would need special rules here too. */ if (!is_intel_cpu(cputype)) return 0; if (deep_sleep_states(cpunum) && cputype != CPU_NEHALEM) return 0; return 1; } int decode_tsc_current(char **buf, int cpunum, enum cputype cputype, double mhz, unsigned long long tsc) { double cmhz; if (!tsc_reliable(cputype, cpunum)) return -1; cmhz = cpufreq_mhz(cpunum, mhz); if (cmhz != 0.0) mhz = cmhz; return fmt_tsc(buf, tsc, mhz); } #ifdef STANDALONE int is_intel_cpu(int cpu) { return 1; } /* claim this TSC is reliable always */ char *processor_flags = "nonstop_tsc"; static inline u64 rdtscll(void) { unsigned a,b; asm volatile("rdtsc" : "=a" (a), "=d" (b)); return (u64)a | (((u64)b) << 32); } int main(void) { char *buf; u64 tsc = rdtscll(); printf("%Lx tsc\n", tsc); if (decode_tsc_current(&buf, 0, CPU_CORE2, 0.0, tsc) >= 0) printf("%s\n", buf); else printf("failed\n"); return 0; } #endif mcelog-100/diskdb.c0000664000175000017500000000344712247142742013665 0ustar vorlonvorlon/* High level interface to disk based DIMM database */ /* Note: obsolete: new design is in memdb.c */ #include #include #include #include "mcelog.h" #include "diskdb.h" #include "paths.h" #include "dimm.h" #include "dmi.h" char *error_trigger; unsigned error_thresh = 20; char *dimm_db_fn = DIMM_DB_FILENAME; static void checkdimmdb(void) { if (open_dimm_db(dimm_db_fn) < 0) exit(1); } int diskdb_modifier(int opt) { char *end; switch (opt) { case O_DATABASE: dimm_db_fn = optarg; checkdmi(); checkdimmdb(); break; case O_ERROR_TRIGGER: checkdmi(); open_dimm_db(dimm_db_fn); error_thresh = strtoul(optarg, &end, 0); if (end == optarg || *end != ',') usage(); error_trigger = end + 1; break; default: return 0; } return 1; } void diskdb_resolve_addr(u64 addr) { if (open_dimm_db(dimm_db_fn) >= 0) new_error(addr, error_thresh, error_trigger); } void diskdb_usage(void) { fprintf(stderr, "Manage disk DIMM error database\n" " mcelog [options] --drop-old-memory|--reset-memory locator\n" " mcelog --dump-memory locator\n" " old can be either locator or name\n" "Disk database options:" "--database fn Set filename of DIMM database (default " DIMM_DB_FILENAME ")\n" "--error-trigger cmd,thresh Run cmd on exceeding thresh errors per DIMM\n"); } static void dimm_common(int ac, char **av) { no_syslog(); checkdmi(); checkdimmdb(); argsleft(ac, av); } int diskdb_cmd(int opt, int ac, char **av) { char *arg = optarg; switch (opt) { case O_DUMP_MEMORY: dimm_common(ac, av); if (arg) dump_dimm(arg); else dump_all_dimms(); return 1; case O_RESET_MEMORY: dimm_common(ac, av); reset_dimm(arg); return 1; case O_DROP_OLD_MEMORY: dimm_common(ac, av); gc_dimms(); return 1; } return 0; } mcelog-100/input/0000775000175000017500000000000012247142742013410 5ustar vorlonvorlonmcelog-100/input/dimm00000664000175000017500000000014012247142742014334 0ustar vorlonvorlon# dimm0, channel0 corrected error CPU 0 2 PROCESSOR 0:0x106a0 STATUS 0x8800000000000080 MISC 0 mcelog-100/input/xen0000664000175000017500000000021212247142742014120 0ustar vorlonvorlon(XEN) MCE: The hardware reports a non fatal, correctable incident occurred on CPU 1. (XEN) Bank 2: d400008000040150 at 182c480179cf0 mcelog-100/input/GENCACHE0000775000175000017500000000210212247142742014466 0ustar vorlonvorlon#!/bin/sh # GENCACHE cpu level type track # generate a memory error. All fields are optional. # see SDM 3a chapter 15 for details # # level: # 0 level 0 # 1 level 1 # 2 level 2 # 3 level generic # # ctype # 0 instruction # 1 data # 2 generic # # track # 0 no tracking # 1 green: below threshold # 2 yellow: above threshold # 3 reserved cpu=${1:-0} level=${2:-0} ctype=${3:-0} track=${4:-1} case "$ctype" in instr) ctype=0 ;; data) ctype=1 ;; generic) ctype=2 ;; [0-3]) ;; *) echo "Unknown ctype $ctype" ; exit 1 esac case "$level" in L0) level=0 ;; L1) level=1 ;; L2) level=2 ;; LG) level=3 ;; [0-3]) ;; *) echo "Unknown Cache $level" ; exit 1 esac case "$track" in none) track=0 ;; green) track=1 ;; yellow) track=2 ;; [0-3]) ;; *) echo "Unknown tracking flag $track" ; exit 1 esac echo "# cache error on cpu $cpu level $level type $ctype track $track" echo "CPU $cpu 2" # XXX use cpu in socket echo "# nehalem" echo "PROCESSOR 0:0x106a0" printf "MCGCAP 0x%x\n" $[1 << 11] printf "STATUS 0x%08x%08x\n" \ $[0x88000000 + ($track << (53-32))] $[0x100 + $level + ($ctype << 2)] mcelog-100/input/GENMEM0000775000175000017500000000152312247142742014307 0ustar vorlonvorlon#!/bin/sh # GENMEM socketid channel dimm corr-err-cnt uc-flag # generate a memory error. All fields are optional. # suitable to be fed into mce-inject or mcelog --ascii # Note: DIMMs only work when mcelog is in Nehalem mode # this cannot be forced through mce-inject, but only on the command line # setting ucflag and injecting can panic your system socketid=${1:-0} channel=${2:-0} dimm=${3:-0} corr_err_cnt=${4:-0} if [ ! -z "$5" ] ; then ucflag=$[1 << (61-32)] else ucflag=0 fi echo "# memory error on socket $socketid ch $channel dimm $dimm" echo "CPU 0 2" # XXX use cpu in socket echo "# nehalem" echo "PROCESSOR 0:0x106a0" printf "MCGCAP 0x%x\n" $[1 << 10] echo "SOCKETID $socketid" printf "STATUS 0x%08x%08x\n" \ $[0x88000000 + ($corr_err_cnt << 6) + $ucflag] $[0xb0 + $channel] printf "MISC 0x%08x\n" $[($channel << 18) + ($dimm << 16)] mcelog-100/input/GENPAGE0000775000175000017500000000154312247142742014407 0ustar vorlonvorlon#!/bin/sh # GENMPAGE pfn socketid channel dimm corr-err-cnt # generate a memory error on a page. All fields are optional. # dimm/channel can be out of sync with the address # XXX page max 44bit for now # suitable to be fed into mce-inject or mcelog --ascii # Note: DIMMs only work when mcelog is in Nehalem mode # this cannot be forced through mce-inject, but only on the command line page=${1:-$RANDOM} socketid=${2:-0} channel=${3:-0} dimm=${4:-0} corr_err_cnt=${5:-0} printf "# memory error on page %08x000 socket $socketid ch $channel dimm $dimm \n" $page echo "CPU 0 2" # XXX use cpu in socket echo "# nehalem" echo "PROCESSOR 0:0x106a0" echo "SOCKETID $socketid" printf "STATUS 0x%08x%08x\n" \ $[0x88000000 + (1 << (58-32)) + ($corr_err_cnt << 6)] $[0xb0 + $channel] printf "MISC 0x%08x\n" $[($channel << 18) + ($dimm << 16)] printf "ADDR 0x%08x000\n" $page mcelog-100/input/full10000664000175000017500000000016712247142742014362 0ustar vorlonvorlonCPU 0 BANK 1 STATUS 1234 TSC 3062652eaab RIP 3f:<5678> PROCESSOR 0:6f5 TIME 123456789 SOCKETID 1 MCGCAP 0xabc APICID f mcelog-100/input/simple10000664000175000017500000000010212247142742014676 0ustar vorlonvorlonCPU 0 2 STATUS 1234 TSC 3062652eaab RIP 3f:<5678> MCGSTATUS 0x123 mcelog-100/nehalem.h0000664000175000017500000000033112247142742014030 0ustar vorlonvorlonvoid nehalem_decode_model(u64 status, u64 misc); void xeon75xx_decode_model(struct mce *m, unsigned msize); void decode_memory_controller(u32 status); void nehalem_memerr_misc(struct mce *m, int *channel, int *dimm); mcelog-100/leaky-bucket.c0000664000175000017500000001217712247142742015005 0ustar vorlonvorlon/* Copyright (C) 2009 Intel Corporation Author: Andi Kleen Leaky bucket algorithm. This is used for all error triggers. mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include "leaky-bucket.h" time_t __attribute__((weak)) bucket_time(void) { return time(NULL); } static void bucket_age(const struct bucket_conf *c, struct leaky_bucket *b, time_t now) { long diff; unsigned age; diff = now - b->tstamp; age = (diff / (double)c->agetime) * c->capacity; if (age > b->count) b->count = 0; else b->count -= age; } /* Account increase in leaky bucket. Return 1 if bucket overflowed. */ int __bucket_account(const struct bucket_conf *c, struct leaky_bucket *b, unsigned inc, time_t t) { if (c->capacity == 0) return 0; bucket_age(c, b, t); if (b->count < c->capacity) { b->count += inc; b->tstamp = t; return 1; } return 0; } int bucket_account(const struct bucket_conf *c, struct leaky_bucket *b, unsigned inc) { return __bucket_account(c, b, inc, bucket_time()); } static int timeconv(char unit, int *out) { unsigned corr = 1; switch (unit) { case 'd': corr *= 24; case 'h': corr *= 60; case 'm': corr *= 60; case 0: break; default: return -1; } *out = corr; return 0; } /* Format leaky bucket as a string. Caller must free string */ char *bucket_output(const struct bucket_conf *c, struct leaky_bucket *b) { char *buf; if (c->capacity == 0) { asprintf(&buf, "not enabled"); } else { int unit = 0; //bucket_age(c, b, bucket_time()); timeconv(c->tunit, &unit); asprintf(&buf, "%u in %u%c", b->count + b->excess, c->agetime/unit, c->tunit); } return buf; } /* Parse user specified capacity / rate string */ /* capacity / time time: number [hmds] capacity: number [kmg] */ static int parse_rate(const char *rate, struct bucket_conf *c) { char cunit[2], tunit[2]; unsigned cap, t; int n; int unit; cunit[0] = 0; tunit[0] = 0; n = sscanf(rate, "%u %1s / %u %1s", &cap, cunit, &t, tunit); if (n != 4) { cunit[0] = 0; tunit[0] = 0; if (n <= 2) { n = sscanf(rate, "%u / %u %1s", &cap, &t, tunit); if (n < 2) return -1; } else return -1; } if (t == 0 || cap == 0) return -1; switch (tolower(cunit[0])) { case 'g': cap *= 1000; case 'm': cap *= 1000; case 'k': cap *= 1000; case 0: break; default: return -1; } c->tunit = tolower(tunit[0]); if (timeconv(c->tunit, &unit) < 0) return -1; c->agetime = unit * t; c->capacity = cap; return 0; } /* Initialize leaky bucket conf for given user rate/capacity string. <0 on error */ int bucket_conf_init(struct bucket_conf *c, const char *rate) { if (parse_rate(rate, c) < 0) return -1; c->trigger = NULL; return 0; } /* Initialize leaky bucket instance. */ void bucket_init(struct leaky_bucket *b) { b->count = 0; b->excess = 0; b->tstamp = bucket_time(); } #ifdef TEST_LEAKY_BUCKET /* Stolen from the cpp documentation */ #define xstr(_s) str(_s) #define str(_s) #_s #define THRESHOLD_EVENTS_PER_PERIOD 100 #define EVENTS_PER_LOGGED_EVENT 10 #define SECONDS_PER_EVENT 86 /* Needs to be SECONDS_PER_EVENT * EVENTS_PER_LOGGED_EVENT * THRESHOLD_EVENTS_PER_PERIOD */ #define THRESHOLD_PERIOD 86000 #if THRESHOLD_PERIOD != (SECONDS_PER_EVENT * EVENTS_PER_LOGGED_EVENT * THRESHOLD_EVENTS_PER_PERIOD) # error THRESHOLD_PERIOD is Wrong! #endif #define RATE_STRING xstr(THRESHOLD_EVENTS_PER_PERIOD) " / " xstr(THRESHOLD_PERIOD) #define EVENTS_PER_PERIOD_IN_TEST (THRESHOLD_EVENTS_PER_PERIOD * EVENTS_PER_LOGGED_EVENT) #define PERIODS_TO_TEST 3 #define TOTAL_SECONDS_FOR_TEST (PERIODS_TO_TEST * THRESHOLD_PERIOD) #define TOTAL_EVENTS (PERIODS_TO_TEST * EVENTS_PER_PERIOD_IN_TEST) int main(int argc, char **argv) { struct bucket_conf c; struct leaky_bucket b; time_t start_time; time_t event_time; int ret; int i; #ifdef TEST_LEAKY_BUCKET_DEBUG printf("Testing with a rate of " RATE_STRING "\n"); #endif ret = bucket_conf_init(&c, RATE_STRING); if (ret) return ret; bucket_init(&b); start_time = b.tstamp; for (i = 1; i <= TOTAL_EVENTS; i++) { event_time = start_time + i * SECONDS_PER_EVENT; ret = __bucket_account(&c, &b, 1, event_time); #ifdef TEST_LEAKY_BUCKET_DEBUG if (ret) printf("Logging entry %d at %ld %ld\n", i, event_time - start_time, b.tstamp); #else if (i < THRESHOLD_EVENTS_PER_PERIOD) { if (!ret){ fprintf(stderr, "Did not log initial events - FAIL.\n"); return -1; } } else { if (!(i % EVENTS_PER_LOGGED_EVENT) && !ret) { fprintf(stderr, "Did not log initial events - FAIL.\n"); return -1; } } #endif } return 0; } #endif mcelog-100/.gitignore0000664000175000017500000000010112247142742014231 0ustar vorlonvorlon*.o *~ mcelog dbquery *.orig *.rej .gdb_history .depend tsc core mcelog-100/lk10-mcelog.pdf0000664000175000017500000035344612247142742014776 0ustar vorlonvorlon%PDF-1.4 % 5 0 obj << /S /GoTo /D (section.1) >> endobj 8 0 obj (Abstract) endobj 9 0 obj << /S /GoTo /D (section.2) >> endobj 12 0 obj (Introduction) endobj 13 0 obj << /S /GoTo /D (section.3) >> endobj 16 0 obj (Basic error architecture) endobj 17 0 obj << /S /GoTo /D (subsection.3.1) >> endobj 20 0 obj (EDAC) endobj 21 0 obj << /S /GoTo /D (section.4) >> endobj 24 0 obj (Soft errors versus hard errors) endobj 25 0 obj << /S /GoTo /D (section.5) >> endobj 28 0 obj (Logging) endobj 29 0 obj << /S /GoTo /D (section.6) >> endobj 32 0 obj (Error accounting) endobj 33 0 obj << /S /GoTo /D (subsection.6.1) >> endobj 36 0 obj (mcelog client) endobj 37 0 obj << /S /GoTo /D (subsection.6.2) >> endobj 40 0 obj (mcelog triggers) endobj 41 0 obj << /S /GoTo /D (section.7) >> endobj 44 0 obj (Bad page offlining) endobj 45 0 obj << /S /GoTo /D (section.8) >> endobj 48 0 obj (Cache error handling) endobj 49 0 obj << /S /GoTo /D (section.9) >> endobj 52 0 obj (Deployment) endobj 53 0 obj << /S /GoTo /D (section.10) >> endobj 56 0 obj (Conclusion) endobj 57 0 obj << /S /GoTo /D [58 0 R /Fit ] >> endobj 66 0 obj << /Length 2391 /Filter /FlateDecode >> stream xڍXɒWF0[ VxA$,)js+D[s!YY[./3+7`O?tf~dx؄Igdagay7s[svwgcHX&OeWMw䯦2_Jo>w "(B1}. HynxZ;مʏ c_% ~~T`rhL']vQj2ˡphG["8I#f+? O0~[w `i.'Rs0_v wn`\ OeZmZ>ZSd8v d{Ӎewv@'.̺upq 0Z jF|ۂ7jޗ5(,sFqpT{vUYѹT5PyvB9 G`זe],v[\{Y$^~naz>2m*0qj8sٱWe&M-,X7`:cՊj| ~H7#-szT\xGPzS3d/H%&9AcWܔ*]A=!"hRzMq{v5}r@^ X!2\q`UrMEBO֖l^{*~^;خ%1&><<01?tgjOs_!Da52N\RZ=H>Oz#C.if)h,1dƩ`EQ#@S&+O '$~2J ,#cMۺՖc@˙=CL oxYs&!fg `1Şgp4ɼnԈJ`S6j ٱ~O'Z'58EGlRx/LC<=XU@AV5֐xJ^Sjpw!ߺ 1@1gGE`|M9AT9+NqGoaXg( %4*#xOd9 ? (:!X)*YyC+_EA56_(eZO!E~,,\>zdpN؂%b3W BD8ݪiWOݪԛ<h'z}X*ZTKۑwAgˤc2/v%*E,䔙_d "I U^xk'| ]*Z-I.5*ZtaJ].TBLnq"gպ<c^\.FJeX XU6v WRR"oo7f ;ai YCҭ=tƾ_9;mjjUʽf툳Iï6[ ؋Ѥm~\<`ǎcc~A:P#3:HIJ52/.ϻLO2*_^c:*[騦G\z&UCȨؗa@w=WQs|+-n"hP.@MXT'@Ck {PzJL,SgZ|f|Ѥđc-Ho\rsJ^ϚYAz_SUCP|uoX{hǻB1Wy O ?6`S8ɝ7!-.h73sg)/;nD~Z,1rHӄKvdG[#>ԅKtze(`H93c 9e%<ϗx˒ O%B.`h`Yn2qUr7<g+>=|q(hF:f'cߝG )0|"O{Φ $ i"h{zKɼVjY ."52?5Iy4Vq1:fLk3;iǁAӯ "_cj,ZMJ%uw98Q׷r 0 endstream endobj 58 0 obj << /Type /Page /Contents 66 0 R /Resources 65 0 R /MediaBox [0 0 595.276 841.89] /Parent 74 0 R /Annots [ 59 0 R 60 0 R 61 0 R 62 0 R 63 0 R ] >> endobj 59 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [511.627 315.713 519.074 325.211] /Subtype /Link /A << /S /GoTo /D (cite.xeon-ras) >> >> endobj 60 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [338.751 245.715 345.227 259.431] /Subtype /Link /A << /S /GoTo /D (Hfootnote.1) >> >> endobj 61 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [135.575 177.969 142.051 191.685] /Subtype /Link /A << /S /GoTo /D (Hfootnote.2) >> >> endobj 62 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [365.057 166.781 372.504 176.17] /Subtype /Link /A << /S /GoTo /D (cite.kleen2004) >> >> endobj 63 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [129.247 153.123 136.694 162.621] /Subtype /Link /A << /S /GoTo /D (cite.intel-sdm) >> >> endobj 67 0 obj << /D [58 0 R /XYZ 70.866 773.242 null] >> endobj 68 0 obj << /D [58 0 R /XYZ 70.866 773.242 null] >> endobj 6 0 obj << /D [58 0 R /XYZ 70.866 626.862 null] >> endobj 10 0 obj << /D [58 0 R /XYZ 70.866 539.127 null] >> endobj 14 0 obj << /D [58 0 R /XYZ 70.866 384.328 null] >> endobj 72 0 obj << /D [58 0 R /XYZ 87.006 149.849 null] >> endobj 73 0 obj << /D [58 0 R /XYZ 87.006 127.74 null] >> endobj 65 0 obj << /Font << /F56 69 0 R /F58 70 0 R /F59 71 0 R >> /ProcSet [ /PDF /Text ] >> endobj 83 0 obj << /Length 3834 /Filter /FlateDecode >> stream x[KFϯQSq޼rv]g&⌸K2I/6)iSDh4=E,z{VO?Yģ2M#UP27#04 e'ї&K ɴ#%H#0kiI$EPNr PF8oH#ԖE% mSgF,ٱ|Xw|6u xV]9 H!ZNV %=v E7ɦt͛{/vM@uJ7 MYPdA GC':F-\XR)h { !/@lHk IH60I_Ls»@ǃ"%&MH݅es\.޶CnE6mgײZ{|K9OΘ#:?`f}(2[R%Ub/Y EZh? IO.^xVH}+wa[lƳ+X-XG|K_od_ԹMMr0+B,Sn8IQbOh/d m&]i* ƌf": zfFwYQ-^43+I\"JX,nòX1&8C]v渌ziVkogϼf]0궚H.csȃW|dѳ>IyJ (X\PmN.l*Hk"*QKt's309= qf@RXd)93ڕ.k,^ )- :JԴ,/$5ZtiFJd|h8δt&8hf,:t ^;Shg&VđSДCl/s%j5:e=ȯcs\ڕޗվjKվcC}˓S3NJwCmS{^$|Q\Qf 3sdٯbQ~rmT^:`qk/6&D g&5F]t5O\˸E+]Ј'Y]qrPW"VWr9&6+>=`M=O}՟5+1k2y.e3`j#~Їkd@Q7tC9FG2NmwCp TM[!ͣP;UC_*3ql 7ɌNcmVapԗO[մnaowywE0&ng߿{0Lol?TlvXt8{¯i.jik9ROGPbk@CL ILOޔZzKjǶ>6{}Kp=hoAކW*!<%9h zM롎M@">`Wk35F/ 9s*8cy(%d[g Q*lIloG'<NxVgWe2IќoJza&} PEbH9 F]b㛾NZܚ킕_с̔e6zˡi.DW\ڼmٵ{^٘b4AS=04v !=uY |v~ 5i z׶5z"-}0~3>`hğ~9.gO"(SWu;d~AJt/YоĚiQj)I7tmc*HZXԅ^ otxxrCq&I0j'52fDnʚ u+{4K4Ҏ[?[N %#cs^ؼ2c 3CR(?tszZټKhѣ#a߽]acԬ>fVQBp[&l0`amektx%!z$tP[S&J7tTwm`]:BfRD;QCck&xsO7cFB.Fσa Dj EU{ڙ* Y3Yn]Ȑ o}t 8܀UC+'cvLiJl97H Mf ɭz͔0=ahmXZsq* lH=l:Du,ڵdw7QMX ;Azn_6վW\j *ǾCa$1<& Ħ|4GC=ک9vќ-b~Ǣ;D^$gTayAHl9IIxJF3{ F;pU o1?.wL#3C'K5}e&+D} endstream endobj 82 0 obj << /Type /Page /Contents 83 0 R /Resources 81 0 R /MediaBox [0 0 595.276 841.89] /Parent 74 0 R /Annots [ 64 0 R 78 0 R 79 0 R 80 0 R ] >> endobj 64 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [407.725 398.431 415.172 408.059] /Subtype /Link /A << /S /GoTo /D (cite.EDAC) >> >> endobj 78 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [323.938 119.676 330.413 133.393] /Subtype /Link /A << /S /GoTo /D (Hfootnote.3) >> >> endobj 79 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [330.228 94.94 337.675 104.328] /Subtype /Link /A << /S /GoTo /D (cite.schroeder2009) >> >> endobj 80 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [364.155 94.94 371.602 104.328] /Subtype /Link /A << /S /GoTo /D (cite.li2009) >> >> endobj 84 0 obj << /D [82 0 R /XYZ 70.866 773.242 null] >> endobj 85 0 obj << /D [82 0 R /XYZ 267.674 466.931 null] >> endobj 18 0 obj << /D [82 0 R /XYZ 70.866 431.538 null] >> endobj 22 0 obj << /D [82 0 R /XYZ 70.866 339.585 null] >> endobj 86 0 obj << /D [82 0 R /XYZ 87.006 90.946 null] >> endobj 81 0 obj << /Font << /F56 69 0 R /F59 71 0 R >> /ProcSet [ /PDF /Text ] >> endobj 93 0 obj << /Length 4732 /Filter /FlateDecode >> stream x\K6`^oskk]5ĮZ%)izx(uGR"L$2,V=V ׯ߼n+W5Θ'MKK8<>n LYӲS7V-j R ]/x;S֧alv;i= xDf]?lw|dӭnXKW{:7-Uu߽ݱ۟VA0z}vOzi8jos D%Ӫ|XpV/`z!bfl10,DQmhVއkFu EJxl~+cb0L+̈́[0V݁=sfҥf)lZXHdQZ:S9E;uvEpV߾x<0Yi&p}9ۇ]GJ J$}yy pӴҎp[oIx~! 7GB(5 d.>ŧVNX 40v.G*1??ãxX,+9vISlД[[G˻wDVPsDJ) ZRѡQ( Bzu$UZztGCh t>WsՏ|4,+Fzʳt1.9=vkx?1˵W{Vߥ;x:6s&޻U2t~3osJ m9>Y>wχ3P>b\jEъ4/1#T 6X[ Ec麾J7h4\ƕĕA^k̸t7$ xey#xw ӲZ?G~:]}F)r ŸLU=Wo4 $UE|w4k@ 61;f s֪ʁ蘱iHDf$E| fFcŔYEVPVa\0/!-ya^sѼHnLe^A86ڇr- \X[Q୥\8k|ќ\cl7䌊?4T[IRaZm-ͧcF>5j}xi )ɼkx0c442 |o$)k=M-}OmG%h,]-Ģ./zkrίV W(3t(CB ?@j]7WQX~hZ* >`JXug$FΝKD@ؑBP,ay )>G'ȯP'91Z)V>2}}EܐMpmER@"2,v>yn iHi 'y},EfN[cb% )yths_Rсߦ1$vc[ AE/o)TAgk,3WZlЀ6%kI d:En2#Q )ƃ?Sh0\x[jk(f⢑L*wEcQy?|Gt~* . a$ ğO|*)-0g~o:,ЮAPQ%(7-i&2+ ,*ZzO)X}]?i$$!i39J_:˅DfΔqr/`3N z`(i#er>PC$-SCPQp!5fn<ېPr/tZ)dXmcmŠHހF^'M:Ol( aB3&*8hyr/^`mF mFUi],`e,.)j$s0/Wa(L4\riTN?HA* K7,iaƒ$H=>yct$L{GKZEiZ1`8⥒B*" 20 @j)AP4#V GKA̪:|l3 %l䗙 ϙ@4Wl&%,o4[iL:TaHS*KJ,. )U.1>şVKh[1ݐ fgʿ3caC)%@2nq[ ^$Bg*0҅tԳ:|b;c7O8`MCf)%&#ܩq׃ڕl`b-&-e횞JU"+%f9`* P (W?0n]1rxyBLqNIƃiؑ{04K>Ea?1$uu_\Y/dYHLyOUrw#8W]]lg7B(Ѵސlt;CɴpBQ -aō]͔EdY\wp~pJw#S怈*KCA Mt9%e!I02;tb+C3'\~w>epl`eS]RiG'GW}9%q zf?i…;Y07-J/ؔ(-HV.2 `[Z2)?!ipW g$wᗉ'1iNF8&Kk{5 h k@|"O:Ϥon|IV(7>*qgntd(sRtb?'Qq q0k0$Z{ӱVnLؕhk-Hإ} /wh&ƫ֜\v/ 2 r+h^]y̦Y@tt?<˙*b h~哝'_ c+IZG_çCHG2+FE$︆Djz>u<N%=biG?{nr$^&M~>hdaramẗXcBF_,p!t"'P2FI=b߸SM++#TVJ nL$<~8C|TFX肎6)ܡ 1R>1?SL?^?_bY> 1F.KY4Yh̀OT[P{ym,l!Jdq)b|)*V=t5Bɡz}L m)\OpKEsvӵ:_qYauc<WDj}(%f0x8w6ؽZsFvF]Pɇ2z(<łұv)}^2A4">YB]v~X6fyӤq}F-AS<v9elCV.5b-b&xrLPv"ϧsl`bIRtTë*)âbi<"REP!]vrbÆP| dQ6u,~\B#އ 1sA@϶$L)ǔ~;QNJah&wPBj7|G?lW?绅IbTp֏5o !\ѥ r7$!H/[$=x;!1iTn̦%5f;]Ξl˽(Fה1H)ӛzƫwpw]4n})w)>mKcM]lc aQVAKSd++:{/M kƿEmemC /IlS,mS\:}7K! ,ErAralTx>B| endstream endobj 92 0 obj << /Type /Page /Contents 93 0 R /Resources 91 0 R /MediaBox [0 0 595.276 841.89] /Parent 74 0 R /Annots [ 90 0 R ] >> endobj 90 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [474.039 354.172 480.515 367.889] /Subtype /Link /A << /S /GoTo /D (Hfootnote.4) >> >> endobj 94 0 obj << /D [92 0 R /XYZ 70.866 773.242 null] >> endobj 26 0 obj << /D [92 0 R /XYZ 70.866 773.242 null] >> endobj 30 0 obj << /D [92 0 R /XYZ 70.866 610.487 null] >> endobj 96 0 obj << /D [92 0 R /XYZ 279.347 394.041 null] >> endobj 34 0 obj << /D [92 0 R /XYZ 70.866 245.352 null] >> endobj 97 0 obj << /D [92 0 R /XYZ 70.866 192.957 null] >> endobj 98 0 obj << /D [92 0 R /XYZ 70.866 177.784 null] >> endobj 100 0 obj << /D [92 0 R /XYZ 70.866 166.825 null] >> endobj 101 0 obj << /D [92 0 R /XYZ 70.866 155.866 null] >> endobj 102 0 obj << /D [92 0 R /XYZ 70.866 144.908 null] >> endobj 103 0 obj << /D [92 0 R /XYZ 70.866 133.949 null] >> endobj 104 0 obj << /D [92 0 R /XYZ 70.866 122.99 null] >> endobj 105 0 obj << /D [92 0 R /XYZ 70.866 112.031 null] >> endobj 106 0 obj << /D [92 0 R /XYZ 70.866 98.719 null] >> endobj 107 0 obj << /D [92 0 R /XYZ 87.006 84.707 null] >> endobj 91 0 obj << /Font << /F59 71 0 R /F56 69 0 R /F68 95 0 R /F70 99 0 R >> /ProcSet [ /PDF /Text ] >> endobj 113 0 obj << /Length 3541 /Filter /FlateDecode >> stream xZs6_=5S1$`7s}h:%N)RCRq}E[NK /`m΢ハ|uՋ̆y곛۳8abYPf}s-`\,UAd柴2Ӽ,zuJdXi gV-uZcϖ*b򻅎V9~nhZ73|ϟ>, .1Ǧ:xYfa-268Y6'$S2gpCr͌nLG_nR"+ŹLꁒ')|+djt&_x<>F a2T@G(g5`>N*NP5)M Cl2͋5][Ą֭PEQ+& ƾl~eF(y"NQ/vӶ"D&}5lfn=VU֘_ˢiBbCgRqȧOQDt.#zSf;"5 nT0lq貯7PWe0HqNbt)ti|Q  X;BR:?Z:NH6>"@ L>Dj:\ҹuߵ1BbT3UͶŒbz{f1v=ĒasӜaP;^S9]TnJU}%ڠC/m7ַuR'zAyH:yκ$npkPYoWWd n8g{<־ eg ƛpUPl-ȞDnZH3}nG':_ ݚvƕ?:E n!',a1jAGcЄzt YJ7ĦDTEM+싮xUG6սS\c>a cN5 SQ/IDؑoYQp،}1 LxܽL7^y-Pq$9ԒIr%:QhAsH8_IGn])wsɿSWHy8j([1FB'AQuYA6Ȝd, *`!z -DPY3é8RXW NwzɭuA y9e _ĸsdlRY{ ٠W 6WMй(Hѳ6;"H8¶0(mЫ[9qZ/uɭsn]s=0I΍Гk2XI,CT.@6pl3F-Kѥogx/c8 <&z\xM-l [|iє/Tʺ'&xAXYtW'GD"RעOOg3w jrі\ šA*Ӧfvs8\Hr8?- j I>8l r-|;&%awE.'#y5,zz z5zk  =a¹ŤL%zP? IKA]lCU%Ae8LAnG~j`,]̾$_}`7R6>ʸXpW|H\U j/_t;KI3Iþ^(1 ^)SܽT@Z\RvVbQ 3?# YWƑNa,e-6 欄H 3Q ~CBoDiˈ(f9z_yAtE#%[yJGOXˡ鐗ɷo/{qMO"\*[C ̯`NqϿSE{ ~.@ =(5T 1?`P#APsP5`8rqkʭSv!Gq>AZ/O/} y|tٟ:T!BQFءǗ0bom޵:n'(Bm8Id8µp7⠘s{ԓrxb:n6P0)*D]I> endobj 108 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [454.193 239.335 461.64 248.723] /Subtype /Link /A << /S /GoTo /D (cite.li2009) >> >> endobj 109 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [473.478 239.335 480.926 248.723] /Subtype /Link /A << /S /GoTo /D (cite.schroeder2009) >> >> endobj 110 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [460.563 90.184 468.01 99.682] /Subtype /Link /A << /S /GoTo /D (cite.tang2006) >> >> endobj 114 0 obj << /D [112 0 R /XYZ 70.866 773.242 null] >> endobj 115 0 obj << /D [112 0 R /XYZ 70.866 773.242 null] >> endobj 116 0 obj << /D [112 0 R /XYZ 70.866 762.283 null] >> endobj 117 0 obj << /D [112 0 R /XYZ 70.866 751.324 null] >> endobj 118 0 obj << /D [112 0 R /XYZ 70.866 740.365 null] >> endobj 119 0 obj << /D [112 0 R /XYZ 70.866 729.406 null] >> endobj 120 0 obj << /D [112 0 R /XYZ 70.866 718.447 null] >> endobj 121 0 obj << /D [112 0 R /XYZ 70.866 707.488 null] >> endobj 122 0 obj << /D [112 0 R /XYZ 70.866 696.529 null] >> endobj 123 0 obj << /D [112 0 R /XYZ 70.866 685.57 null] >> endobj 124 0 obj << /D [112 0 R /XYZ 70.866 674.611 null] >> endobj 38 0 obj << /D [112 0 R /XYZ 70.866 638.433 null] >> endobj 125 0 obj << /D [112 0 R /XYZ 70.866 518.819 null] >> endobj 126 0 obj << /D [112 0 R /XYZ 70.866 505.446 null] >> endobj 127 0 obj << /D [112 0 R /XYZ 70.866 494.487 null] >> endobj 128 0 obj << /D [112 0 R /XYZ 70.866 483.528 null] >> endobj 129 0 obj << /D [112 0 R /XYZ 70.866 472.569 null] >> endobj 130 0 obj << /D [112 0 R /XYZ 70.866 461.61 null] >> endobj 131 0 obj << /D [112 0 R /XYZ 70.866 450.651 null] >> endobj 132 0 obj << /D [112 0 R /XYZ 70.866 439.692 null] >> endobj 133 0 obj << /D [112 0 R /XYZ 70.866 428.733 null] >> endobj 134 0 obj << /D [112 0 R /XYZ 70.866 417.774 null] >> endobj 135 0 obj << /D [112 0 R /XYZ 70.866 406.815 null] >> endobj 136 0 obj << /D [112 0 R /XYZ 70.866 395.857 null] >> endobj 137 0 obj << /D [112 0 R /XYZ 70.866 384.898 null] >> endobj 138 0 obj << /D [112 0 R /XYZ 70.866 373.939 null] >> endobj 139 0 obj << /D [112 0 R /XYZ 70.866 362.98 null] >> endobj 140 0 obj << /D [112 0 R /XYZ 70.866 352.021 null] >> endobj 141 0 obj << /D [112 0 R /XYZ 70.866 341.062 null] >> endobj 142 0 obj << /D [112 0 R /XYZ 70.866 330.103 null] >> endobj 42 0 obj << /D [112 0 R /XYZ 70.866 280.742 null] >> endobj 111 0 obj << /Font << /F56 69 0 R /F59 71 0 R /F68 95 0 R /F71 143 0 R /F72 144 0 R >> /ProcSet [ /PDF /Text ] >> endobj 152 0 obj << /Length 4020 /Filter /FlateDecode >> stream x]۶ݿm }sIORnf:;1Hri߻_ ϙ4].\^$/$~v/PI\$ȳ8OҋXUuCV2p躦B#w=m뫚MJ%Q]G.NCyM? f+ #F~_ 4t_2jUzq\\il-`W64Չ!9TTD7\-pJE2F3iH촭qE~:/b[d@ZsK8;$UsηO V!)c;NTX1b opNMqAB۩r-`n&͍ |5C~5Ӗs8It Yrqכ k9UΓ-cªzmER`f~Cft|]#fDZG"<4,b˱'(}7ϸrj0y _/,.oO+kG626g?>lR?&@x?NnL+ᕜ4&\!E[f3rնLdqa[CMi<实ʪdT![NWN@p8nxϰöJa!gf*ĩYlrS%Mt iij,N,zRه8=E/}i̔6SXY(ר,Hj$&8<\LE p7ŽRnMz5fFqW-gQPF6p# ]?qd?pwr+gaD y 6fbC%(dvfwIḲ%}3j/X1jxrF&㒢R RcI.Nߗm $@`H@y-TI|>S_=06! @ ,K9q@sEYb`VdL>c+05ǹ[ćሖ+Vrs퍄|ɦ`ՓDjrԓrB.4A$ZhNګ9ZP$uFPtB#oBEF׀MO)Uv'+wENHJ֎=6Ӓ́~Zb'Q5]F\Řy/Y\d ) 时`Vґ +DƥKNn槝e52hEBK <#T#n%#PfW|zhw[ؼNyuvn#\4]t%*8}wMmv`;ǙPÙXKγe/i%kpL}Oz/{ hỈenn(9qߖ\FY~iWTZxgsG8!I ‘0Of@Wm? Ls9a-P[a]HMZK?i(ޣɶE\ I$-x6Hy集П6ATװLd =zHY `g p1KRD/Kt^)r^D-r>"tII9 1'8N_pƄJA<صdw~UJ(N4џS?@3'cy !oߤ`̑f qLNɵgQHؒR#-!#&h"l5+"D (`1 [)?ز6I-csM$ Ȏ ?П.{84["V<4.AqR1y >1V= 'Gȝ_:@>?@_LޯlCCiIRUsJ܌Ü7(S$G]y=@1"PGr&(-Z2v@߲~|)b+| !,k2IF9~EG襜Y0$l9)9Y< ?<| (,b3FgxO3XiMm6yݵ@&;jC1Y-ەG#z<ĩwsoW endstream endobj 151 0 obj << /Type /Page /Contents 152 0 R /Resources 150 0 R /MediaBox [0 0 595.276 841.89] /Parent 74 0 R /Annots [ 146 0 R 147 0 R 148 0 R ] >> endobj 146 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [112.895 515.039 119.371 528.756] /Subtype /Link /A << /S /GoTo /D (Hfootnote.5) >> >> endobj 147 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [514.355 268.027 521.802 277.23] /Subtype /Link /A << /S /GoTo /D (cite.acpi) >> >> endobj 148 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [448.892 240.928 456.339 250.426] /Subtype /Link /A << /S /GoTo /D (cite.apei-driver) >> >> endobj 153 0 obj << /D [151 0 R /XYZ 70.866 773.242 null] >> endobj 154 0 obj << /D [151 0 R /XYZ 70.866 455.861 null] >> endobj 155 0 obj << /D [151 0 R /XYZ 70.866 458.223 null] >> endobj 156 0 obj << /D [151 0 R /XYZ 70.866 447.264 null] >> endobj 157 0 obj << /D [151 0 R /XYZ 70.866 436.305 null] >> endobj 158 0 obj << /D [151 0 R /XYZ 70.866 425.346 null] >> endobj 159 0 obj << /D [151 0 R /XYZ 70.866 414.387 null] >> endobj 160 0 obj << /D [151 0 R /XYZ 70.866 403.428 null] >> endobj 161 0 obj << /D [151 0 R /XYZ 70.866 392.469 null] >> endobj 162 0 obj << /D [151 0 R /XYZ 70.866 381.51 null] >> endobj 163 0 obj << /D [151 0 R /XYZ 70.866 370.552 null] >> endobj 164 0 obj << /D [151 0 R /XYZ 70.866 359.593 null] >> endobj 165 0 obj << /D [151 0 R /XYZ 70.866 348.634 null] >> endobj 166 0 obj << /D [151 0 R /XYZ 70.866 337.675 null] >> endobj 167 0 obj << /D [151 0 R /XYZ 70.866 326.716 null] >> endobj 46 0 obj << /D [151 0 R /XYZ 70.866 168.293 null] >> endobj 168 0 obj << /D [151 0 R /XYZ 87.006 111.595 null] >> endobj 150 0 obj << /Font << /F56 69 0 R /F68 95 0 R /F59 71 0 R /F71 143 0 R /F72 144 0 R /F70 99 0 R >> /ProcSet [ /PDF /Text ] >> endobj 177 0 obj << /Length 2779 /Filter /FlateDecode >> stream xڝYK۸WjE )\3ql H$=K~{1I`whtv`Y ><[NV0 g:A4KWWhP>{w&7M?_DakzSQ;&zh>_(XkIZ& S5Fxɿu>l|x-35 Rf5(K*zZIEo1q&x=]7`~$,^FJ}F3XOC$wmn\<H!H  A k^WQj[,xx2 ;nX 2]6ֲ4u?ton,-M8;z{U0+stu+ `"Wj/1!LYc!4>a1jzМH|9h1T] K2׈oF?2Y3`͢0_@5\U99tms!+FN \H 3:АN޶#xRbd0=Vub ڻ:#3qc"7tb,̲XI vꮖw1Vv4\5, >DwO~(v">MUW=pn#@k@f0a/dS7 3q ڃK_A.l|ZF*Z"38)܀)Wl@8'qMSDZ2p~{p..ce.ZBNh  ./UZ .lsH؉j3":,qid:?1^v7;:q~n%B3˙% )P# piJ,`luBgK˓㖧Sq%Sa<9XVyËt) *(,daW^z]IWO-\HՆgr\z>bf@%{P׌ c_y )..YrIQ;bjOI!CPu}߲Ҙ% pwRS~'vjB%V[#S_$ڽac§$%|h$G!PSzbF4 1MЕZi3pXc1?ܢ)''MBYBjRHx6y= @*M𤷣6=yL*w<~4Ԟ1@&_ 'ۚhn)XVVևl̽ ;Ks A/-f>mep{Qy FDvp(*tX[ug^>vU=B2Dۓ yZyҗeCLMKp@(+g)U/0I[.L~: grk=\>LJR$λrxgx_Cxh1>=,1lz׫EszՎ^=yx [h%6h`xPe66;ppl.;RS_k }x\n7p,KۏDU̍(&Zk]J tl%5mwʥ EnO0%^M^vfq }]}?G6ʻϡa78ǪGV>?sٜ$7^=3qu;ݻ~#I$rwm \ZðHe&U?TJ|Mb_b/^s)w bv1!~OȌn3^Bmc)Xyo0?@F8יOi/Z ŇsaϹOEu<=>>xqE߹ڏ=e d9h:Jq{t=iXPx5(IA%"D͡8/+_~ַ8&tը Hyw 34 8Y6xG0 5_ NԄ/߄aoh> endobj 149 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 0] /Rect [147.237 734.079 154.684 743.577] /Subtype /Link /A << /S /GoTo /D (cite.intel-sdm) >> >> endobj 171 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [392.533 229.97 525.434 241.72] /Subtype/Link/A<> >> endobj 180 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.499 217.102 141.31 227.751] /Subtype/Link/A<> >> endobj 172 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [147.749 181.038 470.469 192.106] /Subtype/Link/A<> >> endobj 173 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [327.078 144.291 525.434 156.042] /Subtype/Link/A<> >> endobj 181 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.499 131.424 213.309 142.072] /Subtype/Link/A<> >> endobj 174 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [366.351 94.677 525.434 106.428] /Subtype/Link/A<> >> endobj 182 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.499 81.81 325.08 92.458] /Subtype/Link/A<> >> endobj 178 0 obj << /D [176 0 R /XYZ 70.866 773.242 null] >> endobj 50 0 obj << /D [176 0 R /XYZ 70.866 488.492 null] >> endobj 54 0 obj << /D [176 0 R /XYZ 70.866 374.341 null] >> endobj 179 0 obj << /D [176 0 R /XYZ 70.866 246.705 null] >> endobj 76 0 obj << /D [176 0 R /XYZ 70.866 251.188 null] >> endobj 88 0 obj << /D [176 0 R /XYZ 70.866 213.615 null] >> endobj 77 0 obj << /D [176 0 R /XYZ 70.866 177.551 null] >> endobj 89 0 obj << /D [176 0 R /XYZ 70.866 127.937 null] >> endobj 175 0 obj << /Font << /F56 69 0 R /F68 95 0 R /F59 71 0 R /F58 70 0 R >> /ProcSet [ /PDF /Text ] >> endobj 189 0 obj << /Length 704 /Filter /FlateDecode >> stream xڝTKo0W@*6޲}[&*r@!61tV{<f0?n#Mx(h{c8-c|5h!c_N6ȿ)'N$Y κ+R|)v th:s +˅djv۪dG^Hb$yQ^̟ؐ*՚Z6L1g7J^aM Jk.ʮ3H}q\DJK*纒">ZkA٪rM\Ilk|@FC )E {_ǣ$U,A`_4emM[P!ʨnWQE;rΨYWmj<%qi_ &,Ә"cZZ{Pv(EGnƥkȹ͐!S)xjtH{#. N LǃރDSRg+^TN\h6!d,YpL3SolHӛ-نj\`;#DhrE7k,+]y:ZGQҶ|sٕkl\@qKA&vi}KusmA稒ܟlUv$K@dMHv٪jAsZR@:1y3?s$^ endstream endobj 188 0 obj << /Type /Page /Contents 189 0 R /Resources 187 0 R /MediaBox [0 0 595.276 841.89] /Parent 192 0 R /Annots [ 183 0 R 184 0 R 185 0 R 191 0 R 186 0 R ] >> endobj 183 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.499 709.922 442.4 721.061] /Subtype/Link/A<> >> endobj 184 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [378.038 686.795 517.485 698.546] /Subtype/Link/A<> >> endobj 185 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [117.624 650.73 525.434 662.481] /Subtype/Link/A<> >> endobj 191 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.499 637.863 219.855 648.512] /Subtype/Link/A<> >> endobj 186 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [257.243 614.666 475.235 626.416] /Subtype/Link/A<> >> endobj 190 0 obj << /D [188 0 R /XYZ 70.866 773.242 null] >> endobj 145 0 obj << /D [188 0 R /XYZ 70.866 773.242 null] >> endobj 170 0 obj << /D [188 0 R /XYZ 70.866 728.339 null] >> endobj 169 0 obj << /D [188 0 R /XYZ 70.866 706.435 null] >> endobj 75 0 obj << /D [188 0 R /XYZ 70.866 683.308 null] >> endobj 87 0 obj << /D [188 0 R /XYZ 70.866 634.376 null] >> endobj 187 0 obj << /Font << /F56 69 0 R /F58 70 0 R >> /ProcSet [ /PDF /Text ] >> endobj 194 0 obj [277.8 277.8 777.8 500 777.8 500 530.9 750 758.5 714.7 827.9 738.2 643.1 786.2 831.3 439.6 554.5 849.3] endobj 196 0 obj [833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500] endobj 198 0 obj [799.4] endobj 200 0 obj [500 500 167 333 556 278 333 333 0 333 675 0 556 389 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 214 250 333 420 500 500 833 778 333 333 333 500 675 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 675 675 675 500 920 611 611 667 722 611 611 722 722 333 444 667 556 833 667 722 611 722 611 500 556 722 611 833 611 556 556 389 278 389 422 500 333 500 500 444 500 444 278 500 500 278 278 444 278 722 500 500 500 500 389 389 278 500 444 667 444 444 389 400 275 400 541 0 0 0 333 500 556 889 500 500 333 1000 500 333 944 0 0 0 0 0 0 556 556 350 500] endobj 201 0 obj [556 167 333 667 278 333 333 0 333 570 0 667 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 278 250 333 555 500 500 1000 833 333 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 930 722 667 722 722 667 611 778 778 389 500 778 667 944 722 778 611 778 722 556 667 722 722 1000 722 722 667 333 278 333 581 500 333 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500] endobj 202 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 203 0 obj [556 556 167 333 611 278 333 333 0 333 564 0 611 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 180 250 333 408 500 500 833 778 333 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 480 200 480 541 0 0 0 333 500 444 1000 500 500 333 1000 556 333 889 0 0 0 0 0 0 444 444 350 500 1000 333 980 389 333 722 0 0 722 0 333 500 500 500 500 200 500 333 760 276 500 564 333 760] endobj 204 0 obj << /Length1 767 /Length2 1310 /Length3 0 /Length 1849 /Filter /FlateDecode >> stream xڭRkXgU܊IЀ )`P]bfL:L [bMtQVPBUPJ "*F mAQD֧ڿ̟|{&0Ğa`o %l l ,Fa`"6|R ye<3<1y'ɘ9 Bb`b&_*7A0 )0@a GP s“`# )J)0LI @0T XBa`4ysoT ML/u dr@A0~.]4'!D!* DRDG0g/w`-#ވB$"i2<(2I00,MÀzdH8Q,M )a^48rEiA!X `%byl Lՙ~$L9( [+WbteޑCa-_Av`mX0JL>2OXy°S70Ă۫2*:Oc$/PѴ^:p%^lY"rT5w I8'.3%l_A)m%[fˊw>[o-~ݓ4 ۹:eTṳ -~Ĕ ~ܓ{qV+D{mAOmKcfv}&^ ! .Uy6pF nzNժ5U>TC%Y憞_yRmk>[ް=OFb޽~3Mm=6e݊BSF:ڏZQ?|Ue{3G &qޑVVPTiw\͋Rp#Zozˇڜ]w:][ZT0j+l53sl,|aʏ 9s:e3朱 u< 6 rZ9I9Bx>ɯ:.sEuPh}PAґ*ܙ{skSK3μk~๠yin8L(G썛#IMSvZjoZo=:w)rpjCxq8rAv7v!I/4lrq=nQG#ogxܯs,yEmy^Ɠ{fY;vNMƸ΁cen\t +[ݿ7=, >B=1׭,T<&%:ٚZ_0Y> endobj 206 0 obj << /Length1 797 /Length2 1996 /Length3 0 /Length 2551 /Filter /FlateDecode >> stream xڭRy8k9Y$RħZ&kfd߲F$Kf^c45c:})Z0( ")|Go;\_z빞yI255Q(aJ 2D53@b $o t t00` $_bIA:vx/ )3D PMB6wN` H04 $H"SaMKT@z eS$BTJ(@}`H{wBoo6ޟL /`2@:`A:GQ7;Hfصf)d1DwhAdS-4KnTnd*Hh_wKNy:Z҇n m,dH'T_71BctoAV?LNIl,^>d^ `^B\_V]|Y9 /G)mVJLO&:i1E=>tb*7Am1.t2@ᏘOMuK3:|}ҏ?TbvdZpX~Cbu,r). qٴD턑`x#1~ Bof [ m]Ϳj9St6^( 9Ӂ)BN1W$d̽{祺4!tFK{k{*A_bC7S 2E6i̱D);[sz$"K]d#a L\/@ygprh˓+RWjڴ8Iё{SMRC5<_s[;>B|D N5rV\g\>*=$KGzaZp8%mRຈ@PTm\9a؎U#-W" 移P煔n6a$Z3aV'X<5mw(!q6|)kJVKcLe =櫯˜Z>փSƮeNg?T(Y[ΧտYplxyu#:#.*{pT2\f%2=qtbuS>s]?n{e +U6=_pVީsv>ZFcAZ1iPу3|q*p, ؙ1h ɞI7,OQ>yǰq*+LdߚƟ}pfmn(ƥD&v!Fsj)~ .ڵB6r2ݯm1ݨc.V2?r gMJmǚ,V8K-&Vfv׿G>RB[Ӄ6itŞ~,?Y+]g>ȵInם{ɞʕHkޗr=W^|WCn~6x6P*n;ĺ8>vh-^Nҙ.~S-%2ުoE6>0wĺqV?=^ 3h3N8XB)\2[@](,CyGXf#Z:M,LP*"R;W{n˙&~a[3L $pUe_5ҭw7P='Gb-Ϭq1> A\wa-,@$Nx"CaVP_o'[kٮ3?[QҤ^rV}:~;=bwi`٩Zdձ6T˥.f?*/Ur5/iYI"8kW:M+h~FFv]oJXJݐKơ߫G3 fkNM|<Ǧαq*"cɣ}s23nOGv FB>E_Vy㔢ϥ|;Ui8ōntYQf?BNȱ-#JPF HeR(CfW32R$۽,ֱi\ [3Y_d ?YbQs+{0G~SíYRG|x;"z,ʹG$y*Źh:C21 S{NU39 wռ/A4zs ˦i)Lh&4P;2Q}'fn_ͣN/yz^UR}2'oį?zrM|zNy endstream endobj 207 0 obj << /Type /FontDescriptor /FontName /KGLKDC+CMR10 /Flags 4 /FontBBox [-251 -250 1009 969] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 69 /XHeight 431 /CharSet (/five/four/one/percent) /FontFile 206 0 R >> endobj 208 0 obj << /Length1 745 /Length2 579 /Length3 0 /Length 1091 /Filter /FlateDecode >> stream xSU uLOJu+53Rp T03RUu.JM,sI,IR04Tp,MW04U002222RUp/,L(Qp)2WpM-LNSM,HZRQZZTeh\ǥrg^Z9D8&UZT tБ @'T*qJB7ܭ4'/1d<(0s3s* s JKR|SRЕB曚Y.Y옗khg`l ,vˬHM ,IPHK)N楠;z`EFkCb,WRY`P "0*ʬP6300*B+.׼̼t#S3ĢJ.QF Ն y) @(CV!-  y Q.L_89WT(ZZ()ע(L.-*J+`XiKMHMy-?ٺ%ku/byb˛"vL 6^G[g_J*\Eׯ'"5[,`_Fxes4<͘HjY:7(ן)jq iMR2.x?~6[>n]츓$~{Vvmε6gIݳpPY3> endobj 210 0 obj << /Length1 1612 /Length2 12057 /Length3 0 /Length 12890 /Filter /FlateDecode >> stream xڭweT]ݒ-݃;; !8wݾ}{Oث֬Y5k7%:PPq0ssUtrT`TZލ.@S)Z$66+///"%@ bce hi3W럞6Vw~oT`k WVѕUH+i@S{9@ X:;9ZU+;+ ۼozA ;`b~ `hnfw߄@.Nw0'W  xϪ"!`kS_]m'H 'sJ8@O_̀ W{w04\mŀ2uüc՝ /՛@^v;?9؀]Ll9﹭lYGK'+?n܁.7毙}'ajhZ"2+9Shg*3 +C{Z^}qo'; 7uo6{6$8`f:Z 'Bln 4vMG #]ѿ `dea7_ hE:"ߩGkR,s'2s7aZb /`$͝,ux/˻{Ё@O99mjF7gp\Bj A0?ک; 5| Sk1eWnoǞ;xGGNۛNMlTvs>Ţ5fT K4~GHMq B7O*z`dhG%L.}v,%;4 @la= Zش&dW.\JO *Vy+ `څ]dZw+up.f& &NN缭QJ 4w5eGy2x5ܧ֡5gvhR{IG .% P[%x+ȕ`QL\4p;m&3mWs`cRtv񨉫R =1U}e_uRK4(j}*jCk&ɒMD#G1 ;?H则Zq}6C85[ 4ljc[⫮dtZXQy+p$Fyf-xP*KB&KBmDžU%O V\;Q[r?5Xc(DdY/?Oxak?fvu|c@X 6Yu.xC =*)++,:obv z:jNv%y2_XA_K әUls;j]_Cg3b*z˅kP+ d 7SZ_ &jUUM/"h`uVO󓟕$$Vjb+T7:$.8 =h)3ARz_j՘  dpD Ή?1<>=#&4a*IYHή "ͮisdٞg1#acI҇DJ',o=80&y[m:df\v`d' bADJent5 탑[;fMV1}E5IN 4,gDō"Tt/y J&lYRY}L/ܱ @,kR{K0 U?V7W-P\|9FT쌸/T SϟH-}L^QȊBX.h'-, cM6pqnVk8B:lNЙyadC"pڸ=YЊ9}dE0] qR"[~PtfGacpz:4v8^@ Æ0}؀ χra W 6cU9څ2c dgrd|` YbD@NM7bZD#+.3.uR7fPVCIa:M5'%~|S#C1Yx(βvtAV=¤v5g+k 7{}NRȰv Jn+$\Σ=eA/|*p*A,24bSҚB2cDz߯]f2DiGORJ=iDDѣ:20?ӁkE?'C|ۅ&~ }elO #C;4V^dJA>iwݒǰ|}Ybbrsp/V1.j{!Zr g!+b5 aeŔ:؎Kpv˜= .n?^kqPDh!Eݩ0t<OBÎ>e0$vN{SmS W4`j99Ződ)σ:}nI qFq?nX]cCV+Mc"XAmu[-=2;'/fG'#/408p"[ڼÁ$qESD|bGpW:cG0̕N_#_W"ܴP,gD-/lTV/,r>8Mz<aw7VinX(`MCYM}B~ym`'NqZ^Ci*7vr •ڑ-l3P+^Yw ꆲظ\L hBjnHٔ !"w i QB F_F->j8AV!/1w0P@۳ Й F6~ʆ|5L{cv?FQw:e`j<ȥwžQ}Ml8OfR7 DAGeB$b읨BxnM8Ѩъ/N#t ̢*!eBz6g:'~[Zyy͚" ǧ,"} YܟviBȘė9e[x,2<`%oaA`6UBwBu,eS P .PϸlėKK(ЍG`*;<'%.@ dWU̼Vr3RGxn4=s4 o.w,7ZUQ[/9Б4|=Hz,qu6 EjO32T\9 {RflTN>(i8sYds[TeGْ 5h%#u4?.9I bq"j]x%W1Sւ6Y;Hۙmʵ$MuMP;[@MO;6K2VGyH)Ĺ!?zJʩ?1lm8im5.db(q?>FQ\z%` zPovXƬ?{xY[=N8n|[Æ4Vj(5;G( WeЖ7$/_:x 5>ő66fۡJ*ē[iěvb0[" g q6"Aٟ_Y/]*YQwQ˚\%f '0UfzlviE|p`KƤi[LjSi2eu^߉ -sA̓3Xw QMru m\^/(7,זri6ʡ_+ޘE?J|ޥ$e+T4@Shg9Fؤp[7d.JvB:'*p~; ’I}xbB9{lG̞HHϛAFtPC=: $Qd RǠSBDxNj&4D_z,-XӏT"z,V8tQ#=czLȫs+JϬ6¢nu@ yEzj&)] bGh:E7xM^OZKebxU tT_`~,d݃T4&3EFiзqEgR)O(e9Ʊ,&bD:~  #ݺy*ON %,ə.n_![MK _T{ڇ_۬UFz VPr^Nl~'qhݞ*>Q$*,Td U*~-=ğ&)JDCAEi#m5oAVUc,Jy@ޚVOnڋ VeĽgǟ)ylQu_-ϵw)?6WzF4­!7V| q䦣8鈞Uþpȳ OAEcp٩b9ktxJ.a$1魲r%߁2T|GD[Gd\/](ۼ! Ra&\Eq[_~ݖp_z7V&IgϴY@䥥'528!9/#KG]J ʍw@d;n0![%NV|V_OhyH^2t? |vɥj'ty˰:}?y 8kS~u+\3xa^(cFd14Vc;']ˌiP|\Ha`+M0Zug&FG`(Y˳&1v۴j(߆,I&)ث^mu2Q7v~he[4Wic@:ۉ|ǰ^`2}ϬWd-% 7 `N0jqX@"pC-k;xMQ[z؈qBEv0דOG)k;Ɵvɍ}2? KvP&@G8N0/ AJO"SU.q֋1$UMnT;Ss[Vw6բM枃\l)NL|G:X]=ޱ&~ /ZCi-]m~EOhum b8~vnXL#ʵ)pܺQH,2 D/k:69 iMp<>[݂"0-XmzP-ѩT c~2d䒙!yf+2-(**Ɲ['[2 mAb#CM&=0 C1joHQLxnGB.on{jxX3'C^2=Dᡬp=e9#%'D8>'slD8D"ߚDaqUgm%SQ0__FW )wd}&C'RǽQ 'SZ3> N/@J#CÄs^\{y~nS=f-l,RKnV(A1׻xYB“i+65[IhWF4Sʰe\Jn'*L^rC\%pDMbwͽVȮ~O%̗vv^w_uҔIZ%{o͛BH˕TSgZHn&4(w槠Y)&E"ÊSQgD۾{I$rE>S@}CXG8Q:lYƣ6U!D˪CFɽѩW&) +8fuq }g\a&B}͋4DYCW=m@8G{TOJ&7&5'H|I#xyGPAU2{BβY0Vժ7ab\a0 RNn!58ҨWG^L>sO'F]r]gXY:Kq$?v'Y_w  98\U/L,q 3 6O&)wAUE[-G i*z^%aM\܍ϛyɑ٫-.4yJ }Sk]38֝ELS34aRKƥn:g^4Fbj7, 䁃41J8/WɆ#u&/[K6b`xBOVD\+tFF8+L$x@<0Fj-n|Qhǩjvܺ,^Jz_E ]ю}OtڙDzLxBM>,sǼt:^%hB{&AGk [kֺl?y xm&{GZU,f;iVw\ y|XMo  ߝftj}C\?׌ZA诓-uηCIemu#دQ8cٟerf]GnbD!nhڄSQ߻e%<۲+\bh2ޏB\䘭]HA)Nem$܊NO>tG R3QLuKQ'^Ixst871Ƨ.B|TF[ʌ^o##`ec)V32qX& j{7م+"5&3J&ZyP1RlsZa5׵mpBZn KiАT2ʑCX@:(.G)շ8? UJ]r9̗ ,wRibn-q5p2E1X總oo5X'KT)[]ɳA9YecA͏eb@/cʢ;HUf: 3ΘmܡgGޏ ]1骷S6c^:KAB7LN2zFǾ5y8×"7̯7k} Y Ͳ[-Nf$o.13SOյm%V͸ 1'{]be[(}:"ѬVy;KUga~x`\;t~RHEX 9%|%|0!YP59!)s ve`Uz[&>޷u4A̸D 4V8ZZe) Nbyv.q+Żfqn(V o1 QO |ju;?i觋&RĺJCoBIW.x)| |'9fl ׫Jc?p_f~PRM_3RAtZ@^; u|6 .Cș2 hshԨM$94|K}{ׯâ[^ژ'xRRǖV;d)C|wLڢJؘOZ̈x;QjopWƌCX'dݣ ͳG\hX)N>OXu\iHҨoB5ԁKimYs!܁mQ#TDÐi?Y$Y_2AbĈJ< B.l2p7pÓK!WӍKh/*8*isJKDA+6_F7 n3'OFu]''iT11"o\rJlko2g^q╼1Ftz.BwA.P٤ Mw0$ @zۥȗR}H0i͓Z&VƊ25zcB%YnU/ЇHND.>f="ɶo7 Kp%G$Sb_j-e#lL"?p幜EDfSfiYN^j4]1)b8d¦ǸA:uW43vjwJ5 ܸ+WPQ=;`@!a"3=j#8"AB웋t׆rOԡ^R::UV! mYdJ¹(1F&!=Y/<|1D9j^>=vAnٝt 5aT.ɤȆ{\Cɦ2}CzؠۜB.#8 ְ20[ +:$)!>[u\>pwɿi)_|ӹ"(__;^b1PMhnVs]!^mO^o@~-p LNAER_Ng@1}`JKO7u'*=lMQld1c746>ܩqmga@K}u݀l [DpX+׈h?c0_%9d1lwv$ۢI?Py<9|LpϥtRL- e GK 7)|č`HlrDeļYCϲQOE{Bi>Izc*ko2_7ڈ=W {FփaƵ1 hrQp~9E" P~9M nһeSRO&ZA0U 75 !mԛ.>?l5H!9qy5BA%pF^TNOm44̹:sDMF: țfS]fư(NB0dbN˭Um5 8\v' -fCXf}xvȉ <8\7BS`x>.3MYqwb͖#pl!z.B}6ݑ9kls;m,W$]mW顬q Vr85EqnPOQlY -3zqwCsHZaiFRz|RW ;w ߑNy3LAisщpaWWBd~H)>7"ϟvM["ɭ1أ[WŒnv󣤫J~*ަjQWX 43$DQx_F%)/eddɫm]]fL,FIl6alz?~n4Q2-s˯O Kimt#A<`1NOuh)f$QL\"|TŢ˿/FŔ)N`9uGb. Tf}n5WU/~p˾>lO-7[i/uX8 pw -rm" j w/jYm瘁kوߡIS^~.՜\' $[ BPZrBbuɕ3sgPxyeդfY\CCC`=+̓M$ >3-}u0pejw֥ݍ1Z4ڳPVnJ/ז}8{ӗOW ǦG>4a+is_#fзF#VW/DK,y.7sM}=h`^m O9PW!5fB=҇:MsCU^d~lʵ(|"^FvR j9F߬*\$#>f%jwyè*(vЏ(ʸ(`6-E+2ɰ_L?\L+Q ׵6havy@D{LKPC*Nrhʎl/Ć?ڣcC[^@3e 5C ~3&]j.GwM=-ꝗ YCƪ}Y:6#tؒceOHwshcIu$߱!gd]h0m|kCa@9Xj3Ut]!d7%9t*%Ŗև9܁~;ȿi kHrϝ7R@|U Sgb%b endstream endobj 211 0 obj << /Type /FontDescriptor /FontName /BMKUAM+NimbusMonL-Regu /Flags 4 /FontBBox [-12 -237 650 811] /Ascent 625 /CapHeight 557 /Descent -147 /ItalicAngle 0 /StemV 41 /XHeight 426 /CharSet (/A/I/P/R/S/X/a/asciitilde/at/b/c/colon/d/e/f/five/g/h/hyphen/i/k/l/m/n/o/one/p/period/r/s/seven/six/slash/t/three/two/u/underscore/v/w/x/zero) /FontFile 210 0 R >> endobj 212 0 obj << /Length1 1626 /Length2 12316 /Length3 0 /Length 13160 /Filter /FlateDecode >> stream xڭveTۖ-ww+ww,8V%Cp}sߟ~G%s͵{)(ubdeb(lU6`^yF)nDw9FN@>& 4Xyyy`;wFCU៖Byt>v6@[wup@@4FZQ :YA&y H 0;qmMA%08M@i@7_. 9lglMM"n7Ma{S;:98U%ڎw7li 6q}0^'## W-c hgm^7 gG?0F@Gww쿦>{#;;kG#ڌ {ms-"_"kknl>kghIm@3DfE{INe-[_5o>+?2Fw qRl@_}NF#5F h r2Yo)d |YYXŧn2KJ]3Ki*I߁Kn?ZQ/11~ rx%bY}ϓ޿HښMZ5'#[M/_ngw]݀&s` ˔T*#ݝPvy9~ߔ R`1f#tIlb]9Zw QR5#=gסuXn<Ò;}tæC1IiE=:whk>+S}Fg4^[9*+ΑO㑋4·͖$Ch߾ Hי" by%|~B67!oG1Z9 XIê]#4rc Aq%E*.|2FLH P`fpĝ=$iI(1+s)ۗB:"k_Qk~J@εe<}e@@x3{uƚb *Ӌ&Fxl!yzvSkw6bI!L$Iw(wCvoQ> jk8JHC(SvEа?53ڿGb~&lEj簺mjru kRTc͒E? 4[+TA~Aߣ'CA٠3M09uy&wC͗ n.5P}CN 0ؘa emZ~t3sI  ԨXyDqYTFOEg\BDh; 7%B 8g}fSUنF JEkc*d۱b Z~g|r!p,I+#^-TwXn]w͝7N !$b~WkVql(%>`+G EfW&h؊ yfB8|7) Qt/|`?0oeOBQ UVC|]04B7e=LZI&aIjd 5ӓַ>˨vȼl4A@R"/Ʋu//L\t li~Fo%іxSfL֬M5PWgkA+n96z'}Go)yxZRZO-

-͕3_JVnqJ!flVrM:vP,_׹m\Tc!o 0;?lZ>$ϸ;lvn5ˍd/J9מ ;~Ek=SOQ)C= ұz7D}'El0O3(>0-K-T|Ք ӭ$#4OFU*:It>@%8.4V 3 WenlYֵNb柠f_nO -3>D=|EUjiO&Ľ#V\Fkc3:Rm5;3{+5hg.01HfE@m]qGrC6l;_\mz|]{lNq5ʟfg|6oRa?YE[Xnvx J;J6Y ?;8:";W:1JֆKVH.xlIٺf< iWw7ۺͩ׮;Nl7mC^u3{~fMZc(.ԏk& 1,'LNݺۄVi8ϬkYȍ&8ЎH°%l;sp]ئDͣ]Q{;1xP;w:0 KԘ%a5E/NdKhn f=)k`gd#7wp7#mN?v{B+m|u6uԛ7bsK%:)=q{ !,_UU왰:"+WA K7q5xrj&R^:$E~q'?-~l_рi k/,M̓ۃ9&@PZ'/T3s LCUTiU :Aʗj :ruAEhLQ2"'NLeg?n' >)5\!ϑT86M{8D@<)⺃ 聀`PpNVڻR+o ~bŨj9n`xWtޞv6oFމbSZvY+8>Y8pnRd2D(Y.+ceXM$P=[W4d)%r ;JNdXAʂ˻͌=ɋiXo&DS\΀ʥfCW"`IWg6]29%ȑ#_UnpO)o0+j=Akqߙa˧}}6~p0|&t]=30~h&hZ:Uw +HIzt5Rn?h~:=AgA,ZCeGGP/=Z5ȹ/Z~)*m5ބO Rh$d s>|,_15rcV!=N1?.cBZ lX{ }ۃl@U %/ֺÉ7gr9 /[O3?85o?ڱ2vd*"4v:p|`eUJ{FGʡr4w6Ȼ=Bg>ˌSw\ j8oxџpHȯf(2xYuEZ_O8bKl`f("לʢAkE`Q]]3vm,k>"s7i]j,~6 eϕm3yz F f\&NdZ"J?P=5` ;+,G>f+>Qv:'ݨ}uۣBNEWFxGu˕g KjC|8F}@ W @UX) &ct|?ɨv2ϻ⫚鋇2s(>B-V}#dtIf Y|l&XBZ-d&*bF͖Kh/w6lJju0*IsyHWiŻ3羨\g꘼)KJ]}!4|  SV;ZSb`[kTbZev0@Sb*Lh8UƜX6/uJ22 hƶU~s8R_vK8.v/98!NCd81lZ{ećA9&s]]kGr>d+D8Dh)/Wupʏ@' xJl\o2|#M[_bMw;_+mwm?pTz|E%?S'Lvsjm6"MVRsӺܻ/\F=o{dϏg>jQ<ސ7X#u ^]!Mb]^vrE^\EV4$pl渳0ubR#}W`Ӌ.[U<$V50l [D Yk&GZy!@Lu--,m24R[)c$nDc [%JʜA[ߣnmx--eZ8Zani%~~$yEuAL}oKj)mHs|w7F3,edqlH3g҂ - {Y1{XHmV׸jOCM=Ymᮮ0&23' ޹X9XCna<3!~{u}_QeGǨ zD1-9xJ`яP띪lSNˇ@ф6hH'dT-LUmG9 m ]Np]RKI\0l,zֈrß۶d#؏tM`h'Ed15B@ǟ֨X|hTpύoj禑+7DNNFQɶ4Qg_A`Sq9މFG$c|A9cf@?KV_'gܼzZ,ODa!gZvÜ,3QB >o`Mz1fh/ 虃SE6Kr7yޒbiP̬  _Ma&I5CSWjE|> ń$&|8RZN %#$^(EBg0:gQbܜeH-A>^}_dnzYyP>qIȸ /PW)> '#$YZE.}ǔO]4 T"J[n(/pĤ(yfckۈsLpʺJ(ly%Hv+ Zmz,+ YpxɫwlGVY^"?jGlPT_Ts\f.)>ވIcxuEҔ=#X/l5aRaD2EǨy̜Tc_ґV{D `Deh.I.f\Hd"E]|쩕Q%uRs3m0^NķFxY~i>\V5$J.dmUw͊ AUgc旛;"]GPbBw'矹a2Dy/Nئb(30ʗ:*7Сjzz元e%e\" ec.%jY?J0]pw#[dyHˡOaQlbyffئ)x~~~W>(l֝Y%)X~#ZAy!Z@DtF${*;iE೺`Mic䓖8dcDגF?2N(mISCRz i<Ҁ]4ւBol;`efӤ[@y tȽ`-(U6S_C\(K@e#9tsXLQV: MQ 鑵nOuSBz/FDiWu JhtI/%?t(Y6YepLkfY#>+%ϧ-<] fw0?{=B~!+ԫsEmL(`>_)NlRG[/ nR~ Q*q?Ȗ*$*[?j̈́g a Q%/VΤ])}sLm hܳO 0͵hyax^KWbѷjSQ_)T`QbsK[q* I)Yߔ23kj$qHJJq #j>V`:Ԡ{ef|,t &]s emVX4QBC\rmrѮ342ջopYJSSs^HjcIGE1_4Or=G)WuQͿoço[ +W"Rh%h,0qjUOT|}(iRuA u̳Mc>qEHˆEy4 m"_"S_H02ӀR?g+aMKGRQx.*;n)##T#R;eodh;nTaM"ï^盷Jc^x7N&N$Ks |>zj mа 8$z|R!QA4A5K>;|Y|0a6@xgJ q<]>laG/>HOrB3BMK7ت]Ф*Mntu >\'&~D2O M&[U떚9(-sVpXir t.6eVE$UA*/DҋgaC.rk׽<D멩 d9y%u;m7UZx.Uz V 'Hx2$@2FKt% [i.T [;AT)|`24E3(0Q; i.*% R)jkb 2 fOUHgA鸋&!&?f ڨPWr? EB[ݰҼ9[&XRRH> endobj 214 0 obj << /Length1 1630 /Length2 17545 /Length3 0 /Length 18399 /Filter /FlateDecode >> stream xڬUTݒ%%lwwwٸC➸;$$p?Gzc|+"֌1c&'VT23:13r-l\ld锁f.v68rrG34&...8r3JMY?-<-+h p6L-E-)y @h t4(Y[d-N@j#?9rݍh@G ' '8,l]L!nj/Bv#l)99;;Z;fU7OgsCr;YuLFSҿ|az -l@wr&Nֆsw ' [d@ p:X;Y'_?9X8;Mᘘ4vY501nb>WDP%ahbgk019M SO[oM_w7;1Kwd,kCC kӭ7߶ٚF 'q w9oeW5:Z[jOښ / "rBR4@ſaFsx1s9Y}7)g9CgG wߺUIۙ3:*Ά&q8:_ oq@ce;c`˴_ε9Ó:L!% v=~i\jB?PAqJU{/8ՋeN3"*v1]p.kTl5oRk %I-M~qqy JfƈD;ŅR~ i6hĶFNզIZw$_v_W<+Ers =T ӡJg,Ve༈IPOʍ]}Ln]:}$Ws XB8~xô.|pdE :D-lOJ!~Әԣj9擎'$S% ԓ?3t$UVM]!vN~x4YS0A}t8MWHXAMJgդ6oh4ŷ5/:N-OThhQ2G],Ҽgۃ"$#3Seqc![e㮄k&ol,[CDŴK{%8CNjzo[~*żlØ(-cw@"赛Cg--H&g}:5˪@k 1mv\4K1?ZDn&teD}{!ц> ogz)h.,w0+&Z4)-Ve|Vo -z]𘹮mJs@f·,;;Y:i%opV(.A>/(,X~8Ip&)mP^Y5vu/ SRn5H d{&*3oSzZBV /{Vҩy Pؗd˫Gl42w5O ^9nXܹEI<ݽ #/䔘w}hD uǘPY09l`,4p,-bHء BqPŗ(zUNl4b"pa0^ ?[M^`x뱙|}qcBIXs* )6[;xnas]LQ:[° \ʹ=?ۦ|X`ڲUpY)2>_k7u*e۷JߺSpbMP#BC"?dvG ũ.ݢ<!&%oЂl)sFdXpJ߅793p^?E8y=eD5|UУsb=`٨O7m:̦f.7Bnb[kjڏp rW˳ݏz;E kDܦՌ TL֪oU&Dd?ƓrQ-a\1@" FPYbKzkrZIQb7rHO 8>IJ-0Q{HH(|֘I:L).};U}go5QI5y "[Wxќ@4$EԬ%%괍1S3."5.q])_АᓑRϱY[[ EDt0ޕ]$۶43l'\cataixe& zx?`0DlJ 3~dngI{ -u7i(*1_hH'^|}^z H8STs*a?t]Q'JFTQ< R)ad@mx6׌X'7,eT_sݜg.7bNv,~l@@޼AR9Չ-LE8D{~%-Ik$9wS%QS# cFb\+zj8۪Nv0%:K\O/v Djj{qZWQ"x+2׭;5 ޓLwc6vҏ? jB8?qb%x" 55AQݰOm|&l;bŔoΡj tp(&~i 44`nGŎ+_wp(XA\x}!¶"vKq5A(l9kCl7\ߕz&s 7z1Y%A&Qy;mrhqVGx0rpмAcA -Sm #[6]|(Uq@=8}ML/ X~M~ʴ^=ʘ0Z?h:fESi9xd*"omHI n^(^$Uge7`Jߍa`*vqܷÒ{oli3vA |ZM߶_77JJ-h9?o: t4/a)+-[i44iW|eKX8ZHK5dDp-klHAzI܅:{xrK\c S{Cߡ^ͨAyɤ~+ZzQmZ2c`04d)̹໖XӚj;GZOfSbf[2CUK-ZęM>P0-t:A\ G`K*8b=%+J/&7JSX 5[jk4^k N:OdZ",Uib|WQ?m4KTU;Yl\<v'] pj*xF<\*˟ԎУJb偨1fga4o?3Ru}ۃ^cG]ǛMT Pj\h[ALgLNi8ë,6 Nʹ-Dݖ%a_jgw};"K?f){%1 wA#Yzq݀;/MwDK]] 6yX3$\awP%"OUoib,ǽ  >}yە?=vC$NQ匽D3:qEWHyHj>ԚUX PG-Ъ]ԟ{6xDL^tl'i٘Tyo[{_)!X)a!XM1ҢoYXM]}Ĩm53ZH@Nc$Pinl5"p8l RHyUe>W xD$ka|)q}NJ 7QVԈRB"Z^Hyu|W!_vKwb>s9T-;Awu0oABgkPf!IL,(6AstI95jDڑ#0L@#xq9:~b7uo=3 Wо݉N}^4g&ˎY=1Vmm~6pb 9*|LYƭsWQ8+K٢'8AHn#+AMblHΊzVasPeO:p^qoMx OuĪn|d/ d8^8tt:}Ql17Sm*T5Q-)CgA*:9rhrkD?nd'k 5eђuQ Oo?7H4/'[ vI$VDzThfܻ~B%1(P*J q$* 4+Z1J;u_դJ ^P2k칻 G{;9,Ŭ'd4i ю0mNj{z"h,d7=)QNT :ؑ!N46㙇դָ~?#m`60WUy߰[TƤk=CƝ]ᯑƲ*^C0ӑ]L9dh 1쾊Zh9Xk68i_RrinȲ#1~v\0pAc{%]8YMn.&a|ۊh=X2*BO\ ]kjn(5ꏟa^k0AG 5s4Yav?v2xlu5( 9W45nWzeX"U2uݻr CGGW9㜸z76zz/)onl5< i޲R[W+ZM&jSep2-IߓB5ʜ=*T-d5A@cWix7ޣ5+eQ¯ brdaeTXo R+hYF߬v,Vc-Ϊx}1YCSr:W#Tt5sx(bl<~g9gs}=Ԟe@geNg=촤 5n Nr,a:E"&GTݬF)SKZx]qJ{&/g[CN[B2)]*_.aG\퇍8*rcF*CHaH6$%T}SА݆mmwwja!:=*v&1'~F Lf|(@6)`=%tjh瀦uʵ$8) 65aSBi2Uɫڶ"- vM!*|0s|zisQ9}6,E:z:¾$ԑ~:Վ~P`9EyCdD>4ث(4(p% QJX1G/_qQ+I{jg Z)!tnE`jgR>~jڎ~ao/BQ}ǘnYI2ZG*8'P䥝pѸ%+g @k|F4F6k`1΂a͌-A=/цȝ@|NZ! h96fĤki|KQow2xyvm`"ifbs E#:>-"?WVүj|iL=+wׅCҨB7B>.di#iޣŒ0'p9|MsfuY|GS Fi?7Ayur󞙞mޚtͺbR@F8r/vX"6 `%[>EćG Bk` Ws!n#-J@Lv :Ҁ_QuE,EZ<ո|S/&[ j?%ӍV_܌ѱx(;)Hs%y7Ќpe|uѳҳ0f>Mw3q}CL4DLo y_N4}V2!b xw Kw!0gLPk֫6&%iָFob |RvV$yJ Q%\:XfWkvB . 9C? ʉu~0s' "yoǹesy>eL6{ň ™fqHǹ\c--K7 #͢s^ 1 JAVd>xqogH' Z>Fv"A^GKX#V1-I)}_?˲`oj@C?ȹ&41:&qнY/t4&dw ?yRO4ݬ<F xb$b6j+i2wd2v 1=$JVK5&!ؼ C"+7qGJ8/2'9z&?V]7y-t\ceN.|% eءvz|rr**Ҿ(MYÿ[KLP'i!b3BxyJOQf J$7b @)6IykjBB=slC)=ڟ0.he0/^m蔥yx\z}; H=BPqQYá-ዌ اmե#^LU̲B:ۚ'KJ ?N"nz" ;QA xoHxv811* +dĻ+BVp06V2Ŷp2G*2yPA0qK;@OguxUu{KOt`VK܃vFNeZּ{_, Y5<؅+SL Sxn1y9{8DkA"fyKKP]ahNw~׶`:<šQQ/BW; bƷM^lP=olZG}7ug(w[Q\qp~QBW̎^!2R,`Umxިi#|3DJX镔H ԃeэiFB$\EtХXwlCqr]كT TeiǕ(֠\=,h4ARkm/ %d}j>t |ٺ|UgqfB ~3wFsFד6l2ok&Di/>N-^hz]n,ca[<8CpT hBZԠ&-\iM#|!>}㗕YCH]9'c0p~;!DFwQ:u,HcoիD`B>yro:c yT~ǣ ? kVM 16 Xz8؝^em #XQ ^P!= ` @UoOL%pTޑwiX-ךl7| ^rg-o!-xs/'SGz5hICz3Z{C|`ʂAa\AO03̾۱8҄m+,4@k&8O:ظKQz% DXCض2'4"/5{0dE QA/#?0"fl .@$ckCu!H>`Yhzъ̥z*05bl:b(ZF~ 'k] w\҉1MO~)\6vшSy@yeXon@&5aN?~857t(+[6 G|ߑ*Yo71K-0/^œȃ~{|Wxu<{vq[b]i[nx2\7?]LϥHWKN]{ݍ1ǘI>@>]d!ȀKYłaFIkDzZ gXc~@!Pd1NYhwg0ÕL<r` q _V1&\O7mvG֖ɚKۖ*BRetlf1Y9|$'XP2YЯBd{7}ԖUqԨ3z=3%IwNcfy jn(܀'?}셶׽ 8j]EP_(|G:Y49<l #vjPЖs Vw iAk)*w../b'% SY3'@#hu<l۳Y,st =?- Zy;( YNX` fš R_ݮ!w;NWb9S5?^Ђ ǫ =}\l1/WJxڨ2CuX6^hQ80τL.}Mڨ*3voZ%0i顡~ (F;3Ғ24,4f^eߤn-t c&{ `bwxU0A}eby:ϰ/x"sa)~0;2sz Ouv Q!WN0jOܨHTGf!MN8%kI /&nhqXnv#[&z*k2,PCG[ehxK=1yd, bGљ`"y%>>@@NЄ|smx^뎿p~I9] ۴ᘙsgQHEHIӼPLWLY XfZhKifHw(vxUTM 6T\7Q/|6٤j U9}pt.˷ԋAS԰u7TDUlKAn)B746^sh._uCv@#/O yzkk*ajasY!OWOq'uKCs4{Vw:#iB |mw!N1m8o=9_*rLOFn:67*|*%-IS|RR$kpDY?=7{B! r;̈́cT0h_:fjHӔ)o父aP:DZ[i{tŗʹo~89Ύ-c ^yO@{f]xzOkC׀SWnfNnKb$;_ZnT#M*~ OZYT+GXz C͔pN&G"Nհ*nsyz%gyG!̶} ,vT%&i~ MW٩2z`\ҭzI@TIWȵ_k;BcԦTL_A,N3&Lˑ :">vqqnuʪ×TAB9g=~ ԰jݹѣ,ތIPEPw 2wk#jI<ܖ3Œz$ "uU/|FwD|:- &,٠z,nTAP-,zXL w>F}yMdž+A,ҁ"+B2vQTe՝vD"RkzJӜKvH{ P$7XC0\nakqCe?Vy,/\S>eXMOdzڎ-p%A#Wg3@d{ij+h]~}zjzӱI8,zo<@3dk9pB)YUX",*?{!ח3J5iΟ&᪩d5{ 0%թvQt9[*{/竭^9مy|G;({iSyZ855>gK?̏^xEhru.R;VkAe yNQIFX= c?5 iSA bͰ 4޼e & Y:t;/<;4LF;3wڋX[|Ľ}b ߒULQd8ՠݘ#KG^eȏljꞃ^ehX8u1Kj jr{1DDҌ)RKkq*QE|3Z(H;6#nZzm箂.w薥^'ȕxNՏ7DWaܔs 3Xa  ^__o1};0"wZ!AU}H^vw061I?(Hxcb*uӷG,RkX@M;D"0<5|6Yݖ93T[Gɤ-q B GU 6lkR*{ү}&YCE\ZWIσI))ت&Q6/`2t[렲a'U÷UЙFFYbEP<2hmYxu1n4DE7f.\a z&HipeOtbČD]9<_Ҭw) h5-[M.9 ܷw rH4Jbn;4^t>68M:`kޛ[Ɍ">n} LxYĒ%#qj!34rk7ع<"(5fJa聎q1S L#Ԁ:jL |L_L!P|KY?_ ȼPˢoChL5^ _+vAy$YgM.l>A"y+Ux].,R\S.h>t&R%\iEMV]jU _\_$XVn8ɷ,Pof %/k'+ qdq÷Zlw~m `h9:lXoEcrBft #iCSd-ߗBHD&<=&z8b.eH+kruƭ\ٴGz9B˞k7Ȉs#yGRUL9ᵶA@#sxd(X@;R_"F(TX6nƑC8Rtd(8tHt!{c3J4M_ u$I{`4U!H`>Ӎ+Ĺ;.CVv^H(fa4`ܵ["^\cwf3:̈[:)98QF(^/5rt !ӄBvL{ٚHk(sib:#Hsl,;m10r?}Y;7JC3U7z &rY+F[MvfUY|[Xڨ/h>x6BoU|']ul4Q6(QIRD5!Cho3[oDUx{'-XO%tO ~)ȉss+lcUð@D`'tvn(|GuA}䜏+MyTFhbC,gQMK{\C?{Q}*wȒ) Mމ&Cg7=GǃuK`aY{<+^Q;b, vئ{xQ0:Yh>RMz%a͎%|'Vh9v;m xsO̾%1ճlͣXݎ)Vb;JPVjf]$)4H}hJ♭+U3VK< Nx7}g՞LBx c,&l\ePdNSy/ei6r5dqʋQ~N6Kvt]ɓlTũ-QK'ZS (,T[Еk01͌?Bh}z# SRxf0Lƥ"d,0qtq1g *xqG$SJU:88ghV4z/&=WwʏE?Ae$tTPt[.vfn"_&7V 7^{u`}7AJ?miqe胾x=(w6\M2@>`Q!J?.n+=bvy]t%9^짊z6A7r Q\0>ޭE^{Xp.$?'wdL\.'T8нh{Y:y?x+,ql>>?Z,ڣOˈƊ/eT񪪭 rҷ|rG{H\b{4MddB~Xb>u"^fnF?)—nX,\Cu,t=4FNW8hҍ-S9OM5M,lnߩ*Q  .>dGAG'0Q8j"Zδw%'YŮo"]kuBi}o',:1|ӕtX-Nncj\aYIn;14S+٧=Fi&11oׯYsߦ.]2,oN+="\]#]eJ3QPlB68)2P$K395_6mȄrӄ1aϽ#<[ɍTV[bz?Xv72 S>o="O[s)Ψ5A_cCO3Hf+W ߭܏HuZJn%T&$.Ǔt\ޜd&m&<Pӎ(% |7DƵr5́;nɦhqDneTa\Qom5yr-uӧꯍcY=q}l6mPOq,`_Jp`#R@i|4EkXheZ[PrNRlrJ Ty1S#95G8R>'UH eilZqo)+'sDv2ZɺkDLc "h>$a"J=e/^QCWǎ}ۛuX(.8$֮GM5Y6o>H./N[n8uz.7%Gבۊo%'2" >H6e@:-O}+4U6܏ ^g`zo-hJk~C;c'a/XCT'鰑+Q6-2Sk4q8· #߸`ŝFbʘ;,;&䛧PZ"79;x \һ2${pL;M;&I[8Mxϡz$LypR^9jtTBK^f{؀yY+J[(.3ft<n!LB7"627'(z^?,W5f)agJqxhMmFK"*#-Bz.e9>ϫ@ |u){jBfQnj{ endstream endobj 215 0 obj << /Type /FontDescriptor /FontName /ZCMNAI+NimbusRomNo9L-Regu /Flags 4 /FontBBox [-168 -281 1000 924] /Ascent 678 /CapHeight 651 /Descent -216 /ItalicAngle 0 /StemV 85 /XHeight 450 /CharSet (/A/B/C/D/E/F/G/H/I/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/a/b/bar/bracketleft/bracketright/c/colon/comma/d/e/eight/endash/f/fi/five/fl/four/g/h/hyphen/i/j/k/l/m/n/nine/o/one/p/parenleft/parenright/period/plus/q/quotedbl/quoteright/r/registered/s/seven/six/t/three/two/u/underscore/v/w/x/y/z/zero) /FontFile 214 0 R >> endobj 216 0 obj << /Length1 1647 /Length2 16023 /Length3 0 /Length 16885 /Filter /FlateDecode >> stream xڬcx_&vvl۶mgǶtlmm;̙3y2s>}VݫZw)Phmoh#Ϡ tq3|apb.@7kGq7 /@ hXY,<<Yq B]aA{ONN滌W! ᪑·# qoZGӓFݡy:6v{rKs7l+;pL!_%`Oe5 qZKPQ14AIosLk2ݮq6qkL?A0N0.욇nH|E:M@:gb%[IqWE4l#|%Hz(Q8 ^+  1k+݀Ը޻lzh-Rm;(2>O4{cM—.i2umDѢ"`J M#ٴ>H #:a<A ~DF WuW\DD71xyA8[FD!#,8 Tc"Gj͐\kJ;=Ը DOЙh:'(ū0Ujy[ۍ^{(`j lȳ![U-fiPͬËͼXWWh#ЬbJ@kK20g~V6:(#UyQ92ӪYj-= %'hmP\S6K=ȻAob'T:֑&^) _c%E)tJ{|xԄƭEIg:4:߱x.9_N&/ʖծ]2.i´(#ID!9a;{ 5/V:sFl"bd` svy2I 6IPZwJEEtMa ɢ:A9FR2[g ¶!/mWk_ r'&V2{k{ S%RAιqxЃ .%w5KLqQE_Ǽdʍv8.5EOw,Q4%$:+u<:с]8[P'KZ-Z^~}H ח|Vu@5vYYںd+#z{5ɏX؞ SG Flwڝ4*^0D6=lrcGM4l~bHI\EB꫏MmX0 pD467pn8(ǰȩ1rYzMUJjj(+ݚ 2¯̚EIӄ"(d^EªO?8WP^ _`.8F3фkf)WȌBo!(&DjLкNti;Ӓ74UIP6+}I#U-SRܗ䩵 p :`I4K:tyA#4]i#!/G?J5]UhOJӆJZŅwNXPg}W ΁v֟!:$p' Ԡ-gO~jI"7&9ƞ [s@N0׻1MǕ, 60]s|xaNZ"G+{Q*imw[S䷻\MN*L^ F51Wn*v?^m,/ڑR-b\N7 *Ÿ[< &|[Ĺ7g ɚf";``q9bXn #|țE0)&Nţ?^ jִ)Me{ a2 kv q*{,JcѴlVRjO’{̀aSgVE*1wPʿpa#mHkٙϡu|UrTsZc3y?Zlruqž~QT#'J*I!D2gȺ_l_=t(QcCF L?9 WϢ^iNs"b#y>rq_WOej\g'T~{'+C3h5->ǷI˰#om@ -6eiM%ڮ 7tWd^9WDd}L9ip굑J7Y -K~G[$?2 ezIPMx>GM!2 gZd{ktGesBp;XL_MЈ'gNђ1˩LMhZ\8-1Omr!Cɩ3QF S" rtMeøLI=}rpuZ(q!7w٭ VdT/zߦTI ҭ8Ð9&Mn6/<{9kwnU~,{SWnŲdG鳅ߴAr,a|u*4F_я,a-#xYPCn7t+w}5YЬ-t0%|U+D;;oF"5Xqwٽ|,,ݻC*x4J+;h@v`-im甕 XEƜަ9V:yMlr-̺dg1[d@: sc@SM1 '-B|D1QQ랑Cvb8Fqjz%4.eݸ5յsިSĎ&V*2T-[9#7Mmty y:}* !a0kcxU9Ͼ۩6x.C:愩_!C]f]r F1229 Q5L3^6$)4H2ypoL@_j,ioI saa\Cl-nSV/UGoNHmk]ŏ)wwPJ܍,go^,Sz|=*-Q*|#Hӌ8jR|=1+Ɣx˭7<-qg/c"^5<~m&Qr6v-O5Џ0vZX *h'ߺY4g=vlT,rVXouBV3T6] u,}įt$V.҈" 'o>bWV9DWkr)6QJн؇l*:tX&I5 <\*זWJ ´VW<ß4^Ɏ{rxw CA *JkŴ޶ ( jd'}U|5ȆSȦoKG '[Oњgyz~MfX*JCv/Ofsڧ3a/Ik1 @la%Dw? @`1&ŒCԥv9cF}m~)cIնǃ0-Ztm.Z(\߇lfAFh!NbDJm³) !+"okc6RtF8xI=A1y|c90rjaUEډ-)|]1;**@^qVZDg{Cv,@XR\(` 9ޣt" !*sWNgq3rAHk*{6X0aYy0Ekhy{Mƶ%DA&E_tgu"#s WK"{! 3n:ZdPxӗ[2#uan:',ѦL&>j~zl~,/J7R5I??c`iӮ,r%Py!߇@4af=XP}&VD¸nWzbeۥ5<`!h')EDm,pBڕGGe A3Jp,qi'C|Qc&8ʏkfWٕZRu SۗkITrqfl.UtN7[7&6-\G&秿 W\<{2l9Z'IlP `vfJdm\CHss7f H%N}ҕfC4rqwJ0:b"|] XIMY{'DJb_=hO2 JBLwь)?3 fywj@Lm!,b\ \:J VnܷI;߾+A c!ݯDf3ƤZku˚ h5jh$dI962UhdUh{5S lu`ĎԒ& lڋwP w`,f&-k[8Ş|Gl2@ }Kl+;D6-?b4<;!%eXG/q -V6ؿ2~:Ok `N_I-yݵ; **hQMeqSX~G!#^P3#`ÚF#y2Lb-^~Vb\n٤$PXt w%軠Ҵe~P~ƣ]rfcgdY'*~SCEp s6.t.~U˴S[R.FOaN 1eVJFd]h!uآu lCќu1&0/0-Ƴ1ȋ1~4 Qd(M h+&6Yb;$̻'?وR +N@  Ϟc@[^)uh"" j}(ʒٸc8' Y jN21ˇ~3@iNI3E>_MN6\E[aBs̵b`hK.#8 +)|5)+W atO5,CW){!0& ɶ|u9-dp?YɡqVEc\VCO I2|:wY@95R{/徨 &y`t EKNE {nvx6[XjI]}`ǐX}IE׋JTueLrj˝?RgnFr #KK_ٹ#9IGS,a^LO4h=o9/u%nl)U%accM7$>~'9@5T(Qa{55[Ռt#T'Y>8EFG]Q^bϟ!Y?dɈuH؃ G l L Z>CFΏ=HOs]Qo=X'ï\߰.X|E2xkô-29Hɪ:U>\-3b^; #v->蚁 +BbV0Q8Փϓgwz@ Qd)w<Ōm+ƒ=9. "hD'nz_til0\êGӳfZS+6IMsK$Fiʇ%BB$m}o j N F;- #j~W.d&ԳO_%`Kiw+&Qc1PV-oq&CXoviz;){.&׽(پPZ{i?Œ V/7'0 -ԐI?tV1C7wq`iRsОyF6 Opm%xPL?s~$ww.e{Y?4 pתZzi1Tdaz}ZFqVVGY"dOĄn1~5AxP>+ !lkXDubff" .tAG_XM<`j~ҎҜ#9ȥ(ZN +Hi<" [u"c @Kwl&tiכX yz,N#vaQsa 0+CZg62=(6M%'NFDB%[=%MW>>b=0l$:t92kq p_\8DLf@g=s 㢩%I`&p.B2z{4c׹d&YnMؤ;BEM'P`ة@JALWN\HQg~9j8h:.ʁ"lɞ3`k?u( ;\a+vI A db\zlٕD:YEX4\_^ 3K /Rp*XdcL<h9̘ΐ(ֵҠǩv5=^A<JZ4Pz'4˳ڢz$TBOCJD*1B#R{ڬpn:b>+c"V`XW^ MC&H}àߚd.% H $7_#Ukjܨ/5P5HQE(0:֔x,aK.)2ʓg(fSMClSL'$1XL-Տ?,+lg@@ȨϬMZ-7(GݹwNaXL@sNMzY[2:`n)/Q{^5:ИU-W Ԏ 6ce[ۡȑhp|Vf18')cnAIθ,^aj~_P'.|_J ,A 7f;(@9:M`d+aq=':8A`E (>/1ŇQĺI6M(91y^LKٹNDd:7 3/4wd;,hwF?o]h -=E;ݳ֖N͠@S.!{ME9_o;MJwhi*9#V@ɛd}ho)'*sۃKyF6-*:g Rx4Ii{b9)JDa |쒠Uqmu8٤wfV{T}*{3tidFAlKlq0湒l܏:xP8"U(LUTWE5 uZ٧ct}(Y[94 qov_X(K&kQ`K+&c ->Dw[ړrTG>3&ߵ0ṟ*G$kn_GE(b`)q`p)1l<+`O]׶=ZLY_&orBy~qp!Z`2SU )~׮,-bFiP٥;L| FL3mlk4֮ˉ{S`>uR1烉jc O"~/(j5ž.#d\}ewx]zTf*En45Ѝ4>P(&?NB{/niPwSZZ4mH?>2~8{#!QV#禛Kŧº\@]:/2D$97U@pZ-_޿ŸEF.- ϹEt6|,^ bqئ~wA,r~dWz 9v{;޴nS*o V;+n; r)bPqT%Hv(̵a鴏h;Liۮ;(3$..<;sR*/nwq2HY]'^2Zb4Ru_qAN(QAR]q1C]3y3(7-َYkL9ɦɰˆ\H L%C35; xO<.ʖ=p#RF`ٛ=SWv3}̻E0D,5S9eSkD?CsNWqlD }m[m,-C (jm;x$!ƝjFٓ;/VK`9_6覌d|ycT9I9 IYGu߂@ݻ=?DMCNQ @5U!`- <^*: x+r_llFnIѸ FD! FsJfK3X9+SIV,5jadp$i2;8 r5 <֊t zuY#6RJ|K̭Pr>_vZзlG $gBnwwZ/4< N9ה_Ʋ ӏ3/rMRJCғɥGkƋu7y ¶h~:Y E\KvbMeb́S{( L࿱Vf2 l“eLkwɲ&KP0BI&D"-' G"Xb{\WW[Qą߹PįG1D0%XxD A`|%"_Cxl.gP}~P9c8ʠA'Ֆ"nK֊.^Z2EVt'wB֞r$r"T$HcT\mMK;&*n) a H$G \71iT/ S!'m; UBq\l,SD Y^4*#n-ӷ!da?{PV6N_Z^)ig*&7c=YDgظI>ab)se=·JmfvjJV{CR޴e ^|>ew"xr c`^z2zOOqzڐ A/<VOL6V^Ni0+jc-fJ8eOAEWԭE+lмčV/~[=R#"s̿aOo#"fjF2[dT(7>sVkT榘4 jBu1߷GJݚ%]XlPjPnmt?~lHAѐK&rRe_ C8*#DZ~"YsMGKu%V ᇆkuŹp& ;v\%~y54T'.ku `Iϡxj`wH-g1.JZ:9չjHTXwۖ~nZW*8R1_]CSI!_D ?@p]R6}IՎ|#Ӊ"NRRWAvehգ~>/yvߵ e^0~[ Lړ[ Fx ~_o:4Ѻ[מU xDRBA@Bș*hn&X)sa?v>e3%+-z{{}8raSoΐjoZH:x<|R#Lu%K0l02[(ɓ[Qt}vu#-&\ev$ڗQ'Jȕhw7qgF3&+`"^Cmhh4U[42渷&:Y@;^&[q`8_*6cJfjhOYt!K >Y uN&xUdD]~ lU0w/MMnL ; #j-Q'6q|w`s{f| T_`FmME)wtR POu'>%:ve9p:H2O 3E˕=b#wk8 6/o"@ySFky wQLpy8TV\ y~^$MEPHHnBL?A\VTWNI.1S3h<4&8YVNQ`UsK3ԛD8-,.`cIۂ`"],-ߚ7w!u'n=^uݣŸ/ Lӄ{9A0q2zg*{;nS/ua9"]e;b ct!0&vZ*މB,k~#gkli.FEx;|&lyGͦ~꧍Hh濳qlH2e3W Ė_:$2mlv($vtH?7j#ih3@Jc^wtMt7p޻B]@:i \7wWWQϼ~s9`sZn >sk rH3 O_eGNȱ:EGY 7+rn -κoT4#R* 9IQ PexqA ځpduAsO[[t̢_C4 j7 F)z8r^ oY:jfCϊDL=!1tf[j%n 2',w5ŴjQsVT7fZDϭv`灞".n}{|ёFfd1[闁-pWvfg6V%&8 e\ǺG<?$ Mn$;ޮdl(7] Y:oJGi~(Tuj.*6X PWH\<5^Yl_C0\^pD֩ffXgg2NלMsF4,K|~$!,L;ů(p *dPuӏ(4L;k{z@*a;nk۬ endstream endobj 217 0 obj << /Type /FontDescriptor /FontName /SLIUCQ+NimbusRomNo9L-ReguItal /Flags 4 /FontBBox [-169 -270 1010 924] /Ascent 668 /CapHeight 668 /Descent -193 /ItalicAngle -15 /StemV 78 /XHeight 441 /CharSet (/A/B/C/D/E/G/H/I/K/L/M/N/O/P/R/S/T/U/V/a/b/backslash/bar/braceleft/braceright/bracketleft/bracketright/c/comma/d/dollar/e/eight/endash/f/fi/five/four/g/h/hyphen/i/k/l/m/n/numbersign/o/one/p/parenleft/percent/period/quotedbl/quoteright/r/s/six/slash/t/three/two/u/underscore/v/w/y/zero) /FontFile 216 0 R >> endobj 199 0 obj << /Type /Encoding /Differences [2/fi/fl 34/quotedbl/numbersign/dollar/percent 39/quoteright/parenleft/parenright 43/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon 64/at/A/B/C/D/E/F/G/H/I 75/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y 91/bracketleft/backslash/bracketright 95/underscore 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde 150/endash 174/registered] >> endobj 193 0 obj << /Type /Encoding /Differences [58/period 75/K] >> endobj 197 0 obj << /Type /Encoding /Differences [161/minus] >> endobj 195 0 obj << /Type /Encoding /Differences [37/percent 49/one 52/four/five] >> endobj 144 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ULYUGM+CMMI10 /FontDescriptor 205 0 R /FirstChar 58 /LastChar 75 /Widths 194 0 R /Encoding 193 0 R >> endobj 143 0 obj << /Type /Font /Subtype /Type1 /BaseFont /KGLKDC+CMR10 /FontDescriptor 207 0 R /FirstChar 37 /LastChar 53 /Widths 196 0 R /Encoding 195 0 R >> endobj 99 0 obj << /Type /Font /Subtype /Type1 /BaseFont /FSFYYG+CMSY9 /FontDescriptor 209 0 R /FirstChar 161 /LastChar 161 /Widths 198 0 R /Encoding 197 0 R >> endobj 70 0 obj << /Type /Font /Subtype /Type1 /BaseFont /BMKUAM+NimbusMonL-Regu /FontDescriptor 211 0 R /FirstChar 45 /LastChar 126 /Widths 202 0 R /Encoding 199 0 R >> endobj 71 0 obj << /Type /Font /Subtype /Type1 /BaseFont /TGBWOE+NimbusRomNo9L-Medi /FontDescriptor 213 0 R /FirstChar 3 /LastChar 121 /Widths 201 0 R /Encoding 199 0 R >> endobj 69 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ZCMNAI+NimbusRomNo9L-Regu /FontDescriptor 215 0 R /FirstChar 2 /LastChar 174 /Widths 203 0 R /Encoding 199 0 R >> endobj 95 0 obj << /Type /Font /Subtype /Type1 /BaseFont /SLIUCQ+NimbusRomNo9L-ReguItal /FontDescriptor 217 0 R /FirstChar 2 /LastChar 150 /Widths 200 0 R /Encoding 199 0 R >> endobj 74 0 obj << /Type /Pages /Count 6 /Parent 218 0 R /Kids [58 0 R 82 0 R 92 0 R 112 0 R 151 0 R 176 0 R] >> endobj 192 0 obj << /Type /Pages /Count 1 /Parent 218 0 R /Kids [188 0 R] >> endobj 218 0 obj << /Type /Pages /Count 7 /Kids [74 0 R 192 0 R] >> endobj 219 0 obj << /Type /Outlines /First 7 0 R /Last 55 0 R /Count 10 >> endobj 55 0 obj << /Title 56 0 R /A 53 0 R /Parent 219 0 R /Prev 51 0 R >> endobj 51 0 obj << /Title 52 0 R /A 49 0 R /Parent 219 0 R /Prev 47 0 R /Next 55 0 R >> endobj 47 0 obj << /Title 48 0 R /A 45 0 R /Parent 219 0 R /Prev 43 0 R /Next 51 0 R >> endobj 43 0 obj << /Title 44 0 R /A 41 0 R /Parent 219 0 R /Prev 31 0 R /Next 47 0 R >> endobj 39 0 obj << /Title 40 0 R /A 37 0 R /Parent 31 0 R /Prev 35 0 R >> endobj 35 0 obj << /Title 36 0 R /A 33 0 R /Parent 31 0 R /Next 39 0 R >> endobj 31 0 obj << /Title 32 0 R /A 29 0 R /Parent 219 0 R /Prev 27 0 R /Next 43 0 R /First 35 0 R /Last 39 0 R /Count -2 >> endobj 27 0 obj << /Title 28 0 R /A 25 0 R /Parent 219 0 R /Prev 23 0 R /Next 31 0 R >> endobj 23 0 obj << /Title 24 0 R /A 21 0 R /Parent 219 0 R /Prev 15 0 R /Next 27 0 R >> endobj 19 0 obj << /Title 20 0 R /A 17 0 R /Parent 15 0 R >> endobj 15 0 obj << /Title 16 0 R /A 13 0 R /Parent 219 0 R /Prev 11 0 R /Next 23 0 R /First 19 0 R /Last 19 0 R /Count -1 >> endobj 11 0 obj << /Title 12 0 R /A 9 0 R /Parent 219 0 R /Prev 7 0 R /Next 15 0 R >> endobj 7 0 obj << /Title 8 0 R /A 5 0 R /Parent 219 0 R /Next 11 0 R >> endobj 220 0 obj << /Names [(Doc-Start) 68 0 R (Hfootnote.1) 72 0 R (Hfootnote.2) 73 0 R (Hfootnote.3) 86 0 R (Hfootnote.4) 107 0 R (Hfootnote.5) 168 0 R] /Limits [(Doc-Start) (Hfootnote.5)] >> endobj 221 0 obj << /Names [(cite.EDAC) 87 0 R (cite.acpi) 169 0 R (cite.apei-driver) 170 0 R (cite.intel-sdm) 77 0 R (cite.kleen2004) 76 0 R (cite.li2009) 89 0 R] /Limits [(cite.EDAC) (cite.li2009)] >> endobj 222 0 obj << /Names [(cite.schroeder2009) 88 0 R (cite.tang2006) 145 0 R (cite.xeon-ras) 75 0 R (figure.1) 85 0 R (figure.2) 96 0 R (lstlisting.-1) 154 0 R] /Limits [(cite.schroeder2009) (lstlisting.-1)] >> endobj 223 0 obj << /Names [(lstlisting.1) 97 0 R (lstlisting.2) 125 0 R (lstnumber.-1.1) 155 0 R (lstnumber.-1.10) 164 0 R (lstnumber.-1.11) 165 0 R (lstnumber.-1.12) 166 0 R] /Limits [(lstlisting.1) (lstnumber.-1.12)] >> endobj 224 0 obj << /Names [(lstnumber.-1.13) 167 0 R (lstnumber.-1.2) 156 0 R (lstnumber.-1.3) 157 0 R (lstnumber.-1.4) 158 0 R (lstnumber.-1.5) 159 0 R (lstnumber.-1.6) 160 0 R] /Limits [(lstnumber.-1.13) (lstnumber.-1.6)] >> endobj 225 0 obj << /Names [(lstnumber.-1.7) 161 0 R (lstnumber.-1.8) 162 0 R (lstnumber.-1.9) 163 0 R (lstnumber.1.1) 98 0 R (lstnumber.1.10) 116 0 R (lstnumber.1.11) 117 0 R] /Limits [(lstnumber.-1.7) (lstnumber.1.11)] >> endobj 226 0 obj << /Names [(lstnumber.1.12) 118 0 R (lstnumber.1.13) 119 0 R (lstnumber.1.14) 120 0 R (lstnumber.1.15) 121 0 R (lstnumber.1.16) 122 0 R (lstnumber.1.17) 123 0 R] /Limits [(lstnumber.1.12) (lstnumber.1.17)] >> endobj 227 0 obj << /Names [(lstnumber.1.18) 124 0 R (lstnumber.1.2) 100 0 R (lstnumber.1.3) 101 0 R (lstnumber.1.4) 102 0 R (lstnumber.1.5) 103 0 R (lstnumber.1.6) 104 0 R] /Limits [(lstnumber.1.18) (lstnumber.1.6)] >> endobj 228 0 obj << /Names [(lstnumber.1.7) 105 0 R (lstnumber.1.8) 115 0 R (lstnumber.1.9) 106 0 R (lstnumber.2.1) 126 0 R (lstnumber.2.10) 135 0 R (lstnumber.2.11) 136 0 R] /Limits [(lstnumber.1.7) (lstnumber.2.11)] >> endobj 229 0 obj << /Names [(lstnumber.2.12) 137 0 R (lstnumber.2.13) 138 0 R (lstnumber.2.14) 139 0 R (lstnumber.2.15) 140 0 R (lstnumber.2.16) 141 0 R (lstnumber.2.17) 142 0 R] /Limits [(lstnumber.2.12) (lstnumber.2.17)] >> endobj 230 0 obj << /Names [(lstnumber.2.2) 127 0 R (lstnumber.2.3) 128 0 R (lstnumber.2.4) 129 0 R (lstnumber.2.5) 130 0 R (lstnumber.2.6) 131 0 R (lstnumber.2.7) 132 0 R] /Limits [(lstnumber.2.2) (lstnumber.2.7)] >> endobj 231 0 obj << /Names [(lstnumber.2.8) 133 0 R (lstnumber.2.9) 134 0 R (page.1) 67 0 R (page.2) 84 0 R (page.3) 94 0 R (page.4) 114 0 R] /Limits [(lstnumber.2.8) (page.4)] >> endobj 232 0 obj << /Names [(page.5) 153 0 R (page.6) 178 0 R (page.7) 190 0 R (section*.1) 179 0 R (section.1) 6 0 R (section.10) 54 0 R] /Limits [(page.5) (section.10)] >> endobj 233 0 obj << /Names [(section.2) 10 0 R (section.3) 14 0 R (section.4) 22 0 R (section.5) 26 0 R (section.6) 30 0 R (section.7) 42 0 R] /Limits [(section.2) (section.7)] >> endobj 234 0 obj << /Names [(section.8) 46 0 R (section.9) 50 0 R (subsection.3.1) 18 0 R (subsection.6.1) 34 0 R (subsection.6.2) 38 0 R] /Limits [(section.8) (subsection.6.2)] >> endobj 235 0 obj << /Kids [220 0 R 221 0 R 222 0 R 223 0 R 224 0 R 225 0 R] /Limits [(Doc-Start) (lstnumber.1.11)] >> endobj 236 0 obj << /Kids [226 0 R 227 0 R 228 0 R 229 0 R 230 0 R 231 0 R] /Limits [(lstnumber.1.12) (page.4)] >> endobj 237 0 obj << /Kids [232 0 R 233 0 R 234 0 R] /Limits [(page.5) (subsection.6.2)] >> endobj 238 0 obj << /Kids [235 0 R 236 0 R 237 0 R] /Limits [(Doc-Start) (subsection.6.2)] >> endobj 239 0 obj << /Dests 238 0 R >> endobj 240 0 obj << /Type /Catalog /Pages 218 0 R /Outlines 219 0 R /Names 239 0 R /PageMode/UseOutlines /OpenAction 57 0 R >> endobj 241 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.3)/Keywords() /CreationDate (D:20100820203652+02'00') /ModDate (D:20100820203652+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX using libpoppler, Version 3.141592-1.40.3-2.2 (Web2C 7.5.6) kpathsea version 3.5.6) >> endobj xref 0 242 0000000001 65535 f 0000000002 00000 f 0000000003 00000 f 0000000004 00000 f 0000000000 00000 f 0000000015 00000 n 0000004633 00000 n 0000111514 00000 n 0000000060 00000 n 0000000086 00000 n 0000004691 00000 n 0000111428 00000 n 0000000131 00000 n 0000000162 00000 n 0000004750 00000 n 0000111303 00000 n 0000000208 00000 n 0000000251 00000 n 0000009821 00000 n 0000111242 00000 n 0000000302 00000 n 0000000325 00000 n 0000009880 00000 n 0000111154 00000 n 0000000371 00000 n 0000000420 00000 n 0000015237 00000 n 0000111066 00000 n 0000000466 00000 n 0000000492 00000 n 0000015296 00000 n 0000110941 00000 n 0000000538 00000 n 0000000573 00000 n 0000015415 00000 n 0000110867 00000 n 0000000624 00000 n 0000000656 00000 n 0000021088 00000 n 0000110793 00000 n 0000000707 00000 n 0000000741 00000 n 0000022244 00000 n 0000110705 00000 n 0000000787 00000 n 0000000824 00000 n 0000028058 00000 n 0000110617 00000 n 0000000870 00000 n 0000000909 00000 n 0000032926 00000 n 0000110529 00000 n 0000000955 00000 n 0000000984 00000 n 0000032986 00000 n 0000110454 00000 n 0000001031 00000 n 0000001060 00000 n 0000003581 00000 n 0000003743 00000 n 0000003898 00000 n 0000004051 00000 n 0000004204 00000 n 0000004359 00000 n 0000009089 00000 n 0000004926 00000 n 0000001110 00000 n 0000004515 00000 n 0000004574 00000 n 0000109773 00000 n 0000109431 00000 n 0000109601 00000 n 0000004809 00000 n 0000004868 00000 n 0000110121 00000 n 0000035658 00000 n 0000033107 00000 n 0000033227 00000 n 0000009240 00000 n 0000009393 00000 n 0000009551 00000 n 0000009997 00000 n 0000008934 00000 n 0000005020 00000 n 0000009702 00000 n 0000009761 00000 n 0000009939 00000 n 0000035718 00000 n 0000033167 00000 n 0000033287 00000 n 0000015025 00000 n 0000016069 00000 n 0000014891 00000 n 0000010079 00000 n 0000015178 00000 n 0000109945 00000 n 0000015355 00000 n 0000015474 00000 n 0000015533 00000 n 0000109270 00000 n 0000015592 00000 n 0000015652 00000 n 0000015712 00000 n 0000015772 00000 n 0000015832 00000 n 0000015891 00000 n 0000015951 00000 n 0000016010 00000 n 0000019951 00000 n 0000020104 00000 n 0000020265 00000 n 0000022304 00000 n 0000019797 00000 n 0000016175 00000 n 0000020418 00000 n 0000020479 00000 n 0000020540 00000 n 0000020601 00000 n 0000020662 00000 n 0000020723 00000 n 0000020784 00000 n 0000020845 00000 n 0000020906 00000 n 0000020967 00000 n 0000021027 00000 n 0000021148 00000 n 0000021209 00000 n 0000021270 00000 n 0000021331 00000 n 0000021392 00000 n 0000021453 00000 n 0000021513 00000 n 0000021574 00000 n 0000021635 00000 n 0000021696 00000 n 0000021757 00000 n 0000021818 00000 n 0000021879 00000 n 0000021940 00000 n 0000022001 00000 n 0000022061 00000 n 0000022122 00000 n 0000022183 00000 n 0000109110 00000 n 0000108949 00000 n 0000035475 00000 n 0000026680 00000 n 0000026834 00000 n 0000026985 00000 n 0000031366 00000 n 0000028179 00000 n 0000026526 00000 n 0000022425 00000 n 0000027144 00000 n 0000027205 00000 n 0000027266 00000 n 0000027327 00000 n 0000027388 00000 n 0000027449 00000 n 0000027510 00000 n 0000027571 00000 n 0000027632 00000 n 0000027693 00000 n 0000027753 00000 n 0000027814 00000 n 0000027875 00000 n 0000027936 00000 n 0000027997 00000 n 0000028118 00000 n 0000035597 00000 n 0000035536 00000 n 0000031523 00000 n 0000031869 00000 n 0000032066 00000 n 0000032457 00000 n 0000033347 00000 n 0000031172 00000 n 0000028312 00000 n 0000032865 00000 n 0000033046 00000 n 0000031696 00000 n 0000032262 00000 n 0000032663 00000 n 0000034410 00000 n 0000034608 00000 n 0000034777 00000 n 0000035233 00000 n 0000035778 00000 n 0000034239 00000 n 0000033454 00000 n 0000035414 00000 n 0000035005 00000 n 0000110234 00000 n 0000108731 00000 n 0000035861 00000 n 0000108864 00000 n 0000035982 00000 n 0000108800 00000 n 0000036087 00000 n 0000108280 00000 n 0000036112 00000 n 0000036682 00000 n 0000037151 00000 n 0000037498 00000 n 0000038160 00000 n 0000040128 00000 n 0000040357 00000 n 0000043027 00000 n 0000043267 00000 n 0000044476 00000 n 0000044701 00000 n 0000057712 00000 n 0000058079 00000 n 0000071360 00000 n 0000071716 00000 n 0000090236 00000 n 0000090753 00000 n 0000107759 00000 n 0000110311 00000 n 0000110379 00000 n 0000111586 00000 n 0000111780 00000 n 0000111983 00000 n 0000112197 00000 n 0000112420 00000 n 0000112648 00000 n 0000112872 00000 n 0000113098 00000 n 0000113318 00000 n 0000113539 00000 n 0000113765 00000 n 0000113983 00000 n 0000114163 00000 n 0000114337 00000 n 0000114517 00000 n 0000114698 00000 n 0000114816 00000 n 0000114931 00000 n 0000115022 00000 n 0000115116 00000 n 0000115154 00000 n 0000115281 00000 n trailer << /Size 242 /Root 240 0 R /Info 241 0 R /ID [<2B52798D7C5A2A29602ABD19D5C52FE9> <2B52798D7C5A2A29602ABD19D5C52FE9>] >> startxref 115612 %%EOF mcelog-100/memutil.c0000664000175000017500000000301712247142742014072 0ustar vorlonvorlon/* Copyright (C) 2008 Intel Corporation Author: Andi Kleen Memory allocation utilities mcelog 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; version 2. mcelog 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. You should find a copy of v2 of the GNU General Public License somewhere 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 #include #include #include #include #include #include "mcelog.h" #include "memutil.h" void Enomem(void) { Eprintf("out of memory"); exit(ENOMEM); } void *xalloc(size_t size) { void *m = calloc(1, size); if (!m) Enomem(); return m; } void *xalloc_nonzero(size_t size) { void *m = malloc(size); if (!m) Enomem(); return m; } void *xrealloc(void *old, size_t size) { void *m = realloc(old, size); if (!m) Enomem(); return m; } char *xstrdup(char *str) { str = strdup(str); if (!str) Enomem(); return str; } /* Override weak glibc version */ int asprintf(char **strp, const char *fmt, ...) { int n; va_list ap; va_start(ap, fmt); n = vasprintf(strp, fmt, ap); va_end(ap); if (n < 0) Enomem(); return n; }