Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 *  SMI methods for use with dell-smbios
  3 *
  4 *  Copyright (c) Red Hat <mjg@redhat.com>
  5 *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
  6 *  Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
  7 *  Copyright (c) 2017 Dell Inc.
  8 *
  9 *  This program is free software; you can redistribute it and/or modify
 10 *  it under the terms of the GNU General Public License version 2 as
 11 *  published by the Free Software Foundation.
 12 */
 13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 14
 15#include <linux/dmi.h>
 16#include <linux/gfp.h>
 17#include <linux/io.h>
 18#include <linux/module.h>
 19#include <linux/mutex.h>
 20#include <linux/platform_device.h>
 21#include "../../firmware/dcdbas.h"
 22#include "dell-smbios.h"
 23
 24static int da_command_address;
 25static int da_command_code;
 26static struct calling_interface_buffer *buffer;
 27struct platform_device *platform_device;
 28static DEFINE_MUTEX(smm_mutex);
 29
 30static const struct dmi_system_id dell_device_table[] __initconst = {
 31	{
 32		.ident = "Dell laptop",
 33		.matches = {
 34			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 35			DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
 36		},
 37	},
 38	{
 39		.matches = {
 40			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 41			DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
 42		},
 43	},
 44	{
 45		.matches = {
 46			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 47			DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
 48		},
 49	},
 50	{
 51		.ident = "Dell Computer Corporation",
 52		.matches = {
 53			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 54			DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
 55		},
 56	},
 57	{ }
 58};
 59MODULE_DEVICE_TABLE(dmi, dell_device_table);
 60
 61static void parse_da_table(const struct dmi_header *dm)
 62{
 63	struct calling_interface_structure *table =
 64		container_of(dm, struct calling_interface_structure, header);
 65
 66	/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
 67	 * 6 bytes of entry
 68	 */
 69	if (dm->length < 17)
 70		return;
 71
 72	da_command_address = table->cmdIOAddress;
 73	da_command_code = table->cmdIOCode;
 74}
 75
 76static void find_cmd_address(const struct dmi_header *dm, void *dummy)
 77{
 78	switch (dm->type) {
 79	case 0xda: /* Calling interface */
 80		parse_da_table(dm);
 81		break;
 82	}
 83}
 84
 85int dell_smbios_smm_call(struct calling_interface_buffer *input)
 86{
 87	struct smi_cmd command;
 88	size_t size;
 89
 90	size = sizeof(struct calling_interface_buffer);
 91	command.magic = SMI_CMD_MAGIC;
 92	command.command_address = da_command_address;
 93	command.command_code = da_command_code;
 94	command.ebx = virt_to_phys(buffer);
 95	command.ecx = 0x42534931;
 96
 97	mutex_lock(&smm_mutex);
 98	memcpy(buffer, input, size);
 99	dcdbas_smi_request(&command);
100	memcpy(input, buffer, size);
101	mutex_unlock(&smm_mutex);
102	return 0;
103}
104
105/* When enabled this indicates that SMM won't work */
106static bool test_wsmt_enabled(void)
107{
108	struct calling_interface_token *wsmt;
109
110	/* if token doesn't exist, SMM will work */
111	wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
112	if (!wsmt)
113		return false;
114
115	/* If token exists, try to access over SMM but set a dummy return.
116	 * - If WSMT disabled it will be overwritten by SMM
117	 * - If WSMT enabled then dummy value will remain
118	 */
119	buffer->cmd_class = CLASS_TOKEN_READ;
120	buffer->cmd_select = SELECT_TOKEN_STD;
121	memset(buffer, 0, sizeof(struct calling_interface_buffer));
122	buffer->input[0] = wsmt->location;
123	buffer->output[0] = 99;
124	dell_smbios_smm_call(buffer);
125	if (buffer->output[0] == 99)
126		return true;
127
128	return false;
129}
130
131int init_dell_smbios_smm(void)
132{
133	int ret;
134	/*
135	 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
136	 * is passed to SMI handler.
137	 */
138	buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
139	if (!buffer)
140		return -ENOMEM;
141
142	dmi_walk(find_cmd_address, NULL);
143
144	if (test_wsmt_enabled()) {
145		pr_debug("Disabling due to WSMT enabled\n");
146		ret = -ENODEV;
147		goto fail_wsmt;
148	}
149
150	platform_device = platform_device_alloc("dell-smbios", 1);
151	if (!platform_device) {
152		ret = -ENOMEM;
153		goto fail_platform_device_alloc;
154	}
155
156	ret = platform_device_add(platform_device);
157	if (ret)
158		goto fail_platform_device_add;
159
160	ret = dell_smbios_register_device(&platform_device->dev,
161					  &dell_smbios_smm_call);
162	if (ret)
163		goto fail_register;
164
165	return 0;
166
167fail_register:
168	platform_device_del(platform_device);
169
170fail_platform_device_add:
171	platform_device_put(platform_device);
172
173fail_wsmt:
174fail_platform_device_alloc:
175	free_page((unsigned long)buffer);
176	return ret;
177}
178
179void exit_dell_smbios_smm(void)
180{
181	if (platform_device) {
182		dell_smbios_unregister_device(&platform_device->dev);
183		platform_device_unregister(platform_device);
184		free_page((unsigned long)buffer);
185	}
186}