Evgeny Zinoviev has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/33052
Change subject: util/smctool: A tool for reading Apple SMC keys ......................................................................
util/smctool: A tool for reading Apple SMC keys
SMC is an embedded controller on Apple computers. With this tool you can read SMC keys. It supports various data types and can output result in various formats.
Example how to get battery capacity level:
smctool -k B0FC -t ui16 # returns Full Capacity of Battery 0 smctool -k B0RM -t ui16 # returns Remaining Capacity of Batery 0
So B0RM / B0FC * 100 = battery capacity in percents.
Another example, reading current fan speed, which is returned as unsigned fixed point number:
sudo ./smctool -k F0Ac -t fpe2
Change-Id: I6161203e52b7f7eaa1205de06e8241d7d40a7481 Signed-off-by: Evgeny Zinoviev me@ch1p.io --- A util/smctool/Makefile A util/smctool/description.md A util/smctool/smc.c A util/smctool/smc.h A util/smctool/smctool.c 5 files changed, 538 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/52/33052/1
diff --git a/util/smctool/Makefile b/util/smctool/Makefile new file mode 100644 index 0000000..ce7cca2 --- /dev/null +++ b/util/smctool/Makefile @@ -0,0 +1,38 @@ +## +## Makefile for smctool +## +## Copyright (C) 2019 Evgeny Zinoviev me@ch1p.io +## +## 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; version 2 of the License. +## +## 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. +## + +CC = gcc +CFLAGS = -O2 -Wall -W +PROGRAM = smctool +INSTALL = /usr/bin/env install +PREFIX = /usr/local + +all: $(PROGRAM) + +$(PROGRAM): smc.o smctool.o + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +install: $(PROGRAM) + $(INSTALL) $(PROGRAM) $(PREFIX)/sbin + +clean: + rm -f *.o $(PROGRAM) + +distclean: clean + +%.o: %.c + $(CC) $(CFLAGS) -c $^ -I. -o $@ + +.PHONY: all install clean distclean diff --git a/util/smctool/description.md b/util/smctool/description.md new file mode 100644 index 0000000..37a2d2a --- /dev/null +++ b/util/smctool/description.md @@ -0,0 +1 @@ +Reads keys from Apple SMC. Supports various data types. `C` diff --git a/util/smctool/smc.c b/util/smctool/smc.c new file mode 100644 index 0000000..a875b2c --- /dev/null +++ b/util/smctool/smc.c @@ -0,0 +1,156 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 Evgeny Zinoviev me@ch1p.io + * + * SMC interacting code is based on applesmc linux driver by: + * Copyright (C) 2007 Nicolas Boichat nicolas@boichat.ch + * Copyright (C) 2010 Henrik Rydberg rydberg@euromail.se + * + * 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; version 2 of the License. + * + * 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. + */ + +#include <stdlib.h> +#include <sys/io.h> +#include <errno.h> +#include <stdio.h> +#include "smc.h" + +/* + * wait_read - Wait for a byte to appear on SMC port. Callers must + * hold applesmc_lock. + */ +int wait_read(void) +{ + uint8_t status; + int us; + for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { + usleep(us); + status = inb(APPLESMC_CMD_PORT); + + /* read: wait for smc to settle */ + if (status & 0x01) + return 0; + } + + fprintf(stderr, "%s() fail: 0x%02x\n", __func__, status); + return -EIO; +} + +/* + * send_byte - Write to SMC port, retrying when necessary. Callers + * must hold applesmc_lock. + */ +int send_byte(uint8_t cmd, uint16_t port) +{ + uint8_t status; + int us; + + outb(cmd, port); + for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { + usleep(us); + status = inb(APPLESMC_CMD_PORT); + /* write: wait for smc to settle */ + if (status & 0x02) + continue; + /* ready: cmd accepted, return */ + if (status & 0x04) + return 0; + /* timeout: give up */ + if (us << 1 == APPLESMC_MAX_WAIT) + break; + /* busy: long wait and resend */ + usleep(APPLESMC_RETRY_WAIT); + outb(cmd, port); + } + + fprintf(stderr, "%s(0x%02x, 0x%04x) fail: 0x%02x\n", + __func__, cmd, port, status); + return -EIO; +} + +int send_command(uint8_t cmd) +{ + return send_byte(cmd, APPLESMC_CMD_PORT); +} + +int send_argument(const char *key) +{ + int i; + + for (i = 0; i < 4; i++) + if (send_byte(key[i], APPLESMC_DATA_PORT)) + return -EIO; + return 0; +} + +int read_smc(uint8_t cmd, const char *key, uint8_t *buffer, uint8_t len) +{ + uint8_t status, data = 0; + int i; + + if (send_command(cmd) || send_argument(key)) { + fprintf(stderr, "%.4s: read arg fail\n", key); + return -EIO; + } + + /* This has no effect on newer (2012) SMCs */ + if (send_byte(len, APPLESMC_DATA_PORT)) { + fprintf(stderr, "%.4s: read len fail\n", key); + return -EIO; + } + + for (i = 0; i < len; i++) { + if (wait_read()) { + fprintf(stderr, "%.4s: read data[%d] fail\n", key, i); + return -EIO; + } + buffer[i] = inb(APPLESMC_DATA_PORT); + } + + /* Read the data port until bit0 is cleared */ + for (i = 0; i < 16; i++) { + usleep(APPLESMC_MIN_WAIT); + status = inb(APPLESMC_CMD_PORT); + if (!(status & 0x01)) + break; + data = inb(APPLESMC_DATA_PORT); + } + + if (i) + fprintf(stderr, "flushed %d bytes, last value is: %d\n", + i, data); + + return 0; +} + +int write_smc(uint8_t cmd, const char *key, const uint8_t *buffer, uint8_t len) +{ + int i; + + if (send_command(cmd) || send_argument(key)) { + fprintf(stderr, "%s: write arg fail\n", key); + return -EIO; + } + + if (send_byte(len, APPLESMC_DATA_PORT)) { + fprintf(stderr, "%.4s: write len fail\n", key); + return -EIO; + } + + for (i = 0; i < len; i++) { + if (send_byte(buffer[i], APPLESMC_DATA_PORT)) { + fprintf(stderr, "%s: write data fail\n", key); + return -EIO; + } + } + + return 0; +} diff --git a/util/smctool/smc.h b/util/smctool/smc.h new file mode 100644 index 0000000..dffd8db --- /dev/null +++ b/util/smctool/smc.h @@ -0,0 +1,45 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 Evgeny Zinoviev me@ch1p.io + * + * 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; version 2 of the License. + * + * 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. + */ + +#ifndef SMC_H +#define SMC_H + +#include <stdbool.h> +#include <unistd.h> +#include <stdint.h> + +/* data port used by Apple SMC */ +#define APPLESMC_DATA_PORT 0x300 +/* command/status port used by Apple SMC */ +#define APPLESMC_CMD_PORT 0x304 + +#define APPLESMC_MAX_DATA_LENGTH 32 + +/* wait up to 128 ms for a status change. */ +#define APPLESMC_MIN_WAIT 0x0010 +#define APPLESMC_RETRY_WAIT 0x0100 +#define APPLESMC_MAX_WAIT 0x20000 + +#define APPLESMC_READ_CMD 0x10 +#define APPLESMC_WRITE_CMD 0x11 + +int wait_read(void); +int send_byte(uint8_t cmd, uint16_t port); +int send_command(uint8_t cmd); +int send_argument(const char *key); +int read_smc(uint8_t cmd, const char *key, uint8_t *buffer, uint8_t len); +int write_smc(uint8_t cmd, const char *key, const uint8_t *buffer, uint8_t len); + +#endif /* SMC_H */ diff --git a/util/smctool/smctool.c b/util/smctool/smctool.c new file mode 100644 index 0000000..fda7904 --- /dev/null +++ b/util/smctool/smctool.c @@ -0,0 +1,298 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 Evgeny Zinoviev me@ch1p.io + * + * 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; version 2 of the License. + * + * 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. + */ + +#include <getopt.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <endian.h> +#include <sys/io.h> +#include <stdio.h> +#include <errno.h> +#include "smc.h" +#include "smctool.h" + +static bool fp_bits(char c, uint8_t *dest) +{ + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) { + *dest = strtol(&c, NULL, 16); + return true; + } + return false; +} + +static void print_bits(uint8_t len, uint8_t *ptr) +{ + for (int i = len - 1; i >= 0; i--) { + for (int j = 7; j >= 0; j--) + printf("%u", (ptr[i] >> j) & 1); + + if (i > 0) + printf(" "); + } +} + +static void print_usage(const char *name) +{ + printf("usage: %s <options>\n", name); + printf("\n" + "Options:\n" + " -h, --help: print this help\n" + " -k, --key <name>: key name\n" + " -t, --type <type>: data type, see below\n" + " --output-hex\n" + " --output-bin\n" + "\n" + "Supported data types:\n" + " ui8, ui16, ui32, si8, si16, flag, fpXY, spXY\n" + "\n" + " fp and sp are unsigned and signed fixed point\n" + " data types respectively.\n" + "\n" + " The X in fp and sp data types is integer bits count\n" + " and Y is fraction bits count.\n" + "\n" + " For example,\n" + " fpe2 means 14 integer bits, 2 fraction bits,\n" + " sp78 means 7 integer bits, 8 fraction bits\n" + " (and one sign bit).\n" + "\n"); +} + + +enum output_format {dec, hex, bin}; + +enum smctype {none, ui8, ui16, ui32, si8, si16, flag, fp, sp}; +struct { + enum smctype type; + uint8_t len; + char *str; + char *decfmt; + char *hexfmt; +} types[] = { + {ui8, 1, "ui8", "%u", "0x%02x"}, + {ui16, 2, "ui16", "%u", "0x%04x"}, + {ui32, 4, "ui32", "%u", "0x%08x"}, + {si8, 1, "si8", "%d", "0x%02x"}, + {si16, 2, "si16", "%d", "0x%04x"}, + {flag, 1, "flag", "%u", "0x%01x"}, +}; + +int main(int argc, char *argv[]) +{ + bool show_help = false; + int opt, option_index = 0; + char name[KEYBUFSIZE+1] = {0}; + char typebuf[KEYBUFSIZE+1] = {0}; + uint8_t fp_int_bits = 0, fp_fraction_bits = 0; + + enum output_format of = dec; + char *ofmt = NULL; + char fmtbuf[32]; + + enum smctype type = none; + uint8_t len; + + struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"key", 1, 0, 'k'}, + {"type", 1, 0, 't'}, + {"output-hex", 0, 0, 1000}, + {"output-bin", 0, 0, 1001}, + {0, 0, 0, 0} + }; + + if (argv[1] == NULL) { + print_usage(argv[0]); + exit(0); + } + + while ((opt = getopt_long(argc, argv, "hk:t:", + long_options, &option_index)) != EOF) { + switch (opt) { + case 'h': + show_help = true; + break; + + case 'k': + snprintf(name, KEYBUFSIZE, "%s", optarg); + break; + + case 't': + snprintf(typebuf, KEYBUFSIZE, "%s", optarg); + break; + + case 1000: // output-hex + of = hex; + break; + + case 1001: // output-bin + of = bin; + break; + } + } + + if (optind < argc) { + fprintf(stderr, "Error: Extra parameter found.\n"); + print_usage(argv[0]); + exit(1); + } + + if (show_help) { + print_usage(argv[0]); + exit(0); + } + + /* Validate key name */ + if (strlen(name) != 4) { + fprintf(stderr, "Key name must be 4 characters long.\n"); + exit(1); + } + + /* Validate key type */ + if ((typebuf[0] == 'f' || typebuf[0] == 's') && typebuf[1] == 'p') { + if (!fp_bits(typebuf[2], &fp_int_bits) + || !fp_bits(typebuf[3], &fp_fraction_bits)) { + fprintf(stderr, "Invalid fixed point data type.\n"); + exit(1); + } + if (typebuf[0] == 'f' && fp_int_bits + fp_fraction_bits != 16) { + fprintf(stderr, + "Invalid unsigned fixed point data type.\n"); + exit(1); + } + if (typebuf[0] == 's' && fp_int_bits + fp_fraction_bits != 15) { + fprintf(stderr, + "Invalid signed fixed point data type.\n"); + exit(1); + } + type = typebuf[0] == 'f' ? fp : sp; + len = 2; + ofmt = "%d.%u"; + } + + if (type == none) { + for (unsigned int i = 0; i < ARRAY_SIZE(types); i++) { + if (strcmp(typebuf, types[i].str)) + continue; + + type = types[i].type; + len = types[i].len; + if (of == dec) + ofmt = types[i].decfmt; + else if (of == hex) + ofmt = types[i].hexfmt; + break; + } + } + + if (type == none) { + fprintf(stderr, "Key type "%s" is not known.\n", typebuf); + exit(1); + } + + /* Check permissions */ + if (geteuid() != 0) { + fprintf(stderr, "You must be root.\n"); + exit(1); + } + + /* Open SMC port */ + if (ioperm(APPLESMC_DATA_PORT, 0x10, 1)) { + fprintf(stderr, "ioperm: %s\n", strerror(errno)); + exit(1); + } + + /* Read key from SMC */ + int retval; + uint32_t buf = 0; + + retval = read_smc(APPLESMC_READ_CMD, name, (uint8_t *)&buf, len); + assert(retval == 0); + + /* Handle returned value according to the requested type */ + buf = be32toh(buf); + + int32_t fp_int = 0; + uint16_t fp_fraction = 0; + bool fp_sign = false; + + switch (type) { + case flag: + buf = (buf >> 24) & 1; + break; + + case ui8: + buf = (buf >> 24) & 0xff; + break; + + case si8: + buf = (buf >> 24) & 0xff; + if (of == dec) + buf = (int8_t)buf; + break; + + case ui16: + buf = (buf >> 16) & 0xffff; + break; + + case si16: + buf = (buf >> 16) & 0xffff; + if (of == dec) + buf = (int16_t)buf; + break; + + case fp: + buf = (buf >> 16) & 0xffff; + fp_int = buf >> fp_fraction_bits; + fp_fraction = buf & (0xffff >> fp_int_bits); + break; + + case sp: + buf = (buf >> 16) & 0xffff; + fp_sign = ((buf >> 15) & 1) == 1; + + buf &= ~(1 << 15); + + fp_int = buf >> fp_fraction_bits; + fp_fraction = buf & (0xffff >> fp_int_bits); + + if (fp_sign) + fp_int *= -1; + break; + + case ui32: + case none: // to avoid gcc warning + break; + } + + /* Output the result */ + if (of != bin) { + strcpy(fmtbuf, "%s = "); + strcat(fmtbuf, ofmt); + strcat(fmtbuf, "\n"); + + if (type == fp || type == sp) + printf(fmtbuf, name, fp_int, fp_fraction); + else + printf(fmtbuf, name, buf); + } else { + printf("%s = ", name); + print_bits(len, (uint8_t *)&buf); + printf("\n"); + } + + return 0; +}
Evgeny Zinoviev has uploaded a new patch set (#2). ( https://review.coreboot.org/c/coreboot/+/33052 )
Change subject: util/smctool: A tool for reading Apple SMC keys ......................................................................
util/smctool: A tool for reading Apple SMC keys
SMC is an embedded controller on Apple computers. With this tool you can read SMC keys. It supports various data types and can output result in various formats.
Example how to get battery capacity level:
smctool -k B0FC -t ui16 # returns Full Capacity of Battery 0 smctool -k B0RM -t ui16 # returns Remaining Capacity of Batery 0
So B0RM / B0FC * 100 = battery capacity in percents.
Another example, reading current fan speed, which is returned as unsigned fixed point number:
sudo ./smctool -k F0Ac -t fpe2
Change-Id: I6161203e52b7f7eaa1205de06e8241d7d40a7481 Signed-off-by: Evgeny Zinoviev me@ch1p.io --- A util/smctool/Makefile A util/smctool/description.md A util/smctool/smc.c A util/smctool/smc.h A util/smctool/smctool.c 5 files changed, 538 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/52/33052/2
Hello build bot (Jenkins),
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/33052
to look at the new patch set (#3).
Change subject: util/smctool: A tool for reading Apple SMC keys ......................................................................
util/smctool: A tool for reading Apple SMC keys
SMC is an embedded controller on Apple computers. With this tool you can read SMC keys. It supports various data types and can output result in various formats.
Example how to get battery capacity level:
smctool -k B0FC -t ui16 # returns Full Capacity of Battery 0 smctool -k B0RM -t ui16 # returns Remaining Capacity of Batery 0
So B0RM / B0FC * 100 = battery capacity in percents.
Another example, reading current fan speed, which is returned as unsigned fixed point number:
smctool -k F0Ac -t fpe2
Change-Id: I6161203e52b7f7eaa1205de06e8241d7d40a7481 Signed-off-by: Evgeny Zinoviev me@ch1p.io --- A util/smctool/Makefile A util/smctool/description.md A util/smctool/smc.c A util/smctool/smc.h A util/smctool/smctool.c 5 files changed, 538 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/52/33052/3
Hello Patrick Rudolph, Paul Menzel, build bot (Jenkins),
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/33052
to look at the new patch set (#4).
Change subject: util/smctool: A tool for reading Apple SMC keys ......................................................................
util/smctool: A tool for reading Apple SMC keys
SMC is an embedded controller on Apple computers. With this tool you can read SMC keys. It supports various data types and can output result in various formats.
Example how to get battery capacity level:
smctool -k B0FC -t ui16 # returns Full Capacity of Battery 0 smctool -k B0RM -t ui16 # returns Remaining Capacity of Batery 0
So B0RM / B0FC * 100 = battery capacity in percents.
Another example, reading current fan speed, which is returned as unsigned fixed point number:
smctool -k F0Ac -t fpe2
Change-Id: I6161203e52b7f7eaa1205de06e8241d7d40a7481 Signed-off-by: Evgeny Zinoviev me@ch1p.io --- A util/smctool/Makefile A util/smctool/description.md A util/smctool/smc.c A util/smctool/smc.h A util/smctool/smctool.c A util/smctool/smctool.h 6 files changed, 560 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/52/33052/4
build bot (Jenkins) has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/33052 )
Change subject: util/smctool: A tool for reading Apple SMC keys ......................................................................
Patch Set 4:
(1 comment)
https://review.coreboot.org/#/c/33052/4/util/smctool/smctool.h File util/smctool/smctool.h:
https://review.coreboot.org/#/c/33052/4/util/smctool/smctool.h@19 PS4, Line 19: #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) Prefer ARRAY_SIZE(a)
Peter Lemenkov has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/33052 )
Change subject: util/smctool: A tool for reading Apple SMC keys ......................................................................
Patch Set 4: Code-Review+1
Looks good to me. Should be a very handy application for those who deals with Apple hardware.
Idwer Vollering has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/33052 )
Change subject: util/smctool: A tool for reading Apple SMC keys ......................................................................
Patch Set 4:
(2 comments)
https://review.coreboot.org/c/coreboot/+/33052/4/util/smctool/Makefile File util/smctool/Makefile:
https://review.coreboot.org/c/coreboot/+/33052/4/util/smctool/Makefile@16 PS4, Line 16: CC = gcc One would like to be able to override CC=gcc (with CC=clang), so please adjust this to read 'CC ?= gcc'.
https://review.coreboot.org/c/coreboot/+/33052/4/util/smctool/smctool.c File util/smctool/smctool.c:
https://review.coreboot.org/c/coreboot/+/33052/4/util/smctool/smctool.c@20 PS4, Line 20: endian This include isn't portable, please surround it with '#ifdef (__FreeBSD__) #include <sys/endian.h> #else ... #endif'
Stefan Reinauer has abandoned this change. ( https://review.coreboot.org/c/coreboot/+/33052?usp=email )
Change subject: util/smctool: A tool for reading Apple SMC keys ......................................................................
Abandoned