diff options
author | Alistair Popple <alistair@popple.id.au> | 2017-06-06 23:11:20 +1000 |
---|---|---|
committer | Alistair Popple <alistair@popple.id.au> | 2017-06-28 16:13:55 +1000 |
commit | 15392117158014da555d5a399ac1e4c322ca741b (patch) | |
tree | b5d30efe40628d6ea89640c3584173a37e6e3f06 /libpdbg | |
parent | 602f056021f67960b1f76f2984aea9d21eeaefbd (diff) | |
download | pdbg-15392117158014da555d5a399ac1e4c322ca741b.tar.gz pdbg-15392117158014da555d5a399ac1e4c322ca741b.zip |
libpdbg: Add support for P9 thread operations
This patch adds support for starting and stopping threads on POWER9 as well as
instruction ramming which is required to read/write GPRs, etc.
Signed-off-by: Alistair Popple <alistair@popple.id.au>
Diffstat (limited to 'libpdbg')
-rw-r--r-- | libpdbg/chip.c | 327 | ||||
-rw-r--r-- | libpdbg/operations.h | 16 | ||||
-rw-r--r-- | libpdbg/p8chip.c | 344 | ||||
-rw-r--r-- | libpdbg/p9chip.c | 240 | ||||
-rw-r--r-- | libpdbg/target.c | 18 | ||||
-rw-r--r-- | libpdbg/target.h | 11 |
6 files changed, 649 insertions, 307 deletions
diff --git a/libpdbg/chip.c b/libpdbg/chip.c index cde11a3..5265afe 100644 --- a/libpdbg/chip.c +++ b/libpdbg/chip.c @@ -24,62 +24,6 @@ #include "operations.h" #include "bitutils.h" -#define RAS_STATUS_TIMEOUT 100 - -#define DIRECT_CONTROLS_REG 0x0 -#define DIRECT_CONTROL_SP_STEP PPC_BIT(61) -#define DIRECT_CONTROL_SP_START PPC_BIT(62) -#define DIRECT_CONTROL_SP_STOP PPC_BIT(63) -#define RAS_MODE_REG 0x1 -#define MR_THREAD_IN_DEBUG PPC_BIT(43) -#define MR_DO_SINGLE_MODE PPC_BIT(50) -#define RAS_STATUS_REG 0x2 -#define RAS_STATUS_SRQ_EMPTY PPC_BIT(8) -#define RAS_STATUS_LSU_QUIESCED PPC_BIT(9) -#define RAS_STATUS_INST_COMPLETE PPC_BIT(12) -#define RAS_STATUS_THREAD_ACTIVE PPC_BIT(48) -#define RAS_STATUS_TS_QUIESCE PPC_BIT(49) -#define POW_STATUS_REG 0x4 -#define PMC_POW_STATE PPC_BITMASK(4, 5) -#define CORE_POW_STATE PPC_BITMASK(23, 25) -#define THREAD_ACTIVE_REG 0x1310e -#define THREAD_ACTIVE PPC_BITMASK(0, 7) -#define RAM_THREAD_ACTIVE PPC_BITMASK(8, 15) -#define SPR_MODE_REG 0x13281 -#define SPR_MODE_SPRC_WR_EN PPC_BIT(3) -#define SPR_MODE_SPRC_SEL PPC_BITMASK(16, 19) -#define SPR_MODE_SPRC_T_SEL PPC_BITMASK(20, 27) -#define L0_SCOM_SPRC_REG 0x13280 -#define SCOM_SPRC_SCRATCH_SPR 0x40 -#define SCR0_REG 0x13283 -#define RAM_MODE_REG 0x13c00 -#define RAM_MODE_ENABLE PPC_BIT(0) -#define RAM_CTRL_REG 0x13c01 -#define RAM_THREAD_SELECT PPC_BITMASK(0, 2) -#define RAM_INSTR PPC_BITMASK(3, 34) -#define RAM_STATUS_REG 0x13c02 -#define RAM_CONTROL_RECOV PPC_BIT(0) -#define RAM_STATUS PPC_BIT(1) -#define RAM_EXCEPTION PPC_BIT(2) -#define LSU_EMPTY PPC_BIT(3) -#define SCOM_EX_GP3 0xf0012 -#define PMSPCWKUPFSP_REG 0xf010d -#define FSP_SPECIAL_WAKEUP PPC_BIT(0) -#define EX_PM_GP0_REG 0xf0100 -#define SPECIAL_WKUP_DONE PPC_BIT(31) - -/* How long (in us) to wait for a special wakeup to complete */ -#define SPECIAL_WKUP_TIMEOUT 10 - -/* Opcodes */ -#define MTNIA_OPCODE 0x00000002UL -#define MFNIA_OPCODE 0x00000004UL -#define MFMSR_OPCODE 0x7c0000a6UL -#define MTMSR_OPCODE 0x7c000124UL -#define MFSPR_OPCODE 0x7c0002a6UL -#define MTSPR_OPCODE 0x7c0003a6UL -#define LD_OPCODE 0xe8000000UL - static uint64_t mfspr(uint64_t reg, uint64_t spr) { if (reg > 31) @@ -136,168 +80,39 @@ static uint64_t ld(uint64_t rt, uint64_t ds, uint64_t ra) return LD_OPCODE | (rt << 21) | (ra << 16) | (ds << 2); } -static int assert_special_wakeup(struct chiplet *chip) -{ - int i = 0; - uint64_t gp0; - - /* Assert special wakeup to prevent low power states */ - CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, FSP_SPECIAL_WAKEUP)); - - /* Poll for completion */ - do { - usleep(1); - CHECK_ERR(pib_read(&chip->target, EX_PM_GP0_REG, &gp0)); - - if (i++ > SPECIAL_WKUP_TIMEOUT) { - PR_ERROR("Timeout waiting for special wakeup on %s@0x%08lx\n", chip->target.name, - dt_get_address(chip->target.dn, 0, NULL)); - return -1; - } - } while (!(gp0 & SPECIAL_WKUP_DONE)); - - return 0; -} - -static int deassert_special_wakeup(struct chiplet *chip) -{ - /* Assert special wakeup to prevent low power states */ - CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, 0)); - - return 0; -} - uint64_t thread_status(struct thread *thread) { return thread->status; } -static uint64_t get_thread_status(struct thread *thread) -{ - uint64_t val, mode_reg, thread_status = thread->status; - - /* Need to activete debug mode to get complete status */ - CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &mode_reg)); - mode_reg |= MR_THREAD_IN_DEBUG; - CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, mode_reg)); - - /* Read status */ - CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &val)); - - thread_status = SETFIELD(THREAD_STATUS_ACTIVE, thread_status, !!(val & RAS_STATUS_THREAD_ACTIVE)); - thread_status = SETFIELD(THREAD_STATUS_QUIESCE, thread_status, !!(val & RAS_STATUS_TS_QUIESCE)); - - /* Read POW status */ - CHECK_ERR(pib_read(&thread->target, POW_STATUS_REG, &val)); - thread_status = SETFIELD(THREAD_STATUS_STATE, thread_status, GETFIELD(PMC_POW_STATE, val)); - - /* Clear debug mode */ - mode_reg &= ~MR_THREAD_IN_DEBUG; - CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, mode_reg)); - - return thread_status; -} - /* * Single step the thread count instructions. */ -int ram_step_thread(struct thread *thread, int count) -{ - int i; - uint64_t ras_mode, ras_status; - - /* Activate single-step mode */ - CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &ras_mode)); - ras_mode |= MR_DO_SINGLE_MODE; - CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode)); - - /* Step the core */ - for (i = 0; i < count; i++) { - CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STEP)); - - /* Wait for step to complete */ - do { - CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &ras_status)); - } while (!(ras_status & RAS_STATUS_INST_COMPLETE)); - } - - /* Deactivate single-step mode */ - ras_mode &= ~MR_DO_SINGLE_MODE; - CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode)); - - return 0; -} - -int ram_stop_thread(struct thread *thread) +int ram_step_thread(struct target *thread_target, int count) { - int i = 0; - uint64_t val; - struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); - - do { - /* Quiese active thread */ - CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STOP)); - - /* Wait for thread to quiese */ - CHECK_ERR(pib_read(&chip->target, RAS_STATUS_REG, &val)); - if (i++ > RAS_STATUS_TIMEOUT) { - PR_ERROR("Unable to quiesce thread %d (0x%016llx).\n", - thread->id, val); - PR_ERROR("Continuing anyway.\n"); - if (val & PPC_BIT(48)) { - PR_ERROR("Unable to continue\n"); - } - break; - } - - /* We can continue ramming if either the - * thread is not active or the SRQ/LSU/TS bits - * are set. */ - } while ((val & RAS_STATUS_THREAD_ACTIVE) && - !((val & RAS_STATUS_SRQ_EMPTY) - && (val & RAS_STATUS_LSU_QUIESCED) - && (val & RAS_STATUS_TS_QUIESCE))); - + struct thread *thread; - /* Make the threads RAM thread active */ - CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val)); - val |= PPC_BIT(8) >> thread->id; - CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val)); - - return 0; + assert(!strcmp(thread_target->class, "thread")); + thread = target_to_thread(thread_target); + return thread->step(thread, count); } -int ram_start_thread(struct thread *thread) +int ram_start_thread(struct target *thread_target) { - uint64_t val; - struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); - - /* Activate thread */ - CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_START)); - - /* Restore thread active */ - CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val)); - val &= ~(PPC_BIT(8) >> thread->id); - val |= PPC_BIT(thread->id); - CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val)); + struct thread *thread; - return 0; + assert(!strcmp(thread_target->class, "thread")); + thread = target_to_thread(thread_target); + return thread->start(thread); } -/* We can only ram a thread if all the threads on the core/chip are - * quiesced */ -int ram_status(struct chiplet *chip) +int ram_stop_thread(struct target *thread_target) { - struct dt_node *dn; - - dt_for_each_compatible(chip->target.dn, dn, "ibm,power8-thread") { - struct thread *thread; - thread = target_to_thread(dn->target); - if (!(get_thread_status(thread) & THREAD_STATUS_QUIESCE)) - return -1; - } + struct thread *thread; - return 0; + assert(!strcmp(thread_target->class, "thread")); + thread = target_to_thread(thread_target); + return thread->stop(thread); } /* @@ -311,29 +126,11 @@ int ram_status(struct chiplet *chip) static int ram_instructions(struct thread *thread, uint64_t *opcodes, uint64_t *results, int len, unsigned int lpar) { - uint64_t ram_mode, val, opcode, r0 = 0, r1 = 0; - struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); + uint64_t ram_mode, opcode, r0 = 0, r1 = 0, scratch = 0; int i; int exception = 0; - /* Check to see if the parent chip is in a state that can RAM instructions */ - if (ram_status(chip)) - return 1; - - if (!(thread_status(thread) & THREAD_STATUS_ACTIVE)) - return 2; - - /* Activate RAM mode */ - CHECK_ERR(pib_read(&chip->target, RAM_MODE_REG, &ram_mode)); - ram_mode |= RAM_MODE_ENABLE; - CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode)); - - /* Setup SPRC to use SPRD */ - val = SPR_MODE_SPRC_WR_EN; - val = SETFIELD(SPR_MODE_SPRC_SEL, val, 1 << (3 - lpar)); - val = SETFIELD(SPR_MODE_SPRC_T_SEL, val, 1 << (7 - thread->id)); - CHECK_ERR(pib_write(&chip->target, SPR_MODE_REG, val)); - CHECK_ERR(pib_write(&chip->target, L0_SCOM_SPRC_REG, SCOM_SPRC_SCRATCH_SPR)); + CHECK_ERR(thread->ram_setup(thread)); /* RAM instructions */ for (i = -2; i < len + 2; i++) { @@ -343,51 +140,29 @@ static int ram_instructions(struct thread *thread, uint64_t *opcodes, /* Save r0 (assumes opcodes don't touch other registers) */ opcode = mtspr(277, 0); else if (i < len) { - CHECK_ERR(pib_write(&chip->target, SCR0_REG, results[i])); + scratch = results[i]; opcode = opcodes[i]; } else if (i == len) { /* Restore r0 */ - CHECK_ERR(pib_write(&chip->target, SCR0_REG, r0)); + scratch = r0; opcode = mfspr(0, 277); } else if (i == len + 1) { /* Restore r1 */ - CHECK_ERR(pib_write(&chip->target, SCR0_REG, r1)); + scratch = r1; opcode = mfspr(0, 277); } - /* ram instruction */ - val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread->id); - val = SETFIELD(RAM_INSTR, val, opcode); - CHECK_ERR(pib_write(&chip->target, RAM_CTRL_REG, val)); - - /* wait for completion */ - do { - CHECK_ERR(pib_read(&chip->target, RAM_STATUS_REG, &val)); - } while (!((val & PPC_BIT(1)) || ((val & PPC_BIT(2)) && (val & PPC_BIT(3))))); - - if (!(val & PPC_BIT(1))) { - exception = GETFIELD(PPC_BITMASK(2,3), val) == 0x3; - if (exception) { - /* Skip remaining instructions */ - i = len - 1; - continue; - } else - PR_ERROR("RAMMING failed with status 0x%llx\n", val); - } + CHECK_ERR(thread->ram_instruction(thread, opcode, &scratch)); - /* Save the results */ - CHECK_ERR(pib_read(&chip->target, SCR0_REG, &val)); if (i == -2) - r1 = val; + r1 = scratch; else if (i == -1) - r0 = val; + r0 = scratch; else if (i < len) - results[i] = val; + results[i] = scratch; } - /* Disable RAM mode */ - ram_mode &= ~RAM_MODE_ENABLE; - CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode)); + CHECK_ERR(thread->ram_destroy(thread)); return exception; } @@ -481,55 +256,3 @@ int ram_getmem(struct thread *thread, uint64_t addr, uint64_t *value) *value = results[3]; return 0; } - -/* - * Initialise all viable threads for ramming on the given chiplet. - */ -static int p8_thread_probe(struct target *target) -{ - struct thread *thread = target_to_thread(target); - - thread->id = (dt_get_address(target->dn, 0, NULL) >> 4) & 0xf; - thread->status = get_thread_status(thread); - - return 0; -} - -struct thread p8_thread = { - .target = { - .name = "POWER8 Thread", - .compatible = "ibm,power8-thread", - .class = "thread", - .probe = p8_thread_probe, - }, -}; -DECLARE_HW_UNIT(p8_thread); - -static int p8_chiplet_probe(struct target *target) -{ - uint64_t value; - struct chiplet *chiplet = target_to_chiplet(target); - int i, count = 0, rc = 0; - - /* Work out if this chip is actually present */ - if (pib_read(target, SCOM_EX_GP3, &value)) { - PR_DEBUG("Error reading chip GP3 register\n"); - return -1; - } - - if (!GETFIELD(PPC_BIT(0), value)) - return -1; - - assert_special_wakeup(chiplet); - return 0; -} - -struct chiplet p8_chiplet = { - .target = { - .name = "POWER8 Chiplet", - .compatible = "ibm,power8-core", - .class = "chiplet", - .probe = p8_chiplet_probe, - }, -}; -DECLARE_HW_UNIT(p8_chiplet); diff --git a/libpdbg/operations.h b/libpdbg/operations.h index 4175af3..62ff5c4 100644 --- a/libpdbg/operations.h +++ b/libpdbg/operations.h @@ -45,6 +45,16 @@ int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint6 #define THREAD_STATUS_SLEEP PPC_BITMASK(61, 62) #define THREAD_STATUS_QUIESCE PPC_BIT(60) +/* Opcodes for instruction ramming */ +#define OPCODE_MASK 0xfc0003ffUL +#define MTNIA_OPCODE 0x00000002UL +#define MFNIA_OPCODE 0x00000004UL +#define MFMSR_OPCODE 0x7c0000a6UL +#define MTMSR_OPCODE 0x7c000124UL +#define MFSPR_OPCODE 0x7c0002a6UL +#define MTSPR_OPCODE 0x7c0003a6UL +#define LD_OPCODE 0xe8000000UL + int ram_getgpr(struct thread *thread, int gpr, uint64_t *value); int ram_putgpr(struct thread *thread, int gpr, uint64_t value); int ram_getnia(struct thread *thread, uint64_t *value); @@ -55,9 +65,9 @@ int ram_getmsr(struct thread *thread, uint64_t *value); int ram_putmsr(struct thread *thread, uint64_t value); int ram_getmem(struct thread *thread, uint64_t addr, uint64_t *value); uint64_t thread_status(struct thread *thread); -int ram_stop_thread(struct thread *thread); -int ram_step_thread(struct thread *thread, int count); -int ram_start_thread(struct thread *thread); +int ram_stop_thread(struct target *thread); +int ram_step_thread(struct target *thread, int count); +int ram_start_thread(struct target *thread); void fsi_destroy(struct target *target); /* GDB server functionality */ diff --git a/libpdbg/p8chip.c b/libpdbg/p8chip.c new file mode 100644 index 0000000..c11b68c --- /dev/null +++ b/libpdbg/p8chip.c @@ -0,0 +1,344 @@ +/* Copyright 2016 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. + */ +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <ccan/array_size/array_size.h> +#include <unistd.h> + +#include "target.h" +#include "operations.h" +#include "bitutils.h" + +#define RAS_STATUS_TIMEOUT 100 + +#define DIRECT_CONTROLS_REG 0x0 +#define DIRECT_CONTROL_SP_STEP PPC_BIT(61) +#define DIRECT_CONTROL_SP_START PPC_BIT(62) +#define DIRECT_CONTROL_SP_STOP PPC_BIT(63) +#define RAS_MODE_REG 0x1 +#define MR_THREAD_IN_DEBUG PPC_BIT(43) +#define MR_DO_SINGLE_MODE PPC_BIT(50) +#define RAS_STATUS_REG 0x2 +#define RAS_STATUS_SRQ_EMPTY PPC_BIT(8) +#define RAS_STATUS_LSU_QUIESCED PPC_BIT(9) +#define RAS_STATUS_INST_COMPLETE PPC_BIT(12) +#define RAS_STATUS_THREAD_ACTIVE PPC_BIT(48) +#define RAS_STATUS_TS_QUIESCE PPC_BIT(49) +#define POW_STATUS_REG 0x4 +#define PMC_POW_STATE PPC_BITMASK(4, 5) +#define CORE_POW_STATE PPC_BITMASK(23, 25) +#define THREAD_ACTIVE_REG 0x1310e +#define THREAD_ACTIVE PPC_BITMASK(0, 7) +#define RAM_THREAD_ACTIVE PPC_BITMASK(8, 15) +#define SPR_MODE_REG 0x13281 +#define SPR_MODE_SPRC_WR_EN PPC_BIT(3) +#define SPR_MODE_SPRC_SEL PPC_BITMASK(16, 19) +#define SPR_MODE_SPRC_T_SEL PPC_BITMASK(20, 27) +#define L0_SCOM_SPRC_REG 0x13280 +#define SCOM_SPRC_SCRATCH_SPR 0x40 +#define SCR0_REG 0x13283 +#define RAM_MODE_REG 0x13c00 +#define RAM_MODE_ENABLE PPC_BIT(0) +#define RAM_CTRL_REG 0x13c01 +#define RAM_THREAD_SELECT PPC_BITMASK(0, 2) +#define RAM_INSTR PPC_BITMASK(3, 34) +#define RAM_STATUS_REG 0x13c02 +#define RAM_CONTROL_RECOV PPC_BIT(0) +#define RAM_STATUS PPC_BIT(1) +#define RAM_EXCEPTION PPC_BIT(2) +#define LSU_EMPTY PPC_BIT(3) +#define SCOM_EX_GP3 0xf0012 +#define PMSPCWKUPFSP_REG 0xf010d +#define FSP_SPECIAL_WAKEUP PPC_BIT(0) +#define EX_PM_GP0_REG 0xf0100 +#define SPECIAL_WKUP_DONE PPC_BIT(31) + +/* How long (in us) to wait for a special wakeup to complete */ +#define SPECIAL_WKUP_TIMEOUT 10 + +static int assert_special_wakeup(struct chiplet *chip) +{ + int i = 0; + uint64_t gp0; + + /* Assert special wakeup to prevent low power states */ + CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, FSP_SPECIAL_WAKEUP)); + + /* Poll for completion */ + do { + usleep(1); + CHECK_ERR(pib_read(&chip->target, EX_PM_GP0_REG, &gp0)); + + if (i++ > SPECIAL_WKUP_TIMEOUT) { + PR_ERROR("Timeout waiting for special wakeup on %s@0x%08lx\n", chip->target.name, + dt_get_address(chip->target.dn, 0, NULL)); + return -1; + } + } while (!(gp0 & SPECIAL_WKUP_DONE)); + + return 0; +} + +static int deassert_special_wakeup(struct chiplet *chip) +{ + /* Assert special wakeup to prevent low power states */ + CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, 0)); + + return 0; +} + +static uint64_t get_thread_status(struct thread *thread) +{ + uint64_t val, mode_reg, thread_status = thread->status; + + /* Need to activete debug mode to get complete status */ + CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &mode_reg)); + mode_reg |= MR_THREAD_IN_DEBUG; + CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, mode_reg)); + + /* Read status */ + CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &val)); + + thread_status = SETFIELD(THREAD_STATUS_ACTIVE, thread_status, !!(val & RAS_STATUS_THREAD_ACTIVE)); + thread_status = SETFIELD(THREAD_STATUS_QUIESCE, thread_status, !!(val & RAS_STATUS_TS_QUIESCE)); + + /* Read POW status */ + CHECK_ERR(pib_read(&thread->target, POW_STATUS_REG, &val)); + thread_status = SETFIELD(THREAD_STATUS_STATE, thread_status, GETFIELD(PMC_POW_STATE, val)); + + /* Clear debug mode */ + mode_reg &= ~MR_THREAD_IN_DEBUG; + CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, mode_reg)); + + return thread_status; +} + +static int p8_thread_step(struct thread *thread, int count) +{ + int i; + uint64_t ras_mode, ras_status; + + /* Activate single-step mode */ + CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &ras_mode)); + ras_mode |= MR_DO_SINGLE_MODE; + CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode)); + + /* Step the core */ + for (i = 0; i < count; i++) { + CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STEP)); + + /* Wait for step to complete */ + do { + CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &ras_status)); + } while (!(ras_status & RAS_STATUS_INST_COMPLETE)); + } + + /* Deactivate single-step mode */ + ras_mode &= ~MR_DO_SINGLE_MODE; + CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode)); + + return 0; +} + +static int p8_thread_stop(struct thread *thread) +{ + int i = 0; + uint64_t val; + struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); + + do { + /* Quiese active thread */ + CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STOP)); + + /* Wait for thread to quiese */ + CHECK_ERR(pib_read(&chip->target, RAS_STATUS_REG, &val)); + if (i++ > RAS_STATUS_TIMEOUT) { + PR_ERROR("Unable to quiesce thread %d (0x%016llx).\n", + thread->id, val); + PR_ERROR("Continuing anyway.\n"); + if (val & PPC_BIT(48)) { + PR_ERROR("Unable to continue\n"); + } + break; + } + + /* We can continue ramming if either the + * thread is not active or the SRQ/LSU/TS bits + * are set. */ + } while ((val & RAS_STATUS_THREAD_ACTIVE) && + !((val & RAS_STATUS_SRQ_EMPTY) + && (val & RAS_STATUS_LSU_QUIESCED) + && (val & RAS_STATUS_TS_QUIESCE))); + + + /* Make the threads RAM thread active */ + CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val)); + val |= PPC_BIT(8) >> thread->id; + CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val)); + + return 0; +} + +static int p8_thread_start(struct thread *thread) +{ + uint64_t val; + struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); + + /* Activate thread */ + CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_START)); + + /* Restore thread active */ + CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val)); + val &= ~(PPC_BIT(8) >> thread->id); + val |= PPC_BIT(thread->id); + CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val)); + + return 0; +} + +static int p8_ram_setup(struct thread *thread) +{ + struct dt_node *dn; + struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); + uint64_t ram_mode, val; + + /* We can only ram a thread if all the threads on the core/chip are + * quiesced */ + dt_for_each_compatible(chip->target.dn, dn, "ibm,power8-thread") { + struct thread *tmp; + tmp = target_to_thread(dn->target); + if (!(get_thread_status(tmp) & THREAD_STATUS_QUIESCE)) + return 1; + } + + if (!(thread_status(thread) & THREAD_STATUS_ACTIVE)) + return 2; + + /* Activate RAM mode */ + CHECK_ERR(pib_read(&chip->target, RAM_MODE_REG, &ram_mode)); + ram_mode |= RAM_MODE_ENABLE; + CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode)); + + /* Setup SPRC to use SPRD */ + val = SPR_MODE_SPRC_WR_EN; + val = SETFIELD(SPR_MODE_SPRC_SEL, val, 1 << (3 - 0)); + val = SETFIELD(SPR_MODE_SPRC_T_SEL, val, 1 << (7 - thread->id)); + CHECK_ERR(pib_write(&chip->target, SPR_MODE_REG, val)); + CHECK_ERR(pib_write(&chip->target, L0_SCOM_SPRC_REG, SCOM_SPRC_SCRATCH_SPR)); + + return 0; +} + +static int p8_ram_instruction(struct thread *thread, uint64_t opcode, uint64_t *scratch) +{ + struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); + uint64_t val; + + CHECK_ERR(pib_write(&chip->target, SCR0_REG, *scratch)); + + /* ram instruction */ + val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread->id); + val = SETFIELD(RAM_INSTR, val, opcode); + CHECK_ERR(pib_write(&chip->target, RAM_CTRL_REG, val)); + + /* wait for completion */ + do { + CHECK_ERR(pib_read(&chip->target, RAM_STATUS_REG, &val)); + } while (!((val & PPC_BIT(1)) || ((val & PPC_BIT(2)) && (val & PPC_BIT(3))))); + + if (!(val & PPC_BIT(1))) { + if (GETFIELD(PPC_BITMASK(2,3), val) == 0x3) { + return 1; + } else { + PR_ERROR("RAMMING failed with status 0x%llx\n", val); + return 2; + } + } + + /* Save the results */ + CHECK_ERR(pib_read(&chip->target, SCR0_REG, scratch)); +} + +static int p8_ram_destroy(struct thread *thread) +{ + struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); + uint64_t ram_mode; + + /* Disable RAM mode */ + CHECK_ERR(pib_read(&chip->target, RAM_MODE_REG, &ram_mode)); + ram_mode &= ~RAM_MODE_ENABLE; + CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode)); +} + +/* + * Initialise all viable threads for ramming on the given chiplet. + */ +static int p8_thread_probe(struct target *target) +{ + struct thread *thread = target_to_thread(target); + + thread->id = (dt_get_address(target->dn, 0, NULL) >> 4) & 0xf; + thread->status = get_thread_status(thread); + + return 0; +} + +struct thread p8_thread = { + .target = { + .name = "POWER8 Thread", + .compatible = "ibm,power8-thread", + .class = "thread", + .probe = p8_thread_probe, + }, + .step = p8_thread_step, + .start = p8_thread_start, + .stop = p8_thread_stop, + .ram_setup = p8_ram_setup, + .ram_instruction = p8_ram_instruction, + .ram_destroy = p8_ram_destroy, +}; +DECLARE_HW_UNIT(p8_thread); + +static int p8_chiplet_probe(struct target *target) +{ + uint64_t value; + struct chiplet *chiplet = target_to_chiplet(target); + int i, count = 0, rc = 0; + + /* Work out if this chip is actually present */ + if (pib_read(target, SCOM_EX_GP3, &value)) { + PR_DEBUG("Error reading chip GP3 register\n"); + return -1; + } + + if (!GETFIELD(PPC_BIT(0), value)) + return -1; + + assert_special_wakeup(chiplet); + return 0; +} + +struct chiplet p8_chiplet = { + .target = { + .name = "POWER8 Chiplet", + .compatible = "ibm,power8-core", + .class = "chiplet", + .probe = p8_chiplet_probe, + }, +}; +DECLARE_HW_UNIT(p8_chiplet); diff --git a/libpdbg/p9chip.c b/libpdbg/p9chip.c new file mode 100644 index 0000000..ed0fdeb --- /dev/null +++ b/libpdbg/p9chip.c @@ -0,0 +1,240 @@ +/* Copyright 2016 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. + */ +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "target.h" +#include "operations.h" +#include "bitutils.h" + +#define P9_RAS_STATUS 0x10a02 +#define P9_THREAD_INFO 0x10a9b +#define P9_DIRECT_CONTROL 0x10a9c +#define P9_RAS_MODEREG 0x10a9d +#define P9_RAM_MODEREG 0x10a4e +#define P9_RAM_CTRL 0x10a4f +#define P9_RAM_STATUS 0x10a50 +#define P9_SCOMC 0x10a80 +#define P9_SPR_MODE 0x10a84 +#define P9_SCR0_REG 0x10a86 + +/* PCB Slave Registers */ +#define NET_CTRL0 0xf0040 +#define NET_CTRL0_CHIPLET_ENABLE PPC_BIT(0) +#define PPM_GPMMR 0xf0100 +#define PPM_SPWKUP_OTR 0xf010a +#define SPECIAL_WKUP_DONE PPC_BIT(1) + +#define RAS_STATUS_TIMEOUT 100 +#define SPECIAL_WKUP_TIMEOUT 10 + +static uint64_t thread_read(struct thread *thread, uint64_t addr, uint64_t *data) +{ + struct target *chip = require_target_parent(&thread->target); + + return pib_read(chip, addr, data); +} + +static uint64_t thread_write(struct thread *thread, uint64_t addr, uint64_t data) +{ + struct target *chip = require_target_parent(&thread->target); + + return pib_write(chip, addr, data); +} + +static uint64_t p9_get_thread_status(struct thread *thread) +{ + uint64_t value, status = THREAD_STATUS_ACTIVE; + + thread_read(thread, P9_RAS_STATUS, &value); + if (GETFIELD(PPC_BITMASK(8*thread->id, 3 + 8*thread->id), value) == 0xf) + status |= THREAD_STATUS_QUIESCE; + + return status; +} + +static int p9_thread_probe(struct target *target) +{ + struct thread *thread = target_to_thread(target); + uint64_t value; + + thread->id = dt_prop_get_u32(target->dn, "tid"); + thread->status = p9_get_thread_status(thread); + + return 0; +} + +static int p9_thread_start(struct thread *thread) +{ + uint64_t value; + + thread_write(thread, P9_DIRECT_CONTROL, PPC_BIT(6 + 8*thread->id)); + thread_write(thread, P9_RAS_MODEREG, 0); + + return 0; +} + +static int p9_thread_stop(struct thread *thread) +{ + int i = 0; + uint64_t value; + + thread_write(thread, P9_DIRECT_CONTROL, PPC_BIT(7 + 8*thread->id)); + while(!(p9_get_thread_status(thread) & THREAD_STATUS_QUIESCE)) { + if (i++ > RAS_STATUS_TIMEOUT) { + PR_ERROR("Unable to quiesce thread\n"); + break; + } + } + + /* Fence interrupts. We can't do a read-modify-write here due to an + * errata */ + thread_write(thread, P9_RAS_MODEREG, PPC_BIT(57)); + + return 0; +} + +static int p9_ram_setup(struct thread *thread) +{ + struct dt_node *dn; + struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); + uint64_t ras_status; + + /* We can only ram a thread if all the threads on the core/chip are + * quiesced */ + dt_for_each_compatible(chip->target.dn, dn, "ibm,power9-thread") { + struct thread *tmp; + + /* If this thread wasn't enabled it may not yet have been probed + so do that now. This will also update the thread status */ + p9_thread_probe(dn->target); + tmp = target_to_thread(dn->target); + if (tmp->status != (THREAD_STATUS_QUIESCE | THREAD_STATUS_ACTIVE)) + return 1; + } + + /* Enable ram mode */ + CHECK_ERR(thread_write(thread, P9_RAM_MODEREG, PPC_BIT(0))); + + /* Setup SPRC to use SPRD */ + CHECK_ERR(thread_write(thread, P9_SPR_MODE, 0x00000ff000000000)); + CHECK_ERR(thread_write(thread, P9_SCOMC, 0x0)); + + return 0; +} + +static int p9_ram_instruction(struct thread *thread, uint64_t opcode, uint64_t *scratch) +{ + struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target); + uint64_t predecode, value; + + switch(opcode & OPCODE_MASK) { + case MTNIA_OPCODE: + predecode = 8; + + /* Not currently supported as we can only MTNIA from LR */ + PR_ERROR("MTNIA is not currently supported\n"); + break; + + case MFNIA_OPCODE: + opcode = 0x1ac804; + predecode = 2; + break; + + case MTMSR_OPCODE: + predecode = 8; + break; + + default: + predecode = 0; + } + + CHECK_ERR(thread_write(thread, P9_SCR0_REG, *scratch)); + value = SETFIELD(PPC_BITMASK(0, 1), 0ull, thread->id); + value = SETFIELD(PPC_BITMASK(2, 5), value, predecode); + value = SETFIELD(PPC_BITMASK(8, 39), value, opcode); + CHECK_ERR(thread_write(thread, P9_RAM_CTRL, value)); + do { + CHECK_ERR(thread_read(thread, P9_RAM_STATUS, &value)); + if (((value & PPC_BIT(0)) || (value & PPC_BIT(2)))) + return 1; + } while (!(value & PPC_BIT(1) && !(value & PPC_BIT(3)))); + CHECK_ERR(thread_read(thread, P9_SCR0_REG, scratch)); + + return 0; +} + +static int p9_ram_destroy(struct thread *thread) +{ + /* Disable ram mode */ + CHECK_ERR(thread_write(thread, P9_RAM_MODEREG, 0)); + + return 0; +} + +struct thread p9_thread = { + .target = { + .name = "POWER9 Thread", + .compatible = "ibm,power9-thread", + .class = "thread", + .probe = p9_thread_probe, + }, + .start = p9_thread_start, + .stop = p9_thread_stop, + .ram_setup = p9_ram_setup, + .ram_instruction = p9_ram_instruction, + .ram_destroy = p9_ram_destroy, +}; +DECLARE_HW_UNIT(p9_thread); + +static int p9_chiplet_probe(struct target *target) +{ + int i; + uint64_t value; + + if (pib_read(target, NET_CTRL0, &value)) + return -1; + + if (!(value & NET_CTRL0_CHIPLET_ENABLE)) + return -1; + + CHECK_ERR(pib_write(target, PPM_SPWKUP_OTR, PPC_BIT(0))); + do { + usleep(1); + CHECK_ERR(pib_read(target, PPM_GPMMR, &value)); + + if (i++ > SPECIAL_WKUP_TIMEOUT) { + PR_ERROR("Timeout waiting for special wakeup on %s@0x%08lx\n", target->name, + dt_get_address(target->dn, 0, NULL)); + return -1; + } + } while (!(value & SPECIAL_WKUP_DONE)); + + return 0; +} + +struct chiplet p9_chiplet = { + .target = { + .name = "POWER9 Chiplet", + .compatible = "ibm,power9-core", + .class = "chiplet", + .probe = p9_chiplet_probe, + }, +}; +DECLARE_HW_UNIT(p9_chiplet); diff --git a/libpdbg/target.c b/libpdbg/target.c index c733dad..7642e18 100644 --- a/libpdbg/target.c +++ b/libpdbg/target.c @@ -37,22 +37,28 @@ int pib_read(struct target *pib_dt, uint64_t addr, uint64_t *data) { struct pib *pib; struct dt_node *dn = pib_dt->dn; + int rc; dn = get_class_target_addr(dn, "pib", &addr); pib_dt = dn->target; pib = target_to_pib(pib_dt); - return pib->read(pib, addr, data); + rc = pib->read(pib, addr, data); +// printf("pib_read 0x%016llx = 0x%016llx\n", addr, *data); + return rc; } int pib_write(struct target *pib_dt, uint64_t addr, uint64_t data) { struct pib *pib; struct dt_node *dn = pib_dt->dn; + int rc; dn = get_class_target_addr(dn, "pib", &addr); pib_dt = dn->target; pib = target_to_pib(pib_dt); - return pib->write(pib, addr, data); + rc = pib->write(pib, addr, data); +// printf("pib_write 0x%016llx = 0x%016llx\n", addr, data); + return rc; } int opb_read(struct target *opb_dt, uint32_t addr, uint32_t *data) @@ -105,6 +111,14 @@ int fsi_write(struct target *fsi_dt, uint32_t addr, uint32_t data) return fsi->write(fsi, addr64, data); } +struct target *require_target_parent(struct target *target) +{ + struct dt_node *dn; + + assert((dn = target->dn)); + return dn->parent->target; +} + /* Finds the given class. Returns NULL if not found. */ struct target_class *find_target_class(const char *name) { diff --git a/libpdbg/target.h b/libpdbg/target.h index 651ab3c..ba91cf5 100644 --- a/libpdbg/target.h +++ b/libpdbg/target.h @@ -47,6 +47,7 @@ struct target { struct list_node class_link; }; +struct target *require_target_parent(struct target *target); struct target_class *find_target_class(const char *name); struct target_class *require_target_class(const char *name); @@ -108,6 +109,16 @@ struct thread { struct target target; uint64_t status; int id; + int (*step)(struct thread *, int); + int (*start)(struct thread *); + int (*stop)(struct thread *); + + /* ram_setup() should be called prior to using ram_instruction() to + * actually ram the instruction and return the result. ram_destroy() + * should be called at completion to clean-up. */ + int (*ram_setup)(struct thread *); + int (*ram_instruction)(struct thread *, uint64_t opcode, uint64_t *scratch); + int (*ram_destroy)(struct thread *); }; #define target_to_thread(x) container_of(x, struct thread, target) |