[PATCH 13/13] ACPI/IPMI: Add IPMI operation region test device driver

From: Lv Zheng
Date: Tue Jul 23 2013 - 04:11:11 EST


This patch is only used for test purpose and should not be merged by any
public Linux kernel repositories.

This patch contains one driver that can drive a fake test device accessing
IPMI operation region fields.

Signed-off-by: Lv Zheng <lv.zheng@xxxxxxxxx>
---
drivers/acpi/Kconfig | 68 +++++++++++++
drivers/acpi/Makefile | 1 +
drivers/acpi/ipmi_test.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 323 insertions(+)
create mode 100644 drivers/acpi/ipmi_test.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index d129869..e3dd3fd 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -377,6 +377,74 @@ config ACPI_BGRT
data from the firmware boot splash. It will appear under
/sys/firmware/acpi/bgrt/ .

+config ACPI_IPMI_TEST
+ tristate "IPMI operation region tester"
+ help
+ This is a test device written for such fake ACPI namespace device.
+ Device (PMIT)
+ {
+ Name (_HID, "ZETA0000") // _HID: Hardware ID
+ Name (_STA, 0x0F) // _STA: Status
+ OperationRegion (SYSI, IPMI, 0x0600, 0x0100)
+ Field (SYSI, BufferAcc, Lock, Preserve)
+ {
+ AccessAs (BufferAcc, 0x01),
+ Offset (0x01),
+ GDIC, 8, // Get Device ID Command
+ }
+ Method (GDIM, 0, NotSerialized) // GDIM: Get Device ID Method
+ {
+ Name (GDIR, Package (0x08)
+ {
+ 0x00,
+ 0x00,
+ 0x0000,
+ 0x00,
+ 0x00,
+ Buffer (0x03) {0x00, 0x00, 0x00},
+ Buffer (0x02) {0x00, 0x00},
+ 0x00000000
+ })
+ Name (BUFF, Buffer (0x42) {})
+ CreateByteField (BUFF, 0x00, STAT)
+ CreateByteField (BUFF, 0x01, LENG)
+ CreateByteField (BUFF, 0x02, CMPC)
+ CreateByteField (BUFF, 0x03, DID)
+ CreateByteField (BUFF, 0x04, DREV)
+ CreateWordField (BUFF, 0x05, FREV)
+ CreateByteField (BUFF, 0x07, SREV)
+ CreateByteField (BUFF, 0x08, ADS)
+ CreateByteField (BUFF, 0x09, VID0)
+ CreateByteField (BUFF, 0x0A, VID1)
+ CreateByteField (BUFF, 0x0B, VID2)
+ CreateByteField (BUFF, 0x0C, PID0)
+ CreateByteField (BUFF, 0x0D, PID1)
+ CreateDWordField (BUFF, 0x0E, AFRI)
+ Store (0x00, LENG)
+ Store (Store (BUFF, GDIC), BUFF)
+ If (LAnd (LEqual (STAT, 0x00), LEqual (CMPC, 0x00)))
+ {
+ Name (VBUF, Buffer (0x03) { 0x00, 0x00, 0x00 })
+ Name (PBUF, Buffer (0x02) { 0x00, 0x00 })
+ Store (DID, Index (GDIR, 0x00))
+ Store (DREV, Index (GDIR, 0x01))
+ Store (FREV, Index (GDIR, 0x02))
+ Store (SREV, Index (GDIR, 0x03))
+ Store (ADS, Index (GDIR, 0x04))
+ Store (VID0, Index (VBUF, 0x00))
+ Store (VID1, Index (VBUF, 0x01))
+ Store (VID2, Index (VBUF, 0x02))
+ Store (VBUF, Index (GDIR, 0x05))
+ Store (PID0, Index (PBUF, 0x00))
+ Store (PID1, Index (PBUF, 0x01))
+ Store (PBUF, Index (GDIR, 0x06))
+ Store (AFRI, Index (GDIR, 0x07))
+ }
+ Return (GDIR)
+ }
+ }
+ It is for validation purpose, only calls "Get Device ID" command.
+
source "drivers/acpi/apei/Kconfig"

endif # ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 81dbeb8..1476623 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_I2C) += acpi_i2c.o
+obj-$(CONFIG_ACPI_IPMI_TEST) += ipmi_test.o

