From be43a0489e06f46ff645d9c88e28d75f6bf6ea1b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 29 Nov 2017 15:36:52 +1000 Subject: fast-reboot: move sreset direct controls to direct-controls.c Signed-off-by: Nicholas Piggin Signed-off-by: Stewart Smith --- core/direct-controls.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++ core/fast-reboot.c | 284 +------------------------------------------- include/direct-controls.h | 29 +++++ 3 files changed, 322 insertions(+), 283 deletions(-) create mode 100644 include/direct-controls.h diff --git a/core/direct-controls.c b/core/direct-controls.c index c5ba80e..2f3c18d 100644 --- a/core/direct-controls.c +++ b/core/direct-controls.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -21,6 +22,297 @@ #include #include + +/**************** mambo direct controls ****************/ + +extern unsigned long callthru_tcl(const char *str, int len); + +static void mambo_sreset_cpu(struct cpu_thread *cpu) +{ + uint32_t core_id = pir_to_core_id(cpu->pir); + uint32_t thread_id = pir_to_thread_id(cpu->pir); + char tcl_cmd[50]; + + snprintf(tcl_cmd, sizeof(tcl_cmd), "mysim cpu %i:%i set spr pc 0x100", core_id, thread_id); + callthru_tcl(tcl_cmd, strlen(tcl_cmd)); +} + +/**************** POWER8 direct controls ****************/ + +#define P8_EX_TCTL_DIRECT_CONTROLS(t) (0x10013000 + (t) * 0x10) +#define P8_DIRECT_CTL_STOP PPC_BIT(63) +#define P8_DIRECT_CTL_PRENAP PPC_BIT(47) +#define P8_DIRECT_CTL_SRESET PPC_BIT(60) + +static int p8_set_special_wakeup(struct cpu_thread *cpu) +{ + uint64_t val, poll_target, stamp; + uint32_t core_id; + int rc; + + /* + * Note: HWP checks for checkstops, but I assume we don't need to + * as we wouldn't be running if one was present + */ + + /* Grab core ID once */ + core_id = pir_to_core_id(cpu->pir); + + prlog(PR_DEBUG, "RESET Waking up core 0x%x\n", core_id); + + /* + * The original HWp reads the XSCOM first but ignores the result + * and error, let's do the same until I know for sure that is + * not necessary + */ + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), + &val); + + /* Then we write special wakeup */ + rc = xscom_write(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, + EX_PM_SPECIAL_WAKEUP_PHYP), + PPC_BIT(0)); + if (rc) { + prerror("RESET: XSCOM error %d asserting special" + " wakeup on 0x%x\n", rc, cpu->pir); + return rc; + } + + /* + * HWP uses the history for Perf register here, dunno why it uses + * that one instead of the pHyp one, maybe to avoid clobbering it... + * + * In any case, it does that to check for run/nap vs.sleep/winkle/other + * to decide whether to poll on checkstop or not. Since we don't deal + * with checkstop conditions here, we ignore that part. + */ + + /* + * Now poll for completion of special wakeup. The HWP is nasty here, + * it will poll at 5ms intervals for up to 200ms. This is not quite + * acceptable for us at runtime, at least not until we have the + * ability to "context switch" HBRT. In practice, because we don't + * winkle, it will never take that long, so we increase the polling + * frequency to 1us per poll. However we do have to keep the same + * timeout. + * + * We don't use time_wait_ms() either for now as we don't want to + * poll the FSP here. + */ + stamp = mftb(); + poll_target = stamp + msecs_to_tb(200); + val = 0; + while (!(val & EX_PM_GP0_SPECIAL_WAKEUP_DONE)) { + /* Wait 1 us */ + time_wait_us(1); + + /* Read PM state */ + rc = xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_GP0), + &val); + if (rc) { + prerror("RESET: XSCOM error %d reading PM state on" + " 0x%x\n", rc, cpu->pir); + return rc; + } + /* Check timeout */ + if (mftb() > poll_target) + break; + } + + /* Success ? */ + if (val & EX_PM_GP0_SPECIAL_WAKEUP_DONE) { + uint64_t now = mftb(); + prlog(PR_TRACE, "RESET: Special wakeup complete after %ld us\n", + tb_to_usecs(now - stamp)); + return 0; + } + + /* + * We timed out ... + * + * HWP has a complex workaround for HW255321 which affects + * Murano DD1 and Venice DD1. Ignore that for now + * + * Instead we just dump some XSCOMs for error logging + */ + prerror("RESET: Timeout on special wakeup of 0x%0x\n", cpu->pir); + prerror("RESET: PM0 = 0x%016llx\n", val); + val = -1; + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), + &val); + prerror("RESET: SPC_WKUP = 0x%016llx\n", val); + val = -1; + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, + EX_PM_IDLE_STATE_HISTORY_PHYP), + &val); + prerror("RESET: HISTORY = 0x%016llx\n", val); + + return OPAL_HARDWARE; +} + +static int p8_clr_special_wakeup(struct cpu_thread *cpu) +{ + uint64_t val; + uint32_t core_id; + int rc; + + /* + * Note: HWP checks for checkstops, but I assume we don't need to + * as we wouldn't be running if one was present + */ + + /* Grab core ID once */ + core_id = pir_to_core_id(cpu->pir); + + prlog(PR_DEBUG, "RESET: Releasing core 0x%x wakeup\n", core_id); + + /* + * The original HWp reads the XSCOM first but ignores the result + * and error, let's do the same until I know for sure that is + * not necessary + */ + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), + &val); + + /* Then we write special wakeup */ + rc = xscom_write(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, + EX_PM_SPECIAL_WAKEUP_PHYP), 0); + if (rc) { + prerror("RESET: XSCOM error %d deasserting" + " special wakeup on 0x%x\n", rc, cpu->pir); + return rc; + } + + /* + * The original HWp reads the XSCOM again with the comment + * "This puts an inherent delay in the propagation of the reset + * transition" + */ + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), + &val); + + return 0; +} + +static void p8_set_direct_ctl(struct cpu_thread *cpu, uint64_t bits) +{ + uint32_t core_id = pir_to_core_id(cpu->pir); + uint32_t chip_id = pir_to_chip_id(cpu->pir); + uint32_t thread_id = pir_to_thread_id(cpu->pir); + uint32_t xscom_addr; + + xscom_addr = XSCOM_ADDR_P8_EX(core_id, + P8_EX_TCTL_DIRECT_CONTROLS(thread_id)); + + xscom_write(chip_id, xscom_addr, bits); +} + +static int p8_sreset_all_prepare(void) +{ + struct cpu_thread *cpu; + + prlog(PR_DEBUG, "RESET: Resetting from cpu: 0x%x (core 0x%x)\n", + this_cpu()->pir, pir_to_core_id(this_cpu()->pir)); + + /* Assert special wakup on all cores. Only on operational cores. */ + for_each_ungarded_primary(cpu) { + if (p8_set_special_wakeup(cpu) != OPAL_SUCCESS) + return OPAL_HARDWARE; + } + + prlog(PR_DEBUG, "RESET: Stopping the world...\n"); + + /* Put everybody in stop except myself */ + for_each_ungarded_cpu(cpu) { + if (cpu != this_cpu()) + p8_set_direct_ctl(cpu, P8_DIRECT_CTL_STOP); + } + + return OPAL_SUCCESS; +} + +static void p8_sreset_all_finish(void) +{ + struct cpu_thread *cpu; + + for_each_ungarded_primary(cpu) + p8_clr_special_wakeup(cpu); +} + +static void p8_sreset_all_others(void) +{ + struct cpu_thread *cpu; + + prlog(PR_DEBUG, "RESET: Pre-napping all threads but one...\n"); + + /* Put everybody in pre-nap except myself */ + for_each_ungarded_cpu(cpu) { + if (cpu != this_cpu()) + p8_set_direct_ctl(cpu, P8_DIRECT_CTL_PRENAP); + } + + prlog(PR_DEBUG, "RESET: Resetting all threads but one...\n"); + + /* Reset everybody except my own core threads */ + for_each_ungarded_cpu(cpu) { + if (cpu != this_cpu()) + p8_set_direct_ctl(cpu, P8_DIRECT_CTL_SRESET); + } +} + +int sreset_all_prepare(void) +{ + if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) + return OPAL_SUCCESS; + + if (proc_gen == proc_gen_p8) + return p8_sreset_all_prepare(); + + return OPAL_UNSUPPORTED; +} + +void sreset_all_finish(void) +{ + if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) + return; + + if (proc_gen == proc_gen_p8) + return p8_sreset_all_finish(); +} + +int sreset_all_others(void) +{ + if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) { + struct cpu_thread *cpu; + + for_each_ungarded_cpu(cpu) { + if (cpu == this_cpu()) + continue; + mambo_sreset_cpu(cpu); + } + + return OPAL_SUCCESS; + } + + if (proc_gen == proc_gen_p8) { + p8_sreset_all_others(); + return OPAL_SUCCESS; + } + + return OPAL_UNSUPPORTED; +} + + +/**************** POWER9 direct controls ****************/ + #define P9_RAS_STATUS 0x10a02 #define P9_THREAD_QUIESCED(t) PPC_BITMASK(0 + 8*(t), 3 + 8*(t)) #define P9_QUIESCE_RETRIES 100 diff --git a/core/fast-reboot.c b/core/fast-reboot.c index 84574db..4b7e9aa 100644 --- a/core/fast-reboot.c +++ b/core/fast-reboot.c @@ -27,295 +27,13 @@ #include #include #include - -#define P8_EX_TCTL_DIRECT_CONTROLS(t) (0x10013000 + (t) * 0x10) -#define P8_DIRECT_CTL_STOP PPC_BIT(63) -#define P8_DIRECT_CTL_PRENAP PPC_BIT(47) -#define P8_DIRECT_CTL_SRESET PPC_BIT(60) - +#include /* Flag tested by the OPAL entry code */ uint8_t reboot_in_progress; static volatile bool fast_boot_release; static struct lock reset_lock = LOCK_UNLOCKED; -static int p8_set_special_wakeup(struct cpu_thread *cpu) -{ - uint64_t val, poll_target, stamp; - uint32_t core_id; - int rc; - - /* - * Note: HWP checks for checkstops, but I assume we don't need to - * as we wouldn't be running if one was present - */ - - /* Grab core ID once */ - core_id = pir_to_core_id(cpu->pir); - - prlog(PR_DEBUG, "RESET Waking up core 0x%x\n", core_id); - - /* - * The original HWp reads the XSCOM first but ignores the result - * and error, let's do the same until I know for sure that is - * not necessary - */ - xscom_read(cpu->chip_id, - XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), - &val); - - /* Then we write special wakeup */ - rc = xscom_write(cpu->chip_id, - XSCOM_ADDR_P8_EX_SLAVE(core_id, - EX_PM_SPECIAL_WAKEUP_PHYP), - PPC_BIT(0)); - if (rc) { - prerror("RESET: XSCOM error %d asserting special" - " wakeup on 0x%x\n", rc, cpu->pir); - return rc; - } - - /* - * HWP uses the history for Perf register here, dunno why it uses - * that one instead of the pHyp one, maybe to avoid clobbering it... - * - * In any case, it does that to check for run/nap vs.sleep/winkle/other - * to decide whether to poll on checkstop or not. Since we don't deal - * with checkstop conditions here, we ignore that part. - */ - - /* - * Now poll for completion of special wakeup. The HWP is nasty here, - * it will poll at 5ms intervals for up to 200ms. This is not quite - * acceptable for us at runtime, at least not until we have the - * ability to "context switch" HBRT. In practice, because we don't - * winkle, it will never take that long, so we increase the polling - * frequency to 1us per poll. However we do have to keep the same - * timeout. - * - * We don't use time_wait_ms() either for now as we don't want to - * poll the FSP here. - */ - stamp = mftb(); - poll_target = stamp + msecs_to_tb(200); - val = 0; - while (!(val & EX_PM_GP0_SPECIAL_WAKEUP_DONE)) { - /* Wait 1 us */ - time_wait_us(1); - - /* Read PM state */ - rc = xscom_read(cpu->chip_id, - XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_GP0), - &val); - if (rc) { - prerror("RESET: XSCOM error %d reading PM state on" - " 0x%x\n", rc, cpu->pir); - return rc; - } - /* Check timeout */ - if (mftb() > poll_target) - break; - } - - /* Success ? */ - if (val & EX_PM_GP0_SPECIAL_WAKEUP_DONE) { - uint64_t now = mftb(); - prlog(PR_TRACE, "RESET: Special wakeup complete after %ld us\n", - tb_to_usecs(now - stamp)); - return 0; - } - - /* - * We timed out ... - * - * HWP has a complex workaround for HW255321 which affects - * Murano DD1 and Venice DD1. Ignore that for now - * - * Instead we just dump some XSCOMs for error logging - */ - prerror("RESET: Timeout on special wakeup of 0x%0x\n", cpu->pir); - prerror("RESET: PM0 = 0x%016llx\n", val); - val = -1; - xscom_read(cpu->chip_id, - XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), - &val); - prerror("RESET: SPC_WKUP = 0x%016llx\n", val); - val = -1; - xscom_read(cpu->chip_id, - XSCOM_ADDR_P8_EX_SLAVE(core_id, - EX_PM_IDLE_STATE_HISTORY_PHYP), - &val); - prerror("RESET: HISTORY = 0x%016llx\n", val); - - return OPAL_HARDWARE; -} - -static int p8_clr_special_wakeup(struct cpu_thread *cpu) -{ - uint64_t val; - uint32_t core_id; - int rc; - - /* - * Note: HWP checks for checkstops, but I assume we don't need to - * as we wouldn't be running if one was present - */ - - /* Grab core ID once */ - core_id = pir_to_core_id(cpu->pir); - - prlog(PR_DEBUG, "RESET: Releasing core 0x%x wakeup\n", core_id); - - /* - * The original HWp reads the XSCOM first but ignores the result - * and error, let's do the same until I know for sure that is - * not necessary - */ - xscom_read(cpu->chip_id, - XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), - &val); - - /* Then we write special wakeup */ - rc = xscom_write(cpu->chip_id, - XSCOM_ADDR_P8_EX_SLAVE(core_id, - EX_PM_SPECIAL_WAKEUP_PHYP), 0); - if (rc) { - prerror("RESET: XSCOM error %d deasserting" - " special wakeup on 0x%x\n", rc, cpu->pir); - return rc; - } - - /* - * The original HWp reads the XSCOM again with the comment - * "This puts an inherent delay in the propagation of the reset - * transition" - */ - xscom_read(cpu->chip_id, - XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), - &val); - - return 0; -} - -static void p8_set_direct_ctl(struct cpu_thread *cpu, uint64_t bits) -{ - uint32_t core_id = pir_to_core_id(cpu->pir); - uint32_t chip_id = pir_to_chip_id(cpu->pir); - uint32_t thread_id = pir_to_thread_id(cpu->pir); - uint32_t xscom_addr; - - xscom_addr = XSCOM_ADDR_P8_EX(core_id, - P8_EX_TCTL_DIRECT_CONTROLS(thread_id)); - - xscom_write(chip_id, xscom_addr, bits); -} - -static int p8_sreset_all_prepare(void) -{ - struct cpu_thread *cpu; - - prlog(PR_DEBUG, "RESET: Resetting from cpu: 0x%x (core 0x%x)\n", - this_cpu()->pir, pir_to_core_id(this_cpu()->pir)); - - /* Assert special wakup on all cores. Only on operational cores. */ - for_each_ungarded_primary(cpu) { - if (p8_set_special_wakeup(cpu) != OPAL_SUCCESS) - return OPAL_HARDWARE; - } - - prlog(PR_DEBUG, "RESET: Stopping the world...\n"); - - /* Put everybody in stop except myself */ - for_each_ungarded_cpu(cpu) { - if (cpu != this_cpu()) - p8_set_direct_ctl(cpu, P8_DIRECT_CTL_STOP); - } - - return OPAL_SUCCESS; -} - -static void p8_sreset_all_finish(void) -{ - struct cpu_thread *cpu; - - for_each_ungarded_primary(cpu) - p8_clr_special_wakeup(cpu); -} - -static void p8_sreset_all_others(void) -{ - struct cpu_thread *cpu; - - prlog(PR_DEBUG, "RESET: Pre-napping all threads but one...\n"); - - /* Put everybody in pre-nap except myself */ - for_each_ungarded_cpu(cpu) { - if (cpu != this_cpu()) - p8_set_direct_ctl(cpu, P8_DIRECT_CTL_PRENAP); - } - - prlog(PR_DEBUG, "RESET: Resetting all threads but one...\n"); - - /* Reset everybody except my own core threads */ - for_each_ungarded_cpu(cpu) { - if (cpu != this_cpu()) - p8_set_direct_ctl(cpu, P8_DIRECT_CTL_SRESET); - } -} - -extern unsigned long callthru_tcl(const char *str, int len); - -static void mambo_sreset_cpu(struct cpu_thread *cpu) -{ - uint32_t core_id = pir_to_core_id(cpu->pir); - uint32_t thread_id = pir_to_thread_id(cpu->pir); - char tcl_cmd[50]; - - snprintf(tcl_cmd, sizeof(tcl_cmd), "mysim cpu %i:%i set spr pc 0x100", core_id, thread_id); - callthru_tcl(tcl_cmd, strlen(tcl_cmd)); -} - -static int sreset_all_prepare(void) -{ - if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) - return OPAL_SUCCESS; - - if (proc_gen == proc_gen_p8) - return p8_sreset_all_prepare(); - - return OPAL_UNSUPPORTED; -} - -static void sreset_all_finish(void) -{ - if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) - return; - - if (proc_gen == proc_gen_p8) - return p8_sreset_all_finish(); -} - -static int sreset_all_others(void) -{ - if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) { - struct cpu_thread *cpu; - - for_each_ungarded_cpu(cpu) { - if (cpu == this_cpu()) - continue; - mambo_sreset_cpu(cpu); - } - return OPAL_SUCCESS; - } - - if (proc_gen == proc_gen_p8) { - p8_sreset_all_others(); - return OPAL_SUCCESS; - } - - return OPAL_UNSUPPORTED; -} - static bool cpu_state_wait_all_others(enum cpu_thread_state state, unsigned long timeout_tb) { diff --git a/include/direct-controls.h b/include/direct-controls.h new file mode 100644 index 0000000..9df1549 --- /dev/null +++ b/include/direct-controls.h @@ -0,0 +1,29 @@ +/* Copyright 2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DIRECT_CONTROLS_H +#define __DIRECT_CONTROLS_H + +#include +#include +#include + +/* fast reboot APIs */ +extern int sreset_all_prepare(void); +extern int sreset_all_others(void); +extern void sreset_all_finish(void); + +#endif /* __DIRECT_CONTROLS_H */ -- cgit v1.1