diff mbox

[v9,17/17] tools/wmi: add a sample for dell smbios communication over WMI

Message ID a60ca82dca6e453f7bd920c082de48ea4378b7d2.1508259916.git.mario.limonciello@dell.com (mailing list archive)
State Superseded, archived
Delegated to: Darren Hart
Headers show

Commit Message

Limonciello, Mario Oct. 17, 2017, 6:22 p.m. UTC
This application uses the character device /dev/wmi/dell-smbios
to perform SMBIOS communications from userspace.

It offers demonstrations of a few simple tasks:
 - Running a class/select command
 - Querying a token value
 - Activating a token

Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
 MAINTAINERS                     |   1 +
 tools/Makefile                  |  14 +--
 tools/wmi/Makefile              |  18 ++++
 tools/wmi/dell-smbios-example.c | 214 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 241 insertions(+), 6 deletions(-)
 create mode 100644 tools/wmi/Makefile
 create mode 100644 tools/wmi/dell-smbios-example.c

Comments

Edward O'Callaghan Oct. 18, 2017, 2:33 a.m. UTC | #1
Reviewed-by: Edward O'Callaghan <quasisec@google.com>

On Wed, Oct 18, 2017 at 5:22 AM, Mario Limonciello
<mario.limonciello@dell.com> wrote:
> This application uses the character device /dev/wmi/dell-smbios
> to perform SMBIOS communications from userspace.
>
> It offers demonstrations of a few simple tasks:
>  - Running a class/select command
>  - Querying a token value
>  - Activating a token
>
> Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
> ---
>  MAINTAINERS                     |   1 +
>  tools/Makefile                  |  14 +--
>  tools/wmi/Makefile              |  18 ++++
>  tools/wmi/dell-smbios-example.c | 214 ++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 241 insertions(+), 6 deletions(-)
>  create mode 100644 tools/wmi/Makefile
>  create mode 100644 tools/wmi/dell-smbios-example.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 84afbf8ef7d5..64529a3f6910 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3997,6 +3997,7 @@ M:        Mario Limonciello <mario.limonciello@dell.com>
>  L:     platform-driver-x86@vger.kernel.org
>  S:     Maintained
>  F:     drivers/platform/x86/dell-smbios-wmi.c
> +F:     tools/wmi/dell-smbios-example.c
>
>  DELL LAPTOP DRIVER
>  M:     Matthew Garrett <mjg59@srcf.ucam.org>
> diff --git a/tools/Makefile b/tools/Makefile
> index 9dfede37c8ff..9d2fd2606810 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -29,6 +29,7 @@ help:
>         @echo '  usb                    - USB testing tools'
>         @echo '  virtio                 - vhost test module'
>         @echo '  vm                     - misc vm tools'
> +       @echo '  wmi                    - WMI interface examples'
>         @echo '  x86_energy_perf_policy - Intel energy policy tool'
>         @echo ''
>         @echo 'You can do:'
> @@ -57,7 +58,7 @@ acpi: FORCE
>  cpupower: FORCE
>         $(call descend,power/$@)
>
> -cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds: FORCE
> +cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds wmi: FORCE
>         $(call descend,$@)
>
>  liblockdep: FORCE
> @@ -92,7 +93,7 @@ kvm_stat: FORCE
>  all: acpi cgroup cpupower gpio hv firewire liblockdep \
>                 perf selftests spi turbostat usb \
>                 virtio vm net x86_energy_perf_policy \
> -               tmon freefall iio objtool kvm_stat
> +               tmon freefall iio objtool kvm_stat wmi
>
>  acpi_install:
>         $(call descend,power/$(@:_install=),install)
> @@ -100,7 +101,7 @@ acpi_install:
>  cpupower_install:
>         $(call descend,power/$(@:_install=),install)
>
> -cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install net_install objtool_install:
> +cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install net_install objtool_install wmi_install:
>         $(call descend,$(@:_install=),install)
>
>  liblockdep_install:
> @@ -125,7 +126,8 @@ install: acpi_install cgroup_install cpupower_install gpio_install \
>                 hv_install firewire_install iio_install liblockdep_install \
>                 perf_install selftests_install turbostat_install usb_install \
>                 virtio_install vm_install net_install x86_energy_perf_policy_install \
> -               tmon_install freefall_install objtool_install kvm_stat_install
> +               tmon_install freefall_install objtool_install kvm_stat_install \
> +               wmi_install
>
>  acpi_clean:
>         $(call descend,power/acpi,clean)
> @@ -133,7 +135,7 @@ acpi_clean:
>  cpupower_clean:
>         $(call descend,power/cpupower,clean)
>
> -cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
> +cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
>         $(call descend,$(@:_clean=),clean)
>
>  liblockdep_clean:
> @@ -171,6 +173,6 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
>                 perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
>                 vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
>                 freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
> -               gpio_clean objtool_clean leds_clean
> +               gpio_clean objtool_clean leds_clean wmi_clean
>
>  .PHONY: FORCE
> diff --git a/tools/wmi/Makefile b/tools/wmi/Makefile
> new file mode 100644
> index 000000000000..e664f1167388
> --- /dev/null
> +++ b/tools/wmi/Makefile
> @@ -0,0 +1,18 @@
> +PREFIX ?= /usr
> +SBINDIR ?= sbin
> +INSTALL ?= install
> +CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
> +CC = $(CROSS_COMPILE)gcc
> +
> +TARGET = dell-smbios-example
> +
> +all: $(TARGET)
> +
> +%: %.c
> +       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
> +
> +clean:
> +       $(RM) $(TARGET)
> +
> +install: dell-smbios-example
> +       $(INSTALL) -D -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/$(SBINDIR)/$(TARGET)
> diff --git a/tools/wmi/dell-smbios-example.c b/tools/wmi/dell-smbios-example.c
> new file mode 100644
> index 000000000000..69c4dd9c6056
> --- /dev/null
> +++ b/tools/wmi/dell-smbios-example.c
> @@ -0,0 +1,214 @@
> +/*
> + *  Sample application for SMBIOS communication over WMI interface
> + *  Performs the following:
> + *  - Simple class/select lookup for TPM information
> + *  - Simple query of known tokens and their values
> + *  - Simple activation of a token
> + *
> + *  Copyright (C) 2017 Dell, Inc.
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License version 2 as
> + *  published by the Free Software Foundation.
> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/ioctl.h>
> +#include <unistd.h>
> +
> +/* if uapi header isn't installed, this might not yet exist */
> +#ifndef __packed
> +#define __packed __attribute__((packed))
> +#endif
> +#include <linux/wmi.h>
> +
> +/* It would be better to discover these using udev, but for a simple
> + * application they're hardcoded
> + */
> +static const char *ioctl_devfs = "/dev/wmi/dell-smbios";
> +static const char *token_sysfs =
> +                       "/sys/bus/platform/devices/dell-smbios.0/tokens";
> +static const char *buffer_sysfs =
> +                       "/sys/bus/wmi/devices/A80593CE-A997-11DA-B012-B622A1EF5492/required_buffer_size";
> +
> +static void show_buffer(struct dell_wmi_smbios_buffer *buffer)
> +{
> +       printf("Call: %x/%x [%x,%x,%x,%x]\nResults: [%8x,%8x,%8x,%8x]\n",
> +       buffer->std.class, buffer->std.select,
> +       buffer->std.input[0], buffer->std.input[1],
> +       buffer->std.input[2], buffer->std.input[3],
> +       buffer->std.output[0], buffer->std.output[1],
> +       buffer->std.output[2], buffer->std.output[3]);
> +}
> +
> +static int run_wmi_smbios_cmd(struct dell_wmi_smbios_buffer *buffer)
> +{
> +       int fd;
> +       int ret;
> +
> +       fd = open(ioctl_devfs, O_NONBLOCK);
> +       ret = ioctl(fd, DELL_WMI_SMBIOS_CMD, buffer);
> +       close(fd);
> +       return ret;
> +}
> +
> +static int find_token(__u16 token, __u16 *location, __u16 *value)
> +{
> +       char location_sysfs[60];
> +       char value_sysfs[57];
> +       char buf[4096];
> +       FILE *f;
> +       int ret;
> +
> +       ret = sprintf(value_sysfs, "%s/%04x_value", token_sysfs, token);
> +       if (ret < 0) {
> +               printf("sprintf value failed\n");
> +               return 2;
> +       }
> +       f = fopen(value_sysfs, "rb");
> +       if (!f) {
> +               printf("failed to open %s\n", value_sysfs);
> +               return 2;
> +       }
> +       fread(buf, 1, 4096, f);
> +       fclose(f);
> +       *value = (__u16) strtol(buf, NULL, 16);
> +
> +       ret = sprintf(location_sysfs, "%s/%04x_location", token_sysfs, token);
> +       if (ret < 0) {
> +               printf("sprintf location failed\n");
> +               return 1;
> +       }
> +       f = fopen(location_sysfs, "rb");
> +       if (!f) {
> +               printf("failed to open %s\n", location_sysfs);
> +               return 2;
> +       }
> +       fread(buf, 1, 4096, f);
> +       fclose(f);
> +       *location = (__u16) strtol(buf, NULL, 16);
> +
> +       if (*location)
> +               return 0;
> +       return 2;
> +}
> +
> +static int token_is_active(__u16 *location, __u16 *cmpvalue,
> +                          struct dell_wmi_smbios_buffer *buffer)
> +{
> +       int ret;
> +
> +       buffer->std.class = CLASS_TOKEN_READ;
> +       buffer->std.select = SELECT_TOKEN_STD;
> +       buffer->std.input[0] = *location;
> +       ret = run_wmi_smbios_cmd(buffer);
> +       if (ret != 0 || buffer->std.output[0] != 0)
> +               return ret;
> +       ret = (buffer->std.output[1] == *cmpvalue);
> +       return ret;
> +}
> +
> +static int query_token(__u16 token, struct dell_wmi_smbios_buffer *buffer)
> +{
> +       __u16 location;
> +       __u16 value;
> +       int ret;
> +
> +       ret = find_token(token, &location, &value);
> +       if (ret != 0) {
> +               printf("unable to find token %04x\n", token);
> +               return 1;
> +       }
> +       return token_is_active(&location, &value, buffer);
> +}
> +
> +static int activate_token(struct dell_wmi_smbios_buffer *buffer,
> +                  __u16 token)
> +{
> +       __u16 location;
> +       __u16 value;
> +       int ret;
> +
> +       ret = find_token(token, &location, &value);
> +       if (ret != 0) {
> +               printf("unable to find token %04x\n", token);
> +               return 1;
> +       }
> +       buffer->std.class = CLASS_TOKEN_WRITE;
> +       buffer->std.select = SELECT_TOKEN_STD;
> +       buffer->std.input[0] = location;
> +       buffer->std.input[1] = 1;
> +       ret = run_wmi_smbios_cmd(buffer);
> +       return ret;
> +}
> +
> +static int query_buffer_size(__u64 *buffer_size)
> +{
> +       char buf[4096];
> +       FILE *f;
> +
> +       f = fopen(buffer_sysfs, "rb");
> +       if (!f)
> +               return -EINVAL;
> +       fread(buf, 1, 4096, f);
> +       fclose(f);
> +       *buffer_size = strtol(buf, NULL, 10);
> +       return EXIT_SUCCESS;
> +}
> +
> +int main(void)
> +{
> +       struct dell_wmi_smbios_buffer *buffer;
> +       int ret;
> +       __u64 value = 0;
> +
> +       ret = query_buffer_size(&value);
> +       if (ret == EXIT_FAILURE || !value) {
> +               printf("Unable to read buffer size\n");
> +               goto out;
> +       }
> +       printf("Detected required buffer size %lld\n", value);
> +
> +       buffer = malloc(value);
> +       if (buffer == NULL) {
> +               printf("failed to alloc memory for ioctl\n");
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +       buffer->length = value;
> +
> +       /* simple SMBIOS call for looking up TPM info */
> +       buffer->std.class = CLASS_FLASH_INTERFACE;
> +       buffer->std.select = SELECT_FLASH_INTERFACE;
> +       buffer->std.input[0] = 2;
> +       ret = run_wmi_smbios_cmd(buffer);
> +       if (ret) {
> +               printf("smbios ioctl failed: %d\n", ret);
> +               ret = EXIT_FAILURE;
> +               goto out;
> +       }
> +       show_buffer(buffer);
> +
> +       /* query some tokens */
> +       ret = query_token(CAPSULE_EN_TOKEN, buffer);
> +       printf("UEFI Capsule enabled token is: %d\n", ret);
> +       ret = query_token(CAPSULE_DIS_TOKEN, buffer);
> +       printf("UEFI Capsule disabled token is: %d\n", ret);
> +
> +       /* activate UEFI capsule token if disabled */
> +       if (ret) {
> +               printf("Enabling UEFI capsule token");
> +               if (activate_token(buffer, CAPSULE_EN_TOKEN)) {
> +                       printf("activate failed\n");
> +                       ret = -1;
> +                       goto out;
> +               }
> +       }
> +       ret = EXIT_SUCCESS;
> +out:
> +       free(buffer);
> +       return ret;
> +}
> --
> 2.14.1
>
Pali Rohár Oct. 18, 2017, 7:29 a.m. UTC | #2
On Tuesday 17 October 2017 13:22:01 Mario Limonciello wrote:
> diff --git a/tools/wmi/dell-smbios-example.c b/tools/wmi/dell-smbios-example.c
> new file mode 100644
> index 000000000000..69c4dd9c6056
> --- /dev/null
> +++ b/tools/wmi/dell-smbios-example.c
> @@ -0,0 +1,214 @@
> +/*
> + *  Sample application for SMBIOS communication over WMI interface
> + *  Performs the following:
> + *  - Simple class/select lookup for TPM information
> + *  - Simple query of known tokens and their values
> + *  - Simple activation of a token
> + *
> + *  Copyright (C) 2017 Dell, Inc.
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License version 2 as
> + *  published by the Free Software Foundation.
> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/ioctl.h>
> +#include <unistd.h>
> +
> +/* if uapi header isn't installed, this might not yet exist */
> +#ifndef __packed
> +#define __packed __attribute__((packed))
> +#endif
> +#include <linux/wmi.h>
> +
> +/* It would be better to discover these using udev, but for a simple
> + * application they're hardcoded
> + */
> +static const char *ioctl_devfs = "/dev/wmi/dell-smbios";
> +static const char *token_sysfs =
> +			"/sys/bus/platform/devices/dell-smbios.0/tokens";
> +static const char *buffer_sysfs =
> +			"/sys/bus/wmi/devices/A80593CE-A997-11DA-B012-B622A1EF5492/required_buffer_size";

Greg, Alan, could userspace expects those paths to be part of kernel
<--> userspace ABI? Looking e.g. at "dell-smbios.0" name and I'm not
sure if this is something which is going to be stable between kernel
versions and forever as part of ABI.

Also if everything is part of smbios API, would not it better to provide
everything via IOCTL over /dev/wmi/dell-smbios? I think this code is too
complicated, just because for correct IOCTL buffer size it needs to read
other properties via sysfs, etc... For me it looks like that it is not a
good API for userspace developers.
Limonciello, Mario Oct. 18, 2017, 1:55 p.m. UTC | #3
> -----Original Message-----

> From: Pali Rohár [mailto:pali.rohar@gmail.com]

> Sent: Wednesday, October 18, 2017 2:29 AM

> To: Greg KH <greg@kroah.com>; Alan Cox <gnomes@lxorguk.ukuu.org.uk>

> Cc: dvhart@infradead.org; Andy Shevchenko <andy.shevchenko@gmail.com>;

> LKML <linux-kernel@vger.kernel.org>; platform-driver-x86@vger.kernel.org; Andy

> Lutomirski <luto@kernel.org>; quasisec@google.com; rjw@rjwysocki.net;

> mjg59@google.com; hch@lst.de; Limonciello, Mario

> <Mario_Limonciello@Dell.com>

> Subject: Re: [PATCH v9 17/17] tools/wmi: add a sample for dell smbios

> communication over WMI

> 

> On Tuesday 17 October 2017 13:22:01 Mario Limonciello wrote:

> > diff --git a/tools/wmi/dell-smbios-example.c b/tools/wmi/dell-smbios-example.c

> > new file mode 100644

> > index 000000000000..69c4dd9c6056

> > --- /dev/null

> > +++ b/tools/wmi/dell-smbios-example.c

> > @@ -0,0 +1,214 @@

> > +/*

> > + *  Sample application for SMBIOS communication over WMI interface

> > + *  Performs the following:

> > + *  - Simple class/select lookup for TPM information

> > + *  - Simple query of known tokens and their values

> > + *  - Simple activation of a token

> > + *

> > + *  Copyright (C) 2017 Dell, Inc.

> > + *

> > + *  This program is free software; you can redistribute it and/or modify

> > + *  it under the terms of the GNU General Public License version 2 as

> > + *  published by the Free Software Foundation.

> > + */

> > +

> > +#include <errno.h>

> > +#include <fcntl.h>

> > +#include <stdio.h>

> > +#include <stdlib.h>

> > +#include <sys/ioctl.h>

> > +#include <unistd.h>

> > +

> > +/* if uapi header isn't installed, this might not yet exist */

> > +#ifndef __packed

> > +#define __packed __attribute__((packed))

> > +#endif

> > +#include <linux/wmi.h>

> > +

> > +/* It would be better to discover these using udev, but for a simple

> > + * application they're hardcoded

> > + */

> > +static const char *ioctl_devfs = "/dev/wmi/dell-smbios";

> > +static const char *token_sysfs =

> > +			"/sys/bus/platform/devices/dell-smbios.0/tokens";

> > +static const char *buffer_sysfs =

> > +			"/sys/bus/wmi/devices/A80593CE-A997-11DA-B012-

> B622A1EF5492/required_buffer_size";

> 

> Greg, Alan, could userspace expects those paths to be part of kernel

> <--> userspace ABI? Looking e.g. at "dell-smbios.0" name and I'm not

> sure if this is something which is going to be stable between kernel

> versions and forever as part of ABI.


In my sample application to be distributed with the kernel these are 
hardcoded paths, but if more dependencies were used, I would
expect all 3 of these paths to be discovered using udev.  
I do include a comment for that point specifically.

> 

> Also if everything is part of smbios API, would not it better to provide

> everything via IOCTL over /dev/wmi/dell-smbios? I think this code is too

> complicated, just because for correct IOCTL buffer size it needs to read

> other properties via sysfs, etc... For me it looks like that it is not a

> good API for userspace developers.

> 

> --


This does give me an idea, how about a read on the character device
will return required buffer size instead of needing to find a sysfs 
attribute?  This seems more intuitive to me.

Token information is provided over sysfs for multiple reasons.
1) It's applicable to all dispatchers.  Even if the WMI dispatcher wasn't
used it's useful for userspace to query through.  For example the SMI call
to get tokens in libsmbios can be simplified to just read sysfs files.

