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@xxxxxxxxx> 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@xxxxxx> > > 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@xxxxxx> > + * > + * 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@xxxxxx>"); > +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@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > - To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html