⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vfpmodule.c

📁 linux 内核源代码
💻 C
字号:
/* *  linux/arch/arm/vfp/vfpmodule.c * *  Copyright (C) 2004 ARM Limited. *  Written by Deep Blue Solutions Limited. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/init.h>#include <asm/thread_notify.h>#include <asm/vfp.h>#include "vfpinstr.h"#include "vfp.h"/* * Our undef handlers (in entry.S) */void vfp_testing_entry(void);void vfp_support_entry(void);void vfp_null_entry(void);void (*vfp_vector)(void) = vfp_null_entry;union vfp_state *last_VFP_context[NR_CPUS];/* * Dual-use variable. * Used in startup: set to non-zero if VFP checks fail * After startup, holds VFP architecture */unsigned int VFP_arch;static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v){	struct thread_info *thread = v;	union vfp_state *vfp;	__u32 cpu = thread->cpu;	if (likely(cmd == THREAD_NOTIFY_SWITCH)) {		u32 fpexc = fmrx(FPEXC);#ifdef CONFIG_SMP		/*		 * On SMP, if VFP is enabled, save the old state in		 * case the thread migrates to a different CPU. The		 * restoring is done lazily.		 */		if ((fpexc & FPEXC_EN) && last_VFP_context[cpu]) {			vfp_save_state(last_VFP_context[cpu], fpexc);			last_VFP_context[cpu]->hard.cpu = cpu;		}		/*		 * Thread migration, just force the reloading of the		 * state on the new CPU in case the VFP registers		 * contain stale data.		 */		if (thread->vfpstate.hard.cpu != cpu)			last_VFP_context[cpu] = NULL;#endif		/*		 * Always disable VFP so we can lazily save/restore the		 * old state.		 */		fmxr(FPEXC, fpexc & ~FPEXC_EN);		return NOTIFY_DONE;	}	vfp = &thread->vfpstate;	if (cmd == THREAD_NOTIFY_FLUSH) {		/*		 * Per-thread VFP initialisation.		 */		memset(vfp, 0, sizeof(union vfp_state));		vfp->hard.fpexc = FPEXC_EN;		vfp->hard.fpscr = FPSCR_ROUND_NEAREST;		/*		 * Disable VFP to ensure we initialise it first.		 */		fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);	}	/* flush and release case: Per-thread VFP cleanup. */	if (last_VFP_context[cpu] == vfp)		last_VFP_context[cpu] = NULL;	return NOTIFY_DONE;}static struct notifier_block vfp_notifier_block = {	.notifier_call	= vfp_notifier,};/* * Raise a SIGFPE for the current process. * sicode describes the signal being raised. */void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs){	siginfo_t info;	memset(&info, 0, sizeof(info));	info.si_signo = SIGFPE;	info.si_code = sicode;	info.si_addr = (void __user *)(instruction_pointer(regs) - 4);	/*	 * This is the same as NWFPE, because it's not clear what	 * this is used for	 */	current->thread.error_code = 0;	current->thread.trap_no = 6;	send_sig_info(SIGFPE, &info, current);}static void vfp_panic(char *reason){	int i;	printk(KERN_ERR "VFP: Error: %s\n", reason);	printk(KERN_ERR "VFP: EXC 0x%08x SCR 0x%08x INST 0x%08x\n",		fmrx(FPEXC), fmrx(FPSCR), fmrx(FPINST));	for (i = 0; i < 32; i += 2)		printk(KERN_ERR "VFP: s%2u: 0x%08x s%2u: 0x%08x\n",		       i, vfp_get_float(i), i+1, vfp_get_float(i+1));}/* * Process bitmask of exception conditions. */static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs){	int si_code = 0;	pr_debug("VFP: raising exceptions %08x\n", exceptions);	if (exceptions == VFP_EXCEPTION_ERROR) {		vfp_panic("unhandled bounce");		vfp_raise_sigfpe(0, regs);		return;	}	/*	 * If any of the status flags are set, update the FPSCR.	 * Comparison instructions always return at least one of	 * these flags set.	 */	if (exceptions & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V))		fpscr &= ~(FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V);	fpscr |= exceptions;	fmxr(FPSCR, fpscr);#define RAISE(stat,en,sig)				\	if (exceptions & stat && fpscr & en)		\		si_code = sig;	/*	 * These are arranged in priority order, least to highest.	 */	RAISE(FPSCR_DZC, FPSCR_DZE, FPE_FLTDIV);	RAISE(FPSCR_IXC, FPSCR_IXE, FPE_FLTRES);	RAISE(FPSCR_UFC, FPSCR_UFE, FPE_FLTUND);	RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF);	RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV);	if (si_code)		vfp_raise_sigfpe(si_code, regs);}/* * Emulate a VFP instruction. */static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs){	u32 exceptions = VFP_EXCEPTION_ERROR;	pr_debug("VFP: emulate: INST=0x%08x SCR=0x%08x\n", inst, fpscr);	if (INST_CPRTDO(inst)) {		if (!INST_CPRT(inst)) {			/*			 * CPDO			 */			if (vfp_single(inst)) {				exceptions = vfp_single_cpdo(inst, fpscr);			} else {				exceptions = vfp_double_cpdo(inst, fpscr);			}		} else {			/*			 * A CPRT instruction can not appear in FPINST2, nor			 * can it cause an exception.  Therefore, we do not			 * have to emulate it.			 */		}	} else {		/*		 * A CPDT instruction can not appear in FPINST2, nor can		 * it cause an exception.  Therefore, we do not have to		 * emulate it.		 */	}	return exceptions & ~VFP_NAN_FLAG;}/* * Package up a bounce condition. */void VFP9_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs){	u32 fpscr, orig_fpscr, exceptions, inst;	pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc);	/*	 * Enable access to the VFP so we can handle the bounce.	 */	fmxr(FPEXC, fpexc & ~(FPEXC_EX|FPEXC_FPV2|FPEXC_INV|FPEXC_UFC|FPEXC_OFC|FPEXC_IOC));	orig_fpscr = fpscr = fmrx(FPSCR);	/*	 * If we are running with inexact exceptions enabled, we need to	 * emulate the trigger instruction.  Note that as we're emulating	 * the trigger instruction, we need to increment PC.	 */	if (fpscr & FPSCR_IXE) {		regs->ARM_pc += 4;		goto emulate;	}	barrier();	/*	 * Modify fpscr to indicate the number of iterations remaining	 */	if (fpexc & FPEXC_EX) {		u32 len;		len = fpexc + (1 << FPEXC_LENGTH_BIT);		fpscr &= ~FPSCR_LENGTH_MASK;		fpscr |= (len & FPEXC_LENGTH_MASK) << (FPSCR_LENGTH_BIT - FPEXC_LENGTH_BIT);	}	/*	 * Handle the first FP instruction.  We used to take note of the	 * FPEXC bounce reason, but this appears to be unreliable.	 * Emulate the bounced instruction instead.	 */	inst = fmrx(FPINST);	exceptions = vfp_emulate_instruction(inst, fpscr, regs);	if (exceptions)		vfp_raise_exceptions(exceptions, inst, orig_fpscr, regs);	/*	 * If there isn't a second FP instruction, exit now.	 */	if (!(fpexc & FPEXC_FPV2))		return;	/*	 * The barrier() here prevents fpinst2 being read	 * before the condition above.	 */	barrier();	trigger = fmrx(FPINST2);	orig_fpscr = fpscr = fmrx(FPSCR); emulate:	exceptions = vfp_emulate_instruction(trigger, fpscr, regs);	if (exceptions)		vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs);}static void vfp_enable(void *unused){	u32 access = get_copro_access();	/*	 * Enable full access to VFP (cp10 and cp11)	 */	set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11));}#include <linux/smp.h>/* * VFP support code initialisation. */static int __init vfp_init(void){	unsigned int vfpsid;	unsigned int cpu_arch = cpu_architecture();	u32 access = 0;	if (cpu_arch >= CPU_ARCH_ARMv6) {		access = get_copro_access();		/*		 * Enable full access to VFP (cp10 and cp11)		 */		set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11));	}	/*	 * First check that there is a VFP that we can use.	 * The handler is already setup to just log calls, so	 * we just need to read the VFPSID register.	 */	vfp_vector = vfp_testing_entry;	barrier();	vfpsid = fmrx(FPSID);	barrier();	vfp_vector = vfp_null_entry;	printk(KERN_INFO "VFP support v0.3: ");	if (VFP_arch) {		printk("not present\n");		/*		 * Restore the copro access register.		 */		if (cpu_arch >= CPU_ARCH_ARMv6)			set_copro_access(access);	} else if (vfpsid & FPSID_NODOUBLE) {		printk("no double precision support\n");	} else {		smp_call_function(vfp_enable, NULL, 1, 1);		VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT;  /* Extract the architecture version */		printk("implementor %02x architecture %d part %02x variant %x rev %x\n",			(vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT,			(vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT,			(vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT,			(vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT,			(vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT);		vfp_vector = vfp_support_entry;		thread_register_notifier(&vfp_notifier_block);		/*		 * We detected VFP, and the support code is		 * in place; report VFP support to userspace.		 */		elf_hwcap |= HWCAP_VFP;	}	return 0;}late_initcall(vfp_init);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -