diff options
Diffstat (limited to 'arch/powerpc/kernel')
41 files changed, 951 insertions, 490 deletions
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 8d1a419df35..71901fbda4a 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -18,12 +18,10 @@ CFLAGS_REMOVE_cputable.o = -pg -mno-sched-epilog CFLAGS_REMOVE_prom_init.o = -pg -mno-sched-epilog CFLAGS_REMOVE_btext.o = -pg -mno-sched-epilog CFLAGS_REMOVE_prom.o = -pg -mno-sched-epilog - -ifdef CONFIG_DYNAMIC_FTRACE -# dynamic ftrace setup. +# do not trace tracer code CFLAGS_REMOVE_ftrace.o = -pg -mno-sched-epilog -endif - +# timers used by tracing +CFLAGS_REMOVE_time.o = -pg -mno-sched-epilog endif obj-y := cputable.o ptrace.o syscalls.o \ @@ -61,6 +59,7 @@ obj-$(CONFIG_HIBERNATION) += swsusp.o suspend.o \ obj64-$(CONFIG_HIBERNATION) += swsusp_asm64.o obj-$(CONFIG_MODULES) += module.o module_$(CONFIG_WORD_SIZE).o obj-$(CONFIG_44x) += cpu_setup_44x.o +obj-$(CONFIG_FSL_BOOKE) += cpu_setup_fsl_booke.o dbell.o extra-$(CONFIG_PPC_STD_MMU) := head_32.o extra-$(CONFIG_PPC64) := head_64.o @@ -76,7 +75,7 @@ obj-y += time.o prom.o traps.o setup-common.o \ obj-$(CONFIG_PPC32) += entry_32.o setup_32.o obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o obj-$(CONFIG_KGDB) += kgdb.o -obj-$(CONFIG_PPC_MULTIPLATFORM) += prom_init.o +obj-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o obj-$(CONFIG_MODULES) += ppc_ksyms.o obj-$(CONFIG_BOOTX_TEXT) += btext.o obj-$(CONFIG_SMP) += smp.o @@ -94,6 +93,7 @@ obj-$(CONFIG_AUDIT) += audit.o obj64-$(CONFIG_AUDIT) += compat_audit.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o +obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 73cb6a3229a..5ffcfaa77d6 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -187,7 +187,7 @@ static struct aligninfo aligninfo[128] = { { 4, ST+F+S+U }, /* 11 1 1010: stfsux */ { 8, ST+F+U }, /* 11 1 1011: stfdux */ INVALID, /* 11 1 1100 */ - INVALID, /* 11 1 1101 */ + { 4, LD+F }, /* 11 1 1101: lfiwzx */ INVALID, /* 11 1 1110 */ INVALID, /* 11 1 1111 */ }; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 19ee491e9e2..1e40bc05394 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -49,7 +49,7 @@ #include <asm/iseries/alpaca.h> #endif #ifdef CONFIG_KVM -#include <asm/kvm_44x.h> +#include <linux/kvm_host.h> #endif #if defined(CONFIG_BOOKE) || defined(CONFIG_40x) @@ -284,9 +284,6 @@ int main(void) #endif /* ! CONFIG_PPC64 */ /* About the CPU features table */ - DEFINE(CPU_SPEC_ENTRY_SIZE, sizeof(struct cpu_spec)); - DEFINE(CPU_SPEC_PVR_MASK, offsetof(struct cpu_spec, pvr_mask)); - DEFINE(CPU_SPEC_PVR_VALUE, offsetof(struct cpu_spec, pvr_value)); DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features)); DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup)); DEFINE(CPU_SPEC_RESTORE, offsetof(struct cpu_spec, cpu_restore)); @@ -361,8 +358,6 @@ int main(void) DEFINE(PTE_SIZE, sizeof(pte_t)); #ifdef CONFIG_KVM - DEFINE(TLBE_BYTES, sizeof(struct kvmppc_44x_tlbe)); - DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack)); DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid)); DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr)); diff --git a/arch/powerpc/kernel/cpu_setup_44x.S b/arch/powerpc/kernel/cpu_setup_44x.S index 10b4ab1008a..7d606f89a83 100644 --- a/arch/powerpc/kernel/cpu_setup_44x.S +++ b/arch/powerpc/kernel/cpu_setup_44x.S @@ -34,6 +34,7 @@ _GLOBAL(__setup_cpu_440grx) blr _GLOBAL(__setup_cpu_460ex) _GLOBAL(__setup_cpu_460gt) +_GLOBAL(__setup_cpu_460sx) mflr r4 bl __init_fpu_44x bl __fixup_440A_mcheck diff --git a/arch/powerpc/kernel/cpu_setup_6xx.S b/arch/powerpc/kernel/cpu_setup_6xx.S index 72d1d739525..54f767e31a1 100644 --- a/arch/powerpc/kernel/cpu_setup_6xx.S +++ b/arch/powerpc/kernel/cpu_setup_6xx.S @@ -15,9 +15,14 @@ #include <asm/ppc_asm.h> #include <asm/asm-offsets.h> #include <asm/cache.h> +#include <asm/mmu.h> _GLOBAL(__setup_cpu_603) mflr r4 +BEGIN_MMU_FTR_SECTION + li r10,0 + mtspr SPRN_SPRG4,r10 /* init SW LRU tracking */ +END_MMU_FTR_SECTION_IFSET(MMU_FTR_NEED_DTLB_SW_LRU) BEGIN_FTR_SECTION bl __init_fpu_registers END_FTR_SECTION_IFCLR(CPU_FTR_FPU_UNAVAILABLE) diff --git a/arch/powerpc/kernel/cpu_setup_fsl_booke.S b/arch/powerpc/kernel/cpu_setup_fsl_booke.S new file mode 100644 index 00000000000..eb4b9adcedb --- /dev/null +++ b/arch/powerpc/kernel/cpu_setup_fsl_booke.S @@ -0,0 +1,31 @@ +/* + * This file contains low level CPU setup functions. + * Kumar Gala <galak@kernel.crashing.org> + * Copyright 2009 Freescale Semiconductor, Inc. + * + * Based on cpu_setup_6xx code by + * Benjamin Herrenschmidt <benh@kernel.crashing.org> + * + * 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 <asm/processor.h> +#include <asm/cputable.h> +#include <asm/ppc_asm.h> + +_GLOBAL(__setup_cpu_e200) + /* enable dedicated debug exception handling resources (Debug APU) */ + mfspr r3,SPRN_HID0 + ori r3,r3,HID0_DAPUEN@l + mtspr SPRN_HID0,r3 + b __setup_e200_ivors +_GLOBAL(__setup_cpu_e500v1) +_GLOBAL(__setup_cpu_e500v2) + b __setup_e500_ivors +_GLOBAL(__setup_cpu_e500mc) + b __setup_e500mc_ivors + diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 923f87aff20..cd1b687544f 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -35,6 +35,10 @@ const char *powerpc_base_platform; * and ppc64 */ #ifdef CONFIG_PPC32 +extern void __setup_cpu_e200(unsigned long offset, struct cpu_spec* spec); +extern void __setup_cpu_e500v1(unsigned long offset, struct cpu_spec* spec); +extern void __setup_cpu_e500v2(unsigned long offset, struct cpu_spec* spec); +extern void __setup_cpu_e500mc(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_440ep(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_440epx(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_440gx(unsigned long offset, struct cpu_spec* spec); @@ -43,6 +47,7 @@ extern void __setup_cpu_440spe(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_440x5(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_460ex(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_460gt(unsigned long offset, struct cpu_spec* spec); +extern void __setup_cpu_460sx(unsigned long offset, struct cpu_spec *spec); extern void __setup_cpu_603(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_604(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_750(unsigned long offset, struct cpu_spec* spec); @@ -726,6 +731,8 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_setup = __setup_cpu_750, .machine_check = machine_check_generic, .platform = "ppc750", + .oprofile_cpu_type = "ppc/750", + .oprofile_type = PPC_OPROFILE_G4, }, { /* 750FX rev 2.0 must disable HID0[DPM] */ .pvr_mask = 0xffffffff, @@ -741,6 +748,8 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_setup = __setup_cpu_750, .machine_check = machine_check_generic, .platform = "ppc750", + .oprofile_cpu_type = "ppc/750", + .oprofile_type = PPC_OPROFILE_G4, }, { /* 750FX (All revs except 2.0) */ .pvr_mask = 0xffff0000, @@ -756,6 +765,8 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_setup = __setup_cpu_750fx, .machine_check = machine_check_generic, .platform = "ppc750", + .oprofile_cpu_type = "ppc/750", + .oprofile_type = PPC_OPROFILE_G4, }, { /* 750GX */ .pvr_mask = 0xffff0000, @@ -771,6 +782,8 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_setup = __setup_cpu_750fx, .machine_check = machine_check_generic, .platform = "ppc750", + .oprofile_cpu_type = "ppc/750", + .oprofile_type = PPC_OPROFILE_G4, }, { /* 740/750 (L2CR bit need fixup for 740) */ .pvr_mask = 0xffff0000, @@ -1077,7 +1090,8 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_name = "e300c2", .cpu_features = CPU_FTRS_E300C2, .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, - .mmu_features = MMU_FTR_USE_HIGH_BATS, + .mmu_features = MMU_FTR_USE_HIGH_BATS | + MMU_FTR_NEED_DTLB_SW_LRU, .icache_bsize = 32, .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, @@ -1090,7 +1104,8 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_name = "e300c3", .cpu_features = CPU_FTRS_E300, .cpu_user_features = COMMON_USER, - .mmu_features = MMU_FTR_USE_HIGH_BATS, + .mmu_features = MMU_FTR_USE_HIGH_BATS | + MMU_FTR_NEED_DTLB_SW_LRU, .icache_bsize = 32, .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, @@ -1105,7 +1120,8 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_name = "e300c4", .cpu_features = CPU_FTRS_E300, .cpu_user_features = COMMON_USER, - .mmu_features = MMU_FTR_USE_HIGH_BATS, + .mmu_features = MMU_FTR_USE_HIGH_BATS | + MMU_FTR_NEED_DTLB_SW_LRU, .icache_bsize = 32, .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, @@ -1634,6 +1650,19 @@ static struct cpu_spec __initdata cpu_specs[] = { .machine_check = machine_check_440A, .platform = "ppc440", }, + { /* 460SX */ + .pvr_mask = 0xffffff00, + .pvr_value = 0x13541800, + .cpu_name = "460SX", + .cpu_features = CPU_FTRS_44X, + .cpu_user_features = COMMON_USER_BOOKE, + .mmu_features = MMU_FTR_TYPE_44x, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_460sx, + .machine_check = machine_check_440A, + .platform = "ppc440", + }, { /* default match */ .pvr_mask = 0x00000000, .pvr_value = 0x00000000, @@ -1687,6 +1716,7 @@ static struct cpu_spec __initdata cpu_specs[] = { PPC_FEATURE_UNIFIED_CACHE, .mmu_features = MMU_FTR_TYPE_FSL_E, .dcache_bsize = 32, + .cpu_setup = __setup_cpu_e200, .machine_check = machine_check_e200, .platform = "ppc5554", } @@ -1706,6 +1736,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", .oprofile_type = PPC_OPROFILE_FSL_EMB, + .cpu_setup = __setup_cpu_e500v1, .machine_check = machine_check_e500, .platform = "ppc8540", }, @@ -1724,6 +1755,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", .oprofile_type = PPC_OPROFILE_FSL_EMB, + .cpu_setup = __setup_cpu_e500v2, .machine_check = machine_check_e500, .platform = "ppc8548", }, @@ -1733,12 +1765,14 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_name = "e500mc", .cpu_features = CPU_FTRS_E500MC, .cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU, - .mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS, + .mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS | + MMU_FTR_USE_TLBILX, .icache_bsize = 64, .dcache_bsize = 64, .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", /* xxx - galak, e500mc? */ .oprofile_type = PPC_OPROFILE_FSL_EMB, + .cpu_setup = __setup_cpu_e500mc, .machine_check = machine_check_e500, .platform = "ppce500mc", }, @@ -1762,74 +1796,84 @@ static struct cpu_spec __initdata cpu_specs[] = { static struct cpu_spec the_cpu_spec; -struct cpu_spec * __init identify_cpu(unsigned long offset, unsigned int pvr) +static void __init setup_cpu_spec(unsigned long offset, struct cpu_spec *s) { - struct cpu_spec *s = cpu_specs; struct cpu_spec *t = &the_cpu_spec; - int i; + struct cpu_spec old; - s = PTRRELOC(s); t = PTRRELOC(t); + old = *t; - for (i = 0; i < ARRAY_SIZE(cpu_specs); i++,s++) - if ((pvr & s->pvr_mask) == s->pvr_value) { - /* - * If we are overriding a previous value derived - * from the real PVR with a new value obtained - * using a logical PVR value, don't modify the - * performance monitor fields. - */ - if (t->num_pmcs && !s->num_pmcs) { - t->cpu_name = s->cpu_name; - t->cpu_features = s->cpu_features; - t->cpu_user_features = s->cpu_user_features; - t->icache_bsize = s->icache_bsize; - t->dcache_bsize = s->dcache_bsize; - t->cpu_setup = s->cpu_setup; - t->cpu_restore = s->cpu_restore; - t->platform = s->platform; - /* - * If we have passed through this logic once - * before and have pulled the default case - * because the real PVR was not found inside - * cpu_specs[], then we are possibly running in - * compatibility mode. In that case, let the - * oprofiler know which set of compatibility - * counters to pull from by making sure the - * oprofile_cpu_type string is set to that of - * compatibility mode. If the oprofile_cpu_type - * already has a value, then we are possibly - * overriding a real PVR with a logical one, and, - * in that case, keep the current value for - * oprofile_cpu_type. - */ - if (t->oprofile_cpu_type == NULL) - t->oprofile_cpu_type = s->oprofile_cpu_type; - } else - *t = *s; - *PTRRELOC(&cur_cpu_spec) = &the_cpu_spec; + /* Copy everything, then do fixups */ + *t = *s; + + /* + * If we are overriding a previous value derived from the real + * PVR with a new value obtained using a logical PVR value, + * don't modify the performance monitor fields. + */ + if (old.num_pmcs && !s->num_pmcs) { + t->num_pmcs = old.num_pmcs; + t->pmc_type = old.pmc_type; + t->oprofile_type = old.oprofile_type; + t->oprofile_mmcra_sihv = old.oprofile_mmcra_sihv; + t->oprofile_mmcra_sipr = old.oprofile_mmcra_sipr; + t->oprofile_mmcra_clear = old.oprofile_mmcra_clear; + + /* + * If we have passed through this logic once before and + * have pulled the default case because the real PVR was + * not found inside cpu_specs[], then we are possibly + * running in compatibility mode. In that case, let the + * oprofiler know which set of compatibility counters to + * pull from by making sure the oprofile_cpu_type string + * is set to that of compatibility mode. If the + * oprofile_cpu_type already has a value, then we are + * possibly overriding a real PVR with a logical one, + * and, in that case, keep the current value for + * oprofile_cpu_type. + */ + if (old.oprofile_cpu_type == NULL) + t->oprofile_cpu_type = s->oprofile_cpu_type; + } - /* - * Set the base platform string once; assumes - * we're called with real pvr first. - */ - if (*PTRRELOC(&powerpc_base_platform) == NULL) - *PTRRELOC(&powerpc_base_platform) = t->platform; + *PTRRELOC(&cur_cpu_spec) = &the_cpu_spec; + + /* + * Set the base platform string once; assumes + * we're called with real pvr first. + */ + if (*PTRRELOC(&powerpc_base_platform) == NULL) + *PTRRELOC(&powerpc_base_platform) = t->platform; #if defined(CONFIG_PPC64) || defined(CONFIG_BOOKE) - /* ppc64 and booke expect identify_cpu to also call - * setup_cpu for that processor. I will consolidate - * that at a later time, for now, just use #ifdef. - * we also don't need to PTRRELOC the function pointer - * on ppc64 and booke as we are running at 0 in real - * mode on ppc64 and reloc_offset is always 0 on booke. - */ - if (s->cpu_setup) { - s->cpu_setup(offset, s); - } + /* ppc64 and booke expect identify_cpu to also call setup_cpu for + * that processor. I will consolidate that at a later time, for now, + * just use #ifdef. We also don't need to PTRRELOC the function + * pointer on ppc64 and booke as we are running at 0 in real mode + * on ppc64 and reloc_offset is always 0 on booke. + */ + if (s->cpu_setup) { + s->cpu_setup(offset, s); + } #endif /* CONFIG_PPC64 || CONFIG_BOOKE */ +} + +struct cpu_spec * __init identify_cpu(unsigned long offset, unsigned int pvr) +{ + struct cpu_spec *s = cpu_specs; + int i; + + s = PTRRELOC(s); + + for (i = 0; i < ARRAY_SIZE(cpu_specs); i++,s++) { + if ((pvr & s->pvr_mask) == s->pvr_value) { + setup_cpu_spec(offset, s); return s; } + } + BUG(); + return NULL; } diff --git a/arch/powerpc/kernel/crash_dump.c b/arch/powerpc/kernel/crash_dump.c index 19671aca659..5fb667a6089 100644 --- a/arch/powerpc/kernel/crash_dump.c +++ b/arch/powerpc/kernel/crash_dump.c @@ -48,7 +48,7 @@ static void __init create_trampoline(unsigned long addr) * branch to "addr" we jump to ("addr" + 32 MB). Although it requires * two instructions it doesn't require any registers. */ - patch_instruction(p, PPC_NOP_INSTR); + patch_instruction(p, PPC_INST_NOP); patch_branch(++p, addr + PHYSICAL_START, 0); } diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c new file mode 100644 index 00000000000..1493734cd87 --- /dev/null +++ b/arch/powerpc/kernel/dbell.c @@ -0,0 +1,44 @@ +/* + * Author: Kumar Gala <galak@kernel.crashing.org> + * + * Copyright 2009 Freescale Semiconductor Inc. + * + * 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/stddef.h> +#include <linux/kernel.h> +#include <linux/smp.h> +#include <linux/threads.h> + +#include <asm/dbell.h> + +#ifdef CONFIG_SMP +unsigned long dbell_smp_message[NR_CPUS]; + +void smp_dbell_message_pass(int target, int msg) +{ + int i; + + if(target < NR_CPUS) { + set_bit(msg, &dbell_smp_message[target]); + ppc_msgsnd(PPC_DBELL, 0, target); + } + else if(target == MSG_ALL_BUT_SELF) { + for_each_online_cpu(i) { + if (i == smp_processor_id()) + continue; + set_bit(msg, &dbell_smp_message[i]); + ppc_msgsnd(PPC_DBELL, 0, i); + } + } + else { /* target == MSG_ALL */ + for_each_online_cpu(i) + set_bit(msg, &dbell_smp_message[i]); + ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0); + } +} +#endif diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 6f7eb7e00c7..4dd38f12915 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -63,7 +63,7 @@ debug_transfer_to_handler: .globl crit_transfer_to_handler crit_transfer_to_handler: -#ifdef CONFIG_FSL_BOOKE +#ifdef CONFIG_PPC_BOOK3E_MMU mfspr r0,SPRN_MAS0 stw r0,MAS0(r11) mfspr r0,SPRN_MAS1 @@ -78,7 +78,7 @@ crit_transfer_to_handler: mfspr r0,SPRN_MAS7 stw r0,MAS7(r11) #endif /* CONFIG_PHYS_64BIT */ -#endif /* CONFIG_FSL_BOOKE */ +#endif /* CONFIG_PPC_BOOK3E_MMU */ #ifdef CONFIG_44x mfspr r0,SPRN_MMUCR stw r0,MMUCR(r11) @@ -914,7 +914,7 @@ exc_exit_restart_end: mtspr SPRN_##exc_lvl_srr0,r9; \ mtspr SPRN_##exc_lvl_srr1,r10; -#if defined(CONFIG_FSL_BOOKE) +#if defined(CONFIG_PPC_BOOK3E_MMU) #ifdef CONFIG_PHYS_64BIT #define RESTORE_MAS7 \ lwz r11,MAS7(r1); \ @@ -956,7 +956,7 @@ ret_from_crit_exc: lwz r10,crit_srr1@l(r10); mtspr SPRN_SRR0,r9; mtspr SPRN_SRR1,r10; - RET_FROM_EXC_LEVEL(SPRN_CSRR0, SPRN_CSRR1, RFCI) + RET_FROM_EXC_LEVEL(SPRN_CSRR0, SPRN_CSRR1, PPC_RFCI) #endif /* CONFIG_40x */ #ifdef CONFIG_BOOKE @@ -967,7 +967,7 @@ ret_from_crit_exc: stw r10,KSP_LIMIT(r9) RESTORE_xSRR(SRR0,SRR1); RESTORE_MMU_REGS; - RET_FROM_EXC_LEVEL(SPRN_CSRR0, SPRN_CSRR1, RFCI) + RET_FROM_EXC_LEVEL(SPRN_CSRR0, SPRN_CSRR1, PPC_RFCI) .globl ret_from_debug_exc ret_from_debug_exc: @@ -981,7 +981,7 @@ ret_from_debug_exc: RESTORE_xSRR(SRR0,SRR1); RESTORE_xSRR(CSRR0,CSRR1); RESTORE_MMU_REGS; - RET_FROM_EXC_LEVEL(SPRN_DSRR0, SPRN_DSRR1, RFDI) + RET_FROM_EXC_LEVEL(SPRN_DSRR0, SPRN_DSRR1, PPC_RFDI) .globl ret_from_mcheck_exc ret_from_mcheck_exc: @@ -992,7 +992,7 @@ ret_from_mcheck_exc: RESTORE_xSRR(CSRR0,CSRR1); RESTORE_xSRR(DSRR0,DSRR1); RESTORE_MMU_REGS; - RET_FROM_EXC_LEVEL(SPRN_MCSRR0, SPRN_MCSRR1, RFMCI) + RET_FROM_EXC_LEVEL(SPRN_MCSRR0, SPRN_MCSRR1, PPC_RFMCI) #endif /* CONFIG_BOOKE */ /* @@ -1176,59 +1176,27 @@ _GLOBAL(_mcount) bctr _GLOBAL(ftrace_caller) - /* Based off of objdump optput from glibc */ - stwu r1,-48(r1) - stw r3, 12(r1) - stw r4, 16(r1) - stw r5, 20(r1) - stw r6, 24(r1) - mflr r3 - lwz r4, 52(r1) - mfcr r5 - stw r7, 28(r1) - stw r8, 32(r1) - stw r9, 36(r1) - stw r10,40(r1) - stw r3, 44(r1) - stw r5, 8(r1) + MCOUNT_SAVE_FRAME + /* r3 ends up with link register */ subi r3, r3, MCOUNT_INSN_SIZE .globl ftrace_call ftrace_call: bl ftrace_stub nop - lwz r6, 8(r1) - lwz r0, 44(r1) - lwz r3, 12(r1) - mtctr r0 - lwz r4, 16(r1) - mtcr r6 - lwz r5, 20(r1) - lwz r6, 24(r1) - lwz r0, 52(r1) - lwz r7, 28(r1) - lwz r8, 32(r1) - mtlr r0 - lwz r9, 36(r1) - lwz r10,40(r1) - addi r1, r1, 48 +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +.globl ftrace_graph_call +ftrace_graph_call: + b ftrace_graph_stub +_GLOBAL(ftrace_graph_stub) +#endif + MCOUNT_RESTORE_FRAME + /* old link register ends up in ctr reg */ bctr #else _GLOBAL(mcount) _GLOBAL(_mcount) - stwu r1,-48(r1) - stw r3, 12(r1) - stw r4, 16(r1) - stw r5, 20(r1) - stw r6, 24(r1) - mflr r3 - lwz r4, 52(r1) - mfcr r5 - stw r7, 28(r1) - stw r8, 32(r1) - stw r9, 36(r1) - stw r10,40(r1) - stw r3, 44(r1) - stw r5, 8(r1) + + MCOUNT_SAVE_FRAME subi r3, r3, MCOUNT_INSN_SIZE LOAD_REG_ADDR(r5, ftrace_trace_function) @@ -1236,28 +1204,55 @@ _GLOBAL(_mcount) mtctr r5 bctrl - nop - lwz r6, 8(r1) - lwz r0, 44(r1) - lwz r3, 12(r1) - mtctr r0 - lwz r4, 16(r1) - mtcr r6 - lwz r5, 20(r1) - lwz r6, 24(r1) - lwz r0, 52(r1) - lwz r7, 28(r1) - lwz r8, 32(r1) - mtlr r0 - lwz r9, 36(r1) - lwz r10,40(r1) - addi r1, r1, 48 +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + b ftrace_graph_caller +#endif + MCOUNT_RESTORE_FRAME bctr #endif _GLOBAL(ftrace_stub) blr +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +_GLOBAL(ftrace_graph_caller) + /* load r4 with local address */ + lwz r4, 44(r1) + subi r4, r4, MCOUNT_INSN_SIZE + + /* get the parent address */ + addi r3, r1, 52 + + bl prepare_ftrace_return + nop + + MCOUNT_RESTORE_FRAME + /* old link register ends up in ctr reg */ + bctr + +_GLOBAL(return_to_handler) + /* need to save return values */ + stwu r1, -32(r1) + stw r3, 20(r1) + stw r4, 16(r1) + stw r31, 12(r1) + mr r31, r1 + + bl ftrace_return_to_handler + nop + + /* return value has real return address */ + mtlr r3 + + lwz r3, 20(r1) + lwz r4, 16(r1) + lwz r31,12(r1) + lwz r1, 0(r1) + + /* Jump back to real return address */ + blr +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ + #endif /* CONFIG_MCOUNT */ diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 383ed6eb008..abfc3233047 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -908,6 +908,12 @@ _GLOBAL(ftrace_caller) ftrace_call: bl ftrace_stub nop +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +.globl ftrace_graph_call +ftrace_graph_call: + b ftrace_graph_stub +_GLOBAL(ftrace_graph_stub) +#endif ld r0, 128(r1) mtlr r0 addi r1, r1, 112 @@ -931,13 +937,90 @@ _GLOBAL(_mcount) ld r5,0(r5) mtctr r5 bctrl - nop + + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + b ftrace_graph_caller +#endif ld r0, 128(r1) mtlr r0 addi r1, r1, 112 _GLOBAL(ftrace_stub) blr -#endif -#endif +#endif /* CONFIG_DYNAMIC_FTRACE */ + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +_GLOBAL(ftrace_graph_caller) + /* load r4 with local address */ + ld r4, 128(r1) + subi r4, r4, MCOUNT_INSN_SIZE + + /* get the parent address */ + ld r11, 112(r1) + addi r3, r11, 16 + + bl .prepare_ftrace_return + nop + + ld r0, 128(r1) + mtlr r0 + addi r1, r1, 112 + blr + +_GLOBAL(return_to_handler) + /* need to save return values */ + std r4, -24(r1) + std r3, -16(r1) + std r31, -8(r1) + mr r31, r1 + stdu r1, -112(r1) + + bl .ftrace_return_to_handler + nop + + /* return value has real return address */ + mtlr r3 + + ld r1, 0(r1) + ld r4, -24(r1) + ld r3, -16(r1) + ld r31, -8(r1) + + /* Jump back to real return address */ + blr + +_GLOBAL(mod_return_to_handler) + /* need to save return values */ + std r4, -32(r1) + std r3, -24(r1) + /* save TOC */ + std r2, -16(r1) + std r31, -8(r1) + mr r31, r1 + stdu r1, -112(r1) + + /* + * We are in a module using the module's TOC. + * Switch to our TOC to run inside the core kernel. + */ + LOAD_REG_IMMEDIATE(r4,ftrace_return_to_handler) + ld r2, 8(r4) + + bl .ftrace_return_to_handler + nop + + /* return value has real return address */ + mtlr r3 + + ld r1, 0(r1) + ld r4, -32(r1) + ld r3, -24(r1) + ld r2, -16(r1) + ld r31, -8(r1) + + /* Jump back to real return address */ + blr +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +#endif /* CONFIG_FUNCTION_TRACER */ diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index 60c60ccf5e3..5b5d16b2cac 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c @@ -5,6 +5,9 @@ * * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box. * + * Added function graph tracer code, taken from x86 that was written + * by Frederic Weisbecker, and ported to PPC by Steven Rostedt. + * */ #include <linux/spinlock.h> @@ -20,14 +23,6 @@ #include <asm/code-patching.h> #include <asm/ftrace.h> -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(fmt , ...) do { } while (0) -#endif - -static unsigned int ftrace_nop = PPC_NOP_INSTR; - #ifdef CONFIG_PPC32 # define GET_ADDR(addr) addr #else @@ -35,37 +30,23 @@ static unsigned int ftrace_nop = PPC_NOP_INSTR; # define GET_ADDR(addr) (*(unsigned long *)addr) #endif - -static unsigned int ftrace_calc_offset(long ip, long addr) +#ifdef CONFIG_DYNAMIC_FTRACE +static unsigned int ftrace_nop_replace(void) { - return (int)(addr - ip); + return PPC_INST_NOP; } -static unsigned char *ftrace_nop_replace(void) +static unsigned int +ftrace_call_replace(unsigned long ip, unsigned long addr, int link) { - return (char *)&ftrace_nop; -} - -static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) -{ - static unsigned int op; + unsigned int op; - /* - * It would be nice to just use create_function_call, but that will - * update the code itself. Here we need to just return the - * instruction that is going to be modified, without modifying the - * code. - */ addr = GET_ADDR(addr); - /* Set to "bl addr" */ - op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffc); + /* if (link) set op to 'bl' else 'b' */ + op = create_branch((unsigned int *)ip, addr, link ? 1 : 0); - /* - * No locking needed, this must be called via kstop_machine - * which in essence is like running on a uniprocessor machine. - */ - return (unsigned char *)&op; + return op; } #ifdef CONFIG_PPC64 @@ -77,10 +58,9 @@ static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) #endif static int -ftrace_modify_code(unsigned long ip, unsigned char *old_code, - unsigned char *new_code) +ftrace_modify_code(unsigned long ip, unsigned int old, unsigned int new) { - unsigned char replaced[MCOUNT_INSN_SIZE]; + unsigned int replaced; /* * Note: Due to modules and __init, code can @@ -93,15 +73,15 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, */ /* read the text we want to modify */ - if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) + if (probe_kernel_read(&replaced, (void *)ip, MCOUNT_INSN_SIZE)) return -EFAULT; /* Make sure it is what we expect it to be */ - if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) + if (replaced != old) return -EINVAL; /* replace the text with the new text */ - if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE)) + if (probe_kernel_write((void *)ip, &new, MCOUNT_INSN_SIZE)) return -EPERM; flush_icache_range(ip, ip + 8); @@ -119,6 +99,8 @@ static int test_24bit_addr(unsigned long ip, unsigned long addr) return create_branch((unsigned int *)ip, addr, 0); } +#ifdef CONFIG_MODULES + static int is_bl_op(unsigned int op) { return (op & 0xfc000003) == 0x48000001; @@ -175,7 +157,7 @@ __ftrace_make_nop(struct module *mod, * 0xe8, 0x4c, 0x00, 0x28, ld r2,40(r12) */ - DEBUGP("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc); + pr_debug("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc); /* Find where the trampoline jumps to */ if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { @@ -183,7 +165,7 @@ __ftrace_make_nop(struct module *mod, return -EFAULT; } - DEBUGP(" %08x %08x", jmp[0], jmp[1]); + pr_debug(" %08x %08x", jmp[0], jmp[1]); /* verify that this is what we expect it to be */ if (((jmp[0] & 0xffff0000) != 0x3d820000) || @@ -199,18 +181,18 @@ __ftrace_make_nop(struct module *mod, offset = ((unsigned)((unsigned short)jmp[0]) << 16) + (int)((short)jmp[1]); - DEBUGP(" %x ", offset); + pr_debug(" %x ", offset); /* get the address this jumps too */ tramp = mod->arch.toc + offset + 32; - DEBUGP("toc: %lx", tramp); + pr_debug("toc: %lx", tramp); if (probe_kernel_read(jmp, (void *)tramp, 8)) { printk(KERN_ERR "Failed to read %lx\n", tramp); return -EFAULT; } - DEBUGP(" %08x %08x\n", jmp[0], jmp[1]); + pr_debug(" %08x %08x\n", jmp[0], jmp[1]); ptr = ((unsigned long)jmp[0] << 32) + jmp[1]; @@ -287,7 +269,7 @@ __ftrace_make_nop(struct module *mod, * 0x4e, 0x80, 0x04, 0x20 bctr */ - DEBUGP("ip:%lx jumps to %lx", ip, tramp); + pr_debug("ip:%lx jumps to %lx", ip, tramp); /* Find where the trampoline jumps to */ if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { @@ -295,7 +277,7 @@ __ftrace_make_nop(struct module *mod, return -EFAULT; } - DEBUGP(" %08x %08x ", jmp[0], jmp[1]); + pr_debug(" %08x %08x ", jmp[0], jmp[1]); /* verify that this is what we expect it to be */ if (((jmp[0] & 0xffff0000) != 0x3d600000) || @@ -311,7 +293,7 @@ __ftrace_make_nop(struct module *mod, if (tramp & 0x8000) tramp -= 0x10000; - DEBUGP(" %x ", tramp); + pr_debug(" %x ", tramp); if (tramp != addr) { printk(KERN_ERR @@ -320,7 +302,7 @@ __ftrace_make_nop(struct module *mod, return -EINVAL; } - op = PPC_NOP_INSTR; + op = PPC_INST_NOP; if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE)) return -EPERM; @@ -330,12 +312,13 @@ __ftrace_make_nop(struct module *mod, return 0; } #endif /* PPC64 */ +#endif /* CONFIG_MODULES */ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr) { - unsigned char *old, *new; unsigned long ip = rec->ip; + unsigned int old, new; /* * If the calling address is more that 24 bits away, @@ -344,11 +327,12 @@ int ftrace_make_nop(struct module *mod, */ if (test_24bit_addr(ip, addr)) { /* within range */ - old = ftrace_call_replace(ip, addr); + old = ftrace_call_replace(ip, addr, 1); new = ftrace_nop_replace(); return ftrace_modify_code(ip, old, new); } +#ifdef CONFIG_MODULES /* * Out of range jumps are called from modules. * We should either already have a pointer to the module @@ -373,9 +357,13 @@ int ftrace_make_nop(struct module *mod, mod = rec->arch.mod; return __ftrace_make_nop(mod, rec, addr); - +#else + /* We should not get here without modules */ + return -EINVAL; +#endif /* CONFIG_MODULES */ } +#ifdef CONFIG_MODULES #ifdef CONFIG_PPC64 static int __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) @@ -392,7 +380,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) * b +8; ld r2,40(r1) */ if (((op[0] != 0x48000008) || (op[1] != 0xe8410028)) && - ((op[0] != PPC_NOP_INSTR) || (op[1] != PPC_NOP_INSTR))) { + ((op[0] != PPC_INST_NOP) || (op[1] != PPC_INST_NOP))) { printk(KERN_ERR "Expected NOPs but have %x %x\n", op[0], op[1]); return -EINVAL; } @@ -414,7 +402,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) /* ld r2,40(r1) */ op[1] = 0xe8410028; - DEBUGP("write to %lx\n", rec->ip); + pr_debug("write to %lx\n", rec->ip); if (probe_kernel_write((void *)ip, op, MCOUNT_INSN_SIZE * 2)) return -EPERM; @@ -435,7 +423,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) return -EFAULT; /* It should be pointing to a nop */ - if (op != PPC_NOP_INSTR) { + if (op != PPC_INST_NOP) { printk(KERN_ERR "Expected NOP but have %x\n", op); return -EINVAL; } @@ -454,7 +442,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) return -EINVAL; } - DEBUGP("write to %lx\n", rec->ip); + pr_debug("write to %lx\n", rec->ip); if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE)) return -EPERM; @@ -464,11 +452,12 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) return 0; } #endif /* CONFIG_PPC64 */ +#endif /* CONFIG_MODULES */ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) { - unsigned char *old, *new; unsigned long ip = rec->ip; + unsigned int old, new; /* * If the calling address is more that 24 bits away, @@ -478,10 +467,11 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) if (test_24bit_addr(ip, addr)) { /* within range */ old = ftrace_nop_replace(); - new = ftrace_call_replace(ip, addr); + new = ftrace_call_replace(ip, addr, 1); return ftrace_modify_code(ip, old, new); } +#ifdef CONFIG_MODULES /* * Out of range jumps are called from modules. * Being that we are converting from nop, it had better @@ -493,16 +483,20 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) } return __ftrace_make_call(rec, addr); +#else + /* We should not get here without modules */ + return -EINVAL; +#endif /* CONFIG_MODULES */ } int ftrace_update_ftrace_func(ftrace_func_t func) { unsigned long ip = (unsigned long)(&ftrace_call); - unsigned char old[MCOUNT_INSN_SIZE], *new; + unsigned int old, new; int ret; - memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); - new = ftrace_call_replace(ip, (unsigned long)func); + old = *(unsigned int *)&ftrace_call; + new = ftrace_call_replace(ip, (unsigned long)func, 1); ret = ftrace_modify_code(ip, old, new); return ret; @@ -517,3 +511,115 @@ int __init ftrace_dyn_arch_init(void *data) return 0; } +#endif /* CONFIG_DYNAMIC_FTRACE */ + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + +#ifdef CONFIG_DYNAMIC_FTRACE +extern void ftrace_graph_call(void); +extern void ftrace_graph_stub(void); + +int ftrace_enable_ftrace_graph_caller(void) +{ + unsigned long ip = (unsigned long)(&ftrace_graph_call); + unsigned long addr = (unsigned long)(&ftrace_graph_caller); + unsigned long stub = (unsigned long)(&ftrace_graph_stub); + unsigned int old, new; + + old = ftrace_call_replace(ip, stub, 0); + new = ftrace_call_replace(ip, addr, 0); + + return ftrace_modify_code(ip, old, new); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + unsigned long ip = (unsigned long)(&ftrace_graph_call); + unsigned long addr = (unsigned long)(&ftrace_graph_caller); + unsigned long stub = (unsigned long)(&ftrace_graph_stub); + unsigned int old, new; + + old = ftrace_call_replace(ip, addr, 0); + new = ftrace_call_replace(ip, stub, 0); + + return ftrace_modify_code(ip, old, new); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + +#ifdef CONFIG_PPC64 +extern void mod_return_to_handler(void); +#endif + +/* + * Hook the return address and push it in the stack of return addrs + * in current thread info. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) +{ + unsigned long old; + unsigned long long calltime; + int faulted; + struct ftrace_graph_ent trace; + unsigned long return_hooker = (unsigned long)&return_to_handler; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + +#if CONFIG_PPC64 + /* non core kernel code needs to save and restore the TOC */ + if (REGION_ID(self_addr) != KERNEL_REGION_ID) + return_hooker = (unsigned long)&mod_return_to_handler; +#endif + + return_hooker = GET_ADDR(return_hooker); + + /* + * Protect against fault, even if it shouldn't + * happen. This tool is too much intrusive to + * ignore such a protection. + */ + asm volatile( + "1: " PPC_LL "%[old], 0(%[parent])\n" + "2: " PPC_STL "%[return_hooker], 0(%[parent])\n" + " li %[faulted], 0\n" + "3:\n" + + ".section .fixup, \"ax\"\n" + "4: li %[faulted], 1\n" + " b 3b\n" + ".previous\n" + + ".section __ex_table,\"a\"\n" + PPC_LONG_ALIGN "\n" + PPC_LONG "1b,4b\n" + PPC_LONG "2b,4b\n" + ".previous" + + : [old] "=r" (old), [faulted] "=r" (faulted) + : [parent] "r" (parent), [return_hooker] "r" (return_hooker) + : "memory" + ); + + if (unlikely(faulted)) { + ftrace_graph_stop(); + WARN_ON(1); + return; + } + + calltime = cpu_clock(raw_smp_processor_id()); + + if (ftrace_push_return_trace(old, calltime, + self_addr, &trace.depth) == -EBUSY) { + *parent = old; + return; + } + + trace.func = self_addr; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + current->curr_ret_stack--; + *parent = old; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index d794a637e42..54e68c11ae1 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -108,18 +108,21 @@ __start: * because OF may have I/O devices mapped into that area * (particularly on CHRP). */ -#ifdef CONFIG_PPC_MULTIPLATFORM cmpwi 0,r5,0 beq 1f +#ifdef CONFIG_PPC_OF_BOOT_TRAMPOLINE /* find out where we are now */ bcl 20,31,$+4 0: mflr r8 /* r8 = runtime addr here */ addis r8,r8,(_stext - 0b)@ha addi r8,r8,(_stext - 0b)@l /* current runtime base addr */ bl prom_init +#endif /* CONFIG_PPC_OF_BOOT_TRAMPOLINE */ + + /* We never return. We also hit that trap if trying to boot + * from OF while CONFIG_PPC_OF_BOOT_TRAMPOLINE isn't selected */ trap -#endif /* * Check for BootX signature when supporting PowerMac and branch to @@ -472,12 +475,11 @@ SystemCall: . = 0x1000 InstructionTLBMiss: /* - * r0: stored ctr + * r0: scratch * r1: linux style pte ( later becomes ppc hardware pte ) * r2: ptr to linux-style pte * r3: scratch */ - mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r3,SPRN_IMISS lis r1,PAGE_OFFSET@h /* check if kernel address */ @@ -496,28 +498,27 @@ InstructionTLBMiss: rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- InstructionAddressInvalid /* return if no mapping */ rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ - lwz r3,0(r2) /* get linux-style pte */ - andc. r1,r1,r3 /* check access & ~permission */ + lwz r0,0(r2) /* get linux-style pte */ + andc. r1,r1,r0 /* check access & ~permission */ bne- InstructionAddressInvalid /* return if access not permitted */ - ori r3,r3,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */ + ori r0,r0,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */ /* * NOTE! We are assuming this is not an SMP system, otherwise * we would need to update the pte atomically with lwarx/stwcx. */ - stw r3,0(r2) /* update PTE (accessed bit) */ + stw r0,0(r2) /* update PTE (accessed bit) */ /* Convert linux-style PTE to low word of PPC-style PTE */ - rlwinm r1,r3,32-10,31,31 /* _PAGE_RW -> PP lsb */ - rlwinm r2,r3,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ + rlwinm r1,r0,32-10,31,31 /* _PAGE_RW -> PP lsb */ + rlwinm r2,r0,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ and r1,r1,r2 /* writable if _RW and _DIRTY */ - rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ - rlwimi r3,r3,32-1,31,31 /* _PAGE_USER -> PP lsb */ + rlwimi r0,r0,32-1,30,30 /* _PAGE_USER -> PP msb */ + rlwimi r0,r0,32-1,31,31 /* _PAGE_USER -> PP lsb */ ori r1,r1,0xe04 /* clear out reserved bits */ - andc r1,r3,r1 /* PP = user? (rw&dirty? 2: 3): 0 */ + andc r1,r0,r1 /* PP = user? (rw&dirty? 2: 3): 0 */ BEGIN_FTR_SECTION rlwinm r1,r1,0,~_PAGE_COHERENT /* clear M (coherence not required) */ END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT) mtspr SPRN_RPA,r1 - mfspr r3,SPRN_IMISS tlbli r3 mfspr r3,SPRN_SRR1 /* Need to restore CR0 */ mtcrf 0x80,r3 @@ -528,7 +529,6 @@ InstructionAddressInvalid: addis r1,r1,0x2000 mtspr SPRN_DSISR,r1 /* (shouldn't be needed) */ - mtctr r0 /* Restore CTR */ andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ or r2,r2,r1 mtspr SPRN_SRR1,r2 @@ -549,12 +549,11 @@ InstructionAddressInvalid: . = 0x1100 DataLoadTLBMiss: /* - * r0: stored ctr + * r0: scratch * r1: linux style pte ( later becomes ppc hardware pte ) * r2: ptr to linux-style pte * r3: scratch */ - mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r3,SPRN_DMISS lis r1,PAGE_OFFSET@h /* check if kernel address */ @@ -573,38 +572,48 @@ DataLoadTLBMiss: rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- DataAddressInvalid /* return if no mapping */ rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ - lwz r3,0(r2) /* get linux-style pte */ - andc. r1,r1,r3 /* check access & ~permission */ + lwz r0,0(r2) /* get linux-style pte */ + andc. r1,r1,r0 /* check access & ~permission */ bne- DataAddressInvalid /* return if access not permitted */ - ori r3,r3,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */ + ori r0,r0,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */ /* * NOTE! We are assuming this is not an SMP system, otherwise * we would need to update the pte atomically with lwarx/stwcx. */ - stw r3,0(r2) /* update PTE (accessed bit) */ + stw r0,0(r2) /* update PTE (accessed bit) */ /* Convert linux-style PTE to low word of PPC-style PTE */ - rlwinm r1,r3,32-10,31,31 /* _PAGE_RW -> PP lsb */ - rlwinm r2,r3,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ + rlwinm r1,r0,32-10,31,31 /* _PAGE_RW -> PP lsb */ + rlwinm r2,r0,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ and r1,r1,r2 /* writable if _RW and _DIRTY */ - rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ - rlwimi r3,r3,32-1,31,31 /* _PAGE_USER -> PP lsb */ + rlwimi r0,r0,32-1,30,30 /* _PAGE_USER -> PP msb */ + rlwimi r0,r0,32-1,31,31 /* _PAGE_USER -> PP lsb */ ori r1,r1,0xe04 /* clear out reserved bits */ - andc r1,r3,r1 /* PP = user? (rw&dirty? 2: 3): 0 */ + andc r1,r0,r1 /* PP = user? (rw&dirty? 2: 3): 0 */ BEGIN_FTR_SECTION rlwinm r1,r1,0,~_PAGE_COHERENT /* clear M (coherence not required) */ END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT) mtspr SPRN_RPA,r1 - mfspr r3,SPRN_DMISS + mfspr r2,SPRN_SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r2 +BEGIN_MMU_FTR_SECTION + li r0,1 + mfspr r1,SPRN_SPRG4 + rlwinm r2,r3,20,27,31 /* Get Address bits 15:19 */ + slw r0,r0,r2 + xor r1,r0,r1 + srw r0,r1,r2 + mtspr SPRN_SPRG4,r1 + mfspr r2,SPRN_SRR1 + rlwimi r2,r0,31-14,14,14 + mtspr SPRN_SRR1,r2 +END_MMU_FTR_SECTION_IFSET(MMU_FTR_NEED_DTLB_SW_LRU) tlbld r3 - mfspr r3,SPRN_SRR1 /* Need to restore CR0 */ - mtcrf 0x80,r3 rfi DataAddressInvalid: mfspr r3,SPRN_SRR1 rlwinm r1,r3,9,6,6 /* Get load/store bit */ addis r1,r1,0x2000 mtspr SPRN_DSISR,r1 - mtctr r0 /* Restore CTR */ andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ mtspr SPRN_SRR1,r2 mfspr r1,SPRN_DMISS /* Get failing address */ @@ -624,12 +633,11 @@ DataAddressInvalid: . = 0x1200 DataStoreTLBMiss: /* - * r0: stored ctr + * r0: scratch * r1: linux style pte ( later becomes ppc hardware pte ) * r2: ptr to linux-style pte * r3: scratch */ - mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r3,SPRN_DMISS lis r1,PAGE_OFFSET@h /* check if kernel address */ @@ -648,27 +656,38 @@ DataStoreTLBMiss: rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- DataAddressInvalid /* return if no mapping */ rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ - lwz r3,0(r2) /* get linux-style pte */ - andc. r1,r1,r3 /* check access & ~permission */ + lwz r0,0(r2) /* get linux-style pte */ + andc. r1,r1,r0 /* check access & ~permission */ bne- DataAddressInvalid /* return if access not permitted */ - ori r3,r3,_PAGE_ACCESSED|_PAGE_DIRTY + ori r0,r0,_PAGE_ACCESSED|_PAGE_DIRTY /* * NOTE! We are assuming this is not an SMP system, otherwise * we would need to update the pte atomically with lwarx/stwcx. */ - stw r3,0(r2) /* update PTE (accessed/dirty bits) */ + stw r0,0(r2) /* update PTE (accessed/dirty bits) */ /* Convert linux-style PTE to low word of PPC-style PTE */ - rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ + rlwimi r0,r0,32-1,30,30 /* _PAGE_USER -> PP msb */ li r1,0xe05 /* clear out reserved bits & PP lsb */ - andc r1,r3,r1 /* PP = user? 2: 0 */ + andc r1,r0,r1 /* PP = user? 2: 0 */ BEGIN_FTR_SECTION rlwinm r1,r1,0,~_PAGE_COHERENT /* clear M (coherence not required) */ END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT) mtspr SPRN_RPA,r1 - mfspr r3,SPRN_DMISS + mfspr r2,SPRN_SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r2 +BEGIN_MMU_FTR_SECTION + li r0,1 + mfspr r1,SPRN_SPRG4 + rlwinm r2,r3,20,27,31 /* Get Address bits 15:19 */ + slw r0,r0,r2 + xor r1,r0,r1 + srw r0,r1,r2 + mtspr SPRN_SPRG4,r1 + mfspr r2,SPRN_SRR1 + rlwimi r2,r0,31-14,14,14 + mtspr SPRN_SRR1,r2 +END_MMU_FTR_SECTION_IFSET(MMU_FTR_NEED_DTLB_SW_LRU) tlbld r3 - mfspr r3,SPRN_SRR1 /* Need to restore CR0 */ - mtcrf 0x80,r3 rfi #ifndef CONFIG_ALTIVEC diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index ebaedafc8e6..50ef505b8fb 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -1360,6 +1360,7 @@ _GLOBAL(__start_initialization_multiplatform) b .__after_prom_start _INIT_STATIC(__boot_from_prom) +#ifdef CONFIG_PPC_OF_BOOT_TRAMPOLINE /* Save parameters */ mr r31,r3 mr r30,r4 @@ -1390,7 +1391,10 @@ _INIT_STATIC(__boot_from_prom) /* Do all of the interaction with OF client interface */ mr r8,r26 bl .prom_init - /* We never return */ +#endif /* #CONFIG_PPC_OF_BOOT_TRAMPOLINE */ + + /* We never return. We also hit that trap if trying to boot + * from OF while CONFIG_PPC_OF_BOOT_TRAMPOLINE isn't selected */ trap _STATIC(__after_prom_start) diff --git a/arch/powerpc/kernel/head_booke.h b/arch/powerpc/kernel/head_booke.h index fce2df98850..95f39f1e68d 100644 --- a/arch/powerpc/kernel/head_booke.h +++ b/arch/powerpc/kernel/head_booke.h @@ -10,6 +10,15 @@ mtspr SPRN_IVOR##vector_number,r26; \ sync +#if (THREAD_SHIFT < 15) +#define ALLOC_STACK_FRAME(reg, val) \ + addi reg,reg,val +#else +#define ALLOC_STACK_FRAME(reg, val) \ + addis reg,reg,val@ha; \ + addi reg,reg,val@l +#endif + #define NORMAL_EXCEPTION_PROLOG \ mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ mtspr SPRN_SPRG1,r11; \ @@ -20,7 +29,7 @@ beq 1f; \ mfspr r1,SPRN_SPRG3; /* if from user, start at top of */\ lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ - addi r1,r1,THREAD_SIZE; \ + ALLOC_STACK_FRAME(r1, THREAD_SIZE); \ 1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ mr r11,r1; \ stw r10,_CCR(r11); /* save various registers */\ @@ -70,10 +79,10 @@ /* only on e500mc/e200 */ #define DEBUG_STACK_BASE dbgirq_ctx -#ifdef CONFIG_PPC_E500MC -#define DEBUG_SPRG SPRN_SPRG9 -#else +#ifdef CONFIG_E200 #define DEBUG_SPRG SPRN_SPRG6W +#else +#define DEBUG_SPRG SPRN_SPRG9 #endif #define EXC_LVL_FRAME_OVERHEAD (THREAD_SIZE - INT_FRAME_SIZE - EXC_LVL_SIZE) @@ -279,7 +288,7 @@ label: lwz r11,GPR11(r8); \ mfspr r8,DEBUG_SPRG; \ \ - RFDI; \ + PPC_RFDI; \ b .; \ \ /* continue normal handling for a debug exception... */ \ diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 36ffb3504a4..4c22620d009 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -103,10 +103,15 @@ invstr: mflr r6 /* Make it accessible */ or r7,r7,r4 mtspr SPRN_MAS6,r7 tlbsx 0,r6 /* search MSR[IS], SPID=PID0 */ -#ifndef CONFIG_E200 mfspr r7,SPRN_MAS1 andis. r7,r7,MAS1_VALID@h bne match_TLB + + mfspr r7,SPRN_MMUCFG + rlwinm r7,r7,21,28,31 /* extract MMUCFG[NPIDS] */ + cmpwi r7,3 + bne match_TLB /* skip if NPIDS != 3 */ + mfspr r7,SPRN_PID1 slwi r7,r7,16 or r7,r7,r4 @@ -120,7 +125,7 @@ invstr: mflr r6 /* Make it accessible */ or r7,r7,r4 mtspr SPRN_MAS6,r7 tlbsx 0,r6 /* Fall through, we had to match */ -#endif + match_TLB: mfspr r7,SPRN_MAS0 rlwinm r3,r7,16,20,31 /* Extract MAS0(Entry) */ @@ -168,7 +173,7 @@ skpinv: addi r6,r6,1 /* Increment */ /* grab and fixup the RPN */ mfspr r6,SPRN_MAS1 /* extract MAS1[SIZE] */ - rlwinm r6,r6,25,27,30 + rlwinm r6,r6,25,27,31 li r8,-1 addi r6,r6,10 slw r6,r8,r6 /* convert to mask */ @@ -194,7 +199,7 @@ skpinv: addi r6,r6,1 /* Increment */ xori r6,r4,1 /* Setup TMP mapping in the other Address space */ slwi r6,r6,12 oris r6,r6,(MAS1_VALID|MAS1_IPROT)@h - ori r6,r6,(MAS1_TSIZE(BOOKE_PAGESZ_4K))@l + ori r6,r6,(MAS1_TSIZE(BOOK3E_PAGESZ_4K))@l mtspr SPRN_MAS1,r6 mfspr r6,SPRN_MAS2 li r7,0 /* temp EPN = 0 */ @@ -215,14 +220,19 @@ skpinv: addi r6,r6,1 /* Increment */ /* 4. Clear out PIDs & Search info */ li r6,0 + mtspr SPRN_MAS6,r6 mtspr SPRN_PID0,r6 -#ifndef CONFIG_E200 + + mfspr r7,SPRN_MMUCFG + rlwinm r7,r7,21,28,31 /* extract MMUCFG[NPIDS] */ + cmpwi r7,3 + bne 2f /* skip if NPIDS != 3 */ + mtspr SPRN_PID1,r6 mtspr SPRN_PID2,r6 -#endif - mtspr SPRN_MAS6,r6 /* 5. Invalidate mapping we started in */ +2: lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */ rlwimi r7,r3,16,4,15 /* Setup MAS0 = TLBSEL | ESEL(r3) */ mtspr SPRN_MAS0,r7 @@ -247,10 +257,10 @@ skpinv: addi r6,r6,1 /* Increment */ lis r6,0x1000 /* Set MAS0(TLBSEL) = TLB1(1), ESEL = 0 */ mtspr SPRN_MAS0,r6 lis r6,(MAS1_VALID|MAS1_IPROT)@h - ori r6,r6,(MAS1_TSIZE(BOOKE_PAGESZ_64M))@l + ori r6,r6,(MAS1_TSIZE(BOOK3E_PAGESZ_64M))@l mtspr SPRN_MAS1,r6 - lis r6,MAS2_VAL(PAGE_OFFSET, BOOKE_PAGESZ_64M, M_IF_SMP)@h - ori r6,r6,MAS2_VAL(PAGE_OFFSET, BOOKE_PAGESZ_64M, M_IF_SMP)@l + lis r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_SMP)@h + ori r6,r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_SMP)@l mtspr SPRN_MAS2,r6 mtspr SPRN_MAS3,r8 tlbwe @@ -298,26 +308,14 @@ skpinv: addi r6,r6,1 /* Increment */ SET_IVOR(12, WatchdogTimer); SET_IVOR(13, DataTLBError); SET_IVOR(14, InstructionTLBError); - SET_IVOR(15, DebugDebug); -#if defined(CONFIG_E500) && !defined(CONFIG_PPC_E500MC) SET_IVOR(15, DebugCrit); -#endif - SET_IVOR(32, SPEUnavailable); - SET_IVOR(33, SPEFloatingPointData); - SET_IVOR(34, SPEFloatingPointRound); -#ifndef CONFIG_E200 - SET_IVOR(35, PerformanceMonitor); -#endif -#ifdef CONFIG_PPC_E500MC - SET_IVOR(36, Doorbell); -#endif /* Establish the interrupt vector base */ lis r4,interrupt_base@h /* IVPR only uses the high 16-bits */ mtspr SPRN_IVPR,r4 /* Setup the defaults for TLB entries */ - li r2,(MAS4_TSIZED(BOOKE_PAGESZ_4K))@l + li r2,(MAS4_TSIZED(BOOK3E_PAGESZ_4K))@l #ifdef CONFIG_E200 oris r2,r2,MAS4_TLBSELD(1)@h #endif @@ -329,12 +327,6 @@ skpinv: addi r6,r6,1 /* Increment */ oris r2,r2,HID0_DOZE@h mtspr SPRN_HID0, r2 #endif -#ifdef CONFIG_E200 - /* enable dedicated debug exception handling resources (Debug APU) */ - mfspr r2,SPRN_HID0 - ori r2,r2,HID0_DAPUEN@l - mtspr SPRN_HID0,r2 -#endif #if !defined(CONFIG_BDI_SWITCH) /* @@ -706,15 +698,13 @@ interrupt_base: /* Performance Monitor */ EXCEPTION(0x2060, PerformanceMonitor, performance_monitor_exception, EXC_XFER_STD) -#ifdef CONFIG_PPC_E500MC - EXCEPTION(0x2070, Doorbell, unknown_exception, EXC_XFER_STD) -#endif + EXCEPTION(0x2070, Doorbell, doorbell_exception, EXC_XFER_STD) + + CRITICAL_EXCEPTION(0x2080, CriticalDoorbell, unknown_exception) /* Debug Interrupt */ DEBUG_DEBUG_EXCEPTION -#if defined(CONFIG_E500) && !defined(CONFIG_PPC_E500MC) DEBUG_CRIT_EXCEPTION -#endif /* * Local functions @@ -897,6 +887,47 @@ KernelSPE: * Global functions */ +/* Adjust or setup IVORs for e200 */ +_GLOBAL(__setup_e200_ivors) + li r3,DebugDebug@l + mtspr SPRN_IVOR15,r3 + li r3,SPEUnavailable@l + mtspr SPRN_IVOR32,r3 + li r3,SPEFloatingPointData@l + mtspr SPRN_IVOR33,r3 + li r3,SPEFloatingPointRound@l + mtspr SPRN_IVOR34,r3 + sync + blr + +/* Adjust or setup IVORs for e500v1/v2 */ +_GLOBAL(__setup_e500_ivors) + li r3,DebugCrit@l + mtspr SPRN_IVOR15,r3 + li r3,SPEUnavailable@l + mtspr SPRN_IVOR32,r3 + li r3,SPEFloatingPointData@l + mtspr SPRN_IVOR33,r3 + li r3,SPEFloatingPointRound@l + mtspr SPRN_IVOR34,r3 + li r3,PerformanceMonitor@l + mtspr SPRN_IVOR35,r3 + sync + blr + +/* Adjust or setup IVORs for e500mc */ +_GLOBAL(__setup_e500mc_ivors) + li r3,DebugDebug@l + mtspr SPRN_IVOR15,r3 + li r3,PerformanceMonitor@l + mtspr SPRN_IVOR35,r3 + li r3,Doorbell@l + mtspr SPRN_IVOR36,r3 + li r3,CriticalDoorbell@l + mtspr SPRN_IVOR37,r3 + sync + blr + /* * extern void loadcam_entry(unsigned int index) * @@ -1089,7 +1120,7 @@ __secondary_start: mtspr SPRN_SPRG3,r4 /* Setup the defaults for TLB entries */ - li r4,(MAS4_TSIZED(BOOKE_PAGESZ_4K))@l + li r4,(MAS4_TSIZED(BOOK3E_PAGESZ_4K))@l mtspr SPRN_MAS4,r4 /* Jump to start_secondary */ diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 23b8b5e36f9..5576147e57b 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -171,7 +171,7 @@ int show_interrupts(struct seq_file *p, void *v) { int i = *(loff_t *)v, j; struct irqaction *action; - irq_desc_t *desc; + struct irq_desc *desc; unsigned long flags; if (i == 0) { @@ -190,7 +190,7 @@ int show_interrupts(struct seq_file *p, void *v) seq_printf(p, "%3d: ", i); #ifdef CONFIG_SMP for_each_online_cpu(j) - seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); + seq_printf(p, "%10u ", kstat_irqs_cpu(i, j)); #else seq_printf(p, "%10u ", kstat_irqs(i)); #endif /* CONFIG_SMP */ @@ -231,7 +231,7 @@ void fixup_irqs(cpumask_t map) if (irq_desc[irq].status & IRQ_PER_CPU) continue; - cpus_and(mask, irq_desc[irq].affinity, map); + cpumask_and(&mask, irq_desc[irq].affinity, &map); if (any_online_cpu(mask) == NR_CPUS) { printk("Breaking affinity for irq %i\n", irq); mask = map; @@ -1038,7 +1038,7 @@ arch_initcall(irq_late_init); static int virq_debug_show(struct seq_file *m, void *private) { unsigned long flags; - irq_desc_t *desc; + struct irq_desc *desc; const char *p; char none[] = "none"; int i; diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c index 8992b031a7b..8fbb12508bf 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -329,7 +329,7 @@ static unsigned long stub_for_addr(Elf64_Shdr *sechdrs, restore r2. */ static int restore_r2(u32 *instruction, struct module *me) { - if (*instruction != PPC_NOP_INSTR) { + if (*instruction != PPC_INST_NOP) { printk("%s: Expect noop after relocate, got %08x\n", me->name, *instruction); return 0; diff --git a/arch/powerpc/kernel/msi.c b/arch/powerpc/kernel/msi.c index 3bb7d3dd28b..8bbc12d20f5 100644 --- a/arch/powerpc/kernel/msi.c +++ b/arch/powerpc/kernel/msi.c @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include <linux/msi.h> +#include <linux/pci.h> #include <asm/machdep.h> @@ -19,6 +20,10 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type) return -ENOSYS; } + /* PowerPC doesn't support multiple MSI yet */ + if (type == PCI_CAP_ID_MSI && nvec > 1) + return 1; + if (ppc_md.msi_check_device) { pr_debug("msi: Using platform check routine.\n"); return ppc_md.msi_check_device(dev, nvec, type); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 0f418127231..9c69e7e145c 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -38,6 +38,7 @@ #include <asm/eeh.h> static DEFINE_SPINLOCK(hose_spinlock); +LIST_HEAD(hose_list); /* XXX kill that some day ... */ static int global_phb_number; /* Global phb counter */ @@ -49,7 +50,7 @@ resource_size_t isa_mem_base; unsigned int ppc_pci_flags = 0; -static struct dma_mapping_ops *pci_dma_ops; +static struct dma_mapping_ops *pci_dma_ops = &dma_direct_ops; void set_pci_dma_ops(struct dma_mapping_ops *dma_ops) { @@ -113,19 +114,24 @@ void pcibios_free_controller(struct pci_controller *phb) kfree(phb); } +static resource_size_t pcibios_io_size(const struct pci_controller *hose) +{ +#ifdef CONFIG_PPC64 + return hose->pci_io_size; +#else + return hose->io_resource.end - hose->io_resource.start + 1; +#endif +} + int pcibios_vaddr_is_ioport(void __iomem *address) { int ret = 0; struct pci_controller *hose; - unsigned long size; + resource_size_t size; spin_lock(&hose_spinlock); list_for_each_entry(hose, &hose_list, list_node) { -#ifdef CONFIG_PPC64 - size = hose->pci_io_size; -#else - size = hose->io_resource.end - hose->io_resource.start + 1; -#endif + size = pcibios_io_size(hose); if (address >= hose->io_base_virt && address < (hose->io_base_virt + size)) { ret = 1; @@ -136,6 +142,29 @@ int pcibios_vaddr_is_ioport(void __iomem *address) return ret; } +unsigned long pci_address_to_pio(phys_addr_t address) +{ + struct pci_controller *hose; + resource_size_t size; + unsigned long ret = ~0; + + spin_lock(&hose_spinlock); + list_for_each_entry(hose, &hose_list, list_node) { + size = pcibios_io_size(hose); + if (address >= hose->io_base_phys && + address < (hose->io_base_phys + size)) { + unsigned long base = + (unsigned long)hose->io_base_virt - _IO_BASE; + ret = base + (address - hose->io_base_phys); + break; + } + } + spin_unlock(&hose_spinlock); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_address_to_pio); + /* * Return the domain number for this bus. */ @@ -1453,7 +1482,7 @@ void __init pcibios_resource_survey(void) * we proceed to assigning things that were left unassigned */ if (!(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) { - pr_debug("PCI: Assigning unassigned resouces...\n"); + pr_debug("PCI: Assigning unassigned resources...\n"); pci_assign_unassigned_resources(); } diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index 132cd80afa2..d473634e39e 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c @@ -20,6 +20,7 @@ #include <asm/prom.h> #include <asm/sections.h> #include <asm/pci-bridge.h> +#include <asm/ppc-pci.h> #include <asm/byteorder.h> #include <asm/uaccess.h> #include <asm/machdep.h> @@ -43,8 +44,6 @@ static u8* pci_to_OF_bus_map; */ static int pci_assign_all_buses; -LIST_HEAD(hose_list); - static int pci_bus_count; /* This will remain NULL for now, until isa-bridge.c is made common @@ -219,16 +218,23 @@ scan_OF_pci_childs(struct device_node *parent, pci_OF_scan_iterator filter, void static struct device_node *scan_OF_for_pci_dev(struct device_node *parent, unsigned int devfn) { - struct device_node *np; + struct device_node *np, *cnp; const u32 *reg; unsigned int psize; for_each_child_of_node(parent, np) { reg = of_get_property(np, "reg", &psize); - if (reg == NULL || psize < 4) - continue; - if (((reg[0] >> 8) & 0xff) == devfn) + if (reg && psize >= 4 && ((reg[0] >> 8) & 0xff) == devfn) return np; + + /* Note: some OFs create a parent node "multifunc-device" as + * a fake root for all functions of a multi-function device, + * we go down them as well. */ + if (!strcmp(np->name, "multifunc-device")) { + cnp = scan_OF_for_pci_dev(np, devfn); + if (cnp) + return cnp; + } } return NULL; } @@ -491,24 +497,6 @@ long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) return result; } -unsigned long pci_address_to_pio(phys_addr_t address) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { - unsigned int size = hose->io_resource.end - - hose->io_resource.start + 1; - if (address >= hose->io_base_phys && - address < (hose->io_base_phys + size)) { - unsigned long base = - (unsigned long)hose->io_base_virt - _IO_BASE; - return base + (address - hose->io_base_phys); - } - } - return (unsigned int)-1; -} -EXPORT_SYMBOL(pci_address_to_pio); - /* * Null PCI config access functions, for the case when we can't * find a hose. diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index ea8eda8c87c..be574fc0d92 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -43,8 +43,6 @@ unsigned long pci_probe_only = 1; unsigned long pci_io_base = ISA_IO_BASE; EXPORT_SYMBOL(pci_io_base); -LIST_HEAD(hose_list); - static void fixup_broken_pcnet32(struct pci_dev* dev) { if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) { @@ -524,23 +522,6 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus) } EXPORT_SYMBOL_GPL(pcibios_map_io_space); -unsigned long pci_address_to_pio(phys_addr_t address) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { - if (address >= hose->io_base_phys && - address < (hose->io_base_phys + hose->pci_io_size)) { - unsigned long base = - (unsigned long)hose->io_base_virt - _IO_BASE; - return base + (address - hose->io_base_phys); - } - } - return (unsigned int)-1; -} -EXPORT_SYMBOL_GPL(pci_address_to_pio); - - #define IOBASE_BRIDGE_NUMBER 0 #define IOBASE_MEMORY 1 #define IOBASE_IO 2 diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index fb7049c054c..7b44a33f03c 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -33,7 +33,10 @@ #include <linux/mqueue.h> #include <linux/hardirq.h> #include <linux/utsname.h> +#include <linux/ftrace.h> #include <linux/kernel_stat.h> +#include <linux/personality.h> +#include <linux/random.h> #include <asm/pgtable.h> #include <asm/uaccess.h> @@ -595,7 +598,7 @@ void prepare_to_copy(struct task_struct *tsk) /* * Copy a thread.. */ -int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, +int copy_thread(unsigned long clone_flags, unsigned long usp, unsigned long unused, struct task_struct *p, struct pt_regs *regs) { @@ -1008,6 +1011,14 @@ void show_stack(struct task_struct *tsk, unsigned long *stack) unsigned long sp, ip, lr, newsp; int count = 0; int firstframe = 1; +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + int curr_frame = current->curr_ret_stack; + extern void return_to_handler(void); + unsigned long addr = (unsigned long)return_to_handler; +#ifdef CONFIG_PPC64 + addr = *(unsigned long*)addr; +#endif +#endif sp = (unsigned long) stack; if (tsk == NULL) @@ -1030,6 +1041,13 @@ void show_stack(struct task_struct *tsk, unsigned long *stack) ip = stack[STACK_FRAME_LR_SAVE]; if (!firstframe || ip != lr) { printk("["REG"] ["REG"] %pS", sp, ip, (void *)ip); +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + if (ip == addr && curr_frame >= 0) { + printk(" (%pS)", + (void *)current->ret_stack[curr_frame].ret); + curr_frame--; + } +#endif if (firstframe) printk(" (unreliable)"); printk("\n"); @@ -1122,3 +1140,43 @@ void thread_info_cache_init(void) } #endif /* THREAD_SHIFT < PAGE_SHIFT */ + +unsigned long arch_align_stack(unsigned long sp) +{ + if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) + sp -= get_random_int() & ~PAGE_MASK; + return sp & ~0xf; +} + +static inline unsigned long brk_rnd(void) +{ + unsigned long rnd = 0; + + /* 8MB for 32bit, 1GB for 64bit */ + if (is_32bit_task()) + rnd = (long)(get_random_int() % (1<<(23-PAGE_SHIFT))); + else + rnd = (long)(get_random_int() % (1<<(30-PAGE_SHIFT))); + + return rnd << PAGE_SHIFT; +} + +unsigned long arch_randomize_brk(struct mm_struct *mm) +{ + unsigned long ret = PAGE_ALIGN(mm->brk + brk_rnd()); + + if (ret < mm->brk) + return mm->brk; + + return ret; +} + +unsigned long randomize_et_dyn(unsigned long base) +{ + unsigned long ret = PAGE_ALIGN(base + brk_rnd()); + + if (ret < base) + return base; + + return ret; +} diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index f00f83109ab..5ec6a9e2393 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -1075,11 +1075,6 @@ static void __init early_reserve_mem(void) DBG("reserving: %llx -> %llx\n", base, size); lmb_reserve(base, size); } - -#if 0 - DBG("memory reserved, lmbs :\n"); - lmb_dump_all(); -#endif } #ifdef CONFIG_PHYP_DUMP @@ -1221,6 +1216,7 @@ void __init early_init_devtree(void *params) lmb_enforce_memory_limit(limit); lmb_analyze(); + lmb_dump_all(); DBG("Phys. mem: %lx\n", lmb_phys_mem_size()); diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 7f1b33d5e30..2e026c0407d 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -2283,6 +2283,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, */ prom_init_stdout(); + prom_printf("Preparing to boot %s", RELOC(linux_banner)); + /* * Get default machine type. At this point, we do not differentiate * between pSeries SMP and pSeries LPAR diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index ea3a2ec03ff..1ac136b128f 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -20,7 +20,7 @@ WHITELIST="add_reloc_offset __bss_start __bss_stop copy_and_flush _end enter_prom memcpy memset reloc_offset __secondary_hold __secondary_hold_acknowledge __secondary_hold_spinloop __start strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224 -reloc_got2 kernstart_addr memstart_addr" +reloc_got2 kernstart_addr memstart_addr linux_banner" NM="$1" OBJ="$2" diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index fdfe14c4bde..ee4c7609b64 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -46,6 +46,7 @@ EXPORT_SYMBOL(rtas); struct rtas_suspend_me_data { atomic_t working; /* number of cpus accessing this struct */ + atomic_t done; int token; /* ibm,suspend-me */ int error; struct completion *complete; /* wait on this until working == 0 */ @@ -689,7 +690,7 @@ static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE; #ifdef CONFIG_PPC_PSERIES static void rtas_percpu_suspend_me(void *info) { - long rc; + long rc = H_SUCCESS; unsigned long msr_save; int cpu; struct rtas_suspend_me_data *data = @@ -701,7 +702,8 @@ static void rtas_percpu_suspend_me(void *info) msr_save = mfmsr(); mtmsr(msr_save & ~(MSR_EE)); - rc = plpar_hcall_norets(H_JOIN); + while (rc == H_SUCCESS && !atomic_read(&data->done)) + rc = plpar_hcall_norets(H_JOIN); mtmsr(msr_save); @@ -724,6 +726,9 @@ static void rtas_percpu_suspend_me(void *info) smp_processor_id(), rc); data->error = rc; } + + atomic_set(&data->done, 1); + /* This cpu did the suspend or got an error; in either case, * we need to prod all other other cpus out of join state. * Extra prods are harmless. @@ -766,6 +771,7 @@ static int rtas_ibm_suspend_me(struct rtas_args *args) } atomic_set(&data.working, 0); + atomic_set(&data.done, 0); data.token = rtas_token("ibm,suspend-me"); data.error = 0; data.complete = &done; diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 149cb112cd1..13011a96a97 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c @@ -669,7 +669,6 @@ static void remove_flash_pde(struct proc_dir_entry *dp) { if (dp) { kfree(dp->data); - dp->owner = NULL; remove_proc_entry(dp->name, dp->parent); } } diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 705fc4bf380..9774f9fed96 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -35,6 +35,8 @@ #include <linux/debugfs.h> #include <linux/percpu.h> #include <linux/lmb.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/processor.h> @@ -669,3 +671,37 @@ static int powerpc_debugfs_init(void) } arch_initcall(powerpc_debugfs_init); #endif + +static int ppc_dflt_bus_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + + /* We are only intereted in device addition */ + if (action != BUS_NOTIFY_ADD_DEVICE) + return 0; + + set_dma_ops(dev, &dma_direct_ops); + + return NOTIFY_DONE; +} + +static struct notifier_block ppc_dflt_plat_bus_notifier = { + .notifier_call = ppc_dflt_bus_notify, + .priority = INT_MAX, +}; + +static struct notifier_block ppc_dflt_of_bus_notifier = { + .notifier_call = ppc_dflt_bus_notify, + .priority = INT_MAX, +}; + +static int __init setup_bus_notifier(void) +{ + bus_register_notifier(&platform_bus_type, &ppc_dflt_plat_bus_notifier); + bus_register_notifier(&of_platform_bus_type, &ppc_dflt_of_bus_notifier); + + return 0; +} + +arch_initcall(setup_bus_notifier); diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 2d34196bba8..c410c606955 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -202,8 +202,6 @@ void __init early_setup(unsigned long dt_ptr) /* Fix up paca fields required for the boot cpu */ get_paca()->cpu_start = 1; - get_paca()->stab_real = __pa((u64)&initial_stab); - get_paca()->stab_addr = (u64)&initial_stab; /* Probe the machine type */ probe_machine(); @@ -212,20 +210,8 @@ void __init early_setup(unsigned long dt_ptr) DBG("Found, Initializing memory management...\n"); - /* - * Initialize the MMU Hash table and create the linear mapping - * of memory. Has to be done before stab/slb initialization as - * this is currently where the page size encoding is obtained - */ - htab_initialize(); - - /* - * Initialize stab / SLB management except on iSeries - */ - if (cpu_has_feature(CPU_FTR_SLB)) - slb_initialize(); - else if (!firmware_has_feature(FW_FEATURE_ISERIES)) - stab_initialize(get_paca()->stab_real); + /* Initialize the hash table or TLB handling */ + early_init_mmu(); DBG(" <- early_setup()\n"); } @@ -233,22 +219,11 @@ void __init early_setup(unsigned long dt_ptr) #ifdef CONFIG_SMP void early_setup_secondary(void) { - struct paca_struct *lpaca = get_paca(); - /* Mark interrupts enabled in PACA */ - lpaca->soft_enabled = 0; + get_paca()->soft_enabled = 0; - /* Initialize hash table for that CPU */ - htab_initialize_secondary(); - - /* Initialize STAB/SLB. We use a virtual address as it works - * in real mode on pSeries and we want a virutal address on - * iSeries anyway - */ - if (cpu_has_feature(CPU_FTR_SLB)) - slb_initialize(); - else - stab_initialize(lpaca->stab_addr); + /* Initialize the hash table or TLB handling */ + early_init_mmu_secondary(); } #endif /* CONFIG_SMP */ @@ -578,13 +553,6 @@ void ppc64_boot_msg(unsigned int src, const char *msg) printk("[boot]%04x %s\n", src, msg); } -/* Print a termination message (print only -- does not stop the kernel) */ -void ppc64_terminate_msg(unsigned int src, const char *msg) -{ - ppc64_do_msg(PPC64_LINUX_FUNCTION|PPC64_TERM_MESSAGE|src, msg); - printk("[terminate]%04x %s\n", src, msg); -} - void cpu_die(void) { if (ppc_md.cpu_die) diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index a54405ebd7b..00b5078da9a 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -26,12 +26,12 @@ int show_unhandled_signals = 0; * Allocate space for the signal frame */ void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, - size_t frame_size) + size_t frame_size, int is_32) { unsigned long oldsp, newsp; /* Default to using normal stack */ - oldsp = regs->gpr[1]; + oldsp = get_clean_sp(regs, is_32); /* Check for alt stack */ if ((ka->sa.sa_flags & SA_ONSTACK) && diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index b427bf8e1d8..6c0ddfc0603 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h @@ -15,7 +15,7 @@ extern void do_signal(struct pt_regs *regs, unsigned long thread_info_flags); extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, - size_t frame_size); + size_t frame_size, int is_32); extern void restore_sigmask(sigset_t *set); extern int handle_signal32(unsigned long sig, struct k_sigaction *ka, @@ -39,22 +39,12 @@ extern unsigned long copy_vsx_from_user(struct task_struct *task, #ifdef CONFIG_PPC64 -static inline int is_32bit_task(void) -{ - return test_thread_flag(TIF_32BIT); -} - extern int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs); #else /* CONFIG_PPC64 */ -static inline int is_32bit_task(void) -{ - return 1; -} - static inline int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index b13abf30599..d670429a160 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -836,7 +836,7 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, /* Set up Signal Frame */ /* Put a Real Time Context onto stack */ - rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf)); + rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf), 1); addr = rt_sf; if (unlikely(rt_sf == NULL)) goto badframe; @@ -1182,7 +1182,7 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, unsigned long newsp = 0; /* Set up Signal Frame */ - frame = get_sigframe(ka, regs, sizeof(*frame)); + frame = get_sigframe(ka, regs, sizeof(*frame), 1); if (unlikely(frame == NULL)) goto badframe; sc = (struct sigcontext __user *) &frame->sctx; diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index e132891d3ce..2fe6fc64b61 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -402,7 +402,7 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, unsigned long newsp = 0; long err = 0; - frame = get_sigframe(ka, regs, sizeof(*frame)); + frame = get_sigframe(ka, regs, sizeof(*frame), 0); if (unlikely(frame == NULL)) goto badframe; diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 4a2ee08af6a..f41aec85aa4 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -134,44 +134,23 @@ void ppc_enable_pmcs(void) } EXPORT_SYMBOL(ppc_enable_pmcs); -#if defined(CONFIG_6xx) || defined(CONFIG_PPC64) -/* XXX convert to rusty's on_one_cpu */ -static unsigned long run_on_cpu(unsigned long cpu, - unsigned long (*func)(unsigned long), - unsigned long arg) -{ - cpumask_t old_affinity = current->cpus_allowed; - unsigned long ret; - - /* should return -EINVAL to userspace */ - if (set_cpus_allowed(current, cpumask_of_cpu(cpu))) - return 0; - - ret = func(arg); - - set_cpus_allowed(current, old_affinity); - - return ret; -} -#endif - #define SYSFS_PMCSETUP(NAME, ADDRESS) \ -static unsigned long read_##NAME(unsigned long junk) \ +static void read_##NAME(void *val) \ { \ - return mfspr(ADDRESS); \ + *(unsigned long *)val = mfspr(ADDRESS); \ } \ -static unsigned long write_##NAME(unsigned long val) \ +static void write_##NAME(void *val) \ { \ ppc_enable_pmcs(); \ - mtspr(ADDRESS, val); \ - return 0; \ + mtspr(ADDRESS, *(unsigned long *)val); \ } \ static ssize_t show_##NAME(struct sys_device *dev, \ struct sysdev_attribute *attr, \ char *buf) \ { \ struct cpu *cpu = container_of(dev, struct cpu, sysdev); \ - unsigned long val = run_on_cpu(cpu->sysdev.id, read_##NAME, 0); \ + unsigned long val; \ + smp_call_function_single(cpu->sysdev.id, read_##NAME, &val, 1); \ return sprintf(buf, "%lx\n", val); \ } \ static ssize_t __used \ @@ -183,7 +162,7 @@ static ssize_t __used \ int ret = sscanf(buf, "%lx", &val); \ if (ret != 1) \ return -EINVAL; \ - run_on_cpu(cpu->sysdev.id, write_##NAME, val); \ + smp_call_function_single(cpu->sysdev.id, write_##NAME, &val, 1); \ return count; \ } diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index c9564031a2a..926ea864e34 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -1127,3 +1127,19 @@ void div128_by_32(u64 dividend_high, u64 dividend_low, dr->result_low = ((u64)y << 32) + z; } + +static int __init rtc_init(void) +{ + struct platform_device *pdev; + + if (!ppc_md.get_rtc_time) + return -ENODEV; + + pdev = platform_device_register_simple("rtc-generic", -1, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + return 0; +} + +module_init(rtc_init); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 5457e957568..678fbff0d20 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -52,6 +52,10 @@ #include <asm/processor.h> #endif #include <asm/kexec.h> +#include <asm/ppc-opcode.h> +#ifdef CONFIG_FSL_BOOKE +#include <asm/dbell.h> +#endif #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) int (*__debugger)(struct pt_regs *regs); @@ -637,29 +641,6 @@ static void parse_fpe(struct pt_regs *regs) * bits is faster and easier. * */ -#define INST_MFSPR_PVR 0x7c1f42a6 -#define INST_MFSPR_PVR_MASK 0xfc1fffff - -#define INST_DCBA 0x7c0005ec -#define INST_DCBA_MASK 0xfc0007fe - -#define INST_MCRXR 0x7c000400 -#define INST_MCRXR_MASK 0xfc0007fe - -#define INST_STRING 0x7c00042a -#define INST_STRING_MASK 0xfc0007fe -#define INST_STRING_GEN_MASK 0xfc00067e -#define INST_LSWI 0x7c0004aa -#define INST_LSWX 0x7c00042a -#define INST_STSWI 0x7c0005aa -#define INST_STSWX 0x7c00052a - -#define INST_POPCNTB 0x7c0000f4 -#define INST_POPCNTB_MASK 0xfc0007fe - -#define INST_ISEL 0x7c00001e -#define INST_ISEL_MASK 0xfc00003e - static int emulate_string_inst(struct pt_regs *regs, u32 instword) { u8 rT = (instword >> 21) & 0x1f; @@ -670,20 +651,20 @@ static int emulate_string_inst(struct pt_regs *regs, u32 instword) int pos = 0; /* Early out if we are an invalid form of lswx */ - if ((instword & INST_STRING_MASK) == INST_LSWX) + if ((instword & PPC_INST_STRING_MASK) == PPC_INST_LSWX) if ((rT == rA) || (rT == NB_RB)) return -EINVAL; EA = (rA == 0) ? 0 : regs->gpr[rA]; - switch (instword & INST_STRING_MASK) { - case INST_LSWX: - case INST_STSWX: + switch (instword & PPC_INST_STRING_MASK) { + case PPC_INST_LSWX: + case PPC_INST_STSWX: EA += NB_RB; num_bytes = regs->xer & 0x7f; break; - case INST_LSWI: - case INST_STSWI: + case PPC_INST_LSWI: + case PPC_INST_STSWI: num_bytes = (NB_RB == 0) ? 32 : NB_RB; break; default: @@ -695,9 +676,9 @@ static int emulate_string_inst(struct pt_regs *regs, u32 instword) u8 val; u32 shift = 8 * (3 - (pos & 0x3)); - switch ((instword & INST_STRING_MASK)) { - case INST_LSWX: - case INST_LSWI: + switch ((instword & PPC_INST_STRING_MASK)) { + case PPC_INST_LSWX: + case PPC_INST_LSWI: if (get_user(val, (u8 __user *)EA)) return -EFAULT; /* first time updating this reg, @@ -706,8 +687,8 @@ static int emulate_string_inst(struct pt_regs *regs, u32 instword) regs->gpr[rT] = 0; regs->gpr[rT] |= val << shift; break; - case INST_STSWI: - case INST_STSWX: + case PPC_INST_STSWI: + case PPC_INST_STSWX: val = regs->gpr[rT] >> shift; if (put_user(val, (u8 __user *)EA)) return -EFAULT; @@ -775,18 +756,18 @@ static int emulate_instruction(struct pt_regs *regs) return -EFAULT; /* Emulate the mfspr rD, PVR. */ - if ((instword & INST_MFSPR_PVR_MASK) == INST_MFSPR_PVR) { + if ((instword & PPC_INST_MFSPR_PVR_MASK) == PPC_INST_MFSPR_PVR) { rd = (instword >> 21) & 0x1f; regs->gpr[rd] = mfspr(SPRN_PVR); return 0; } /* Emulating the dcba insn is just a no-op. */ - if ((instword & INST_DCBA_MASK) == INST_DCBA) + if ((instword & PPC_INST_DCBA_MASK) == PPC_INST_DCBA) return 0; /* Emulate the mcrxr insn. */ - if ((instword & INST_MCRXR_MASK) == INST_MCRXR) { + if ((instword & PPC_INST_MCRXR_MASK) == PPC_INST_MCRXR) { int shift = (instword >> 21) & 0x1c; unsigned long msk = 0xf0000000UL >> shift; @@ -796,16 +777,16 @@ static int emulate_instruction(struct pt_regs *regs) } /* Emulate load/store string insn. */ - if ((instword & INST_STRING_GEN_MASK) == INST_STRING) + if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) return emulate_string_inst(regs, instword); /* Emulate the popcntb (Population Count Bytes) instruction. */ - if ((instword & INST_POPCNTB_MASK) == INST_POPCNTB) { + if ((instword & PPC_INST_POPCNTB_MASK) == PPC_INST_POPCNTB) { return emulate_popcntb_inst(regs, instword); } /* Emulate isel (Integer Select) instruction */ - if ((instword & INST_ISEL_MASK) == INST_ISEL) { + if ((instword & PPC_INST_ISEL_MASK) == PPC_INST_ISEL) { return emulate_isel(regs, instword); } @@ -1144,6 +1125,24 @@ void vsx_assist_exception(struct pt_regs *regs) #endif /* CONFIG_VSX */ #ifdef CONFIG_FSL_BOOKE + +void doorbell_exception(struct pt_regs *regs) +{ +#ifdef CONFIG_SMP + int cpu = smp_processor_id(); + int msg; + + if (num_online_cpus() < 2) + return; + + for (msg = 0; msg < 4; msg++) + if (test_and_clear_bit(msg, &dbell_smp_message[cpu])) + smp_message_recv(msg); +#else + printk(KERN_WARNING "Received doorbell on non-smp system\n"); +#endif +} + void CacheLockingException(struct pt_regs *regs, unsigned long address, unsigned long error_code) { diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index 7d6c9bb8c77..fc9af47e212 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -18,6 +18,7 @@ #include <asm/udbg.h> void (*udbg_putc)(char c); +void (*udbg_flush)(void); int (*udbg_getc)(void); int (*udbg_getc_poll)(void); @@ -76,6 +77,9 @@ void udbg_puts(const char *s) while ((c = *s++) != '\0') udbg_putc(c); } + + if (udbg_flush) + udbg_flush(); } #if 0 else { @@ -98,6 +102,9 @@ int udbg_write(const char *s, int n) } } + if (udbg_flush) + udbg_flush(); + return n - remain; } diff --git a/arch/powerpc/kernel/udbg_16550.c b/arch/powerpc/kernel/udbg_16550.c index 7b7da8cfd5e..0362a891e54 100644 --- a/arch/powerpc/kernel/udbg_16550.c +++ b/arch/powerpc/kernel/udbg_16550.c @@ -48,14 +48,21 @@ struct NS16550 { static struct NS16550 __iomem *udbg_comport; -static void udbg_550_putc(char c) +static void udbg_550_flush(void) { if (udbg_comport) { while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0) /* wait for idle */; - out_8(&udbg_comport->thr, c); + } +} + +static void udbg_550_putc(char c) +{ + if (udbg_comport) { if (c == '\n') udbg_550_putc('\r'); + udbg_550_flush(); + out_8(&udbg_comport->thr, c); } } @@ -108,6 +115,7 @@ void udbg_init_uart(void __iomem *comport, unsigned int speed, /* Clear & enable FIFOs */ out_8(&udbg_comport->fcr ,0x07); udbg_putc = udbg_550_putc; + udbg_flush = udbg_550_flush; udbg_getc = udbg_550_getc; udbg_getc_poll = udbg_550_getc_poll; } @@ -149,14 +157,21 @@ unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock) } #ifdef CONFIG_PPC_MAPLE -void udbg_maple_real_putc(char c) +void udbg_maple_real_flush(void) { if (udbg_comport) { while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) /* wait for idle */; - real_writeb(c, &udbg_comport->thr); eieio(); + } +} + +void udbg_maple_real_putc(char c) +{ + if (udbg_comport) { if (c == '\n') udbg_maple_real_putc('\r'); + udbg_maple_real_flush(); + real_writeb(c, &udbg_comport->thr); eieio(); } } @@ -165,20 +180,28 @@ void __init udbg_init_maple_realmode(void) udbg_comport = (struct NS16550 __iomem *)0xf40003f8; udbg_putc = udbg_maple_real_putc; + udbg_flush = udbg_maple_real_flush; udbg_getc = NULL; udbg_getc_poll = NULL; } #endif /* CONFIG_PPC_MAPLE */ #ifdef CONFIG_PPC_PASEMI -void udbg_pas_real_putc(char c) +void udbg_pas_real_flush(void) { if (udbg_comport) { while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0) /* wait for idle */; - real_205_writeb(c, &udbg_comport->thr); eieio(); + } +} + +void udbg_pas_real_putc(char c) +{ + if (udbg_comport) { if (c == '\n') udbg_pas_real_putc('\r'); + udbg_pas_real_flush(); + real_205_writeb(c, &udbg_comport->thr); eieio(); } } @@ -187,6 +210,7 @@ void udbg_init_pas_realmode(void) udbg_comport = (struct NS16550 __iomem *)0xfcff03f8UL; udbg_putc = udbg_pas_real_putc; + udbg_flush = udbg_pas_real_flush; udbg_getc = NULL; udbg_getc_poll = NULL; } @@ -195,14 +219,21 @@ void udbg_init_pas_realmode(void) #ifdef CONFIG_PPC_EARLY_DEBUG_44x #include <platforms/44x/44x.h> -static void udbg_44x_as1_putc(char c) +static int udbg_44x_as1_flush(void) { if (udbg_comport) { while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0) /* wait for idle */; - as1_writeb(c, &udbg_comport->thr); eieio(); + } +} + +static void udbg_44x_as1_putc(char c) +{ + if (udbg_comport) { if (c == '\n') udbg_44x_as1_putc('\r'); + udbg_44x_as1_flush(); + as1_writeb(c, &udbg_comport->thr); eieio(); } } @@ -222,19 +253,27 @@ void __init udbg_init_44x_as1(void) (struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR; udbg_putc = udbg_44x_as1_putc; + udbg_flush = udbg_44x_as1_flush; udbg_getc = udbg_44x_as1_getc; } #endif /* CONFIG_PPC_EARLY_DEBUG_44x */ #ifdef CONFIG_PPC_EARLY_DEBUG_40x -static void udbg_40x_real_putc(char c) +static void udbg_40x_real_flush(void) { if (udbg_comport) { while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) /* wait for idle */; - real_writeb(c, &udbg_comport->thr); eieio(); + } +} + +static void udbg_40x_real_putc(char c) +{ + if (udbg_comport) { if (c == '\n') udbg_40x_real_putc('\r'); + udbg_40x_real_flush(); + real_writeb(c, &udbg_comport->thr); eieio(); } } @@ -254,6 +293,7 @@ void __init udbg_init_40x_realmode(void) CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR; udbg_putc = udbg_40x_real_putc; + udbg_flush = udbg_40x_real_flush; udbg_getc = udbg_40x_real_getc; udbg_getc_poll = NULL; } diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index d3694498f3a..819e59f6f7c 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -482,7 +482,7 @@ static void vio_cmo_balance(struct work_struct *work) cmo->excess.size = cmo->entitled - cmo->reserve.size; cmo->excess.free = cmo->excess.size - need; - cancel_delayed_work(container_of(work, struct delayed_work, work)); + cancel_delayed_work(to_delayed_work(work)); spin_unlock_irqrestore(&vio_cmo.lock, flags); } diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 161b9b9691f..b9ef1644a72 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -58,6 +58,7 @@ SECTIONS SCHED_TEXT LOCK_TEXT KPROBES_TEXT + IRQENTRY_TEXT #ifdef CONFIG_PPC32 *(.got1) @@ -181,13 +182,7 @@ SECTIONS __initramfs_end = .; } #endif - . = ALIGN(PAGE_SIZE); - .data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { - __per_cpu_start = .; - *(.data.percpu) - *(.data.percpu.shared_aligned) - __per_cpu_end = .; - } + PERCPU(PAGE_SIZE) . = ALIGN(8); .machine.desc : AT(ADDR(.machine.desc) - LOAD_OFFSET) { |