summaryrefslogtreecommitdiffstats
path: root/libpdbg
diff options
context:
space:
mode:
authorAlistair Popple <alistair@popple.id.au>2016-09-16 16:45:39 +1000
committerAlistair Popple <alistair@popple.id.au>2016-10-25 13:32:35 +1100
commit749351433237295a86afcf451b947bc6621b946f (patch)
tree4141847a38ed325dfa7d94751ad34a899301a7ea /libpdbg
parente622883810266bcbc09ab5e1bae6a137dcda74c7 (diff)
downloadpdbg-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.c36
-rw-r--r--libpdbg/bmcfsi.c152
-rw-r--r--libpdbg/cfam.c261
-rw-r--r--libpdbg/chip.c510
-rw-r--r--libpdbg/fsi2pib.c76
-rw-r--r--libpdbg/i2c.c70
-rw-r--r--libpdbg/operations.h59
-rw-r--r--libpdbg/ram.c306
-rw-r--r--libpdbg/scom.c103
-rw-r--r--libpdbg/target.c68
-rw-r--r--libpdbg/target.h57
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
OpenPOWER on IntegriCloud