2) it's information not coming from ACPI-WMI.  This series is setting
precedent for how to interact with ACPI-WMI methods in userspace.
putting in random data on the IOCTL that is not used in the ACPI-WMI
method or provided by the WMI bus doesn't fit.

3) It is static information that won't change until you reboot.
diff mbox

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 84afbf8ef7d5..64529a3f6910 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3997,6 +3997,7 @@  M:	Mario Limonciello <mario.limonciello@dell.com>
 L:	platform-driver-x86@vger.kernel.org
 S:	Maintained
 F:	drivers/platform/x86/dell-smbios-wmi.c
+F:	tools/wmi/dell-smbios-example.c
 
 DELL LAPTOP DRIVER
 M:	Matthew Garrett <mjg59@srcf.ucam.org>
diff --git a/tools/Makefile b/tools/Makefile
index 9dfede37c8ff..9d2fd2606810 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -29,6 +29,7 @@  help:
 	@echo '  usb                    - USB testing tools'
 	@echo '  virtio                 - vhost test module'
 	@echo '  vm                     - misc vm tools'
+	@echo '  wmi			- WMI interface examples'
 	@echo '  x86_energy_perf_policy - Intel energy policy tool'
 	@echo ''
 	@echo 'You can do:'
@@ -57,7 +58,7 @@  acpi: FORCE
 cpupower: FORCE
 	$(call descend,power/$@)
 
-cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds: FORCE
+cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds wmi: FORCE
 	$(call descend,$@)
 
 liblockdep: FORCE
@@ -92,7 +93,7 @@  kvm_stat: FORCE
 all: acpi cgroup cpupower gpio hv firewire liblockdep \
 		perf selftests spi turbostat usb \
 		virtio vm net x86_energy_perf_policy \
-		tmon freefall iio objtool kvm_stat
+		tmon freefall iio objtool kvm_stat wmi
 
 acpi_install:
 	$(call descend,power/$(@:_install=),install)
@@ -100,7 +101,7 @@  acpi_install:
 cpupower_install:
 	$(call descend,power/$(@:_install=),install)
 
-cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install net_install objtool_install:
+cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install net_install objtool_install wmi_install:
 	$(call descend,$(@:_install=),install)
 
 liblockdep_install:
@@ -125,7 +126,8 @@  install: acpi_install cgroup_install cpupower_install gpio_install \
 		hv_install firewire_install iio_install liblockdep_install \
 		perf_install selftests_install turbostat_install usb_install \
 		virtio_install vm_install net_install x86_energy_perf_policy_install \
-		tmon_install freefall_install objtool_install kvm_stat_install
+		tmon_install freefall_install objtool_install kvm_stat_install \
+		wmi_install
 
 acpi_clean:
 	$(call descend,power/acpi,clean)
