From 9d670280908013004f173b2b86414d9b6918511b Mon Sep 17 00:00:00 2001 From: Jimi Xenidis Date: Thu, 29 Sep 2011 10:55:12 +0000 Subject: powerpc: Split ICSWX ACOP and PID processing Some processors, like embedded, that already have a PID register that is managed by the system. This patch separates the ACOP and PID processing into separate files so that the ACOP code can be shared. Signed-off-by: Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/icswx.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 arch/powerpc/mm/icswx.h (limited to 'arch/powerpc/mm/icswx.h') diff --git a/arch/powerpc/mm/icswx.h b/arch/powerpc/mm/icswx.h new file mode 100644 index 00000000000..07514e49498 --- /dev/null +++ b/arch/powerpc/mm/icswx.h @@ -0,0 +1,39 @@ +#ifndef _ARCH_POWERPC_MM_ICSWX_H_ +#define _ARCH_POWERPC_MM_ICSWX_H_ + +/* + * ICSWX and ACOP Management + * + * Copyright (C) 2011 Anton Blanchard, IBM Corp. + * + * 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 + +/* also used to denote that PIDs are not used */ +#define COP_PID_NONE 0 + +static inline void sync_cop(void *arg) +{ + struct mm_struct *mm = arg; + + if (mm == current->active_mm) + switch_cop(current->active_mm); +} + +#ifdef CONFIG_PPC_ICSWX_PID +extern int get_cop_pid(struct mm_struct *mm); +extern int disable_cop_pid(struct mm_struct *mm); +extern void free_cop_pid(int free_pid); +#else +#define get_cop_pid(m) (COP_PID_NONE) +#define disable_cop_pid(m) (COP_PID_NONE) +#define free_cop_pid(p) +#endif + +#endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */ -- cgit v1.2.3-70-g09d2 From c3dcf53a3fcb01f1d98f6b0cf440bb781dbcbc34 Mon Sep 17 00:00:00 2001 From: Jimi Xenidis Date: Thu, 29 Sep 2011 10:55:14 +0000 Subject: powerpc/icswx: Simple ACOP fault handler This patch adds a fault handler that responds to illegal Coprocessor types. Currently all CTs are treated and illegal. There are two ways to report the fault back to the application. If the application used the record form ("icswx.") then the architected "reject" is emulated. If the application did not used the record form ("icswx") then it is selectable by config whether the failure is silent (as architected) or a SIGILL is generated. In all cases pr_warn() is used to log the bad CT. Signed-off-by: Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/fault.c | 17 +++++ arch/powerpc/mm/icswx.c | 113 +++++++++++++++++++++++++++++++++ arch/powerpc/mm/icswx.h | 23 +++++++ arch/powerpc/platforms/Kconfig.cputype | 13 +++- 4 files changed, 165 insertions(+), 1 deletion(-) (limited to 'arch/powerpc/mm/icswx.h') diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 5efe8c96d37..2f0d1b032a8 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -44,6 +44,8 @@ #include #include +#include "icswx.h" + #ifdef CONFIG_KPROBES static inline int notify_page_fault(struct pt_regs *regs) { @@ -143,6 +145,21 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, is_write = error_code & ESR_DST; #endif /* CONFIG_4xx || CONFIG_BOOKE */ +#ifdef CONFIG_PPC_ICSWX + /* + * we need to do this early because this "data storage + * interrupt" does not update the DAR/DEAR so we don't want to + * look at it + */ + if (error_code & ICSWX_DSI_UCT) { + int ret; + + ret = acop_handle_fault(regs, address, error_code); + if (ret) + return ret; + } +#endif + if (notify_page_fault(regs)) return 0; diff --git a/arch/powerpc/mm/icswx.c b/arch/powerpc/mm/icswx.c index a98850fd777..5d9a59eaad9 100644 --- a/arch/powerpc/mm/icswx.c +++ b/arch/powerpc/mm/icswx.c @@ -17,6 +17,8 @@ #include #include #include +#include + #include "icswx.h" /* @@ -158,3 +160,114 @@ void drop_cop(unsigned long acop, struct mm_struct *mm) spin_unlock(&mm->page_table_lock); } EXPORT_SYMBOL_GPL(drop_cop); + +static int acop_use_cop(int ct) +{ + /* todo */ + return -1; +} + +/* + * Get the instruction word at the NIP + */ +static u32 acop_get_inst(struct pt_regs *regs) +{ + u32 inst; + u32 __user *p; + + p = (u32 __user *)regs->nip; + if (!access_ok(VERIFY_READ, p, sizeof(*p))) + return 0; + + if (__get_user(inst, p)) + return 0; + + return inst; +} + +/** + * @regs: regsiters at time of interrupt + * @address: storage address + * @error_code: Fault code, usually the DSISR or ESR depending on + * processor type + * + * Return 0 if we are able to resolve the data storage fault that + * results from a CT miss in the ACOP register. + */ +int acop_handle_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code) +{ + int ct; + u32 inst = 0; + + if (!cpu_has_feature(CPU_FTR_ICSWX)) { + pr_info("No coprocessors available"); + _exception(SIGILL, regs, ILL_ILLOPN, address); + } + + if (!user_mode(regs)) { + /* this could happen if the HV denies the + * kernel access, for now we just die */ + die("ICSWX from kernel failed", regs, SIGSEGV); + } + + /* Some implementations leave us a hint for the CT */ + ct = ICSWX_GET_CT_HINT(error_code); + if (ct < 0) { + /* we have to peek at the instruction word to figure out CT */ + u32 ccw; + u32 rs; + + inst = acop_get_inst(regs); + if (inst == 0) + return -1; + + rs = (inst >> (31 - 10)) & 0x1f; + ccw = regs->gpr[rs]; + ct = (ccw >> 16) & 0x3f; + } + + if (!acop_use_cop(ct)) + return 0; + + /* at this point the CT is unknown to the system */ + pr_warn("%s[%d]: Coprocessor %d is unavailable", + current->comm, current->pid, ct); + + /* get inst if we don't already have it */ + if (inst == 0) { + inst = acop_get_inst(regs); + if (inst == 0) + return -1; + } + + /* Check if the instruction is the "record form" */ + if (inst & 1) { + /* + * the instruction is "record" form so we can reject + * using CR0 + */ + regs->ccr &= ~(0xful << 28); + regs->ccr |= ICSWX_RC_NOT_FOUND << 28; + + /* Move on to the next instruction */ + regs->nip += 4; + } else { + /* + * There is no architected mechanism to report a bad + * CT so we could either SIGILL or report nothing. + * Since the non-record version should only bu used + * for "hints" or "don't care" we should probably do + * nothing. However, I could see how some people + * might want an SIGILL so it here if you want it. + */ +#ifdef CONFIG_PPC_ICSWX_USE_SIGILL + _exception(SIGILL, regs, ILL_ILLOPN, address); +#else + regs->nip += 4; +#endif + } + + return 0; +} +EXPORT_SYMBOL_GPL(acop_handle_fault); diff --git a/arch/powerpc/mm/icswx.h b/arch/powerpc/mm/icswx.h index 07514e49498..42176bd0884 100644 --- a/arch/powerpc/mm/icswx.h +++ b/arch/powerpc/mm/icswx.h @@ -36,4 +36,27 @@ extern void free_cop_pid(int free_pid); #define free_cop_pid(p) #endif +/* + * These are implementation bits for architected registers. If this + * ever becomes architecture the should be moved to reg.h et. al. + */ +/* UCT is the same bit for Server and Embedded */ +#define ICSWX_DSI_UCT 0x00004000 /* Unavailable Coprocessor Type */ + +#ifdef CONFIG_PPC_BOOK3E +/* Embedded implementation gives us no hints as to what the CT is */ +#define ICSWX_GET_CT_HINT(x) (-1) +#else +/* Server implementation contains the CT value in the DSISR */ +#define ICSWX_DSISR_CTMASK 0x00003f00 +#define ICSWX_GET_CT_HINT(x) (((x) & ICSWX_DSISR_CTMASK) >> 8) +#endif + +#define ICSWX_RC_STARTED 0x8 /* The request has been started */ +#define ICSWX_RC_NOT_IDLE 0x4 /* No coprocessor found idle */ +#define ICSWX_RC_NOT_FOUND 0x2 /* No coprocessor found */ +#define ICSWX_RC_UNDEFINED 0x1 /* Reserved */ + +extern int acop_handle_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code); #endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */ diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 46d2a7c77dc..836b44286b3 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -257,9 +257,20 @@ config PPC_ICSWX_PID depends on PPC_ICSWX && POWER4 default y ---help--- - PID register in server is used explicitly for ICSWX. In + The PID register in server is used explicitly for ICSWX. In embedded systems PID managment is done by the system. +config PPC_ICSWX_USE_SIGILL + bool "Should a bad CT cause a SIGILL?" + depends on PPC_ICSWX + default n + ---help--- + Should a bad CT used for "non-record form ICSWX" cause an + illegal intruction signal or should it be silent as + architected. + + If in doubt, say N here. + config SPE bool "SPE Support" depends on E200 || (E500 && !PPC_E500MC) -- cgit v1.2.3-70-g09d2