drivers/secunet: Add driver to read DMI info from I2C EEPROM

The EEPROM layout is rather arbitrary and /just happened/. It needs a
256kbit part at least.

Change-Id: Iae5c9138e8404acfc3a43dc2c7b55d47d4147060
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/36298
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/drivers/secunet/dmi/Kconfig b/src/drivers/secunet/dmi/Kconfig
new file mode 100644
index 0000000..7e83f90
--- /dev/null
+++ b/src/drivers/secunet/dmi/Kconfig
@@ -0,0 +1,3 @@
+config SECUNET_DMI
+	bool
+	select SMBIOS_PROVIDED_BY_MOBO
diff --git a/src/drivers/secunet/dmi/Makefile.inc b/src/drivers/secunet/dmi/Makefile.inc
new file mode 100644
index 0000000..9c85485
--- /dev/null
+++ b/src/drivers/secunet/dmi/Makefile.inc
@@ -0,0 +1 @@
+ramstage-$(CONFIG_SECUNET_DMI) += smbios.c
diff --git a/src/drivers/secunet/dmi/eeprom.h b/src/drivers/secunet/dmi/eeprom.h
new file mode 100644
index 0000000..c4cdd41
--- /dev/null
+++ b/src/drivers/secunet/dmi/eeprom.h
@@ -0,0 +1,62 @@
+/*
+ * 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 _SECUNET_DMI_EEPROM_H
+#define _SECUNET_DMI_EEPROM_H
+
+#include <stdint.h>
+
+enum bx26_strings {
+	BOARD_MATNR,
+	BOARD_SERIAL_NUMBER,
+	BOARD_VERSION,
+	BOARD_MCTRL_FW_VERSION,
+	BOARD_CCR_FW_VERSION,
+	BOARD_NIC_FW_VERSION,
+	BOARD_LP_VERSION,
+	BOARD_VERSION_ID,
+
+	SYSTEM_PRODUCT_NAME,
+	SYSTEM_VERSION,
+	SYSTEM_SERIAL_NUMBER,
+	SYSTEM_UUID,
+	SYSTEM_MANUFACTURER,
+	SYSTEM_PRODUCTION_DATE,
+	SYSTEM_MLFB,
+	SYSTEM_MATNR,
+};
+
+struct bx26_location {
+	uint16_t offset;
+	uint16_t length;
+};
+
+static const struct bx26_location bx26_locations[] = {
+	[BOARD_MATNR]			= { 0x0000, 0x20 },
+	[BOARD_SERIAL_NUMBER]		= { 0x0020, 0x20 },
+	[BOARD_VERSION]			= { 0x0040, 0x20 },
+	[BOARD_MCTRL_FW_VERSION]	= { 0x0060, 0x20 },
+	[BOARD_CCR_FW_VERSION]		= { 0x0080, 0x20 },
+	[BOARD_NIC_FW_VERSION]		= { 0x00a0, 0x20 },
+	[BOARD_LP_VERSION]		= { 0x00c0, 0x20 },
+	[BOARD_VERSION_ID]		= { 0x0100, 0x20 },
+
+	[SYSTEM_PRODUCT_NAME]		= { 0x4000, 0x20 },
+	[SYSTEM_VERSION]		= { 0x4040, 0x10 },
+	[SYSTEM_SERIAL_NUMBER]		= { 0x4060, 0x10 },
+	[SYSTEM_UUID]			= { 0x4080, 0x24 },
+	[SYSTEM_MANUFACTURER]		= { 0x40c0, 0x20 },
+	[SYSTEM_PRODUCTION_DATE]	= { 0x4100, 0x20 },
+	[SYSTEM_MLFB]			= { 0x4140, 0x20 },
+	[SYSTEM_MATNR]			= { 0x4180, 0x20 },
+};
+
+#endif
diff --git a/src/drivers/secunet/dmi/smbios.c b/src/drivers/secunet/dmi/smbios.c
new file mode 100644
index 0000000..5dfbb5d
--- /dev/null
+++ b/src/drivers/secunet/dmi/smbios.c
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 secunet Security Networks AG
+ *
+ * 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 <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <commonlib/helpers.h>
+#include <uuid.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/i2c_bus.h>
+#include <smbios.h>
+
+#include "eeprom.h"
+
+#define MAX_STRING_LENGTH UUID_STRLEN
+
+static struct device *eeprom;
+
+static const char *eeprom_read_string(const enum bx26_strings idx)
+{
+	static char str[MAX_STRING_LENGTH + 1];
+
+	if (!eeprom) {
+		printk(BIOS_WARNING, "DMI: Serial EEPROM not found\n");
+		str[0] = '\0';
+		return str;
+	}
+
+	const size_t offset = bx26_locations[idx].offset;
+	const size_t length = MIN(bx26_locations[idx].length, MAX_STRING_LENGTH);
+
+	if (i2c_dev_read_at16(eeprom, (u8 *)str, length, offset) != length) {
+		printk(BIOS_WARNING, "DMI: Failed to read serial EEPROM\n");
+		str[0] = '\0';
+	} else {
+		unsigned int i;
+		/* Terminate at first non-printable character. */
+		for (i = 0; i < length; ++i) {
+			if (!isprint(str[i]))
+				break;
+		}
+		str[i] = '\0';
+	}
+
+	return str;
+}
+
+const char *smbios_system_manufacturer(void)
+{
+	return eeprom_read_string(SYSTEM_MANUFACTURER);
+}
+
+const char *smbios_system_product_name(void)
+{
+	return eeprom_read_string(SYSTEM_PRODUCT_NAME);
+}
+
+const char *smbios_system_serial_number(void)
+{
+	return eeprom_read_string(SYSTEM_SERIAL_NUMBER);
+}
+
+const char *smbios_system_version(void)
+{
+	return eeprom_read_string(SYSTEM_VERSION);
+}
+
+void smbios_system_set_uuid(u8 *const uuid)
+{
+	if (parse_uuid(uuid, eeprom_read_string(SYSTEM_UUID))) {
+		printk(BIOS_WARNING, "DMI: Cannot parse UUID\n");
+		memset(uuid, 0x00, UUID_LEN);
+	}
+}
+
+const char *smbios_mainboard_serial_number(void)
+{
+	return eeprom_read_string(BOARD_SERIAL_NUMBER);
+}
+
+const char *smbios_mainboard_version(void)
+{
+	return eeprom_read_string(BOARD_VERSION);
+}
+
+static void enable_dev(struct device *dev)
+{
+	if (dev->path.type != DEVICE_PATH_I2C || (dev->path.i2c.device & 0xf0) != 0x50)
+		return;
+	eeprom = dev;
+}
+
+struct chip_operations drivers_secunet_dmi_ops = {
+	CHIP_NAME("secunet DMI")
+	.enable_dev = enable_dev,
+};