Re: [patch 2/2] ia64: add driver for ACPI methods to call native firmware

Len Brown
2007-09-21T01:03:11Z

I guess x86 does this via SMI,
I don't see any other way, so it looks reasonable to me.

Tony,
Acked-by: Len Brown <len.brown@intel.com>

On Thursday 20 September 2007 16:22, Bjorn Helgaas wrote:
> This driver for HPQ5001 devices installs a global ACPI OpRegion handler.
> AML methods can use this OpRegion to call native firmware entry points.
>
> ACPI does not define a mechanism for AML methods to call native firmware
> interfaces such as PAL or SAL. This OpRegion handler adds such a mechanism.
> After the handler is installed, an AML method can call native firmware by
> storing the arguments and firmware entry point to specific offsets in the
> OpRegion. When AML reads the "return value" offset from the OpRegion, this
> handler loads up the arguments, makes the firmware call, and returns the
> result.
>
> Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
>
> Index: work2/arch/ia64/Kconfig
> =================================> --- work2.orig/arch/ia64/Kconfig 2007-09-19 10:09:43.000000000 -0600
> +++ work2/arch/ia64/Kconfig 2007-09-19 10:51:15.000000000 -0600
> @@ -461,6 +461,16 @@
> firmware extensions, such as the ability to inject memory-errors
> for test-purposes. If you're unsure, say N.
>
> +config IA64_HP_AML_NFW
> + bool "Support ACPI AML calls to native firmware"
> + help
> + This driver installs a global ACPI Operation Region handler for
> + region 0xA1. AML methods can use this OpRegion to call arbitrary
> + native firmware functions. The driver installs the OpRegion
> + handler if there is an HPQ5001 device or if the user supplies
> + the "force" module parameter, e.g., with the "aml_nfw.force"
> + kernel command line option.
> +
> source "drivers/sn/Kconfig"
>
> config KEXEC
> Index: work2/arch/ia64/hp/common/Makefile
> =================================> --- work2.orig/arch/ia64/hp/common/Makefile 2007-09-19 10:09:43.000000000 -0600
> +++ work2/arch/ia64/hp/common/Makefile 2007-09-19 10:51:15.000000000 -0600
> @@ -8,3 +8,4 @@
> obj-y := sba_iommu.o
> obj-$(CONFIG_IA64_HP_ZX1_SWIOTLB) += hwsw_iommu.o
> obj-$(CONFIG_IA64_GENERIC) += hwsw_iommu.o
> +obj-$(CONFIG_IA64_HP_AML_NFW) += aml_nfw.o
> Index: work2/arch/ia64/hp/common/aml_nfw.c
> =================================> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ work2/arch/ia64/hp/common/aml_nfw.c 2007-09-20 14:05:21.000000000 -0600
> @@ -0,0 +1,236 @@
> +/*
> + * OpRegion handler to allow AML to call native firmware
> + *
> + * (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
> + * Bjorn Helgaas <bjorn.helgaas@hp.com>
> + *
> + * 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.
> + *
> + * This driver implements HP Open Source Review Board proposal 1842,
> + * which was approved on 9/20/2006.
> + *
> + * For technical documentation, see the HP SPPA Firmware EAS, Appendix F.
> + *
> + * ACPI does not define a mechanism for AML methods to call native firmware
> + * interfaces such as PAL or SAL. This OpRegion handler adds such a mechanism.
> + * After the handler is installed, an AML method can call native firmware by
> + * storing the arguments and firmware entry point to specific offsets in the
> + * OpRegion. When AML reads the "return value" offset from the OpRegion, this
> + * handler loads up the arguments, makes the firmware call, and returns the
> + * result.
> + */
> +
> +#include <linux/module.h>
> +#include <acpi/acpi_bus.h>
> +#include <acpi/acpi_drivers.h>
> +#include <asm/sal.h>
> +
> +MODULE_AUTHOR("Bjorn Helgaas <bjorn.helgaas@hp.com>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("ACPI opregion handler for native firmware calls");
> +
> +static int force_register;
> +module_param_named(force, force_register, bool, 0);
> +MODULE_PARM_DESC(force, "Install opregion handler even without HPQ5001 device");
> +
> +#define AML_NFW_SPACE 0xA1
> +
> +struct ia64_pdesc {
> + void *ip;
> + void *gp;
> +};
> +
> +/*
> + * N.B. The layout of this structure is defined in the HP SPPA FW EAS, and
> + * the member offsets are embedded in AML methods.
> + */
> +struct ia64_nfw_context {
> + u64 arg[8];
> + struct ia64_sal_retval ret;
> + u64 ip;
> + u64 gp;
> + u64 pad[2];
> +};
> +
> +static void *virt_map(u64 address)
> +{
> + if (address & (1UL << 63))
> + return (void *) (__IA64_UNCACHED_OFFSET | address);
> +
> + return __va(address);
> +}
> +
> +static void aml_nfw_execute(struct ia64_nfw_context *c)
> +{
> + struct ia64_pdesc virt_entry;
> + ia64_sal_handler entry;
> +
> + virt_entry.ip = virt_map(c->ip);
> + virt_entry.gp = virt_map(c->gp);
> +
> + entry = (ia64_sal_handler) &virt_entry;
> +
> + IA64_FW_CALL(entry, c->ret,
> + c->arg[0], c->arg[1], c->arg[2], c->arg[3],
> + c->arg[4], c->arg[5], c->arg[6], c->arg[7]);
> +}
> +
> +static void aml_nfw_read_arg(u8 *offset, u32 bit_width, acpi_integer *value)
> +{
> + switch (bit_width) {
> + case 8:
> + *value = *(u8 *)offset;
> + break;
> + case 16:
> + *value = *(u16 *)offset;
> + break;
> + case 32:
> + *value = *(u32 *)offset;
> + break;
> + case 64:
> + *value = *(u64 *)offset;
> + break;
> + }
> +}
> +
> +static void aml_nfw_write_arg(u8 *offset, u32 bit_width, acpi_integer *value)
> +{
> + switch (bit_width) {
> + case 8:
> + *(u8 *) offset = *value;
> + break;
> + case 16:
> + *(u16 *) offset = *value;
> + break;
> + case 32:
> + *(u32 *) offset = *value;
> + break;
> + case 64:
> + *(u64 *) offset = *value;
> + break;
> + }
> +}
> +
> +static acpi_status aml_nfw_handler(u32 function, acpi_physical_address address,
> + u32 bit_width, acpi_integer *value, void *handler_context,
> + void *region_context)
> +{
> + struct ia64_nfw_context *context = handler_context;
> + u8 *offset = (u8 *) context + address;
> +
> + if (bit_width != 8 && bit_width != 16 &&
> + bit_width != 32 && bit_width != 64)
> + return AE_BAD_PARAMETER;
> +
> + if (address + (bit_width >> 3) > sizeof(struct ia64_nfw_context))
> + return AE_BAD_PARAMETER;
> +
> + switch (function) {
> + case ACPI_READ:
> + if (address = offsetof(struct ia64_nfw_context, ret))
> + aml_nfw_execute(context);
> + aml_nfw_read_arg(offset, bit_width, value);
> + break;
> + case ACPI_WRITE:
> + aml_nfw_write_arg(offset, bit_width, value);
> + break;
> + }
> +
> + return AE_OK;
> +}
> +
> +static struct ia64_nfw_context global_context;
> +static int global_handler_registered;
> +
> +static int aml_nfw_add_global_handler(void)
> +{
> + acpi_status status;
> +
> + if (global_handler_registered)
> + return 0;
> +
> + status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
> + AML_NFW_SPACE, aml_nfw_handler, NULL, &global_context);
> + if (ACPI_FAILURE(status))
> + return -ENODEV;
> +
> + global_handler_registered = 1;
> + printk(KERN_INFO "Global 0x%02X opregion handler registered\n",
> + AML_NFW_SPACE);
> + return 0;
> +}
> +
> +static int aml_nfw_remove_global_handler(void)
> +{
> + acpi_status status;
> +
> + if (!global_handler_registered)
> + return 0;
> +
> + status = acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
> + AML_NFW_SPACE, aml_nfw_handler);
> + if (ACPI_FAILURE(status))
> + return -ENODEV;
> +
> + global_handler_registered = 0;
> + printk(KERN_INFO "Global 0x%02X opregion handler removed\n",
> + AML_NFW_SPACE);
> + return 0;
> +}
> +
> +static int aml_nfw_add(struct acpi_device *device)
> +{
> + /*
> + * We would normally allocate a new context structure and install
> + * the address space handler for the specific device we found.
> + * But the HP-UX implementation shares a single global context
> + * and always puts the handler at the root, so we'll do the same.
> + */
> + return aml_nfw_add_global_handler();
> +}
> +
> +static int aml_nfw_remove(struct acpi_device *device, int type)
> +{
> + return aml_nfw_remove_global_handler();
> +}
> +
> +static const struct acpi_device_id aml_nfw_ids[] = {
> + {"HPQ5001", 0},
> + {"", 0}
> +};
> +
> +static struct acpi_driver acpi_aml_nfw_driver = {
> + .name = "native firmware",
> + .ids = aml_nfw_ids,
> + .ops = {
> + .add = aml_nfw_add,
> + .remove = aml_nfw_remove,
> + },
> +};
> +
> +static int __init aml_nfw_init(void)
> +{
> + int result;
> +
> + if (force_register)
> + aml_nfw_add_global_handler();
> +
> + result = acpi_bus_register_driver(&acpi_aml_nfw_driver);
> + if (result < 0) {
> + aml_nfw_remove_global_handler();
> + return result;
> + }
> +
> + return 0;
> +}
> +
> +static void __exit aml_nfw_exit(void)
> +{
> + acpi_bus_unregister_driver(&acpi_aml_nfw_driver);
> + aml_nfw_remove_global_handler();
> +}
> +
> +module_init(aml_nfw_init);
> +module_exit(aml_nfw_exit);
> -
> To unsubscribe from this list: send the line "unsubscribe linux-ia64" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>