1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Sample application for SMBIOS communication over WMI interface |
4 | * Performs the following: |
5 | * - Simple cmd_class/cmd_select lookup for TPM information |
6 | * - Simple query of known tokens and their values |
7 | * - Simple activation of a token |
8 | * |
9 | * Copyright (C) 2017 Dell, Inc. |
10 | */ |
11 | |
12 | #include <errno.h> |
13 | #include <fcntl.h> |
14 | #include <stdio.h> |
15 | #include <stdlib.h> |
16 | #include <sys/ioctl.h> |
17 | #include <unistd.h> |
18 | |
19 | /* if uapi header isn't installed, this might not yet exist */ |
20 | #ifndef __packed |
21 | #define __packed __attribute__((packed)) |
22 | #endif |
23 | #include <linux/wmi.h> |
24 | |
25 | /* It would be better to discover these using udev, but for a simple |
26 | * application they're hardcoded |
27 | */ |
28 | static const char *ioctl_devfs = "/dev/wmi/dell-smbios" ; |
29 | static const char *token_sysfs = |
30 | "/sys/bus/platform/devices/dell-smbios.0/tokens" ; |
31 | |
32 | static void show_buffer(struct dell_wmi_smbios_buffer *buffer) |
33 | { |
34 | printf("Call: %x/%x [%x,%x,%x,%x]\nResults: [%8x,%8x,%8x,%8x]\n" , |
35 | buffer->std.cmd_class, buffer->std.cmd_select, |
36 | buffer->std.input[0], buffer->std.input[1], |
37 | buffer->std.input[2], buffer->std.input[3], |
38 | buffer->std.output[0], buffer->std.output[1], |
39 | buffer->std.output[2], buffer->std.output[3]); |
40 | } |
41 | |
42 | static int run_wmi_smbios_cmd(struct dell_wmi_smbios_buffer *buffer) |
43 | { |
44 | int fd; |
45 | int ret; |
46 | |
47 | fd = open(ioctl_devfs, O_NONBLOCK); |
48 | ret = ioctl(fd, DELL_WMI_SMBIOS_CMD, buffer); |
49 | close(fd); |
50 | return ret; |
51 | } |
52 | |
53 | static int find_token(__u16 token, __u16 *location, __u16 *value) |
54 | { |
55 | char location_sysfs[60]; |
56 | char value_sysfs[57]; |
57 | char buf[4096]; |
58 | FILE *f; |
59 | int ret; |
60 | |
61 | ret = sprintf(buf: value_sysfs, fmt: "%s/%04x_value" , token_sysfs, token); |
62 | if (ret < 0) { |
63 | printf("sprintf value failed\n" ); |
64 | return 2; |
65 | } |
66 | f = fopen(value_sysfs, "rb" ); |
67 | if (!f) { |
68 | printf("failed to open %s\n" , value_sysfs); |
69 | return 2; |
70 | } |
71 | fread(buf, 1, 4096, f); |
72 | fclose(f); |
73 | *value = (__u16) strtol(buf, NULL, 16); |
74 | |
75 | ret = sprintf(buf: location_sysfs, fmt: "%s/%04x_location" , token_sysfs, token); |
76 | if (ret < 0) { |
77 | printf("sprintf location failed\n" ); |
78 | return 1; |
79 | } |
80 | f = fopen(location_sysfs, "rb" ); |
81 | if (!f) { |
82 | printf("failed to open %s\n" , location_sysfs); |
83 | return 2; |
84 | } |
85 | fread(buf, 1, 4096, f); |
86 | fclose(f); |
87 | *location = (__u16) strtol(buf, NULL, 16); |
88 | |
89 | if (*location) |
90 | return 0; |
91 | return 2; |
92 | } |
93 | |
94 | static int token_is_active(__u16 *location, __u16 *cmpvalue, |
95 | struct dell_wmi_smbios_buffer *buffer) |
96 | { |
97 | int ret; |
98 | |
99 | buffer->std.cmd_class = CLASS_TOKEN_READ; |
100 | buffer->std.cmd_select = SELECT_TOKEN_STD; |
101 | buffer->std.input[0] = *location; |
102 | ret = run_wmi_smbios_cmd(buffer); |
103 | if (ret != 0 || buffer->std.output[0] != 0) |
104 | return ret; |
105 | ret = (buffer->std.output[1] == *cmpvalue); |
106 | return ret; |
107 | } |
108 | |
109 | static int query_token(__u16 token, struct dell_wmi_smbios_buffer *buffer) |
110 | { |
111 | __u16 location; |
112 | __u16 value; |
113 | int ret; |
114 | |
115 | ret = find_token(token, location: &location, value: &value); |
116 | if (ret != 0) { |
117 | printf("unable to find token %04x\n" , token); |
118 | return 1; |
119 | } |
120 | return token_is_active(location: &location, cmpvalue: &value, buffer); |
121 | } |
122 | |
123 | static int activate_token(struct dell_wmi_smbios_buffer *buffer, |
124 | __u16 token) |
125 | { |
126 | __u16 location; |
127 | __u16 value; |
128 | int ret; |
129 | |
130 | ret = find_token(token, location: &location, value: &value); |
131 | if (ret != 0) { |
132 | printf("unable to find token %04x\n" , token); |
133 | return 1; |
134 | } |
135 | buffer->std.cmd_class = CLASS_TOKEN_WRITE; |
136 | buffer->std.cmd_select = SELECT_TOKEN_STD; |
137 | buffer->std.input[0] = location; |
138 | buffer->std.input[1] = 1; |
139 | ret = run_wmi_smbios_cmd(buffer); |
140 | return ret; |
141 | } |
142 | |
143 | static int query_buffer_size(__u64 *buffer_size) |
144 | { |
145 | FILE *f; |
146 | |
147 | f = fopen(ioctl_devfs, "rb" ); |
148 | if (!f) |
149 | return -EINVAL; |
150 | fread(buffer_size, sizeof(__u64), 1, f); |
151 | fclose(f); |
152 | return EXIT_SUCCESS; |
153 | } |
154 | |
155 | int main(void) |
156 | { |
157 | struct dell_wmi_smbios_buffer *buffer; |
158 | int ret; |
159 | __u64 value = 0; |
160 | |
161 | ret = query_buffer_size(buffer_size: &value); |
162 | if (ret == EXIT_FAILURE || !value) { |
163 | printf("Unable to read buffer size\n" ); |
164 | goto out; |
165 | } |
166 | printf("Detected required buffer size %lld\n" , value); |
167 | |
168 | buffer = malloc(value); |
169 | if (buffer == NULL) { |
170 | printf("failed to alloc memory for ioctl\n" ); |
171 | ret = -ENOMEM; |
172 | goto out; |
173 | } |
174 | buffer->length = value; |
175 | |
176 | /* simple SMBIOS call for looking up TPM info */ |
177 | buffer->std.cmd_class = CLASS_FLASH_INTERFACE; |
178 | buffer->std.cmd_select = SELECT_FLASH_INTERFACE; |
179 | buffer->std.input[0] = 2; |
180 | ret = run_wmi_smbios_cmd(buffer); |
181 | if (ret) { |
182 | printf("smbios ioctl failed: %d\n" , ret); |
183 | ret = EXIT_FAILURE; |
184 | goto out; |
185 | } |
186 | show_buffer(buffer); |
187 | |
188 | /* query some tokens */ |
189 | ret = query_token(CAPSULE_EN_TOKEN, buffer); |
190 | printf("UEFI Capsule enabled token is: %d\n" , ret); |
191 | ret = query_token(CAPSULE_DIS_TOKEN, buffer); |
192 | printf("UEFI Capsule disabled token is: %d\n" , ret); |
193 | |
194 | /* activate UEFI capsule token if disabled */ |
195 | if (ret) { |
196 | printf("Enabling UEFI capsule token" ); |
197 | if (activate_token(buffer, CAPSULE_EN_TOKEN)) { |
198 | printf("activate failed\n" ); |
199 | ret = -1; |
200 | goto out; |
201 | } |
202 | } |
203 | ret = EXIT_SUCCESS; |
204 | out: |
205 | free(buffer); |
206 | return ret; |
207 | } |
208 | |