Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Author: Erik Kaneda <erik.kaneda@intel.com>
  4 * Copyright 2020 Intel Corporation
  5 *
  6 * prmt.c
  7 *
  8 * Each PRM service is an executable that is run in a restricted environment
  9 * that is invoked by writing to the PlatformRtMechanism OperationRegion from
 10 * AML bytecode.
 11 *
 12 * init_prmt initializes the Platform Runtime Mechanism (PRM) services by
 13 * processing data in the PRMT as well as registering an ACPI OperationRegion
 14 * handler for the PlatformRtMechanism subtype.
 15 *
 16 */
 17#include <linux/kernel.h>
 18#include <linux/efi.h>
 19#include <linux/acpi.h>
 20#include <linux/prmt.h>
 21#include <asm/efi.h>
 22
 23#pragma pack(1)
 24struct prm_mmio_addr_range {
 25	u64 phys_addr;
 26	u64 virt_addr;
 27	u32 length;
 28};
 29
 30struct prm_mmio_info {
 31	u64 mmio_count;
 32	struct prm_mmio_addr_range addr_ranges[];
 33};
 34
 35struct prm_buffer {
 36	u8 prm_status;
 37	u64 efi_status;
 38	u8 prm_cmd;
 39	guid_t handler_guid;
 40};
 41
 42struct prm_context_buffer {
 43	char signature[ACPI_NAMESEG_SIZE];
 44	u16 revision;
 45	u16 reserved;
 46	guid_t identifier;
 47	u64 static_data_buffer;
 48	struct prm_mmio_info *mmio_ranges;
 49};
 50#pragma pack()
 51
 52
 53static LIST_HEAD(prm_module_list);
 54
 55struct prm_handler_info {
 56	guid_t guid;
 57	u64 handler_addr;
 58	u64 static_data_buffer_addr;
 59	u64 acpi_param_buffer_addr;
 60
 61	struct list_head handler_list;
 62};
 63
 64struct prm_module_info {
 65	guid_t guid;
 66	u16 major_rev;
 67	u16 minor_rev;
 68	u16 handler_count;
 69	struct prm_mmio_info *mmio_info;
 70	bool updatable;
 71
 72	struct list_head module_list;
 73	struct prm_handler_info handlers[];
 74};
 75
 76
 77static u64 efi_pa_va_lookup(u64 pa)
 78{
 79	efi_memory_desc_t *md;
 80	u64 pa_offset = pa & ~PAGE_MASK;
 81	u64 page = pa & PAGE_MASK;
 82
 83	for_each_efi_memory_desc(md) {
 84		if (md->phys_addr < pa && pa < md->phys_addr + PAGE_SIZE * md->num_pages)
 85			return pa_offset + md->virt_addr + page - md->phys_addr;
 86	}
 87
 88	return 0;
 89}
 90
 91
 92#define get_first_handler(a) ((struct acpi_prmt_handler_info *) ((char *) (a) + a->handler_info_offset))
 93#define get_next_handler(a) ((struct acpi_prmt_handler_info *) (sizeof(struct acpi_prmt_handler_info) + (char *) a))
 94
 95static int __init
 96acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end)
 97{
 98	struct acpi_prmt_module_info *module_info;
 99	struct acpi_prmt_handler_info *handler_info;
100	struct prm_handler_info *th;
101	struct prm_module_info *tm;
102	u64 mmio_count = 0;
103	u64 cur_handler = 0;
104	u32 module_info_size = 0;
105	u64 mmio_range_size = 0;
106	void *temp_mmio;
107
108	module_info = (struct acpi_prmt_module_info *) header;
109	module_info_size = struct_size(tm, handlers, module_info->handler_info_count);
110	tm = kmalloc(module_info_size, GFP_KERNEL);
111
112	guid_copy(&tm->guid, (guid_t *) module_info->module_guid);
113	tm->major_rev = module_info->major_rev;
114	tm->minor_rev = module_info->minor_rev;
115	tm->handler_count = module_info->handler_info_count;
116	tm->updatable = true;
117
118	if (module_info->mmio_list_pointer) {
119		/*
120		 * Each module is associated with a list of addr
121		 * ranges that it can use during the service
122		 */
123		mmio_count = *(u64 *) memremap(module_info->mmio_list_pointer, 8, MEMREMAP_WB);
124		mmio_range_size = struct_size(tm->mmio_info, addr_ranges, mmio_count);
125		tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
126		temp_mmio = memremap(module_info->mmio_list_pointer, mmio_range_size, MEMREMAP_WB);
127		memmove(tm->mmio_info, temp_mmio, mmio_range_size);
128	} else {
129		mmio_range_size = struct_size(tm->mmio_info, addr_ranges, mmio_count);
130		tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
131		tm->mmio_info->mmio_count = 0;
132	}
133
134	INIT_LIST_HEAD(&tm->module_list);
135	list_add(&tm->module_list, &prm_module_list);
136
137	handler_info = get_first_handler(module_info);
138	do {
139		th = &tm->handlers[cur_handler];
140
141		guid_copy(&th->guid, (guid_t *)handler_info->handler_guid);
142		th->handler_addr = efi_pa_va_lookup(handler_info->handler_address);
143		th->static_data_buffer_addr = efi_pa_va_lookup(handler_info->static_data_buffer_address);
144		th->acpi_param_buffer_addr = efi_pa_va_lookup(handler_info->acpi_param_buffer_address);
145	} while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info)));
146
147	return 0;
148}
149
150#define GET_MODULE	0
151#define GET_HANDLER	1
152
153static void *find_guid_info(const guid_t *guid, u8 mode)
154{
155	struct prm_handler_info *cur_handler;
156	struct prm_module_info *cur_module;
157	int i = 0;
158
159	list_for_each_entry(cur_module, &prm_module_list, module_list) {
160		for (i = 0; i < cur_module->handler_count; ++i) {
161			cur_handler = &cur_module->handlers[i];
162			if (guid_equal(guid, &cur_handler->guid)) {
163				if (mode == GET_MODULE)
164					return (void *)cur_module;
165				else
166					return (void *)cur_handler;
167			}
168		}
169	}
170
171	return NULL;
172}
173
174
175static struct prm_module_info *find_prm_module(const guid_t *guid)
176{
177	return (struct prm_module_info *)find_guid_info(guid, GET_MODULE);
178}
179
180static struct prm_handler_info *find_prm_handler(const guid_t *guid)
181{
182	return (struct prm_handler_info *) find_guid_info(guid, GET_HANDLER);
183}
184
185/* In-coming PRM commands */
186
187#define PRM_CMD_RUN_SERVICE		0
188#define PRM_CMD_START_TRANSACTION	1
189#define PRM_CMD_END_TRANSACTION		2
190
191/* statuses that can be passed back to ASL */
192
193#define PRM_HANDLER_SUCCESS 		0
194#define PRM_HANDLER_ERROR 		1
195#define INVALID_PRM_COMMAND 		2
196#define PRM_HANDLER_GUID_NOT_FOUND 	3
197#define UPDATE_LOCK_ALREADY_HELD 	4
198#define UPDATE_UNLOCK_WITHOUT_LOCK 	5
199
200/*
201 * This is the PlatformRtMechanism opregion space handler.
202 * @function: indicates the read/write. In fact as the PlatformRtMechanism
203 * message is driven by command, only write is meaningful.
204 *
205 * @addr   : not used
206 * @bits   : not used.
207 * @value  : it is an in/out parameter. It points to the PRM message buffer.
208 * @handler_context: not used
209 */
210static acpi_status acpi_platformrt_space_handler(u32 function,
211						 acpi_physical_address addr,
212						 u32 bits, acpi_integer *value,
213						 void *handler_context,
214						 void *region_context)
215{
216	struct prm_buffer *buffer = ACPI_CAST_PTR(struct prm_buffer, value);
217	struct prm_handler_info *handler;
218	struct prm_module_info *module;
219	efi_status_t status;
220	struct prm_context_buffer context;
221
222	/*
223	 * The returned acpi_status will always be AE_OK. Error values will be
224	 * saved in the first byte of the PRM message buffer to be used by ASL.
225	 */
226	switch (buffer->prm_cmd) {
227	case PRM_CMD_RUN_SERVICE:
228
229		handler = find_prm_handler(&buffer->handler_guid);
230		module = find_prm_module(&buffer->handler_guid);
231		if (!handler || !module)
232			goto invalid_guid;
233
234		ACPI_COPY_NAMESEG(context.signature, "PRMC");
235		context.revision = 0x0;
236		context.reserved = 0x0;
237		context.identifier = handler->guid;
238		context.static_data_buffer = handler->static_data_buffer_addr;
239		context.mmio_ranges = module->mmio_info;
240
241		status = efi_call_virt_pointer(handler, handler_addr,
242					       handler->acpi_param_buffer_addr,
243					       &context);
244		if (status == EFI_SUCCESS) {
245			buffer->prm_status = PRM_HANDLER_SUCCESS;
246		} else {
247			buffer->prm_status = PRM_HANDLER_ERROR;
248			buffer->efi_status = status;
249		}
250		break;
251
252	case PRM_CMD_START_TRANSACTION:
253
254		module = find_prm_module(&buffer->handler_guid);
255		if (!module)
256			goto invalid_guid;
257
258		if (module->updatable)
259			module->updatable = false;
260		else
261			buffer->prm_status = UPDATE_LOCK_ALREADY_HELD;
262		break;
263
264	case PRM_CMD_END_TRANSACTION:
265
266		module = find_prm_module(&buffer->handler_guid);
267		if (!module)
268			goto invalid_guid;
269
270		if (module->updatable)
271			buffer->prm_status = UPDATE_UNLOCK_WITHOUT_LOCK;
272		else
273			module->updatable = true;
274		break;
275
276	default:
277
278		buffer->prm_status = INVALID_PRM_COMMAND;
279		break;
280	}
281
282	return AE_OK;
283
284invalid_guid:
285	buffer->prm_status = PRM_HANDLER_GUID_NOT_FOUND;
286	return AE_OK;
287}
288
289void __init init_prmt(void)
290{
291	struct acpi_table_header *tbl;
292	acpi_status status;
293	int mc;
294
295	status = acpi_get_table(ACPI_SIG_PRMT, 0, &tbl);
296	if (ACPI_FAILURE(status))
297		return;
298
299	mc = acpi_table_parse_entries(ACPI_SIG_PRMT, sizeof(struct acpi_table_prmt) +
300					  sizeof (struct acpi_table_prmt_header),
301					  0, acpi_parse_prmt, 0);
302	acpi_put_table(tbl);
303	/*
304	 * Return immediately if PRMT table is not present or no PRM module found.
305	 */
306	if (mc <= 0)
307		return;
308
309	pr_info("PRM: found %u modules\n", mc);
310
311	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
312						    ACPI_ADR_SPACE_PLATFORM_RT,
313						    &acpi_platformrt_space_handler,
314						    NULL, NULL);
315	if (ACPI_FAILURE(status))
316		pr_alert("PRM: OperationRegion handler could not be installed\n");
317}