cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

aml_nfw.c (5411B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * OpRegion handler to allow AML to call native firmware
      4 *
      5 * (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
      6 *	Bjorn Helgaas <bjorn.helgaas@hp.com>
      7 *
      8 * This driver implements HP Open Source Review Board proposal 1842,
      9 * which was approved on 9/20/2006.
     10 *
     11 * For technical documentation, see the HP SPPA Firmware EAS, Appendix F.
     12 *
     13 * ACPI does not define a mechanism for AML methods to call native firmware
     14 * interfaces such as PAL or SAL.  This OpRegion handler adds such a mechanism.
     15 * After the handler is installed, an AML method can call native firmware by
     16 * storing the arguments and firmware entry point to specific offsets in the
     17 * OpRegion.  When AML reads the "return value" offset from the OpRegion, this
     18 * handler loads up the arguments, makes the firmware call, and returns the
     19 * result.
     20 */
     21
     22#include <linux/module.h>
     23#include <linux/acpi.h>
     24#include <asm/sal.h>
     25
     26MODULE_AUTHOR("Bjorn Helgaas <bjorn.helgaas@hp.com>");
     27MODULE_LICENSE("GPL");
     28MODULE_DESCRIPTION("ACPI opregion handler for native firmware calls");
     29
     30static bool force_register;
     31module_param_named(force, force_register, bool, 0);
     32MODULE_PARM_DESC(force, "Install opregion handler even without HPQ5001 device");
     33
     34#define AML_NFW_SPACE		0xA1
     35
     36struct ia64_pdesc {
     37	void *ip;
     38	void *gp;
     39};
     40
     41/*
     42 * N.B.  The layout of this structure is defined in the HP SPPA FW EAS, and
     43 *	 the member offsets are embedded in AML methods.
     44 */
     45struct ia64_nfw_context {
     46	u64 arg[8];
     47	struct ia64_sal_retval ret;
     48	u64 ip;
     49	u64 gp;
     50	u64 pad[2];
     51};
     52
     53static void *virt_map(u64 address)
     54{
     55	if (address & (1UL << 63))
     56		return (void *) (__IA64_UNCACHED_OFFSET | address);
     57
     58	return __va(address);
     59}
     60
     61static void aml_nfw_execute(struct ia64_nfw_context *c)
     62{
     63	struct ia64_pdesc virt_entry;
     64	ia64_sal_handler entry;
     65
     66	virt_entry.ip = virt_map(c->ip);
     67	virt_entry.gp = virt_map(c->gp);
     68
     69	entry = (ia64_sal_handler) &virt_entry;
     70
     71	IA64_FW_CALL(entry, c->ret,
     72		     c->arg[0], c->arg[1], c->arg[2], c->arg[3],
     73		     c->arg[4], c->arg[5], c->arg[6], c->arg[7]);
     74}
     75
     76static void aml_nfw_read_arg(u8 *offset, u32 bit_width, u64 *value)
     77{
     78	switch (bit_width) {
     79	case 8:
     80		*value = *(u8 *)offset;
     81		break;
     82	case 16:
     83		*value = *(u16 *)offset;
     84		break;
     85	case 32:
     86		*value = *(u32 *)offset;
     87		break;
     88	case 64:
     89		*value = *(u64 *)offset;
     90		break;
     91	}
     92}
     93
     94static void aml_nfw_write_arg(u8 *offset, u32 bit_width, u64 *value)
     95{
     96	switch (bit_width) {
     97	case 8:
     98		*(u8 *) offset = *value;
     99		break;
    100	case 16:
    101		*(u16 *) offset = *value;
    102		break;
    103	case 32:
    104		*(u32 *) offset = *value;
    105		break;
    106	case 64:
    107		*(u64 *) offset = *value;
    108		break;
    109	}
    110}
    111
    112static acpi_status aml_nfw_handler(u32 function, acpi_physical_address address,
    113	u32 bit_width, u64 *value, void *handler_context,
    114	void *region_context)
    115{
    116	struct ia64_nfw_context *context = handler_context;
    117	u8 *offset = (u8 *) context + address;
    118
    119	if (bit_width !=  8 && bit_width != 16 &&
    120	    bit_width != 32 && bit_width != 64)
    121		return AE_BAD_PARAMETER;
    122
    123	if (address + (bit_width >> 3) > sizeof(struct ia64_nfw_context))
    124		return AE_BAD_PARAMETER;
    125
    126	switch (function) {
    127	case ACPI_READ:
    128		if (address == offsetof(struct ia64_nfw_context, ret))
    129			aml_nfw_execute(context);
    130		aml_nfw_read_arg(offset, bit_width, value);
    131		break;
    132	case ACPI_WRITE:
    133		aml_nfw_write_arg(offset, bit_width, value);
    134		break;
    135	}
    136
    137	return AE_OK;
    138}
    139
    140static struct ia64_nfw_context global_context;
    141static int global_handler_registered;
    142
    143static int aml_nfw_add_global_handler(void)
    144{
    145	acpi_status status;
    146
    147	if (global_handler_registered)
    148		return 0;
    149
    150	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
    151		AML_NFW_SPACE, aml_nfw_handler, NULL, &global_context);
    152	if (ACPI_FAILURE(status))
    153		return -ENODEV;
    154
    155	global_handler_registered = 1;
    156	printk(KERN_INFO "Global 0x%02X opregion handler registered\n",
    157		AML_NFW_SPACE);
    158	return 0;
    159}
    160
    161static int aml_nfw_remove_global_handler(void)
    162{
    163	acpi_status status;
    164
    165	if (!global_handler_registered)
    166		return 0;
    167
    168	status = acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
    169		AML_NFW_SPACE, aml_nfw_handler);
    170	if (ACPI_FAILURE(status))
    171		return -ENODEV;
    172
    173	global_handler_registered = 0;
    174	printk(KERN_INFO "Global 0x%02X opregion handler removed\n",
    175		AML_NFW_SPACE);
    176	return 0;
    177}
    178
    179static int aml_nfw_add(struct acpi_device *device)
    180{
    181	/*
    182	 * We would normally allocate a new context structure and install
    183	 * the address space handler for the specific device we found.
    184	 * But the HP-UX implementation shares a single global context
    185	 * and always puts the handler at the root, so we'll do the same.
    186	 */
    187	return aml_nfw_add_global_handler();
    188}
    189
    190static int aml_nfw_remove(struct acpi_device *device)
    191{
    192	return aml_nfw_remove_global_handler();
    193}
    194
    195static const struct acpi_device_id aml_nfw_ids[] = {
    196	{"HPQ5001", 0},
    197	{"", 0}
    198};
    199
    200static struct acpi_driver acpi_aml_nfw_driver = {
    201	.name = "native firmware",
    202	.ids = aml_nfw_ids,
    203	.ops = {
    204		.add = aml_nfw_add,
    205		.remove = aml_nfw_remove,
    206		},
    207};
    208
    209static int __init aml_nfw_init(void)
    210{
    211	int result;
    212
    213	if (force_register)
    214		aml_nfw_add_global_handler();
    215
    216	result = acpi_bus_register_driver(&acpi_aml_nfw_driver);
    217	if (result < 0) {
    218		aml_nfw_remove_global_handler();
    219		return result;
    220	}
    221
    222	return 0;
    223}
    224
    225static void __exit aml_nfw_exit(void)
    226{
    227	acpi_bus_unregister_driver(&acpi_aml_nfw_driver);
    228	aml_nfw_remove_global_handler();
    229}
    230
    231module_init(aml_nfw_init);
    232module_exit(aml_nfw_exit);