[Pdbg] [PATCH 6/7] libpdbg: Add interface to BMC kernel sbefifo driver

Amitay Isaacs amitay at ozlabs.org
Wed Apr 10 18:08:53 AEST 2019


This is based on Ben's original patch.

Signed-off-by: Amitay Isaacs <amitay at ozlabs.org>
---
 Makefile.am       |   1 +
 libpdbg/sbefifo.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++
 libpdbg/target.c  |  44 ++++--
 libpdbg/target.h  |   8 ++
 p9-kernel.dts.m4  |   7 +
 5 files changed, 410 insertions(+), 8 deletions(-)
 create mode 100644 libpdbg/sbefifo.c

diff --git a/Makefile.am b/Makefile.am
index d34cf20..6b5162f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -170,6 +170,7 @@ libpdbg_la_SOURCES = \
 	libpdbg/operations.h \
 	libpdbg/p8chip.c \
 	libpdbg/p9chip.c \
+	libpdbg/sbefifo.c \
 	libpdbg/target.c \
 	libpdbg/target.h \
 	libpdbg/xbus.c
diff --git a/libpdbg/sbefifo.c b/libpdbg/sbefifo.c
new file mode 100644
index 0000000..22b3153
--- /dev/null
+++ b/libpdbg/sbefifo.c
@@ -0,0 +1,358 @@
+/* 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 <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <fcntl.h>
+
+#include "target.h"
+#include "debug.h"
+
+static void sbefifo_op_dump(const char *prefix, uint8_t *buf, size_t buflen)
+{
+	int i;
+
+	if (!prefix)
+		prefix = "";
+
+	for (i=0; i<buflen/4; i++) {
+		PR_ERROR("   %s 0x%02x%02x%02x%02x\n", prefix,
+			 buf[i*4], buf[i*4+1], buf[i*4+2], buf[i*4+3]);
+	}
+}
+
+static int sbefifo_op_read(struct sbefifo *sbefifo, void *buf, size_t *buflen)
+{
+	ssize_t n;
+
+	assert(*buflen > 0);
+
+	n = read(sbefifo->fd, buf, *buflen);
+	if (n < 0) {
+		PR_ERROR("sbefifo: Failed to read, errno=%d\n", errno);
+		return -1;
+	}
+	*buflen = n;
+
+	return 0;
+}
+
+static int sbefifo_op_write(struct sbefifo *sbefifo, void *buf, size_t buflen)
+{
+	ssize_t n;
+
+	n = write(sbefifo->fd, buf, buflen);
+	if (n < 0) {
+		PR_ERROR("sbefifo: Failed to write, errno=%d\n", errno);
+		return -1;
+	}
+	if (n != buflen) {
+		PR_ERROR("sbefifo: Short write %zi of %zi bytes\n", n, buflen);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sbefifo_op_ffdc_read(uint8_t *buf, uint32_t buflen)
+{
+	int offset = 0;
+	uint32_t header, value;
+	uint16_t magic, len_words;
+	int i;
+
+	if (buflen < 4)
+		return -1;
+
+	/* End of ffdc data */
+	if (buflen == 4)
+		return 0;
+
+	header = be32toh(*(uint32_t *)(buf + offset));
+	offset += 4;
+
+	magic = header >> 16;
+	if (magic != 0xffdc) {
+		PR_ERROR("sbefifo: ffdc expected 0xffdc, got 0x%04x\n", magic);
+		return -1;
+	}
+
+	len_words = header & 0xffff;
+	if (offset + len_words * 4 > buflen)
+		return -1;
+
+	value = be32toh(*(uint32_t *)(buf + offset));
+	offset += 4;
+
+	PR_ERROR("FFDC: Sequence = %u\n", value >> 16);
+	PR_ERROR("FFDC: Command = 0x%08x\n", value & 0xffff);
+
+	value = be32toh(*(uint32_t *)(buf + offset));
+	offset += 4;
+
+	PR_ERROR("FFDC: RC = 0x%08x\n", value);
+
+	for (i=0; i<len_words-3; i++) {
+		value = be32toh(*(uint32_t *)(buf + offset));
+		offset += 4;
+
+		PR_ERROR("FFDC: Data: 0x%08x\n", value);
+	}
+
+	return offset;
+}
+
+static int sbefifo_op_ffdc(uint8_t *buf, uint32_t buflen)
+{
+	uint32_t offset = 0;
+	int rc;
+
+	while (1) {
+		rc = sbefifo_op_ffdc_read(buf + offset, buflen - offset);
+		if (rc <= 0)
+			break;
+
+		offset += rc;
+	}
+
+	return rc;
+}
+
+static int sbefifo_op(struct sbefifo *sbefifo,
+		      uint32_t *msg, size_t msg_len, uint16_t cmd,
+		      size_t out_len, uint8_t **out)
+{
+	uint8_t *buf;
+	uint32_t resp[2];
+	size_t buflen;
+	uint32_t word_offset, offset;
+	uint16_t value;
+	int rc;
+
+	assert(msg_len > 0 && out_len > 0);
+
+	/* Allocate extra memory for FFDC (SBEFIFO_MAX_FFDC_SIZE = 0x2000) */
+	buflen = out_len + 0x2000;
+	buf = malloc(buflen);
+	assert(buf);
+
+	rc = sbefifo_op_write(sbefifo, msg, msg_len);
+	if (rc)
+		goto fail;
+
+	rc = sbefifo_op_read(sbefifo, buf, &buflen);
+	if (rc)
+		goto fail;
+
+	/*
+	 * At least 3 words are expected in the response
+	 * header word, status word, header offset word
+	 */
+	if (buflen < 3 * 4) {
+		PR_ERROR("sbefifo: Short read, expected %zu, got %zu\n",
+			 out_len + 3 * 4, buflen);
+		sbefifo_op_dump("DATA:", buf, buflen);
+		goto fail;
+	}
+
+	word_offset = be32toh(*(uint32_t *)(buf + buflen - 4));
+	PR_INFO("sbefifo: status header word offset = %u\n", word_offset);
+
+	offset = buflen - (word_offset * 4);
+
+	resp[0] = be32toh(*(uint32_t *)(buf + offset));
+	offset += 4;
+
+	resp[1] = be32toh(*(uint32_t *)(buf + offset));
+	offset += 4;
+
+	PR_INFO("sbefifo: response %08x %08x\n", resp[0], resp[1]);
+
+	value = resp[0] >> 16;
+	if (value != 0xc0de) {
+		PR_ERROR("sbefifo: Expected magic 0xc0de, got 0x%04x\n", value);
+		goto fail;
+	}
+
+	value = resp[0] & 0xffff;
+	if (value != cmd) {
+		PR_ERROR("sbefifo: Expected command 0x%04x, got 0x%04x\n", cmd, value);
+		goto fail;
+	}
+
+	if (resp[1] == 0) {
+		*out = buf;
+		return 0;
+	} else {
+		PR_ERROR("sbefifo: Operation failed, response=0x%08x\n", resp[1]);
+		sbefifo_op_ffdc(buf + offset, buflen - offset);
+	}
+
+fail:
+	free(buf);
+	return -1;
+}
+
+static int sbefifo_op_getmem(struct sbefifo *sbefifo,
+			     uint64_t addr, uint8_t *data, uint64_t size,
+			     bool ci)
+{
+	uint8_t *out;
+	uint64_t start_addr, end_addr;
+	uint32_t align, offset, len;
+	uint32_t msg[6];
+	uint32_t cmd, flags, count;
+	int rc;
+
+	align = ci ? 8 : 128;
+
+	start_addr = addr & (~(uint64_t)(align-1));
+	end_addr = (addr + size + (align-1)) & (~(uint64_t)(align-1));
+
+	if (end_addr - start_addr > UINT32_MAX) {
+		PR_ERROR("sbefifo: size too large\n");
+		return -EINVAL;
+	}
+
+	offset = addr - start_addr;
+	len = end_addr - start_addr;
+
+	PR_NOTICE("sbefifo: getmem addr=0x%016" PRIx64 ", len=%u\n",
+		  start_addr, len);
+
+	cmd = 0xa401;
+	flags = ci ? 0x0081 : 0x0002;
+
+	msg[0] = htobe32(6);	// number of words
+	msg[1] = htobe32(cmd);
+	msg[2] = htobe32(flags);
+	msg[3] = htobe32(start_addr >> 32);
+	msg[4] = htobe32(start_addr & 0xffffffff);
+	msg[5] = htobe32(len);
+
+	/* Return - data read + length of data read */
+	rc = sbefifo_op(sbefifo, msg, sizeof(msg), cmd, len+4, &out);
+	if (rc)
+		return rc;
+
+	memcpy(data, out+offset, size);
+	count = htobe32(*(uint32_t *)&out[len]);
+	free(out);
+
+	pdbg_progress_tick(count, len);
+
+	if (count != len) {
+		PR_ERROR("sbefifo: getmem read %u bytes of %u\n", count, len);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sbefifo_op_putmem(struct sbefifo *sbefifo,
+			     uint64_t addr, uint8_t *data, uint64_t size,
+			     bool ci)
+{
+	uint8_t *out;
+	uint32_t *msg;
+	uint32_t align, len, msg_len;
+	uint32_t cmd, flags, count;
+	int rc;
+
+	align = ci ? 8 : 128;
+
+	if (addr & (align-1)) {
+		PR_ERROR("sbefifo: Address must be aligned to %d bytes\n", align);
+		return -1;
+	}
+
+	if (size & (align-1)) {
+		PR_ERROR("sbefifo: Data must be multiple of %d bytes\n", align);
+		return -1;
+	}
+
+	if (size > UINT32_MAX) {
+		PR_ERROR("sbefifo: size too large\n");
+		return -1;
+	}
+
+	len = size & 0xffffffff;
+	msg_len = 6 * 4 + len;
+	msg = (uint32_t *)malloc(msg_len);
+	assert(msg);
+
+	PR_NOTICE("sbefifo: putmem addr=0x%016"PRIx64", len=%u\n", addr, len);
+
+	cmd = 0xa402;
+	flags = ci ? 0x0081 : 0x0002;
+
+	msg[0] = htobe32(msg_len/4);	// number of words
+	msg[1] = htobe32(cmd);
+	msg[2] = htobe32(flags);
+	msg[3] = htobe32(addr >> 32);
+	msg[4] = htobe32(addr & 0xffffffff);
+	msg[5] = htobe32(len);
+	memcpy(&msg[6], data, len);
+
+	/* Return - length of data written */
+	rc = sbefifo_op(sbefifo, msg, msg_len, cmd, 4, &out);
+	if (rc)
+		return rc;
+
+	count = be32toh(*(uint32_t *)out);
+	free(out);
+
+	pdbg_progress_tick(count, len);
+
+	if (count != len) {
+		PR_ERROR("sbefifo: putmem wrote %u bytes of %u\n", count, len);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sbefifo_probe(struct pdbg_target *target)
+{
+	struct sbefifo *sf = target_to_sbefifo(target);
+	const char *sbefifo_path;
+
+	sbefifo_path = pdbg_target_property(target, "device-path", NULL);
+	assert(sbefifo_path);
+
+	sf->fd = open(sbefifo_path, O_RDWR | O_SYNC);
+	if (sf->fd < 0) {
+		PR_ERROR("Unable to open sbefifo driver %s\n", sbefifo_path);
+		return -1;
+	}
+
+	return 0;
+}
+
+struct sbefifo kernel_sbefifo = {
+	.target = {
+		.name =	"Kernel based FSI SBE FIFO",
+		.compatible = "ibm,kernel-sbefifo",
+		.class = "sbefifo",
+		.probe = sbefifo_probe,
+	},
+	.mem_read = sbefifo_op_getmem,
+	.mem_write = sbefifo_op_putmem,
+	.fd = -1,
+};
+DECLARE_HW_UNIT(kernel_sbefifo);
diff --git a/libpdbg/target.c b/libpdbg/target.c
index a46bf06..b050bd1 100644
--- a/libpdbg/target.c
+++ b/libpdbg/target.c
@@ -218,22 +218,50 @@ int fsi_write(struct pdbg_target *fsi_dt, uint32_t addr, uint32_t data)
 
 int mem_read(struct pdbg_target *target, uint64_t addr, uint8_t *output, uint64_t size, uint8_t block_size, bool ci)
 {
-	struct adu *adu;
+	int rc = -1;
 
-	assert(pdbg_target_is_class(target, "adu"));
-	adu = target_to_adu(target);
+	assert(pdbg_target_is_class(target, "sbefifo") ||
+	       pdbg_target_is_class(target, "adu"));
 
-	return adu->read(adu, addr, output, size, block_size, ci);
+	if (pdbg_target_is_class(target, "sbefifo")) {
+		struct sbefifo *sbefifo;
+
+		sbefifo = target_to_sbefifo(target);
+		rc = sbefifo->mem_read(sbefifo, addr, output, size, ci);
+	}
+
+	if (pdbg_target_is_class(target, "adu")) {
+		struct adu *adu;
+
+		adu = target_to_adu(target);
+		rc = adu->read(adu, addr, output, size, block_size, ci);
+	}
+
+	return rc;
 }
 
 int mem_write(struct pdbg_target *target, uint64_t addr, uint8_t *input, uint64_t size, uint8_t block_size, bool ci)
 {
-	struct adu *adu;
+	int rc = -1;
 
-	assert(pdbg_target_is_class(target, "adu"));
-	adu = target_to_adu(target);
+	assert(pdbg_target_is_class(target, "sbefifo") ||
+	       pdbg_target_is_class(target, "adu"));
 
-	return adu->write(adu, addr, input, size, block_size, ci);
+	if (pdbg_target_is_class(target, "sbefifo")) {
+		struct sbefifo *sbefifo;
+
+		sbefifo = target_to_sbefifo(target);
+		rc = sbefifo->mem_write(sbefifo, addr, input, size, ci);
+	}
+
+	if (pdbg_target_is_class(target, "adu")) {
+		struct adu *adu;
+
+		adu = target_to_adu(target);
+		rc = adu->write(adu, addr, input, size, block_size, ci);
+	}
+
+	return rc;
 }
 
 struct pdbg_target *require_target_parent(struct pdbg_target *target)
diff --git a/libpdbg/target.h b/libpdbg/target.h
index 6b57142..2d45a6d 100644
--- a/libpdbg/target.h
+++ b/libpdbg/target.h
@@ -115,6 +115,14 @@ struct adu {
 };
 #define target_to_adu(x) container_of(x, struct adu, target)
 
+struct sbefifo {
+	struct pdbg_target target;
+	int (*mem_read)(struct sbefifo *, uint64_t, uint8_t *, uint64_t, bool);
+	int (*mem_write)(struct sbefifo *, uint64_t, uint8_t *, uint64_t, bool);
+	int fd;
+};
+#define target_to_sbefifo(x) container_of(x, struct sbefifo, target)
+
 struct pib {
 	struct pdbg_target target;
 	int (*read)(struct pib *, uint64_t, uint64_t *);
diff --git a/p9-kernel.dts.m4 b/p9-kernel.dts.m4
index aec0881..33d7988 100644
--- a/p9-kernel.dts.m4
+++ b/p9-kernel.dts.m4
@@ -23,6 +23,13 @@
 			include(p9-pib.dts.m4)dnl
 		};
 
+		sbefifo at 2400 { /* Bogus address */
+			reg = <0x0 0x2400 0x7>;
+			index = <0x0>;
+			compatible = "ibm,kernel-sbefifo";
+			device-path = "/dev/sbefifo1";
+		};
+
 		hmfsi at 100000 {
 			#address-cells = <0x2>;
 			#size-cells = <0x1>;
-- 
2.20.1



More information about the Pdbg mailing list