Blob Blame History Raw
From a909f4e024bdb5b0a8ea11f9fde62a9c74438983 Mon Sep 17 00:00:00 2001
From: Mark Salter <msalter@redhat.com>
Date: Mon, 26 Jan 2015 12:42:42 -0500
Subject: [PATCH] Add libdmifs from Linaro

This library module comes from:

  git://git.linaro.org/people/ivan.khoronzhuk/libdmifs.git

For now, it is being built in rather than as a shared lib.

Signed-off-by: Mark Salter <msalter@redhat.com>
---
 Makefile    |   7 +-
 dmidecode.c |   2 +-
 libdmifs.c  | 644 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 libdmifs.h  |  56 ++++++
 4 files changed, 706 insertions(+), 3 deletions(-)
 create mode 100644 libdmifs.c
 create mode 100644 libdmifs.h

diff --git a/Makefile b/Makefile
index d8457da..805d08e 100644
--- a/Makefile
+++ b/Makefile
@@ -57,8 +57,8 @@ all : $(PROGRAMS)
 # Programs
 #

-dmidecode : dmidecode.o dmiopt.o dmioem.o util.o
-	$(CC) $(LDFLAGS) dmidecode.o dmiopt.o dmioem.o util.o -ldmifs -o $@
+dmidecode : dmidecode.o dmiopt.o dmioem.o util.o libdmifs.o
+	$(CC) $(LDFLAGS) dmidecode.o dmiopt.o dmioem.o util.o libdmifs.o -o $@

 biosdecode : biosdecode.o util.o
	$(CC) $(LDFLAGS) biosdecode.o util.o -o $@
@@ -98,6 +98,9 @@ vpdopt.o : vpdopt.c config.h util.h vpdopt.h
 util.o : util.c types.h util.h config.h
	$(CC) $(CFLAGS) -c $< -o $@

+libdmifs.o: libdmifs.c libdmifs.h
+	$(CC) $(CFLAGS) -c $< -o $@
+
 #
 # Commands
 #
diff --git a/dmidecode.c b/dmidecode.c
index 4663cc6..fee3af1 100644
--- a/dmidecode.c
+++ b/dmidecode.c
@@ -57,7 +57,7 @@
 #include <strings.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <libdmifs.h>
+#include "libdmifs.h"

 #include "version.h"
 #include "config.h"