# processor has its own "processor." module_param namespace
processor-y := processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/ipmi_test.c b/drivers/acpi/ipmi_test.c
new file mode 100644
index 0000000..5d144e4
--- /dev/null
+++ b/drivers/acpi/ipmi_test.c
@@ -0,0 +1,254 @@
+/*
+ * An IPMI operation region tester driver
+ *
+ * Copyright (C) 2013 Intel Corporation
+ * Author: Lv Zheng <lv.zheng@xxxxxxxxx>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+
+#define ACPI_IPMI_TEST_NAME "ipmi_test"
+ACPI_MODULE_NAME(ACPI_IPMI_TEST_NAME);
+
+#define ACPI_IPMI_TEST_DEVICE "IPMI Test"
+#define ACPI_IPMI_TEST_CLASS "ipmi_tester"
+
+static const struct acpi_device_id acpi_ipmi_test_ids[] = {
+ {"ZETA0000", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, acpi_ipmi_test_ids);
+
+struct acpi_ipmi_device_id {
+ u64 device_id;
+ u64 device_rev;
+ u64 firmware_rev;
+ u64 ipmi_version;
+ u64 additional_dev_support;
+ u8 *vendor_id;
+ u8 *product_id;
+ u64 aux_firm_rev_info;
+ u8 extra_buf[5];
+} __packed;
+
+struct acpi_ipmi_tester {
+ struct acpi_device *adev;
+ acpi_bus_id name;
+ struct acpi_ipmi_device_id device_id;
+ int registered_group;
+};
+
+#define ipmi_err(tester, fmt, ...) \
+ dev_err(&(tester)->adev->dev, fmt, ##__VA_ARGS__)
+#define ipmi_info(tester, fmt, ...) \
+ dev_info(&(tester)->adev->dev, fmt, ##__VA_ARGS__)
+#define IPMI_ACPI_HANDLE(tester) ((tester)->adev->handle)
+
+static int acpi_ipmi_update_device_id(struct acpi_ipmi_tester *tester)
+{
+ int res = 0;
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer format = { sizeof("NNNNNBBN"), "NNNNNBBN" };
+ struct acpi_buffer device_id = { 0, NULL };
+ union acpi_object *did;
+
+ status = acpi_evaluate_object(IPMI_ACPI_HANDLE(tester), "GDIM", NULL,
+ &buffer);
+ if (ACPI_FAILURE(status) || !buffer.pointer) {
+ ipmi_err(tester, "Evaluating GDIM, status - %d\n", status);
+ return -ENODEV;
+ }
+
+ did = buffer.pointer;
+ if (did->type != ACPI_TYPE_PACKAGE || did->package.count != 8) {
+ ipmi_err(tester, "Invalid GDIM data, type - %d, count - %d\n",
+ did->type, did->package.count);
+ res = -EFAULT;
+ goto err_buf;
+ }
+
+ device_id.length = sizeof(struct acpi_ipmi_device_id);
+ device_id.pointer = &tester->device_id;
+
+ status = acpi_extract_package(did, &format, &device_id);
+ if (ACPI_FAILURE(status)) {
+ ipmi_err(tester, "Invalid GDIM data\n");
+ res = -EFAULT;
+ goto err_buf;
+ }
+
+err_buf:
+ kfree(buffer.pointer);
+ return res;
+}
+
+static ssize_t show_device_id(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *adev = to_acpi_device(device);
+ struct acpi_ipmi_tester *tester = adev->driver_data;
+
+ acpi_ipmi_update_device_id(tester);
+ return sprintf(buf, "%llu\n", tester->device_id.device_id);
+}
+
+static ssize_t show_device_rev(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *adev = to_acpi_device(device);
+ struct acpi_ipmi_tester *tester = adev->driver_data;
+
+ acpi_ipmi_update_device_id(tester);
+ return sprintf(buf, "%llu\n", tester->device_id.device_rev);
+}
+
+static ssize_t show_firmware_rev(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *adev = to_acpi_device(device);
+ struct acpi_ipmi_tester *tester = adev->driver_data;
+
+ acpi_ipmi_update_device_id(tester);
+ return sprintf(buf, "%llu\n", tester->device_id.firmware_rev);
+}
+
+static ssize_t show_ipmi_version(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *adev = to_acpi_device(device);
+ struct acpi_ipmi_tester *tester = adev->driver_data;
+
+ acpi_ipmi_update_device_id(tester);
+ return sprintf(buf, "%llu\n", tester->device_id.ipmi_version);
+}
+
+static ssize_t show_vendor_id(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *adev = to_acpi_device(device);
+ struct acpi_ipmi_tester *tester = adev->driver_data;
+
+ acpi_ipmi_update_device_id(tester);
+ return sprintf(buf, "%02x %02x %02x\n",
+ tester->device_id.vendor_id[0],
+ tester->device_id.vendor_id[1],
+ tester->device_id.vendor_id[2]);
+}
+
+static ssize_t show_product_id(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *adev = to_acpi_device(device);
+ struct acpi_ipmi_tester *tester = adev->driver_data;
+
+ acpi_ipmi_update_device_id(tester);
+ return sprintf(buf, "%02x %02x\n",
+ tester->device_id.product_id[0],
+ tester->device_id.product_id[1]);
+}
+
+static DEVICE_ATTR(device_id, S_IRUGO, show_device_id, NULL);
+static DEVICE_ATTR(device_rev, S_IRUGO, show_device_rev, NULL);
+static DEVICE_ATTR(firmware_rev, S_IRUGO, show_firmware_rev, NULL);
+static DEVICE_ATTR(ipmi_version, S_IRUGO, show_ipmi_version, NULL);
+static DEVICE_ATTR(vendor_id, S_IRUGO, show_vendor_id, NULL);
+static DEVICE_ATTR(product_id, S_IRUGO, show_product_id, NULL);
+
+static struct attribute *acpi_ipmi_test_attrs[] = {
+ &dev_attr_device_id.attr,
+ &dev_attr_device_rev.attr,
+ &dev_attr_firmware_rev.attr,
+ &dev_attr_ipmi_version.attr,
+ &dev_attr_vendor_id.attr,
+ &dev_attr_product_id.attr,
+ NULL,
+};
+
+static struct attribute_group acpi_ipmi_test_group = {
+ .attrs = acpi_ipmi_test_attrs,
+};
+
+static int acpi_ipmi_test_add(struct acpi_device *device)
+{
+ struct acpi_ipmi_tester *tester;
+
+ if (!device)
+ return -EINVAL;
+
+ tester = kzalloc(sizeof(struct acpi_ipmi_tester), GFP_KERNEL);
+ if (!tester)
+ return -ENOMEM;
+
+ tester->adev = device;
+ strcpy(acpi_device_name(device), ACPI_IPMI_TEST_DEVICE);
+ strcpy(acpi_device_class(device), ACPI_IPMI_TEST_CLASS);
+ device->driver_data = tester;
+ if (sysfs_create_group(&device->dev.kobj, &acpi_ipmi_test_group) == 0)
+ tester->registered_group = 1;
+
+ return 0;
+}
+
+static int acpi_ipmi_test_remove(struct acpi_device *device)
+{
+ struct acpi_ipmi_tester *tester;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+
+ tester = acpi_driver_data(device);
+
+ if (tester->registered_group)
+ sysfs_remove_group(&device->dev.kobj, &acpi_ipmi_test_group);
+
+ kfree(tester);
+ return 0;
+}
+
+static struct acpi_driver acpi_ipmi_test_driver = {
+ .name = "ipmi_test",
+ .class = ACPI_IPMI_TEST_CLASS,
+ .ids = acpi_ipmi_test_ids,
+ .ops = {
+ .add = acpi_ipmi_test_add,
+ .remove = acpi_ipmi_test_remove,
+ },
+};
+
+static int __init acpi_ipmi_test_init(void)
+{
+ return acpi_bus_register_driver(&acpi_ipmi_test_driver);
+}
+module_init(acpi_ipmi_test_init);
+
+static void __exit acpi_ipmi_test_exit(void)
+{
+ acpi_bus_unregister_driver(&acpi_ipmi_test_driver);
+}
+module_exit(acpi_ipmi_test_exit);
+
+MODULE_AUTHOR("Lv Zheng <lv.zheng@xxxxxxxxx>");
+MODULE_DESCRIPTION("ACPI IPMI operation region tester driver");
+MODULE_LICENSE("GPL");
--
1.7.10

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/