From 66dfd8bc700123c2e17c8aaf3efebfda95f11c59 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Thu, 10 Jul 2025 18:29:04 +0200 Subject: [PATCH] util/smmstore_debug: add tool for low-level interactions with SMMSTORE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commands and parameters are passed as raw numbers, but that's the point, we want to test it with something more than normal SMMSTORE interface. Upstream-Status: Pending Change-Id: Ib3a32f0bdef038e4ccc2fb2bb8a6c98ae6d0cfbd Signed-off-by: Krystian Hebel Signed-off-by: Michał Żygowski --- util/smmstore_debug/.gitignore | 1 + util/smmstore_debug/Makefile | 42 ++++++ util/smmstore_debug/description.md | 1 + util/smmstore_debug/smmstore_debug.c | 189 +++++++++++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 util/smmstore_debug/.gitignore create mode 100644 util/smmstore_debug/Makefile create mode 100644 util/smmstore_debug/description.md create mode 100644 util/smmstore_debug/smmstore_debug.c diff --git a/util/smmstore_debug/.gitignore b/util/smmstore_debug/.gitignore new file mode 100644 index 0000000000..879ecdfdd2 --- /dev/null +++ b/util/smmstore_debug/.gitignore @@ -0,0 +1 @@ +smmstore_debug diff --git a/util/smmstore_debug/Makefile b/util/smmstore_debug/Makefile new file mode 100644 index 0000000000..dc35049141 --- /dev/null +++ b/util/smmstore_debug/Makefile @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +PRG := smmstore_debug + +CC ?= $(CROSS_COMPILE)gcc +HOSTCC ?= $(CC) +INSTALL ?= /usr/bin/env install +PREFIX ?= /usr/local + +HOSTCFLAGS ?= $(CFLAGS) +HOSTCFLAGS += -Wall -Wextra -MMD -MP -O3 + +HOSTLDFLAGS ?= $(LDFLAGS) + +# there files are in this directory +SRC := smmstore_debug.c + +OBJ := $(SRC:.c=.o) +DEP := $(SRC:.c=.o.d) + +.PHONY: all debug clean install + +all: $(PRG) + +debug: HOSTCFLAGS += -O0 -g +debug: HOSTLDFLAGS += -g +debug: all + +install: $(PRG) + $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin/ + $(INSTALL) $^ $(DESTDIR)$(PREFIX)/bin/ + +clean: + -$(RM) $(PRG) $(OBJ) $(DEP) + +$(PRG): $(OBJ) + $(HOSTCC) -o $@ $^ $(HOSTLDFLAGS) + +%.o: %.c + $(HOSTCC) $(HOSTCFLAGS) -c -o $@ -MF $@.d $< + +-include $(DEP) diff --git a/util/smmstore_debug/description.md b/util/smmstore_debug/description.md new file mode 100644 index 0000000000..dc15307e4a --- /dev/null +++ b/util/smmstore_debug/description.md @@ -0,0 +1 @@ +Debug tool for low-level interactions with SMMSTORE `C` diff --git a/util/smmstore_debug/smmstore_debug.c b/util/smmstore_debug/smmstore_debug.c new file mode 100644 index 0000000000..8e7cfde6d4 --- /dev/null +++ b/util/smmstore_debug/smmstore_debug.c @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STR(x) _STR(x) +#define _STR(x) #x + +#define APM_CNT 0xb2 +#define SMMSTORE_CMD 0xed +#define SMMSTORE_PARAMS_SIZE 0x1000 +#define SMMSTORE_COMBUF_SIZE 0x10000 + +/* Size is doubled to account for page alignment required for mmap() */ +static unsigned char tmpbuf[2 * SMMSTORE_PARAMS_SIZE]; + +static inline unsigned int call_smm (unsigned int cmd, unsigned int arg) +{ + unsigned int res = 0; + asm volatile ( + "outb %b0, $" STR(APM_CNT) "\n\t" + "nop; nop; nop\n\t" + : "=a" (res) + : "a" (cmd), + "b" (arg) + : "memory"); + return res; +} + +static int usage (char *name) +{ + printf ("Usage:\n" + "\t%s --tmp_buf --com_buf --command \n" + "\t%*s [--in_file ] [--out_file ] param1 ...\n" + "\t%s -t -b -c \n" + "\t%*s [-i ] [-o ] param1 ...\n\n", + name, (int)strlen(name), "", name, (int)strlen(name), ""); + printf ("--tmp_buf, -t - physical address of a buffer in low 4GB of RAM. Up to 8KB\n" + " after this address will be overwritten with params and then\n" + " restored. Point it to something that won't break because of\n" + " such temporary modification (e.g. part of CONSOLE in CBMEM,\n" + " **after** the header - so SMI handler can still print to\n" + " the CBMEM console).\n" + "--com_buf, -b - physical address of SMM COMBUFFER, can be read from\n" + " `cbmem -l`. It must be aligned to a page size.\n" + "--command, -c - command to execute, see `coreboot/src/include/smmstore.h`.\n" + " For SMMSTOREv2 this is 5 for read, 6 for write, 7 for\n" + " clear.\n" + "param1 ... - params passed to SMI handler, as set of uint32_t values.\n" + " Numbers are parsed as hexadecimal values, even without '0x'\n" + " prefix. Expected structure depends on the command, refer to\n" + " `smmstore_params_raw*` structure definitions in\n" + " `coreboot/src/include/smmstore.h` file. At least one\n" + " parameter must be specified.\n" + "--in_file, -i - optional, binary file which content is written to com_buf\n" + " before SMM call.\n" + "--out_file, -o - optional, path to binary file (it is created if doesn't exist)\n" + " to which the content of com_buf after SMM call is saved.\n\n" + "Some of the combinations don't make sense (e.g. writing to COMBUFFER before\n" + "read command), but the tool doesn't check this. It is supposed to help\n" + "with testing and debugging, hence it allows for sending garbage to SMMSTORE\n" + "handler, with the assumption that the user knows what he's doing.\n\n" + "The utility must be run with CAP_SYS_RAWIO privilege (e.g. as root)\n" + "and with no kernel_lockdown in place (i.e. without SecureBoot).\n"); + return -1; +} + +static struct option long_options[] = { + {"tmp_buf", required_argument, 0, 't'}, + {"com_buf", required_argument, 0, 'b'}, + {"command", required_argument, 0, 'c'}, + {"out_file", required_argument, 0, 'o'}, + {"in_file", required_argument, 0, 'i'}, + {0, 0, 0, 0 } +}; + +int main (int argc, char *argv[]) +{ + int fd; + void *combuf; + unsigned long combuf_addr = 0; + unsigned char *params; + unsigned long params_phys = 0; + unsigned long params_phys_aligned; + unsigned int command = (unsigned int)-1; + char *in_file = NULL; + char *out_file = NULL; + char *end; + + while (1) { + int idx = 0; + int opt; + + opt = getopt_long (argc, argv, "t:b:c:o:i:", long_options, &idx); + + if (opt == -1) + break; + + switch (opt) { + case 't': + params_phys = strtoul (optarg, &end, 16); + if (params_phys == 0 || params_phys == ULONG_MAX) return usage(argv[0]); + break; + case 'b': + combuf_addr = strtoul (optarg, &end, 16); + if (combuf_addr == 0 || combuf_addr == ULONG_MAX) return usage(argv[0]); + break; + case 'c': + command = strtoul (optarg, &end, 16); + if (command == 0 || command == UINT_MAX) return usage(argv[0]); + break; + case 'o': + out_file = optarg; + break; + case 'i': + in_file = optarg; + break; + default: + return usage(argv[0]); + } + } + + /* Check if all required options were given */ + if (!combuf_addr || !params_phys || command == (unsigned int)-1 || optind == argc) + return usage(argv[0]); + + params_phys_aligned = params_phys & ~((unsigned long)(getpagesize() - 1)); + + /* Map COMBUF and temporary buffer for params */ + fd = open ("/dev/mem", O_SYNC | O_RDWR); + if (fd < 0) { + perror ("Can't access physical memory"); + printf ("This tool must be run as root with SecureBoot disabled.\n"); + return -1; + } + combuf = mmap (NULL, SMMSTORE_COMBUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED | MAP_POPULATE, fd, combuf_addr); + params = mmap (NULL, 2 * SMMSTORE_PARAMS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED | MAP_POPULATE, fd, params_phys_aligned); + close (fd); + + /* Fill COMBUF from in_file */ + if (in_file) { + fd = open (in_file, O_RDONLY); + if (fd < 0) { + perror ("Error opening in_file"); + return -1; + } + read (fd, combuf, SMMSTORE_COMBUF_SIZE); + close (fd); + } + + /* Request IO access to APM_CNT port */ + if (ioperm (APM_CNT, 1, 1) < 0) { + perror ("Can't access IO port"); + printf ("This tool must be run as root with SecureBoot disabled.\n"); + return -1; + } + + /* Backup temporary buffer and fill params */ + memcpy (tmpbuf, params, sizeof(tmpbuf)); + for (int i = 0; i < (argc - optind) && i < (int)(SMMSTORE_PARAMS_SIZE / sizeof (unsigned int)); i++) { + unsigned int *param = (unsigned int *)(params + (params_phys - params_phys_aligned)); + param[i] = strtoul (argv[i + optind], &end, 16); + } + + printf ("SMI handler returned %#x\n", call_smm ((command << 8) | SMMSTORE_CMD, params_phys)); + + /* Restore original contents of memory used for params */ + memcpy (params, tmpbuf, sizeof(tmpbuf)); + + /* Store COMBUF to out_file */ + if (out_file) { + fd = creat (out_file, 0666); + if (fd < 0) { + perror ("Error opening out_file"); + return -1; + } + write (fd, combuf, SMMSTORE_COMBUF_SIZE); + close (fd); + } + + return 0; +}