From: Mark Brown Date: Fri, 16 Jan 2015 22:50:34 +0000 (+0000) Subject: Merge remote-tracking branch 'lsk/v3.10/topic/arm64-misc' into linux-linaro-lsk X-Git-Tag: firefly_0821_release~3680^2~35^2~3 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=d1f7a4c0bfda09bebb1948e2169a6ce6a7319bd3;p=firefly-linux-kernel-4.4.55.git Merge remote-tracking branch 'lsk/v3.10/topic/arm64-misc' into linux-linaro-lsk Conflicts: arch/arm/include/asm/psci.h arch/arm/kernel/psci.c arch/arm/kernel/psci_smp.c arch/arm/kernel/setup.c arch/arm64/Kconfig arch/arm64/include/asm/cpu_ops.h arch/arm64/include/asm/pgtable.h arch/arm64/include/asm/psci.h arch/arm64/kernel/head.S arch/arm64/kernel/psci.c arch/arm64/kernel/ptrace.c --- d1f7a4c0bfda09bebb1948e2169a6ce6a7319bd3 diff --cc arch/arm/kernel/psci.c index 0daf4f252284,f73891b6b730..f705c1b27bb3 --- a/arch/arm/kernel/psci.c +++ b/arch/arm/kernel/psci.c @@@ -17,7 -17,9 +17,10 @@@ #include #include +#include + #include + #include + #include #include #include @@@ -27,12 -30,8 +31,12 @@@ struct psci_operations psci_ops; +/* Type of psci support. Currently can only be enabled or disabled */ +#define PSCI_SUP_DISABLED 0 +#define PSCI_SUP_ENABLED 1 + - static unsigned int psci; static int (*invoke_psci_fn)(u32, u32, u32, u32); + typedef int (*psci_initcall_t)(const struct device_node *); enum psci_function { PSCI_FN_CPU_SUSPEND, @@@ -55,14 -50,12 +55,14 @@@ static int psci_to_linux_errno(int errn switch (errno) { case PSCI_RET_SUCCESS: return 0; - case PSCI_RET_EOPNOTSUPP: + case PSCI_RET_NOT_SUPPORTED: return -EOPNOTSUPP; - case PSCI_RET_EINVAL: + case PSCI_RET_INVALID_PARAMS: return -EINVAL; - case PSCI_RET_EPERM: + case PSCI_RET_DENIED: return -EPERM; - case PSCI_RET_EALREADYON: ++ case PSCI_RET_ALREADY_ON: + return -EAGAIN; }; return -EINVAL; diff --cc arch/arm/kernel/psci_smp.c index 23a11424c568,fc1dbd258255..3af4190e4d9d --- a/arch/arm/kernel/psci_smp.c +++ b/arch/arm/kernel/psci_smp.c @@@ -14,9 -14,10 +14,13 @@@ */ #include +#include +#include +#include + #include + #include + #include + #include #include #include diff --cc arch/arm64/Kconfig index 279594b83781,4142cab9d4ff..d64c1a0e9dd0 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@@ -1,10 -1,8 +1,11 @@@ config ARM64 def_bool y select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE + select ARCH_HAS_OPP select ARCH_USE_CMPXCHG_LOCKREF + select ARCH_HAS_OPP + select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST + select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_WANT_OPTIONAL_GPIOLIB select ARCH_WANT_COMPAT_IPC_PARSE_VERSION select ARCH_WANT_FRAME_POINTERS @@@ -29,10 -23,9 +30,11 @@@ select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select HARDIRQS_SW_RESEND + select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK + select HAVE_C_RECORDMCOUNT + select HAVE_CC_STACKPROTECTOR select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG diff --cc arch/arm64/include/asm/cpu_ops.h index 152413076503,27207fee2521..d7b4b38a8e86 --- a/arch/arm64/include/asm/cpu_ops.h +++ b/arch/arm64/include/asm/cpu_ops.h @@@ -39,9 -39,7 +39,10 @@@ struct device_node * from the cpu to be killed. * @cpu_die: Makes a cpu leave the kernel. Must not fail. Called from the * cpu being killed. + * @cpu_kill: Ensures a cpu has left the kernel. Called from another cpu. + * @cpu_suspend: Suspends a cpu and saves the required context. May fail owing + * to wrong parameters or error conditions. Called from the + * CPU being suspended. Must be called with IRQs disabled. */ struct cpu_operations { const char *name; @@@ -52,10 -50,8 +53,11 @@@ #ifdef CONFIG_HOTPLUG_CPU int (*cpu_disable)(unsigned int cpu); void (*cpu_die)(unsigned int cpu); + int (*cpu_kill)(unsigned int cpu); #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND + int (*cpu_suspend)(unsigned long); +#endif }; extern const struct cpu_operations *cpu_ops[NR_CPUS]; diff --cc arch/arm64/kernel/psci.c index 0e32ab453e5b,9e9798f91172..e58a67974f98 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@@ -19,14 -18,17 +19,19 @@@ #include #include #include +#include + #include + #include + #include + #include #include #include #include #include #include +#include + #include #define PSCI_POWER_STATE_TYPE_STANDBY 0 #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 @@@ -57,15 -65,8 +68,10 @@@ enum psci_function PSCI_FN_MAX, }; +static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state); + static u32 psci_function_id[PSCI_FN_MAX]; - #define PSCI_RET_SUCCESS 0 - #define PSCI_RET_EOPNOTSUPP -1 - #define PSCI_RET_EINVAL -2 - #define PSCI_RET_EPERM -3 - static int psci_to_linux_errno(int errno) { switch (errno) { @@@ -82,34 -83,16 +88,27 @@@ return -EINVAL; } - #define PSCI_POWER_STATE_ID_MASK 0xffff - #define PSCI_POWER_STATE_ID_SHIFT 0 - #define PSCI_POWER_STATE_TYPE_MASK 0x1 - #define PSCI_POWER_STATE_TYPE_SHIFT 16 - #define PSCI_POWER_STATE_AFFL_MASK 0x3 - #define PSCI_POWER_STATE_AFFL_SHIFT 24 - static u32 psci_power_state_pack(struct psci_power_state state) { - return ((state.id & PSCI_POWER_STATE_ID_MASK) - << PSCI_POWER_STATE_ID_SHIFT) | - ((state.type & PSCI_POWER_STATE_TYPE_MASK) - << PSCI_POWER_STATE_TYPE_SHIFT) | - ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK) - << PSCI_POWER_STATE_AFFL_SHIFT); + return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT) + & PSCI_0_2_POWER_STATE_ID_MASK) | + ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT) + & PSCI_0_2_POWER_STATE_TYPE_MASK) | + ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT) + & PSCI_0_2_POWER_STATE_AFFL_MASK); } +static void psci_power_state_unpack(u32 power_state, + struct psci_power_state *state) +{ - state->id = (power_state >> PSCI_POWER_STATE_ID_SHIFT) - & PSCI_POWER_STATE_ID_MASK; - state->type = (power_state >> PSCI_POWER_STATE_TYPE_SHIFT) - & PSCI_POWER_STATE_TYPE_MASK; - state->affinity_level = (power_state >> PSCI_POWER_STATE_AFFL_SHIFT) - & PSCI_POWER_STATE_AFFL_MASK; ++ state->id = (power_state >> PSCI_0_2_POWER_STATE_ID_SHIFT) ++ & PSCI_0_2_POWER_STATE_ID_MASK; ++ state->type = (power_state >> PSCI_0_2_POWER_STATE_TYPE_SHIFT) ++ & PSCI_0_2_POWER_STATE_TYPE_MASK; ++ state->affinity_level = (power_state >> PSCI_0_2_POWER_STATE_AFFL_SHIFT) ++ & PSCI_0_2_POWER_STATE_AFFL_MASK; +} + /* * The following two functions are invoked via the invoke_psci_fn pointer * and will not be inlined, allowing us to piggyback on the AAPCS. @@@ -187,107 -178,135 +194,135 @@@ static int psci_migrate(unsigned long c return psci_to_linux_errno(err); } - static const struct of_device_id psci_of_match[] __initconst = { - { .compatible = "arm,psci", }, - {}, - }; + static int psci_affinity_info(unsigned long target_affinity, + unsigned long lowest_affinity_level) + { + int err; + u32 fn; - int __init psci_dt_register_idle_states(struct cpuidle_driver *drv, - struct device_node *state_nodes[]) + fn = psci_function_id[PSCI_FN_AFFINITY_INFO]; + err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0); + return err; + } + + static int psci_migrate_info_type(void) { - int cpu, i; - struct psci_power_state *psci_states; - const struct cpu_operations *cpu_ops_ptr; + int err; + u32 fn; + + fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE]; + err = invoke_psci_fn(fn, 0, 0, 0); + return err; + } + + static int get_set_conduit_method(struct device_node *np) + { + const char *method; + + pr_info("probing for conduit method from DT.\n"); + + if (of_property_read_string(np, "method", &method)) { + pr_warn("missing \"method\" property\n"); + return -ENXIO; + } - if (!state_nodes) + if (!strcmp("hvc", method)) { + invoke_psci_fn = __invoke_psci_fn_hvc; + } else if (!strcmp("smc", method)) { + invoke_psci_fn = __invoke_psci_fn_smc; + } else { + pr_warn("invalid \"method\" property: %s\n", method); return -EINVAL; - /* - * This is belt-and-braces: make sure that if the idle - * specified protocol is psci, the cpu_ops have been - * initialized to psci operations. Anything else is - * a recipe for mayhem. - */ - for_each_cpu(cpu, drv->cpumask) { - cpu_ops_ptr = cpu_ops[cpu]; - if (WARN_ON(!cpu_ops_ptr || strcmp(cpu_ops_ptr->name, "psci"))) - return -EOPNOTSUPP; } + return 0; + } - psci_states = kcalloc(drv->state_count, sizeof(*psci_states), - GFP_KERNEL); -static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd) ++static void psci_sys_reset(char str, const char *cmd) + { + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); + } - if (!psci_states) { - pr_warn("psci idle state allocation failed\n"); - return -ENOMEM; - } + static void psci_sys_poweroff(void) + { + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); + } + + /* + * PSCI Function IDs for v0.2+ are well defined so use + * standard values. + */ + static int psci_0_2_init(struct device_node *np) + { + int err, ver; + + err = get_set_conduit_method(np); - for_each_cpu(cpu, drv->cpumask) { - if (per_cpu(psci_power_state, cpu)) { - pr_warn("idle states already initialized on cpu %u\n", - cpu); - continue; + if (err) + goto out_put_node; + + ver = psci_get_version(); + + if (ver == PSCI_RET_NOT_SUPPORTED) { + /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */ + pr_err("PSCI firmware does not comply with the v0.2 spec.\n"); + err = -EOPNOTSUPP; + goto out_put_node; + } else { + pr_info("PSCIv%d.%d detected in firmware.\n", + PSCI_VERSION_MAJOR(ver), + PSCI_VERSION_MINOR(ver)); + + if (PSCI_VERSION_MAJOR(ver) == 0 && + PSCI_VERSION_MINOR(ver) < 2) { + err = -EINVAL; + pr_err("Conflicting PSCI version detected.\n"); + goto out_put_node; } - per_cpu(psci_power_state, cpu) = psci_states; } + pr_info("Using standard PSCI v0.2 function IDs\n"); + psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND; + psci_ops.cpu_suspend = psci_cpu_suspend; - for (i = 0; i < drv->state_count; i++) { - u32 psci_power_state; + psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; + psci_ops.cpu_off = psci_cpu_off; - if (!state_nodes[i]) { - /* - * An index with a missing node pointer falls back to - * simple STANDBYWFI - */ - psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY; - continue; - } + psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON; + psci_ops.cpu_on = psci_cpu_on; - if (of_property_read_u32(state_nodes[i], "entry-method-param", - &psci_power_state)) { - pr_warn(" * %s missing entry-method-param property\n", - state_nodes[i]->full_name); - /* - * If entry-method-param property is missing, fall - * back to STANDBYWFI state - */ - psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY; - continue; - } + psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE; + psci_ops.migrate = psci_migrate; - pr_debug("psci-power-state %#x index %u\n", - psci_power_state, i); - psci_power_state_unpack(psci_power_state, &psci_states[i]); - } + psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO; + psci_ops.affinity_info = psci_affinity_info; - return 0; + psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] = + PSCI_0_2_FN_MIGRATE_INFO_TYPE; + psci_ops.migrate_info_type = psci_migrate_info_type; + + arm_pm_restart = psci_sys_reset; + + pm_power_off = psci_sys_poweroff; + + out_put_node: + of_node_put(np); + return err; } - void __init psci_init(void) + /* + * PSCI < v0.2 get PSCI Function IDs via DT. + */ + static int psci_0_1_init(struct device_node *np) { - struct device_node *np; - const char *method; u32 id; + int err; - np = of_find_matching_node(NULL, psci_of_match); - if (!np) - return; - - pr_info("probing function IDs from device-tree\n"); + err = get_set_conduit_method(np); - if (of_property_read_string(np, "method", &method)) { - pr_warning("missing \"method\" property\n"); + if (err) goto out_put_node; - } - if (!strcmp("hvc", method)) { - invoke_psci_fn = __invoke_psci_fn_hvc; - } else if (!strcmp("smc", method)) { - invoke_psci_fn = __invoke_psci_fn_smc; - } else { - pr_warning("invalid \"method\" property: %s\n", method); - goto out_put_node; - } + pr_info("Using PSCI v0.1 Function IDs from DT\n"); if (!of_property_read_u32(np, "cpu_suspend", &id)) { psci_function_id[PSCI_FN_CPU_SUSPEND] = id; @@@ -364,20 -404,37 +420,49 @@@ static void cpu_psci_cpu_die(unsigned i pr_crit("unable to power off CPU%u (%d)\n", cpu, ret); } + + static int cpu_psci_cpu_kill(unsigned int cpu) + { + int err, i; + + if (!psci_ops.affinity_info) + return 1; + /* + * cpu_kill could race with cpu_die and we can + * potentially end up declaring this cpu undead + * while it is dying. So, try again a few times. + */ + + for (i = 0; i < 10; i++) { + err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); + if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) { + pr_info("CPU%d killed.\n", cpu); + return 1; + } + + msleep(10); + pr_info("Retrying again to check for CPU kill\n"); + } + + pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n", + cpu, err); + /* Make op_cpu_kill() fail. */ + return 0; + } #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND +static int cpu_psci_cpu_suspend(unsigned long index) +{ + struct psci_power_state *state = __get_cpu_var(psci_power_state); + + if (!state) + return -EOPNOTSUPP; + + return psci_ops.cpu_suspend(state[index], virt_to_phys(cpu_resume)); +} +#endif + const struct cpu_operations cpu_psci_ops = { .name = "psci", .cpu_init = cpu_psci_cpu_init, @@@ -386,10 -443,8 +471,11 @@@ #ifdef CONFIG_HOTPLUG_CPU .cpu_disable = cpu_psci_cpu_disable, .cpu_die = cpu_psci_cpu_die, + .cpu_kill = cpu_psci_cpu_kill, #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND + .cpu_suspend = cpu_psci_cpu_suspend, +#endif }; #endif diff --cc arch/arm64/kernel/ptrace.c index bc09a147454f,71c6a623e226..8ba6b0fa1753 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@@ -643,23 -638,28 +643,28 @@@ static int compat_gpr_get(struct task_s switch (idx) { case 15: - reg = (void *)&task_pt_regs(target)->pc; + reg = task_pt_regs(target)->pc; break; case 16: - reg = (void *)&task_pt_regs(target)->pstate; + reg = task_pt_regs(target)->pstate; break; case 17: - reg = (void *)&task_pt_regs(target)->orig_x0; + reg = task_pt_regs(target)->orig_x0; break; default: - reg = (void *)&task_pt_regs(target)->regs[idx]; + reg = task_pt_regs(target)->regs[idx]; } - ret = copy_to_user(ubuf, ®, sizeof(reg)); - if (ret) - break; - - ubuf += sizeof(reg); + if (kbuf) { + memcpy(kbuf, ®, sizeof(reg)); + kbuf += sizeof(reg); + } else { + ret = copy_to_user(ubuf, ®, sizeof(reg)); + if (ret) + break; + + ubuf += sizeof(reg); + } } return ret;