summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Stone <al.stone@linaro.org>2013-04-29 13:44:53 +0100
committerGraeme Gregory <gg@slimlogic.co.uk>2013-06-17 10:29:21 +0100
commit4b069f5946b670e7edacc5fd54559a7c3c22e3c8 (patch)
tree6abad3711c06761f0eb436e871e38f56b8dc0ac5
parent625b8a3753db00985914ec5fc9678da42416f408 (diff)
scripts: add mab Make Acpi Binary
This executable creates ACPI binary blobs in the case where they are not provided by the boot firmware of the device from OEM. Signed-off-by: Al Stone <al.stone@linaro.org> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
-rw-r--r--scripts/Makefile1
-rw-r--r--scripts/mab/Makefile13
-rw-r--r--scripts/mab/mab.c558
-rw-r--r--scripts/mab/mab.h92
4 files changed, 664 insertions, 0 deletions
diff --git a/scripts/Makefile b/scripts/Makefile
index 01e7adb838d9..8ac44ada6e89 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -37,6 +37,7 @@ subdir-$(CONFIG_MODVERSIONS) += genksyms
subdir-y += mod
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
subdir-$(CONFIG_DTC) += dtc
+subdir-$(CONFIG_MAB) += mab
# Let clean descend into subdirs
subdir- += basic kconfig package selinux
diff --git a/scripts/mab/Makefile b/scripts/mab/Makefile
new file mode 100644
index 000000000000..4b920aa1e1bc
--- /dev/null
+++ b/scripts/mab/Makefile
@@ -0,0 +1,13 @@
+# scripts/mab makefile
+
+hostprogs-y := mab
+always := $(hostprogs-y)
+
+mab-objs := mab.o
+
+# need to be able to see the ACPI headers for table structs
+
+HOSTCFLAGS_MAB := -g -I$(src)
+
+HOSTCFLAGS_mab.o := $(HOSTCFLAGS_MAB)
+
diff --git a/scripts/mab/mab.c b/scripts/mab/mab.c
new file mode 100644
index 000000000000..487d6868f26d
--- /dev/null
+++ b/scripts/mab/mab.c
@@ -0,0 +1,558 @@
+/*
+ * mab.c: main program for the tool to Make A Blob of ACPI tables
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * Copyright (c) 2013, Al Stone <ahs3@redhat.com>
+ *
+ * NB: all values are assumed to be little-endian in the blob.
+ *
+ */
+
+#include "mab.h"
+
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+void usage(void)
+{
+ printf("%s %s\n", PROGNAME, VERSION);
+ printf("usage: %s -d <dir> -o <blob> -i <manifest> ", PROGNAME);
+ printf("[-c <iasl-cmd>] [-q]\n");
+ printf("\n");
+ printf(" -d <dir> => directory of ASL files\n");
+ printf(" -o <blob> => file name for resulting ACPI blob\n");
+ printf(" -i <manifest> => list of AML files needed\n");
+ printf(" -c <iasl-cmd> => iasl command to use (default: iasl -l)\n");
+ printf(" -q => if given, supress output\n");
+}
+
+int get_file_size(char *fname)
+{
+ struct stat buf;
+ int err;
+
+ err = stat(fname, &buf);
+ if (err)
+ printf("? cannot stat %s (%d)\n", fname, err);
+ return (err == 0 ? buf.st_size : 0);
+}
+
+int valid_sig(char *sig)
+{
+ char **p;
+
+ p = known_sigs;
+ while (*p != NULL) {
+ if (strcmp(*p, sig) == 0)
+ return 1;
+ p++;
+ }
+ return 0;
+}
+
+struct table *build_table_entry(char *dir, char *buf)
+{
+ struct table *tmp;
+ char sig[MAX_LINE];
+ char fname[MAX_LINE];
+ int ii, n;
+ char *p;
+
+ tmp = (struct table *)malloc(sizeof(struct table));
+ if (tmp != NULL) {
+ memset(tmp, 0, sizeof(struct table));
+ memset(sig, 0, MAX_LINE);
+ memset(fname, 0, MAX_LINE);
+
+ /* parse the line */
+ n = 0;
+ for (ii = 0; ii < strlen(buf); ii++) {
+ if (buf[ii] == ':' || isspace(buf[ii]))
+ break;
+ sig[n++] = buf[ii];
+ }
+ if (!valid_sig(sig)) {
+ free(tmp);
+ return NULL;
+ }
+ while (buf[ii] == ':' || isspace(buf[ii]))
+ ii++;
+ n = 0;
+ for (; ii < strlen(buf); ii++) {
+ if (buf[ii] == '\n' || !isascii(buf[ii]))
+ break;
+ fname[n++] = buf[ii];
+ }
+
+ /* fill in the struct */
+ tmp->signature = (char *)malloc(strlen(sig) + 1);
+ memset(tmp->signature, 0, strlen(sig) + 1);
+ memcpy(tmp->signature, sig, strlen(sig));
+
+ n = strlen(fname) + strlen(dir) + 2;
+ tmp->asl_name = (char *)malloc(n);
+ memset(tmp->asl_name, 0, n);
+ strcpy(tmp->asl_name, dir);
+ strcat(tmp->asl_name, "/");
+ strcat(tmp->asl_name, fname);
+
+ tmp->aml_name = (char *)malloc(n);
+ memset(tmp->aml_name, 0, n);
+ strcpy(tmp->aml_name, tmp->asl_name);
+ p = strrchr(tmp->aml_name, '.');
+ if (p)
+ strcpy(p, ".aml");
+
+ tmp->file_size = -1; /* need to build .aml file first */
+ tmp->offset = -1;
+ }
+
+ return tmp;
+}
+
+int read_manifest(char *dir, char *fname)
+{
+ FILE *fp;
+ struct table *p;
+ char line[MAX_LINE];
+ char *tmp;
+ char buf[MAX_LINE];
+
+ memset(buf, 0, MAX_LINE);
+ strcpy(buf, dir);
+ strcat(buf, "/");
+ strcat(buf, fname);
+ fp = fopen(buf, "r");
+ if (!fp)
+ return -ENOENT;
+
+ LIST_INIT(&thead);
+ memset(line, 0, MAX_LINE);
+ tmp = fgets(line, MAX_LINE, fp);
+ while (tmp != NULL) {
+ if (strlen(line) > 0 && line[0] != '#' && line[0] != '\n') {
+ p = build_table_entry(dir, line);
+ if (p)
+ LIST_INSERT_HEAD(&thead, p, tables);
+ }
+ memset(line, 0, MAX_LINE);
+ tmp = fgets(line, MAX_LINE, fp);
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+void set_blob_header(unsigned char *blob, int blob_size)
+{
+ uint32_t *psize;
+
+ /* the resulting blob contents are always little-endian */
+ memcpy(blob, BLOB_MAGIC, BLOB_MAGIC_SIZE);
+
+ psize = (uint32_t *)(blob + BLOB_MAGIC_SIZE);
+ *psize = (uint32_t)(htobe32(blob_size));
+
+}
+
+struct table *find_table(char *sig)
+{
+ struct table *np;
+
+ LIST_FOREACH(np, &thead, tables) {
+ if (strcmp(np->signature, sig) == 0)
+ return np;
+ }
+ return NULL;
+}
+
+void write_table(unsigned char *blob, struct table *tp, int offset)
+{
+ unsigned char *buf;
+ unsigned char *start;
+ FILE *fp;
+
+ buf = (unsigned char *)malloc(tp->file_size);
+ memset(buf, 0, tp->file_size);
+
+ tp->offset = offset;
+ start = (unsigned char *)(blob + BLOB_HEADER_SIZE + offset);
+ fp = fopen(tp->aml_name, "r");
+ if (fp) {
+ fread(start, 1, tp->file_size, fp);
+ fclose(fp);
+ }
+}
+
+void write_blob(char *dir, char *fname, unsigned char *blob, int size)
+{
+ FILE *fp;
+ char newname[MAX_LINE];
+
+ memset(newname, 0, MAX_LINE);
+ strcpy(newname, dir);
+ strcat(newname, "/");
+ strcat(newname, fname);
+
+ fp = fopen(newname, "w+");
+ if (fp) {
+ fwrite(blob, 1, size, fp);
+ fclose(fp);
+ } else {
+ printf("? could not open the blob file: %s\n", newname);
+ }
+}
+
+void set_checksum(unsigned char *start, int len, uint8_t *cksum)
+{
+ uint8_t newsum, oldsum;
+ uint8_t *p;
+
+ newsum = 0;
+ for (p = (uint8_t *)start; p < (uint8_t *)(start + len); p++)
+ newsum += *p;
+
+ oldsum = *cksum;
+ newsum = (uint8_t)(newsum - oldsum);
+
+ *cksum = (uint8_t)(0 - newsum);
+}
+
+int add_table(unsigned char *blob, char *table_name, int offset, int reqd)
+{
+ struct table *p;
+
+ p = find_table(table_name);
+ if (p) {
+ write_table(blob, p, offset);
+ } else {
+ if (reqd) {
+ printf("? %s table is required\n", table_name);
+ return 0;
+ }
+ }
+
+ /* NB: ACPI table size cannot be zero -- there must be a header */
+ return p->file_size;
+}
+
+void fixup_rsdp(unsigned char *blob)
+{
+ /* We could use the 32-bit RSDT address but that has
+ * essentially been deprecated. Instead, use the 64-bit
+ * XSDT address though be sure to use little-endian values.
+ */
+ const int RSDP_CHECKSUM_BYTES = 20;
+ const int RSDP_CHECKSUM_OFFSET = 8;
+ const int XSDT_ADDR_OFFSET = 24;
+
+ uint8_t *pcksum;
+ uint64_t *tmp;
+ struct table *rsdpp;
+ struct table *p;
+
+ /* NB: there are no safety checks on the find_table()
+ * return value because if we've gotten this far and
+ * the RSDP doesn't exist, something else has gone
+ * seriously wrong far earlier.
+ */
+ rsdpp = find_table("rsdp");
+ tmp = (uint64_t *)(blob + BLOB_HEADER_SIZE +
+ rsdpp->offset + XSDT_ADDR_OFFSET);
+
+ p = find_table("xsdt");
+ if (p)
+ *tmp = p->offset;
+ else
+ *tmp = (uint64_t)0;
+
+ /* always reset the checksum, even if it is seldom used */
+ pcksum = (uint8_t *)(blob + BLOB_HEADER_SIZE);
+ pcksum = (uint8_t *)(pcksum + rsdpp->offset + RSDP_CHECKSUM_OFFSET);
+ set_checksum((unsigned char *)(blob + BLOB_HEADER_SIZE + rsdpp->offset),
+ RSDP_CHECKSUM_BYTES, pcksum);
+}
+
+void fixup_facp(unsigned char *blob, int *offset)
+{
+ const int DSDT_ADDR_OFFSET = 40;
+ const int FACP_CHECKSUM_OFFSET = 9;
+ const int FIRMWARE_CTRL_OFFSET = 36;
+ const int X_DSDT_ADDR_OFFSET = 140;
+ const int X_FIRMWARE_CTRL_OFFSET = 132;
+
+ struct table *facpp;
+ struct table *p;
+ uint32_t *stmp;
+ uint64_t *ltmp;
+ uint8_t *pcksum;
+
+ facpp = find_table("facp");
+
+ /* add in the DSDT and X_DSDT addresses */
+ stmp = (uint32_t *)(blob + BLOB_HEADER_SIZE +
+ facpp->offset + DSDT_ADDR_OFFSET);
+ ltmp = (uint64_t *)(blob + BLOB_HEADER_SIZE +
+ facpp->offset + X_DSDT_ADDR_OFFSET);
+ p = find_table("dsdt");
+ if (p) {
+ *stmp = (uint32_t)p->offset;
+ *ltmp = (uint64_t)p->offset;
+ } else {
+ *stmp = (uint32_t)0;
+ *ltmp = (uint64_t)0;
+ return;
+ }
+
+ /* add in the FIRMWARE_CTRL and X_FIRMWARE_CTRL addresses */
+ stmp = (uint32_t *)(blob + BLOB_HEADER_SIZE +
+ facpp->offset + FIRMWARE_CTRL_OFFSET);
+ ltmp = (uint64_t *)(blob + BLOB_HEADER_SIZE +
+ facpp->offset + X_FIRMWARE_CTRL_OFFSET);
+ p = find_table("facs");
+ if (p) {
+ *stmp = (uint32_t)p->offset;
+ *ltmp = (uint64_t)p->offset;
+ } else {
+ *stmp = (uint32_t)0;
+ *ltmp = (uint64_t)0;
+ return;
+ }
+
+ /* always reset the checksum, even if it is seldom used */
+ pcksum = (uint8_t *)(blob + BLOB_HEADER_SIZE);
+ pcksum = (uint8_t *)(pcksum + facpp->offset + FACP_CHECKSUM_OFFSET);
+ set_checksum((unsigned char *)(blob + BLOB_HEADER_SIZE + facpp->offset),
+ facpp->file_size, pcksum);
+}
+
+void fixup_xsdt(unsigned char *blob, int *offset)
+{
+ const int FACP_ADDR_OFFSET = 36;
+ const int XSDT_CHECKSUM_OFFSET = 9;
+ const int XSDT_HEADER_SIZE = 36;
+
+ struct table *xsdtp;
+ struct table *p;
+ uint64_t *tmp;
+ uint8_t *pcksum;
+ int delta;
+ int allowed;
+
+ xsdtp = find_table("xsdt");
+ tmp = (uint64_t *)(blob + BLOB_HEADER_SIZE +
+ xsdtp->offset + FACP_ADDR_OFFSET);
+ allowed = (xsdtp->file_size - XSDT_HEADER_SIZE) / sizeof(uint64_t);
+
+ /* first table must be FACP (aka FADT) */
+ p = find_table("facp");
+ if (p)
+ *tmp = p->offset;
+ else {
+ *tmp = (uint64_t)0;
+ return;
+ }
+
+ /* any tables not already in use go here */
+ allowed--;
+ tmp++;
+ LIST_FOREACH(p, &thead, tables) {
+ if (p->offset < 0) {
+ if ((unsigned long long)tmp <
+ (unsigned long long)
+ (xsdtp + xsdtp->file_size)) {
+ delta = add_table(blob, p->signature,
+ *offset, NOT_REQUIRED);
+ *offset += delta;
+ *tmp++ = p->offset;
+ allowed--;
+ if (allowed < 1)
+ break;
+ }
+ }
+ }
+
+ /* always reset the checksum, even if it is seldom used */
+ pcksum = (uint8_t *)(blob + BLOB_HEADER_SIZE);
+ pcksum = (uint8_t *)(pcksum + xsdtp->offset + XSDT_CHECKSUM_OFFSET);
+ set_checksum((unsigned char *)(blob + BLOB_HEADER_SIZE + xsdtp->offset),
+ xsdtp->file_size, pcksum);
+}
+
+void build_aml(int q, char *dir, char *iasl_cmd, struct table *tp)
+{
+ char cmd[MAX_LINE];
+ struct stat mbuf;
+ struct stat sbuf;
+
+ if (!tp)
+ return;
+
+ if ((stat(tp->aml_name, &mbuf) == 0) &&
+ (stat(tp->asl_name, &sbuf) == 0)) {
+ if (mbuf.st_mtime > sbuf.st_mtime)
+ return; /* AML file is newer than ASL file */
+ }
+
+ strcpy(cmd, iasl_cmd);
+ strcat(cmd, " ");
+ strcat(cmd, tp->asl_name);
+ if (q)
+ strcat(cmd, " >/dev/null 2&1");
+
+ system(cmd);
+}
+
+int main(int argc, char *argv[])
+{
+ int err = 0;
+ int ii, jj;
+ int blob_size;
+ int offset;
+ int delta;
+ unsigned char *blob;
+ char *manifest_name;
+ char *homedir;
+ char *acpi_blob_name;
+ char *iasl_cmd;
+ char sig[SIG_LENGTH];
+ struct table *np;
+ int opt;
+ int quiet;
+
+ /* parameter handling */
+ manifest_name = NULL;
+ homedir = NULL;
+ acpi_blob_name = NULL;
+ iasl_cmd = NULL;
+ quiet = 0;
+
+ while ((opt = getopt(argc, argv, "d:o:i:c:q")) != EOF) {
+ switch (opt) {
+ case 'd':
+ homedir = optarg;
+ break;
+ case 'o':
+ acpi_blob_name = optarg;
+ break;
+ case 'i':
+ manifest_name = optarg;
+ break;
+ case 'c':
+ iasl_cmd = optarg;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ default:
+ usage();
+ return 1;
+ }
+ }
+
+ if ((argc > (optind + 1)) ||
+ (manifest_name == NULL) ||
+ (acpi_blob_name == NULL) ||
+ (homedir == NULL)) {
+ printf("? missing a parameter value\n");
+ usage();
+ return 1;
+ }
+ if (!iasl_cmd)
+ iasl_cmd = "iasl -l";
+
+ /* what tables do we need to do something about? */
+ err = read_manifest(homedir, manifest_name);
+ if (err) {
+ printf("? could not read manifest: %s\n", manifest_name);
+ return 1;
+ }
+
+ /* build the AML for each of the files */
+ LIST_FOREACH(np, &thead, tables) {
+ build_aml(quiet, homedir, iasl_cmd, np);
+ np->file_size = get_file_size(np->aml_name);
+ }
+
+ /* build up the contents of the blob, table by table */
+ blob_size = 0;
+ LIST_FOREACH(np, &thead, tables)
+ blob_size += np->file_size;
+ blob = (unsigned char *)malloc(blob_size + BLOB_HEADER_SIZE);
+ memset(blob, 0, blob_size + BLOB_HEADER_SIZE);
+
+ set_blob_header(blob, blob_size);
+ offset = 0;
+
+ delta = add_table(blob, "rsdp", offset, REQUIRED);
+ if (!delta)
+ return 1;
+ offset += delta;
+
+ delta = add_table(blob, "xsdt", offset, REQUIRED);
+ if (!delta)
+ return 1;
+ offset += delta;
+
+ delta = add_table(blob, "facp", offset, REQUIRED);
+ if (!delta)
+ return 1;
+ offset += delta;
+
+ delta = add_table(blob, "dsdt", offset, REQUIRED);
+ if (!delta)
+ return 1;
+ offset += delta;
+
+ delta = add_table(blob, "facs", offset, REQUIRED);
+ if (!delta)
+ return 1;
+ offset += delta;
+
+ /* patch up all the offsets if needed */
+ fixup_rsdp(blob);
+ fixup_facp(blob, &offset);
+
+ /* this fixup MUST always be called LAST -- it uses any unused tables */
+ fixup_xsdt(blob, &offset);
+
+ /* all done, so write out the blob */
+ write_blob(homedir, acpi_blob_name, blob, blob_size + BLOB_HEADER_SIZE);
+
+ if (!quiet) {
+ printf("%s %s\n", PROGNAME, VERSION);
+ LIST_FOREACH(np, &thead, tables) {
+ if (np->offset < 0) {
+ for (jj = 0; jj < SIG_LENGTH; jj++)
+ sig[jj] = toupper(np->signature[jj]);
+ printf("? no room in XSDT for %s (%4s)\n",
+ basename(np->asl_name), sig);
+ }
+ }
+ ii = 0;
+ LIST_FOREACH(np, &thead, tables) {
+ printf("[%03d] %4s : %s (%d bytes @ 0x%08x)\n", ii,
+ np->signature, basename(np->aml_name),
+ np->file_size, np->offset);
+ ii++;
+ }
+ }
+
+ return err;
+}
+
diff --git a/scripts/mab/mab.h b/scripts/mab/mab.h
new file mode 100644
index 000000000000..4ac3ae6972c9
--- /dev/null
+++ b/scripts/mab/mab.h
@@ -0,0 +1,92 @@
+#ifndef _MAB_H
+#define _MAB_H
+/*
+ * mab.h: tool to Make A Blob of ACPI tables
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * Copyright (c) 2013, Al Stone <ahs3@redhat.com>
+ *
+ * NB: all values are assumed to be little-endian in the blob.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <sys/queue.h>
+
+#define BLOB_HEADER_SIZE 8
+#define BLOB_MAGIC "ACPI"
+#define BLOB_MAGIC_SIZE 4
+#define MAX_LINE 1024
+#define NOT_REQUIRED 0
+#define REQUIRED 1
+#define SIG_LENGTH 4
+
+const char VERSION[] = { "0.27.2" };
+const char PROGNAME[] = { "mab" };
+
+char *known_sigs[] = {
+ "apic",
+ "bert",
+ "cpep",
+ "dsdt",
+ "ecdt",
+ "einj",
+ "erst",
+ "facp",
+ "facs",
+ "fpdt",
+ "gtdt",
+ "hest",
+ "mcfg",
+ "mchi",
+ "mpst",
+ "msct",
+ "oem",
+ "pcct",
+ "pmtt",
+ "rasf",
+ "rsdp",
+ "slit",
+ "spmi",
+ "srat",
+ "ssdt",
+ "uefi",
+ "xsdt",
+ NULL
+};
+
+LIST_HEAD(table_head, table) thead;
+struct table_head *theadp;
+
+struct table {
+ char *signature; /* an ACPI table name/signature */
+ char *asl_name; /* ASL file name */
+ char *aml_name; /* corresponding AML name */
+ int file_size; /* aka table size */
+ int offset; /* location in the blob */
+ LIST_ENTRY(table) tables;
+};
+
+int add_table(unsigned char *blob, char *table_name, int offset, int reqd);
+void build_aml(int q, char *dir, char *iasl_cmd, struct table *tp);
+struct table *build_table_entry(char *dir, char *buf);
+struct table *find_table(char *sig);
+int get_file_size(char *fname);
+int read_manifest(char *dir, char *fname);
+void set_blob_header(unsigned char *blob, int blob_size);
+void set_checksum(unsigned char *start, int len, uint8_t *cksum);
+void write_blob(char *dir, char *fname, unsigned char *blob, int size);
+void write_table(unsigned char *blob, struct table *tp, int offset);
+void usage(void);
+int valid_sig(char *sig);
+
+void fixup_facp(unsigned char *blob, int *offset);
+void fixup_rsdp(unsigned char *blob);
+void fixup_xsdt(unsigned char *blob, int *offset);
+
+#endif