Skip to content

Commit

Permalink
[ACPI] Avoid BIOS inflicted crashes by evaluating _PDC only once
Browse files Browse the repository at this point in the history
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: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Venkatesh Pallipadi authored and Len Brown committed Dec 1, 2005
1 parent d2149b5 commit 05131ec
Show file tree
Hide file tree
Showing 16 changed files with 243 additions and 231 deletions.
2 changes: 1 addition & 1 deletion arch/i386/kernel/acpi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

58 changes: 0 additions & 58 deletions arch/i386/kernel/acpi/cstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
75 changes: 75 additions & 0 deletions arch/i386/kernel/acpi/processor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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);
67 changes: 0 additions & 67 deletions arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -373,19 +311,14 @@ 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)
return (-ENOMEM);

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;
Expand Down
12 changes: 0 additions & 12 deletions arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
5 changes: 5 additions & 0 deletions arch/ia64/kernel/cpufreq/Makefile
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += acpi-cpufreq.o

ifneq ($(CONFIG_ACPI_PROCESSOR),)
obj-y += acpi-processor.o
endif

51 changes: 0 additions & 51 deletions arch/ia64/kernel/cpufreq/acpi-cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,48 +269,6 @@ acpi_cpufreq_verify (
}


/*
* processor_init_pdc - 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. Actual call to _PDC is done in driver/acpi/processor.c
*/
static void
processor_init_pdc (
struct acpi_processor_performance *perf,
unsigned int cpu,
struct acpi_object_list *obj_list
)
{
union acpi_object *obj;
u32 *buf;

dprintk("processor_init_pdc\n");

perf->pdc = NULL;
/* 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;
}


static int
acpi_cpufreq_cpu_init (
struct cpufreq_policy *policy)
Expand All @@ -320,14 +278,7 @@ acpi_cpufreq_cpu_init (
struct cpufreq_acpi_io *data;
unsigned int result = 0;

union acpi_object arg0 = {ACPI_TYPE_BUFFER};
u32 arg0_buf[3];
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 = kmalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL);
if (!data)
Expand All @@ -337,9 +288,7 @@ acpi_cpufreq_cpu_init (

acpi_io_data[cpu] = data;

processor_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;
Expand Down
Loading

0 comments on commit 05131ec

Please sign in to comment.