Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 05131ecc authored by Venkatesh Pallipadi's avatar Venkatesh Pallipadi Committed by Len Brown
Browse files

[ACPI] Avoid BIOS inflicted crashes by evaluating _PDC only once

Linux invokes the AML _PDC method (Processor Driver Capabilities)
to tell the BIOS what features it can handle.  While the ACPI
spec says nothing about the OS invoking _PDC multiple times,
doing so with changing bits seems to hopelessly confuse the BIOS
on multiple platforms up to and including crashing the system.

Factor out the _PDC invocation so Linux invokes it only once.

http://bugzilla.kernel.org/show_bug.cgi?id=5483



Signed-off-by: default avatarVenkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent d2149b54
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3,6 +3,6 @@ obj-$(CONFIG_X86_IO_APIC) += earlyquirk.o
obj-$(CONFIG_ACPI_SLEEP)	+= sleep.o wakeup.o

ifneq ($(CONFIG_ACPI_PROCESSOR),)
obj-y				+= cstate.o
obj-y				+= cstate.o processor.o
endif
+0 −58
Original line number Diff line number Diff line
@@ -14,64 +14,6 @@
#include <acpi/processor.h>
#include <asm/acpi.h>

static void acpi_processor_power_init_intel_pdc(struct acpi_processor_power
						*pow)
{
	struct acpi_object_list *obj_list;
	union acpi_object *obj;
	u32 *buf;

	/* allocate and initialize pdc. It will be used later. */
	obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL);
	if (!obj_list) {
		printk(KERN_ERR "Memory allocation error\n");
		return;
	}

	obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL);
	if (!obj) {
		printk(KERN_ERR "Memory allocation error\n");
		kfree(obj_list);
		return;
	}

	buf = kmalloc(12, GFP_KERNEL);
	if (!buf) {
		printk(KERN_ERR "Memory allocation error\n");
		kfree(obj);
		kfree(obj_list);
		return;
	}

	buf[0] = ACPI_PDC_REVISION_ID;
	buf[1] = 1;
	buf[2] = ACPI_PDC_C_CAPABILITY_SMP;

	obj->type = ACPI_TYPE_BUFFER;
	obj->buffer.length = 12;
	obj->buffer.pointer = (u8 *) buf;
	obj_list->count = 1;
	obj_list->pointer = obj;
	pow->pdc = obj_list;

	return;
}

/* Initialize _PDC data based on the CPU vendor */
void acpi_processor_power_init_pdc(struct acpi_processor_power *pow,
				   unsigned int cpu)
{
	struct cpuinfo_x86 *c = cpu_data + cpu;

	pow->pdc = NULL;
	if (c->x86_vendor == X86_VENDOR_INTEL)
		acpi_processor_power_init_intel_pdc(pow);

	return;
}

EXPORT_SYMBOL(acpi_processor_power_init_pdc);

/*
 * Initialize bm_flags based on the CPU cache properties
 * On SMP it depends on cache configuration
+75 −0
Original line number Diff line number Diff line
/*
 * arch/i386/kernel/acpi/processor.c
 *
 * Copyright (C) 2005 Intel Corporation
 * 	Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
 * 	- Added _PDC for platforms with Intel CPUs
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>

#include <acpi/processor.h>
#include <asm/acpi.h>

static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c)
{
	struct acpi_object_list *obj_list;
	union acpi_object *obj;
	u32 *buf;

	/* allocate and initialize pdc. It will be used later. */
	obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL);
	if (!obj_list) {
		printk(KERN_ERR "Memory allocation error\n");
		return;
	}

	obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL);
	if (!obj) {
		printk(KERN_ERR "Memory allocation error\n");
		kfree(obj_list);
		return;
	}

	buf = kmalloc(12, GFP_KERNEL);
	if (!buf) {
		printk(KERN_ERR "Memory allocation error\n");
		kfree(obj);
		kfree(obj_list);
		return;
	}

	buf[0] = ACPI_PDC_REVISION_ID;
	buf[1] = 1;
	buf[2] = ACPI_PDC_C_CAPABILITY_SMP;

	if (cpu_has(c, X86_FEATURE_EST))
		buf[2] |= ACPI_PDC_EST_CAPABILITY_SMP;

	obj->type = ACPI_TYPE_BUFFER;
	obj->buffer.length = 12;
	obj->buffer.pointer = (u8 *) buf;
	obj_list->count = 1;
	obj_list->pointer = obj;
	pr->pdc = obj_list;

	return;
}

