/* $NetBSD: eehandlers.c,v 1.18 2013/07/02 11:59:46 joerg Exp $ */ /*- * Copyright (c) 1996 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" extern char *path_eeprom; extern int eval; extern int update_checksums; extern int ignore_checksum; extern int fix_checksum; extern int cksumfail; extern u_short writecount; static char err_str[BUFSIZE]; static void badval(const struct keytabent *, char *); static int doio(const struct keytabent *, u_char *, ssize_t, int); static u_char ee_checksum(u_char *, size_t); static void ee_hwupdate(const struct keytabent *, char *); static void ee_num8(const struct keytabent *, char *); static void ee_num16(const struct keytabent *, char *); static void ee_screensize(const struct keytabent *, char *); static void ee_truefalse(const struct keytabent *, char *); static void ee_bootdev(const struct keytabent *, char *); static void ee_kbdtype(const struct keytabent *, char *); static void ee_constype(const struct keytabent *, char *); static void ee_diagpath(const struct keytabent *, char *); static void ee_banner(const struct keytabent *, char *); static void ee_notsupp(const struct keytabent *, char *); static const struct keytabent eekeytab[] = { { "hwupdate", 0x10, ee_hwupdate }, { "memsize", 0x14, ee_num8 }, { "memtest", 0x15, ee_num8 }, { "scrsize", 0x16, ee_screensize }, { "watchdog_reboot", 0x17, ee_truefalse }, { "default_boot", 0x18, ee_truefalse }, { "bootdev", 0x19, ee_bootdev }, { "kbdtype", 0x1e, ee_kbdtype }, { "console", 0x1f, ee_constype }, { "keyclick", 0x21, ee_truefalse }, { "diagdev", 0x22, ee_bootdev }, { "diagpath", 0x28, ee_diagpath }, { "columns", 0x50, ee_num8 }, { "rows", 0x51, ee_num8 }, { "ttya_use_baud", 0x58, ee_truefalse }, { "ttya_baud", 0x59, ee_num16 }, { "ttya_no_rtsdtr", 0x5b, ee_truefalse }, { "ttyb_use_baud", 0x60, ee_truefalse }, { "ttyb_baud", 0x61, ee_num16 }, { "ttyb_no_rtsdtr", 0x63, ee_truefalse }, { "banner", 0x68, ee_banner }, { "secure", 0, ee_notsupp }, { "bad_login", 0, ee_notsupp }, { "password", 0, ee_notsupp }, { NULL, 0, ee_notsupp }, }; #define BARF(kt) { \ badval((kt), arg); \ ++eval; \ return; \ } #define FAILEDREAD(kt) { \ warnx("%s", err_str); \ warnx("failed to read field `%s'", (kt)->kt_keyword); \ ++eval; \ return; \ } #define FAILEDWRITE(kt) { \ warnx("%s", err_str); \ warnx("failed to update field `%s'", (kt)->kt_keyword); \ ++eval; \ return; \ } void ee_action(char *keyword, char *arg) { const struct keytabent *ktent; for (ktent = eekeytab; ktent->kt_keyword != NULL; ++ktent) { if (strcmp(ktent->kt_keyword, keyword) == 0) { (*ktent->kt_handler)(ktent, arg); return; } } warnx("unknown keyword %s", keyword); ++eval; } void ee_dump(void) { const struct keytabent *ktent; for (ktent = eekeytab; ktent->kt_keyword != NULL; ++ktent) (*ktent->kt_handler)(ktent, NULL); } static void ee_hwupdate(const struct keytabent *ktent, char *arg) { uint32_t hwtime; time_t t; char *cp, *cp2; if (arg) { if ((strcmp(arg, "now") == 0) || (strcmp(arg, "today") == 0)) { if ((t = time(NULL)) == (time_t)(-1)) { warnx("can't get current time"); ++eval; return; } } else if ((t = parsedate(arg, NULL, NULL)) == (time_t)(-1)) BARF(ktent); hwtime = (uint32_t)t; /* XXX 32 bit time_t on hardware */ if (hwtime != t) warnx("time overflow"); if (doio(ktent, (u_char *)&hwtime, sizeof(hwtime), IO_WRITE)) FAILEDWRITE(ktent); } else { if (doio(ktent, (u_char *)&hwtime, sizeof(hwtime), IO_READ)) FAILEDREAD(ktent); t = (time_t)hwtime; /* XXX 32 bit time_t on hardware */ } cp = ctime(&t); if (cp != NULL && (cp2 = strrchr(cp, '\n')) != NULL) *cp2 = '\0'; printf("%s=%" PRId64, ktent->kt_keyword, (int64_t)t); if (cp != NULL) printf(" (%s)", cp); printf("\n"); } static void ee_num8(const struct keytabent *ktent, char *arg) { u_char num8 = 0; u_int num32; int i; if (arg) { for (i = 0; i < (int)strlen(arg) - 1; ++i) if (!isdigit((unsigned char)arg[i])) BARF(ktent); num32 = atoi(arg); if (num32 > 0xff) BARF(ktent); num8 += num32; if (doio(ktent, &num8, sizeof(num8), IO_WRITE)) FAILEDWRITE(ktent); } else if (doio(ktent, &num8, sizeof(num8), IO_READ)) FAILEDREAD(ktent); printf("%s=%d\n", ktent->kt_keyword, num8); } static void ee_num16(const struct keytabent *ktent, char *arg) { u_int16_t num16 = 0; u_int num32; int i; if (arg) { for (i = 0; i < (int)strlen(arg) - 1; ++i) if (!isdigit((unsigned char)arg[i])) BARF(ktent); num32 = atoi(arg); if (num32 > 0xffff) BARF(ktent); num16 += num32; if (doio(ktent, (u_char *)&num16, sizeof(num16), IO_WRITE)) FAILEDWRITE(ktent); } else if (doio(ktent, (u_char *)&num16, sizeof(num16), IO_READ)) FAILEDREAD(ktent); printf("%s=%d\n", ktent->kt_keyword, num16); } static const struct strvaltabent scrsizetab[] = { { "1152x900", EE_SCR_1152X900 }, { "1024x1024", EE_SCR_1024X1024 }, { "1600x1280", EE_SCR_1600X1280 }, { "1440x1440", EE_SCR_1440X1440 }, { NULL, 0 }, }; static void ee_screensize(const struct keytabent *ktent, char *arg) { const struct strvaltabent *svp; u_char scsize; if (arg) { for (svp = scrsizetab; svp->sv_str != NULL; ++svp) if (strcmp(svp->sv_str, arg) == 0) break; if (svp->sv_str == NULL) BARF(ktent); scsize = svp->sv_val; if (doio(ktent, &scsize, sizeof(scsize), IO_WRITE)) FAILEDWRITE(ktent); } else { if (doio(ktent, &scsize, sizeof(scsize), IO_READ)) FAILEDREAD(ktent); for (svp = scrsizetab; svp->sv_str != NULL; ++svp) if (svp->sv_val == scsize) break; if (svp->sv_str == NULL) { warnx("unknown %s value %d", ktent->kt_keyword, scsize); return; } } printf("%s=%s\n", ktent->kt_keyword, svp->sv_str); } static const struct strvaltabent truthtab[] = { { "true", EE_TRUE }, { "false", EE_FALSE }, { NULL, 0 }, }; static void ee_truefalse(const struct keytabent *ktent, char *arg) { const struct strvaltabent *svp; u_char truth; if (arg) { for (svp = truthtab; svp->sv_str != NULL; ++svp) if (strcmp(svp->sv_str, arg) == 0) break; if (svp->sv_str == NULL) BARF(ktent); truth = svp->sv_val; if (doio(ktent, &truth, sizeof(truth), IO_WRITE)) FAILEDWRITE(ktent); } else { if (doio(ktent, &truth, sizeof(truth), IO_READ)) FAILEDREAD(ktent); for (svp = truthtab; svp->sv_str != NULL; ++svp) if (svp->sv_val == truth) break; if (svp->sv_str == NULL) { warnx("unknown truth value 0x%x for %s", truth, ktent->kt_keyword); return; } } printf("%s=%s\n", ktent->kt_keyword, svp->sv_str); } static void ee_bootdev(const struct keytabent *ktent, char *arg) { u_char dev[5]; int i; size_t arglen; char *cp; if (arg) { /* * The format of the string we accept is the following: * cc(n,n,n) * where: * c -- an alphabetical character [a-z] * n -- a number in hexadecimal, between 0 and ff, * with no leading `0x'. */ arglen = strlen(arg); if (arglen < 9 || arglen > 12 || arg[2] != '(' || arg[arglen - 1] != ')') BARF(ktent); /* Handle the first 2 letters. */ for (i = 0; i < 2; ++i) { if (arg[i] < 'a' || arg[i] > 'z') BARF(ktent); dev[i] = (u_char)arg[i]; } /* Handle the 3 `0x'-less hex values. */ cp = &arg[3]; for (i = 2; i < 5; ++i) { if (*cp == '\0') BARF(ktent); if (*cp >= '0' && *cp <= '9') dev[i] = *cp++ - '0'; else if (*cp >= 'a' && *cp <= 'f') dev[i] = 10 + (*cp++ - 'a'); else BARF(ktent); /* Deal with a second digit. */ if (*cp >= '0' && *cp <= '9') { dev[i] <<= 4; dev[i] &= 0xf0; dev[i] += *cp++ - '0'; } else if (*cp >= 'a' && *cp <= 'f') { dev[i] <<= 4; dev[i] &= 0xf0; dev[i] += 10 + (*cp++ - 'a'); } /* Ensure we have the correct delimiter. */ if ((*cp == ',' && i < 4) || (*cp == ')' && i == 4)) { ++cp; continue; } else BARF(ktent); } if (doio(ktent, (u_char *)&dev[0], sizeof(dev), IO_WRITE)) FAILEDWRITE(ktent); } else if (doio(ktent, (u_char *)&dev[0], sizeof(dev), IO_READ)) FAILEDREAD(ktent); printf("%s=%c%c(%x,%x,%x)\n", ktent->kt_keyword, dev[0], dev[1], dev[2], dev[3], dev[4]); } static void ee_kbdtype(const struct keytabent *ktent, char *arg) { u_char kbd = 0; u_int kbd2; int i; if (arg) { for (i = 0; i < (int)strlen(arg) - 1; ++i) if (!isdigit((unsigned char)arg[i])) BARF(ktent); kbd2 = atoi(arg); if (kbd2 > 0xff) BARF(ktent); kbd += kbd2; if (doio(ktent, &kbd, sizeof(kbd), IO_WRITE)) FAILEDWRITE(ktent); } else if (doio(ktent, &kbd, sizeof(kbd), IO_READ)) FAILEDREAD(ktent); printf("%s=%d (%s)\n", ktent->kt_keyword, kbd, kbd ? "other" : "Sun"); } static const struct strvaltabent constab[] = { { "b&w", EE_CONS_BW }, { "ttya", EE_CONS_TTYA }, { "ttyb", EE_CONS_TTYB }, { "color", EE_CONS_COLOR }, { "p4opt", EE_CONS_P4OPT }, { NULL, 0 }, }; static void ee_constype(const struct keytabent *ktent, char *arg) { const struct strvaltabent *svp; u_char cons; if (arg) { for (svp = constab; svp->sv_str != NULL; ++svp) if (strcmp(svp->sv_str, arg) == 0) break; if (svp->sv_str == NULL) BARF(ktent); cons = svp->sv_val; if (doio(ktent, &cons, sizeof(cons), IO_WRITE)) FAILEDWRITE(ktent); } else { if (doio(ktent, &cons, sizeof(cons), IO_READ)) FAILEDREAD(ktent); for (svp = constab; svp->sv_str != NULL; ++svp) if (svp->sv_val == cons) break; if (svp->sv_str == NULL) { warnx("unknown type 0x%x for %s", cons, ktent->kt_keyword); return; } } printf("%s=%s\n", ktent->kt_keyword, svp->sv_str); } static void ee_diagpath(const struct keytabent *ktent, char *arg) { char path[40]; memset(path, 0, sizeof(path)); if (arg) { if (strlen(arg) > sizeof(path)) BARF(ktent); memcpy(path, arg, sizeof path); if (doio(ktent, (u_char *)&path[0], sizeof(path), IO_WRITE)) FAILEDWRITE(ktent); } else if (doio(ktent, (u_char *)&path[0], sizeof(path), IO_READ)) FAILEDREAD(ktent); printf("%s=%s\n", ktent->kt_keyword, path); } static void ee_banner(const struct keytabent *ktent, char *arg) { char string[80]; u_char enable; struct keytabent kt; kt.kt_keyword = "enable_banner"; kt.kt_offset = EE_BANNER_ENABLE_LOC; kt.kt_handler = ee_notsupp; memset(string, '\0', sizeof(string)); if (arg) { if (strlen(arg) > sizeof(string)) BARF(ktent); if (*arg != '\0') { enable = EE_TRUE; memcpy(string, arg, sizeof string); if (doio(ktent, (u_char *)string, sizeof(string), IO_WRITE)) FAILEDWRITE(ktent); } else { enable = EE_FALSE; if (doio(ktent, (u_char *)string, sizeof(string), IO_READ)) FAILEDREAD(ktent); } if (doio(&kt, &enable, sizeof(enable), IO_WRITE)) FAILEDWRITE(&kt); } else { if (doio(ktent, (u_char *)string, sizeof(string), IO_READ)) FAILEDREAD(ktent); if (doio(&kt, &enable, sizeof(enable), IO_READ)) FAILEDREAD(&kt); } printf("%s=%s (%s)\n", ktent->kt_keyword, string, enable == EE_TRUE ? "enabled" : "disabled"); } /* ARGSUSED */ static void ee_notsupp(const struct keytabent *ktent, char *arg) { warnx("field `%s' not yet supported", ktent->kt_keyword); } static void badval(const struct keytabent *ktent, char *arg) { warnx("inappropriate value `%s' for field `%s'", arg, ktent->kt_keyword); } static int doio(const struct keytabent *ktent, u_char *buf, ssize_t len, int wr) { int fd, rval = 0; u_char *buf2; buf2 = (u_char *)calloc(1, len); if (buf2 == NULL) { memcpy(err_str, "memory allocation failed", sizeof err_str); return (1); } fd = open(path_eeprom, wr == IO_WRITE ? O_RDWR : O_RDONLY, 0640); if (fd < 0) { (void)snprintf(err_str, sizeof err_str, "open: %s: %s", path_eeprom, strerror(errno)); free(buf2); return (1); } if (lseek(fd, (off_t)ktent->kt_offset, SEEK_SET) < (off_t)0) { (void)snprintf(err_str, sizeof err_str, "lseek: %s: %s", path_eeprom, strerror(errno)); rval = 1; goto done; } if (read(fd, buf2, len) != len) { (void)snprintf(err_str, sizeof err_str, "read: %s: %s", path_eeprom, strerror(errno)); return (1); } if (wr == IO_WRITE) { if (memcmp(buf, buf2, len) == 0) goto done; if (lseek(fd, (off_t)ktent->kt_offset, SEEK_SET) < (off_t)0) { (void)snprintf(err_str, sizeof err_str, "lseek: %s: %s", path_eeprom, strerror(errno)); rval = 1; goto done; } ++update_checksums; if (write(fd, buf, len) < 0) { (void)snprintf(err_str, sizeof err_str, "write: %s: %s", path_eeprom, strerror(errno)); rval = 1; goto done; } } else memmove(buf, buf2, len); done: free(buf2); (void)close(fd); return (rval); } /* * Read from eeLastHwUpdate to just before eeReserved. Calculate * a checksum, and deposit 3 copies of it sequentially starting at * eeChecksum[0]. Increment the write count, and deposit 3 copies * of it sequentially starting at eeWriteCount[0]. */ void ee_updatechecksums(void) { struct keytabent kt; u_char checkme[EE_SIZE - EE_HWUPDATE_LOC]; u_char checksum; int i; kt.kt_keyword = "eeprom contents"; kt.kt_offset = EE_HWUPDATE_LOC; kt.kt_handler = ee_notsupp; if (doio(&kt, checkme, sizeof(checkme), IO_READ)) { cksumfail = 1; FAILEDREAD(&kt); } checksum = ee_checksum(checkme, sizeof(checkme)); kt.kt_keyword = "eeprom checksum"; for (i = 0; i < 4; ++i) { kt.kt_offset = EE_CKSUM_LOC + (i * sizeof(checksum)); if (doio(&kt, &checksum, sizeof(checksum), IO_WRITE)) { cksumfail = 1; FAILEDWRITE(&kt); } } kt.kt_keyword = "eeprom writecount"; for (i = 0; i < 4; ++i) { kt.kt_offset = EE_WC_LOC + (i * sizeof(writecount)); if (doio(&kt, (u_char *)&writecount, sizeof(writecount), IO_WRITE)) { cksumfail = 1; FAILEDWRITE(&kt); } } } void ee_verifychecksums(void) { struct keytabent kt; u_char checkme[EE_SIZE - EE_HWUPDATE_LOC]; u_char checksum, ochecksum[3]; u_short owritecount[3]; /* * Verify that the EEPROM's write counts match, and update the * global copy for use later. */ kt.kt_keyword = "eeprom writecount"; kt.kt_offset = EE_WC_LOC; kt.kt_handler = ee_notsupp; if (doio(&kt, (u_char *)&owritecount, sizeof(owritecount), IO_READ)) { cksumfail = 1; FAILEDREAD(&kt); } if (owritecount[0] != owritecount[1] || owritecount[0] != owritecount[2]) { warnx("eeprom writecount mismatch %s", ignore_checksum ? "(ignoring)" : (fix_checksum ? "(fixing)" : "")); if (!ignore_checksum && !fix_checksum) { cksumfail = 1; return; } writecount = MAXIMUM(owritecount[0], owritecount[1]); writecount = MAXIMUM(writecount, owritecount[2]); } else writecount = owritecount[0]; /* * Verify that the EEPROM's checksums match and are correct. */ kt.kt_keyword = "eeprom checksum"; kt.kt_offset = EE_CKSUM_LOC; if (doio(&kt, ochecksum, sizeof(ochecksum), IO_READ)) { cksumfail = 1; FAILEDREAD(&kt); } if (ochecksum[0] != ochecksum[1] || ochecksum[0] != ochecksum[2]) { warnx("eeprom checksum mismatch %s", ignore_checksum ? "(ignoring)" : (fix_checksum ? "(fixing)" : "")); if (!ignore_checksum && !fix_checksum) { cksumfail = 1; return; } } kt.kt_keyword = "eeprom contents"; kt.kt_offset = EE_HWUPDATE_LOC; if (doio(&kt, checkme, sizeof(checkme), IO_READ)) { cksumfail = 1; FAILEDREAD(&kt); } checksum = ee_checksum(checkme, sizeof(checkme)); if (ochecksum[0] != checksum) { warnx("eeprom checksum incorrect %s", ignore_checksum ? "(ignoring)" : (fix_checksum ? "(fixing)" : "")); if (!ignore_checksum && !fix_checksum) { cksumfail = 1; return; } } if (fix_checksum) ee_updatechecksums(); } static u_char ee_checksum(u_char *area, size_t len) { u_char sum = 0; while (len--) sum += *area++; return (0x100 - sum); }