diff --git a/libdmifs.c b/libdmifs.c
new file mode 100644
index 0000000..9ad2414
--- /dev/null
+++ b/libdmifs.c
@@ -0,0 +1,644 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <endian.h>
+#include "libdmifs.h"
+
+/*
+ * maximum number of entries can be calculated based on maximum table
+ * size and minimum size of structure (4 + 2 NULL symbols).
+ */
+#define MIN_HEADER_SIZE			4
+#define MIN_DMI_STRUCT_SIZE		(MIN_HEADER_SIZE + 2)
+#define MAX_ENTRIES_COUNT		(~0U/MIN_DMI_STRUCT_SIZE)
+#define POSITION_ERR			(MAX_ENTRIES_COUNT + 1)
+
+/* list of dmi entries */
+
+/**
+ * dmilist_insert - inserts new dmi entry to ordered dmi_list,
+ * sorting according to the position in dmi table
+ * @dmi_list: pointer on pointer to the first dmi entry in the list
+ * @new: new entry to be inserted in the ordered dmi_list.
+ *
+ * Returns 0 on success and -1 when error.
+ */
+static int dmilist_insert(struct dmi_entry **dmi_list, struct dmi_entry *new)
+{
+	struct dmi_entry *old;
+	struct dmi_entry *cur = *dmi_list;
+
+	if (!cur) {
+		new->next = NULL;
+		*dmi_list = new;
+		return 0;
+	}
+
+	old = NULL;
+	while (cur) {
+		if (cur->position < new->position) {
+			old = cur;
+			cur = cur->next;
+			continue;
+		} else if (cur->position > new->position) {
+			if (old) {
+				old->next = new;
+				new->next = cur;
+				return 0;
+			}
+			new->next = cur;
+			*dmi_list = new;
+			return 0;
+		}
+
+		fprintf(stderr, "dmitable is broken");
+		return -1;
+	}
+
+	old->next = new;
+	new->next = NULL;
+
+	return 0;
+}
+
+static void dmilist_free_entry(struct dmi_entry *entry)
+{
+	free(entry->d_name);
+	free(entry);
+}
+
+static void dmilist_free(struct dmi_entry **dmi_list)
+{
+	struct dmi_entry *old;
+	struct dmi_entry *cur = *dmi_list;
+
+	if (!cur)
+		return;
+
+	while (cur) {
+		old = cur;
+		cur = cur->next;
+		dmilist_free_entry(old);
+	}
+
+	*dmi_list = NULL;
+}
+
+/* dmi sysfs attribute reading */
+
+/**
+ * dmi_get_smbios - get smbios data
+ * @smbios: pointer on array to read raw smbios table in
+ *
+ * Returns read data size in bytes on success and 0 when error.
+ */
+static int dmi_get_smbios(unsigned char *smbios)
+{
+	FILE *file;
+	int count = 0;
+	enum {SMBIOS_SIZE = 32};
+
+	file = fopen("/sys/firmware/dmi/smbios_raw_header", "rb");
+	if (!file) {
+		fprintf(stderr, "no \"smbios\" sysfs entry\n");
+		return count;
+	}
+
+	count = fread(smbios, sizeof(char), SMBIOS_SIZE, file);
+	if (!feof(file)) {
+		fprintf(stderr, "Error while reading a file\n");
+		goto err;
+	}
+
+err:
+	fclose(file);
+	return count;
+}
+
+/**
+ * read_position - reads position of dmi entry as it's inside dmi table
+ * @dir: appropriate directory of dmi entry position
+ *
+ * returns dmi entry position in dmi table on success and POSITION_ERR when
+ * error occurred.
+ */
+static unsigned int read_position(char *dir)
+{
+	FILE *file;
+	char pos[10];
+	unsigned int position;
+
+	file = fopen("position", "r");
+	if (!file) {
+		fprintf(stderr, "no \"position\" in \"%s\"\n", dir);
+		return POSITION_ERR;
+	}
+
+	if (!fgets(pos, 10, file) && ferror(file)) {
+		fclose(file);
+		fprintf(stderr, "Error while working with \"position\" in %s\n",
+			dir);
+		return POSITION_ERR;
+	}
+
+	fclose(file);
+
+	position = strtoul(pos, NULL, 0);
+	/* position can't be more than number of entries */
+	if (position > MAX_ENTRIES_COUNT) {
+		fprintf(stderr, "position is incorrect %d\n", position);
+		return POSITION_ERR;
+	}
+
+	return position;
+}
+
+/**
+ * read_raw - reads raw data of dmi entry
+ * @dir: appropriate directory of dmi entry position
+ * @data: pointer to the array where raw data will be read
+ * @max_size: maximum data size possible to read
+ *
+ * Returns read data size in bytes on success and 0 when error.
+ * In the same time return value < 6 is also error as at least header is
+ * 4 bytes in size + 2 NULLs.
+ */
+static unsigned int read_raw(char *dir,
+			     unsigned char *buf, unsigned int max_size)
+{
+	FILE *file;
+	unsigned int count;
+	unsigned int size = 0;
+
+	file = fopen("raw", "rb");
+	if (!file) {
+		fprintf(stderr, "no \"raw\" in \"%s\"\n", dir);
+		return 0;
+	}
+
+	count = fread(buf, sizeof(char), max_size, file);
+	if (!feof(file)) {
+		fprintf(stderr, "Error while reading \"raw\" file\n");
+		goto err;
+	}
+
+	if (count < MIN_DMI_STRUCT_SIZE) {
+		fprintf(stderr, "DMI header cannot be less than 4 bytes");
+		goto err;
+	}
+
+	/* check if structure is correct */
+	if (buf[count - 1] || buf[count - 2]) {
+		fprintf(stderr, "Bad raw file of \"%s\"\n", dir);
+		goto err;
+	}
+
+	size = count;
+err:
+	fclose(file);
+	return size;
+}
+
+/**
+ * add_entry_to_dmilist - generates empty dmi entry and places it to dmi list
+ * @dmi_list: dmi list where allocated dmi entry will be put
+ * @dir: the directory name appropriate to dmi entry
+ *
+ * Returns 0 on success and -1 on error
+ */
+static int add_entry_to_dmilist(struct dmi_entry **dmi_list, char *dir)
+{
+	char *dir_name;
+	unsigned int position;
+	struct dmi_entry *entry;
+
+	position = read_position(dir);
+	if (position == POSITION_ERR) {
+		fprintf(stderr,
+			"Cannot read smbios position for \"%s\"\n", dir);
+		return -1;
+	}
+
+	dir_name = malloc(strlen(dir) + 1);
+	if (!dir_name) {
+		fprintf(stderr, "Cannot allocate memory for dir\n");
+		return -1;
+	}
+	strcpy(dir_name, dir);
+
+	entry = malloc(sizeof(struct dmi_entry));
+	if (!entry) {
+		fprintf(stderr, "Cannot allocate memory for dmi struct\n");
+		free(dir_name);
+		return -1;
+	}
+
+	entry->d_name = dir_name;
+	entry->position = position;
+
+	if (dmilist_insert(dmi_list, entry)) {
+		fprintf(stderr, "Cannot insert new dmientry in the list");
+		dmilist_free_entry(entry);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * gather_entry_data - reads data from given dmi directory raw attribute end
+ * binds it with given dmi entry.
+ * @entry: dmi entry for what data will be read
+ * @rdata: pointer to array where data will be read in
+ * @max_size: maximum data size possible to read for now
+ *
+ * Returns read raw data size including two NULLs at the end
+ * On error returns 0
+ */
+static unsigned int gather_entry_data(struct dmi_entry *entry,
+				      unsigned char *rdata,
+				      unsigned int max_size)
+{
+	unsigned int dsize;
+
+	dsize = read_raw(entry->d_name, rdata, max_size);
+	if (dsize < MIN_DMI_STRUCT_SIZE) {
+		fprintf(stderr,
+			"Cannot read DMI raw for \"%s\"\n", entry->d_name);
+		return 0;
+	}
+
+	entry->h.data = rdata;
+	entry->h.type = rdata[0];
+	entry->h.length = rdata[1];
+	entry->h.handle = le16toh(rdata[2] | (rdata[3] << 8));
+
+	return dsize;
+}
+
+/**
+ * populate_dmilist - populate dmi entries in dmi list by raw data
+ * @dmi_list: dmi list with dmi entries to be populated
+ * @max_size: supposed size of whole dmi table. Can be taken from
+ *	table SMBIOS entry.
+ *
+ * Returns dmi raw table size on success, otherwise 0.
+ */
+static unsigned int populate_dmilist(struct dmi_entry *dmi_list,
+				     unsigned int max_size)
+{
+	unsigned int dsize;
+	struct dmi_entry *entry;
+	unsigned char *raw_table, *tp;
+
+	/* allocate memory for whole dmi table */
+	raw_table = malloc(max_size);
+	if (!raw_table) {
+		fprintf(stderr, "Cannot allocate memory for DMI table\n");
+		return 0;
+	}
+
+	tp = raw_table;
+	for (entry = dmi_list; entry; entry = entry->next) {
+		if (max_size < MIN_DMI_STRUCT_SIZE) {
+			fprintf(stderr, "Max size of DMI table is reached\n");
+			goto err;
+		}
+
+		if (chdir(entry->d_name)) {
+			fprintf(stderr, "Cannot change dir to %s\n",
+				entry->d_name);
+			goto err;
+		}
+
+		dsize = gather_entry_data(entry, tp, max_size);
+		if (dsize < MIN_DMI_STRUCT_SIZE) {
+			if (chdir("../"))
+				fprintf(stderr, "Cannot change dir to ../\n");
+			goto err;
+		}
+
+		if (chdir("../")) {
+			fprintf(stderr, "Cannot change dir to ../\n");
+			goto err;
+		}
+
+		tp += dsize;
+		max_size -= dsize;
+	}
+
+	return tp - raw_table;
+
+err:
+	fprintf(stderr, "Cannot gather data for dir %s\n", entry->d_name);
+	free(raw_table);
+	return 0;
+}
+
+/**
+ * create_dmilist - creates ordered linked dmi list with empty entries.
+ * The empty entry is entry with empty dmi header data
+ * @entry_count: pointer to dmi entry count to be updated while creating.
+ *
+ * On success returns dmi_entry pointer on first entry in the list
+ * Otherwise returns NULL. Updates entry_count.
+ */
+static struct dmi_entry *create_dmilist(unsigned int *entry_count)
+{
+	DIR *dmi;
+	char *cwd;
+	void *ret = NULL;
+	struct dirent *dir_entry;
+	struct dmi_entry *dmi_list = NULL;
+
+	dmi = opendir(".");
+	if (!dmi) {
+		fprintf(stderr, "Cannot open cwd\n");
+		return ret;
+	}
+
+	*entry_count = 0;
+	readdir(dmi); /* miss "." */
+	readdir(dmi); /* miss ".." */
+	while ((dir_entry = readdir(dmi))) {
+		if (chdir(dir_entry->d_name)) {
+			fprintf(stderr, "Cannot change dir to %s\n",
+				dir_entry->d_name);
+			goto err;
+		}
+
+		if (add_entry_to_dmilist(&dmi_list, dir_entry->d_name)) {
+			fprintf(stderr, "Cannot add \"%s\" to dmi_list\n",
+				dir_entry->d_name);
+			if (chdir("../"))
+				fprintf(stderr, "Cannot change dir to ../\n");
+			goto err;
+		}
+
+		if (chdir("../")) {
+			fprintf(stderr, "Cannot change dir to ../\n");
+			goto err;
+		}
+
+		(*entry_count)++;
+	}
+	/* now dmilist is complete */
+	ret = dmi_list;
+err:
+	if (closedir(dmi)) {
+		cwd = getcwd(NULL, 1024);
+		fprintf(stderr, "Cannot close %s", cwd);
+		free(cwd);
+		ret = NULL;
+	}
+
+	if (ret == dmi_list)
+		return dmi_list;
+
+	dmilist_free(&dmi_list);
+	return ret;
+}
+
+/**
+ * dmi_put_dmilist - frees dmi list alloted by dmi_get_dmilist()
+ * @dmi_list: dmi list to be free
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+static int dmi_put_dmilist(struct dmi_entry *dmi_list)
+{
+	unsigned char *raw_table;
+
+	if (!dmi_list) {
+		fprintf(stderr, "Cannot free dmi_list NULL pointer\n");
+		return -1;
+	}
+
+	raw_table = dmi_list->h.data;
+	if (!raw_table) {
+		fprintf(stderr, "Cannot free raw_table NULL pointer\n");
+		return -1;
+	}
+
+	free(raw_table);
+	dmilist_free(&dmi_list);
+
+	return 0;
+}
+
+/**
+ * dmi_get_dmilist - creates ordered dmi list with all dmi entries present
+ * on dmi sysfs. All raw entries data are situated contiguously in the RAM.
+ * @entry_count: read count of dmi list entries, that's dmi structs in dmi table
+ * @dmi_size: read whole size in bytes of dmi table.
+ * @max_size: supposed size of whole dmi table. Can be taken from SMBIOS
+ *	entry point table.
+ *
+ * Returns a pointer to first dmi entry in the list. When list is no longer
+ * needed it has to be freed with dmi_put_dmilist().
+ * In case of error returns NULL.
+ */
+static struct dmi_entry *dmi_get_dmilist(unsigned int *entry_count,
+					 unsigned int *dmi_size,
+					 unsigned int max_size)
+{
+	char *cwd;
+	struct dmi_entry *dmi_list = NULL;
+	char root[] = "/sys/firmware/dmi/entries/";
+
+	cwd = getcwd(NULL, 1024);
+	if (!cwd) {
+		fprintf(stderr, "Cannot get current directory\n");
+		return NULL;
+	}
+
+	if (chdir(root)) {
+		fprintf(stderr, "Cannot change dir to %s\n", root);
+		goto err;
+	}
+
+	/*
+	 * Create dmi_list then fill it in. Do this in two steps to
+	 * have contiguous raw data for whole table
+	 */
+	dmi_list = create_dmilist(entry_count);
+	if (!dmi_list) {
+		fprintf(stderr, "Cannot create dmi_list\n");
+		goto err;
+	}
+
+	*dmi_size = populate_dmilist(dmi_list, max_size);
+	if (*dmi_size == 0) {
+		dmilist_free(&dmi_list);
+		fprintf(stderr, "Cannot fill in dmi_list by data from raw\n");
+		goto err;
+	}
+
+err:
+	if (chdir(cwd)) {
+		fprintf(stderr, "Cannot change root dir to %s\n", cwd);
+		dmi_put_dmilist(dmi_list);
+		dmi_list = NULL;
+	}
+
+	free(cwd);
+	return dmi_list;
+}
+
+static int dmi_checksum(unsigned char *buf, unsigned char len)
+{
+	int i;
+	unsigned char sum = 0;
+
+	for (i = 0; i < len; i++)
+		sum += buf[i];
+
+	return sum == 0;
+}
+
+/**
+ * dmi_get_expected_size - read dmi table size from SMBIOS entry point table.
+ * Verify SMBIOS entry point table checksum.
+ *
+ * Returns dmi table size read from SMBIOS entry point table. Return 0 on error.
+ */
+static unsigned int dmi_get_expected_size(unsigned char *smbios)
+{
+	if (!memcmp(smbios, "_SM3_", 5) &&
+	    dmi_checksum(smbios, smbios[0x06]))
+		return le32toh(smbios[0x0C] | (smbios[0x0D] << 8) |
+			       (smbios[0x0E] << 16) | (smbios[0x0F] << 24));
+	else if (!memcmp(smbios, "_SM_", 4) &&
+		 dmi_checksum(smbios, smbios[0x05]) &&
+		 dmi_checksum(smbios + 0x10, 0x0F))
+		return le16toh(smbios[0x16] | (smbios[0x17] << 8));
+	else if (!memcmp(smbios, "_DMI_", 5) &&
+		 dmi_checksum(smbios, 0x0F))
+		return le16toh(smbios[0x06] | (smbios[0x07] << 8));
+
+	return 0;
+}
+
+/* API */
+
+/**
+ * dmi_get_table - allocate dmi table
+ *
+ * Returns pointer on allocated dmi_table struct on success,
+ * otherwise returns NULL. When table is not needed it has to be
+ * put by dmi_put_table.
+ */
+struct dmi_table *dmi_get_table(void)
+{
+	struct dmi_table *dt;
+	unsigned int max_dmisize;
+
+	dt = malloc(sizeof(struct dmi_table));
+
+	if (!dmi_get_smbios(dt->smbios))
+		goto err;
+
+	max_dmisize = dmi_get_expected_size(dt->smbios);
+	if (max_dmisize < MIN_DMI_STRUCT_SIZE) {
+		fprintf(stderr, "SMBIOS entry point is incorrect\n");
+		goto err;
+	}
+
+	dt->dmi_list = dmi_get_dmilist(&dt->dmi_count,
+				       &dt->dmi_size, max_dmisize + 1);
+	if (!dt->dmi_list)
+		goto err;
+
+	return dt;
+
+err:
+	free(dt);
+	return NULL;
+}
+
+/**
+ * dmi_put_table - used in pair with dmi_get_dmitable
+ * @dt: dmi table pointer taken by dmi_get_table
+ */
+int dmi_put_table(struct dmi_table *dt)
+{
+	int ret = -1;
+
+	if (!dt) {
+		fprintf(stderr, "pointer for DMI table is incorrect\n");
+		return ret;
+	}
+
+	ret = dmi_put_dmilist(dt->dmi_list);
+	free(dt);
+
+	return ret;
+}
+
+/**
+ * dmi_get_raw_data - give a pointer to raw dmi table, all raw dmi entries are
+ * contiguously situated within this memory in original right order.
+ * @dmi_list: dmi list only taken by dmi_get_dmilist
+ */
+void *dmi_get_raw_data(struct dmi_entry *dmi_list)
+{
+	return dmi_list->h.data;
+}
+
+/**
+ * dmi_get_entry_by_handle - searches an entry in dmi list by given handle
+ * @dmi_list: dmi list to search for
+ * @handle: handle to find entry with
+ *
+ * Returns entry with given handle on success and NULL otherwise
+ */
+struct dmi_entry *dmi_get_entry_by_handle(struct dmi_entry *dmi_list,
+					  int handle)
+{
+	struct dmi_entry *entry;
+
+	for (entry = dmi_list; entry; entry = entry->next)
+		if (handle == entry->h.handle)
+			return entry;
+
+	return NULL;
+}
+
+/**
+ * dmi_get_next_entry_by_type - searches a next entry in dmi list by type
+ * @dmi_entry: the dmi_entry in dmi list to search beginnig with
+ * @type: type of dmi entry to find
+ */
+struct dmi_entry *dmi_get_next_entry_by_type(struct dmi_entry *dmi_entry,
+					     unsigned char type)
+{
+	struct dmi_entry *entry;
+
+	for (entry = dmi_entry->next; entry; entry = entry->next)
+		if (type == entry->h.type)
+			return entry;
+
+	return NULL;
+}
+
+/**
+ * dmifs_is_exist - checks if dmi sysfs exists
+ *
+ * Returns 1 on success and 0 if no
+ */
+int dmifs_is_exist(void)
+{
+	int ret;
+	char dmi_cat[] = "/sys/firmware/dmi/entries/";
+	DIR *dmi_dir = opendir(dmi_cat);
+
+	ret = dmi_dir ? 1 : 0;
+
+	if (closedir(dmi_dir))
+		fprintf(stderr, "Cannot close %s", dmi_cat);
+
+	return ret;
+}
diff --git a/libdmifs.h b/libdmifs.h
new file mode 100644
index 0000000..218d07f
--- /dev/null
+++ b/libdmifs.h
@@ -0,0 +1,56 @@
+#ifndef LIBDMIFS_H
+#define LIBDMIFS_H
+
+/**
+ * dmi_hdata - information of dmi entry header
+ * type: type of dmi entry
+ * length: length of formated area of dmi entry
+ * data: pointer to whole raw dmi entry data
+ */
+struct dmi_hdata {
+	unsigned char type;
+	unsigned char length;
+	unsigned short handle;
+	unsigned char *data;
+};
+
+/**
+ * dmi_entry - holds information about dmi entry
+ * next: internal in list pointer
+ * h: dmi header data, including raw data
+ * position: position within the dmi table
+ * d_name: dirctory entry name in sysfs
+ */
+struct dmi_entry {
+	struct dmi_entry *next;
+	struct dmi_hdata h;
+	unsigned int position;
+	char *d_name;
+};
+
+/**
+ * dmi_table - contains whole information about smbios/dmi tables
+ * @smbios: pointer to raw data of smbios table
+ * @dmi_count: number of dmi entries in dmi table
+ * @dmi_size: length of dmi table in bytes
+ * @dmi_list: single list of dmi table entries, raw data of which contiguously
+ *	situated in memnory. The list can be used to take a pointer to raw data
+ *	of whole dmi table.
+ */
+struct dmi_table {
+	unsigned char smbios[32];
+	unsigned int dmi_count;
+	unsigned int dmi_size;
+	struct dmi_entry *dmi_list;
+};
+
+int dmifs_is_exist(void);
+struct dmi_table *dmi_get_table(void);
+int dmi_put_table(struct dmi_table *dt);
+void *dmi_get_raw_data(struct dmi_entry *dmi_list);
+struct dmi_entry *dmi_get_entry_by_handle(struct dmi_entry *dmi_list,
+					  int handle);
+struct dmi_entry *dmi_get_next_entry_by_type(struct dmi_entry *dmi_entry,
+					     unsigned char type);
+
+#endif
--
1.9.3