diff options
author | Alistair Popple <alistair@popple.id.au> | 2016-09-16 16:45:39 +1000 |
---|---|---|
committer | Alistair Popple <alistair@popple.id.au> | 2016-10-25 13:32:35 +1100 |
commit | 749351433237295a86afcf451b947bc6621b946f (patch) | |
tree | 4141847a38ed325dfa7d94751ad34a899301a7ea /libpdbg | |
parent | e622883810266bcbc09ab5e1bae6a137dcda74c7 (diff) | |
download | pdbg-749351433237295a86afcf451b947bc6621b946f.tar.gz pdbg-749351433237295a86afcf451b947bc6621b946f.zip |
Add support for i2c access to secondary processors
This includes better support for selecting targets, probing thread
status and a number of other minor bugfixes. Also adds an option to
print version numbers.
Signed-off-by: Alistair Popple <alistair@popple.id.au>
Diffstat (limited to 'libpdbg')
-rw-r--r-- | libpdbg/adu.c | 36 | ||||
-rw-r--r-- | libpdbg/bmcfsi.c | 152 | ||||
-rw-r--r-- | libpdbg/cfam.c | 261 | ||||
-rw-r--r-- | libpdbg/chip.c | 510 | ||||
-rw-r--r-- | libpdbg/fsi2pib.c | 76 | ||||
-rw-r--r-- | libpdbg/i2c.c | 70 | ||||
-rw-r--r-- | libpdbg/operations.h | 59 | ||||
-rw-r--r-- | libpdbg/ram.c | 306 | ||||
-rw-r--r-- | libpdbg/scom.c | 103 | ||||
-rw-r--r-- | libpdbg/target.c | 68 | ||||
-rw-r--r-- | libpdbg/target.h | 57 |
11 files changed, 1106 insertions, 592 deletions
diff --git a/libpdbg/adu.c b/libpdbg/adu.c index b8c9478..75cd829 100644 --- a/libpdbg/adu.c +++ b/libpdbg/adu.c @@ -62,26 +62,26 @@ #define FBC_ALTD_DATA_DONE PPC_BIT(3) #define FBC_ALTD_PBINIT_MISSING PPC_BIT(18) -static int adu_lock(void) +static int adu_lock(struct target *target) { uint64_t val; - CHECK_ERR(getscom(&val, ALTD_CMD_REG)); + CHECK_ERR(read_target(target, ALTD_CMD_REG, &val)); if (val & FBC_LOCKED) PR_INFO("ADU already locked! Ignoring.\n"); val |= FBC_LOCKED; - CHECK_ERR(putscom(val, ALTD_CMD_REG)); + CHECK_ERR(write_target(target, ALTD_CMD_REG, val)); return 0; } -static int adu_unlock(void) +static int adu_unlock(struct target *target) { uint64_t val; - CHECK_ERR(getscom(&val, ALTD_CMD_REG)); + CHECK_ERR(read_target(target, ALTD_CMD_REG, &val)); if (!(val & FBC_LOCKED)) { PR_INFO("ADU already unlocked!\n"); @@ -89,36 +89,36 @@ static int adu_unlock(void) } val &= ~FBC_LOCKED; - CHECK_ERR(putscom(val, ALTD_CMD_REG)); + CHECK_ERR(write_target(target, ALTD_CMD_REG, val)); return 0; } -static int adu_reset(void) +static int adu_reset(struct target *target) { uint64_t val; - CHECK_ERR(getscom(&val, ALTD_CMD_REG)); + CHECK_ERR(read_target(target, ALTD_CMD_REG, &val)); val |= FBC_ALTD_CLEAR_STATUS | FBC_ALTD_RESET_AD_PCB; - CHECK_ERR(putscom(val, ALTD_CMD_REG)); + CHECK_ERR(write_target(target, ALTD_CMD_REG, val)); return 0; } /* Return size bytes of memory in *output. *output must point to an * array large enough to hold size bytes. */ -int adu_getmem(uint64_t start_addr, uint8_t *output, uint64_t size) +int adu_getmem(struct target *target, uint64_t start_addr, uint8_t *output, uint64_t size) { int rc = 0; uint64_t addr, cmd_reg, ctrl_reg, val; - CHECK_ERR(adu_lock()); + CHECK_ERR(adu_lock(target)); ctrl_reg = TTYPE_TREAD; ctrl_reg = SETFIELD(FBC_ALTD_TTYPE, ctrl_reg, TTYPE_DMA_PARTIAL_READ); ctrl_reg = SETFIELD(FBC_ALTD_TSIZE, ctrl_reg, 8); - CHECK_ERR(getscom(&cmd_reg, ALTD_CMD_REG)); + CHECK_ERR(read_target(target, ALTD_CMD_REG, &cmd_reg)); cmd_reg |= FBC_ALTD_START_OP; cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_SYSTEM); cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_MEDIUM); @@ -129,18 +129,18 @@ int adu_getmem(uint64_t start_addr, uint8_t *output, uint64_t size) retry: /* Clear status bits */ - CHECK_ERR(adu_reset()); + CHECK_ERR(adu_reset(target)); /* Set the address */ ctrl_reg = SETFIELD(FBC_ALTD_ADDRESS, ctrl_reg, addr); - CHECK_ERR(putscom(ctrl_reg, ALTD_CONTROL_REG)); + CHECK_ERR(write_target(target, ALTD_CONTROL_REG, ctrl_reg)); /* Start the command */ - CHECK_ERR(putscom(cmd_reg, ALTD_CMD_REG)); + CHECK_ERR(write_target(target, ALTD_CMD_REG, cmd_reg)); /* Wait for completion */ do { - CHECK_ERR(getscom(&val, ALTD_STATUS_REG)); + CHECK_ERR(read_target(target, ALTD_STATUS_REG, &val)); } while (!val); if( !(val & FBC_ALTD_ADDR_DONE) || @@ -157,7 +157,7 @@ int adu_getmem(uint64_t start_addr, uint8_t *output, uint64_t size) } /* Read data */ - CHECK_ERR(getscom(&data, ALTD_DATA_REG)); + CHECK_ERR(read_target(target, ALTD_DATA_REG, &data)); /* ADU returns data in big-endian form in the register */ data = __builtin_bswap64(data); @@ -174,7 +174,7 @@ int adu_getmem(uint64_t start_addr, uint8_t *output, uint64_t size) } - adu_unlock(); + adu_unlock(target); return rc; } diff --git a/libpdbg/bmcfsi.c b/libpdbg/bmcfsi.c index 7a33b96..13f32b6 100644 --- a/libpdbg/bmcfsi.c +++ b/libpdbg/bmcfsi.c @@ -22,9 +22,11 @@ #include <sys/stat.h> #include <sys/mman.h> #include <time.h> +#include <assert.h> #include "bitutils.h" #include "operations.h" +#include "target.h" #define FSI_CLK 4 //GPIOA4 #define FSI_DAT 5 //GPIOA5 @@ -78,7 +80,8 @@ enum fsi_result { /* FSI private data */ static void *gpio_reg = NULL; static int mem_fd = 0; -static int slave = 0; + +static void fsi_reset(struct target *target); static uint32_t readl(void *addr) { @@ -211,14 +214,14 @@ static inline void fsi_send_bit(uint64_t bit) } /* Format a CFAM address into an FSI slaveId, command and address. */ -static uint64_t fsi_abs_ar(uint8_t slave_id, int processor_id, uint32_t addr, int read) +static uint64_t fsi_abs_ar(uint32_t addr, int read) { - addr |= 0x80000 * processor_id; + uint32_t slave_id = (addr >> 21) & 0x3; /* Reformat the address. I'm not sure I fully understand this * yet but we basically shift the bottom byte and add 0b01 * (for the write word?) */ - addr = ((addr & 0x3fff00) | ((addr & 0xff) << 2)) << 1; + addr = ((addr & 0x1fff00) | ((addr & 0xff) << 2)) << 1; addr |= 0x3; addr |= slave_id << 26; addr |= (0x8ULL | !!(read)) << 22; @@ -240,7 +243,6 @@ static void fsi_break(void) /* Crank things - not sure if we need this yet */ write_gpio(FSI_CLK, 1); write_gpio(FSI_DAT, 1); /* Data standby state */ - clock_cycle(FSI_CLK, 5000); /* Send break command */ write_gpio(FSI_DAT, 0); @@ -352,13 +354,15 @@ static enum fsi_result fsi_d_poll_wait(uint8_t slave_id, uint64_t *resp, int len return rc; } -static int fsi_getcfam(struct scom_backend *backend, int processor_id, - uint32_t *value, uint32_t addr) +static int fsi_getcfam(struct target *target, uint64_t addr, uint64_t *value) { uint64_t seq; uint64_t resp; enum fsi_result rc; + /* This must be the last target in a chain */ + assert(!target->next); + /* Format of the read sequence is: * 6666555555555544444444443333333333222222222211111111110000000000 * 3210987654321098765432109876543210987654321098765432109876543210 @@ -366,7 +370,7 @@ static int fsi_getcfam(struct scom_backend *backend, int processor_id, * ii1001aaaaaaaaaaaaaaaaaaa011cccc * * Where: - * ii = slaveId (hardcoded to 11 for the moment) + * ii = slaveId * a = address bit * 011 = write word size * d = data bit @@ -375,28 +379,31 @@ static int fsi_getcfam(struct scom_backend *backend, int processor_id, * When applying the sequence it should be inverted (active * low) */ - seq = fsi_abs_ar(slave, processor_id, addr, 1) << 36; + seq = fsi_abs_ar(addr, 1) << 36; fsi_send_seq(seq, 28); if ((rc = fsi_read_resp(&resp, 36)) == FSI_BUSY) - rc = fsi_d_poll_wait(slave, &resp, 36); + rc = fsi_d_poll_wait(0, &resp, 36); if (rc != FSI_ACK) { - PR_ERROR("getcfam error. Response: 0x%01x\n", rc); + PR_DEBUG("getcfam error. Response: 0x%01x\n", rc); rc = -1; } *value = resp & 0xffffffff; + return rc; } -static int fsi_putcfam(struct scom_backend *backend, int processor_id, - uint32_t data, uint32_t addr) +static int fsi_putcfam(struct target *target, uint64_t addr, uint64_t data) { uint64_t seq; uint64_t resp; enum fsi_result rc; + /* This must be the last target in a chain */ + assert(!target->next); + /* Format of the sequence is: * 6666555555555544444444443333333333222222222211111111110000000000 * 3210987654321098765432109876543210987654321098765432109876543210 @@ -404,7 +411,7 @@ static int fsi_putcfam(struct scom_backend *backend, int processor_id, * ii1000aaaaaaaaaaaaaaaaaaa011ddddddddddddddddddddddddddddddddcccc * * Where: - * ii = slaveId (hardcoded to 11 for the moment) + * ii = slaveId * a = address bit * 011 = write word size * d = data bit @@ -413,52 +420,37 @@ static int fsi_putcfam(struct scom_backend *backend, int processor_id, * When applying the sequence it should be inverted (active * low) */ - seq = fsi_abs_ar(slave, processor_id, addr, 0) << 36; + seq = fsi_abs_ar(addr, 0) << 36; seq |= ((uint64_t) data & 0xffffffff) << (4); fsi_send_seq(seq, 60); if ((rc = fsi_read_resp(&resp, 4)) == FSI_BUSY) - rc = fsi_d_poll_wait(slave, &resp, 4); + rc = fsi_d_poll_wait(0, &resp, 4); - if (rc != FSI_ACK) { + if (rc != FSI_ACK) PR_DEBUG("putcfam error. Response: 0x%01x\n", rc); - } else + else rc = 0; return rc; } -static int fsi_getscom(struct scom_backend *backend, int processor_id, - uint64_t *value, uint32_t addr) +static void fsi_reset(struct target *target) { - uint32_t result; + uint64_t val64, old_base = target->base; - usleep(FSI2PIB_RELAX); + target->base = 0; - /* Get scom works by putting the address in FSI_CMD_REG and - * reading the result from FST_DATA[01]_REG. */ - CHECK_ERR(fsi_putcfam(backend, processor_id, addr, FSI_CMD_REG)); - CHECK_ERR(fsi_getcfam(backend, processor_id, &result, FSI_DATA0_REG)); - *value = (uint64_t) result << 32; - CHECK_ERR(fsi_getcfam(backend, processor_id, &result, FSI_DATA1_REG)); - *value |= result; - return 0; -} - -static int fsi_putscom(struct scom_backend *backend, int processor_id, - uint64_t value, uint32_t addr) -{ - usleep(FSI2PIB_RELAX); - - CHECK_ERR(fsi_putcfam(backend, processor_id, FSI_RESET_CMD, FSI_RESET_REG)); - CHECK_ERR(fsi_putcfam(backend, processor_id, (value >> 32) & 0xffffffff, FSI_DATA0_REG)); - CHECK_ERR(fsi_putcfam(backend, processor_id, value & 0xffffffff, FSI_DATA1_REG)); - CHECK_ERR(fsi_putcfam(backend, processor_id, FSI_CMD_REG_WRITE | addr, FSI_CMD_REG)); + fsi_break(); - return 0; + /* Clear own id on the master CFAM to access hMFSI ports */ + fsi_getcfam(target, 0x800, &val64); + val64 &= ~(PPC_BIT32(6) | PPC_BIT32(7)); + fsi_putcfam(target, 0x800, val64); + target->base = old_base; } -static void fsi_destroy(struct scom_backend *backend) +static void fsi_destroy(struct target *target) { set_direction_out(FSI_CLK); set_direction_out(FSI_DAT); @@ -475,59 +467,39 @@ static void fsi_destroy(struct scom_backend *backend) write_gpio(CRONUS_SEL, 0); //Set Cronus control to FSP2 } -struct scom_backend *fsi_init(void) +int fsi_target_init(struct target *target, const char *name, uint64_t base, struct target *next) { - int i; - uint32_t val; - uint64_t val64; - struct scom_backend *backend; - - if (gpio_reg) { - /* FIXME .... */ - PR_ERROR("One a single instance of this backend is supported\n"); - return NULL; - } - - backend = malloc(sizeof(*backend)); - if (!backend) - return NULL; - - mem_fd = open("/dev/mem", O_RDWR | O_SYNC); - if (mem_fd < 0) { - perror("Unable to open /dev/mem"); - exit(1); + if (!mem_fd) { + mem_fd = open("/dev/mem", O_RDWR | O_SYNC); + if (mem_fd < 0) { + perror("Unable to open /dev/mem"); + exit(1); + } } - gpio_reg = mmap(NULL, getpagesize(), - PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GPIO_BASE); - if (gpio_reg == MAP_FAILED) { - perror("Unable to map GPIO register memory"); - exit(1); - } - - set_direction_out(CRONUS_SEL); - set_direction_out(FSI_ENABLE); - set_direction_out(FSI_DAT_EN); + if (!gpio_reg) { + /* We only have to do this init once per backend */ + gpio_reg = mmap(NULL, getpagesize(), + PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GPIO_BASE); + if (gpio_reg == MAP_FAILED) { + perror("Unable to map GPIO register memory"); + exit(1); + } - write_gpio(FSI_ENABLE, 1); - write_gpio(CRONUS_SEL, 1); //Set Cronus control to BMC + set_direction_out(CRONUS_SEL); + set_direction_out(FSI_ENABLE); + set_direction_out(FSI_DAT_EN); - slave = 0; - backend->getscom = fsi_getscom; - backend->putscom = fsi_putscom; - backend->getcfam = fsi_getcfam; - backend->putcfam = fsi_putcfam; - backend->destroy = fsi_destroy; - backend->priv = NULL; + write_gpio(FSI_ENABLE, 1); + write_gpio(CRONUS_SEL, 1); //Set Cronus control to BMC - fsi_break(); + fsi_reset(target); + } - /* Clear own id on the master CFAM to access hMFSI ports */ - if (fsi_getcfam(backend, 0, &val, 0x800)) - return NULL; - val &= ~(PPC_BIT32(6) | PPC_BIT32(7)); - if (fsi_putcfam(backend, 0, val, 0x800)) - return NULL; + /* No cascaded devices after this one. */ + assert(next == NULL); + target_init(target, name, base, fsi_getcfam, fsi_putcfam, fsi_destroy, + next); - return backend; + return 0; } diff --git a/libpdbg/cfam.c b/libpdbg/cfam.c new file mode 100644 index 0000000..51d0b84 --- /dev/null +++ b/libpdbg/cfam.c @@ -0,0 +1,261 @@ +/* Copyright 2016 IBM Corp. + * + * Most of the PIB2OPB code is based on code from skiboot. + * + * 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 <unistd.h> +#include <stdio.h> + +#include "target.h" +#include "bitutils.h" +#include "operations.h" + +#define FSI_DATA0_REG 0x0 +#define FSI_DATA1_REG 0x1 +#define FSI_CMD_REG 0x2 +#define FSI_CMD_REG_WRITE PPC_BIT32(0) + +#define FSI_RESET_REG 0x6 +#define FSI_RESET_CMD PPC_BIT32(0) + +#define FSI_SET_PIB_RESET_REG 0x7 +#define FSI_SET_PIB_RESET PPC_BIT32(0) + +/* For some reason the FSI2PIB engine dies with frequent + * access. Letting it have a bit of a rest seems to stop the + * problem. This sets the number of usecs to sleep between SCOM + * accesses. */ +#define FSI2PIB_RELAX 50 + +/* + * Bridge registers on XSCOM that allow generatoin + * of OPB cycles + */ +#define PIB2OPB_REG_CMD 0x0 +#define OPB_CMD_WRITE 0x80000000 +#define OPB_CMD_READ 0x00000000 +#define OPB_CMD_8BIT 0x00000000 +#define OPB_CMD_16BIT 0x20000000 +#define OPB_CMD_32BIT 0x60000000 +#define PIB2OPB_REG_STAT 0x1 +#define OPB_STAT_ANY_ERR 0x80000000 +#define OPB_STAT_ERR_OPB 0x7FEC0000 +#define OPB_STAT_ERRACK 0x00100000 +#define OPB_STAT_BUSY 0x00010000 +#define OPB_STAT_READ_VALID 0x00020000 +#define OPB_STAT_ERR_CMFSI 0x0000FC00 +#define OPB_STAT_ERR_HMFSI 0x000000FC +#define OPB_STAT_ERR_BASE (OPB_STAT_ANY_ERR | \ + OPB_STAT_ERR_OPB | \ + OPB_STAT_ERRACK) +#define PIB2OPB_REG_LSTAT 0x2 +#define PIB2OPB_REG_RESET 0x4 +#define PIB2OPB_REG_cRSIC 0x5 +#define PIB2OPB_REG_cRSIM 0x6 +#define PIB2OPB_REG_cRSIS 0x7 +#define PIB2OPB_REG_hRSIC 0x8 +#define PIB2OPB_REG_hRSIM 0x9 +#define PIB2OPB_REG_hRSIS 0xA + +#define OPB_ERR_XSCOM_ERR -1; +#define OPB_ERR_TIMEOUT_ERR -1; +#define OPB_ERR_BAD_OPB_ADDR -1; + +/* We try up to 1.2ms for an OPB access */ +#define MFSI_OPB_MAX_TRIES 1200 + +static int fsi2pib_getscom(struct target *target, uint64_t addr, uint64_t *value) +{ + uint64_t result; + + usleep(FSI2PIB_RELAX); + + /* Get scom works by putting the address in FSI_CMD_REG and + * reading the result from FST_DATA[01]_REG. */ + CHECK_ERR(write_next_target(target, FSI_RESET_REG, FSI_RESET_CMD)); + CHECK_ERR(write_next_target(target, FSI_CMD_REG, addr)); + CHECK_ERR(read_next_target(target, FSI_DATA0_REG, &result)); + *value = result << 32; + CHECK_ERR(read_next_target(target, FSI_DATA1_REG, &result)); + *value |= result; + + return 0; +} + +static int fsi2pib_putscom(struct target *target, uint64_t addr, uint64_t value) +{ + usleep(FSI2PIB_RELAX); + + CHECK_ERR(write_next_target(target, FSI_RESET_REG, FSI_RESET_CMD)); + CHECK_ERR(write_next_target(target, FSI_DATA0_REG, (value >> 32) & 0xffffffff)); + CHECK_ERR(write_next_target(target, FSI_DATA1_REG, value & 0xffffffff)); + CHECK_ERR(write_next_target(target, FSI_CMD_REG, FSI_CMD_REG_WRITE | addr)); + + return 0; +} + +int fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next) +{ + target_init(target, name, base, fsi2pib_getscom, fsi2pib_putscom, NULL, next); + + return 0; +} + +static uint64_t opb_poll(struct target *target, uint64_t *read_data) +{ + unsigned long retries = MFSI_OPB_MAX_TRIES; + uint64_t sval; + uint32_t stat; + int64_t rc; + + /* We try again every 10us for a bit more than 1ms */ + for (;;) { + /* Read OPB status register */ + rc = read_next_target(target, PIB2OPB_REG_STAT, &sval); + if (rc) { + /* Do something here ? */ + PR_ERROR("XSCOM error %lld read OPB STAT\n", rc); + return -1; + } + PR_DEBUG(" STAT=0x%16llx...\n", sval); + + stat = sval >> 32; + + /* Complete */ + if (!(stat & OPB_STAT_BUSY)) + break; + if (retries-- == 0) { + /* This isn't supposed to happen (HW timeout) */ + PR_ERROR("OPB POLL timeout !\n"); + return -1; + } + usleep(1); + } + + /* + * TODO: Add the full error analysis that skiboot has. For now + * we just reset things so we can continue. Also need to + * improve error handling as we expect these occasionally when + * probing the system. + */ + if (stat & OPB_STAT_ANY_ERR) { + write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0)); + write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0)); + PR_DEBUG("OPB Error. Status 0x%08x\n", stat); + rc = -1; + } else if (read_data) { + if (!(stat & OPB_STAT_READ_VALID)) { + PR_DEBUG("Read successful but no data !\n"); + rc = -1; + } + *read_data = sval & 0xffffffff; + } + + return rc; +} + +static int opb_read(struct target *target, uint64_t addr, uint64_t *data) +{ + uint64_t opb_cmd = OPB_CMD_READ | OPB_CMD_32BIT; + int64_t rc; + + if (addr > 0x00ffffff) + return OPB_ERR_BAD_OPB_ADDR; + + /* Turn the address into a byte address */ + addr = (addr & 0xffff00) | ((addr & 0xff) << 2); + opb_cmd |= addr; + opb_cmd <<= 32; + + PR_DEBUG("MFSI_OPB_READ: Writing 0x%16llx to XSCOM %llx\n", + opb_cmd, target->base); + + rc = write_next_target(target, PIB2OPB_REG_CMD, opb_cmd); + if (rc) { + PR_ERROR("XSCOM error %lld writing OPB CMD\n", rc); + return OPB_ERR_XSCOM_ERR; + } + return opb_poll(target, data); +} + +static int opb_write(struct target *target, uint64_t addr, uint64_t data) +{ + uint64_t opb_cmd = OPB_CMD_WRITE | OPB_CMD_32BIT; + int64_t rc; + + if (addr > 0x00ffffff) + return OPB_ERR_BAD_OPB_ADDR; + + addr = (addr & 0xffff00) | ((addr & 0xff) << 2); + opb_cmd |= addr; + opb_cmd <<= 32; + opb_cmd |= data; + + PR_DEBUG("MFSI_OPB_WRITE: Writing 0x%16llx to XSCOM %llx\n", + opb_cmd, target->base); + + rc = write_next_target(target, PIB2OPB_REG_CMD, opb_cmd); + if (rc) { + PR_ERROR("XSCOM error %lld writing OPB CMD\n", rc); + return OPB_ERR_XSCOM_ERR; + } + return opb_poll(target, NULL); +} + +int opb_target_init(struct target *target, const char *name, uint64_t base, struct target *next) +{ + target_init(target, name, base, opb_read, opb_write, NULL, next); + + /* Clear any outstanding errors */ + write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0)); + write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0)); + + return 0; +} + +int mfsi_target_init(struct target *target, const char *name, uint64_t base, struct target *next) +{ + target_init(target, name, base, NULL, NULL, NULL, next); + + return 0; +} + +#define HMFSI_STRIDE 0x80000 +int hmfsi_target_probe(struct target *cfam, struct target *targets, int max_target_count) +{ + struct target *target = targets; + uint64_t value, chip_id; + int target_count = 0, i; + + for (i = 0; i < 8 && i < max_target_count; i++) { + mfsi_target_init(target, "MFSI Port", 0x80000 + i * HMFSI_STRIDE, cfam); + if (read_target(target, 0xc09, &value)) { + target_del(target); + continue; + } + + /* Ignore unknown chip ids */ + chip_id = GETFIELD(PPC_BITMASK32(12, 19), value); + if (chip_id != 0xea && chip_id != 0xd3) { + target_del(target); + continue; + } + + target++; + target_count++; + } + + return target_count; +} diff --git a/libpdbg/chip.c b/libpdbg/chip.c new file mode 100644 index 0000000..9a0b963 --- /dev/null +++ b/libpdbg/chip.c @@ -0,0 +1,510 @@ +/* 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 "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 1000 + +/* 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 + +static uint64_t mfspr(uint64_t reg, uint64_t spr) +{ + if (reg > 31) + PR_ERROR("Invalid register specified\n"); + + return MFSPR_OPCODE | (reg << 21) | ((spr & 0x1f) << 16) | ((spr & 0x3e0) << 6); +} + +static uint64_t mtspr(uint64_t spr, uint64_t reg) +{ + if (reg > 31) + PR_ERROR("Invalid register specified\n"); + + return MTSPR_OPCODE | (reg << 21) | ((spr & 0x1f) << 16) | ((spr & 0x3e0) << 6); +} + +static uint64_t mfnia(uint64_t reg) +{ + if (reg > 31) + PR_ERROR("Invalid register specified\n"); + + return MFNIA_OPCODE | (reg << 21); +} + +static uint64_t mtnia(uint64_t reg) +{ + if (reg > 31) + PR_ERROR("Invalid register specified\n"); + + return MTNIA_OPCODE | (reg << 21); +} + +static uint64_t mfmsr(uint64_t reg) +{ + if (reg > 31) + PR_ERROR("Invalid register specified\n"); + + return MFMSR_OPCODE | (reg << 21); +} + +static uint64_t mtmsr(uint64_t reg) +{ + if (reg > 31) + PR_ERROR("Invalid register specified\n"); + + return MTMSR_OPCODE | (reg << 21); +} + +static int assert_special_wakeup(struct target *chip) +{ + int i = 0; + uint64_t gp0; + + /* Assert special wakeup to prevent low power states */ + CHECK_ERR(write_target(chip, PMSPCWKUPFSP_REG, FSP_SPECIAL_WAKEUP)); + + /* Poll for completion */ + do { + usleep(1); + CHECK_ERR(read_target(chip, EX_PM_GP0_REG, &gp0)); + + if (i++ > SPECIAL_WKUP_TIMEOUT) { + PR_ERROR("Timeout waiting for special wakeup\n"); + return -1; + } + } while (!(gp0 & SPECIAL_WKUP_DONE)); + + return 0; +} + +static int deassert_special_wakeup(struct target *chip) +{ + /* Assert special wakeup to prevent low power states */ + CHECK_ERR(write_target(chip, PMSPCWKUPFSP_REG, 0)); + + return 0; +} + +uint64_t chiplet_thread_status(struct target *thread) +{ + return thread->status; +} + +static uint64_t get_thread_status(struct target *thread) +{ + uint64_t val, mode_reg, thread_status = 0; + + /* Need to activete debug mode to get complete status */ + CHECK_ERR(read_target(thread, RAS_MODE_REG, &mode_reg)); + mode_reg |= MR_THREAD_IN_DEBUG; + CHECK_ERR(write_target(thread, RAS_MODE_REG, mode_reg)); + + /* Read status */ + CHECK_ERR(read_target(thread, RAS_STATUS_REG, &val)); + + if (val & RAS_STATUS_THREAD_ACTIVE) + thread_status |= THREAD_STATUS_ACTIVE; + + if (val & RAS_STATUS_TS_QUIESCE) + thread_status |= THREAD_STATUS_QUIESCE; + + /* Read POW status */ + CHECK_ERR(read_target(thread, 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(write_target(thread, RAS_MODE_REG, mode_reg)); + + return thread_status; +} + +/* + * Single step the thread count instructions. + */ +int ram_step_thread(struct target *thread, int count) +{ + int i; + uint64_t ras_mode, ras_status; + + /* Activate single-step mode */ + CHECK_ERR(read_target(thread, RAS_MODE_REG, &ras_mode)); + ras_mode |= MR_DO_SINGLE_MODE; + CHECK_ERR(write_target(thread, RAS_MODE_REG, ras_mode)); + + /* Step the core */ + for (i = 0; i < count; i++) { + CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STEP)); + + /* Wait for step to complete */ + do { + CHECK_ERR(read_target(thread, RAS_STATUS_REG, &ras_status)); + } while (!(ras_status & RAS_STATUS_INST_COMPLETE)); + } + + /* Deactivate single-step mode */ + ras_mode &= ~MR_DO_SINGLE_MODE; + CHECK_ERR(write_target(thread, RAS_MODE_REG, ras_mode)); + + return 0; +} + +int ram_stop_thread(struct target *thread) +{ + int i = 0, thread_id = (thread->base >> 4) & 0x7; + uint64_t val; + struct target *chip = thread->next; + + /* Skip inactive threads */ + if (!GETFIELD(THREAD_STATUS_ACTIVE, thread->status)) + return -1; + + do { + /* Quiese active thread */ + CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STOP)); + + /* Wait for thread to quiese */ + CHECK_ERR(read_target(chip, RAS_STATUS_REG, &val)); + if (i++ > RAS_STATUS_TIMEOUT) { + PR_ERROR("Unable to quiesce thread %d (0x%016llx).\n", + thread->index, 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(read_target(chip, THREAD_ACTIVE_REG, &val)); + val |= PPC_BIT(8) >> thread_id; + CHECK_ERR(write_target(chip, THREAD_ACTIVE_REG, val)); + + return 0; +} + +int ram_start_thread(struct target *thread) +{ + uint64_t val; + struct target *chip = thread->next; + int thread_id = (thread->base >> 4) & 0x7; + + if (!GETFIELD(THREAD_STATUS_ACTIVE, thread->status)) + return -1; + + /* Activate thread */ + CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_START)); + + /* Restore thread active */ + CHECK_ERR(read_target(chip, THREAD_ACTIVE_REG, &val)); + val &= ~(PPC_BIT(8) >> thread_id); + val |= PPC_BIT(thread_id); + CHECK_ERR(write_target(chip, THREAD_ACTIVE_REG, val)); + + return 0; +} + +/* + * RAMs the opcodes in *opcodes and store the results of each opcode + * into *results. *results must point to an array the same size as + * *opcodes. Each entry from *results is put into SCR0 prior to + * executing an opcode so that it may also be used to pass in + * data. Note that only register r0 is saved and restored so opcodes + * must not touch other registers. + */ +static int ram_instructions(struct target *thread, uint64_t *opcodes, + uint64_t *results, int len, unsigned int lpar) +{ + uint64_t ram_mode, val, opcode, r0 = 0; + struct target *chiplet = thread->next; + int thread_id = (thread->base >> 4) & 0x7; + int i; + + /* Activate RAM mode */ + CHECK_ERR(read_target(chiplet, RAM_MODE_REG, &ram_mode)); + ram_mode |= RAM_MODE_ENABLE; + CHECK_ERR(write_target(chiplet, 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(write_target(chiplet, SPR_MODE_REG, val)); + CHECK_ERR(write_target(chiplet, L0_SCOM_SPRC_REG, SCOM_SPRC_SCRATCH_SPR)); + + /* RAM instructions */ + for (i = -1; i <= len; i++) { + if (i < 0) + /* Save r0 (assumes opcodes don't touch other registers) */ + opcode = mtspr(277, 0); + else if (i < len) { + CHECK_ERR(write_target(chiplet, SCR0_REG, results[i])); + opcode = opcodes[i]; + } else if (i >= len) { + /* Restore r0 */ + CHECK_ERR(write_target(chiplet, SCR0_REG, r0)); + opcode = mfspr(0, 277); + } + + /* ram instruction */ + val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread_id); + val = SETFIELD(RAM_INSTR, val, opcode); + CHECK_ERR(write_target(chiplet, RAM_CTRL_REG, val)); + + /* wait for completion */ + do { + CHECK_ERR(read_target(chiplet, RAM_STATUS_REG, &val)); + } while (!val); + + if (!(val & RAM_STATUS)) + PR_ERROR("RAMMING failed with status 0x%llx\n", val); + + /* Save the results */ + CHECK_ERR(read_target(chiplet, SCR0_REG, &val)); + if (i < 0) + r0 = val; + else if (i < len) + results[i] = val; + } + + /* Disable RAM mode */ + ram_mode &= ~RAM_MODE_ENABLE; + CHECK_ERR(write_target(chiplet, RAM_MODE_REG, ram_mode)); + + return 0; +} + +/* + * Get gpr value. Chip must be stopped. + */ +int ram_getgpr(struct target *thread, int gpr, uint64_t *value) +{ + uint64_t opcodes[] = {mtspr(277, gpr)}; + uint64_t results[] = {0}; + + CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); + *value = results[0]; + return 0; +} + +int ram_putgpr(struct target *thread, int gpr, uint64_t value) +{ + uint64_t opcodes[] = {mfspr(gpr, 277)}; + uint64_t results[] = {value}; + + CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); + + return 0; +} + +int ram_getnia(struct target *thread, uint64_t *value) +{ + uint64_t opcodes[] = {mfnia(0), mtspr(277, 0)}; + uint64_t results[] = {0, 0}; + + CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); + *value = results[1]; + return 0; +} + +int ram_putnia(struct target *thread, uint64_t value) +{ + uint64_t opcodes[] = {mfspr(0, 277), mtnia(0)}; + uint64_t results[] = {value, 0}; + + CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); + return 0; +} + +int ram_getspr(struct target *thread, int spr, uint64_t *value) +{ + uint64_t opcodes[] = {mfspr(0, spr), mtspr(277, 0)}; + uint64_t results[] = {0, 0}; + + CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); + *value = results[1]; + return 0; +} + +int ram_putspr(struct target *thread, int spr, uint64_t value) +{ + uint64_t opcodes[] = {mfspr(0, 277), mtspr(spr, 0)}; + uint64_t results[] = {value, 0}; + + CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); + return 0; +} + +int ram_getmsr(struct target *thread, uint64_t *value) +{ + uint64_t opcodes[] = {mfmsr(0), mtspr(277, 0)}; + uint64_t results[] = {0, 0}; + + CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); + *value = results[1]; + return 0; +} + +int ram_putmsr(struct target *thread, uint64_t value) +{ + uint64_t opcodes[] = {mfspr(0, 277), mtmsr(0)}; + uint64_t results[] = {value, 0}; + + CHECK_ERR(ram_instructions(thread, opcodes, results, ARRAY_SIZE(opcodes), 0)); + return 0; +} + +int thread_target_init(struct target *thread, const char *name, uint64_t thread_id, struct target *next) +{ + target_init(thread, name, 0x13000 | (thread_id << 4), NULL, NULL, NULL, next); + thread->status = get_thread_status(thread); + + /* Threads always exist, although they may not be in a useful state for most operations */ + return 0; +} + +/* + * Initialise all viable threads for ramming on the given chiplet. + */ +int thread_target_probe(struct target *chiplet, struct target *targets, int max_target_count) +{ + struct target *thread = targets; + int i, count = 0; + + for (i = 0; i < THREADS_PER_CORE && i < max_target_count; i++) { + thread_target_init(thread, "Thread", i, chiplet); + thread++; + count++; + } + + return count; +} + +int chiplet_target_init(struct target *target, const char *name, uint64_t chip_id, struct target *next) +{ + uint64_t value, base; + + base = 0x10000000 | (chip_id << 24); + + target_init(target, name, base, NULL, NULL, NULL, next); + + /* Work out if this chip is actually present */ + if (read_target(target, SCOM_EX_GP3, &value)) { + PR_DEBUG("Error reading chip GP3 register\n"); + return -1; + } + + /* Return 0 is a chip is actually present. We still leave the + target initialised even if it isn't present as a user may + want to continue anyway. */ + return -!GETFIELD(PPC_BIT(0), value); +} + +/* Initialises all possible chiplets on the given processor + * target. *targets should point to pre-allocated memory with enough + * free space for the maximum number of targets. Returns the number of + * chips found. */ +int chiplet_target_probe(struct target *processor, struct target *targets, int max_target_count) +{ + struct target *target = targets; + int i, count = 0, rc = 0; + + for (i = 0; i <= 0xf && i < max_target_count; i++) { + if (i == 0 || i == 7 || i == 8 || i == 0xf) + /* 0, 7, 8 & 0xf are reserved */ + continue; + + if (!(rc = chiplet_target_init(target, "Chiplet", i, processor))) { + assert_special_wakeup(target); + target++; + count++; + } else + target_del(target); + } + + if (rc) + /* The last target is invalid, zero it out */ + memset(target, 0, sizeof(*target)); + + return count; +} diff --git a/libpdbg/fsi2pib.c b/libpdbg/fsi2pib.c new file mode 100644 index 0000000..0c13724 --- /dev/null +++ b/libpdbg/fsi2pib.c @@ -0,0 +1,76 @@ +/* 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 <unistd.h> + +#include "target.h" +#include "bitutils.h" +#include "operations.h" + +#define FSI_DATA0_REG 0x1000 +#define FSI_DATA1_REG 0x1001 +#define FSI_CMD_REG 0x1002 +#define FSI_CMD_REG_WRITE PPC_BIT32(0) + +#define FSI_RESET_REG 0x1006 +#define FSI_RESET_CMD PPC_BIT32(0) + +#define FSI_SET_PIB_RESET_REG 0x1007 +#define FSI_SET_PIB_RESET PPC_BIT32(0) + +/* For some reason the FSI2PIB engine dies with frequent + * access. Letting it have a bit of a rest seems to stop the + * problem. This sets the number of usecs to sleep between SCOM + * accesses. */ +#define FSI2PIB_RELAX 50 + +static int fsi2pib_getscom(struct target *target, uint64_t addr, uint64_t *value) +{ + uint64_t result; + + usleep(FSI2PIB_RELAX); + + /* Get scom works by putting the address in FSI_CMD_REG and + * reading the result from FST_DATA[01]_REG. */ + CHECK_ERR(write_next_target(target, FSI_CMD_REG, addr)); + CHECK_ERR(read_next_target(target, FSI_DATA0_REG, &result)); + *value = result << 32; + CHECK_ERR(read_next_target(target, FSI_DATA1_REG, &result)); + *value |= result; + return 0; +} + +static int fsi2pib_putscom(struct target *target, uint64_t addr, uint64_t value) +{ + usleep(FSI2PIB_RELAX); + + CHECK_ERR(write_next_target(target, FSI_RESET_REG, FSI_RESET_CMD)); + CHECK_ERR(write_next_target(target, FSI_DATA0_REG, (value >> 32) & 0xffffffff)); + CHECK_ERR(write_next_target(target, FSI_DATA1_REG, value & 0xffffffff)); + CHECK_ERR(write_next_target(target, FSI_CMD_REG, FSI_CMD_REG_WRITE | addr)); + + return 0; +} + +int fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next) +{ + target->name = name; + target->read = fsi2pib_getscom; + target->write = fsi2pib_putscom; + target->base = base; + target->next = next; + + return -1; +} diff --git a/libpdbg/i2c.c b/libpdbg/i2c.c index 07a1dd1..d551658 100644 --- a/libpdbg/i2c.c +++ b/libpdbg/i2c.c @@ -19,6 +19,8 @@ #include <string.h> #include <fcntl.h> #include <unistd.h> +#include <endian.h> +#include <sys/ioctl.h> #include <linux/i2c-dev.h> #include "bitutils.h" @@ -39,22 +41,6 @@ static int i2c_set_addr(int fd, int addr) return 0; } -static int i2c_getcfam(struct scom_backend *backend, int processor_id, - uint32_t *value, uint32_t addr) -{ - PR_ERROR("TODO: cfam access not supported with i2c backend\n"); - - return -1; -} - -static int i2c_putcfam(struct scom_backend *backend, int processor_id, - uint32_t value, uint32_t addr) -{ - PR_ERROR("TODO: cfam access not supported with i2c backend\n"); - - return -1; -} - static int i2c_set_scom_addr(struct i2c_data *i2c_data, uint32_t addr) { uint8_t data[4]; @@ -72,16 +58,10 @@ static int i2c_set_scom_addr(struct i2c_data *i2c_data, uint32_t addr) return 0; } -static int i2c_getscom(struct scom_backend *backend, int processor_id, - uint64_t *value, uint32_t addr) +static int i2c_getscom(struct target *target, uint64_t addr, uint64_t *value) { - struct i2c_data *i2c_data = backend->priv; - uint8_t data[8]; - - if (processor_id) { - PR_ERROR("TODO: secondary processor access not supported with i2c backend\n"); - return -1; - } + struct i2c_data *i2c_data = target->priv; + uint64_t data; CHECK_ERR(i2c_set_scom_addr(i2c_data, addr)); @@ -90,22 +70,16 @@ static int i2c_getscom(struct scom_backend *backend, int processor_id, return -1; } - *value = *((uint64_t *) data); + *value = le64toh(data); return 0; } -static int i2c_putscom(struct scom_backend *backend, int processor_id, - uint64_t value, uint32_t addr) +static int i2c_putscom(struct target *target, uint64_t addr, uint64_t value) { - struct i2c_data *i2c_data = backend->priv; + struct i2c_data *i2c_data = target->priv; uint8_t data[12]; - if (processor_id) { - PR_ERROR("TODO: secondary processor access not supported with i2c backend\n"); - return -1; - } - /* Setup scom address */ addr <<= 1; data[3] = GETFIELD(PPC_BITMASK32(0, 7), addr); @@ -132,43 +106,39 @@ static int i2c_putscom(struct scom_backend *backend, int processor_id, return 0; } -static void i2c_destroy(struct scom_backend *backend) +static void i2c_destroy(struct target *target) { - struct i2c_data *i2c_data = backend->priv; + struct i2c_data *i2c_data = target->priv; close(i2c_data->fd); - free(backend->priv); + free(target->priv); } /* * Initialise a i2c backend on the given bus at the given bus address. */ -struct scom_backend *i2c_init(char *bus, int addr) +int i2c_target_init(struct target *target, const char *name, struct target *next, + const char *bus, int addr) { - struct scom_backend *backend; struct i2c_data *i2c_data; - backend = malloc(sizeof(*backend)); i2c_data = malloc(sizeof(*i2c_data)); - if (!backend || !i2c_data) + if (!i2c_data) exit(1); i2c_data->addr = addr; i2c_data->fd = open(bus, O_RDWR); if (i2c_data->fd < 0) { perror("Error opening bus"); - exit(1); + return -1; } if (i2c_set_addr(i2c_data->fd, addr) < 0) - exit(1); + return -1; - backend->getscom = i2c_getscom; - backend->putscom = i2c_putscom; - backend->getcfam = i2c_getcfam; - backend->putcfam = i2c_putcfam; - backend->destroy = i2c_destroy; - backend->priv = i2c_data; + target_init(target, name, addr, i2c_getscom, i2c_putscom, i2c_destroy, + next); + target->priv = i2c_data; - return backend; + return 0; } diff --git a/libpdbg/operations.h b/libpdbg/operations.h index 2811093..8e3033d 100644 --- a/libpdbg/operations.h +++ b/libpdbg/operations.h @@ -16,6 +16,8 @@ #ifndef __OPERATIONS_H #define __OPERATIONS_H +#include "target.h" + /* Error codes */ #define EFSI 1 @@ -35,38 +37,45 @@ #define THREADS_PER_CORE 8 -/* Structure to allow alternative backend implentations */ -struct scom_backend { - void (*destroy)(struct scom_backend *backend); - int (*getscom)(struct scom_backend *backend, int processor_id, uint64_t *value, uint32_t addr); - int (*putscom)(struct scom_backend *backend, int processor_id, uint64_t value, uint32_t addr); - int (*getcfam)(struct scom_backend *backend, int processor_id, uint32_t *value, uint32_t addr); - int (*putcfam)(struct scom_backend *backend, int processor_id, uint32_t value, uint32_t addr); - int processor_id; - void *priv; -}; +#define FSI2PIB_BASE 0x1000 /* Alter display unit functions */ -int adu_getmem(uint64_t addr, uint8_t *output, uint64_t size); +int adu_getmem(struct target *target, uint64_t addr, uint8_t *output, uint64_t size); /* Functions to ram instructions */ -int ram_getgpr(int chip, int thread, int gpr, uint64_t *value); -int ram_getnia(int chip, int thread, uint64_t *value); -int ram_getspr(int chip, int thread, int spr, uint64_t *value); -int ram_running_threads(uint64_t chip, uint64_t *active_threads); -int ram_stop_chip(uint64_t chip, uint64_t *thread_active); -int ram_start_chip(uint64_t chip, uint64_t thread_active); +#define THREAD_STATUS_ACTIVE PPC_BIT(63) +#define THREAD_STATUS_STATE PPC_BITMASK(61, 62) +#define THREAD_STATUS_DOZE PPC_BIT(62) +#define THREAD_STATUS_NAP PPC_BIT(61) +#define THREAD_STATUS_SLEEP PPC_BITMASK(61, 62) +#define THREAD_STATUS_QUIESCE PPC_BIT(60) + +int ram_getgpr(struct target *thread, int gpr, uint64_t *value); +int ram_putgpr(struct target *thread, int gpr, uint64_t value); +int ram_getnia(struct target *thread, uint64_t *value); +int ram_putnia(struct target *thread, uint64_t value); +int ram_getspr(struct target *thread, int spr, uint64_t *value); +int ram_putspr(struct target *thread, int spr, uint64_t value); +int ram_getmsr(struct target *thread, uint64_t *value); +int ram_putmsr(struct target *thread, uint64_t value); +uint64_t chiplet_thread_status(struct target *thread); +int ram_stop_thread(struct target *thread); +int ram_step_thread(struct target *thread, int count); +int ram_start_thread(struct target *thread); /* GDB server functionality */ int gdbserver_start(uint16_t port); -/* scom backend functions. Most other operations use these. */ -int backend_init(int processor_id); -void backend_destroy(void); -void backend_set_processor(int processor_id); -int getscom(uint64_t *value, uint32_t addr); -int putscom(uint64_t value, uint32_t addr); -int getcfam(uint32_t *value, uint32_t addr); -int putcfam(uint32_t value, uint32_t addr); +int fsi_target_init(struct target *target, const char *name, uint64_t base, struct target *next); +int fsi_target_probe(struct target *targets, int max_target_count); +int i2c_target_init(struct target *target, const char *name, struct target *next, + const char *bus, int addr); +int fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next); +int opb_target_init(struct target *target, const char *name, uint64_t base, struct target *next); +int thread_target_init(struct target *thread, const char *name, uint64_t thread_id, struct target *next); +int thread_target_probe(struct target *chiplet, struct target *targets, int max_target_count); +int chiplet_target_init(struct target *target, const char *name, uint64_t chip_id, struct target *next); +int chiplet_target_probe(struct target *processor, struct target *targets, int max_target_count); +int hmfsi_target_probe(struct target *cfam, struct target *targets, int max_target_count); #endif diff --git a/libpdbg/ram.c b/libpdbg/ram.c deleted file mode 100644 index 4d9954b..0000000 --- a/libpdbg/ram.c +++ /dev/null @@ -1,306 +0,0 @@ -/* 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 "operations.h" -#include "bitutils.h" - -#define RAS_STATUS_TIMEOUT 100 - -#define DIRECT_CONTROLS_REG 0x10013000 -#define DIRECT_CONTROL_SP_START PPC_BIT(62) -#define DIRECT_CONTROL_SP_STOP PPC_BIT(63) -#define RAS_STATUS_REG 0x10013002 -#define RAS_STATUS_SRQ_EMPTY PPC_BIT(8) -#define RAS_STATUS_NCU_QUIESCED PPC_BIT(10) -#define RAS_STATUS_TS_QUIESCE PPC_BIT(49) -#define THREAD_ACTIVE_REG 0x1001310E -#define THREAD_ACTIVE PPC_BITMASK(0, 7) -#define RAM_THREAD_ACTIVE PPC_BITMASK(8, 15) -#define SPR_MODE_REG 0x10013281 -#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 0x10013280 -#define SCOM_SPRC_SCRATCH_SPR 0x40 -#define SCR0_REG 0x10013283 -#define RAM_MODE_REG 0x10013c00 -#define RAM_MODE_ENABLE PPC_BIT(0) -#define RAM_CTRL_REG 0x10013c01 -#define RAM_THREAD_SELECT PPC_BITMASK(0, 2) -#define RAM_INSTR PPC_BITMASK(3, 34) -#define RAM_STATUS_REG 0x10013c02 -#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 PMSPCWKUPFSP_REG 0x100f010b -#define FSP_SPECIAL_WAKEUP PPC_BIT(0) - -/* Opcodes */ -#define MFNIA_OPCODE 0x00000004UL -#define MFSPR_OPCODE 0x7c0002a6UL -#define MTSPR_OPCODE 0x7c0003a6UL - -static uint64_t mfspr(uint64_t reg, uint64_t spr) -{ - if (reg > 31) - PR_ERROR("Invalid register specified\n"); - - return MFSPR_OPCODE | (reg << 21) | ((spr & 0x1f) << 16) | ((spr & 0x3e0) << 6); -} - -static uint64_t mtspr(uint64_t spr, uint64_t reg) -{ - if (reg > 31) - PR_ERROR("Invalid register specified\n"); - - return MTSPR_OPCODE | (reg << 21) | ((spr & 0x1f) << 16) | ((spr & 0x3e0) << 6); -} - -static uint64_t mfnia(uint64_t reg) -{ - if (reg > 31) - PR_ERROR("Invalid register specified\n"); - - return MFNIA_OPCODE | (reg << 21); -} - -/* - * Return a mask of currently active threads. - */ -int ram_running_threads(uint64_t chip, uint64_t *running_threads) -{ - uint64_t addr, val; - int thread; - - chip <<= 24; - *running_threads = 0; - - for (thread = 0; thread < THREADS_PER_CORE; thread++) { - /* Wait for thread to quiese */ - addr = RAS_STATUS_REG | chip; - addr |= thread << 4; - CHECK_ERR(getscom(&val, addr)); - - if (!((val & RAS_STATUS_SRQ_EMPTY) - && (val & RAS_STATUS_TS_QUIESCE))) - *running_threads |= 1 << (THREADS_PER_CORE - thread - 1); - } - return 0; -} - -/* - * Stop active threads on the chip. Returns the active_threads in - * *active_threads. This should be passed to ram_start_chip() to - * restart the stopped threads. - */ -int ram_stop_chip(uint64_t chip, uint64_t *active_threads) -{ - int i = 0, thread; - uint64_t addr, val, thread_active; - - chip <<= 24; - - /* Assert special wakeup to prevent low power states */ - addr = PMSPCWKUPFSP_REG | chip; - CHECK_ERR(putscom(FSP_SPECIAL_WAKEUP, addr)); - - /* Read active threads */ - addr = THREAD_ACTIVE_REG | chip; - CHECK_ERR(getscom(&val, addr)); - thread_active = GETFIELD(THREAD_ACTIVE, val); - - for (thread = 0; thread < THREADS_PER_CORE; thread++) { - /* Skip inactive threads (stupid IBM bit ordering) */ - if (!(thread_active & (1 << (THREADS_PER_CORE - thread - 1)))) - continue; - - /* Quiese active thread */ - addr = DIRECT_CONTROLS_REG | chip; - addr |= thread << 4; - val = DIRECT_CONTROL_SP_STOP; - CHECK_ERR(putscom(val, addr)); - - /* Wait for thread to quiese */ - addr = RAS_STATUS_REG | chip; - addr |= thread << 4; - i = 0; - do { - CHECK_ERR(getscom(&val, addr)); - if (i++ > RAS_STATUS_TIMEOUT) { - PR_ERROR("Unable to quiesce thread %d (0x%016llx).\n", - thread, val); - PR_ERROR("Continuing anyway.\n"); - break; - } - } while (!((val & RAS_STATUS_SRQ_EMPTY) - /* - * Workbook says check for bit 10 but it - * never seems to get set and everything - * still works as expected. - */ - /* && (val & RAS_STATUS_NCU_QUIESCED) */ - && (val & RAS_STATUS_TS_QUIESCE))); - } - - /* Make the RAM threads active */ - addr = THREAD_ACTIVE_REG | chip; - CHECK_ERR(getscom(&val, addr)); - val = SETFIELD(RAM_THREAD_ACTIVE, val, (1 << THREADS_PER_CORE) - 1); - CHECK_ERR(putscom(val, addr)); - - /* Activate RAM mode */ - addr = RAM_MODE_REG | chip; - CHECK_ERR(getscom(&val, addr)); - val |= RAM_MODE_ENABLE; - CHECK_ERR(putscom(val, addr)); - - *active_threads = thread_active; - return 0; -} - -int ram_start_chip(uint64_t chip, uint64_t thread_active) -{ - uint64_t addr, val; - int thread; - - chip <<= 24; - - /* De-activate RAM mode */ - addr = RAM_MODE_REG | chip; - CHECK_ERR(putscom(0, addr)); - - /* Start cores which were active */ - for (thread = 0; thread < THREADS_PER_CORE; thread++) { - if (!(thread_active & (1 << (THREADS_PER_CORE - thread - 1)))) - continue; - - /* Activate thread */ - addr = DIRECT_CONTROLS_REG | chip; - addr |= thread << 4; - val = DIRECT_CONTROL_SP_START; - CHECK_ERR(putscom(val, addr)); - } - - /* De-assert special wakeup to enable low power states */ - addr = PMSPCWKUPFSP_REG | chip; - CHECK_ERR(putscom(0, addr)); - - return 0; -} - -/* - * RAMs the opcodes in *opcodes and store the results of each opcode - * into *results. *results must point to an array the same size as - * *opcodes. Note that only register r0 is saved and restored so - * opcodes must not touch other registers. - */ -static int ram_instructions(uint64_t *opcodes, uint64_t *results, int len, - uint64_t chip, unsigned int lpar, uint64_t thread) -{ - uint64_t addr, val, opcode, r0 = 0; - int i; - - /* Setup SPRC to use SPRD */ - chip <<= 24; - addr = SPR_MODE_REG | chip; - 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)); - CHECK_ERR(putscom(val, addr)); - - addr = L0_SCOM_SPRC_REG | chip; - val = SCOM_SPRC_SCRATCH_SPR; - CHECK_ERR(putscom(val, addr)); - - /* RAM instructions */ - addr = RAM_CTRL_REG | chip; - for (i = -1; i <= len; i++) { - if (i < 0) - /* Save r0 (assumes opcodes don't touch other registers) */ - opcode = mtspr(277, 0); - else if (i < len) - opcode = opcodes[i]; - else if (i >= len) { - /* Restore r0 */ - CHECK_ERR(putscom(r0, SCR0_REG | chip)); - opcode = mfspr(0, 277); - } - - /* ram instruction */ - val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread); - val = SETFIELD(RAM_INSTR, val, opcode); - CHECK_ERR(putscom(val, addr)); - - /* wait for completion */ - do { - CHECK_ERR(getscom(&val, RAM_STATUS_REG | chip)); - } while (!val); - - if (!(val & RAM_STATUS)) - PR_ERROR("RAMMING failed with status 0x%llx\n", val); - - /* Save the results */ - CHECK_ERR(getscom(&val, SCR0_REG | chip)); - if (i < 0) - r0 = val; - else if (i < len) - results[i] = val; - } - - return 0; -} - -/* - * Get gpr value. Chip must be stopped. - */ -int ram_getgpr(int chip, int thread, int gpr, uint64_t *value) -{ - uint64_t opcodes[] = {mtspr(277, gpr)}; - uint64_t results[] = {0}; - - CHECK_ERR(ram_instructions(opcodes, results, ARRAY_SIZE(opcodes), - chip, 0, thread)); - *value = results[0]; - return 0; -} - -int ram_getnia(int chip, int thread, uint64_t *value) -{ - uint64_t opcodes[] = {mfnia(0), mtspr(277, 0)}; - uint64_t results[] = {0, 0}; - - CHECK_ERR(ram_instructions(opcodes, results, ARRAY_SIZE(opcodes), - chip, 0, thread)); - *value = results[0]; - return 0; -} - -int ram_getspr(int chip, int thread, int spr, uint64_t *value) -{ - uint64_t opcodes[] = {mfspr(0, spr), mtspr(277, 0)}; - uint64_t results[] = {0, 0}; - - CHECK_ERR(ram_instructions(opcodes, results, ARRAY_SIZE(opcodes), - chip, 0, thread)); - *value = results[1]; - return 0; -} diff --git a/libpdbg/scom.c b/libpdbg/scom.c deleted file mode 100644 index ec93962..0000000 --- a/libpdbg/scom.c +++ /dev/null @@ -1,103 +0,0 @@ -/* 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 <stdint.h> -#include <stdio.h> - -#include "bitutils.h" -#include "backend.h" -#include "operations.h" - -struct scom_backend *backend = NULL; - -#define FSI_SET_PIB_RESET_REG 0x1007 -#define FSI_SET_PIB_RESET PPC_BIT32(0) - -int backend_i2c_init(char *bus, int addr) -{ - backend = i2c_init(bus, addr); - if (!backend) - return -1; - - backend->processor_id = 0; - - return 0; -} - -int backend_fsi_init(int processor_id) -{ - backend = fsi_init(); - if (!backend) - return -1; - - backend->processor_id = processor_id; - - /* Make sure the FSI2PIB engine is in a good state */ - if (putcfam(FSI_SET_PIB_RESET, FSI_SET_PIB_RESET_REG)) - return -1; - - return 0; -} - -void backend_set_processor(int processor_id) -{ - backend->processor_id = processor_id; -} - -void backend_destroy(void) -{ - if (!backend || backend->destroy) - backend->destroy(backend); -} - -int getscom(uint64_t *value, uint32_t addr) -{ - if (!backend || !backend->getscom) { - PR_ERROR("Backend does not support %s\n", __FUNCTION__); - return -1; - } - - return backend->getscom(backend, backend->processor_id, value, addr); -} - -int putscom(uint64_t value, uint32_t addr) -{ - if (!backend || !backend->getscom) { - PR_ERROR("Backend does not support %s\n", __FUNCTION__); - return -1; - } - - return backend->putscom(backend, backend->processor_id, value, addr); -} - -int getcfam(uint32_t *value, uint32_t addr) -{ - if (!backend || !backend->getscom) { - PR_ERROR("Backend does not support %s\n", __FUNCTION__); - return -1; - } - - return backend->getcfam(backend, backend->processor_id, value, addr); -} - -int putcfam(uint32_t value, uint32_t addr) -{ - if (!backend || !backend->putscom) { - PR_ERROR("Backend does not support %s\n", __FUNCTION__); - return -1; - } - - return backend->putcfam(backend, backend->processor_id, value, addr); -} diff --git a/libpdbg/target.c b/libpdbg/target.c new file mode 100644 index 0000000..97641ee --- /dev/null +++ b/libpdbg/target.c @@ -0,0 +1,68 @@ +#include <stdio.h> +#include <stdint.h> +#include <assert.h> +#include <ccan/list/list.h> + +#include "target.h" + +void target_init(struct target *target, const char *name, uint64_t base, + target_read read, target_write write, + target_destroy destroy, struct target *next) +{ + target->name = name; + target->index = -1; + target->base = base; + target->read = read; + target->write = write; + target->destroy = destroy; + target->next = next; + + list_head_init(&target->children); + + if (next) + list_add_tail(&next->children, &target->link); +} + +void target_del(struct target *target) +{ + if (target->destroy) + target->destroy(target); + + /* We don't recursively destroy things yet */ + assert(list_empty(&target->children)); + if (target->next) + list_del(&target->link); +} + +int read_target(struct target *target, uint64_t addr, uint64_t *value) +{ +// printf("Target %s read 0x%0llx\n", target->name, addr); + + if (target->read) + return target->read(target, addr, value); + else + /* If there is no read method fall through to the next one */ + return read_next_target(target, addr, value); +} + +int read_next_target(struct target *target, uint64_t addr, uint64_t *value) +{ + assert(target->next); + return read_target(target->next, target->base + addr, value); +} + +int write_target(struct target *target, uint64_t addr, uint64_t value) +{ +// printf("Target %s write 0x%0llx\n", target->name, addr); + + if (target->write) + return target->write(target, addr, value); + else + return write_next_target(target, addr, value); +} + +int write_next_target(struct target *target, uint64_t addr, uint64_t value) +{ + assert(target->next); + return write_target(target->next, target->base + addr, value); +} diff --git a/libpdbg/target.h b/libpdbg/target.h new file mode 100644 index 0000000..250bc1b --- /dev/null +++ b/libpdbg/target.h @@ -0,0 +1,57 @@ +/* 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. + */ +#ifndef __TARGET_H +#define __TARGET_H + +#include <stdint.h> +#include <ccan/list/list.h> + +struct target; +typedef int (*target_read)(struct target *target, uint64_t addr, uint64_t *value); +typedef int (*target_write)(struct target *target, uint64_t addr, uint64_t value); +typedef void (*target_destroy)(struct target *target); + +struct target { + const char *name; + int index; + uint64_t base; + target_read read; + target_write write; + target_destroy destroy; + struct target *next; + struct list_node link; + struct list_head children; + struct list_node class_link; + void *priv; + uint64_t status; +}; + +struct target_class { + const char *name; + struct list_head targets; +}; + +int read_target(struct target *target, uint64_t addr, uint64_t *value); +int read_next_target(struct target *target, uint64_t addr, uint64_t *value); +int write_target(struct target *target, uint64_t addr, uint64_t value); +int write_next_target(struct target *target, uint64_t addr, uint64_t value); +void target_init(struct target *target, const char *name, uint64_t base, + target_read read, target_write write, + target_destroy destroy, struct target *next); +void target_class_add(struct target_class *class, struct target *target, int index); +void target_del(struct target *target); + +#endif |