[PATCH] ppc64: Detect altivec via firmware on unknown CPUs
This patch adds detection of the Altivec capability of the CPU via the firmware in addition to the cpu table. This allows newer CPUs that aren't in the table to still have working altivec support in the kernel. It also fixes a problem where if a CPU isn't recognized as having altivec features, and takes an altivec unavailable exception due to userland issuing altivec instructions, the kernel would happily enable it and context switch the registers ... but not all of them (it would basically forget vrsave). With this patch, the kernel will refuse to enable altivec when the feature isn't detected for the CPU (SIGILL). Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Родитель
547ee84cea
Коммит
187335a4ec
|
@ -922,7 +922,9 @@ fp_unavailable_common:
|
||||||
altivec_unavailable_common:
|
altivec_unavailable_common:
|
||||||
EXCEPTION_PROLOG_COMMON(0xf20, PACA_EXGEN)
|
EXCEPTION_PROLOG_COMMON(0xf20, PACA_EXGEN)
|
||||||
#ifdef CONFIG_ALTIVEC
|
#ifdef CONFIG_ALTIVEC
|
||||||
|
BEGIN_FTR_SECTION
|
||||||
bne .load_up_altivec /* if from user, just load it up */
|
bne .load_up_altivec /* if from user, just load it up */
|
||||||
|
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
|
||||||
#endif
|
#endif
|
||||||
bl .save_nvgprs
|
bl .save_nvgprs
|
||||||
addi r3,r1,STACK_FRAME_OVERHEAD
|
addi r3,r1,STACK_FRAME_OVERHEAD
|
||||||
|
|
|
@ -885,6 +885,7 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
|
||||||
const char *full_path, void *data)
|
const char *full_path, void *data)
|
||||||
{
|
{
|
||||||
char *type = get_flat_dt_prop(node, "device_type", NULL);
|
char *type = get_flat_dt_prop(node, "device_type", NULL);
|
||||||
|
u32 *prop;
|
||||||
|
|
||||||
/* We are scanning "cpu" nodes only */
|
/* We are scanning "cpu" nodes only */
|
||||||
if (type == NULL || strcmp(type, "cpu") != 0)
|
if (type == NULL || strcmp(type, "cpu") != 0)
|
||||||
|
@ -916,6 +917,20 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if we have a VMX and eventually update CPU features */
|
||||||
|
prop = (u32 *)get_flat_dt_prop(node, "ibm,vmx", NULL);
|
||||||
|
if (prop && (*prop) > 0) {
|
||||||
|
cur_cpu_spec->cpu_features |= CPU_FTR_ALTIVEC;
|
||||||
|
cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_ALTIVEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Same goes for Apple's "altivec" property */
|
||||||
|
prop = (u32 *)get_flat_dt_prop(node, "altivec", NULL);
|
||||||
|
if (prop) {
|
||||||
|
cur_cpu_spec->cpu_features |= CPU_FTR_ALTIVEC;
|
||||||
|
cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_ALTIVEC;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1104,7 +1119,9 @@ void __init early_init_devtree(void *params)
|
||||||
|
|
||||||
DBG("Scanning CPUs ...\n");
|
DBG("Scanning CPUs ...\n");
|
||||||
|
|
||||||
/* Retreive hash table size from flattened tree */
|
/* Retreive hash table size from flattened tree plus other
|
||||||
|
* CPU related informations (altivec support, boot CPU ID, ...)
|
||||||
|
*/
|
||||||
scan_flat_dt(early_init_dt_scan_cpus, NULL);
|
scan_flat_dt(early_init_dt_scan_cpus, NULL);
|
||||||
|
|
||||||
/* If hash size wasn't obtained above, we calculate it now based on
|
/* If hash size wasn't obtained above, we calculate it now based on
|
||||||
|
|
|
@ -450,14 +450,12 @@ void kernel_fp_unavailable_exception(struct pt_regs *regs)
|
||||||
|
|
||||||
void altivec_unavailable_exception(struct pt_regs *regs)
|
void altivec_unavailable_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_ALTIVEC
|
|
||||||
if (user_mode(regs)) {
|
if (user_mode(regs)) {
|
||||||
/* A user program has executed an altivec instruction,
|
/* A user program has executed an altivec instruction,
|
||||||
but this kernel doesn't support altivec. */
|
but this kernel doesn't support altivec. */
|
||||||
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
|
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
|
printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
|
||||||
"%lx at %lx\n", regs->trap, regs->nip);
|
"%lx at %lx\n", regs->trap, regs->nip);
|
||||||
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
|
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче