cpu-probe.c
来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 891 行 · 第 1/2 页
C
891 行
/* * arch/mips64/kernel/cpu-probe.c * * Processor capabilities determination functions. * * Copyright (C) xxxx the Anonymous * Copyright (C) 2003, 2004 Maciej W. Rozycki * * 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. */#include <linux/init.h>#include <linux/kernel.h>#include <linux/ptrace.h>#include <linux/stddef.h>#include <asm/bugs.h>#include <asm/compiler.h>#include <asm/cpu.h>#include <asm/fpu.h>#include <asm/mipsregs.h>#include <asm/system.h>/* * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, * the implementation of the "wait" feature differs between CPU families. This * points to the function that implements CPU specific wait. * The wait instruction stops the pipeline and reduces the power consumption of * the CPU very much. */void (*cpu_wait)(void) = NULL;static void r3081_wait(void){ unsigned long cfg = read_c0_conf(); write_c0_conf(cfg | R30XX_CONF_HALT);}static void r39xx_wait(void){ unsigned long cfg = read_c0_conf(); write_c0_conf(cfg | TX39_CONF_HALT);}static void r4k_wait(void){ __asm__(".set\tmips3\n\t" "wait\n\t" ".set\tmips0");}void au1k_wait(void){#ifdef CONFIG_PM /* using the wait instruction makes CP0 counter unusable */ __asm__(".set\tmips3\n\t" "wait\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" ".set\tmips0");#else __asm__("nop\n\t" "nop");#endif}static inline void check_wait(void){ struct cpuinfo_mips *c = ¤t_cpu_data; printk("Checking for 'wait' instruction... "); switch (c->cputype) { case CPU_R3081: case CPU_R3081E: cpu_wait = r3081_wait; printk(" available.\n"); break; case CPU_TX3927: cpu_wait = r39xx_wait; printk(" available.\n"); break; case CPU_R4200:/* case CPU_R4300: */ case CPU_R4600: case CPU_R4640: case CPU_R4650: case CPU_R4700: case CPU_R5000: case CPU_NEVADA: case CPU_RM7000: case CPU_RM9000: case CPU_TX49XX: case CPU_4KC: case CPU_4KEC: case CPU_4KSC: case CPU_5KC:/* case CPU_20KC:*/ case CPU_24K: case CPU_25KF: cpu_wait = r4k_wait; printk(" available.\n"); break; case CPU_AU1000: case CPU_AU1100: case CPU_AU1500: cpu_wait = au1k_wait; printk(" available.\n"); break; default: printk(" unavailable.\n"); break; }}static inline void align_mod(const int align, const int mod){ asm volatile( ".set push\n\t" ".set noreorder\n\t" ".balign %0\n\t" ".rept %1\n\t" "nop\n\t" ".endr\n\t" ".set pop" : : "n" (align), "n" (mod));}static inline void mult_sh_align_mod(long *v1, long *v2, long *w, const int align, const int mod){ unsigned long flags; int m1, m2; long p, s, lv1, lv2, lw; /* * We want the multiply and the shift to be isolated from the * rest of the code to disable gcc optimizations. Hence the * asm statements that execute nothing, but make gcc not know * what the values of m1, m2 and s are and what lv2 and p are * used for. */ local_irq_save(flags); /* * The following code leads to a wrong result of the first * dsll32 when executed on R4000 rev. 2.2 or 3.0 (PRId * 00000422 or 00000430, respectively). * * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and * 3.0" by MIPS Technologies, Inc., errata #16 and #28 for * details. I got no permission to duplicate them here, * sigh... --macro */ asm volatile( "" : "=r" (m1), "=r" (m2), "=r" (s) : "0" (5), "1" (8), "2" (5)); align_mod(align, mod); /* * The trailing nop is needed to fullfill the two-instruction * requirement between reading hi/lo and staring a mult/div. * Leaving it out may cause gas insert a nop itself breaking * the desired alignment of the next chunk. */ asm volatile( ".set push\n\t" ".set noat\n\t" ".set noreorder\n\t" ".set nomacro\n\t" "mult %2, %3\n\t" "dsll32 %0, %4, %5\n\t" "mflo $0\n\t" "dsll32 %1, %4, %5\n\t" "nop\n\t" ".set pop" : "=&r" (lv1), "=r" (lw) : "r" (m1), "r" (m2), "r" (s), "I" (0) : "hi", "lo", GCC_REG_ACCUM); /* We have to use single integers for m1 and m2 and a double * one for p to be sure the mulsidi3 gcc's RTL multiplication * instruction has the workaround applied. Older versions of * gcc have correct umulsi3 and mulsi3, but other * multiplication variants lack the workaround. */ asm volatile( "" : "=r" (m1), "=r" (m2), "=r" (s) : "0" (m1), "1" (m2), "2" (s)); align_mod(align, mod); p = m1 * m2; lv2 = s << 32; asm volatile( "" : "=r" (lv2) : "0" (lv2), "r" (p)); local_irq_restore(flags); *v1 = lv1; *v2 = lv2; *w = lw;}static inline void check_mult_sh(void){ long v1[8], v2[8], w[8]; int bug, fix, i; printk("Checking for the multiply/shift bug... "); /* * Testing discovered false negatives for certain code offsets * into cache lines. Hence we test all possible offsets for * the worst assumption of an R4000 I-cache line width of 32 * bytes. * * We can't use a loop as alignment directives need to be * immediates. */ mult_sh_align_mod(&v1[0], &v2[0], &w[0], 32, 0); mult_sh_align_mod(&v1[1], &v2[1], &w[1], 32, 1); mult_sh_align_mod(&v1[2], &v2[2], &w[2], 32, 2); mult_sh_align_mod(&v1[3], &v2[3], &w[3], 32, 3); mult_sh_align_mod(&v1[4], &v2[4], &w[4], 32, 4); mult_sh_align_mod(&v1[5], &v2[5], &w[5], 32, 5); mult_sh_align_mod(&v1[6], &v2[6], &w[6], 32, 6); mult_sh_align_mod(&v1[7], &v2[7], &w[7], 32, 7); bug = 0; for (i = 0; i < 8; i++) if (v1[i] != w[i]) bug = 1; if (bug == 0) { printk("no.\n"); return; } printk("yes, workaround... "); fix = 1; for (i = 0; i < 8; i++) if (v2[i] != w[i]) fix = 0; if (fix == 1) { printk("yes.\n"); return; } printk("no.\n"); panic("Reliable operation impossible!\n"#ifndef CONFIG_CPU_R4000 "Configure for R4000 to enable the workaround."#else "Please report to <linux-mips@linux-mips.org>."#endif );}static volatile int daddi_ov __initdata = 0;asmlinkage void __init do_daddi_ov(struct pt_regs *regs){ daddi_ov = 1; regs->cp0_epc += 4;}static inline void check_daddi(void){ extern asmlinkage void handle_daddi_ov(void); unsigned long flags; void *handler; long v, tmp; printk("Checking for the daddi bug... "); local_irq_save(flags); handler = set_except_vector(12, handle_daddi_ov); /* * The following code fails to trigger an overflow exception * when executed on R4000 rev. 2.2 or 3.0 (PRId 00000422 or * 00000430, respectively). * * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and * 3.0" by MIPS Technologies, Inc., erratum #23 for details. * I got no permission to duplicate it here, sigh... --macro */ asm volatile( ".set push\n\t" ".set noat\n\t" ".set noreorder\n\t" ".set nomacro\n\t" "addiu %1, $0, %2\n\t" "dsrl %1, %1, 1\n\t"#ifdef HAVE_AS_SET_DADDI ".set daddi\n\t"#endif "daddi %0, %1, %3\n\t" ".set pop" : "=r" (v), "=&r" (tmp) : "I" (0xffffffffffffdb9a), "I" (0x1234)); set_except_vector(12, handler); local_irq_restore(flags); if (daddi_ov) { printk("no.\n"); return; } printk("yes, workaround... "); local_irq_save(flags); handler = set_except_vector(12, handle_daddi_ov); asm volatile( "addiu %1, $0, %2\n\t" "dsrl %1, %1, 1\n\t" "daddi %0, %1, %3" : "=r" (v), "=&r" (tmp) : "I" (0xffffffffffffdb9a), "I" (0x1234)); set_except_vector(12, handler); local_irq_restore(flags); if (daddi_ov) { printk("yes.\n"); return; } printk("no.\n"); panic("Reliable operation impossible!\n"#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400) "Configure for R4000 or R4400 to enable the workaround."#else "Please report to <linux-mips@linux-mips.org>."#endif );}static inline void check_daddiu(void){ long v, w, tmp; printk("Checking for the daddiu bug... "); /* * The following code leads to a wrong result of daddiu when * executed on R4400 rev. 1.0 (PRId 00000440). * * See "MIPS R4400PC/SC Errata, Processor Revision 1.0" by * MIPS Technologies, Inc., erratum #7 for details. * * According to "MIPS R4000PC/SC Errata, Processor Revision * 2.2 and 3.0" by MIPS Technologies, Inc., erratum #41 this * problem affects R4000 rev. 2.2 and 3.0 (PRId 00000422 and * 00000430, respectively), too. Testing failed to trigger it * so far. * * I got no permission to duplicate the errata here, sigh... * --macro */ asm volatile( ".set push\n\t" ".set noat\n\t" ".set noreorder\n\t" ".set nomacro\n\t" "addiu %2, $0, %3\n\t" "dsrl %2, %2, 1\n\t"#ifdef HAVE_AS_SET_DADDI ".set daddi\n\t"#endif "daddiu %0, %2, %4\n\t" "addiu %1, $0, %4\n\t" "daddu %1, %2\n\t" ".set pop" : "=&r" (v), "=&r" (w), "=&r" (tmp) : "I" (0xffffffffffffdb9a), "I" (0x1234)); if (v == w) { printk("no.\n"); return; } printk("yes, workaround... "); asm volatile( "addiu %2, $0, %3\n\t" "dsrl %2, %2, 1\n\t" "daddiu %0, %2, %4\n\t" "addiu %1, $0, %4\n\t" "daddu %1, %2" : "=&r" (v), "=&r" (w), "=&r" (tmp) : "I" (0xffffffffffffdb9a), "I" (0x1234)); if (v == w) { printk("yes.\n"); return; } printk("no.\n"); panic("Reliable operation impossible!\n"#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400) "Configure for R4000 or R4400 to enable the workaround."#else "Please report to <linux-mips@linux-mips.org>."#endif );}void __init check_bugs(void){ check_wait(); check_mult_sh(); check_daddi(); check_daddiu();}/* * Probe whether cpu has config register by trying to play with * alternate cache bit and see whether it matters. * It's used by cpu_probe to distinguish between R3000A and R3081. */static inline int cpu_has_confreg(void){#ifdef CONFIG_CPU_R3000 extern unsigned long r3k_cache_size(unsigned long); unsigned long size1, size2; unsigned long cfg = read_c0_conf(); size1 = r3k_cache_size(ST0_ISC); write_c0_conf(cfg ^ R30XX_CONF_AC); size2 = r3k_cache_size(ST0_ISC); write_c0_conf(cfg); return size1 != size2;#else return 0;#endif}/* * Get the FPU Implementation/Revision. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?