55cd3f01d6
pvpanic was not properly detected when _STA was missing. ACPI 6.0 April 2015, 6.3.7 _STA (Status) If a device object (including the processor object) does not have an _STA object, then OSPM assumes that all of the above bits are set (i.e., the device is present, enabled, shown in the UI, and functioning). Not adhering to the specification made pvpanic dormant under QEMU 2.3. The original patch used acpi_bus_get_status_handle, which was not being exported, so module build blew up; switch to acpi_bus_get_status and use the status it populates. Populated status is a bitfield so we can make the code self-documenting. We do not check 'present' because 'enabled' has to be false in that case by specification. Older QEMUs set 0xff to status and newer ones do 0xb. Suggested-by: Igor Mammedov <imammedo@redhat.com> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> [dvhart@linux.intel.com: Merge acpi_bug_get_status fix to avoid bisect breakage] Signed-off-by: Darren Hart <dvhart@linux.intel.com>
124 lines
2.8 KiB
C
124 lines
2.8 KiB
C
/*
|
|
* pvpanic.c - pvpanic Device Support
|
|
*
|
|
* Copyright (C) 2013 Fujitsu.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/acpi.h>
|
|
|
|
MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
|
|
MODULE_DESCRIPTION("pvpanic device driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
static int pvpanic_add(struct acpi_device *device);
|
|
static int pvpanic_remove(struct acpi_device *device);
|
|
|
|
static const struct acpi_device_id pvpanic_device_ids[] = {
|
|
{ "QEMU0001", 0 },
|
|
{ "", 0 },
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
|
|
|
|
#define PVPANIC_PANICKED (1 << 0)
|
|
|
|
static u16 port;
|
|
|
|
static struct acpi_driver pvpanic_driver = {
|
|
.name = "pvpanic",
|
|
.class = "QEMU",
|
|
.ids = pvpanic_device_ids,
|
|
.ops = {
|
|
.add = pvpanic_add,
|
|
.remove = pvpanic_remove,
|
|
},
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static void
|
|
pvpanic_send_event(unsigned int event)
|
|
{
|
|
outb(event, port);
|
|
}
|
|
|
|
static int
|
|
pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
|
|
void *unused)
|
|
{
|
|
pvpanic_send_event(PVPANIC_PANICKED);
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
static struct notifier_block pvpanic_panic_nb = {
|
|
.notifier_call = pvpanic_panic_notify,
|
|
.priority = 1, /* let this called before broken drm_fb_helper */
|
|
};
|
|
|
|
|
|
static acpi_status
|
|
pvpanic_walk_resources(struct acpi_resource *res, void *context)
|
|
{
|
|
switch (res->type) {
|
|
case ACPI_RESOURCE_TYPE_END_TAG:
|
|
return AE_OK;
|
|
|
|
case ACPI_RESOURCE_TYPE_IO:
|
|
port = res->data.io.minimum;
|
|
return AE_OK;
|
|
|
|
default:
|
|
return AE_ERROR;
|
|
}
|
|
}
|
|
|
|
static int pvpanic_add(struct acpi_device *device)
|
|
{
|
|
int ret;
|
|
|
|
ret = acpi_bus_get_status(device);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (!device->status.enabled || !device->status.functional)
|
|
return -ENODEV;
|
|
|
|
acpi_walk_resources(device->handle, METHOD_NAME__CRS,
|
|
pvpanic_walk_resources, NULL);
|
|
|
|
if (!port)
|
|
return -ENODEV;
|
|
|
|
atomic_notifier_chain_register(&panic_notifier_list,
|
|
&pvpanic_panic_nb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pvpanic_remove(struct acpi_device *device)
|
|
{
|
|
|
|
atomic_notifier_chain_unregister(&panic_notifier_list,
|
|
&pvpanic_panic_nb);
|
|
return 0;
|
|
}
|
|
|
|
module_acpi_driver(pvpanic_driver);
|