/* Initialize _PDC data based on the CPU vendor */
void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
{
	unsigned int cpu = pr->id;
	struct cpuinfo_x86 *c = cpu_data + cpu;

	pr->pdc = NULL;
	if (c->x86_vendor == X86_VENDOR_INTEL)
		init_intel_pdc(pr, c);

	return;
}

EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
+0 −67
Original line number Diff line number Diff line
@@ -297,68 +297,6 @@ acpi_cpufreq_guess_freq (
}


/* 
 * acpi_processor_cpu_init_pdc_est - let BIOS know about the SMP capabilities
 * of this driver
 * @perf: processor-specific acpi_io_data struct
 * @cpu: CPU being initialized
 *
 * To avoid issues with legacy OSes, some BIOSes require to be informed of
 * the SMP capabilities of OS P-state driver. Here we set the bits in _PDC 
 * accordingly, for Enhanced Speedstep. Actual call to _PDC is done in
 * driver/acpi/processor.c
 */
static void 
acpi_processor_cpu_init_pdc_est(
		struct acpi_processor_performance *perf, 
		unsigned int cpu,
		struct acpi_object_list *obj_list
		)
{
	union acpi_object *obj;
	u32 *buf;
	struct cpuinfo_x86 *c = cpu_data + cpu;
	dprintk("acpi_processor_cpu_init_pdc_est\n");

	if (!cpu_has(c, X86_FEATURE_EST))
		return;

	/* Initialize pdc. It will be used later. */
	if (!obj_list)
		return;
		
	if (!(obj_list->count && obj_list->pointer))
		return;

	obj = obj_list->pointer;
	if ((obj->buffer.length == 12) && obj->buffer.pointer) {
		buf = (u32 *)obj->buffer.pointer;
       		buf[0] = ACPI_PDC_REVISION_ID;
       		buf[1] = 1;
       		buf[2] = ACPI_PDC_EST_CAPABILITY_SMP;
		perf->pdc = obj_list;
	}
	return;
}
 

/* CPU specific PDC initialization */
static void 
acpi_processor_cpu_init_pdc(
		struct acpi_processor_performance *perf, 
		unsigned int cpu,
		struct acpi_object_list *obj_list
		)
{
	struct cpuinfo_x86 *c = cpu_data + cpu;
	dprintk("acpi_processor_cpu_init_pdc\n");
	perf->pdc = NULL;
	if (cpu_has(c, X86_FEATURE_EST))
		acpi_processor_cpu_init_pdc_est(perf, cpu, obj_list);
	return;
}


static int
acpi_cpufreq_cpu_init (
	struct cpufreq_policy   *policy)
@@ -373,9 +311,6 @@ acpi_cpufreq_cpu_init (
	struct acpi_object_list 	arg_list = {1, &arg0};

	dprintk("acpi_cpufreq_cpu_init\n");
	/* setup arg_list for _PDC settings */
        arg0.buffer.length = 12;
        arg0.buffer.pointer = (u8 *) arg0_buf;

	data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL);
	if (!data)
@@ -383,9 +318,7 @@ acpi_cpufreq_cpu_init (

	acpi_io_data[cpu] = data;

	acpi_processor_cpu_init_pdc(&data->acpi_data, cpu, &arg_list);
	result = acpi_processor_register_performance(&data->acpi_data, cpu);
	data->acpi_data.pdc = NULL;

	if (result)
		goto err_free;
+0 −12
Original line number Diff line number Diff line
@@ -364,22 +364,10 @@ static struct acpi_processor_performance p;
 */
static int centrino_cpu_init_acpi(struct cpufreq_policy *policy)
{
	union acpi_object		arg0 = {ACPI_TYPE_BUFFER};
	u32				arg0_buf[3];
	struct acpi_object_list		arg_list = {1, &arg0};
	unsigned long			cur_freq;
	int				result = 0, i;
	unsigned int			cpu = policy->cpu;

	/* _PDC settings */
	arg0.buffer.length = 12;
	arg0.buffer.pointer = (u8 *) arg0_buf;
	arg0_buf[0] = ACPI_PDC_REVISION_ID;
	arg0_buf[1] = 1;
	arg0_buf[2] = ACPI_PDC_EST_CAPABILITY_SMP_MSR;

	p.pdc = &arg_list;

	/* register with ACPI core */
	if (acpi_processor_register_performance(&p, cpu)) {
		dprintk(KERN_INFO PFX "obtaining ACPI data failed\n");
Loading