From da5498c4e064de11192df358c1f076a8e77b87c4 Mon Sep 17 00:00:00 2001 From: Kamala Narasimhan Date: Mon, 9 Feb 2009 21:42:53 -0500 Subject: [PATCH] Xen acpi-wmi implementation. Xen acpi-wmi driver provides the necessary interface for userspace module (qemu) to communicate with acpi wmi wrapper kernel driver which then communicates with the base firmware. The result returned by the base firmware is communicated back to the userspace module (qemu) through this driver. --- drivers/xen/Kconfig | 8 +- drivers/xen/Makefile | 1 + drivers/xen/acpi-wmi/Makefile | 2 + drivers/xen/acpi-wmi/acpi-wmi.c | 345 ++++++++++++++++++++++++++++++++ include/xen/public/acpi-wmi.h | 94 +++++++++ 5 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 drivers/xen/acpi-wmi/Makefile create mode 100644 drivers/xen/acpi-wmi/acpi-wmi.c create mode 100644 include/xen/public/acpi-wmi.h diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 1abc0d4e..f3e01927 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -263,6 +263,13 @@ config XEN_SYSFS help Xen hypervisor attributes will show up under /sys/hypervisor/. +config XEN_ACPI_WMI_WRAPPER + tristate "Xen ACPI WMI wrapper driver" + depends on ACPI_WMI + help + Facilitates OEM specific hotkey implementation within + guest space. + choice prompt "Xen version compatibility" default XEN_COMPAT_030002_AND_LATER @@ -310,5 +317,4 @@ config XEN_XENCOMM config XEN_DEVMEM def_bool y - endif diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 56fc080b..11074212 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_XEN_GRANT_DEV) += gntdev/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_UTIL) += sfc_netutil/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND) += sfc_netfront/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_BACKEND) += sfc_netback/ +obj-$(CONFIG_XEN_ACPI_WMI_WRAPPER) += acpi-wmi/ diff --git a/drivers/xen/acpi-wmi/Makefile b/drivers/xen/acpi-wmi/Makefile new file mode 100644 index 00000000..afe5d9d2 --- /dev/null +++ b/drivers/xen/acpi-wmi/Makefile @@ -0,0 +1,2 @@ + +obj-y := acpi-wmi.o diff --git a/drivers/xen/acpi-wmi/acpi-wmi.c b/drivers/xen/acpi-wmi/acpi-wmi.c new file mode 100644 index 00000000..e1f04163 --- /dev/null +++ b/drivers/xen/acpi-wmi/acpi-wmi.c @@ -0,0 +1,345 @@ +/****************************************************************************** + * drivers/xen/acpi-wmi/acpi-wmi.c + * + * Copyright (c) 2009 Kamala Narasimhan + * Copyright (c) 2009 Citrix Systems, Inc. + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* Xen acpi-wmi implementation provides the interface required for userspace + * module (qemu) to communicate with acpi wmi wrapper kernel driver. + * Upon receiving request from qemu to call a WMI method or query or set WMI + * data, it communicates the request to kernel acpi wmi layer which then + * interacts with the base firmware to get the necessary information/execute + * relevant AML method etc. The result returned by the base firmware is then + * communicated back to the userspace module (qemu). + */ + +#include +#include +#include +#include +#include + +/* #define XEN_WMI_DEBUG */ + +static bool xen_wmi_misc_dev_registered = false; +static int xen_wmi_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +static struct file_operations xen_wmi_fops = { + .owner = THIS_MODULE, + .ioctl = xen_wmi_ioctl, +}; + +static struct miscdevice xen_wmi_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = XEN_WMI_DEVICE_NAME, + .fops = &xen_wmi_fops, +}; + +/* + * xen_wmi_copy_input_buffer + */ +int xen_wmi_copy_input_buffer(xen_wmi_buffer_t *user_buf, struct acpi_buffer *acpi_buf) +{ + if ( user_buf->length <= 0 ) + return XEN_WMI_SUCCESS; + + acpi_buf->pointer = kmalloc(user_buf->length, GFP_KERNEL); + if ( acpi_buf->pointer == NULL ) + { + printk("XEN WMI: xen_wmi_copy_input_buffer - Buffer allocation failure\n"); + return XEN_WMI_NOT_ENOUGH_MEMORY; + } + + if ( copy_from_user(acpi_buf->pointer, + (char __user *)user_buf->pointer, + user_buf->length) ) + { + printk("XEN WMI: Unable to copy input buffer argument\n"); + kfree(acpi_buf->pointer); + return XEN_WMI_EFAULT; + } + + acpi_buf->length = user_buf->length; + return XEN_WMI_SUCCESS; +} + +/* + * xen_wmi_copy_output_buffer + */ +int xen_wmi_copy_output_buffer(struct acpi_buffer *acpi_buf, xen_wmi_buffer_t *user_buf) +{ + /* IMPORTANT NOTE: It is a little short sighted to assume that the return type + * will not be anything other than buffer type. A follow-up check-in will + * cover more types. + */ + + union acpi_object *acpi_obj = acpi_buf->pointer; + + if ( acpi_obj == NULL ) + { + printk("XEN WMI: Invalid acpi buffer!\n"); + return XEN_WMI_EFAULT; + } + + if ( acpi_obj->type != ACPI_TYPE_BUFFER ) + { + printk("XEN WMI: Unsupported acpi return object type - %d\n", acpi_obj->type); + return XEN_WMI_UNSUPPORTED_TYPE; + } + + if ( copy_to_user((char __user *) user_buf->copied_length, &acpi_obj->buffer.length, sizeof(size_t)) ) + { + printk("XEN WMI: Invalid copied length user buffer!\n"); + return XEN_WMI_INVALID_ARGUMENT; + } + + if ( user_buf->length < acpi_obj->buffer.length ) + { + printk("XEN WMI: Required buffer length is - %d\n", acpi_obj->buffer.length); + printk("XEN WMI: Passed in length is - %d\n", user_buf->length); + return XEN_WMI_BUFFER_TOO_SMALL; + } + + if ( copy_to_user((char __user *) user_buf->pointer, acpi_obj->buffer.pointer, acpi_obj->buffer.length) ) + { + printk("XEN WMI: Invalid user output buffer\n"); + return XEN_WMI_NOT_ENOUGH_MEMORY; + } + + return XEN_WMI_SUCCESS; +} + +#ifdef XEN_WMI_DEBUG + +/* + * xen_wmi_print_buffer + */ +void xen_wmi_print_buffer(struct acpi_buffer *acpi_buf) +{ + int count; + + printk("XEN WMI: Output buffer length is - %d\n", acpi_buf->buffer.length); + printk("XEN WMI: Buffer: "); + for (count=0; count < acpi_buf->buffer.length; count++) + printk("%d ", ((byte *)(acpi_buf->buffer.pointer))[count]); + printk("\n"); +} + +#endif /* XEN_WMI_DEBUG */ + +/* + * xen_wmi_invoke_method + */ +int xen_wmi_invoke_method(xen_wmi_obj_invocation_data_t *obj_inv_data) +{ + int result; + struct acpi_buffer in_buf, *in_arg = NULL, out_buf = {ACPI_ALLOCATE_BUFFER, NULL}; + + result = xen_wmi_copy_input_buffer(&obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.in_buf, + &in_buf); + if ( result != XEN_WMI_SUCCESS ) + return result; + + if ( in_buf.length > 0 ) + in_arg = &in_buf; + + result = wmi_evaluate_method(obj_inv_data->guid, + obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.instance, + obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.method_id, + in_arg, &out_buf); + + if ( in_arg != NULL ) + kfree(in_buf.pointer); + + if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS ) + { +#ifdef XEN_WMI_DEBUG + xen_wmi_print_buffer(&out_buf); +#endif + result = xen_wmi_copy_output_buffer(&out_buf, + &obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.out_buf); + kfree(out_buf.pointer); + } + else if ( result != XEN_WMI_SUCCESS ) + printk("XEN WMI- Invoke WMI method failed with error - %d\n", result); + + return result; +} + +/* + * xen_wmi_query_object + */ +int xen_wmi_query_object(xen_wmi_obj_invocation_data_t *obj_inv_data) +{ + int result; + struct acpi_buffer out_buf = {ACPI_ALLOCATE_BUFFER, NULL}; + + result = wmi_query_block(obj_inv_data->guid, + obj_inv_data->xen_wmi_arg.xen_wmi_query_obj_arg.instance, + &out_buf); + + if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS ) + { +#ifdef XEN_WMI_DEBUG + xen_wmi_print_buffer(&out_buf); +#endif + result = xen_wmi_copy_output_buffer(&out_buf, + &obj_inv_data->xen_wmi_arg.xen_wmi_query_obj_arg.out_buf); + kfree(out_buf.pointer); + } + else + printk("XEN WMI - Query WMI object failed with error - %d; output buffer length - %d\n", + result, out_buf.length); + + return result; +} + +/* + * xen_wmi_set_object + */ +int xen_wmi_set_object(xen_wmi_obj_invocation_data_t *obj_inv_data) +{ + int result; + struct acpi_buffer in_buf; + + if ( obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.in_buf.length <= 0 ) + return XEN_WMI_INVALID_ARGUMENT; + + result = xen_wmi_copy_input_buffer(&obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.in_buf, + &in_buf); + if ( result != XEN_WMI_SUCCESS ) + return result; + + result = wmi_set_block(obj_inv_data->guid, + obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.instance, + &in_buf); + if ( result != XEN_WMI_SUCCESS ) + printk("XEN WMI: Set object failed with error - %d\n", result); + + kfree(in_buf.pointer); + return result; +} + +/* + * xen_wmi_get_event_data + */ +int xen_wmi_get_event_data(xen_wmi_obj_invocation_data_t *obj_inv_data) +{ + int result; + struct acpi_buffer out_buf = {ACPI_ALLOCATE_BUFFER, NULL}; + + result = wmi_get_event_data(obj_inv_data->xen_wmi_arg.xen_wmi_event_data_arg.event_id, + &out_buf); + + if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS ) + { +#ifdef XEN_WMI_DEBUG + xen_wmi_print_buffer(&out_buf); +#endif + result = xen_wmi_copy_output_buffer(&out_buf, + &obj_inv_data->xen_wmi_arg.xen_wmi_event_data_arg.out_buf); + kfree(out_buf.pointer); + } + else + printk("XEN WMI: Get event data failed with error - %d\n", result); + + return result; +} + +/* + * xen_wmi_ioctl + */ +static int xen_wmi_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + xen_wmi_obj_invocation_data_t obj_inv_data; + +#ifdef XEN_WMI_DEBUG + printk("XEN WMI: In xen_wmi_ioctl - %d\n", cmd); +#endif + + memset(&obj_inv_data, 0, sizeof(obj_inv_data)); + if ( copy_from_user(&obj_inv_data, (char __user *)arg, sizeof(obj_inv_data)) ) + { + printk("XEN WMI: Invalid object invocation parameter\n"); + return XEN_WMI_EFAULT; + } + + switch ( cmd ) + { + case XEN_WMI_IOCTL_CALL_METHOD: + return xen_wmi_invoke_method(&obj_inv_data); + case XEN_WMI_IOCTL_QUERY_OBJECT: + return xen_wmi_query_object(&obj_inv_data); + case XEN_WMI_IOCTL_SET_OBJECT: + return xen_wmi_set_object(&obj_inv_data); + case XEN_WMI_IOCTL_GET_EVENT_DATA: + return xen_wmi_get_event_data(&obj_inv_data); + } + + return XEN_WMI_ENOIOCTLCMD; +} + +/* + * xen_wmi_init + */ +static int __init xen_wmi_init(void) +{ + int ret; + + ret = misc_register(&xen_wmi_misc); + if ( ret < 0 ) + printk("XEN WMI: misc_register failed with error - %d\n", ret); + else + xen_wmi_misc_dev_registered = true; + +#ifdef XEN_WMI_DEBUG + printk("XEN WMI: xen-acpi-wmi misc_register succeeded!\n"); +#endif + return ret; +} + +/* + * xen_wmi_exit + */ +static void xen_wmi_exit(void) +{ + int ret; + + if ( xen_wmi_misc_dev_registered == false ) + return; + + if ( (ret = misc_deregister(&xen_wmi_misc)) < 0 ) + printk("XEN WMI: misc_deregister failed with error - %d\n", ret); +} + +module_init(xen_wmi_init); +module_exit(xen_wmi_exit); +MODULE_LICENSE("Dual BSD/GPL"); + diff --git a/include/xen/public/acpi-wmi.h b/include/xen/public/acpi-wmi.h new file mode 100644 index 00000000..097639ba --- /dev/null +++ b/include/xen/public/acpi-wmi.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * acpi-wmi.h + * + * Interface to /proc/misc/xen-acpi-wmi + * + * Copyright (c) 2009 Kamala Narasimhan + * Copyright (c) 2009 Citrix Systems, Inc. + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#ifndef _XEN_WMI_ACPI +#define _XEN_WMI_ACPI + +/* + * Userspace Interface + */ + +#define XEN_WMI_DEVICE_NAME "xen-acpi-wmi" +#define XEN_WMI_GUID_SIZE 16 + +#define XEN_WMI_SUCCESS 0 +#define XEN_WMI_UNSUPPORTED_TYPE -1 +#define XEN_WMI_BUFFER_TOO_SMALL -11 +#define XEN_WMI_NOT_ENOUGH_MEMORY -12 +#define XEN_WMI_EFAULT -14 +#define XEN_WMI_INVALID_ARGUMENT -22 +#define XEN_WMI_ENOIOCTLCMD -515 + +#define XEN_WMI_IOCTL_CALL_METHOD 100 +#define XEN_WMI_IOCTL_QUERY_OBJECT 101 +#define XEN_WMI_IOCTL_SET_OBJECT 102 +#define XEN_WMI_IOCTL_GET_EVENT_DATA 103 + +typedef unsigned char byte; + +typedef struct xen_wmi_buffer { + size_t length; + void *pointer; + size_t *copied_length; +} xen_wmi_buffer_t; + +typedef struct xen_wmi_obj_invocation_data { + byte guid[XEN_WMI_GUID_SIZE]; + union { + struct { + ushort instance; + uint method_id; + xen_wmi_buffer_t in_buf; + xen_wmi_buffer_t out_buf; + } xen_wmi_method_arg; + + struct { + ushort instance; + xen_wmi_buffer_t out_buf; + } xen_wmi_query_obj_arg; + + struct { + ushort instance; + xen_wmi_buffer_t in_buf; + } xen_wmi_set_obj_arg; + + struct { + ushort event_id; + xen_wmi_buffer_t out_buf; + } xen_wmi_event_data_arg; + } xen_wmi_arg; +} xen_wmi_obj_invocation_data_t; + +#endif /* _XEN_WMI_ACPI */ + -- 2.39.5