@@ -133,7 +135,7 @@  acpi_clean:
 cpupower_clean:
 	$(call descend,power/cpupower,clean)
 
-cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
+cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
 	$(call descend,$(@:_clean=),clean)
 
 liblockdep_clean:
@@ -171,6 +173,6 @@  clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
 		perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
 		vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
 		freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
-		gpio_clean objtool_clean leds_clean
+		gpio_clean objtool_clean leds_clean wmi_clean
 
 .PHONY: FORCE
diff --git a/tools/wmi/Makefile b/tools/wmi/Makefile
new file mode 100644
index 000000000000..e664f1167388
--- /dev/null
+++ b/tools/wmi/Makefile
@@ -0,0 +1,18 @@ 
+PREFIX ?= /usr
+SBINDIR ?= sbin
+INSTALL ?= install
+CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
+CC = $(CROSS_COMPILE)gcc
+
+TARGET = dell-smbios-example
+
+all: $(TARGET)
+
+%: %.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+clean:
+	$(RM) $(TARGET)
+
+install: dell-smbios-example
+	$(INSTALL) -D -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/$(SBINDIR)/$(TARGET)
diff --git a/tools/wmi/dell-smbios-example.c b/tools/wmi/dell-smbios-example.c
new file mode 100644
index 000000000000..69c4dd9c6056
--- /dev/null
+++ b/tools/wmi/dell-smbios-example.c
@@ -0,0 +1,214 @@ 
+/*
+ *  Sample application for SMBIOS communication over WMI interface
+ *  Performs the following:
+ *  - Simple class/select lookup for TPM information
+ *  - Simple query of known tokens and their values
+ *  - Simple activation of a token
+ *
+ *  Copyright (C) 2017 Dell, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+/* if uapi header isn't installed, this might not yet exist */
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+#include <linux/wmi.h>
+
+/* It would be better to discover these using udev, but for a simple
+ * application they're hardcoded
+ */
+static const char *ioctl_devfs = "/dev/wmi/dell-smbios";
+static const char *token_sysfs =
+			"/sys/bus/platform/devices/dell-smbios.0/tokens";
+static const char *buffer_sysfs =
+			"/sys/bus/wmi/devices/A80593CE-A997-11DA-B012-B622A1EF5492/required_buffer_size";
+
+static void show_buffer(struct dell_wmi_smbios_buffer *buffer)
+{
+	printf("Call: %x/%x [%x,%x,%x,%x]\nResults: [%8x,%8x,%8x,%8x]\n",
+	buffer->std.class, buffer->std.select,
+	buffer->std.input[0], buffer->std.input[1],
+	buffer->std.input[2], buffer->std.input[3],
+	buffer->std.output[0], buffer->std.output[1],
+	buffer->std.output[2], buffer->std.output[3]);
+}
+
+static int run_wmi_smbios_cmd(struct dell_wmi_smbios_buffer *buffer)
+{
+	int fd;
+	int ret;
+
+	fd = open(ioctl_devfs, O_NONBLOCK);
+	ret = ioctl(fd, DELL_WMI_SMBIOS_CMD, buffer);
+	close(fd);
+	return ret;
+}
+
+static int find_token(__u16 token, __u16 *location, __u16 *value)
+{
+	char location_sysfs[60];
+	char value_sysfs[57];
+	char buf[4096];
+	FILE *f;
+	int ret;
+
+	ret = sprintf(value_sysfs, "%s/%04x_value", token_sysfs, token);
+	if (ret < 0) {
+		printf("sprintf value failed\n");
+		return 2;
+	}
+	f = fopen(value_sysfs, "rb");
+	if (!f) {
+		printf("failed to open %s\n", value_sysfs);
+		return 2;
+	}
+	fread(buf, 1, 4096, f);
+	fclose(f);
+	*value = (__u16) strtol(buf, NULL, 16);
+
+	ret = sprintf(location_sysfs, "%s/%04x_location", token_sysfs, token);
+	if (ret < 0) {
+		printf("sprintf location failed\n");
+		return 1;
+	}
+	f = fopen(location_sysfs, "rb");
+	if (!f) {
+		printf("failed to open %s\n", location_sysfs);
+		return 2;
+	}
+	fread(buf, 1, 4096, f);
+	fclose(f);
+	*location = (__u16) strtol(buf, NULL, 16);
+
+	if (*location)
+		return 0;
+	return 2;
+}
+
+static int token_is_active(__u16 *location, __u16 *cmpvalue,
+			   struct dell_wmi_smbios_buffer *buffer)
+{
+	int ret;
+
+	buffer->std.class = CLASS_TOKEN_READ;
+	buffer->std.select = SELECT_TOKEN_STD;
+	buffer->std.input[0] = *location;
+	ret = run_wmi_smbios_cmd(buffer);
+	if (ret != 0 || buffer->std.output[0] != 0)
+		return ret;
+	ret = (buffer->std.output[1] == *cmpvalue);
+	return ret;
+}
+
+static int query_token(__u16 token, struct dell_wmi_smbios_buffer *buffer)
+{
+	__u16 location;
+	__u16 value;
+	int ret;
+
+	ret = find_token(token, &location, &value);
+	if (ret != 0) {
+		printf("unable to find token %04x\n", token);
+		return 1;
+	}
+	return token_is_active(&location, &value, buffer);
+}
+
+static int activate_token(struct dell_wmi_smbios_buffer *buffer,
+		   __u16 token)
+{
+	__u16 location;
+	__u16 value;
+	int ret;
+
+	ret = find_token(token, &location, &value);
+	if (ret != 0) {
+		printf("unable to find token %04x\n", token);
+		return 1;
+	}
+	buffer->std.class = CLASS_TOKEN_WRITE;
+	buffer->std.select = SELECT_TOKEN_STD;
+	buffer->std.input[0] = location;
+	buffer->std.input[1] = 1;
+	ret = run_wmi_smbios_cmd(buffer);
+	return ret;
+}
+
+static int query_buffer_size(__u64 *buffer_size)
+{
+	char buf[4096];
+	FILE *f;
+
+	f = fopen(buffer_sysfs, "rb");
+	if (!f)
+		return -EINVAL;
+	fread(buf, 1, 4096, f);
+	fclose(f);
+	*buffer_size = strtol(buf, NULL, 10);
+	return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+	struct dell_wmi_smbios_buffer *buffer;
+	int ret;
+	__u64 value = 0;
+
+	ret = query_buffer_size(&value);
+	if (ret == EXIT_FAILURE || !value) {
+		printf("Unable to read buffer size\n");
+		goto out;
+	}
+	printf("Detected required buffer size %lld\n", value);
+
+	buffer = malloc(value);
+	if (buffer == NULL) {
+		printf("failed to alloc memory for ioctl\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+	buffer->length = value;
+
+	/* simple SMBIOS call for looking up TPM info */
+	buffer->std.class = CLASS_FLASH_INTERFACE;
+	buffer->std.select = SELECT_FLASH_INTERFACE;
+	buffer->std.input[0] = 2;
+	ret = run_wmi_smbios_cmd(buffer);
+	if (ret) {
+		printf("smbios ioctl failed: %d\n", ret);
+		ret = EXIT_FAILURE;
+		goto out;
+	}
+	show_buffer(buffer);
+
+	/* query some tokens */
+	ret = query_token(CAPSULE_EN_TOKEN, buffer);
+	printf("UEFI Capsule enabled token is: %d\n", ret);
+	ret = query_token(CAPSULE_DIS_TOKEN, buffer);
+	printf("UEFI Capsule disabled token is: %d\n", ret);
+
+	/* activate UEFI capsule token if disabled */
+	if (ret) {
+		printf("Enabling UEFI capsule token");
+		if (activate_token(buffer, CAPSULE_EN_TOKEN)) {
+			printf("activate failed\n");
+			ret = -1;
+			goto out;
+		}
+	}
+	ret = EXIT_SUCCESS;
+out:
+	free(buffer);
+	return ret;
+}