aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/external
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/external')
-rw-r--r--roms/skiboot/external/Makefile.check34
-rw-r--r--roms/skiboot/external/boot-tests/bmc_support.sh147
-rwxr-xr-xroms/skiboot/external/boot-tests/boot_test.sh303
-rwxr-xr-xroms/skiboot/external/boot-tests/extract_gcov.sh30
-rw-r--r--roms/skiboot/external/boot-tests/fsp_support.sh178
-rw-r--r--roms/skiboot/external/boot-tests/openbmc_support.sh129
-rw-r--r--roms/skiboot/external/boot-tests/smc_support.sh128
-rw-r--r--roms/skiboot/external/common/.gitignore3
-rw-r--r--roms/skiboot/external/common/arch_flash.h38
-rw-r--r--roms/skiboot/external/common/arch_flash_arm.c374
-rw-r--r--roms/skiboot/external/common/arch_flash_arm_io.h86
-rw-r--r--roms/skiboot/external/common/arch_flash_common.c50
-rw-r--r--roms/skiboot/external/common/arch_flash_powerpc.c242
-rw-r--r--roms/skiboot/external/common/arch_flash_powerpc_io.h0
-rw-r--r--roms/skiboot/external/common/arch_flash_unknown.c42
-rw-r--r--roms/skiboot/external/common/arch_flash_unknown_io.h0
-rwxr-xr-xroms/skiboot/external/common/get_arch.sh11
-rw-r--r--roms/skiboot/external/common/rules.mk76
-rw-r--r--roms/skiboot/external/devicetree/.gitignore1
-rw-r--r--roms/skiboot/external/devicetree/Makefile14
-rw-r--r--roms/skiboot/external/devicetree/p9-simics.dts21
-rw-r--r--roms/skiboot/external/devicetree/p9.dts212
-rw-r--r--roms/skiboot/external/ffspart/.gitignore7
-rw-r--r--roms/skiboot/external/ffspart/Makefile44
-rw-r--r--roms/skiboot/external/ffspart/config.h24
-rw-r--r--roms/skiboot/external/ffspart/ffspart.c548
-rw-r--r--roms/skiboot/external/ffspart/rules.mk37
-rw-r--r--roms/skiboot/external/ffspart/test/files/03-tiny-pnor.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/03-tiny-pnor.outbin0 -> 2560 bytes
-rw-r--r--roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.outbin0 -> 2560 bytes
-rw-r--r--roms/skiboot/external/ffspart/test/files/05-hdr-overlap.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/06-small-flash.in5
-rw-r--r--roms/skiboot/external/ffspart/test/files/07-big-files.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/08-small-files.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/10-bad-input.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/11-long-name.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/12-bad-numbers-base.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/13-bad-numbers-size.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/14-bad-input-flags.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/15-overlapping-partitions.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/16-create-blank.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/16-create-blank.outbin0 -> 2560 bytes
-rw-r--r--roms/skiboot/external/ffspart/test/files/17-toc.in5
-rw-r--r--roms/skiboot/external/ffspart/test/files/17-toc.outbin0 -> 2560 bytes
-rw-r--r--roms/skiboot/external/ffspart/test/files/18-eraseblock-gt-first-partition.in2
-rwxr-xr-xroms/skiboot/external/ffspart/test/make-check-test1
-rw-r--r--roms/skiboot/external/ffspart/test/results/00-usage.err0
-rw-r--r--roms/skiboot/external/ffspart/test/results/00-usage.out19
-rw-r--r--roms/skiboot/external/ffspart/test/results/01-param-sanity.err0
-rw-r--r--roms/skiboot/external/ffspart/test/results/01-param-sanity.out19
-rw-r--r--roms/skiboot/external/ffspart/test/results/01.1-param-sanity.err0
-rw-r--r--roms/skiboot/external/ffspart/test/results/01.1-param-sanity.out19
-rw-r--r--roms/skiboot/external/ffspart/test/results/05-hdr-overlap.err5
-rw-r--r--roms/skiboot/external/ffspart/test/results/05-hdr-overlap.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/06-small-flash.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/06-small-flash.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/07-big-files.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/07-big-files.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/08-small-files.err2
-rw-r--r--roms/skiboot/external/ffspart/test/results/08-small-files.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/10-bad-input.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/10-bad-input.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/11-long-name.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/11-long-name.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/14-bad-input-flags.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/14-bad-input-flags.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.out0
-rwxr-xr-xroms/skiboot/external/ffspart/test/test-ffspart5
-rw-r--r--roms/skiboot/external/ffspart/test/tests/00-usage12
-rw-r--r--roms/skiboot/external/ffspart/test/tests/01-param-sanity12
-rw-r--r--roms/skiboot/external/ffspart/test/tests/01.1-param-sanity12
-rw-r--r--roms/skiboot/external/ffspart/test/tests/03-tiny-pnor15
-rw-r--r--roms/skiboot/external/ffspart/test/tests/04-tiny-pnor225
-rw-r--r--roms/skiboot/external/ffspart/test/tests/05-hdr-overlap14
-rw-r--r--roms/skiboot/external/ffspart/test/tests/06-small-flash13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/07-big-files26
-rw-r--r--roms/skiboot/external/ffspart/test/tests/08-small-files22
-rw-r--r--roms/skiboot/external/ffspart/test/tests/10-bad-input13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/11-long-name11
-rw-r--r--roms/skiboot/external/ffspart/test/tests/12-bad-numbers-base13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/13-bad-numbers-size13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/14-bad-input-flags13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/15-overlapping-partitions13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/16-create-blank15
-rw-r--r--roms/skiboot/external/ffspart/test/tests/17-toc15
-rw-r--r--roms/skiboot/external/ffspart/test/tests/18-eraseblock-gt-first-partition45
-rwxr-xr-xroms/skiboot/external/fwts/generate-fwts-olog227
-rwxr-xr-xroms/skiboot/external/fwts/merge-fwts-olog103
-rw-r--r--roms/skiboot/external/gard/.gitignore11
-rw-r--r--roms/skiboot/external/gard/Makefile43
-rw-r--r--roms/skiboot/external/gard/Makefile.dist17
-rw-r--r--roms/skiboot/external/gard/config.h33
-rw-r--r--roms/skiboot/external/gard/gard.c1017
-rw-r--r--roms/skiboot/external/gard/gard.h74
-rw-r--r--roms/skiboot/external/gard/opal-gard.130
-rw-r--r--roms/skiboot/external/gard/rules.mk51
-rwxr-xr-xroms/skiboot/external/gard/test/add_test.sh67
-rw-r--r--roms/skiboot/external/gard/test/files/blank.binbin0 -> 20480 bytes
-rw-r--r--roms/skiboot/external/gard/test/files/data-p9.binbin0 -> 20480 bytes
-rw-r--r--roms/skiboot/external/gard/test/files/data1.binbin0 -> 20480 bytes
-rwxr-xr-xroms/skiboot/external/gard/test/make-check-test1
-rw-r--r--roms/skiboot/external/gard/test/results/00-list.err0
-rw-r--r--roms/skiboot/external/gard/test/results/00-list.out5
-rw-r--r--roms/skiboot/external/gard/test/results/01-show_1.err0
-rw-r--r--roms/skiboot/external/gard/test/results/01-show_1.out8
-rw-r--r--roms/skiboot/external/gard/test/results/02-usage.err24
-rw-r--r--roms/skiboot/external/gard/test/results/02-usage.out1
-rw-r--r--roms/skiboot/external/gard/test/results/03-show_1-p9.err0
-rw-r--r--roms/skiboot/external/gard/test/results/03-show_1-p9.out10
-rw-r--r--roms/skiboot/external/gard/test/results/04-create-bad-instance.err2
-rw-r--r--roms/skiboot/external/gard/test/results/04-create-bad-instance.out0
-rw-r--r--roms/skiboot/external/gard/test/results/05-create-bad-unit.err2
-rw-r--r--roms/skiboot/external/gard/test/results/05-create-bad-unit.out0
-rw-r--r--roms/skiboot/external/gard/test/results/06-create-long-path.err2
-rw-r--r--roms/skiboot/external/gard/test/results/06-create-long-path.out0
-rw-r--r--roms/skiboot/external/gard/test/results/07-create-slash.err2
-rw-r--r--roms/skiboot/external/gard/test/results/07-create-slash.out0
-rw-r--r--roms/skiboot/external/gard/test/results/08-create-duplicate.err1
-rw-r--r--roms/skiboot/external/gard/test/results/08-create-duplicate.out0
-rw-r--r--roms/skiboot/external/gard/test/results/09-create-last-unit.err0
-rw-r--r--roms/skiboot/external/gard/test/results/09-create-last-unit.out4
-rw-r--r--roms/skiboot/external/gard/test/results/10-clear-single.err0
-rw-r--r--roms/skiboot/external/gard/test/results/10-clear-single.out7
-rw-r--r--roms/skiboot/external/gard/test/results/11-clear-first.err0
-rw-r--r--roms/skiboot/external/gard/test/results/11-clear-first.out13
-rwxr-xr-xroms/skiboot/external/gard/test/test-gard5
-rw-r--r--roms/skiboot/external/gard/test/tests/00-list10
-rw-r--r--roms/skiboot/external/gard/test/tests/01-show_110
-rw-r--r--roms/skiboot/external/gard/test/tests/02-usage17
-rw-r--r--roms/skiboot/external/gard/test/tests/03-show_1-p910
-rw-r--r--roms/skiboot/external/gard/test/tests/04-create-bad-instance10
-rw-r--r--roms/skiboot/external/gard/test/tests/05-create-bad-unit10
-rw-r--r--roms/skiboot/external/gard/test/tests/06-create-long-path10
-rw-r--r--roms/skiboot/external/gard/test/tests/07-create-slash10
-rw-r--r--roms/skiboot/external/gard/test/tests/08-create-duplicate10
-rw-r--r--roms/skiboot/external/gard/test/tests/09-create-last-unit16
-rw-r--r--roms/skiboot/external/gard/test/tests/10-clear-single23
-rw-r--r--roms/skiboot/external/gard/test/tests/11-clear-first26
-rw-r--r--roms/skiboot/external/gard/units.c242
-rw-r--r--roms/skiboot/external/lpc/Makefile8
-rw-r--r--roms/skiboot/external/lpc/lpc.c178
-rw-r--r--roms/skiboot/external/mambo/Makefile10
-rw-r--r--roms/skiboot/external/mambo/README.md73
-rw-r--r--roms/skiboot/external/mambo/cvc.binbin0 -> 9984 bytes
-rw-r--r--roms/skiboot/external/mambo/mambo-socket-proxy.c347
-rw-r--r--roms/skiboot/external/mambo/mambo_utils.tcl794
-rw-r--r--roms/skiboot/external/mambo/qtrace_utils.tcl16
-rw-r--r--roms/skiboot/external/mambo/skiboot.tcl732
-rw-r--r--roms/skiboot/external/memboot/Makefile15
-rw-r--r--roms/skiboot/external/memboot/README24
-rw-r--r--roms/skiboot/external/memboot/memboot.c143
-rwxr-xr-xroms/skiboot/external/npu/run_procedure.sh109
-rw-r--r--roms/skiboot/external/opal-prd/.gitignore5
-rw-r--r--roms/skiboot/external/opal-prd/Makefile75
-rw-r--r--roms/skiboot/external/opal-prd/config.h24
-rw-r--r--roms/skiboot/external/opal-prd/hostboot-interface.h759
-rw-r--r--roms/skiboot/external/opal-prd/i2c.c257
-rw-r--r--roms/skiboot/external/opal-prd/i2c.h17
-rw-r--r--roms/skiboot/external/opal-prd/module.c49
-rw-r--r--roms/skiboot/external/opal-prd/module.h10
-rw-r--r--roms/skiboot/external/opal-prd/opal-prd.878
-rw-r--r--roms/skiboot/external/opal-prd/opal-prd.c2799
-rw-r--r--roms/skiboot/external/opal-prd/opal-prd.h15
-rw-r--r--roms/skiboot/external/opal-prd/opal-prd.service11
-rw-r--r--roms/skiboot/external/opal-prd/pnor.c217
-rw-r--r--roms/skiboot/external/opal-prd/pnor.h31
-rw-r--r--roms/skiboot/external/opal-prd/test/test_pnor.c46
-rw-r--r--roms/skiboot/external/opal-prd/test/test_pnor_ops.c238
-rw-r--r--roms/skiboot/external/opal-prd/thunk.S213
-rwxr-xr-xroms/skiboot/external/pci-scripts/phberr.py658
-rw-r--r--roms/skiboot/external/pci-scripts/ppc.py28
-rw-r--r--roms/skiboot/external/pflash/.gitignore6
-rw-r--r--roms/skiboot/external/pflash/Makefile48
-rw-r--r--roms/skiboot/external/pflash/Makefile.dist11
-rw-r--r--roms/skiboot/external/pflash/TODO5
-rwxr-xr-xroms/skiboot/external/pflash/build-all-arch.sh35
-rw-r--r--roms/skiboot/external/pflash/config.h24
-rw-r--r--roms/skiboot/external/pflash/pflash.196
-rw-r--r--roms/skiboot/external/pflash/pflash.c1230
-rw-r--r--roms/skiboot/external/pflash/progress.c92
-rw-r--r--roms/skiboot/external/pflash/progress.h15
-rw-r--r--roms/skiboot/external/pflash/rules.mk57
-rw-r--r--roms/skiboot/external/pflash/test/files/01-info.ffs6
-rw-r--r--roms/skiboot/external/pflash/test/files/02-erase.ffs4
-rw-r--r--roms/skiboot/external/pflash/test/files/03-erase-parts.ffs4
-rw-r--r--roms/skiboot/external/pflash/test/files/04-program-rand.ffs4
-rw-r--r--roms/skiboot/external/pflash/test/files/05-bad-numbers.ffs4
-rw-r--r--roms/skiboot/external/pflash/test/files/06-miscprint.ffs6
-rwxr-xr-xroms/skiboot/external/pflash/test/make-check-test1
-rw-r--r--roms/skiboot/external/pflash/test/results/00-usage.err0
-rw-r--r--roms/skiboot/external/pflash/test/results/00-usage.out114
-rw-r--r--roms/skiboot/external/pflash/test/results/01-info.err0
-rw-r--r--roms/skiboot/external/pflash/test/results/01-info.out15
-rw-r--r--roms/skiboot/external/pflash/test/results/02-erase.err0
-rw-r--r--roms/skiboot/external/pflash/test/results/02-erase.out105
-rw-r--r--roms/skiboot/external/pflash/test/results/03-erase-parts.err0
-rw-r--r--roms/skiboot/external/pflash/test/results/03-erase-parts.out106
-rw-r--r--roms/skiboot/external/pflash/test/results/04-program-rand.err0
-rw-r--r--roms/skiboot/external/pflash/test/results/04-program-rand.out111
-rw-r--r--roms/skiboot/external/pflash/test/results/05-bad-numbers.err0
-rw-r--r--roms/skiboot/external/pflash/test/results/05-bad-numbers.out224
-rw-r--r--roms/skiboot/external/pflash/test/results/06-miscprint.err0
-rw-r--r--roms/skiboot/external/pflash/test/results/06-miscprint.out54
-rwxr-xr-xroms/skiboot/external/pflash/test/test-pflash59
-rw-r--r--roms/skiboot/external/pflash/test/tests/00-usage13
-rw-r--r--roms/skiboot/external/pflash/test/tests/01-info23
-rw-r--r--roms/skiboot/external/pflash/test/tests/02-erase27
-rw-r--r--roms/skiboot/external/pflash/test/tests/03-erase-parts56
-rw-r--r--roms/skiboot/external/pflash/test/tests/04-program-rand55
-rw-r--r--roms/skiboot/external/pflash/test/tests/05-bad-numbers66
-rw-r--r--roms/skiboot/external/pflash/test/tests/06-miscprint46
-rw-r--r--roms/skiboot/external/read_esel.sh44
-rw-r--r--roms/skiboot/external/shared/Makefile54
-rw-r--r--roms/skiboot/external/shared/config.h24
-rw-r--r--roms/skiboot/external/shared/rules.mk19
-rwxr-xr-xroms/skiboot/external/test/test.sh101
-rw-r--r--roms/skiboot/external/trace/Makefile8
-rw-r--r--roms/skiboot/external/trace/dump_trace.c366
-rw-r--r--roms/skiboot/external/trace/trace.c110
-rw-r--r--roms/skiboot/external/trace/trace.h28
-rw-r--r--roms/skiboot/external/xscom-utils/.gitignore3
-rw-r--r--roms/skiboot/external/xscom-utils/Makefile61
-rwxr-xr-xroms/skiboot/external/xscom-utils/adu_scoms.py300
-rw-r--r--roms/skiboot/external/xscom-utils/getscom.123
-rw-r--r--roms/skiboot/external/xscom-utils/getscom.c167
-rw-r--r--roms/skiboot/external/xscom-utils/getsram.123
-rw-r--r--roms/skiboot/external/xscom-utils/getsram.c132
-rw-r--r--roms/skiboot/external/xscom-utils/putscom.118
-rw-r--r--roms/skiboot/external/xscom-utils/putscom.c115
-rw-r--r--roms/skiboot/external/xscom-utils/sram.c139
-rw-r--r--roms/skiboot/external/xscom-utils/sram.h14
-rw-r--r--roms/skiboot/external/xscom-utils/xscom.c206
-rw-r--r--roms/skiboot/external/xscom-utils/xscom.h26
239 files changed, 18089 insertions, 0 deletions
diff --git a/roms/skiboot/external/Makefile.check b/roms/skiboot/external/Makefile.check
new file mode 100644
index 000000000..d3d04c7ff
--- /dev/null
+++ b/roms/skiboot/external/Makefile.check
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+# -*-Makefile-*-
+
+TOOL=gard ffspart pflash
+CHECK_TOOL=$(patsubst %,check-%,$(TOOL))
+TOOL_COVERAGE=$(patsubst %,%-coverage,$(TOOL))
+TOOL_TEST_CLEAN=$(patsubst %,%-test-clean,$(TOOL))
+
+.PHONY: external-check
+external-check: $(CHECK_TOOL)
+
+.PHONY: check
+check: external-check
+
+#Makefile knows to build and clean it before checking, should also
+#make clean before checking. If not, .o files for different
+#architectures might be lying around and clean once done to avoid the
+#opposite
+.PHONY: $(CHECK_TOOL)
+$(CHECK_TOOL):
+ $(call QTEST, RUN-TEST , make CC=$(HOSTCC) CROSS='' CROSS_COMPILE='' \
+ -C external/$(patsubst check-%,%,$@) check, $@)
+
+clean: $(TOOL_TEST_CLEAN)
+
+.PHONY: $(TOOL_COVERAGE)
+coverage: $(TOOL_COVERAGE)
+
+$(TOOL_COVERAGE): $(patsubst %-coverage, check-%, $@)
+
+.PHONY: $(TOOL_TEST_CLEAN)
+$(TOOL_TEST_CLEAN):
+ $(call QTEST, CLEANUP , make -C external/$(patsubst %-test-clean,%,$@) clean, $@)
+
diff --git a/roms/skiboot/external/boot-tests/bmc_support.sh b/roms/skiboot/external/boot-tests/bmc_support.sh
new file mode 100644
index 000000000..82b9e8ece
--- /dev/null
+++ b/roms/skiboot/external/boot-tests/bmc_support.sh
@@ -0,0 +1,147 @@
+#Number of times to sleep
+BOOT_TIMEOUT="5";
+
+#Path to memboot binary
+#MEMBOOT=${MEMBOOT:-memboot};
+
+#Username/password for ssh to BMC machines
+SSHUSER=${SSHUSER:-sysadmin};
+export SSHPASS=${SSHPASS:-superuser};
+
+#Username/password for IPMI
+IPMI_AUTH="-U ${IPMI_USER:-admin} -P ${IPMI_PASS:-admin}"
+PFLASH_TO_COPY=${PFLASH_TO_COPY:-}
+PFLASH_BINARY=/usr/local/bin/pflash
+
+# Strip control characters from IPMI before grepping?
+STRIP_CONTROL=0
+
+# How do we SSH/SCP in?
+SSHCMD="sshpass -e ssh -l $SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $target";
+
+function sshcmd {
+ $SSHCMD $*;
+}
+
+# remotecp file target target_location
+function remotecp {
+ sshpass -e ssh -o User=$SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $2 dd of=$3 < $1;
+}
+
+function is_off {
+ return $([ "$($IPMI_COMMAND chassis power status)" = "Chassis Power is off" ]);
+}
+
+function poweroff {
+ $IPMI_COMMAND chassis power off
+ # give it some time
+ sleep 10
+}
+
+function force_primary_side {
+ # Now we force booting from primary (not golden) side
+ $IPMI_COMMAND raw 0x04 0x30 0x5c 0x01 0x00 0x00 0 0 0 0 0 0
+ # and from somewhere else we get this raw command. Obvious really.
+ $IPMI_COMMAND raw 0x04 0x30 0xd2 0x01 0x00 0x00 0 0 0 0 0 0
+ sleep 8
+}
+
+function flash {
+ if [ ! -z "$PFLASH_TO_COPY" ]; then
+ remotecp $PFLASH_TO_COPY $target /tmp/pflash
+ $SSHCMD chmod +x /tmp/pflash
+ PFLASH_BINARY=/tmp/pflash
+ fi
+ if [ ! -z "$PNOR" ]; then
+ remotecp $PNOR $target /tmp/image.pnor;
+ fi
+ if [ "${LID[0]}" != "" ]; then
+ remotecp ${LID[0]} $target /tmp/skiboot.lid;
+ fi
+ if [ "${LID[1]}" != "" ]; then
+ remotecp ${LID[1]} $target /tmp/bootkernel
+ fi
+ if [ "${arbitrary_lid[1]}" != "" ]; then
+ remotecp ${arbitrary_lid[1]} $target /tmp/$(basename ${arbitrary_lid[1]})
+ fi
+
+ if [ "$?" -ne "0" ] ; then
+ error "Couldn't copy firmware image";
+ fi
+
+ # Habenaro doesn't have md5sum
+ #flash_md5=$(md5sum "$1" | cut -f 1 -d ' ');
+ #$SSHCMD "flash_md5r=\$(md5sum /tmp/image.pnor | cut -f 1 -d ' ');
+ # if [ \"$flash_md5\" != \"\$flash_md5r\" ] ; then
+ # exit 1;
+ # fi";
+ #if [ "$?" -ne "0" ] ; then
+ # error "Firmware MD5s don't match";
+ #fi
+
+ # flash it
+ if [ ! -z "$PNOR" ]; then
+ msg "Flashing full PNOR"
+ $SSHCMD "$PFLASH_BINARY -E -f -p /tmp/image.pnor"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/image.pnor"
+ $SSHCMD "rm /tmp/image.pnor"
+ fi
+
+ if [ ! -z "${LID[0]}" ] ; then
+ msg "Flashing PAYLOAD PNOR partition"
+ $SSHCMD "$PFLASH_BINARY -e -f -P PAYLOAD -p /tmp/skiboot.lid"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/pskiboot.lid"
+ $SSHCMD "rm /tmp/skiboot.lid"
+ fi
+
+ if [ ! -z "${LID[1]}" ] ; then
+ msg "Flashing BOOTKERNEL PNOR partition"
+ $SSHCMD "$PFLASH_BINARY -e -f -P BOOTKERNEL -p /tmp/bootkernel"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/bootkernel"
+ $SSHCMD "rm /tmp/bootkernel"
+ fi
+
+ if [ ! -z "${arbitrary_lid[0]}" -a ! -z "${arbitrary_lid[1]}" ] ; then
+ msg "Flashing ${arbitrary_lid[0]} PNOR partition"
+ $SSHCMD "$PFLASH_BINARY -e -f -P ${arbitrary_lid[0]} -p /tmp/$(basename ${arbitrary_lid[1]})"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/$(basename ${arbitrary_lid[1]})"
+ $SSHCMD "rm /tmp/$(basename ${arbitrary_lid[1]})"
+ fi
+
+}
+
+function boot_firmware {
+ $IPMI_COMMAND chassis power on > /dev/null;
+ i=0;
+ while [ "$($IPMI_COMMAND chassis power status)" = "Chassis Power is off" -a \( "$i" -lt "$BOOT_TIMEOUT" \) ] ; do
+ msg -n ".";
+ sleep $BOOT_SLEEP_PERIOD;
+ i=$(expr $i + 1);
+ done
+ if [ "$i" -eq "$BOOT_TIMEOUT" ] ; then
+ error "Couldn't power on $target";
+ fi
+}
+
+function machine_sanity_test {
+ sshcmd true;
+ if [ $? -ne 0 ]; then
+ echo "$target: Failed to SSH to $target..."
+ echo "$target: Command was: $SSHCMD true"
+ error "Try connecting manually to diagnose the issue."
+ fi
+ # No further sanity tests for BMC machines.
+ true
+}
diff --git a/roms/skiboot/external/boot-tests/boot_test.sh b/roms/skiboot/external/boot-tests/boot_test.sh
new file mode 100755
index 000000000..233467c23
--- /dev/null
+++ b/roms/skiboot/external/boot-tests/boot_test.sh
@@ -0,0 +1,303 @@
+#!/bin/bash
+# Lets try for /bin/sh but bashisms will sneak in.
+
+# partial bash strict mode
+set -uo pipefail
+
+V=0;
+target=""
+
+if [ -f ~/.skiboot_boot_tests ]; then
+ source ~/.skiboot_boot_tests
+fi
+
+# Utility functions
+function error {
+ unset SSHPASS
+ if [ ! -z "$target" ]; then
+ echo "$target: $*" >&2
+ else
+ echo "$0: $*" >&2
+ fi
+
+ exit 1
+}
+
+function msg {
+ if [ $V -ne 0 ]; then
+ if [ ! -z "$target" ]; then
+ echo "$target: $*"
+ else
+ echo "$0: $*"
+ fi
+ fi
+}
+
+# Generic conf
+BOOT_SLEEP_PERIOD=10
+FUNCTIONS_NEEDED="sshpass ssh ipmitool md5sum rsync expect";
+
+function linux_boot {
+ if [ $STRIP_CONTROL -eq 1 ]; then
+ STRIPCOMMAND="col -b -l 1"
+ else
+ STRIPCOMMAND="cat"
+ fi
+
+ #Everyone is going to forget to disconnect - force them off
+ ipmiresult=$($IPMI_COMMAND sol deactivate 2>&1);
+ retval=$?
+ if [ $retval -ne 0 -a "$ipmiresult" != "Info: SOL payload already de-activated" ]; then
+ msg "IPMI sol deactivate failed; IPMI may have stalled, may just be IPMI. Good luck."
+ fi
+
+ LINUXBOOT_LOG=$(mktemp --tmpdir boot-test-$target.XXXXXX);
+ cat <<EOF | expect > $LINUXBOOT_LOG
+set timeout 300
+spawn $IPMI_COMMAND sol activate
+expect {
+timeout { send_user "\nTimeout waiting for petitboot\n"; exit 1 }
+eof { send_user "\nUnexpected EOF\n;" exit 1 }
+"Welcome to Petitboot"
+}
+
+close
+exit 0
+EOF
+ retval=$?
+ $IPMI_COMMAND sol deactivate > /dev/null;
+ if [ $retval -ne 0 ]; then
+ msg "Waiting for linux has timed out"
+ msg "Boot log follows:"
+ cat $LINUXBOOT_LOG
+ if [ $keep_log_failure -eq 0 ]; then
+ rm -f $LINUXBOOT_LOG
+ fi
+ return 1
+ else
+ if [ $keep_log_success -eq 0 ]; then
+ rm -f $LINUXBOOT_LOG
+ fi
+ return 0
+ fi
+}
+
+function boot_test {
+ # The functions called (e.g. flash, boot) are from the *_support files
+ if [ $bootonly -ne 1 ]; then
+ msg "Flashing ${target}..."
+ flash $@;
+ fi
+
+ if [ $nobooting -ne 1 ] ; then
+ msg "Booting $target..."
+ boot_firmware;
+ msg "firmware looks good, waiting for linux";
+
+ linux_boot;
+ if [ $? -ne 0 ] ; then
+ error "Couldn't reach petitboot on $target";
+ fi
+ msg "$target has booted";
+ fi
+ unset SSHPASS;
+}
+
+function sanity_test {
+ $IPMI_COMMAND chassis power status > /dev/null;
+ if [ $? -ne 0 ]; then
+ echo "$target: Failed to connect to $target with IPMI..."
+ echo "$target: Command was: $IPMI_COMMAND chassis power status"
+ error "Try connecting manually to diagnose the issue."
+ fi
+
+ # do further machine-type specific tests
+ machine_sanity_test
+}
+
+function usage {
+ cat <<EOF
+boot_test.sh tests the bootability of a given target, optionally after
+ flashing new firmware onto the target.
+
+There are three usage modes.
+
+1) boot_test.sh -h
+ Print this help
+
+2) boot_test.sh [-vdp] -t target -B -b (fsp|bmc|smc|openbmc)
+ Boot test the target without flashing. Specify the type of machine
+ (FSP, BMC, SMC, OpenBMC) with the -b option.
+
+3) boot_test.sh [-vdp] -b bmc -t target -P pnor [-N]
+ boot_test.sh [-vdp] -b bmc -t target [-1 PAYLOAD] [-2 BOOTKERNEL] [-N]
+ boot_test.sh [-vdp] -b bmc -t target [-F eyecatcher:lid] [-N]
+ boot_test.sh [-vdp] -b fsp -t target [-1 lid1] [-2 lid2] [-3 lid3]
+
+ Flash the given firmware before boot testing.
+
+ For a BMC target, -P specifies a full PNOR.
+
+ For a BMC target, -1/-2 specify the PAYLOAD and BOOTKERNEL PNOR partitions
+ respectively; -e specifies the partition name for -3.
+ Only the given partitions will be flashed.
+
+ For an FSP target, -1/-2/-3 specify lids. Any combination of lids is
+ acceptable.
+
+Common Options:
+
+ -p powers off the machine if it is running. Without -p, a running machine
+ will cause the script to error out.
+
+ -v makes the script print some progress messages. Recommended.
+
+ -d makes the script print lots of things (set -vx).
+ Only use this for debugging the script: it's highly likely that
+ successful booting into Petitboot will not be detected with this option.
+
+ -b BMC type (bmc or fsp).
+
+ -k keep logs on failure.
+
+ -K keep logs on success or failure.
+
+ -N No booting.
+EOF
+ exit 1;
+}
+
+## 'Main' script begins
+
+# Check prereqs
+for func in $FUNCTIONS_NEEDED ; do
+ if ! command -v "$func" &> /dev/null ; then
+ error "I require command $func but it is not in \$PATH ($PATH)";
+ fi
+done
+
+# Parse options
+V=0;
+bootonly=0;
+nobooting=0;
+powerdown=0;
+firmware_supplied=0;
+target=""
+method=""
+PNOR=""
+arbitrary_lid[0]=""
+arbitrary_lid[1]=""
+LID[0]=""
+LID[1]=""
+LID[2]=""
+keep_log_success=0
+keep_log_failure=0
+while getopts "kKhvdpB1:2:3:P:t:b:F:N" OPT; do
+ case "$OPT" in
+ v)
+ V=1;
+ ;;
+ h)
+ usage;
+ ;;
+ d)
+ set -vx;
+ ;;
+ k)
+ keep_log_failure=1;
+ ;;
+ K)
+ keep_log_failure=1;
+ keep_log_success=1;
+ ;;
+ B)
+ bootonly=1;
+ if [ $firmware_supplied -eq 1 ]; then
+ usage
+ fi
+ ;;
+ N)
+ nobooting=1;
+ if [ $firmware_supplied -eq 0 ] ; then
+ error "Firmware not supplied."
+ fi
+ ;;
+ p)
+ powerdown=1;
+ ;;
+ b)
+ method=$OPTARG;
+ ;;
+ 1|2|3)
+ firmware_supplied=1;
+ if [ ! -e "$OPTARG" ] ; then
+ error "Couldn't stat $OPTARG";
+ fi
+ LID[$(expr ${OPT} - 1)]="$OPTARG"
+ ;;
+ P)
+ firmware_supplied=1;
+ if [ ! -e "$OPTARG" ] ; then
+ error "Couldn't stat $OPTARG";
+ fi
+ PNOR="$OPTARG"
+ ;;
+ F)
+ firmware_supplied=1;
+ arbitrary_lid[0]=`echo "$OPTARG" | cut -s -f1 -d:`;
+ arbitrary_lid[1]=`echo "$OPTARG" | cut -s -f2 -d:`;
+ if [ -z "${arbitrary_lid[0]}" -o -z "${arbitrary_lid[1]}" ] ; then
+ error "-F must be in the format eyecatcher:lid, e.g. GARD:gard.bin";
+ fi
+ ;;
+ t)
+ target=$OPTARG;
+ ;;
+ \?)
+ usage;
+ ;;
+ esac
+done
+
+shift $(expr $OPTIND - 1);
+
+# Pull out the target and test
+if [ "$target" = "" ]; then
+ usage;
+fi
+
+if ! ping -c 1 "$target" &> /dev/null ; then
+ error "Couldn't ping $target";
+fi
+
+if [ "$#" -ne 0 ]; then
+ usage
+fi
+
+
+# pull in the relevant config file and set things up
+source $(dirname $(readlink -f $0))/${method}_support.sh
+IPMI_COMMAND="ipmitool -I lanplus -H $target $IPMI_AUTH"
+
+msg "Running sanity test"
+sanity_test
+msg "Passed."
+
+# check the target is down
+# (pulls in is_off from ${method}_support.sh)
+if ! is_off; then
+ if [ $powerdown -eq 1 ]; then
+ poweroff
+ else
+ error "$target is not turned off";
+ fi
+fi
+
+force_primary_side # ensure we're booting from side we flash.
+
+# run the boot test
+echo "$target: Boot testing $target";
+begin_t=$(date +%s);
+boot_test
+
+echo "$target: Done in $(expr $(date +%s) - $begin_t ) seconds";
diff --git a/roms/skiboot/external/boot-tests/extract_gcov.sh b/roms/skiboot/external/boot-tests/extract_gcov.sh
new file mode 100755
index 000000000..c40e01e56
--- /dev/null
+++ b/roms/skiboot/external/boot-tests/extract_gcov.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+if [ "$SKIBOOT_GCOV" != 1 ]; then
+ echo "Skipping GCOV test on physical hardware. Enable with SKIBOOT_GCOV=1"
+ exit 0;
+fi
+
+if [ ! -f ~/.skiboot_boot_tests ]; then
+ if [ -z $FSPSSHUSER ] || [ -z $FSPSSHPASS ] ; then
+ echo "Skipping extract gcov due to missing ~/.skiboot_boot_tests"
+ echo "Set FSPSSHUSER and FSPSSHPASS in ~/.skiboot_boot_tests"
+ exit 0;
+ fi
+fi
+
+source ~/.skiboot_boot_tests
+
+target=$1
+SSHUSER=$FSPSSHUSER
+SSHPASS=$FSPSSHPASS
+
+export SSHUSER SSHPASS
+
+SSHCMD="sshpass -e ssh -l $SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $target";
+REMOTECPCMD="sshpass -e scp -o User=$SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ";
+
+$SSHCMD rm -f skiboot-$target.dump
+echo "Dumping skiboot memory from host: $target... (takes time)"
+$SSHCMD sh --login -c \"getmemproc 30000000 3145728 -fb skiboot-$target.dump\"
+$REMOTECPCMD $target:skiboot-$target.dump skiboot-$target.dump
diff --git a/roms/skiboot/external/boot-tests/fsp_support.sh b/roms/skiboot/external/boot-tests/fsp_support.sh
new file mode 100644
index 000000000..fc0d66d16
--- /dev/null
+++ b/roms/skiboot/external/boot-tests/fsp_support.sh
@@ -0,0 +1,178 @@
+#Number of times to sleep
+BOOT_TIMEOUT="20";
+
+#Username/password for for ssh to FSP machines
+SSHUSER=${FSPSSHUSER:-}
+SSHPASS=${FSPSSHPASS:-}
+
+if [ -z $SSHUSER ] || [ -z $SSHPASS ] ; then
+ msg "Set FSPSSHUSER and FSPSSHPASS in ENV or ~/.skiboot_boot_tests"
+ exit 1;
+fi
+
+export SSHUSER SSHPASS
+
+#IPMI
+IPMI_AUTH="-P ${IPMI_PASS:-foo}";
+
+# Strip control characters from IPMI before grepping?
+STRIP_CONTROL=1
+
+# How do we SSH in, cp files across?
+SSHCMD="sshpass -e ssh -l $SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $target";
+REMOTECPCMD="sshpass -e scp -o User=$SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ";
+
+GET_PROFILE='. /etc/profile; test -e /home/dev/.profile && . /home/dev/.profile';
+
+function sshcmd {
+ $SSHCMD $*;
+}
+
+function is_off {
+ state=$($SSHCMD "$GET_PROFILE; smgr mfgState");
+ return $([ "$state" = "standby" ]);
+}
+
+function poweroff {
+ i=0;
+ state=$($SSHCMD "$GET_PROFILE; smgr mfgState");
+ if [ "$state" = "standby" ]; then
+ # already off
+ return 0
+ fi
+ $SSHCMD "$GET_PROFILE; panlexec -f 8";
+ msg "Waiting 30 seconds..."
+ sleep 30
+ state=$($SSHCMD "$GET_PROFILE; smgr mfgState");
+ while [ "$state" != "standby" -a "$i" -lt "$BOOT_TIMEOUT" ] ; do
+ msg "Waiting $BOOT_SLEEP_PERIOD more seconds..."
+ sleep $BOOT_SLEEP_PERIOD;
+ i=$(expr $i + 1);
+ state=$($SSHCMD "$GET_PROFILE; smgr mfgState");
+ done;
+ # sleep a little bit longer --- p81 was getting a bit confused.
+ sleep 10
+ msg "Finishing with state '$state'."
+}
+
+function force_primary_side {
+ return 0
+}
+
+function flash {
+ #Make a backup of the current lids
+ $REMOTECPCMD $target:/opt/extucode/80f00100.lid 80f00100.lid.bak &&
+ $REMOTECPCMD $target:/opt/extucode/80f00101.lid 80f00101.lid.bak &&
+ $REMOTECPCMD $target:/opt/extucode/80f00102.lid 80f00102.lid.bak;
+ if [ $? -ne 0 ] ; then
+ error "Couldn't make backup of currently installed lids";
+ fi
+
+ if [ "${LID[0]}" != "" ]; then
+ $REMOTECPCMD ${LID[0]} $target:/opt/extucode/80f00100.lid ||
+ error "Error copying lid ${LID[0]}";
+ sum=$(md5sum ${LID[0]} | cut -f 1 -d ' ');
+ $SSHCMD "$GET_PROFILE;
+ sumr=\$(md5sum /opt/extucode/80f00100.lid | cut -f 1 -d ' ');
+ if [ \"$sum\" != \"\$sumr\" ] ; then
+ exit 1;
+ fi;" || error "MD5sum doesn't match for ${LID[0]}"
+
+ fi
+
+ if [ "${LID[1]}" != "" ]; then
+ $REMOTECPCMD ${LID[1]} $target:/opt/extucode/80f00101.lid ||
+ error "Error copying lid";
+ sum=$(md5sum ${LID[1]} | cut -f 1 -d ' ');
+ $SSHCMD "$GET_PROFILE;
+ sumr=\$(md5sum /opt/extucode/80f00101.lid | cut -f 1 -d ' ');
+ if [ \"$sum\" != \"\$sumr\" ] ; then
+ exit 1;
+ fi;" || error "MD5sum doesn't match for ${LID[1]}"
+ fi
+
+ if [ "${LID[2]}" != "" ]; then
+ $REMOTECPCMD ${LID[2]} $target:/opt/extucode/80f00102.lid ||
+ error "Error copying lid";
+ sum=$(md5sum ${LID[2]} | cut -f 1 -d ' ');
+ $SSHCMD "$GET_PROFILE;
+ sumr=\$(md5sum /opt/extucode/80f00102.lid | cut -f 1 -d ' ');
+ if [ \"$sum\" != \"\$sumr\" ] ; then
+ exit 1;
+ fi;" || error "MD5sum doesn't match for ${LID[2]}"
+ fi
+
+
+ $SSHCMD "$GET_PROFILE;
+ if [ \$(smgr mfgState) != 'standby' ] ; then
+ exit 1;
+ fi
+ cupdmfg -opt | grep '80f0010'";
+ if [ $? -ne 0 ] ; then
+ error "Could not install lids on the FSP";
+ fi
+
+ sleep 2; #Don't rush the fsp
+}
+
+function boot_firmware {
+ ISTEP_LOG=$(mktemp --tmpdir builder-1.XXXXXX);
+ $SSHCMD "$GET_PROFILE; istep" &> $ISTEP_LOG &
+ msg "Waiting 90 seconds for $target to boot";
+ sleep 90;
+ i=0;
+ state=$($SSHCMD "$GET_PROFILE; smgr mfgState");
+ while [ \( "$state" != "runtime" \) -a \( "$i" -lt "$BOOT_TIMEOUT" \) ] ; do
+ msg "Waiting $BOOT_SLEEP_PERIOD more seconds (istep: `grep iStep $ISTEP_LOG|tail -n 1`)";
+ sleep "$BOOT_SLEEP_PERIOD";
+ i=$(expr $i + 1);
+ state=$($SSHCMD "$GET_PROFILE; smgr mfgState");
+ done;
+
+ if [ "$i" -eq "$BOOT_TIMEOUT" ] ; then
+ state=$($SSHCMD "$GET_PROFILE; smgr mfgState");
+ case "$state" in
+ "ipling")
+ echo "$target: still hasn't come up but firmware hasn't specifically crashed";
+ ;;
+ "dumping")
+ echo "$target: has crashed";
+ ;;
+ "runtime")
+ echo "$target: Oops, looks like system has managed to come up...";
+ ;;
+ "standby")
+ echo "$target: System is powered off? How can this be?";
+ ;;
+ *)
+ echo "$target: is an unknown state '$state'";
+ ;;
+ esac
+ echo "$target: istep log";
+ cat $ISTEP_LOG;
+ rm -rf $ISTEP_LOG
+ error "Boot test on $target failed";
+ fi
+ rm -rf $ISTEP_LOG;
+}
+
+function machine_sanity_test {
+ sshcmd true;
+ if [ $? -ne 0 ]; then
+ echo "$target: Failed to SSH to $target..."
+ echo "$target: Command was: $SSHCMD true"
+ error "Try connecting manually to diagnose the issue."
+ fi
+
+ $SSHCMD "$GET_PROFILE; test -d /nfs/bin"
+ if [ $? -ne 0 ]; then
+ echo "$target: Failed to read /nfs/bin"
+ error "Is /nfs mounted on the FSP?"
+ fi
+
+ $SSHCMD "$GET_PROFILE; which md5sum > /dev/null && which cupdmfg > /dev/null"
+ if [ $? -ne 0 ]; then
+ echo "$target: Missing md5sum or cupdmfg on the FSP?"
+ error "Is /nfs mounted on the FSP?"
+ fi
+}
diff --git a/roms/skiboot/external/boot-tests/openbmc_support.sh b/roms/skiboot/external/boot-tests/openbmc_support.sh
new file mode 100644
index 000000000..8c0b29677
--- /dev/null
+++ b/roms/skiboot/external/boot-tests/openbmc_support.sh
@@ -0,0 +1,129 @@
+#Number of times to sleep
+BOOT_TIMEOUT="5";
+
+#Username/password for ssh to BMC machines
+SSHUSER=${SSHUSER:-root};
+export SSHPASS=${SSHPASS:-0penBmc};
+
+PFLASH_TO_COPY=${PFLASH_TO_COPY:-}
+PFLASH_BINARY=/usr/sbin/pflash
+
+# How do we SSH/SCP in?
+SSHCMD="sshpass -e ssh -l $SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $target";
+
+IPMI_AUTH="-P ${IPMI_PASS:-0penBmc}"
+
+# Strip control characters from IPMI before grepping?
+STRIP_CONTROL=0
+
+function sshcmd {
+ $SSHCMD $*;
+}
+
+# remotecp file target target_location
+function remotecp {
+ sshpass -e scp -o User=$SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $1 $2:$3
+}
+
+function is_off {
+ return $([ "$($SSHCMD /usr/sbin/obmcutil power|grep 'state')" = "state = 0" ]);
+}
+
+function poweroff {
+ $SSHCMD /usr/sbin/obmcutil poweroff
+ # give it some time
+ sleep 5
+}
+
+function force_primary_side {
+ true
+}
+
+function flash {
+ if [ ! -z "$PFLASH_TO_COPY" ]; then
+ remotecp $PFLASH_TO_COPY $target /tmp/pflash
+ $SSHCMD chmod +x /tmp/pflash
+ PFLASH_BINARY=/tmp/pflash
+ fi
+ if [ ! -z "$PNOR" ]; then
+ remotecp $PNOR $target /tmp/image.pnor;
+ fi
+ if [ "${LID[0]}" != "" ]; then
+ remotecp ${LID[0]} $target /tmp/skiboot.lid;
+ fi
+ if [ "${LID[1]}" != "" ]; then
+ remotecp ${LID[1]} $target /tmp/bootkernel
+ fi
+ if [ "${arbitrary_lid[1]}" != "" ]; then
+ remotecp ${arbitrary_lid[1]} $target /tmp/$(basename ${arbitrary_lid[1]})
+ fi
+
+ if [ "$?" -ne "0" ] ; then
+ error "Couldn't copy firmware image";
+ fi
+
+ if [ ! -z "$PNOR" ]; then
+ msg "Flashing full PNOR"
+ $SSHCMD "$PFLASH_BINARY -E -f -p /tmp/image.pnor"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/image.pnor"
+ $SSHCMD "rm /tmp/image.pnor"
+ fi
+
+ if [ ! -z "${LID[0]}" ] ; then
+ msg "Flashing PAYLOAD PNOR partition"
+ $SSHCMD "$PFLASH_BINARY -e -f -P PAYLOAD -p /tmp/skiboot.lid"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/pskiboot.lid"
+ $SSHCMD "rm /tmp/skiboot.lid"
+ fi
+
+ if [ ! -z "${LID[1]}" ] ; then
+ msg "Flashing BOOTKERNEL PNOR partition"
+ $SSHCMD "$PFLASH_BINARY -e -f -P BOOTKERNEL -p /tmp/bootkernel"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/bootkernel"
+ $SSHCMD "rm /tmp/bootkernel"
+ fi
+
+ if [ ! -z "${arbitrary_lid[0]}" -a ! -z "${arbitrary_lid[1]}" ] ; then
+ msg "Flashing ${arbitrary_lid[0]} PNOR partition"
+ $SSHCMD "$PFLASH_BINARY -e -f -P ${arbitrary_lid[0]} -p /tmp/$(basename ${arbitrary_lid[1]})"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/$(basename ${arbitrary_lid[1]})"
+ $SSHCMD "rm /tmp/$(basename ${arbitrary_lid[1]})"
+ fi
+
+}
+
+function boot_firmware {
+ $SSHCMD /usr/sbin/obmcutil poweron
+ i=0;
+ while [ "$($SSHCMD /usr/sbin/obmcutil power|grep state)" = "state = 0" -a \( "$i" -lt "$BOOT_TIMEOUT" \) ] ; do
+ msg -n ".";
+ sleep $BOOT_SLEEP_PERIOD;
+ i=$(expr $i + 1);
+ done
+ if [ "$i" -eq "$BOOT_TIMEOUT" ] ; then
+ error "Couldn't power on $target";
+ fi
+}
+
+function machine_sanity_test {
+ sshcmd true;
+ if [ $? -ne 0 ]; then
+ echo "$target: Failed to SSH to $target..."
+ echo "$target: Command was: $SSHCMD true"
+ error "Try connecting manually to diagnose the issue."
+ fi
+ # No further sanity tests for BMC machines.
+ true
+}
diff --git a/roms/skiboot/external/boot-tests/smc_support.sh b/roms/skiboot/external/boot-tests/smc_support.sh
new file mode 100644
index 000000000..5e2a834d8
--- /dev/null
+++ b/roms/skiboot/external/boot-tests/smc_support.sh
@@ -0,0 +1,128 @@
+#Number of times to sleep
+BOOT_TIMEOUT="5";
+
+#Username/password for ssh to BMC machines
+SSHUSER=${SSHUSER:-ADMIN};
+export SSHPASS=${SSHPASS:-ADMIN};
+
+#Username/password for IPMI
+IPMI_AUTH="-U ${IPMI_USER:-ADMIN} -P ${IPMI_PASS:-ADMIN}"
+PFLASH_TO_COPY=${PFLASH_TO_COPY:-}
+PFLASH_BINARY=/tmp/pflash
+
+# Strip control characters from IPMI before grepping?
+STRIP_CONTROL=0
+
+# How do we SSH/SCP in?
+SSHCMD="sshpass -e ssh -l $SSHUSER -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $target";
+
+function sshcmd {
+ # because BMC:
+ $IPMI_COMMAND $SMC_PRESSHIPMICMD;
+ expect -c "spawn $SSHCMD" -c "set timeout 600" -c "expect \"#\" { send \"$*\\r\" }" -c 'expect "#" { send "exit\r" }' -c 'wait';
+}
+
+# remotecp file target target_location
+function remotecp {
+ rsync -av $1 rsync://$2/files/$3
+}
+
+function is_off {
+ return $([ "$($IPMI_COMMAND chassis power status)" = "Chassis Power is off" ]);
+}
+
+function poweroff {
+ $IPMI_COMMAND chassis power off
+ # give it some time
+ sleep 10
+}
+
+function force_primary_side {
+ true
+}
+
+function flash {
+ if [ ! -z "$PFLASH_TO_COPY" ]; then
+ remotecp $PFLASH_TO_COPY $target pflash
+ sshcmd chmod +x /tmp/rsync_file/pflash
+ PFLASH_BINARY=/tmp/rsync_file/pflash
+ fi
+ if [ ! -z "$PNOR" ]; then
+ remotecp $PNOR $target image.pnor;
+ fi
+ if [ "${LID[0]}" != "" ]; then
+ remotecp ${LID[0]} $target skiboot.lid;
+ fi
+ if [ "${LID[1]}" != "" ]; then
+ remotecp ${LID[1]} $target bootkernel
+ fi
+ if [ "${arbitrary_lid[1]}" != "" ]; then
+ remotecp ${arbitrary_lid[1]} $target $(basename ${arbitrary_lid[1]})
+ fi
+
+ if [ "$?" -ne "0" ] ; then
+ error "Couldn't copy firmware image";
+ fi
+
+ # flash it
+ if [ ! -z "$PNOR" ]; then
+ msg "Flashing full PNOR"
+ sshcmd "$PFLASH_BINARY -E -f -p /tmp/rsync_file/image.pnor"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/rsync_file/image.pnor"
+ sshcmd "rm /tmp/rsync_file/image.pnor"
+ fi
+
+ if [ ! -z "${LID[0]}" ] ; then
+ msg "Flashing PAYLOAD PNOR partition"
+ sshcmd "$PFLASH_BINARY -e -f -P PAYLOAD -p /tmp/rsync_file/skiboot.lid"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/rsync_file/skiboot.lid"
+ sshcmd "rm /tmp/skiboot.lid"
+ fi
+
+ if [ ! -z "${LID[1]}" ] ; then
+ msg "Flashing BOOTKERNEL PNOR partition"
+ sshcmd "$PFLASH_BINARY -e -f -P BOOTKERNEL -p /tmp/rsync_file/bootkernel"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/rsync_file/bootkernel"
+ sshcmd "rm /tmp/rsync_file/bootkernel"
+ fi
+
+ if [ ! -z "${arbitrary_lid[0]}" -a ! -z "${arbitrary_lid[1]}" ] ; then
+ msg "Flashing ${arbitrary_lid[0]} PNOR partition"
+ sshcmd "$PFLASH_BINARY -e -f -P ${arbitrary_lid[0]} -p /tmp/rsync_file/$(basename ${arbitrary_lid[1]})"
+ if [ "$?" -ne "0" ] ; then
+ error "An unexpected pflash error has occurred";
+ fi
+ msg "Removing /tmp/rsync_file/$(basename ${arbitrary_lid[1]})"
+ sshcmd "rm /tmp/rsync_file/$(basename ${arbitrary_lid[1]})"
+ fi
+
+ msg "Clearing mboxd caches..."
+ sshcmd "/bin/mboxctl --clear-cache"
+}
+
+function boot_firmware {
+ $IPMI_COMMAND chassis power on > /dev/null;
+ i=0;
+ while [ "$($IPMI_COMMAND chassis power status)" = "Chassis Power is off" -a \( "$i" -lt "$BOOT_TIMEOUT" \) ] ; do
+ msg -n ".";
+ sleep $BOOT_SLEEP_PERIOD;
+ i=$(expr $i + 1);
+ done
+ if [ "$i" -eq "$BOOT_TIMEOUT" ] ; then
+ error "Couldn't power on $target";
+ fi
+}
+
+function machine_sanity_test {
+ # No further sanity tests for BMC machines.
+ true
+}
diff --git a/roms/skiboot/external/common/.gitignore b/roms/skiboot/external/common/.gitignore
new file mode 100644
index 000000000..3535d9768
--- /dev/null
+++ b/roms/skiboot/external/common/.gitignore
@@ -0,0 +1,3 @@
+ast-sf-ctrl.c
+ast.h
+io.h \ No newline at end of file
diff --git a/roms/skiboot/external/common/arch_flash.h b/roms/skiboot/external/common/arch_flash.h
new file mode 100644
index 000000000..0dff8b704
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015 IBM Corp.
+ */
+
+#ifndef __EXTERNAL_ARCH_FLASH_H
+#define __EXTERNAL_ARCH_FLASH_H
+
+#include <getopt.h>
+#include <libflash/blocklevel.h>
+
+enum flash_access {
+ PNOR_DIRECT,
+ PNOR_MTD,
+ BMC_DIRECT,
+ BMC_MTD,
+ ACCESS_INVAL
+};
+
+int arch_flash_init(struct blocklevel_device **bl, const char *file,
+ bool keep_alive);
+
+void arch_flash_close(struct blocklevel_device *bl, const char *file);
+
+/* Low level functions that an architecture may support */
+
+/*
+ * If called BEFORE init, then this dictates how the flash will be
+ * accessed.
+ * If called AFTER init, then this returns how the flash is being accessed.
+ */
+enum flash_access arch_flash_access(struct blocklevel_device *bl,
+ enum flash_access access);
+
+int arch_flash_erase_chip(struct blocklevel_device *bl);
+int arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b);
+int arch_flash_set_wrprotect(struct blocklevel_device *bl, int set);
+
+#endif /* __EXTERNAL_ARCH_FLASH_H */
diff --git a/roms/skiboot/external/common/arch_flash_arm.c b/roms/skiboot/external/common/arch_flash_arm.c
new file mode 100644
index 000000000..cfe7b96aa
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_arm.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015-2016 IBM Corp.
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+
+#include <ccan/container_of/container_of.h>
+
+#include <libflash/libflash.h>
+#include <libflash/file.h>
+#include "ast.h"
+#include "arch_flash.h"
+#include "arch_flash_arm_io.h"
+
+struct flash_chip;
+
+static struct arch_arm_data {
+ int fd;
+ void *ahb_reg_map;
+ void *gpio_ctrl;
+ size_t ahb_flash_base;
+ size_t ahb_flash_size;
+ void *ahb_flash_map;
+ enum flash_access access;
+ struct flash_chip *flash_chip;
+ struct blocklevel_device *init_bl;
+} arch_data;
+
+uint32_t ast_ahb_readl(uint32_t offset)
+{
+ assert(((offset ^ AHB_REGS_BASE) & ~(AHB_REGS_SIZE - 1)) == 0);
+
+ return readl(arch_data.ahb_reg_map + (offset - AHB_REGS_BASE));
+}
+
+void ast_ahb_writel(uint32_t val, uint32_t offset)
+{
+ assert(((offset ^ AHB_REGS_BASE) & ~(AHB_REGS_SIZE - 1)) == 0);
+
+ writel(val, arch_data.ahb_reg_map + (offset - AHB_REGS_BASE));
+}
+
+int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len)
+{
+ if (reg < arch_data.ahb_flash_base ||
+ (reg + len) > (arch_data.ahb_flash_base + arch_data.ahb_flash_size))
+ return -1;
+ reg -= arch_data.ahb_flash_base;
+
+ if (((reg | (unsigned long)src | len) & 3) == 0) {
+ while(len > 3) {
+ uint32_t val = *(uint32_t *)src;
+ writel(val, arch_data.ahb_flash_map + reg);
+ src += 4;
+ reg += 4;
+ len -= 4;
+ }
+ }
+
+ while(len--) {
+ uint8_t val = *(uint8_t *)src;
+ writeb(val, arch_data.ahb_flash_map + reg++);
+ src += 1;
+ }
+ return 0;
+}
+
+/*
+ * GPIO stuff to be replaced by higher level accessors for
+ * controlling the flash write lock via sysfs
+ */
+
+static inline uint32_t gpio_ctl_readl(uint32_t offset)
+{
+ return readl(arch_data.gpio_ctrl + offset);
+}
+
+static inline void gpio_ctl_writel(uint32_t val, uint32_t offset)
+{
+ writel(val, arch_data.gpio_ctrl + offset);
+}
+
+static bool set_wrprotect(bool protect)
+{
+ uint32_t reg;
+ bool was_protected;
+
+ reg = gpio_ctl_readl(0x20);
+ was_protected = !!(reg & 0x00004000);
+ if (protect)
+ reg |= 0x00004000; /* GPIOF[6] value */
+ else
+ reg &= ~0x00004000; /* GPIOF[6] value */
+ gpio_ctl_writel(reg, 0x20);
+ reg = gpio_ctl_readl(0x24);
+ reg |= 0x00004000; /* GPIOF[6] direction */
+ gpio_ctl_writel(reg, 0x24);
+
+ return was_protected;
+}
+
+int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len)
+{
+ if (reg < arch_data.ahb_flash_base ||
+ (reg + len) > (arch_data.ahb_flash_base + arch_data.ahb_flash_size))
+ return -1;
+ reg -= arch_data.ahb_flash_base;
+
+ if (((reg | (unsigned long)dst | len) & 3) == 0) {
+ while(len > 3) {
+ *(uint32_t *)dst = readl(arch_data.ahb_flash_map + reg);
+ dst += 4;
+ reg += 4;
+ len -= 4;
+ }
+ }
+
+ while(len--) {
+ *(uint8_t *)dst = readb(arch_data.ahb_flash_map + reg++);
+ dst += 1;
+ }
+ return 0;
+}
+
+static void close_devs(void)
+{
+ /*
+ * Old code doesn't do this, not sure why not
+ *
+ * munmap(arch_data.ahb_flash_map, arch_data.ahb_flash_size);
+ * munmap(arch_data.gpio_ctrl, GPIO_CTRL_SIZE);
+ * munmap(arch_data.ahb_reg_map, AHB_REGS_SIZE);
+ * close(arch_data.fd);
+ */
+}
+
+static int open_devs(enum flash_access access)
+{
+ if (access != BMC_DIRECT && access != PNOR_DIRECT)
+ return -1;
+
+ arch_data.fd = open("/dev/mem", O_RDWR | O_SYNC);
+ if (arch_data.fd < 0) {
+ perror("can't open /dev/mem");
+ return -1;
+ }
+
+ arch_data.ahb_reg_map = mmap(0, AHB_REGS_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, arch_data.fd, AHB_REGS_BASE);
+ if (arch_data.ahb_reg_map == MAP_FAILED) {
+ perror("can't map AHB registers /dev/mem");
+ return -1;
+ }
+ arch_data.gpio_ctrl = mmap(0, GPIO_CTRL_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, arch_data.fd, GPIO_CTRL_BASE);
+ if (arch_data.gpio_ctrl == MAP_FAILED) {
+ perror("can't map GPIO control via /dev/mem");
+ return -1;
+ }
+ arch_data.ahb_flash_base = access == BMC_DIRECT ? BMC_FLASH_BASE : PNOR_FLASH_BASE;
+ arch_data.ahb_flash_size = access == BMC_DIRECT ? BMC_FLASH_SIZE : PNOR_FLASH_SIZE;
+ arch_data.ahb_flash_map = mmap(0, arch_data.ahb_flash_size, PROT_READ |
+ PROT_WRITE, MAP_SHARED, arch_data.fd, arch_data.ahb_flash_base);
+ if (arch_data.ahb_flash_map == MAP_FAILED) {
+ perror("can't map flash via /dev/mem");
+ return -1;
+ }
+ return 0;
+}
+
+static struct blocklevel_device *flash_setup(enum flash_access access)
+{
+ int rc;
+ struct blocklevel_device *bl;
+ struct spi_flash_ctrl *fl;
+
+ if (access != BMC_DIRECT && access != PNOR_DIRECT)
+ return NULL;
+
+ /* Open and map devices */
+ rc = open_devs(access);
+ if (rc)
+ return NULL;
+
+ /* Create the AST flash controller */
+ rc = ast_sf_open(access == BMC_DIRECT ? AST_SF_TYPE_BMC : AST_SF_TYPE_PNOR, &fl);
+ if (rc) {
+ fprintf(stderr, "Failed to open controller\n");
+ return NULL;
+ }
+
+ /* Open flash chip */
+ rc = flash_init(fl, &bl, &arch_data.flash_chip);
+ if (rc) {
+ fprintf(stderr, "Failed to open flash chip\n");
+ return NULL;
+ }
+
+ return bl;
+}
+
+static bool is_bmc_part(const char *str) {
+ /*
+ * On AMI firmmware "fullpart" is what they called the BMC partition
+ * On OpenBMC "bmc" is what they called the BMC partition
+ */
+ return strstr(str, "fullpart") || strstr(str, "bmc");
+}
+
+static bool is_pnor_part(const char *str) {
+ /*
+ * On AMI firmware "PNOR" is what they called the full PNOR
+ * On OpenBMC "pnor" is what they called the full PNOR
+ */
+ return strcasestr(str, "pnor");
+}
+
+static char *get_dev_mtd(enum flash_access access)
+{
+ FILE *f;
+ char *ret = NULL, *pos = NULL;
+ char line[255];
+
+ if (access != BMC_MTD && access != PNOR_MTD)
+ return NULL;
+
+ f = fopen("/proc/mtd", "r");
+ if (!f)
+ return NULL;
+
+ while (!pos && fgets(line, sizeof(line), f) != NULL) {
+ /* Going to have issues if we didn't get the full line */
+ if (line[strlen(line) - 1] != '\n')
+ break;
+
+ if (access == BMC_MTD && is_bmc_part(line)) {
+ pos = strchr(line, ':');
+ if (!pos)
+ break;
+
+ } else if (access == PNOR_MTD && is_pnor_part(line)) {
+ pos = strchr(line, ':');
+ if (!pos)
+ break;
+ }
+ }
+ if (pos) {
+ *pos = '\0';
+ if (asprintf(&ret, "/dev/%s", line) == -1)
+ ret = NULL;
+ }
+
+ fclose(f);
+ return ret;
+}
+
+enum flash_access arch_flash_access(struct blocklevel_device *bl,
+ enum flash_access access)
+{
+ if (access == ACCESS_INVAL)
+ return ACCESS_INVAL;
+
+ if (!arch_data.init_bl) {
+ arch_data.access = access;
+ return access;
+ }
+
+ /* Called with a BL not inited here, bail */
+ if (arch_data.init_bl != bl)
+ return ACCESS_INVAL;
+
+ return arch_data.flash_chip ? arch_data.access : ACCESS_INVAL;
+}
+
+int arch_flash_erase_chip(struct blocklevel_device *bl)
+{
+ /* Called with a BL not inited here, bail */
+ if (!arch_data.init_bl || arch_data.init_bl != bl)
+ return -1;
+
+ if (!arch_data.flash_chip) {
+ /* Just assume its a regular erase */
+ int rc;
+ uint64_t total_size;
+
+ rc = blocklevel_get_info(bl, NULL, &total_size, NULL);
+ if (rc)
+ return rc;
+
+ return blocklevel_erase(bl, 0, total_size);
+ }
+
+ return flash_erase_chip(arch_data.flash_chip);
+}
+
+int arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b)
+{
+ /* Called with a BL not inited here, bail */
+ if (!arch_data.init_bl || arch_data.init_bl != bl)
+ return -1;
+
+ if (!arch_data.flash_chip)
+ return -1;
+
+ return flash_force_4b_mode(arch_data.flash_chip, set_4b);
+}
+
+int arch_flash_set_wrprotect(struct blocklevel_device *bl, int set)
+{
+ /* Called with a BL not inited here, bail */
+ if (!arch_data.init_bl || arch_data.init_bl != bl)
+ return -1;
+
+ if (arch_data.access == PNOR_MTD || arch_data.access == BMC_MTD)
+ return 0; /* Kernel looks after this for us */
+
+ if (!arch_data.flash_chip)
+ return -1;
+
+ return set_wrprotect(set);
+}
+
+int arch_flash_init(struct blocklevel_device **r_bl, const char *file, bool keep_alive)
+{
+ struct blocklevel_device *new_bl;
+ int rc = 0;
+
+ /* Check we haven't already inited */
+ if (arch_data.init_bl)
+ return -1;
+
+ if (file) {
+ rc = file_init_path(file, NULL, keep_alive, &new_bl);
+ } else if (arch_data.access == BMC_MTD || arch_data.access == PNOR_MTD) {
+ char *mtd_dev;
+
+ mtd_dev = get_dev_mtd(arch_data.access);
+ if (!mtd_dev) {
+ return -1;
+ }
+ rc = file_init_path(mtd_dev, NULL, keep_alive, &new_bl);
+ free(mtd_dev);
+ } else {
+ new_bl = flash_setup(arch_data.access);
+ if (!new_bl)
+ rc = -1;
+ }
+ if (rc)
+ return rc;
+
+ arch_data.init_bl = new_bl;
+ *r_bl = new_bl;
+ return 0;
+}
+
+void arch_flash_close(struct blocklevel_device *bl, const char *file)
+{
+ if (file || arch_data.access == BMC_MTD || arch_data.access == PNOR_MTD) {
+ file_exit_close(bl);
+ } else {
+ flash_exit_close(bl, &ast_sf_close);
+ close_devs();
+ }
+}
diff --git a/roms/skiboot/external/common/arch_flash_arm_io.h b/roms/skiboot/external/common/arch_flash_arm_io.h
new file mode 100644
index 000000000..85dbaf482
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_arm_io.h
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright 2015 IBM Corp.
+ */
+#ifndef __IO_H
+#define __IO_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <libflash/libflash.h>
+
+/* AST AHB register base */
+#define AHB_REGS_BASE 0x1E600000
+#define AHB_REGS_SIZE 0x00200000
+
+/* AST GPIO control regs */
+#define GPIO_CTRL_BASE 0x1E780000
+#define GPIO_CTRL_SIZE 0x1000
+
+/* AST AHB mapping of PNOR */
+#define PNOR_FLASH_BASE 0x30000000
+#define PNOR_FLASH_SIZE 0x04000000
+
+/* AST AHB mapping of BMC flash */
+#define BMC_FLASH_BASE 0x20000000
+#define BMC_FLASH_SIZE 0x04000000
+
+/* Address of flash mapping on LPC FW space */
+#define LPC_FLASH_BASE 0x0e000000
+#define LPC_CTRL_BASE 0x1e789000
+
+static inline uint8_t readb(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint8_t *)addr;
+}
+
+static inline uint16_t readw(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint16_t *)addr;
+}
+
+static inline uint32_t readl(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint32_t *)addr;
+}
+
+static inline void writeb(uint8_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint8_t *)addr = val;
+}
+
+static inline void writew(uint16_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint16_t *)addr = val;
+}
+
+static inline void writel(uint32_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint32_t *)addr = val;
+}
+
+/*
+ * AHB register and flash access
+ */
+
+extern uint32_t ast_ahb_readl(uint32_t offset);
+extern void ast_ahb_writel(uint32_t val, uint32_t offset);
+extern int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len);
+extern int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len);
+
+static inline void check_platform(bool *has_sfc, bool *has_ast)
+{
+ *has_sfc = false;
+ *has_ast = true;
+}
+
+#endif /* __IO_H */
+
diff --git a/roms/skiboot/external/common/arch_flash_common.c b/roms/skiboot/external/common/arch_flash_common.c
new file mode 100644
index 000000000..c7a6e95ca
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_common.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Default implementations
+ *
+ * Copyright 2015-2017 IBM Corp.
+ */
+
+#include <stdlib.h>
+#include <libflash/blocklevel.h>
+#include "arch_flash.h"
+
+/*
+ * This just assumes that an erase from zero to total size is
+ * 'correct'.
+ * An erase from zero to total size is the correct approach for
+ * powerpc and x86. ARM has it own function which also includes a call
+ * to the flash driver.
+ */
+int __attribute__((weak)) arch_flash_erase_chip(struct blocklevel_device *bl)
+{
+ int rc;
+ uint64_t total_size;
+
+ rc = blocklevel_get_info(bl, NULL, &total_size, NULL);
+ if (rc)
+ return rc;
+
+ return blocklevel_erase(bl, 0, total_size);
+}
+
+int __attribute__((weak,const)) arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b)
+{
+ (void)bl;
+ (void)set_4b;
+ return -1;
+}
+
+enum flash_access __attribute__((weak,const)) arch_flash_access(struct blocklevel_device *bl, enum flash_access access)
+{
+ (void)bl;
+ (void)access;
+ return ACCESS_INVAL;
+}
+
+int __attribute__((weak,const)) arch_flash_set_wrprotect(struct blocklevel_device *bl, int set)
+{
+ (void)bl;
+ (void)set;
+ return -1;
+}
diff --git a/roms/skiboot/external/common/arch_flash_powerpc.c b/roms/skiboot/external/common/arch_flash_powerpc.c
new file mode 100644
index 000000000..87661296d
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_powerpc.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2017 IBM Corp.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <libflash/file.h>
+
+#include "arch_flash.h"
+
+#define FDT_FLASH_PATH "/proc/device-tree/chosen/ibm,system-flash"
+#define SYSFS_MTD_PATH "/sys/class/mtd"
+
+static inline void hint_root(void)
+{
+ fprintf(stderr, "Do you have permission? Are you root?\n");
+}
+
+static int get_dev_attr(const char *dev, const char *attr_file, uint32_t *attr)
+{
+ char *dev_path = NULL;
+ int fd, rc;
+
+ /*
+ * Needs to be large enough to hold at most uint32_t represented as a
+ * string in hex with leading 0x
+ */
+ char attr_buf[10];
+
+ rc = asprintf(&dev_path, "%s/%s/%s", SYSFS_MTD_PATH, dev, attr_file);
+ if (rc < 0) {
+ dev_path = NULL;
+ goto out;
+ }
+
+ fd = open(dev_path, O_RDONLY);
+ if (fd == -1)
+ goto out;
+
+ rc = read(fd, attr_buf, sizeof(attr_buf));
+ close(fd);
+ if (rc == -1)
+ goto out;
+
+ if (attr)
+ *attr = strtol(attr_buf, NULL, 0);
+
+ free(dev_path);
+ return 0;
+
+out:
+ free(dev_path);
+ fprintf(stderr, "Couldn't get MTD attribute '%s' from '%s'\n", dev, attr_file);
+ return -1;
+}
+
+static int get_dev_mtd(const char *fdt_flash_path, char **mtd_path)
+{
+ struct dirent **namelist;
+ char fdt_node_path[PATH_MAX];
+ int count, i, rc, fd;
+ bool done;
+
+ if (!fdt_flash_path)
+ return -1;
+
+ fd = open(fdt_flash_path, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Couldn't open '%s' FDT attribute to determine which flash device to use\n",
+ fdt_flash_path);
+ fprintf(stderr, "Is your skiboot new enough to expose the flash through the device tree?\n");
+ hint_root();
+ return -1;
+ }
+
+ rc = read(fd, fdt_node_path, sizeof(fdt_node_path));
+ close(fd);
+ if (rc == -1) {
+ fprintf(stderr, "Couldn't read flash FDT node from '%s'\n", fdt_flash_path);
+ hint_root();
+ return -1;
+ }
+
+ count = scandir(SYSFS_MTD_PATH, &namelist, NULL, alphasort);
+ if (count == -1) {
+ fprintf(stderr, "Couldn't scan '%s' for MTD\n", SYSFS_MTD_PATH);
+ hint_root();
+ return -1;
+ }
+
+ rc = 0;
+ done = false;
+ for (i = 0; i < count; i++) {
+ struct dirent *dirent;
+ char *dev_path;
+ char fdt_node_path_tmp[PATH_MAX];
+
+ dirent = namelist[i];
+
+ /*
+ * The call to asprintf must happen last as when it succeeds it
+ * will allocate dev_path
+ */
+ if (dirent->d_name[0] == '.' || rc || done ||
+ asprintf(&dev_path, "%s/%s/device/of_node", SYSFS_MTD_PATH, dirent->d_name) < 0) {
+ free(namelist[i]);
+ continue;
+ }
+
+ rc = readlink(dev_path, fdt_node_path_tmp, sizeof(fdt_node_path_tmp) - 1);
+ free(dev_path);
+ if (rc == -1) {
+ /*
+ * This might fail because it could not exist if the system has flash
+ * devices that present as mtd but don't have corresponding FDT
+ * nodes, just continue silently.
+ */
+ free(namelist[i]);
+ /* Should still try the next dir so reset rc */
+ rc = 0;
+ continue;
+ }
+ fdt_node_path_tmp[rc] = '\0';
+
+ if (strstr(fdt_node_path_tmp, fdt_node_path)) {
+ uint32_t flags, size;
+
+ /*
+ * size and flags could perhaps have be gotten another way but this
+ * method is super unlikely to fail so it will do.
+ */
+
+ /* Check to see if device is writeable */
+ rc = get_dev_attr(dirent->d_name, "flags", &flags);
+ if (rc) {
+ free(namelist[i]);
+ continue;
+ }
+
+ /* Get the size of the mtd device while we're at it */
+ rc = get_dev_attr(dirent->d_name, "size", &size);
+ if (rc) {
+ free(namelist[i]);
+ continue;
+ }
+
+ rc = asprintf(&dev_path, "/dev/%s", dirent->d_name);
+ if (rc < 0) {
+ free(namelist[i]);
+ continue;
+ }
+ rc = 0;
+ *mtd_path = dev_path;
+ done = true;
+ }
+ free(namelist[i]);
+ }
+ free(namelist);
+
+ if (!done) {
+ fprintf(stderr, "Couldn't find '%s' corresponding MTD\n", fdt_flash_path);
+ fprintf(stderr, "Is your kernel new enough to expose MTD?\n");
+ }
+
+ /* explicit negative value so as to not return a libflash code */
+ return done ? rc : -1;
+}
+
+static struct blocklevel_device *arch_init_blocklevel(const char *file, bool keep_alive)
+{
+ int rc;
+ struct blocklevel_device *new_bl = NULL;
+ char *real_file = NULL;
+
+ if (!file) {
+ rc = get_dev_mtd(FDT_FLASH_PATH, &real_file);
+ if (rc)
+ return NULL;
+ }
+
+ rc = file_init_path(file ? file : real_file, NULL, keep_alive, &new_bl);
+ if (rc)
+ new_bl = NULL;
+ free(real_file);
+ return new_bl;
+}
+
+/* Skiboot will worry about this for us */
+int __attribute__((const)) arch_flash_set_wrprotect(struct blocklevel_device *bl, int set)
+{
+ (void)bl;
+ (void)set;
+
+ return 0;
+}
+
+enum flash_access __attribute__((const)) arch_flash_access(struct blocklevel_device *bl,
+ enum flash_access access)
+{
+ (void)bl;
+
+ if (access != PNOR_MTD)
+ return ACCESS_INVAL;
+
+ return PNOR_MTD;
+}
+
+int arch_flash_init(struct blocklevel_device **r_bl, const char *file, bool keep_alive)
+{
+ struct blocklevel_device *new_bl;
+
+ /*
+ * In theory here we should check that something crazy wasn't
+ * passed to arch_flash_access() and refuse to init.
+ * However, arch_flash_access won't accept anything except
+ * PNOR_MTD, if they want something different then they should
+ * have checked with arch_flash_access()
+ */
+ new_bl = arch_init_blocklevel(file, keep_alive);
+ if (!new_bl)
+ return -1;
+
+ *r_bl = new_bl;
+ return 0;
+}
+
+void arch_flash_close(struct blocklevel_device *bl, const char *file)
+{
+ (void)file;
+
+ file_exit_close(bl);
+}
diff --git a/roms/skiboot/external/common/arch_flash_powerpc_io.h b/roms/skiboot/external/common/arch_flash_powerpc_io.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_powerpc_io.h
diff --git a/roms/skiboot/external/common/arch_flash_unknown.c b/roms/skiboot/external/common/arch_flash_unknown.c
new file mode 100644
index 000000000..00a355600
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_unknown.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: Apache-2.0
+/* Copyright 2013-2017 IBM Corp.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <libflash/file.h>
+
+#include "arch_flash.h"
+
+int arch_flash_init(struct blocklevel_device **r_bl, const char *file, bool keep_alive)
+{
+ int rc;
+ struct blocklevel_device *new_bl;
+
+ /* Must have passed through a file to operate on */
+ if (!file) {
+ fprintf(stderr, "Cannot operate without a file\n");
+ return -1;
+ }
+
+ rc = file_init_path(file, NULL, keep_alive, &new_bl);
+ if (rc)
+ return -1;
+
+ *r_bl = new_bl;
+ return 0;
+}
+
+void arch_flash_close(struct blocklevel_device *bl, const char *file)
+{
+ (void)file;
+ file_exit_close(bl);
+}
diff --git a/roms/skiboot/external/common/arch_flash_unknown_io.h b/roms/skiboot/external/common/arch_flash_unknown_io.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_unknown_io.h
diff --git a/roms/skiboot/external/common/get_arch.sh b/roms/skiboot/external/common/get_arch.sh
new file mode 100755
index 000000000..682710fcb
--- /dev/null
+++ b/roms/skiboot/external/common/get_arch.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+echo "#if defined(__powerpc__)
+echo -n ARCH_POWERPC
+#elif defined(__arm__)
+echo -n ARCH_ARM
+#else
+echo -n ARCH_UNKNOWN
+#endif" | $1cpp | /bin/sh
+
diff --git a/roms/skiboot/external/common/rules.mk b/roms/skiboot/external/common/rules.mk
new file mode 100644
index 000000000..7190813b8
--- /dev/null
+++ b/roms/skiboot/external/common/rules.mk
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: Apache-2.0
+
+CC ?= $(CROSS_COMPILE)gcc
+LD ?= $(CROSS_COMPILE)ld
+ARCH := $(shell $(GET_ARCH) "$(CROSS_COMPILE)")
+
+ifeq ($(ARCH),ARCH_ARM)
+arch := arm
+ARCH_FILES := arch_flash_common.c arch_flash_arm.c ast-sf-ctrl.c
+else
+ifeq ($(ARCH),ARCH_POWERPC)
+arch := powerpc
+ARCH_FILES := arch_flash_common.c arch_flash_powerpc.c
+else
+arch := unknown
+ARCH_FILES := arch_flash_common.c arch_flash_unknown.c
+endif
+endif
+
+# Use make V=1 for a verbose build.
+ifndef V
+ Q_CC= @echo ' CC ' $@;
+ Q_LINK= @echo ' LINK ' $@;
+ Q_LN= @echo ' LN ' $@;
+ Q_MKDIR=@echo ' MKDIR ' $@;
+endif
+
+
+.PHONY: links
+links: libflash ccan common
+
+libflash:
+ $(Q_LN)ln -sf ../../libflash ./libflash
+
+ccan:
+ $(Q_LN)ln -sf ../../ccan ./ccan
+
+common:
+ $(Q_LN)ln -sf ../common ./common
+
+make_version.sh:
+ $(Q_LN)ln -sf ../../make_version.sh
+
+ARCH_SRC := $(addprefix common/,$(ARCH_FILES))
+ARCH_OBJS := $(addprefix common-,$(ARCH_FILES:.c=.o))
+
+# Arch links are like this so we can have dependencies work (so that we don't
+# run the rule when the links exist), pretty build output (knowing the target
+# name) and a list of the files so we can clean them up.
+ARCH_LINKS ?= common/ast-sf-ctrl.c common/ast.h common/io.h common/lpc.h
+
+arch_links: $(ARCH_LINKS)
+common/ast.h : ../../include/ast.h | common
+ $(Q_LN)ln -sf ../../include/ast.h common/ast.h
+
+common/io.h : ../common/arch_flash_$(arch)_io.h | common
+ $(Q_LN)ln -sf arch_flash_$(arch)_io.h common/io.h
+
+common/ast-sf-ctrl.c : ../../hw/ast-bmc/ast-sf-ctrl.c | common
+ $(Q_LN)ln -sf ../../hw/ast-bmc/ast-sf-ctrl.c common/ast-sf-ctrl.c
+
+common/lpc.h: ../../include/lpc.h | common
+ $(Q_LN)ln -sf ../../include/lpc.h common/lpc.h
+
+.PHONY: arch_clean
+arch_clean:
+ rm -rf $(ARCH_OBJS) $(ARCH_LINKS)
+
+$(ARCH_SRC): | common
+
+$(ARCH_OBJS): common-%.o: common/%.c $(ARCH_LINKS)
+ $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+common-arch_flash.o: $(ARCH_OBJS)
+ $(Q_LD)$(LD) -r $(ARCH_OBJS) -o $@
+
diff --git a/roms/skiboot/external/devicetree/.gitignore b/roms/skiboot/external/devicetree/.gitignore
new file mode 100644
index 000000000..b60ed208c
--- /dev/null
+++ b/roms/skiboot/external/devicetree/.gitignore
@@ -0,0 +1 @@
+*.dtb
diff --git a/roms/skiboot/external/devicetree/Makefile b/roms/skiboot/external/devicetree/Makefile
new file mode 100644
index 000000000..690f633b9
--- /dev/null
+++ b/roms/skiboot/external/devicetree/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Copyright 2019 IBM Corp.
+
+DTC=dtc
+OUT=p9-simics.dtb
+
+all: $(OUT)
+
+%.dtb: %.dts
+ $(DTC) -I dts -O dtb $< -o $@
+
+clean:
+ $(RM) $(OUT)
diff --git a/roms/skiboot/external/devicetree/p9-simics.dts b/roms/skiboot/external/devicetree/p9-simics.dts
new file mode 100644
index 000000000..e24c7b762
--- /dev/null
+++ b/roms/skiboot/external/devicetree/p9-simics.dts
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * POWER9 Simics Device Tree
+ */
+
+/include/ "p9.dts"
+
+/ {
+ simics {
+ };
+
+ /*
+ * This isn't part of our Simics model (yet).
+ * [system_cmp0.cosimulator error] write error to physical address 0x60300d00100e6
+ */
+ lpcm-opb@6030000000000 {
+ lpc@f0000000 {
+ /delete-node/ ipmi@ie4;
+ };
+ };
+};
diff --git a/roms/skiboot/external/devicetree/p9.dts b/roms/skiboot/external/devicetree/p9.dts
new file mode 100644
index 000000000..bbda7cfdd
--- /dev/null
+++ b/roms/skiboot/external/devicetree/p9.dts
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * base POWER9 device tree
+ */
+
+/dts-v1/;
+
+/ {
+ compatible = "ibm,powernv";
+ model = "BML";
+ #size-cells = <0x2>;
+ #address-cells = <0x2>;
+
+ chosen {
+ linux,pci-assign-all-buses = <0x1>;
+ linux,pci-probe-only = <0x0>;
+ linux,platform = <0x100>;
+ ibm,architecture-vec-5 = <0x0 0x800000>;
+ linux,initrd-start = <0x0 0x28000000>;
+ linux,initrd-end = <0x0 0x30000000>;
+ };
+
+ memory@0 {
+ reg = <0x0 0x0 0x0 0x80000000>;
+ ibm,chip-id = <0x0>;
+ device_type = "memory";
+ };
+
+ cpus {
+ #size-cells = <0x0>;
+ #address-cells = <0x1>;
+
+ PowerPC,POWER9@0 {
+ device_type = "cpu";
+ status = "okay";
+ ibm,chip-id = <0x0>;
+ ibm,pir = <0x0>;
+ timebase-frequency = <0x1c4fecc0>;
+ clock-frequency = <0xe27f6600>;
+ ibm,segment-page-sizes = <0xc 0x0 0x1 0xc 0x0 0x10 0x110 0x1 0x10 0x1 0x14 0x111 0x1 0x14 0x2 0x18 0x100 0x1 0x18 0x0 0x22 0x120 0x1 0x22 0x3>;
+ ibm,processor-segment-sizes = <0x1c 0xffffffff 0xffffffff 0xffffffff>;
+ ibm,pa-features = <0x600f63f 0xc70080c0>;
+ i-cache-size = <0x8000>;
+ d-cache-size = <0x8000>;
+ i-cache-line-size = <0x80>;
+ d-cache-line-size = <0x80>;
+ ibm,slb-size = <0x20>;
+ ibm,vmx = <0x2>;
+ reg = <0x0>;
+ ibm,ppc-interrupt-server#s = <0x0>;
+ };
+ };
+
+ xscom@603fc00000000 {
+ compatible = "ibm,xscom", "ibm,power9-xscom";
+ ibm,chip-id = <0x0>;
+ #size-cells = <0x1>;
+ #address-cells = <0x1>;
+ reg = <0x603fc 0x0 0x8 0x0>;
+
+ /* PE#0 supports only one stack */
+ pbcq@4010c00 {
+ ibm,pec-index = <0x0>;
+ reg = <0x4010c00 0x100 0xd010800 0x200>;
+ compatible = "ibm,power9-pbcq";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ stack@0 {
+ /* Stack number */
+ reg = <0x0>;
+ /* Chip-scope PHB index */
+ ibm,phb-index = <0x0>;
+ compatible = "ibm,power9-phb-stack";
+ status = "okay";
+ };
+ };
+
+ /* PE#1 supports two stacks */
+ pbcq@4011000 {
+ ibm,pec-index = <0x1>;
+ reg = <0x4011000 0x100 0xe010800 0x200>;
+ compatible = "ibm,power9-pbcq";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ stack@0 {
+ reg = <0x0>;
+ ibm,phb-index = <0x1>;
+ compatible = "ibm,power9-phb-stack";
+ status = "okay";
+ };
+
+ stack@1 {
+ reg = <0x1>;
+ ibm,phb-index = <0x2>;
+ compatible = "ibm,power9-phb-stack";
+ status = "okay";
+ };
+ };
+
+ /* PE#2 supports three stacks */
+ pbcq@4011400 {
+ ibm,pec-index = <0x2>;
+ reg = <0x4011400 0x100 0xf010800 0x200>;
+ compatible = "ibm,power9-pbcq";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ stack@0 {
+ reg = <0x0>;
+ ibm,phb-index = <0x3>;
+ compatible = "ibm,power9-phb-stack";
+ status = "disabled";
+ };
+
+ stack@1 {
+ reg = <0x1>;
+ ibm,phb-index = <0x4>;
+ compatible = "ibm,power9-phb-stack";
+ status = "disabled";
+ };
+
+ stack@2 {
+ reg = <0x2>;
+ ibm,phb-index = <0x5>;
+ compatible = "ibm,power9-phb-stack";
+ status = "disabled";
+ };
+ };
+
+ chiptod@40000 {
+ primary;
+ reg = <0x40000 0x34>;
+ compatible = "ibm,power-chiptod", "ibm,power9-chiptod";
+ };
+
+ xive@5013400 {
+ reg = <0x5013000 0x300>;
+ compatible = "ibm,power9-xive-x";
+ };
+
+ psihb@5012900 {
+ reg = <0x5012900 0x100>;
+ compatible = "ibm,power9-psihb-x", "ibm,psihb-x";
+
+ /*
+ * This acts as an interrupt remapper for the 16
+ * interrupts coming into the PSI HB.
+ * OPAL will generate the corresponding interrupt-map
+ * property with the final XIVE numbers
+ */
+ #interrupt-cells = <0x1>;
+ #address-cells = <0x0>;
+ #interrupt-map-mask = <0xff>;
+ };
+
+ nx@2010000 {
+ reg = <0x2010000 0x4000>;
+ compatible = "ibm,power9-nx";
+ };
+ };
+
+ lpcm-opb@6030000000000 {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ compatible = "ibm,power9-lpcm-opb", "simple-bus";
+ ibm,chip-id = <0x0>;
+ ranges = <0x00000000 0x60300 0x00000000 0x80000000
+ 0x80000000 0x60300 0x80000000 0x80000000>;
+
+ opb-master@c0010000 {
+ compatible = "ibm,power9-lpcm-opb-master";
+ reg = <0xc0010000 0x60>;
+ };
+
+ opb-arbiter@c0011000 {
+ compatible = "ibm,power9-lpcm-opb-arbiter";
+ reg = <0xc0011000 0x8>;
+ };
+
+ lpc-controller@c0012000 {
+ compatible = "ibm,power9-lpc-controller";
+ reg = <0xc0012000 0x100>;
+ };
+
+ lpc@f0000000 {
+ compatible = "ibm,power9-lpc";
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ ranges = <0x3 0x0 0xf0000000 0x10000000 /* FW space */
+ 0x0 0x0 0xe0000000 0x10000000 /* MEM space */
+ 0x1 0x0 0xd0010000 0x00010000 /* IO space */ >;
+
+ /* Devices on the LPC bus go here */
+
+ serial@i3f8 {
+ compatible = "ns16550";
+ reg = <0x1 0x3f8 0x10>;
+ current-speed = <0x1c200>;
+ clock-frequency = <0x1c2000>;
+ interrupts = <0x4>;
+ };
+
+ ipmi@ie4 {
+ compatible = "ipmi-bt";
+ reg = <0x1 0xe4 0x3>;
+ interrupts = <0x10>;
+ };
+ };
+ };
+};
diff --git a/roms/skiboot/external/ffspart/.gitignore b/roms/skiboot/external/ffspart/.gitignore
new file mode 100644
index 000000000..ed76f20a0
--- /dev/null
+++ b/roms/skiboot/external/ffspart/.gitignore
@@ -0,0 +1,7 @@
+ccan
+common
+ffspart
+libflash
+make_version.sh
+test/test.sh
+
diff --git a/roms/skiboot/external/ffspart/Makefile b/roms/skiboot/external/ffspart/Makefile
new file mode 100644
index 000000000..0a8d910fd
--- /dev/null
+++ b/roms/skiboot/external/ffspart/Makefile
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+# Use make V=1 for a verbose build.
+include rules.mk
+GET_ARCH = ../../external/common/get_arch.sh
+include ../../external/common/rules.mk
+
+all: links arch_links $(EXE)
+
+#Rebuild version.o so that the the version always matches
+#what the test suite will get from ./make_version.sh
+check: version.o all
+ @ln -sf ../../test/test.sh test/test.sh
+ @test/test-ffspart
+
+$(OBJS): | links arch_links
+
+.PHONY: VERSION-always
+.version: VERSION-always
+ @echo $(FFSPART_VERSION) > $@.tmp
+ @cmp -s $@ $@.tmp || cp $@.tmp $@
+ @rm -f $@.tmp
+
+install: all
+ install -D ffspart $(DESTDIR)$(sbindir)/ffspart
+
+.PHONY: dist
+#File is named $(FFSPART_VERSION).tar because the expectation is that ffspart-
+#is always at the start of the verion. This remains consistent with skiboot
+#version strings
+dist: links .version
+ find -L ../ffspart/ -iname '*.[ch]' -print0 | xargs -0 tar -rhf $(FFSPART_VERSION).tar
+ tar --transform 's/Makefile.dist/Makefile/' -rhf $(FFSPART_VERSION).tar \
+ ../ffspart/Makefile.dist ../ffspart/rules.mk \
+ ../ffspart/.version ../ffspart/make_version.sh \
+ ../ffspart/common/*
+
+.PHONY: clean
+clean: arch_clean
+ rm -f $(OBJS) $(EXE) *.o *.d
+.PHONY: distclean
+distclean: clean
+ rm -f *.c~ *.h~ *.sh~ Makefile~ config.mk~ libflash/*.c~ libflash/*.h~
+ rm -f libflash ccan .version .version.tmp
+ rm -f common io.h
diff --git a/roms/skiboot/external/ffspart/config.h b/roms/skiboot/external/ffspart/config.h
new file mode 100644
index 000000000..7894afb6b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/config.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * For CCAN
+ *
+ * Copyright 2017 IBM Corp.
+ */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF 1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN 0
+#define HAVE_LITTLE_ENDIAN 1
+#else
+#define HAVE_BIG_ENDIAN 1
+#define HAVE_LITTLE_ENDIAN 0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
diff --git a/roms/skiboot/external/ffspart/ffspart.c b/roms/skiboot/external/ffspart/ffspart.c
new file mode 100644
index 000000000..98e790b23
--- /dev/null
+++ b/roms/skiboot/external/ffspart/ffspart.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Assemble a FFS Image (no, not that FFS, this FFS)
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/blocklevel.h>
+#include <libflash/ecc.h>
+#include <common/arch_flash.h>
+
+/*
+ * Flags:
+ * - E: ECC for this part
+ */
+
+/*
+ * TODO FIXME
+ * Max line theoretical max size:
+ * - name: 15 chars = 15
+ * - base: 0xffffffff = 10
+ * - size: 0xffffffff = 10
+ * - flag: E = 1
+ *
+ * 36 + 3 separators = 39
+ * Plus \n 40
+ * Lets do 50.
+ */
+#define MAX_LINE (PATH_MAX+255)
+#define MAX_TOCS 10
+#define SEPARATOR ','
+
+/* Full version number (possibly includes gitid). */
+extern const char version[];
+
+static int read_u32(const char *input, uint32_t *val)
+{
+ char *endptr;
+ *val = strtoul(input, &endptr, 0);
+ return (*endptr == SEPARATOR) ? 0 : 1;
+}
+
+static const char *advance_line(const char *input)
+{
+ char *pos = strchr(input, SEPARATOR);
+ if (!pos)
+ return NULL;
+ return pos + 1;
+}
+
+static struct ffs_hdr *parse_toc(const char *line, uint32_t block_size,
+ uint32_t block_count)
+{
+ struct ffs_entry_user user;
+ struct ffs_entry *ent;
+ struct ffs_hdr *hdr;
+ uint32_t tbase;
+ int rc;
+
+ if (read_u32(line, &tbase)) {
+ fprintf(stderr, "Couldn't parse TOC base address\n");
+ return NULL;
+ }
+
+ line = advance_line(line);
+ if (!line) {
+ fprintf(stderr, "Couldn't find TOC flags\n");
+ return NULL;
+ }
+
+ rc = ffs_string_to_entry_user(line, strlen(line), &user);
+ if (rc) {
+ fprintf(stderr, "Couldn't parse TOC flags\n");
+ return NULL;
+ }
+
+ rc = ffs_entry_new("part", tbase, 0, &ent);
+ if (rc) {
+ fprintf(stderr, "Couldn't make entry for TOC@0x%08x\n", tbase);
+ return NULL;
+ }
+
+ rc = ffs_entry_user_set(ent, &user);
+ if (rc) {
+ fprintf(stderr, "Invalid TOC flag\n");
+ ffs_entry_put(ent);
+ return NULL;
+ }
+
+ rc = ffs_hdr_new(block_size, block_count, &ent, &hdr);
+ if (rc) {
+ hdr = NULL;
+ fprintf(stderr, "Couldn't make header for TOC@0x%08x\n", tbase);
+ }
+
+ ffs_entry_put(ent);
+ return hdr;
+}
+
+static int parse_entry(struct blocklevel_device *bl,
+ struct ffs_hdr **tocs, const char *line, bool allow_empty)
+{
+ char name[FFS_PART_NAME_MAX + 2] = { 0 };
+ struct ffs_entry_user user = { 0 };
+ uint32_t pbase, psize, pactual, i;
+ struct ffs_entry *new_entry;
+ struct stat data_stat;
+ const char *filename;
+ bool added = false;
+ uint8_t *data_ptr, ecc = 0;
+ int data_fd, rc;
+ char *pos;
+
+ memcpy(name, line, FFS_PART_NAME_MAX + 1);
+ pos = strchr(name, SEPARATOR);
+ /* There is discussion to be had as to if we should bail here */
+ if (!pos) {
+ fprintf(stderr, "WARNING: Long partition name will get truncated to '%s'\n",
+ name);
+ name[FFS_PART_NAME_MAX] = '\0';
+ } else {
+ *pos = '\0';
+ }
+
+ line = advance_line(line);
+ if (!line || read_u32(line, &pbase)) {
+ fprintf(stderr, "Couldn't parse '%s' partition base address\n",
+ name);
+ return -1;
+ }
+
+ line = advance_line(line);
+ if (!line || read_u32(line, &psize)) {
+ fprintf(stderr, "Couldn't parse '%s' partition length\n",
+ name);
+ return -1;
+ }
+
+ line = advance_line(line);
+ if (!line || !advance_line(line)) {
+ fprintf(stderr, "Couldn't find '%s' partition flags\n",
+ name);
+ return -1;
+ }
+
+ rc = ffs_string_to_entry_user(line, advance_line(line) - 1 - line, &user);
+ if (rc) {
+ fprintf(stderr, "Couldn't parse '%s' partition flags\n",
+ name);
+ return -1;
+ }
+ line = advance_line(line);
+ /* Already checked return value */
+
+ rc = ffs_entry_new(name, pbase, psize, &new_entry);
+ if (rc) {
+ fprintf(stderr, "Invalid entry '%s' 0x%08x for 0x%08x\n",
+ name, pbase, psize);
+ return -1;
+ }
+
+ rc = ffs_entry_user_set(new_entry, &user);
+ if (rc) {
+ fprintf(stderr, "Couldn't set '%s' partition flags\n",
+ name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+
+ if (has_flag(new_entry, FFS_MISCFLAGS_BACKUP)) {
+ rc = ffs_entry_set_act_size(new_entry, 0);
+ if (rc) {
+ fprintf(stderr, "Couldn't set '%s' partition actual size\n",
+ name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+ }
+
+ if (!advance_line(line)) {
+ fprintf(stderr, "Missing TOC field for '%s' partition\n",
+ name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+
+ while (*line != SEPARATOR) {
+ int toc = *(line++);
+
+ if (!isdigit(toc)) {
+ fprintf(stderr, "Bad TOC value %d (%c) for '%s' partition\n",
+ toc, toc, name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+ toc -= '0';
+ if (!tocs[toc]) {
+ fprintf(stderr, "No TOC with ID %d for '%s' partition\n",
+ toc, name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+ rc = ffs_entry_add(tocs[toc], new_entry);
+ if (rc) {
+ fprintf(stderr, "Couldn't add '%s' partition to TOC %d: %d\n",
+ name, toc, rc);
+ ffs_entry_put(new_entry);
+ return rc;
+ }
+ added = true;
+ }
+ if (!added) {
+ /*
+ * They didn't specify a TOC in the TOC field, use
+ * TOC@0 as the default
+ */
+ rc = ffs_entry_add(tocs[0], new_entry);
+ if (rc) {
+ fprintf(stderr, "Couldn't add '%s' partition to default TOC: %d\n",
+ name, rc);
+ ffs_entry_put(new_entry);
+ return rc;
+ }
+ }
+ ffs_entry_put(new_entry);
+
+ if (*line != '\0' && *(line + 1) != '\0') {
+ size_t data_len;
+
+ filename = line + 1;
+
+ /*
+ * Support flashing already ecc'd data as this is the case
+ * for POWER8 SBE image binary.
+ */
+ if (has_ecc(new_entry) && !strstr(filename, ".ecc"))
+ blocklevel_ecc_protect(bl, pbase, psize);
+
+ data_fd = open(filename, O_RDONLY);
+ if (data_fd == -1) {
+ fprintf(stderr, "Couldn't open file '%s' for '%s' partition "
+ "(%m)\n", filename, name);
+ return -1;
+ }
+
+ if (fstat(data_fd, &data_stat) == -1) {
+ fprintf(stderr, "Couldn't stat file '%s' for '%s' partition "
+ "(%m)\n", filename, name);
+ close(data_fd);
+ return -1;
+ }
+
+ data_ptr = calloc(1, psize);
+ if (!data_ptr) {
+ return -1;
+ }
+
+ pactual = data_stat.st_size;
+
+ /*
+ * There's two char device inputs we care about: /dev/zero and
+ * /dev/urandom. Both have a stat.st_size of zero so read in
+ * a full partition worth, accounting for ECC overhead.
+ */
+ if (!pactual && S_ISCHR(data_stat.st_mode)) {
+ pactual = psize;
+
+ if (has_ecc(new_entry)) {
+ pactual = ecc_buffer_size_minus_ecc(pactual);
+
+ /* ECC input size needs to be a multiple of 8 */
+ pactual = pactual & ~0x7;
+ }
+ }
+ /*
+ * Sanity check that the file isn't too large for
+ * partition
+ */
+ if (has_ecc(new_entry) && !strstr(filename, ".ecc"))
+ psize = ecc_buffer_size_minus_ecc(psize);
+ if (pactual > psize) {
+ fprintf(stderr, "File '%s' for partition '%s' is too large,"
+ " %u > %u\n",
+ filename, name, pactual, psize);
+ close(data_fd);
+ return -1;
+ }
+
+ for (data_len = 0; data_len < pactual; data_len += rc) {
+ rc = read(data_fd, &data_ptr[data_len], pactual - data_len);
+ if (rc == -1) {
+ fprintf(stderr, "error reading from '%s'", filename);
+ exit(1);
+ }
+ }
+
+ rc = blocklevel_write(bl, pbase, data_ptr, pactual);
+ if (rc) {
+ fprintf(stderr, "Couldn't write file '%s' for '%s' partition to PNOR "
+ "(%m)\n", filename, name);
+ exit(1);
+ }
+
+ free(data_ptr);
+ close(data_fd);
+ } else {
+ if (!allow_empty) {
+ fprintf(stderr, "Filename missing for partition %s!\n",
+ name);
+ return -1;
+ }
+ if (has_ecc(new_entry)) {
+ i = pbase + 8;
+ while (i < pbase + psize) {
+ rc = blocklevel_write(bl, i, &ecc, sizeof(ecc));
+ if (rc) {
+ fprintf(stderr, "\nError setting ECC byte at 0x%08x\n",
+ i);
+ return rc;
+ }
+ i += 9;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+static void print_version(void)
+{
+ printf("Open-Power FFS format tool %s\n", version);
+}
+
+static void print_help(const char *pname)
+{
+ print_version();
+ printf("Usage: %s [options] -e -s size -c num -i layout_file -p pnor_file ...\n\n", pname);
+ printf(" Options:\n");
+ printf("\t-e, --allow_empty\n");
+ printf("\t\tCreate partition as blank if not specified (sets ECC if flag set)\n\n");
+ printf("\t-s, --block_size=size\n");
+ printf("\t\tSize (in hex with leading 0x) of the blocks on the flash in bytes\n\n");
+ printf("\t-c, --block_count=num\n");
+ printf("\t\tNumber of blocks on the flash\n\n");
+ printf("\t-i, --input=file\n");
+ printf("\t\tFile containing the required partition data\n\n");
+ printf("\t-p, --pnor=file\n");
+ printf("\t\tOutput file to write data\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+ static char line[MAX_LINE];
+
+ char *pnor = NULL, *input = NULL;
+ bool toc_created = false, bad_input = false, allow_empty = false;
+ uint32_t block_size = 0, block_count = 0;
+ struct ffs_hdr *tocs[MAX_TOCS] = { 0 };
+ struct blocklevel_device *bl = NULL;
+ const char *pname = argv[0];
+ int line_number, rc, i;
+ FILE *in_file;
+
+ while(1) {
+ struct option long_opts[] = {
+ {"allow_empty", no_argument, NULL, 'e'},
+ {"block_count", required_argument, NULL, 'c'},
+ {"block_size", required_argument, NULL, 's'},
+ {"debug", no_argument, NULL, 'g'},
+ {"input", required_argument, NULL, 'i'},
+ {"pnor", required_argument, NULL, 'p'},
+ {NULL, 0, 0, 0}
+ };
+ int c, oidx = 0;
+
+ c = getopt_long(argc, argv, "+:ec:gi:p:s:", long_opts, &oidx);
+ if (c == EOF)
+ break;
+ switch(c) {
+ case 'e':
+ allow_empty = true;
+ break;
+ case 'c':
+ block_count = strtoul(optarg, NULL, 0);
+ break;
+ case 'g':
+ libflash_debug = true;
+ break;
+ case 'i':
+ free(input);
+ input = strdup(optarg);
+ if (!input)
+ fprintf(stderr, "Out of memory!\n");
+ break;
+ case 'p':
+ free(pnor);
+ pnor = strdup(optarg);
+ if (!pnor)
+ fprintf(stderr, "Out of memory!\n");
+ break;
+ case 's':
+ block_size = strtoul(optarg, NULL, 0);
+ break;
+ case ':':
+ fprintf(stderr, "Unrecognised option \"%s\" to '%c'\n",
+ optarg, optopt);
+ bad_input = true;
+ break;
+ case '?':
+ fprintf(stderr, "Unrecognised option '%c'\n", optopt);
+ bad_input = true;
+ break;
+ default:
+ fprintf(stderr , "Encountered unknown error parsing options\n");
+ bad_input = true;
+ }
+ }
+
+ if (bad_input || !block_size || !block_count || !input || !pnor) {
+ print_help(pname);
+ return 1;
+ }
+
+ in_file = fopen(input, "r");
+ if (!in_file) {
+ fprintf(stderr, "Couldn't open your input file %s: %m\n", input);
+ return 2;
+ }
+
+ /*
+ * TODO: This won't create the file.
+ * We should do this
+ */
+ rc = arch_flash_init(&bl, pnor, true);
+ if (rc) {
+ fprintf(stderr, "Couldn't initialise architecture flash structures\n");
+ fclose(in_file);
+ return 3;
+ }
+
+ /*
+ * 'Erase' the file, make it all 0xFF
+ * TODO: Add sparse option and don't do this.
+ */
+ rc = blocklevel_erase(bl, 0, block_size * block_count);
+ if (rc) {
+ fprintf(stderr, "Couldn't erase '%s' pnor file\n", pnor);
+ fclose(in_file);
+ return 4;
+ }
+
+ line_number = 0;
+ while (fgets(line, MAX_LINE, in_file) != NULL) {
+ line_number++;
+
+ /* Inline comments in input file */
+ if (line[0] == '#')
+ continue;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ if (line[0] == '@') {
+ int toc_num = line[1];
+ rc = 5;
+
+ if (!isdigit(toc_num)) {
+ fprintf(stderr, "Invalid TOC ID %d (%c)\n",
+ toc_num, toc_num);
+ goto parse_out;
+ }
+
+ toc_num -= '0';
+
+ if (line[2] != SEPARATOR) {
+ fprintf(stderr, "TOC ID too long\n");
+ goto parse_out;
+ }
+
+ if (tocs[toc_num]) {
+ fprintf(stderr, "Duplicate TOC ID %d\n", toc_num);
+ goto parse_out;
+ }
+
+ tocs[toc_num] = parse_toc(&line[3], block_size, block_count);
+ if (!tocs[toc_num])
+ goto parse_out;
+ toc_created = true;
+ } else {
+ if (!toc_created) {
+ fprintf(stderr, "WARNING: Attempting to parse a partition line without any TOCs created.\n");
+ fprintf(stderr, " Generating a default TOC at zero\n");
+ rc = ffs_hdr_new(block_size, block_count, NULL, &tocs[0]);
+ if (rc) {
+ rc = 7;
+ fprintf(stderr, "Couldn't generate a default TOC at zero\n");
+ goto parse_out;
+ }
+ toc_created = true;
+ }
+ rc = parse_entry(bl, tocs, line, allow_empty);
+ if (rc) {
+ rc = 6;
+ goto parse_out;
+ }
+ }
+ }
+
+ for(i = 0; i < MAX_TOCS; i++) {
+ if (tocs[i]) {
+ rc = ffs_hdr_finalise(bl, tocs[i]);
+ if (rc) {
+ rc = 7;
+ fprintf(stderr, "Failed to write out TOC values\n");
+ break;
+ }
+ }
+ }
+
+parse_out:
+ if (rc == 5 || rc == 6)
+ fprintf(stderr, "Failed to parse input file '%s' at line %d\n",
+ input, line_number);
+ arch_flash_close(bl, pnor);
+ fclose(in_file);
+ for(i = 0; i < MAX_TOCS; i++)
+ ffs_hdr_free(tocs[i]);
+ free(input);
+ free(pnor);
+ return rc;
+}
diff --git a/roms/skiboot/external/ffspart/rules.mk b/roms/skiboot/external/ffspart/rules.mk
new file mode 100644
index 000000000..e006dc5b7
--- /dev/null
+++ b/roms/skiboot/external/ffspart/rules.mk
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+.DEFAULT_GOAL := all
+
+override CFLAGS += -O2 -Wall -g -I.
+EXE = ffspart
+OBJS = $(EXE).o version.o
+LIBFLASH_FILES := libflash.c libffs.c ecc.c blocklevel.c file.c
+LIBFLASH_OBJS := $(addprefix libflash-, $(LIBFLASH_FILES:.c=.o))
+LIBFLASH_SRC := $(addprefix libflash/,$(LIBFLASH_FILES))
+OBJS += $(LIBFLASH_OBJS)
+OBJS += common-arch_flash.o
+
+prefix = /usr/local/
+sbindir = $(prefix)/sbin
+
+CC = $(CROSS_COMPILE)gcc
+
+FFSPART_VERSION ?= $(shell ./make_version.sh $(EXE))
+
+version.c: make_version.sh .version
+ @(if [ "a$(FFSPART_VERSION)" = "a" ]; then \
+ echo "#error You need to set FFSPART_VERSION environment variable" > $@ ;\
+ else \
+ echo "const char version[] = \"$(FFSPART_VERSION)\";" ;\
+ fi) > $@
+
+%.o : %.c
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(LIBFLASH_SRC): | links
+
+$(LIBFLASH_OBJS): libflash-%.o : libflash/%.c
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(EXE): $(OBJS)
+ $(Q_CC)$(CC) $(CFLAGS) $^ -lrt -o $@
+
diff --git a/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.in b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.in
new file mode 100644
index 000000000..961c3fefa
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.in
@@ -0,0 +1,4 @@
+ONE,0x00400,0x00000100,EL,,/dev/zero
+TWO,0x00500,0x00000100,EF,,/dev/zero
+THREE,0x600,0x00000100,EF,,/dev/zero
+FOUR,0x0700,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.out b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.out
new file mode 100644
index 000000000..e00fa5c0e
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.out
Binary files differ
diff --git a/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.in b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.in
new file mode 100644
index 000000000..7fc12ba4f
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EL,,SEDCATCH_1
+TWO,0x00000400,0x00000100,EF,,SEDCATCH_2
+THREE,0x00000500,0x00000100,EF,,SEDCATCH_3
+FOUR,0x00000600,0x00000100,EF,,SEDCATCH_4
diff --git a/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.out b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.out
new file mode 100644
index 000000000..617c4ef44
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.out
Binary files differ
diff --git a/roms/skiboot/external/ffspart/test/files/05-hdr-overlap.in b/roms/skiboot/external/ffspart/test/files/05-hdr-overlap.in
new file mode 100644
index 000000000..8af94d2d7
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/05-hdr-overlap.in
@@ -0,0 +1,4 @@
+ONE,0x00000200,0x00000100,EV,,/dev/zero
+TWO,0x00000300,0x00000100,EF,,/dev/zero
+THREE,0x00000400,0x00000100,EF,,/dev/zero
+FOUR,0x00000500,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/06-small-flash.in b/roms/skiboot/external/ffspart/test/files/06-small-flash.in
new file mode 100644
index 000000000..a717888b3
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/06-small-flash.in
@@ -0,0 +1,5 @@
+ONE,0x00000300,0x00000100,EV,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
+FIVE,0x00000700,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/07-big-files.in b/roms/skiboot/external/ffspart/test/files/07-big-files.in
new file mode 100644
index 000000000..0369bf665
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/07-big-files.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,SEDCATCH_1
+TWO,0x00000400,0x00000100,EF,,SEDCATCH_2
+THREE,0x00000500,0x00000100,EF,,SEDCATCH_3
+FOUR,0x00000600,0x00000100,EF,,SEDCATCH_4
diff --git a/roms/skiboot/external/ffspart/test/files/08-small-files.in b/roms/skiboot/external/ffspart/test/files/08-small-files.in
new file mode 100644
index 000000000..0369bf665
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/08-small-files.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,SEDCATCH_1
+TWO,0x00000400,0x00000100,EF,,SEDCATCH_2
+THREE,0x00000500,0x00000100,EF,,SEDCATCH_3
+FOUR,0x00000600,0x00000100,EF,,SEDCATCH_4
diff --git a/roms/skiboot/external/ffspart/test/files/10-bad-input.in b/roms/skiboot/external/ffspart/test/files/10-bad-input.in
new file mode 100644
index 000000000..5c4c90ee6
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/10-bad-input.in
@@ -0,0 +1,4 @@
+ONE0x00000300,0x00000100,EV,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/11-long-name.in b/roms/skiboot/external/ffspart/test/files/11-long-name.in
new file mode 100644
index 000000000..bd32ea89a
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/11-long-name.in
@@ -0,0 +1,4 @@
+This_is_more_than_15_characters,0x00000300,0x00000100,EV,,/dev/zero
+This_is_exactly,0x00000400,0x00000100,EF,,/dev/zero
+This_is_one_le,0x00000500,0x00000100,EF,,/dev/zero
+This_is_one_more,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/12-bad-numbers-base.in b/roms/skiboot/external/ffspart/test/files/12-bad-numbers-base.in
new file mode 100644
index 000000000..8f6fd27a9
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/12-bad-numbers-base.in
@@ -0,0 +1,4 @@
+ONE,0xg0000300,0x00000100,EV,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/13-bad-numbers-size.in b/roms/skiboot/external/ffspart/test/files/13-bad-numbers-size.in
new file mode 100644
index 000000000..b1dacc738
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/13-bad-numbers-size.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00001g00,EV,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/14-bad-input-flags.in b/roms/skiboot/external/ffspart/test/files/14-bad-input-flags.in
new file mode 100644
index 000000000..dcf18f248
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/14-bad-input-flags.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EVZ,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/15-overlapping-partitions.in b/roms/skiboot/external/ffspart/test/files/15-overlapping-partitions.in
new file mode 100644
index 000000000..e496afa17
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/15-overlapping-partitions.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,/dev/zero
+TWO,0x00000350,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/16-create-blank.in b/roms/skiboot/external/ffspart/test/files/16-create-blank.in
new file mode 100644
index 000000000..7cff6bf01
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/16-create-blank.in
@@ -0,0 +1,4 @@
+ONE,0x00400,0x00000100,EL,,
+TWO,0x00500,0x00000100,EF,,
+THREE,0x600,0x00000100,EF,,
+FOUR,0x0700,0x00000100,EF,,
diff --git a/roms/skiboot/external/ffspart/test/files/16-create-blank.out b/roms/skiboot/external/ffspart/test/files/16-create-blank.out
new file mode 100644
index 000000000..43b9583b3
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/16-create-blank.out
Binary files differ
diff --git a/roms/skiboot/external/ffspart/test/files/17-toc.in b/roms/skiboot/external/ffspart/test/files/17-toc.in
new file mode 100644
index 000000000..6020e0fe2
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/17-toc.in
@@ -0,0 +1,5 @@
+@0,0x0,
+ONE,0x00400,0x00000100,EL,,/dev/zero
+TWO,0x00500,0x00000100,EF,,/dev/zero
+THREE,0x600,0x00000100,EF,,/dev/zero
+FOUR,0x0700,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/17-toc.out b/roms/skiboot/external/ffspart/test/files/17-toc.out
new file mode 100644
index 000000000..e00fa5c0e
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/17-toc.out
Binary files differ
diff --git a/roms/skiboot/external/ffspart/test/files/18-eraseblock-gt-first-partition.in b/roms/skiboot/external/ffspart/test/files/18-eraseblock-gt-first-partition.in
new file mode 100644
index 000000000..bed913682
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/18-eraseblock-gt-first-partition.in
@@ -0,0 +1,2 @@
+@0,0x0,
+FIRST,0x400,0x100,,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/make-check-test b/roms/skiboot/external/ffspart/test/make-check-test
new file mode 100755
index 000000000..943528678
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/make-check-test
@@ -0,0 +1 @@
+make -C external/ffspart/ check
diff --git a/roms/skiboot/external/ffspart/test/results/00-usage.err b/roms/skiboot/external/ffspart/test/results/00-usage.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/00-usage.err
diff --git a/roms/skiboot/external/ffspart/test/results/00-usage.out b/roms/skiboot/external/ffspart/test/results/00-usage.out
new file mode 100644
index 000000000..cf7213d7b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/00-usage.out
@@ -0,0 +1,19 @@
+Open-Power FFS format tool VERSION
+Usage: ./ffspart [options] -e -s size -c num -i layout_file -p pnor_file ...
+
+ Options:
+ -e, --allow_empty
+ Create partition as blank if not specified (sets ECC if flag set)
+
+ -s, --block_size=size
+ Size (in hex with leading 0x) of the blocks on the flash in bytes
+
+ -c, --block_count=num
+ Number of blocks on the flash
+
+ -i, --input=file
+ File containing the required partition data
+
+ -p, --pnor=file
+ Output file to write data
+
diff --git a/roms/skiboot/external/ffspart/test/results/01-param-sanity.err b/roms/skiboot/external/ffspart/test/results/01-param-sanity.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/01-param-sanity.err
diff --git a/roms/skiboot/external/ffspart/test/results/01-param-sanity.out b/roms/skiboot/external/ffspart/test/results/01-param-sanity.out
new file mode 100644
index 000000000..cf7213d7b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/01-param-sanity.out
@@ -0,0 +1,19 @@
+Open-Power FFS format tool VERSION
+Usage: ./ffspart [options] -e -s size -c num -i layout_file -p pnor_file ...
+
+ Options:
+ -e, --allow_empty
+ Create partition as blank if not specified (sets ECC if flag set)
+
+ -s, --block_size=size
+ Size (in hex with leading 0x) of the blocks on the flash in bytes
+
+ -c, --block_count=num
+ Number of blocks on the flash
+
+ -i, --input=file
+ File containing the required partition data
+
+ -p, --pnor=file
+ Output file to write data
+
diff --git a/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.err b/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.err
diff --git a/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.out b/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.out
new file mode 100644
index 000000000..cf7213d7b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.out
@@ -0,0 +1,19 @@
+Open-Power FFS format tool VERSION
+Usage: ./ffspart [options] -e -s size -c num -i layout_file -p pnor_file ...
+
+ Options:
+ -e, --allow_empty
+ Create partition as blank if not specified (sets ECC if flag set)
+
+ -s, --block_size=size
+ Size (in hex with leading 0x) of the blocks on the flash in bytes
+
+ -c, --block_count=num
+ Number of blocks on the flash
+
+ -i, --input=file
+ File containing the required partition data
+
+ -p, --pnor=file
+ Output file to write data
+
diff --git a/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.err b/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.err
new file mode 100644
index 000000000..ff8aca201
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.err
@@ -0,0 +1,5 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Adding partition 'THREE' would cause partition 'ONE' at 0x00000200 to overlap with the header
+Couldn't add 'THREE' partition to default TOC: 107
+Failed to parse input file 'FILE' at line 3
diff --git a/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.out b/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.out
diff --git a/roms/skiboot/external/ffspart/test/results/06-small-flash.err b/roms/skiboot/external/ffspart/test/results/06-small-flash.err
new file mode 100644
index 000000000..a5316a90e
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/06-small-flash.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't add 'TWO' partition to default TOC: 108
+Failed to parse input file 'FILE' at line 2
diff --git a/roms/skiboot/external/ffspart/test/results/06-small-flash.out b/roms/skiboot/external/ffspart/test/results/06-small-flash.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/06-small-flash.out
diff --git a/roms/skiboot/external/ffspart/test/results/07-big-files.err b/roms/skiboot/external/ffspart/test/results/07-big-files.err
new file mode 100644
index 000000000..bfb6fa022
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/07-big-files.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+File 'FILE_ONE' for partition 'ONE' is too large, 257 > 227
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/07-big-files.out b/roms/skiboot/external/ffspart/test/results/07-big-files.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/07-big-files.out
diff --git a/roms/skiboot/external/ffspart/test/results/08-small-files.err b/roms/skiboot/external/ffspart/test/results/08-small-files.err
new file mode 100644
index 000000000..c902b642b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/08-small-files.err
@@ -0,0 +1,2 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
diff --git a/roms/skiboot/external/ffspart/test/results/08-small-files.out b/roms/skiboot/external/ffspart/test/results/08-small-files.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/08-small-files.out
diff --git a/roms/skiboot/external/ffspart/test/results/10-bad-input.err b/roms/skiboot/external/ffspart/test/results/10-bad-input.err
new file mode 100644
index 000000000..01c43d81a
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/10-bad-input.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't parse 'ONE0x00000300' partition length
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/10-bad-input.out b/roms/skiboot/external/ffspart/test/results/10-bad-input.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/10-bad-input.out
diff --git a/roms/skiboot/external/ffspart/test/results/11-long-name.err b/roms/skiboot/external/ffspart/test/results/11-long-name.err
new file mode 100644
index 000000000..cf8dcc325
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/11-long-name.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+WARNING: Long partition name will get truncated to 'This_is_more_tha'
+WARNING: Long partition name will get truncated to 'This_is_one_more'
diff --git a/roms/skiboot/external/ffspart/test/results/11-long-name.out b/roms/skiboot/external/ffspart/test/results/11-long-name.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/11-long-name.out
diff --git a/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.err b/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.err
new file mode 100644
index 000000000..cdd836eac
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't parse 'ONE' partition base address
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.out b/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.out
diff --git a/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.err b/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.err
new file mode 100644
index 000000000..19078d5db
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't parse 'ONE' partition length
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.out b/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.out
diff --git a/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.err b/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.err
new file mode 100644
index 000000000..613525c3d
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't parse 'ONE' partition flags
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.out b/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.out
diff --git a/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.err b/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.err
new file mode 100644
index 000000000..c81d3c0e4
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't add 'TWO' partition to default TOC: 107
+Failed to parse input file 'FILE' at line 2
diff --git a/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.out b/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.out
diff --git a/roms/skiboot/external/ffspart/test/test-ffspart b/roms/skiboot/external/ffspart/test/test-ffspart
new file mode 100755
index 000000000..7e38a4e37
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/test-ffspart
@@ -0,0 +1,5 @@
+#! /bin/sh
+
+. test/test.sh
+
+run_tests "test/tests/*" "test/results" "test/files"
diff --git a/roms/skiboot/external/ffspart/test/tests/00-usage b/roms/skiboot/external/ffspart/test/tests/00-usage
new file mode 100644
index 000000000..0ca453f4d
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/00-usage
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+run_binary "./ffspart"
+if [ "$?" -ne 1 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "ffspart"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/01-param-sanity b/roms/skiboot/external/ffspart/test/tests/01-param-sanity
new file mode 100644
index 000000000..d63736006
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/01-param-sanity
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+run_binary "./ffspart" "-c 3 -i /dev/null -p /dev/null"
+if [ "$?" -ne 1 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "ffspart"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/01.1-param-sanity b/roms/skiboot/external/ffspart/test/tests/01.1-param-sanity
new file mode 100644
index 000000000..1ce1f906c
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/01.1-param-sanity
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+run_binary "./ffspart" "-s 1 -i /dev/null -p /dev/null"
+if [ "$?" -ne 1 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "ffspart"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/03-tiny-pnor b/roms/skiboot/external/ffspart/test/tests/03-tiny-pnor
new file mode 100644
index 000000000..e7783394b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/03-tiny-pnor
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+if ! cmp -n $((0x100)) $DATA_DIR/$CUR_TEST.out $DATA_DIR/$CUR_TEST.gen ; then
+ echo "Output differs"
+ fail_test
+fi
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/04-tiny-pnor2 b/roms/skiboot/external/ffspart/test/tests/04-tiny-pnor2
new file mode 100644
index 000000000..3db5f1e29
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/04-tiny-pnor2
@@ -0,0 +1,25 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+i=1;
+while [ $i -lt 5 ] ; do
+ j=0;
+ while [ $j -lt $((0xe0)) ] ; do
+ echo -n "$i" >> $DATA_DIR/$CUR_TEST.$i;
+ j=$(expr $j + 1);
+ done
+ sed -i "s|SEDCATCH_$i|$DATA_DIR\/$CUR_TEST.$i|" $DATA_DIR/$CUR_TEST.in
+ i=$(expr $i + 1);
+done
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+if ! cmp -n $((0x500)) $DATA_DIR/$CUR_TEST.out $DATA_DIR/$CUR_TEST.gen ; then
+ echo "Output differs"
+ fail_test
+fi
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/05-hdr-overlap b/roms/skiboot/external/ffspart/test/tests/05-hdr-overlap
new file mode 100644
index 000000000..39a1a1406
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/05-hdr-overlap
@@ -0,0 +1,14 @@
+#! /bin/sh
+
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 6 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/06-small-flash b/roms/skiboot/external/ffspart/test/tests/06-small-flash
new file mode 100644
index 000000000..7c41e33f0
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/06-small-flash
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 4 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 6 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/07-big-files b/roms/skiboot/external/ffspart/test/tests/07-big-files
new file mode 100644
index 000000000..f7a0064d9
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/07-big-files
@@ -0,0 +1,26 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+i=1;
+while [ $i -lt 5 ] ; do
+ j=0;
+ while [ $j -lt $((0x101)) ] ; do
+ echo -n "$i" >> $DATA_DIR/$CUR_TEST.$i;
+ j=$(expr $j + 1);
+ done
+ sed -i "s|SEDCATCH_$i|$DATA_DIR\/$CUR_TEST.$i|" $DATA_DIR/$CUR_TEST.in
+ i=$(expr $i + 1);
+done
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.1|FILE_ONE|" "$STDERR_OUT"
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/08-small-files b/roms/skiboot/external/ffspart/test/tests/08-small-files
new file mode 100644
index 000000000..1e4f3b3be
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/08-small-files
@@ -0,0 +1,22 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+i=1;
+while [ $i -lt 5 ] ; do
+ j=0;
+ while [ $j -lt $((0xe0)) ] ; do
+ echo -n "$i" >> $DATA_DIR/$CUR_TEST.$i;
+ j=$(expr $j + 1);
+ done
+ sed -i "s|SEDCATCH_$i|$DATA_DIR\/$CUR_TEST.$i|" $DATA_DIR/$CUR_TEST.in
+ i=$(expr $i + 1);
+done
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/10-bad-input b/roms/skiboot/external/ffspart/test/tests/10-bad-input
new file mode 100644
index 000000000..d7c433262
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/10-bad-input
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/11-long-name b/roms/skiboot/external/ffspart/test/tests/11-long-name
new file mode 100644
index 000000000..893aad478
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/11-long-name
@@ -0,0 +1,11 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/12-bad-numbers-base b/roms/skiboot/external/ffspart/test/tests/12-bad-numbers-base
new file mode 100644
index 000000000..d7c433262
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/12-bad-numbers-base
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/13-bad-numbers-size b/roms/skiboot/external/ffspart/test/tests/13-bad-numbers-size
new file mode 100644
index 000000000..d7c433262
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/13-bad-numbers-size
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/14-bad-input-flags b/roms/skiboot/external/ffspart/test/tests/14-bad-input-flags
new file mode 100644
index 000000000..d7c433262
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/14-bad-input-flags
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/15-overlapping-partitions b/roms/skiboot/external/ffspart/test/tests/15-overlapping-partitions
new file mode 100644
index 000000000..6d85748d0
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/15-overlapping-partitions
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 6 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/16-create-blank b/roms/skiboot/external/ffspart/test/tests/16-create-blank
new file mode 100644
index 000000000..02640e4e6
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/16-create-blank
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen --allow_empty"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+if ! cmp $DATA_DIR/$CUR_TEST.out $DATA_DIR/$CUR_TEST.gen ; then
+ echo "Output differs"
+ fail_test
+fi
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/17-toc b/roms/skiboot/external/ffspart/test/tests/17-toc
new file mode 100644
index 000000000..e7783394b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/17-toc
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+if ! cmp -n $((0x100)) $DATA_DIR/$CUR_TEST.out $DATA_DIR/$CUR_TEST.gen ; then
+ echo "Output differs"
+ fail_test
+fi
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/18-eraseblock-gt-first-partition b/roms/skiboot/external/ffspart/test/tests/18-eraseblock-gt-first-partition
new file mode 100644
index 000000000..662665172
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/18-eraseblock-gt-first-partition
@@ -0,0 +1,45 @@
+#! /bin/sh
+
+EXPECTED="ID=01 FIRST 0x00000400..0x00000500 (actual=0x00000100) [----------]"
+FFSIMG=$DATA_DIR/$CUR_TEST.gen
+
+command -v pflash > /dev/null || echo "skipping test: pflash required but not found in PATH" && exit 0
+
+
+# https://github.com/open-power/skiboot/issues/205
+touch $FFSIMG
+
+# Use a block size that works with the existing codebase
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $FFSIMG"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+CONTENTS=$(pflash -i -F $FFSIMG | grep FIRST)
+
+if [ "$CONTENTS" != "$EXPECTED" ]; then
+ echo "Actual: $CONTENTS"
+ echo "Expected: $EXPECTED"
+ fail_test
+fi
+
+# Use a block size that fails with the existing codebase. This test is expected
+# to fail; change it to expect pass when the issue is fixed.
+# https://github.com/open-power/skiboot/issues/202
+run_binary "./ffspart" "-s 0x1000 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $FFSIMG"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+CONTENTS=$(pflash -i -F $FFSIMG | grep FIRST)
+
+if [ "$CONTENTS" != "$EXPECTED" ]; then
+ echo "This case should be marked as fail but ffspart has bugs"
+ echo "https://github.com/open-power/skiboot/issues/202"
+ echo "Actual: $CONTENTS"
+ echo "Expected: $EXPECTED"
+ pass_test
+fi
+
+
+pass_test
diff --git a/roms/skiboot/external/fwts/generate-fwts-olog b/roms/skiboot/external/fwts/generate-fwts-olog
new file mode 100755
index 000000000..c89fbf6df
--- /dev/null
+++ b/roms/skiboot/external/fwts/generate-fwts-olog
@@ -0,0 +1,227 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Copyright 2016 Jeremy Kerr <jk@ozlabs.org>
+
+import os.path
+import re
+import sys
+import string
+import json
+import argparse
+import subprocess
+from pyparsing import Regex, Literal, Word, Combine, OneOrMore, QuotedString, \
+ lineno
+
+json_params = {
+ 'indent': 1,
+ 'sort_keys': True,
+ 'separators': (',', ': '),
+}
+
+def create_parser():
+ # Match a C-style comment starting with two *s
+ comment = Regex(r'/\*\*(?P<content>.*?)\*/', re.DOTALL)
+
+ # Match an @fwts-<tag> annotation (within the comment), plus the proceeding
+ # text
+ annotation = Regex(r'@fwts-(?P<tag>\w+)\W+(?P<text>.*?)(?=@fwts-|\Z)',
+ re.DOTALL)
+
+ # Match the following prlog() call
+ log_call = (((Literal("prerror") + Literal('(').suppress()) |
+ (Literal("prlog") + Literal('(').suppress() +
+ Word(string.ascii_letters + string.digits + '_') +
+ Literal(',').suppress())) +
+ Combine(OneOrMore(QuotedString('"')), adjacent=False) +
+ (Literal(')') | Literal(',')).suppress()
+ )
+
+ pattern = comment + log_call
+ pattern.setWhitespaceChars(string.whitespace + '\n')
+
+ def comment_action(tok):
+ patterns = {}
+ for result in annotation.scanString(tok['content']):
+ patterns.update(result[0][0])
+ return patterns
+
+ def annotation_action(tok):
+ return {
+ tok['tag']: cleanup_content(tok['text'])
+ }
+
+ comment.setParseAction(comment_action)
+ annotation.setParseAction(annotation_action)
+ pattern.parseWithTabs()
+
+ return pattern
+
+def find_sources(dirname):
+ sources = []
+
+ def is_source(fname):
+ return fname.endswith('.c')
+
+ for directory, dirnames, filenames in os.walk(dirname):
+ sources.extend([ os.path.join(directory, fname)
+ for fname in filenames if is_source(fname) ])
+ return sources
+
+def cleanup_content(content):
+ comment_prefix_re = re.compile(r'^\s*\*\s*', re.MULTILINE)
+ whitespace_re = re.compile(r'\s+')
+
+ content = comment_prefix_re.sub(' ', content)
+ content = whitespace_re.sub(' ', content)
+ return content.strip()
+
+def warn(loc, message):
+ print >>sys.stderr, 'WARNING:%s:%d: %s' % (loc[0], loc[1], message)
+
+def log_level_to_fwts(level):
+ level_map = {
+ 'PR_EMERG': 'LOG_LEVEL_CRITICAL',
+ 'PR_ALERT': 'LOG_LEVEL_CRITICAL',
+ 'PR_CRIT': 'LOG_LEVEL_CRITICAL',
+ 'PR_ERR': 'LOG_LEVEL_CRITICAL',
+ 'PR_WARNING': 'LOG_LEVEL_HIGH',
+ 'PR_NOTICE': 'LOG_LEVEL_MEDIUM',
+ 'PR_PRINTF': 'LOG_LEVEL_MEDIUM',
+ }
+ return level_map.get(level, 'LOG_LEVEL_LOW')
+
+def message_to_pattern(loc, msg):
+ """ Convert a C printf()-style template to a pattern suitable for fwts """
+
+ # Somewhat-simplified match for a %-template
+ template_re = re.compile(
+ '%(?P<flag>[-#0 +]*)'
+ '(?P<width>(?:[0-9]*|\*))?'
+ '(?P<precision>\.*(?:[1-9][0-9]*|\*))?'
+ '(?:hh|h|ll|l|L|j|z|t)?'
+ '(?P<conversion>[a-zA-Z%])')
+ global is_regex
+ is_regex = False
+
+ def expand_template(match):
+ global is_regex
+ c = match.group('conversion').lower()
+ if c == '%':
+ return '%'
+ is_regex = True
+ if c in ['d', 'i', 'u']:
+ return '[0-9]+'
+ elif c == 'o':
+ return '[0-7]+'
+ elif c == 'x':
+ return '[0-9a-f]+'
+ elif c == 'p':
+ return '(0x[0-9a-f]+|nil)'
+ elif c == 's':
+ return '.*'
+ else:
+ warn(loc, "Unknown template conversion '%s'" % match.group(0))
+ return '.*'
+
+ escape_re = re.compile(r'\\(?P<char>.)', re.DOTALL)
+ def expand_escape(match):
+ global is_regex
+ c = match.group('char')
+ if c == 'n':
+ return '\n'
+ elif c in ['\\', '"']:
+ return c
+ else:
+ warn(loc, "Unhandled escape sequence '%s'" % match.group(0))
+ is_regex = True
+ return '.'
+
+ pattern = template_re.sub(expand_template, msg)
+ pattern = escape_re.sub(expand_escape, pattern)
+ pattern = pattern.strip()
+
+ compare_mode = "string"
+ if is_regex:
+ compare_mode = "regex"
+
+ return (compare_mode, pattern)
+
+def parse_patterns(parser, fname, tag):
+ patterns = []
+ data = open(fname).read()
+ i = 1
+ for result in parser.scanString(data):
+ (token, loc, _) = result
+ if token[1] == 'prlog':
+ (annotations, logfn, level, msg) = token
+ else:
+ (annotations, logfn, msg) = token
+ level = 'PR_ERR'
+
+ loc = (fname, lineno(loc, data))
+
+ if logfn != 'prlog' and logfn != 'prerror':
+ warn(loc, "unknown log output function '%s'" % logfn)
+
+ compare_mode, pattern_str = message_to_pattern(loc, msg)
+
+ pattern = {
+ 'log_level': log_level_to_fwts(level),
+ 'compare_mode': compare_mode,
+ 'pattern': pattern_str,
+ 'last_tag': tag,
+ }
+
+ pattern.update(annotations)
+
+ if not 'label' in pattern:
+ warn(loc, "missing label")
+ pattern['label'] = '%s:%d' % (fname, i)
+ i += 1
+
+ if not 'advice' in pattern:
+ warn(loc, "missing advice")
+
+ allowed_data = ['compare_mode', 'log_level',
+ 'pattern', 'advice', 'label', 'last_tag']
+ extras = set(pattern.keys()) - set(allowed_data)
+ if extras:
+ warn(loc, "unknown pattern annotation: %s" %
+ ','.join([ "'%s'" % e for e in extras]))
+ for e in extras:
+ del pattern[e]
+
+ patterns.append(pattern)
+
+ return patterns
+
+if __name__ == '__main__':
+ argparser = argparse.ArgumentParser(
+ description='Generate FWTS olog definitions from the skiboot '
+ 'source tree')
+ argparser.add_argument('directories', metavar='DIR', nargs='*',
+ help='path to source files (default .)', default=['.'])
+ argparser.add_argument('--output', '-o', metavar='FILE',
+ type=argparse.FileType('w'), default=sys.stdout,
+ help='output to FILE (default to stdout)', nargs='?')
+ args = argparser.parse_args()
+
+ sources = []
+ for directory in args.directories:
+ try:
+ git_tag = subprocess.check_output(["git","-C", directory, "describe", "--abbrev=0" ], text=True)
+ except:
+ git_tag = "???"
+ git_tag = git_tag.replace("\n", "")
+ sources.extend([ (x, git_tag) for x in find_sources(directory)])
+
+ parser = create_parser()
+ patterns = []
+ for source, tag in sources:
+ patterns.extend(parse_patterns(parser, source, tag))
+
+ data = {'olog_error_warning_patterns': patterns}
+
+ args.output.write(json.dumps(data, **json_params) + '\n')
+
diff --git a/roms/skiboot/external/fwts/merge-fwts-olog b/roms/skiboot/external/fwts/merge-fwts-olog
new file mode 100755
index 000000000..de70d6cfe
--- /dev/null
+++ b/roms/skiboot/external/fwts/merge-fwts-olog
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Copyright 2016 IBM Corp.
+
+import json
+import sys
+
+json_params = {
+ 'indent': 1,
+ 'sort_keys': True,
+ 'separators': (',', ': '),
+}
+
+def get_input():
+ while True:
+ resp = input("Update pattern to match both? (y/n): ")
+ if resp in [ "y", "Y" ]:
+ break
+ elif resp in [ "n", "N" ]:
+ print("New entry will be added.")
+ return False
+ else:
+ print("???")
+ continue
+
+ return input("New pattern: ")
+
+def main():
+ if len(sys.argv) != 4:
+ print("USAGE: merge.py old_olog new_olog output")
+ sys.exit(-1)
+
+ old_olog = sys.argv[1]
+ cur_olog = sys.argv[2]
+ out_olog = sys.argv[3]
+
+ try:
+ old_file = open(old_olog, "r")
+ old_json = json.load(old_file)
+ old_file.close()
+ except Exception as e:
+ print("Failed to parse old olog: %s" % e)
+ sys.exit(-1)
+
+ try:
+ cur_file = open(cur_olog, "r")
+ cur_json = json.load(cur_file)
+ cur_file.close()
+ except Exception as e:
+ print("Failed to parse new olog: %s" % e)
+ sys.exit(-1)
+
+ try:
+ out_file = open(out_olog, "w")
+ except Exception as e:
+ print("Failed to open output file: %s" % e)
+ sys.exit(-1)
+
+ cur_patterns = cur_json["olog_error_warning_patterns"]
+ old_patterns = old_json["olog_error_warning_patterns"]
+
+ # Match current patterns to old definitions, detect when pattern is
+ # different.
+
+ for cp in cur_patterns:
+ for op in old_patterns:
+ if cp["label"] != op["label"]:
+ continue
+
+ if cp["pattern"] != op["pattern"]:
+ print("Pattern update detected.")
+ print("Label: %s" % cp["label"])
+ print("")
+ print("Cur Pattern: %s" % cp["pattern"])
+ print("New Pattern: %s" % op["pattern"])
+ print("")
+
+ user_pattern = get_input()
+
+ if user_pattern == False:
+ continue
+
+ cp["pattern"] = user_pattern
+
+ op["found"] = True
+ break
+
+ # Take any old patterns that are no longer current and move them over
+
+ for op in old_patterns:
+ if "found" in op:
+ continue
+
+ cur_patterns.append(op)
+
+ json.dump(cur_json, out_file, **json_params)
+ out_file.close()
+
+ print("OK")
+
+if __name__ == "__main__":
+ main()
diff --git a/roms/skiboot/external/gard/.gitignore b/roms/skiboot/external/gard/.gitignore
new file mode 100644
index 000000000..7e8f83f20
--- /dev/null
+++ b/roms/skiboot/external/gard/.gitignore
@@ -0,0 +1,11 @@
+o
+*.d
+ccan
+common
+common-ast-sf-ctrl.c
+common-io.h
+gard
+test/test.sh
+libflash
+make_version.sh
+gard-*.tar
diff --git a/roms/skiboot/external/gard/Makefile b/roms/skiboot/external/gard/Makefile
new file mode 100644
index 000000000..e249cf846
--- /dev/null
+++ b/roms/skiboot/external/gard/Makefile
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: Apache-2.0
+# This tool is a linux userland tool and should be completely stand alone
+include rules.mk
+GET_ARCH = ../../external/common/get_arch.sh
+include ../../external/common/rules.mk
+
+all: links arch_links $(EXE)
+
+.PHONY: coverage
+coverage: CFLAGS += -fprofile-arcs -ftest-coverage
+coverage: check
+
+#Rebuild version.o so that the the version always matches
+#what the test suite will get from ./make_version.sh
+check: version.o all
+ @ln -sf ../../make_version.sh make_version.sh
+ @ln -sf ../../test/test.sh test/test.sh
+ @test/test-gard
+
+.PHONY: VERSION-always
+.version: VERSION-always
+ @echo $(GARD_VERSION) > $@.tmp
+ @cmp -s $@ $@.tmp || cp $@.tmp $@
+ @rm -f $@.tmp
+
+.PHONY: dist
+#File is named $(GARD_VERSION).tar because the expectation is that gard-
+#is always at the start of the verion. This remains consistent with skiboot
+#version strings
+dist: arch_links links .version
+ @find -L ../gard/ -iname '*.[ch]' -print0 | xargs -0 tar -rhf $(GARD_VERSION).tar
+ @tar --transform 's/Makefile.dist/Makefile/' -rhf $(GARD_VERSION).tar \
+ ../gard/Makefile.dist ../gard/rules.mk \
+ ../gard/.version ../gard/common/*
+
+clean: arch_clean
+ rm -f $(OBJS) $(EXE) *.o *.d .version .version.tmp
+
+distclean: clean
+ rm -f *.c~ *.h~ *.sh~ Makefile~ config.mk~ libflash/*.c~ libflash/*.h~
+ rm -f libflash ccan common io.h version.c make_version.sh
+ rm -f gard-*.tar
+
diff --git a/roms/skiboot/external/gard/Makefile.dist b/roms/skiboot/external/gard/Makefile.dist
new file mode 100644
index 000000000..e03ad230a
--- /dev/null
+++ b/roms/skiboot/external/gard/Makefile.dist
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#Prevent make from trying to remake arch links symlinks
+#which are fixed files now
+ARCH_LINKS :=
+
+GARD_VERSION := $(shell cat .version)
+
+include rules.mk
+GET_ARCH = common/get_arch.sh
+include common/rules.mk
+
+all: $(EXE)
+
+clean:
+ rm -f $(OBJS) *.o
+distclean: clean
+ rm -f $(EXE)
diff --git a/roms/skiboot/external/gard/config.h b/roms/skiboot/external/gard/config.h
new file mode 100644
index 000000000..42aef6ddb
--- /dev/null
+++ b/roms/skiboot/external/gard/config.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * For CCAN
+ *
+ * Copyright 2015-2017 IBM Corp
+ */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF 1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
+
+#ifndef HAVE_LITTLE_ENDIAN
+#ifndef HAVE_BIG_ENDIAN
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_LITTLE_ENDIAN 1
+#else
+#define HAVE_BIG_ENDIAN 1
+#endif
+#endif
+#endif
+
+/* Keep -Wundef happy by defining whatever isn't on commandline to 0 */
+#if defined(HAVE_LITTLE_ENDIAN) && HAVE_LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN 0
+#endif
+#if defined(HAVE_BIG_ENDIAN) && HAVE_BIG_ENDIAN
+#define HAVE_LITTLE_ENDIAN 0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
diff --git a/roms/skiboot/external/gard/gard.c b/roms/skiboot/external/gard/gard.c
new file mode 100644
index 000000000..53a26d0e9
--- /dev/null
+++ b/roms/skiboot/external/gard/gard.c
@@ -0,0 +1,1017 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Manipulate GARD records in the GARD partition
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#include <ccan/array_size/array_size.h>
+
+#include <mtd/mtd-abi.h>
+
+#include <getopt.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/file.h>
+#include <libflash/blocklevel.h>
+#include <common/arch_flash.h>
+
+#include "gard.h"
+
+#define FDT_PATH "/proc/device-tree"
+#define FDT_FSP_NODE FDT_PATH"/fsps"
+#define FDT_ACTIVE_FLASH_PATH FDT_PATH"/chosen/ibm,system-flash"
+#define SYSFS_MTD_PATH "/sys/class/mtd/"
+#define FLASH_GARD_PART "GUARD"
+
+#define VPNOR_GARD_DIR "/media/pnor-prsv"
+#define VPNOR_GARD_FILE VPNOR_GARD_DIR"/GUARD"
+
+/* Full gard version number (possibly includes gitid). */
+extern const char version[];
+
+
+#define __unused __attribute__((unused))
+
+struct gard_ctx {
+ uint32_t f_size;
+ uint32_t f_pos;
+
+ uint32_t gard_part_idx;
+ uint32_t gard_data_pos;
+ uint32_t gard_data_len;
+
+ struct blocklevel_device *bl;
+ struct ffs_handle *ffs;
+};
+
+static void show_flash_err(int rc)
+{
+ switch (rc) {
+ case FFS_ERR_BAD_MAGIC:
+ fprintf(stderr, "libffs bad magic\n");
+ break;
+ case FFS_ERR_BAD_VERSION:
+ fprintf(stderr, "libffs bad version\n");
+ break;
+ case FFS_ERR_BAD_CKSUM:
+ fprintf(stderr, "libffs bad check sum\n");
+ break;
+ case FFS_ERR_PART_NOT_FOUND:
+ fprintf(stderr, "libffs flash partition not found\n");
+ break;
+ /* ------- */
+ case FLASH_ERR_MALLOC_FAILED:
+ fprintf(stderr, "libflash malloc failed\n");
+ break;
+ case FLASH_ERR_CHIP_UNKNOWN:
+ fprintf(stderr, "libflash unknown flash chip\n");
+ break;
+ case FLASH_ERR_PARM_ERROR:
+ fprintf(stderr, "libflash parameter error\n");
+ break;
+ case FLASH_ERR_ERASE_BOUNDARY:
+ fprintf(stderr, "libflash erase boundary error\n");
+ break;
+ case FLASH_ERR_WREN_TIMEOUT:
+ fprintf(stderr, "libflash WREN timeout\n");
+ break;
+ case FLASH_ERR_WIP_TIMEOUT:
+ fprintf(stderr, "libflash WIP timeout\n");
+ break;
+ case FLASH_ERR_VERIFY_FAILURE:
+ fprintf(stderr, "libflash verification failure\n");
+ break;
+ case FLASH_ERR_4B_NOT_SUPPORTED:
+ fprintf(stderr, "libflash 4byte mode not supported\n");
+ break;
+ case FLASH_ERR_CTRL_CONFIG_MISMATCH:
+ fprintf(stderr, "libflash control config mismatch\n");
+ break;
+ case FLASH_ERR_CHIP_ER_NOT_SUPPORTED:
+ fprintf(stderr, "libflash chip not supported\n");
+ break;
+ case FLASH_ERR_CTRL_CMD_UNSUPPORTED:
+ fprintf(stderr, "libflash unsupported control command\n");
+ break;
+ case FLASH_ERR_CTRL_TIMEOUT:
+ fprintf(stderr, "libflash control timeout\n");
+ break;
+ case FLASH_ERR_ECC_INVALID:
+ fprintf(stderr, "libflash ecc invalid\n");
+ break;
+ default:
+ fprintf(stderr, "A libflash/libffs error has occurred %d\n", rc);
+ }
+}
+
+const struct chip_unit_desc *chip_units;
+int chip_unit_count;
+
+static void set_chip_gen(const struct chip_unit_desc *c)
+{
+ chip_units = c;
+ chip_unit_count = 0;
+
+ while (strcmp("LAST_IN_RANGE", c->desc)) {
+ chip_unit_count++;
+ c++;
+ }
+}
+
+#ifdef __powerpc64__
+static void guess_chip_gen(void)
+{
+ /*
+ * Guesstimate what chip generation based on the PVR if we're running
+ * on ppc64.
+ */
+ uint32_t pvr;
+
+ /* grab the chip type from the PVR SPR */
+ asm ("mfspr %0,0x11f" : "=r" (pvr));
+
+ switch (pvr >> 16) {
+ case 0x004b: /* murano */
+ case 0x004c: /* naples */
+ case 0x004d: /* venice */
+ set_chip_gen(p8_chip_units);
+ return;
+
+ case 0x004e: /* nimbus */
+ case 0x004f: /* axone */
+ set_chip_gen(p9_chip_units);
+ return;
+
+ case 0x0080: /* power10 */
+ set_chip_gen(p10_chip_units);
+ return;
+
+ default:
+ fprintf(stderr, "Unsupported processor (pvr %#x)! Set the processor generation manually with -8, -9 or -0\n", pvr);
+ exit(1);
+ }
+}
+#else
+static void guess_chip_gen(void)
+{
+#ifdef ASSUME_P8
+ set_chip_gen(p8_chip_units);
+#else
+ set_chip_gen(p9_chip_units);
+#endif
+}
+#endif
+
+static const char *target_type_to_str(int type)
+{
+ int i;
+
+ for (i = 0; i < chip_unit_count; i++)
+ if (chip_units[i].type == type)
+ return chip_units[i].desc;
+
+ return "UNKNOWN";
+}
+
+static int str_to_target_type(const char *path)
+{
+ int i, len;
+
+ for (i = 0; i < chip_unit_count; i++) {
+ len = strlen(chip_units[i].desc);
+
+ if (!strncasecmp(chip_units[i].desc, path, len))
+ return chip_units[i].type; /* match! */
+ }
+
+ return -1;
+}
+
+static const char *deconfig_reason_str(enum gard_reason reason)
+{
+ switch (reason) {
+ case GARD_NO_REASON:
+ return "None";
+ case GARD_MANUAL:
+ return "Manual";
+ case GARD_UNRECOVERABLE:
+ return "Unrecoverable";
+ case GARD_FATAL:
+ return "Fatal";
+ case GARD_PREDICTIVE:
+ return "Predictive";
+ case GARD_POWER:
+ return "Power"; // What does this even mean?
+ case GARD_HYP:
+ return "Hypervisor";
+ case GARD_RECONFIG:
+ return "Reconfig";
+ default:
+ return "Unknown";
+ }
+};
+
+static const char *path_type_to_str(enum path_type t)
+{
+ switch (t) {
+ case PATH_NA:
+ return "not applicable";
+ case PATH_AFFINITY:
+ return "affinity";
+ case PATH_PHYSICAL:
+ return "physical";
+ case PATH_DEVICE:
+ return "device";
+ case PATH_POWER:
+ return "power";
+ }
+ return "Unknown";
+}
+
+/*
+ * NB: buffer is assumped to be MAX_PATH_SIZE
+ */
+static char *format_path(struct entity_path *path, char *buffer)
+{
+ int elements = path->type_size & PATH_ELEMENTS_MASK;
+ int i, offset = 0;
+
+ for (i = 0; i < elements; i++) {
+ const struct path_element *e = &path->path_elements[i];
+
+ offset += sprintf(buffer + offset, "/%s%d",
+ target_type_to_str(e->target_type),
+ e->instance);
+ }
+
+ return buffer;
+}
+
+/*
+ * parses a Path string into the entity_path structured provided.
+ *
+ * str - In param, String to parse
+ * parsed - Out param, resultant entity_path
+ *
+ * e.g.
+ *
+ * "/Sys0/Node0/Proc1" -> {
+ * type_size = 0x23,
+ *
+ * path_element[0] = {0, 0}
+ * path_element[1] = {1, 0}
+ * path_element[2] = {2, 1}
+ * }
+ */
+static int parse_path(const char *str, struct entity_path *parsed)
+{
+ int unit_count = 0;
+
+ memset(parsed, 0, sizeof(*parsed));
+
+ while (*str != '\0') {
+ int unit_id = str_to_target_type(++str); /* ++ skips the '/' */
+ long instance;
+ char *end;
+ size_t len;
+
+ if (unit_count > MAX_PATH_ELEMENTS - 1) {
+ fprintf(stderr, "Path has more than 10 components!\n");
+ return -1;
+ }
+
+ /* find the type Id of this component */
+ if (unit_id < 0) { /* unknown unit, bail out */
+ fprintf(stderr, "Unknown unit at: '%s'\n", str);
+ return -1;
+ }
+
+ parsed->path_elements[unit_count].target_type = unit_id;
+
+ /* now parse the instance # */
+ len = strlen(target_type_to_str(unit_id));
+ instance = strtol(str + len, &end, 10);
+
+ if (!isdigit(*(str + len))) {
+ fprintf(stderr, "Missing instance number after '%s'\n",
+ str);
+ return -1;
+ }
+
+ if (*end != '\0' && *end != '/') {
+ fprintf(stderr, "Unable to parse instance after '%s'\n",
+ str);
+ return -1;
+ }
+
+ if (instance > 255 || instance < 0) {
+ fprintf(stderr,
+ "Instance %ld is invalid. Must be 0 to 255\n",
+ instance);
+ return -1;
+ }
+ parsed->path_elements[unit_count].instance = instance;
+
+ str = end;
+ unit_count++;
+ }
+
+ /*
+ * We assume the path is a physical path because every gard record I've
+ * seen so far uses them. We might need to fix this later on, but lets
+ * cross the bridge when we have to.
+ */
+ parsed->type_size = (unit_count & 0xf) |
+ (PATH_PHYSICAL << PATH_TYPE_SHIFT);
+
+ return 0;
+}
+
+static struct gard_record blank_record;
+
+static bool is_valid_record(struct gard_record *g)
+{
+ return memcmp(&blank_record, g, sizeof(*g));
+}
+
+static int do_iterate(struct gard_ctx *ctx,
+ int (*func)(struct gard_ctx *ctx, int pos,
+ struct gard_record *gard, void *priv),
+ void *priv)
+{
+ int rc = 0;
+ unsigned int i;
+ struct gard_record gard, null_gard;
+
+ memset(&null_gard, UINT_MAX, sizeof(gard));
+ for (i = 0; i * sizeof(gard) < ctx->gard_data_len && rc == 0; i++) {
+ memset(&gard, 0, sizeof(gard));
+
+ rc = blocklevel_read(ctx->bl, ctx->gard_data_pos + (i * sizeof(gard)),
+ &gard, sizeof(gard));
+ /* It isn't super clear what constitutes the end, this should do */
+ if (rc || memcmp(&gard, &null_gard, sizeof(gard)) == 0)
+ break;
+
+ rc = func(ctx, i, &gard, priv);
+ }
+
+ return rc;
+}
+
+/*
+ * read the next guard record into the supplied buffer (gard)
+ *
+ * returns the record id (nb: 1 based not zero)
+ *
+ */
+static int __gard_next(struct gard_ctx *ctx, int pos, struct gard_record *gard, int *rc)
+{
+ uint32_t offset = pos * sizeof(*gard);
+
+ if (offset > ctx->gard_data_len) /* too big */
+ return -1;
+
+ /* you lose error handling information, *gruble* */
+ memset(gard, 0, sizeof(*gard));
+ *rc = blocklevel_read(ctx->bl, ctx->gard_data_pos + offset,
+ gard, sizeof(*gard));
+
+ if (!is_valid_record(gard))
+ return -1;
+
+ if (*rc)
+ return -1;
+
+ return pos;
+}
+
+#define for_each_gard(ctx, pos, gard, rc) \
+ for (pos = __gard_next(ctx, 0, gard, rc); \
+ pos >= 0; pos = __gard_next(ctx, ++pos, gard, rc))
+
+static int count_records(struct gard_ctx *ctx)
+{
+ struct gard_record record;
+ int rc, pos, count = 0;
+
+ for_each_gard(ctx, pos, &record, &rc)
+ count++;
+
+ return rc ? rc : count;
+}
+
+static int count_valid_records(struct gard_ctx *ctx)
+{
+ struct gard_record record;
+ int rc, pos, count = 0;
+
+ for_each_gard(ctx, pos, &record, &rc)
+ count++;
+
+ return rc ? rc : count;
+}
+
+static size_t find_longest_path(struct gard_ctx *ctx)
+{
+ char scratch[MAX_PATH_SIZE];
+ struct gard_record gard;
+ size_t len, longest = 0;
+ int rc, pos;
+
+ for_each_gard(ctx, pos, &gard, &rc) {
+ len = strlen(format_path(&gard.target_id, scratch));
+ if (len > longest)
+ longest = len;
+ }
+
+ return longest;
+}
+
+static void draw_ruler(char c, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ putchar(c);
+ putchar('\n');
+}
+
+static int do_list(struct gard_ctx *ctx, int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ /* This header matches the line formatting above in do_list_i() */
+ const char *header = " ID | Error | Type | Path";
+ size_t ruler_size;
+ char scratch[MAX_PATH_SIZE];
+ struct gard_record gard;
+ int rc = 0, pos;
+
+ /* No entries */
+ if (count_valid_records(ctx) == 0) {
+ printf("No GARD entries to display\n");
+ return 0;
+ }
+
+ puts(header);
+
+ ruler_size = strlen(header) + find_longest_path(ctx);
+ draw_ruler('-', ruler_size);
+
+ for_each_gard(ctx, pos, &gard, &rc) {
+ printf(" %08x | %08x | %-10s | %s%s\n",
+ be32toh(gard.record_id),
+ be32toh(gard.errlog_eid),
+ deconfig_reason_str(gard.error_type),
+ format_path(&gard.target_id, scratch),
+ gard.record_id == 0xffffffff ? " *CLEARED*" : "");
+ }
+
+ draw_ruler('=', ruler_size);
+
+ return rc;
+}
+
+static int do_show_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
+{
+ uint32_t id;
+
+ (void)ctx;
+ (void)pos;
+
+ if (!priv || !gard)
+ return -1;
+
+ id = *(uint32_t *)priv;
+
+ if (be32toh(gard->record_id) == id) {
+ unsigned int count, i;
+
+ printf("Record ID: 0x%08x%s\n", id, id == 0xffffffff ? " *CLEARED*" : "");
+ printf("========================\n");
+ printf("Error ID: 0x%08x\n", be32toh(gard->errlog_eid));
+ printf("Error Type: %s (0x%02x)\n",
+ deconfig_reason_str(gard->error_type),
+ gard->error_type);
+ printf("Path Type: %s\n", path_type_to_str(gard->target_id.type_size >> PATH_TYPE_SHIFT));
+ count = gard->target_id.type_size & PATH_ELEMENTS_MASK;
+ for (i = 0; i < count && i < MAX_PATH_ELEMENTS; i++)
+ printf("%*c%s, Instance #%d\n", i + 1, '>', target_type_to_str(gard->target_id.path_elements[i].target_type),
+ gard->target_id.path_elements[i].instance);
+ }
+
+ return 0;
+}
+
+static int do_show(struct gard_ctx *ctx, int argc, char **argv)
+{
+ uint32_t id;
+ int rc;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s option requires a GARD record\n", argv[0]);
+ return -1;
+ }
+
+ id = strtoul(argv[1], NULL, 16);
+
+ rc = do_iterate(ctx, &do_show_i, &id);
+
+ return rc;
+}
+
+static int do_clear_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
+{
+ int largest, rc = 0;
+ char *buf;
+ struct gard_record null_gard;
+
+ if (!gard || !ctx || !priv)
+ return -1;
+
+ /* Not this one */
+ if (be32toh(gard->record_id) != *(uint32_t *)priv)
+ return 0;
+
+ memset(&null_gard, 0xFF, sizeof(null_gard));
+
+ largest = count_records(ctx);
+
+ printf("Clearing gard record 0x%08x...", be32toh(gard->record_id));
+
+ if (largest < 0 || pos > largest) {
+ /* Something went horribly wrong */
+ fprintf(stderr, "largest index out of range %d\n", largest);
+ return -1;
+ }
+
+ if (pos < largest) {
+ /* We're not clearing the last record, shift all the records up */
+ int buf_len = ((largest - pos) * sizeof(struct gard_record));
+ int buf_pos = ctx->gard_data_pos + ((pos + 1) * sizeof(struct gard_record));
+ buf = malloc(buf_len);
+ if (!buf)
+ return -ENOMEM;
+
+ rc = blocklevel_read(ctx->bl, buf_pos, buf, buf_len);
+ if (rc) {
+ free(buf);
+ fprintf(stderr, "Couldn't read from flash at 0x%08x for len 0x%08x\n", buf_pos, buf_len);
+ return rc;
+ }
+
+ rc = blocklevel_smart_write(ctx->bl, buf_pos - sizeof(*gard), buf, buf_len);
+ free(buf);
+ if (rc) {
+ fprintf(stderr, "Couldn't write to flash at 0x%08x for len 0x%08x\n",
+ buf_pos - (int) sizeof(struct gard_record), buf_len);
+ return rc;
+ }
+ }
+
+ /* Now wipe the last record */
+ rc = blocklevel_smart_write(ctx->bl, ctx->gard_data_pos + (largest * sizeof(null_gard)),
+ &null_gard, sizeof(null_gard));
+ printf("done\n");
+
+ return rc;
+}
+
+static int reset_partition(struct gard_ctx *ctx)
+{
+ int no_ecc_len = (ctx->gard_data_len / 9) * 8;
+ struct gard_record *gard;
+ int rc = 0;
+
+ gard = malloc(ctx->gard_data_len);
+ if (!gard) {
+ return FLASH_ERR_MALLOC_FAILED;
+ }
+ memset(gard, 0xFF, ctx->gard_data_len);
+
+ rc = blocklevel_smart_erase(ctx->bl, ctx->gard_data_pos, ctx->gard_data_len);
+ if (rc) {
+ fprintf(stderr, "Couldn't erase the gard partition. Bailing out\n");
+ goto out;
+ }
+
+ rc = blocklevel_write(ctx->bl, ctx->gard_data_pos, gard, no_ecc_len);
+ if (rc)
+ fprintf(stderr, "Couldn't reset the entire gard partition. Bailing out\n");
+
+out:
+ free(gard);
+ return rc;
+}
+
+static int do_clear(struct gard_ctx *ctx, int argc, char **argv)
+{
+ int rc;
+ uint32_t id;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s option requires a GARD record or 'all'\n", argv[0]);
+ return -1;
+ }
+
+ if (strncmp(argv[1], "all", strlen("all")) == 0) {
+ printf("Clearing the entire gard partition...");
+ fflush(stdout);
+ rc = reset_partition(ctx);
+ printf("done\n");
+ } else {
+ id = strtoul(argv[1], NULL, 16);
+ rc = do_iterate(ctx, do_clear_i, &id);
+ }
+
+ return rc;
+}
+
+static int do_create(struct gard_ctx *ctx, int argc, char **argv)
+{
+ int rc, pos, max_id = 0, last_pos = 0;
+ struct gard_record gard;
+ struct entity_path path;
+
+ if (argc < 2) {
+ fprintf(stderr, "create requires path to gard\n");
+ fprintf(stderr, "e.g.\n");
+ fprintf(stderr, " /Sys0/Node0/Proc0\n");
+ fprintf(stderr, " /Sys0/Node0/DIMM15\n");
+ return -1;
+ }
+
+ if (parse_path(argv[1], &path)) {
+ fprintf(stderr, "Unable to parse path\n");
+ return -1;
+ }
+
+ /* check if we already have a gard record applied to this path */
+ for_each_gard(ctx, pos, &gard, &rc) {
+ if (!memcmp(&path, &gard.target_id, sizeof(path))) {
+ fprintf(stderr,
+ "Unit %s is already GARDed by record %#08x\n",
+ argv[1], be32toh(gard.record_id));
+ return -1;
+ }
+
+ /*
+ * Keep track of the largest record ID seen so far,
+ * we'll give the new record the max + 1 to ensure
+ * that it's unique
+ */
+ if (be32toh(gard.record_id) > max_id)
+ max_id = be32toh(gard.record_id);
+
+ last_pos++;
+ }
+
+ /* do we have an empty record to write into? */
+ if (!rc && !is_valid_record(&gard)) {
+ int offset = last_pos * sizeof(gard);
+
+ memset(&gard, 0xff, sizeof(gard));
+
+ gard.record_id = be32toh(max_id + 1);
+ gard.error_type = GARD_MANUAL;
+ gard.target_id = path;
+ gard.errlog_eid = 0x0;
+
+ if (offset > ctx->gard_data_len - sizeof(gard)) {
+ fprintf(stderr, "No space in GUARD for a new record\n");
+ return -1;
+ }
+
+ rc = blocklevel_smart_write(ctx->bl,
+ ctx->gard_data_pos + offset, &gard, sizeof(gard));
+ }
+
+ return rc;
+}
+
+static int check_gard_partition(struct gard_ctx *ctx)
+{
+ int rc;
+ struct gard_record gard;
+ char msg[2];
+
+ if (ctx->gard_data_len == 0 || ctx->gard_data_len % sizeof(struct gard_record) != 0)
+ /* Just warn for now */
+ fprintf(stderr, "The %s partition doesn't appear to be an exact multiple of"
+ "gard records in size: %zd vs %u (or partition is zero in length)\n",
+ FLASH_GARD_PART, sizeof(struct gard_record), ctx->gard_data_len);
+
+ /*
+ * Attempt to read the first record, nothing can really operate if the
+ * first record is dead. There (currently) isn't a way to validate more
+ * than ECC correctness.
+ */
+ rc = blocklevel_read(ctx->bl, ctx->gard_data_pos, &gard, sizeof(gard));
+ if (rc == FLASH_ERR_ECC_INVALID) {
+ fprintf(stderr, "The data at the GUARD partition does not appear to be valid gard data\n");
+ fprintf(stderr, "Clear the entire GUARD partition? [y/N]\n");
+ if (fgets(msg, sizeof(msg), stdin) == NULL) {
+ fprintf(stderr, "Couldn't read from standard input\n");
+ return -1;
+ }
+ if (msg[0] == 'y') {
+ rc = reset_partition(ctx);
+ if (rc) {
+ fprintf(stderr, "Couldn't reset the GUARD partition. Bailing out\n");
+ return rc;
+ }
+ }
+ /*
+ * else leave rc as is so that the main bails out, not going to be
+ * able to do sensible anyway
+ */
+ }
+ return rc;
+}
+
+__attribute__ ((unused))
+static int do_nop(struct gard_ctx *ctx, int argc, char **argv)
+{
+ (void)ctx;
+ (void)argc;
+ fprintf(stderr, "Unimplemented action '%s'\n", argv[0]);
+ return EXIT_SUCCESS;
+}
+
+struct {
+ const char *name;
+ const char *desc;
+ int (*fn)(struct gard_ctx *, int, char **);
+} actions[] = {
+ { "list", "List current GARD records", do_list },
+ { "show", "Show details of a GARD record", do_show },
+ { "clear", "Clear GARD records", do_clear },
+ { "create", "Create a GARD record", do_create },
+};
+
+static void print_version(void)
+{
+ printf("Open-Power GARD tool %s\n", version);
+}
+
+static void usage(const char *progname)
+{
+ unsigned int i;
+
+ print_version();
+ fprintf(stderr, "Usage: %s [-a -e -f <file> -p] <command> [<args>]\n\n",
+ progname);
+ fprintf(stderr, "-8 --p8\n");
+ fprintf(stderr, "-9 --p9\n");
+ fprintf(stderr, "-0 --p10\n\tSet the processor generation\n\n");
+ fprintf(stderr, "-e --ecc\n\tForce reading/writing with ECC bytes.\n\n");
+ fprintf(stderr, "-f --file <file>\n\tDon't search for MTD device,"
+ " read from <file>.\n\n");
+ fprintf(stderr, "-p --part\n\tUsed in conjunction with -f to specify"
+ " that just\n");
+ fprintf(stderr, "\tthe GUARD partition is in <file> and libffs\n");
+ fprintf(stderr, "\tshouldn't be used.\n\n");
+
+
+ fprintf(stderr, "Where <command> is one of:\n\n");
+
+ for (i = 0; i < ARRAY_SIZE(actions); i++) {
+ fprintf(stderr, "\t%-7s\t%s\n",
+ actions[i].name, actions[i].desc);
+ }
+}
+
+static bool is_fsp(void)
+{
+ return access(FDT_FSP_NODE, F_OK) == 0;
+}
+
+static struct option global_options[] = {
+ { "file", required_argument, 0, 'f' },
+ { "part", no_argument, 0, 'p' },
+ { "ecc", no_argument, 0, 'e' },
+ { "p8", no_argument, 0, '8' },
+ { "p9", no_argument, 0, '9' },
+ { "p10", no_argument, 0, '0' },
+ { 0 },
+};
+static const char *global_optstring = "+ef:p890";
+
+int main(int argc, char **argv)
+{
+ const char *action, *progname;
+ char *filename = NULL;
+ struct gard_ctx _ctx, *ctx;
+ uint64_t bl_size;
+ int rc, i = 0;
+ bool part = 0;
+ bool ecc = 0;
+
+ progname = argv[0];
+
+ ctx = &_ctx;
+ memset(ctx, 0, sizeof(*ctx));
+ memset(&blank_record, 0xff, sizeof(blank_record));
+
+ /* process global options */
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, global_optstring, global_options,
+ NULL);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'e':
+ ecc = true;
+ break;
+ case 'f':
+ /* If they specify -f twice */
+ free(filename);
+
+ filename = strdup(optarg);
+ if (!filename) {
+ fprintf(stderr, "Out of memory\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'p':
+ part = true;
+ break;
+ case '8':
+ set_chip_gen(p8_chip_units);
+ break;
+ case '9':
+ set_chip_gen(p9_chip_units);
+ break;
+ case '0':
+ set_chip_gen(p10_chip_units);
+ break;
+ case '?':
+ usage(progname);
+ rc = EXIT_FAILURE;
+ goto out_free;
+ }
+ }
+
+
+ if (is_fsp() && !filename) {
+ fprintf(stderr, "This is the OpenPower gard tool which does "
+ "not support FSP systems\n");
+ return EXIT_FAILURE;
+ }
+
+
+ /*
+ * It doesn't make sense to specify that we have the gard partition but
+ * read from flash
+ */
+ if (part && !filename) {
+ usage(progname);
+ fprintf(stderr, "-p only makes sense when used with -f!\n");
+ return EXIT_FAILURE;
+ }
+
+ /* do we have a command? */
+ if (optind == argc) {
+ usage(progname);
+ rc = EXIT_FAILURE;
+ goto out_free;
+ }
+
+ argc -= optind;
+ argv += optind;
+ action = argv[0];
+
+#ifdef __arm__
+ /*
+ * HACK: Look for a vPNOR GUARD file if we haven't been given anything
+ * explitly. If it exists then we can safely assume that:
+ * a) The host is a P9
+ * b) The file is ECC protected
+ * c) The file is a bare partition.
+ *
+ * This is a stupid hack, but there's not other sane place for it.
+ * arch_init_flash() always looks for a FFS formatted PNOR when
+ * filename is NULL
+ */
+ if (!filename) {
+ struct stat buf;
+
+ if (!stat(VPNOR_GARD_FILE, &buf)) {
+ filename = strdup(VPNOR_GARD_FILE);
+ /* BUG: This ignores the command line settings */
+ part = true;
+ ecc = true;
+ } else if (!stat(VPNOR_GARD_DIR, &buf)) {
+ printf(VPNOR_GARD_FILE" is missing. Nothing to do\n");
+ return 0;
+ }
+ }
+#endif
+
+ if (!chip_units)
+ guess_chip_gen();
+
+ /*
+ * Force libflash to do flash accesses via the MTD. Direct mode is
+ * generally unsafe since it fiddles with the flash controller state
+ * underneath the kernel. Anyone who needs direct mode can use pflash
+ * instead.
+ */
+ arch_flash_access(ctx->bl, PNOR_MTD);
+
+ if (arch_flash_init(&(ctx->bl), filename, true)) {
+ /* Can fail for a few ways, most likely couldn't open MTD device */
+ fprintf(stderr, "Can't open %s\n", filename ? filename : "MTD Device. Are you root?");
+ rc = EXIT_FAILURE;
+ goto out_free;
+ }
+
+ rc = blocklevel_get_info(ctx->bl, NULL, &bl_size, NULL);
+ if (rc)
+ goto out;
+
+ if (bl_size > UINT_MAX) {
+ fprintf(stderr, "MTD device bigger than %i: size: %" PRIu64 "\n",
+ UINT_MAX, bl_size);
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ ctx->f_size = bl_size;
+
+ if (!part) {
+ rc = ffs_init(0, ctx->f_size, ctx->bl, &ctx->ffs, 1);
+ if (rc)
+ goto out;
+
+ rc = ffs_lookup_part(ctx->ffs, FLASH_GARD_PART, &ctx->gard_part_idx);
+ if (rc)
+ goto out;
+
+ rc = ffs_part_info(ctx->ffs, ctx->gard_part_idx, NULL, &(ctx->gard_data_pos),
+ &(ctx->gard_data_len), NULL, NULL);
+ if (rc)
+ goto out;
+ } else {
+ if (ecc) {
+ rc = blocklevel_ecc_protect(ctx->bl, 0, ctx->f_size);
+ if (rc)
+ goto out;
+ }
+
+ ctx->gard_data_pos = 0;
+ ctx->gard_data_len = ctx->f_size;
+ }
+
+ rc = check_gard_partition(ctx);
+ if (rc) {
+ fprintf(stderr, "Does not appear to be sane gard data\n");
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(actions); i++) {
+ if (!strcmp(actions[i].name, action)) {
+ rc = actions[i].fn(ctx, argc, argv);
+ break;
+ }
+ }
+
+out:
+ if (ctx->ffs)
+ ffs_close(ctx->ffs);
+
+ file_exit_close(ctx->bl);
+
+ if (i == ARRAY_SIZE(actions)) {
+ fprintf(stderr, "%s: '%s' isn't a valid command\n", progname, action);
+ usage(progname);
+ rc = EXIT_FAILURE;
+ goto out_free;
+ }
+
+ if (rc > 0) {
+ show_flash_err(rc);
+ if (filename && rc == FFS_ERR_BAD_MAGIC)
+ fprintf(stderr, "Maybe you didn't give a full flash image file?\nDid you mean '--part'?\n");
+ }
+
+out_free:
+ free(filename);
+ return rc;
+}
diff --git a/roms/skiboot/external/gard/gard.h b/roms/skiboot/external/gard/gard.h
new file mode 100644
index 000000000..d59c2a0de
--- /dev/null
+++ b/roms/skiboot/external/gard/gard.h
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2017 IBM Corp.
+ */
+
+#include <stdint.h>
+
+#define MAX_PATH_ELEMENTS 10
+#define PATH_TYPE_SHIFT 4
+#define PATH_ELEMENTS_MASK (0x0F)
+
+/*
+ * Sourced from hostboot: src/include/usr/hwas/common/hwasCallout.H
+ */
+enum gard_reason {
+ GARD_NO_REASON = 0x0,
+ GARD_MANUAL = 0xD2,
+ GARD_UNRECOVERABLE = 0xE2,
+ GARD_FATAL = 0xE3,
+ GARD_PREDICTIVE = 0xE6,
+ GARD_POWER = 0xE9,
+ GARD_HYP = 0xEA,
+ GARD_RECONFIG = 0xEB,
+
+ /*
+ * This should only occur if the GUARD partition isn't correctly
+ * programmed with ECC bits.
+ */
+ GARD_VOID = 0xFF,
+};
+
+/* see src/include/usr/targeting/common/entitypath.H */
+enum path_type {
+ PATH_NA = 0x00,
+ PATH_AFFINITY = 0x01,
+ PATH_PHYSICAL = 0x02,
+ PATH_DEVICE = 0x03,
+ PATH_POWER = 0x04,
+};
+
+struct path_element {
+ uint8_t target_type;
+ uint8_t instance;
+} __attribute__((packed));
+
+struct entity_path {
+ /* First 4 bits are a path_type enum */
+ /* Second 4 bits are the amount of path_elements */
+ uint8_t type_size;
+ struct path_element path_elements[MAX_PATH_ELEMENTS];
+
+} __attribute__((packed));
+
+/* From hostboot: src/include/usr/hwas/common/deconfigGard.H:GardRecord */
+struct gard_record {
+ uint32_t record_id;
+ struct entity_path target_id;
+ uint8_t pad0[3]; /* compiler dependent padding */
+ uint32_t errlog_eid;
+ uint8_t error_type;
+ uint8_t resource_recovery;
+ uint8_t pad1[6];
+} __attribute__((packed));
+
+#define MAX_PATH_SIZE 420
+
+struct chip_unit_desc {
+ int type;
+ const char *desc;
+};
+
+extern const struct chip_unit_desc *chip_units;
+extern const struct chip_unit_desc p8_chip_units[];
+extern const struct chip_unit_desc p9_chip_units[];
+extern const struct chip_unit_desc p10_chip_units[];
diff --git a/roms/skiboot/external/gard/opal-gard.1 b/roms/skiboot/external/gard/opal-gard.1
new file mode 100644
index 000000000..65b214470
--- /dev/null
+++ b/roms/skiboot/external/gard/opal-gard.1
@@ -0,0 +1,30 @@
+.TH opal-gard 1 "19 June 2015"
+.SH NAME
+opal-gard \- GUARD Partition manipulation tool for OpenPower hardware
+.SH SYNOPSIS
+\fBopal-gard\fP [ \-e | \-f \fIfile\fP | \-p ]
+\fIcommand\fP
+.SH DESCRIPTION
+\fBopal-gard\fP allows reading of the GUARD partition on OpenPower hardware though the exposed mtd flash interface. The actual device (usually \fB/dev/mtd0\fR) is automatically detected.
+.SS Options
+\fB\-e, \-\-ecc\fP
+Force reading/writing with ECC bytes.
+.TP
+\fB\-f, \-\-file\fP \fIfile\fR
+Don't search for the \fB/dev/mtd\fR device, use \fIfile\fP instead.
+.TP
+.TP
+\fB\-p, \-\-part\fP
+Used in conjunction with \-f to specify that just the GUARD partition is in the \fIfile\fR and that libffs shouldn't be used.
+.SS Commands
+\fIcommand\fP
+may be one of the following
+.TP
+\fBlist\fP
+List current GARD records
+.TP
+\fBshow\fP
+Show details of a GARD record
+.TP
+\fBclear\fP
+Clear GARD records
diff --git a/roms/skiboot/external/gard/rules.mk b/roms/skiboot/external/gard/rules.mk
new file mode 100644
index 000000000..92ceb9bc0
--- /dev/null
+++ b/roms/skiboot/external/gard/rules.mk
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+.DEFAULT_GOAL := all
+
+override CFLAGS += -O2 -Wall -Werror -Wno-stringop-truncation -I.
+OBJS = version.o gard.o units.o
+LIBFLASH_FILES := libflash.c libffs.c ecc.c blocklevel.c file.c
+LIBFLASH_OBJS := $(addprefix libflash-, $(LIBFLASH_FILES:.c=.o))
+LIBFLASH_SRC := $(addprefix libflash/,$(LIBFLASH_FILES))
+CCAN_FILES := list.c
+CCAN_OBJS := $(addprefix ccan-list-, $(CCAN_FILES:.c=.o))
+CCAN_SRC := $(addprefix ccan/list/,$(CCAN_FILES))
+OBJS += $(LIBFLASH_OBJS) $(CCAN_OBJS)
+OBJS += common-arch_flash.o
+EXE = opal-gard
+
+prefix = /usr/local/
+sbindir = $(prefix)/sbin
+datadir = $(prefix)/share
+mandir = $(datadir)/man
+
+#This will only be unset if we're running out of git tree,
+#../../make_version.sh is garanteed to exist that way
+GARD_VERSION ?= $(shell ../../make_version.sh $(EXE))
+
+version.c: .version
+ @(if [ "a$(GARD_VERSION)" = "a" ]; then \
+ echo "#error You need to set GARD_VERSION environment variable" > $@ ;\
+ else \
+ echo "const char version[] = \"$(GARD_VERSION)\";" ;\
+ fi) > $@
+
+%.o : %.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+$(LIBFLASH_SRC): | links
+$(CCAN_SRC): | links
+
+$(LIBFLASH_OBJS): libflash-%.o : libflash/%.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+$(CCAN_OBJS): ccan-list-%.o: ccan/list/%.c
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(EXE): $(OBJS)
+ $(CC) $(LDFLAGS) $(CFLAGS) $^ -o $@
+
+install: all
+ install -D $(EXE) $(DESTDIR)$(sbindir)/$(EXE)
+ install -D -m 0644 $(EXE).1 $(DESTDIR)$(mandir)/man1/$(EXE).1
+
+
diff --git a/roms/skiboot/external/gard/test/add_test.sh b/roms/skiboot/external/gard/test/add_test.sh
new file mode 100755
index 000000000..6523dffc6
--- /dev/null
+++ b/roms/skiboot/external/gard/test/add_test.sh
@@ -0,0 +1,67 @@
+#!/bin/bash -uex
+#
+# this is a really dumb script for auto-generating test cases from known good-data
+#
+# usage: ./add_test <pass|fail> <inputfile> <testname> [opal-gard subcommand]
+#
+# e.g.
+# ./add_test.sh fail blank.bin create-bad-instance create /sys256
+# ./add_test.sh pass blank.bin create-normal create /sys0/node0/proc0
+#
+# this will generate a test script file and writes the stdout/stderr of the command
+# to the respective files.
+#
+
+cd $(dirname $(realpath $0))/../
+echo $PWD
+
+if [ "$1" = "pass" ]; then
+ check='if [ "$?" -ne 0 ]; then'
+ test_type="pass"
+else
+ check='if [ "$?" -eq 0 ]; then'
+ test_type="fails"
+fi
+shift
+
+file="test/files/$1"
+if [ ! -f "$file" ]; then
+ echo "test file not found!"
+ exit 1;
+fi
+shift
+
+name="$1"
+shift
+
+max="$(ls test/tests/ -1|sort -n | sed 's@\(..\).*@\1@' | tail -1 | sed s@^0*@@)"
+num="$(printf %02d $((max + 1)))"
+
+echo "Adding: $num-$name"
+
+# where we will write the script file
+script_file="test/tests/$num-$name"
+
+echo "making $num-$name: f=$script_file, normally $test_type, cmd='$*'"
+
+cat > $script_file <<EOF
+#! /bin/sh
+
+run_binary "./opal-gard" "-9 -p -e -f $file $*"
+$check
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
+EOF
+
+# generate the .out and .err files
+stdout_file="test/results/$num-$name.out"
+stderr_file="test/results/$num-$name.err"
+
+test_input="$name-$num-input"
+cp $file $test_input
+./opal-gard -f $test_input -p -e $* 2>$stderr_file >$stdout_file
+rm -f $test_input
diff --git a/roms/skiboot/external/gard/test/files/blank.bin b/roms/skiboot/external/gard/test/files/blank.bin
new file mode 100644
index 000000000..c6cc6b8c2
--- /dev/null
+++ b/roms/skiboot/external/gard/test/files/blank.bin
Binary files differ
diff --git a/roms/skiboot/external/gard/test/files/data-p9.bin b/roms/skiboot/external/gard/test/files/data-p9.bin
new file mode 100644
index 000000000..f2141657a
--- /dev/null
+++ b/roms/skiboot/external/gard/test/files/data-p9.bin
Binary files differ
diff --git a/roms/skiboot/external/gard/test/files/data1.bin b/roms/skiboot/external/gard/test/files/data1.bin
new file mode 100644
index 000000000..aa5bf1437
--- /dev/null
+++ b/roms/skiboot/external/gard/test/files/data1.bin
Binary files differ
diff --git a/roms/skiboot/external/gard/test/make-check-test b/roms/skiboot/external/gard/test/make-check-test
new file mode 100755
index 000000000..6b9e5db88
--- /dev/null
+++ b/roms/skiboot/external/gard/test/make-check-test
@@ -0,0 +1 @@
+make -C external/gard/ check
diff --git a/roms/skiboot/external/gard/test/results/00-list.err b/roms/skiboot/external/gard/test/results/00-list.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/00-list.err
diff --git a/roms/skiboot/external/gard/test/results/00-list.out b/roms/skiboot/external/gard/test/results/00-list.out
new file mode 100644
index 000000000..0e20b4aef
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/00-list.out
@@ -0,0 +1,5 @@
+ ID | Error | Type | Path
+-----------------------------------------------------------
+ 00000001 | 90000015 | Predictive | /Sys0/Node0/Proc0
+ 00000002 | 90000016 | Predictive | /Sys0/Node0/Membuf0
+===========================================================
diff --git a/roms/skiboot/external/gard/test/results/01-show_1.err b/roms/skiboot/external/gard/test/results/01-show_1.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/01-show_1.err
diff --git a/roms/skiboot/external/gard/test/results/01-show_1.out b/roms/skiboot/external/gard/test/results/01-show_1.out
new file mode 100644
index 000000000..60fcea264
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/01-show_1.out
@@ -0,0 +1,8 @@
+Record ID: 0x00000001
+========================
+Error ID: 0x90000015
+Error Type: Predictive (0xe6)
+Path Type: physical
+>Sys, Instance #0
+ >Node, Instance #0
+ >Proc, Instance #0
diff --git a/roms/skiboot/external/gard/test/results/02-usage.err b/roms/skiboot/external/gard/test/results/02-usage.err
new file mode 100644
index 000000000..453fcf52f
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/02-usage.err
@@ -0,0 +1,24 @@
+Usage: ./opal-gard [-a -e -f <file> -p] <command> [<args>]
+
+-8 --p8
+-9 --p9
+-0 --p10
+ Set the processor generation
+
+-e --ecc
+ Force reading/writing with ECC bytes.
+
+-f --file <file>
+ Don't search for MTD device, read from <file>.
+
+-p --part
+ Used in conjunction with -f to specify that just
+ the GUARD partition is in <file> and libffs
+ shouldn't be used.
+
+Where <command> is one of:
+
+ list List current GARD records
+ show Show details of a GARD record
+ clear Clear GARD records
+ create Create a GARD record
diff --git a/roms/skiboot/external/gard/test/results/02-usage.out b/roms/skiboot/external/gard/test/results/02-usage.out
new file mode 100644
index 000000000..c862167b4
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/02-usage.out
@@ -0,0 +1 @@
+Open-Power GARD tool VERSION
diff --git a/roms/skiboot/external/gard/test/results/03-show_1-p9.err b/roms/skiboot/external/gard/test/results/03-show_1-p9.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/03-show_1-p9.err
diff --git a/roms/skiboot/external/gard/test/results/03-show_1-p9.out b/roms/skiboot/external/gard/test/results/03-show_1-p9.out
new file mode 100644
index 000000000..c9ae5b21c
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/03-show_1-p9.out
@@ -0,0 +1,10 @@
+Record ID: 0x00000001
+========================
+Error ID: 0x90000007
+Error Type: Fatal (0xe3)
+Path Type: physical
+>Sys, Instance #0
+ >Node, Instance #0
+ >Proc, Instance #0
+ >EQ, Instance #1
+ >EX, Instance #0
diff --git a/roms/skiboot/external/gard/test/results/04-create-bad-instance.err b/roms/skiboot/external/gard/test/results/04-create-bad-instance.err
new file mode 100644
index 000000000..2a8da501a
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/04-create-bad-instance.err
@@ -0,0 +1,2 @@
+Instance 256 is invalid. Must be 0 to 255
+Unable to parse path
diff --git a/roms/skiboot/external/gard/test/results/04-create-bad-instance.out b/roms/skiboot/external/gard/test/results/04-create-bad-instance.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/04-create-bad-instance.out
diff --git a/roms/skiboot/external/gard/test/results/05-create-bad-unit.err b/roms/skiboot/external/gard/test/results/05-create-bad-unit.err
new file mode 100644
index 000000000..aa1af82f5
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/05-create-bad-unit.err
@@ -0,0 +1,2 @@
+Unknown unit at: 'doesnt_exist0'
+Unable to parse path
diff --git a/roms/skiboot/external/gard/test/results/05-create-bad-unit.out b/roms/skiboot/external/gard/test/results/05-create-bad-unit.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/05-create-bad-unit.out
diff --git a/roms/skiboot/external/gard/test/results/06-create-long-path.err b/roms/skiboot/external/gard/test/results/06-create-long-path.err
new file mode 100644
index 000000000..f79a3bda9
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/06-create-long-path.err
@@ -0,0 +1,2 @@
+Path has more than 10 components!
+Unable to parse path
diff --git a/roms/skiboot/external/gard/test/results/06-create-long-path.out b/roms/skiboot/external/gard/test/results/06-create-long-path.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/06-create-long-path.out
diff --git a/roms/skiboot/external/gard/test/results/07-create-slash.err b/roms/skiboot/external/gard/test/results/07-create-slash.err
new file mode 100644
index 000000000..6fd687604
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/07-create-slash.err
@@ -0,0 +1,2 @@
+Unknown unit at: ''
+Unable to parse path
diff --git a/roms/skiboot/external/gard/test/results/07-create-slash.out b/roms/skiboot/external/gard/test/results/07-create-slash.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/07-create-slash.out
diff --git a/roms/skiboot/external/gard/test/results/08-create-duplicate.err b/roms/skiboot/external/gard/test/results/08-create-duplicate.err
new file mode 100644
index 000000000..4c4073953
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/08-create-duplicate.err
@@ -0,0 +1 @@
+Unit /Sys0/Node0/Membuf0 is already GARDed by record 0x000002
diff --git a/roms/skiboot/external/gard/test/results/08-create-duplicate.out b/roms/skiboot/external/gard/test/results/08-create-duplicate.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/08-create-duplicate.out
diff --git a/roms/skiboot/external/gard/test/results/09-create-last-unit.err b/roms/skiboot/external/gard/test/results/09-create-last-unit.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/09-create-last-unit.err
diff --git a/roms/skiboot/external/gard/test/results/09-create-last-unit.out b/roms/skiboot/external/gard/test/results/09-create-last-unit.out
new file mode 100644
index 000000000..62cfcbbd0
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/09-create-last-unit.out
@@ -0,0 +1,4 @@
+ ID | Error | Type | Path
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 00000001 | 00000000 | Manual | /MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255
+================================================================================================================================================================
diff --git a/roms/skiboot/external/gard/test/results/10-clear-single.err b/roms/skiboot/external/gard/test/results/10-clear-single.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/10-clear-single.err
diff --git a/roms/skiboot/external/gard/test/results/10-clear-single.out b/roms/skiboot/external/gard/test/results/10-clear-single.out
new file mode 100644
index 000000000..904e61a56
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/10-clear-single.out
@@ -0,0 +1,7 @@
+Clearing the entire gard partition...done
+ ID | Error | Type | Path
+---------------------------------------------------------
+ 00000001 | 00000000 | Manual | /Sys0/Node0/Proc1
+=========================================================
+Clearing gard record 0x00000001...done
+No GARD entries to display
diff --git a/roms/skiboot/external/gard/test/results/11-clear-first.err b/roms/skiboot/external/gard/test/results/11-clear-first.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/11-clear-first.err
diff --git a/roms/skiboot/external/gard/test/results/11-clear-first.out b/roms/skiboot/external/gard/test/results/11-clear-first.out
new file mode 100644
index 000000000..3b67689ce
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/11-clear-first.out
@@ -0,0 +1,13 @@
+Clearing the entire gard partition...done
+ ID | Error | Type | Path
+---------------------------------------------------------
+ 00000001 | 00000000 | Manual | /Sys0/Node0/Proc0
+ 00000002 | 00000000 | Manual | /Sys0/Node0/Proc1
+=========================================================
+Clearing gard record 0x00000001...done
+ ID | Error | Type | Path
+---------------------------------------------------------
+ 00000002 | 00000000 | Manual | /Sys0/Node0/Proc1
+=========================================================
+Clearing gard record 0x00000002...done
+No GARD entries to display
diff --git a/roms/skiboot/external/gard/test/test-gard b/roms/skiboot/external/gard/test/test-gard
new file mode 100755
index 000000000..10da35156
--- /dev/null
+++ b/roms/skiboot/external/gard/test/test-gard
@@ -0,0 +1,5 @@
+#! /bin/sh
+
+. test/test.sh
+
+run_tests "test/tests/*" "test/results"
diff --git a/roms/skiboot/external/gard/test/tests/00-list b/roms/skiboot/external/gard/test/tests/00-list
new file mode 100644
index 000000000..982acb3d3
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/00-list
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/data1.bin list"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/01-show_1 b/roms/skiboot/external/gard/test/tests/01-show_1
new file mode 100644
index 000000000..48b779eb9
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/01-show_1
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/data1.bin show 1"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/02-usage b/roms/skiboot/external/gard/test/tests/02-usage
new file mode 100644
index 000000000..7ef3c04e6
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/02-usage
@@ -0,0 +1,17 @@
+#! /bin/sh
+
+# This test fails on FSP based system. Hence skip this test.
+if [ -d "/proc/device-tree/fsps" ] ; then
+ return 0
+fi
+
+run_binary "./opal-gard"
+if [ "$?" -ne 1 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "gard"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/03-show_1-p9 b/roms/skiboot/external/gard/test/tests/03-show_1-p9
new file mode 100644
index 000000000..2a7b91b7d
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/03-show_1-p9
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -f test/files/data-p9.bin --p9 show 1"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/04-create-bad-instance b/roms/skiboot/external/gard/test/tests/04-create-bad-instance
new file mode 100644
index 000000000..f9070a3dc
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/04-create-bad-instance
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/blank.bin create /sys256"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/05-create-bad-unit b/roms/skiboot/external/gard/test/tests/05-create-bad-unit
new file mode 100644
index 000000000..be60aed04
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/05-create-bad-unit
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -f test/files/blank.bin create /doesnt_exist0"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/06-create-long-path b/roms/skiboot/external/gard/test/tests/06-create-long-path
new file mode 100644
index 000000000..f3ffaabf6
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/06-create-long-path
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/blank.bin create /sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/07-create-slash b/roms/skiboot/external/gard/test/tests/07-create-slash
new file mode 100644
index 000000000..0e597e346
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/07-create-slash
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -f test/files/blank.bin create /"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/08-create-duplicate b/roms/skiboot/external/gard/test/tests/08-create-duplicate
new file mode 100644
index 000000000..fad5d3499
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/08-create-duplicate
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/data1.bin create /Sys0/Node0/Membuf0"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/09-create-last-unit b/roms/skiboot/external/gard/test/tests/09-create-last-unit
new file mode 100644
index 000000000..4d9b319cc
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/09-create-last-unit
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+cp test/files/blank.bin $DATA_DIR/input
+run_binary "./opal-gard" "-9 -p -e -f $DATA_DIR/input create /MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255"
+if [ "$?" -ne 0 ]; then
+ fail_test
+fi
+
+run_binary "./opal-gard" "-9 -p -e -f $DATA_DIR/input list"
+if [ "$?" -ne 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/10-clear-single b/roms/skiboot/external/gard/test/tests/10-clear-single
new file mode 100644
index 000000000..3a5f962ec
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/10-clear-single
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+set -e
+
+DATA=$(mktemp)
+
+cleanup() {
+ rm -f $DATA
+}
+
+trap cleanup EXIT
+
+dd if=/dev/zero of=$DATA bs=$((0x1000)) count=5 2>/dev/null
+
+run_binary "./opal-gard" "-p -e -f $DATA clear all"
+run_binary "./opal-gard" "-p -e -f $DATA create /sys0/node0/proc1"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+run_binary "./opal-gard" "-p -e -f $DATA clear 00000001"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/11-clear-first b/roms/skiboot/external/gard/test/tests/11-clear-first
new file mode 100644
index 000000000..c59fcc8f0
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/11-clear-first
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+set -e
+
+DATA=$(mktemp)
+
+cleanup() {
+ rm -f $DATA
+}
+
+trap cleanup EXIT
+
+dd if=/dev/zero of=$DATA bs=$((0x1000)) count=5 2>/dev/null
+
+run_binary "./opal-gard" "-p -e -f $DATA clear all"
+run_binary "./opal-gard" "-p -e -f $DATA create /sys0/node0/proc0"
+run_binary "./opal-gard" "-p -e -f $DATA create /sys0/node0/proc1"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+run_binary "./opal-gard" "-p -e -f $DATA clear 00000001"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+run_binary "./opal-gard" "-p -e -f $DATA clear 00000002"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/units.c b/roms/skiboot/external/gard/units.c
new file mode 100644
index 000000000..f3b435a3a
--- /dev/null
+++ b/roms/skiboot/external/gard/units.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Each chip has a set of "units" that are detailed by the System MRW. Granted
+ * they're pretty much fixed for a given chip generation so hardcoding them here
+ * isn't a big deal.
+ *
+ * These tables we generated from obj/genfiles/errl/errludtarget.H. Which is
+ * generated as a part of the hostboot build process. Yeah that's about as
+ * dumb as it sounds, but whatever.
+ *
+ * Copyright 2017 IBM Corp
+ */
+
+#include "gard.h"
+
+/*
+ * Notes:
+ *
+ * When formatting these as strings we print them into a buffer of MAX_PATH_SIZE
+ * Given there is a max path length of ten units MAX_PATH_SIZE might need a
+ * bump is we start using very detailed unit description strings.
+ */
+
+const struct chip_unit_desc p8_chip_units[] = {
+ {0x00, "NA"},
+ {0x01, "Sys"},
+ {0x02, "Node"},
+ {0x03, "DIMM"},
+ {0x04, "Membuf"},
+ {0x05, "Proc"},
+ {0x06, "EX"},
+ {0x07, "Core"},
+ {0x08, "L2"},
+ {0x09, "L3"},
+ {0x0A, "L4"},
+ {0x0B, "MCS"},
+ {0x0D, "MBA"},
+ {0x0E, "XBUS"},
+ {0x0F, "ABUS"},
+ {0x10, "PCI"},
+ {0x11, "DPSS"},
+ {0x12, "APSS"},
+ {0x13, "OCC"},
+ {0x14, "PSI"},
+ {0x15, "FSP"},
+ {0x16, "PNOR"},
+ {0x17, "OSC"},
+ {0x18, "TODCLK"},
+ {0x19, "CONTROL_NODE"},
+ {0x1A, "OSCREFCLK"},
+ {0x1B, "OSCPCICLK"},
+ {0x1C, "REFCLKENDPT"},
+ {0x1D, "PCICLKENDPT"},
+ {0x1E, "NX"},
+ {0x1F, "PORE"},
+ {0x20, "PCIESWITCH"},
+ {0x21, "CAPP"},
+ {0x22, "FSI"},
+ {0x23, "TPM"},
+ {0x24, "SP"},
+ {0x25, "UART"},
+ {0x26, "PS"},
+ {0x27, "FAN"},
+ {0x28, "VRM"},
+ {0x29, "USB"},
+ {0x2A, "ETH"},
+ {0x2B, "PANEL"},
+ {0x2C, "TEST_FAIL"},
+ {0x2D, "LAST_IN_RANGE"}
+};
+
+const struct chip_unit_desc p9_chip_units[] = {
+ {0x00, "NA"},
+ {0x01, "Sys"},
+ {0x02, "Node"},
+ {0x03, "DIMM"},
+ {0x04, "Membuf"},
+ {0x05, "Proc"},
+ {0x06, "EX"},
+ {0x07, "Core"},
+ {0x08, "L2"},
+ {0x09, "L3"},
+ {0x0A, "L4"},
+ {0x0B, "MCS"},
+ /* a hole! */
+ {0x0D, "MBA"},
+ {0x0E, "XBUS"},
+ {0x0F, "ABUS"},
+ {0x10, "PCI"},
+ {0x11, "DPSS"},
+ {0x12, "APSS"},
+ {0x13, "OCC"},
+ {0x14, "PSI"},
+ {0x15, "FSP"},
+ {0x16, "PNOR"},
+ {0x17, "OSC"},
+ {0x18, "TODCLK"},
+ {0x19, "CONTROL_NODE"},
+ {0x1A, "OSCREFCLK"},
+ {0x1B, "OSCPCICLK"},
+ {0x1C, "REFCLKENDPT"},
+ {0x1D, "PCICLKENDPT"},
+ {0x1E, "NX"},
+ {0x1F, "PORE"},
+ {0x20, "PCIESWITCH"},
+ {0x21, "CAPP"},
+ {0x22, "FSI"},
+ {0x23, "EQ"},
+ {0x24, "MCA"},
+ {0x25, "MCBIST"},
+ {0x26, "MI"},
+ {0x27, "DMI"},
+ {0x28, "OBUS"},
+ {0x2A, "SBE"},
+ {0x2B, "PPE"},
+ {0x2C, "PERV"},
+ {0x2D, "PEC"},
+ {0x2E, "PHB"},
+ {0x2F, "SYSREFCLKENDPT"},
+ {0x30, "MFREFCLKENDPT"},
+ {0x31, "TPM"},
+ {0x32, "SP"},
+ {0x33, "UART"},
+ {0x34, "PS"},
+ {0x35, "FAN"},
+ {0x36, "VRM"},
+ {0x37, "USB"},
+ {0x38, "ETH"},
+ {0x39, "PANEL"},
+ {0x3A, "BMC"},
+ {0x3B, "FLASH"},
+ {0x3C, "SEEPROM"},
+ {0x3D, "TMP"},
+ {0x3E, "GPIO_EXPANDER"},
+ {0x3F, "POWER_SEQUENCER"},
+ {0x40, "RTC"},
+ {0x41, "FANCTLR"},
+ {0x42, "OBUS_BRICK"},
+ {0x43, "NPU"},
+ {0x44, "MC"},
+ {0x45, "TEST_FAIL"},
+ {0x46, "MFREFCLK"},
+ {0x47, "SMPGROUP"},
+ {0x48, "OMI"},
+ {0x49, "MCC"},
+ {0x4A, "OMIC"},
+ {0x4B, "OCMB_CHIP"},
+ {0x4C, "MEM_PORT"},
+ {0x4D, "I2C_MUX"},
+ {0x4E, "PMIC"},
+ {0x4F, "LAST_IN_RANGE"},
+};
+
+const struct chip_unit_desc p10_chip_units[] = {
+ {0x00, "NA"},
+ {0x01, "Sys"},
+ {0x02, "Node"},
+ {0x03, "DIMM"},
+ {0x04, "Membuf"},
+ {0x05, "Proc"},
+ {0x06, "EX"},
+ {0x07, "Core"},
+ {0x08, "L2"},
+ {0x09, "L3"},
+ {0x0A, "L4"},
+ {0x0B, "MCS"},
+ /* a hole! */
+ {0x0D, "MBA"},
+ {0x0E, "XBUS"},
+ {0x0F, "ABUS"},
+ {0x10, "PCI"},
+ {0x11, "DPSS"},
+ {0x12, "APSS"},
+ {0x13, "OCC"},
+ {0x14, "PSI"},
+ {0x15, "FSP"},
+ {0x16, "PNOR"},
+ {0x17, "OSC"},
+ {0x18, "TODCLK"},
+ {0x19, "CONTROL_NODE"},
+ {0x1A, "OSCREFCLK"},
+ {0x1B, "OSCPCICLK"},
+ {0x1C, "REFCLKENDPT"},
+ {0x1D, "PCICLKENDPT"},
+ {0x1E, "NX"},
+ {0x1F, "PORE"},
+ {0x20, "PCIESWITCH"},
+ {0x21, "CAPP"},
+ {0x22, "FSI"},
+ {0x23, "EQ"},
+ {0x24, "MCA"},
+ {0x25, "MCBIST"},
+ {0x26, "MI"},
+ {0x27, "DMI"},
+ {0x28, "OBUS"},
+ {0x2A, "SBE"},
+ {0x2B, "PPE"},
+ {0x2C, "PERV"},
+ {0x2D, "PEC"},
+ {0x2E, "PHB"},
+ {0x2F, "SYSREFCLKENDPT"},
+ {0x30, "MFREFCLKENDPT"},
+ {0x31, "TPM"},
+ {0x32, "SP"},
+ {0x33, "UART"},
+ {0x34, "PS"},
+ {0x35, "FAN"},
+ {0x36, "VRM"},
+ {0x37, "USB"},
+ {0x38, "ETH"},
+ {0x39, "PANEL"},
+ {0x3A, "BMC"},
+ {0x3B, "FLASH"},
+ {0x3C, "SEEPROM"},
+ {0x3D, "TMP"},
+ {0x3E, "GPIO_EXPANDER"},
+ {0x3F, "POWER_SEQUENCER"},
+ {0x40, "RTC"},
+ {0x41, "FANCTLR"},
+ {0x42, "OBUS_BRICK"},
+ {0x43, "NPU"},
+ {0x44, "MC"},
+ {0x45, "TEST_FAIL"},
+ {0x46, "MFREFCLK"},
+ {0x47, "SMPGROUP"},
+ {0x48, "OMI"},
+ {0x49, "MCC"},
+ {0x4A, "OMIC"},
+ {0x4B, "OCMB_CHIP"},
+ {0x4C, "MEM_PORT"},
+ {0x4D, "I2C_MUX"},
+ {0x4E, "PMIC"},
+ {0x4F, "NMMU"},
+ {0x50, "PAU"},
+ {0x51, "IOHS"},
+ {0x52, "PAUC"},
+ {0x53, "FC"},
+ {0x54, "LPCREFCLKENDPT"},
+ {0x55, "GENERIC_I2C_DEVICE"},
+ {0x56, "LAST_IN_RANGE"},
+};
+
diff --git a/roms/skiboot/external/lpc/Makefile b/roms/skiboot/external/lpc/Makefile
new file mode 100644
index 000000000..1c14945c8
--- /dev/null
+++ b/roms/skiboot/external/lpc/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+all: lpc
+
+lpc: lpc.c
+ $(CC) -o $@ $^
+
+clean: rm -rf *.[od] lpc
diff --git a/roms/skiboot/external/lpc/lpc.c b/roms/skiboot/external/lpc/lpc.c
new file mode 100644
index 000000000..112183705
--- /dev/null
+++ b/roms/skiboot/external/lpc/lpc.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * LPC operations through debugfs interface
+ *
+ * Copyright 2014-2018 IBM Corp.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#define SYSFS_PREFIX "/sys/kernel/debug/powerpc/lpc"
+
+int main(int argc, char *argv[])
+{
+ char path[256];
+ char *dot;
+ char *eq;
+ int fd, size = 4;
+ bool do_write = false;
+ bool big_endian = false;
+ uint32_t addr, val;
+ ssize_t rc;
+
+ if (argc < 3) {
+ printf("Usage: %s <space> <addr>[.lLwWbBd[,size]][=value]\n", argv[0]);
+ return 0;
+ }
+
+ eq = strchr(argv[2], '=');
+ if (eq) {
+ do_write = true;
+ val = strtoul(eq + 1, NULL, 0);
+ *eq = 0;
+ }
+ dot = strchr(argv[2], '.');
+ if (dot) {
+ *(dot++) = 0;
+ switch(*dot) {
+ case 'L':
+ big_endian = true;
+ case 'l':
+ break;
+ case 'W':
+ big_endian = true;
+ case 'w':
+ size = 2;
+ break;
+ case 'B':
+ big_endian = true;
+ case 'b':
+ size = 1;
+ break;
+ default:
+ fprintf(stderr, "Invalid size specifier\n");
+ exit(1);
+ }
+ }
+ addr = strtoul(argv[2], NULL, 0);
+
+ memset(path, 0, sizeof(path));
+ snprintf(path, 255, SYSFS_PREFIX "/%s", argv[1]);
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ perror("Failed to open sysfs file");
+ exit(1);
+ }
+
+ lseek(fd, addr, SEEK_SET);
+ if (do_write) {
+ uint8_t v8;
+ uint16_t v16;
+ uint32_t v32;
+
+ switch(size) {
+ case 1:
+ val &= 0xff;
+ v8 = val;
+ rc = write(fd, &v8, 1);
+ if (rc != 1) {
+ perror("Failed to write to LPC");
+ exit(1);
+ }
+ printf("[%s] W 0x%08x.%c=0x%02x\n",
+ argv[1], addr, big_endian ? 'B' : 'b', val);
+ break;
+ case 2:
+ val &= 0xffff;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ v16 = big_endian ? bswap_16(val) : val;
+#else
+ v16 = big_endian ? val : bswap_16(val);
+#endif
+ rc = write(fd, &v16, 2);
+ if (rc != 2) {
+ perror("Failed to write to LPC");
+ exit(1);
+ }
+ printf("[%s] W 0x%08x.%c=0x%04x\n",
+ argv[1], addr, big_endian ? 'W' : 'w', val);
+ break;
+ default:
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ v32 = big_endian ? bswap_32(val) : val;
+#else
+ v32 = big_endian ? val : bswap_32(val);
+#endif
+ rc = write(fd, &v32, 4);
+ if (rc != 4) {
+ perror("Failed to write to LPC");
+ exit(1);
+ }
+ printf("[%s] W 0x%08x.%c=0x%08x\n",
+ argv[1], addr, big_endian ? 'L' : 'l', val);
+ break;
+ }
+ } else {
+ uint8_t v8;
+ uint16_t v16;
+ uint32_t v32;
+
+ switch(size) {
+ case 1:
+ rc = read(fd, &v8, 1);
+ if (rc != 1) {
+ perror("Failed to read from LPC");
+ exit(1);
+ }
+ printf("[%s] R 0x%08x.%c=0x%02x\n", argv[1], addr,
+ big_endian ? 'B' : 'b', v8);
+ break;
+ case 2:
+ rc = read(fd, &v16, 2);
+ if (rc != 2) {
+ perror("Failed to read from LPC");
+ exit(1);
+ }
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ v16 = big_endian ? bswap_16(v16) : v16;
+#else
+ v16 = big_endian ? v16 : bswap_16(v16);
+#endif
+ printf("[%s] R 0x%08x.%c=0x%04x\n", argv[1], addr,
+ big_endian ? 'W' : 'w', v16);
+ break;
+ default:
+ rc = read(fd, &v32, 4);
+ if (rc != 4) {
+ perror("Failed to read from LPC");
+ exit(1);
+ }
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ v32 = big_endian ? bswap_32(v32) : v32;
+#else
+ v32 = big_endian ? v32 : bswap_32(v32);
+#endif
+ printf("[%s] R 0x%08x.%c=0x%08x\n", argv[1], addr,
+ big_endian ? 'L' : 'l', v32);
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/roms/skiboot/external/mambo/Makefile b/roms/skiboot/external/mambo/Makefile
new file mode 100644
index 000000000..942ebf20e
--- /dev/null
+++ b/roms/skiboot/external/mambo/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+CFLAGS=-O2 -m64 -pthread -Werror -Wall
+
+CC=$(CROSS)gcc
+
+all: mambo-socket-proxy
+
+clean:
+ rm -rf mambo-socket-proxy
diff --git a/roms/skiboot/external/mambo/README.md b/roms/skiboot/external/mambo/README.md
new file mode 100644
index 000000000..06a0f3185
--- /dev/null
+++ b/roms/skiboot/external/mambo/README.md
@@ -0,0 +1,73 @@
+# Running skiboot and Linux in Mambo
+
+The POWER8 Functional Simulator (aka Mambo) is free to use but not
+open source and is only supported on limited platforms. This is a
+guide to getting started guide with it with skiboot and linux.
+
+## Getting Started
+
+From a bare x86_64 Ubuntu 16.04 install, to running skiboot and linux
+in the simulator, you can do do the following:
+
+### Steps to get Running on Ubuntu
+xterm is needed by the simulator.
+```
+apt-get install xterm
+```
+
+### Download mambo from IBM
+Download systemsim-p8..deb from:
+http://www-304.ibm.com/support/customercare/sas/f/pwrfs/home.html
+```
+dpkg -i systemsim-p8*deb
+```
+
+### Grab your skiboot, linux and initramfs images
+How to build a skiboot.lid is in the top level README file.
+
+Use a 64 bit powerpc kernel here. If compiling yourself, we suggest
+using powernv_defconfig.
+
+If you use op-build to build a full set of OpenPower images, you’ll
+likely be able to extract skiboot, zImage.epapr (or vmlinux and
+rootfs.cpio.xz) from output/images. We suggest using the
+opal_defconfig.
+
+### Setup environment variables
+Setup environment variables to point to your images
+```
+export SKIBOOT_ZIMAGE=$HOME/src/op-build/output/images/zImage.epapr
+export SKIBOOT=$HOME/src/op-build/output/images/skiboot.lid
+export SKIBOOT_AUTORUN=1
+```
+If you want Mambo to autmatically run *AND* exit when the system is
+shutdown (or has a HW crash) use this:
+```
+export SKIBOOT_AUTORUN=2
+```
+If you want a vmlinux and separate initramfs you can also do this:
+```
+export SKIBOOT_ZIMAGE=$HOME/src/op-build/output/images/vmlinux
+export SKIBOOT_INITRD=$HOME/src/op-build/output/images/rootfs.cpio.xz
+export SKIBOOT=$HOME/src/skiboot/skiboot.lid
+export SKIBOOT_AUTORUN=1
+```
+
+### Using Real Container Verification Code for Secure Boot
+
+The CVC code dump is from a real machine, and the code is from the Hostboot
+project (see src/securerom). We just include the dump here for testing
+purposes.
+
+```
+export SKIBOOT_CVC_CODE=$HOME/src/skiboot/external/mambo/cvc.bin
+export SKIBOOT_ENABLE_MAMBO_STB=1
+```
+
+### Run the simulator
+```
+/opt/ibm/systemsim-p8/run/pegasus/power8 -f $HOME/src/skiboot/external/mambo/skiboot.tcl
+```
+
+This should open an xterm and start booting. It should take around
+20sec to get to a petitboot console.
diff --git a/roms/skiboot/external/mambo/cvc.bin b/roms/skiboot/external/mambo/cvc.bin
new file mode 100644
index 000000000..8d4c7e704
--- /dev/null
+++ b/roms/skiboot/external/mambo/cvc.bin
Binary files differ
diff --git a/roms/skiboot/external/mambo/mambo-socket-proxy.c b/roms/skiboot/external/mambo/mambo-socket-proxy.c
new file mode 100644
index 000000000..64b677045
--- /dev/null
+++ b/roms/skiboot/external/mambo/mambo-socket-proxy.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Compile with:
+ * gcc -static -O2 mambo-socket-proxy.c -o mambo-socket-proxy -pthread
+ * Run inside the simulator:
+ * - to forward host ssh connections to sim ssh server
+ * ./mambo-socket-proxy -h 10022 -s 22
+ * Then connect to port 10022 on your host
+ * ssh -p 10022 localhost
+ * - to allow http proxy access from inside the sim to local http proxy
+ * ./mambo-socket-proxy -b proxy.mynetwork -h 3128 -s 3128
+ *
+ * Copyright (C) 2017 Michael Neuling <mikey@neuling.org>, IBM
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <getopt.h>
+
+#define CALL_TCL 86
+#define BOGUS_SOCKET_CONN_PROBE_CODE 224
+#define BOGUS_SOCKET_CONN_SEND_CODE 225
+#define BOGUS_SOCKET_CONN_RECV_CODE 226
+
+static inline int callthru2(int command, unsigned long arg1, unsigned long arg2)
+{
+ register int c asm("r3") = command;
+ register unsigned long a1 asm("r4") = arg1;
+ register unsigned long a2 asm("r5") = arg2;
+ asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2));
+ return (c);
+}
+
+static inline int callthru3(int command, unsigned long arg1, unsigned long arg2,
+ unsigned long arg3)
+{
+ register int c asm("r3") = command;
+ register unsigned long a1 asm("r4") = arg1;
+ register unsigned long a2 asm("r5") = arg2;
+ register unsigned long a3 asm("r6") = arg3;
+ asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2),
+ "r"(a3));
+ return (c);
+}
+
+static inline int callthru4(int command, unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4)
+{
+ register int c asm("r3") = command;
+ register unsigned long a1 asm("r4") = arg1;
+ register unsigned long a2 asm("r5") = arg2;
+ register unsigned long a3 asm("r6") = arg3;
+ register unsigned long a4 asm("r7") = arg4;
+ asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2),
+ "r"(a3), "r"(a4));
+ return (c);
+}
+
+static inline int callthru5(int command, unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4, unsigned long arg5)
+{
+ register int c asm("r3") = command;
+ register unsigned long a1 asm("r4") = arg1;
+ register unsigned long a2 asm("r5") = arg2;
+ register unsigned long a3 asm("r6") = arg3;
+ register unsigned long a4 asm("r7") = arg4;
+ register unsigned long a5 asm("r8") = arg5;
+ asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2),
+ "r"(a3), "r"(a4), "r"(a5));
+ return (c);
+}
+
+unsigned long callthru_tcl(const char *str, int strlen)
+{
+ return callthru2(CALL_TCL, (unsigned long)str,
+ (unsigned long)strlen);
+}
+
+unsigned long bogus_socket_conn_probe(int dev, void *addr, int conn)
+{
+ return callthru3(BOGUS_SOCKET_CONN_PROBE_CODE,
+ (unsigned long)dev,
+ (unsigned long)addr,
+ (unsigned long)conn);
+}
+
+unsigned long bogus_socket_conn_recv(int dev, void *addr, int maxlen, int conn)
+{
+ return callthru4(BOGUS_SOCKET_CONN_RECV_CODE,
+ (unsigned long)dev,
+ (unsigned long)addr,
+ (unsigned long)maxlen,
+ (unsigned long)conn);
+}
+
+unsigned long bogus_socket_conn_send(int dev, void *addr, int maxlen, int conn)
+{
+ return callthru5(BOGUS_SOCKET_CONN_SEND_CODE,
+ (unsigned long)dev,
+ (unsigned long)addr,
+ (unsigned long)maxlen,
+ 0,
+ (unsigned long)conn);
+}
+
+#define BUF_MAX 1024
+
+struct sock_info {
+ char *host;
+ int sock;
+ int dev;
+ int open;
+ int conn;
+};
+
+void *recv_thread(void *ptr)
+{
+ struct timeval timeout;
+ struct sock_info *si = ptr;
+ char buf[BUF_MAX];
+ int len;
+ fd_set set;
+
+ /* 1 sec */
+
+
+ while(1) {
+ FD_ZERO(&set);
+ FD_SET(si->sock, &set);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ /* Set timeout to 1 second */
+ len = select(si->sock+1, &set, NULL, NULL, &timeout);
+ if (len <= 0) /* timeout */
+ len = -1;
+ else /* Receive from mambo tcp server */
+ len = recv(si->sock, &buf, BUF_MAX, 0);
+ if (len == 0) {
+ si->open = 0;
+ return NULL; /* closed */
+ }
+ if (len != -1) {
+ bogus_socket_conn_send(si->dev, &buf, len, si->conn);
+ }
+ if (!si->open)
+ return NULL;
+ }
+}
+
+#define POLL_MAX_NS 10000000
+
+void *send_thread(void *ptr)
+{
+ struct sock_info *si = ptr;
+ char buf[BUF_MAX];
+ int len;
+ struct timespec t;
+ int fault_retry = 16;
+
+ t.tv_sec = 0;
+ t.tv_nsec = POLL_MAX_NS;
+
+ while(1) {
+ /* Send to mambo tcp server */
+ len = bogus_socket_conn_recv(si->dev, &buf, BUF_MAX, si->conn);
+ if (len == -3 && fault_retry--) {
+ /* Page fault. Touch the buf and try again */
+ memset(buf, 0, BUF_MAX);
+ continue;
+ }
+ fault_retry = 16;
+
+ if (len == -1) /* EAGAIN */
+ nanosleep(&t , NULL);
+ else if (len > 0)
+ send(si->sock, &buf, len, 0);
+ else {
+ si->open = 0;
+ return NULL; /* closed */
+ }
+ if (!si->open)
+ return NULL;
+ }
+
+}
+
+void *connect_sockets(void *ptr)
+{
+ struct sock_info *si = ptr;
+ pthread_t recv, send;
+ unsigned long rc = 0;
+
+ if (pthread_create(&recv, NULL, recv_thread, si) ||
+ pthread_create(&send, NULL, send_thread, si)) {
+ rc = -1;
+ goto out;
+ }
+
+ if (pthread_join(recv, NULL) || pthread_join(send, NULL)) {
+ rc = -1;
+ goto out;
+ }
+
+out:
+ /* FIXME: Do shutdown better */
+ shutdown(si->sock, SHUT_WR);
+ si->open = 0;
+ free(si);
+ return (void *)rc;
+}
+
+void print_usage() {
+ printf("Usage:\n");
+ printf(" mambo-socket-proxy [-b <host>] -h <host port> -s <sim port>\n");
+ printf("\n");
+ printf(" -h <host port> : Port on the host to forward\n");
+ printf(" -s <host port> : Port in the sim to forward\n");
+ printf(" -b <host machine> : Connect sim port to host network\n");
+ printf("\n");
+}
+
+int main (int argc, char *argv[])
+{
+ char cmd[128];
+ struct sockaddr_in ser, client;
+ pthread_t sockets_thread;
+ struct sock_info *si;
+ int sock, conn, rc = 0, option = 0, one_shot = 0, c, sock_desc = 0;
+ char *host = NULL;
+ int host_port = -1, sim_port = -1;
+ int dev = 1; /* backwards starts at 1 so forwards can use 0 */
+
+ while ((option = getopt(argc, argv,"rb:h:s:")) != -1) {
+ switch (option) {
+ case 'b' :
+ host = optarg;
+ break;
+ case 'h' :
+ host_port = atoi(optarg);
+ break;
+ case 's' :
+ sim_port = atoi(optarg);
+ break;
+ default:
+ print_usage();
+ exit(1);
+ }
+ }
+
+ if (host_port == -1 || sim_port ==-1) {
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * A host/backwards connection will use dev=0 and conn >= 0.
+ * The forwards connection will use dev >= 1 and conn=0
+ */
+ if (host) {
+ sock_desc = socket(PF_INET, SOCK_STREAM, 0);
+ ser.sin_family = AF_INET;
+ ser.sin_addr.s_addr = INADDR_ANY;
+ ser.sin_port = htons(sim_port);
+
+ if (bind(sock_desc, (struct sockaddr *) &ser, sizeof(ser)) < 0) {
+ perror("Can't connect to sim port");
+ rc = -1;
+ goto out;
+ }
+
+ listen(sock_desc, 3);
+ } else {
+ /*
+ * Cleaning up old bogus socket.
+ */
+ sprintf(cmd, "mysim bogus socket cleanup");
+ callthru_tcl(cmd, strlen(cmd));
+ sleep(1); /* Cleanup takes a while */
+ sprintf(cmd, "mysim bogus socket init 0 server "
+ "127.0.0.1 %i poll 0 nonblock", host_port);
+ callthru_tcl(cmd, strlen(cmd));
+ }
+
+ while (1) {
+
+ if (host) {
+ sock = accept(sock_desc, (struct sockaddr *)&client, (socklen_t*)&c);
+ if (sock < 0) {
+ perror("accept failed");
+ rc = -1;
+ goto out;
+ }
+
+ sprintf(cmd, "mysim bogus socket init %i client %s %i poll 0",
+ dev, host, host_port);
+ callthru_tcl(cmd, strlen(cmd));
+ while (bogus_socket_conn_probe(dev, NULL, 0) == -1)
+ sleep(1);
+ } else {
+ struct timespec t;
+ t.tv_sec = 0;
+ t.tv_nsec = 10000000;
+ do {
+ conn = bogus_socket_conn_probe(0, NULL, -1);
+ nanosleep(&t , NULL);
+ } while (conn == -1);
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ ser.sin_family = AF_INET;
+ ser.sin_port = htons(sim_port);
+ ser.sin_addr.s_addr = inet_addr("127.0.0.1");
+ memset(ser.sin_zero, '\0', sizeof ser.sin_zero);
+
+ if (connect(sock, (struct sockaddr *) &ser, sizeof(ser))) {
+ perror("Can't connect to sim port");
+ rc = -1;
+ goto out;
+ }
+ }
+
+ si = malloc(sizeof(struct sock_info));
+ si->host = host;
+ si->sock = sock;
+ si->dev = host?dev:0;
+ si->open = 1;
+ si->conn = host?0:conn;
+
+ if (pthread_create(&sockets_thread, NULL, connect_sockets, si)) {
+ rc = -1;
+ goto out;
+ }
+
+ if (one_shot)
+ break;
+ ++dev; // FIXME: do a real allocator
+ }
+out:
+ exit(rc);
+}
diff --git a/roms/skiboot/external/mambo/mambo_utils.tcl b/roms/skiboot/external/mambo/mambo_utils.tcl
new file mode 100644
index 000000000..96f8971ab
--- /dev/null
+++ b/roms/skiboot/external/mambo/mambo_utils.tcl
@@ -0,0 +1,794 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Make Mambo behave a bit more like gdb
+#
+set target_t 0
+set target_c 0
+set target_p 0
+
+proc target { { t 0 } { c 0 } { p 0 } } {
+ global target_t
+ global target_c
+ global target_p
+
+ set target_t $t
+ set target_c $c
+ set target_p $p
+
+ return "targeting cpu $p:$c:$t"
+}
+
+proc p { reg { t -1 } { c -1 } { p -1 } } {
+ global target_t
+ global target_c
+ global target_p
+
+ if { $t == -1 } { set t $target_t }
+ if { $c == -1 } { set c $target_c }
+ if { $p == -1 } { set p $target_p }
+
+ switch -regexp $reg {
+ ^r$ {
+ set val [mysim cpu $p:$c:$t display gprs]
+ }
+ ^r[0-9]+$ {
+ regexp "r(\[0-9\]*)" $reg dummy num
+ set val [mysim cpu $p:$c:$t display gpr $num]
+ }
+ ^f[0-9]+$ {
+ regexp "f(\[0-9\]*)" $reg dummy num
+ set val [mysim cpu $p:$c:$t display fpr $num]
+ }
+ ^v[0-9]+$ {
+ regexp "v(\[0-9\]*)" $reg dummy num
+ set val [mysim cpu $p:$c:$t display vmxr $num]
+ }
+ default {
+ set val [mysim cpu $p:$c:$t display spr $reg]
+ }
+ }
+
+ return "$val"
+}
+
+#
+# behave like gdb
+#
+proc sr { reg val { t -1} { c -1 } { p -1 } } {
+ global target_t
+ global target_c
+ global target_p
+
+ if { $t == -1 } { set t $target_t }
+ if { $c == -1 } { set c $target_c }
+ if { $p == -1 } { set p $target_p }
+
+ switch -regexp $reg {
+ ^r[0-9]+$ {
+ regexp "r(\[0-9\]*)" $reg dummy num
+ mysim cpu $p:$c:$t set gpr $num $val
+ }
+ ^f[0-9]+$ {
+ regexp "f(\[0-9\]*)" $reg dummy num
+ mysim cpu $p:$c:$t set fpr $num $val
+ }
+ ^v[0-9]+$ {
+ regexp "v(\[0-9\]*)" $reg dummy num
+ mysim cpu $p:$c:$t set vmxr $num $val
+ }
+ default {
+ mysim cpu $p:$c:$t set spr $reg $val
+ }
+ }
+ p $reg $t
+}
+
+proc b { addr } {
+ mysim trigger set pc $addr "just_stop"
+ set at [i $addr]
+ puts "breakpoint set at $at"
+}
+
+# Run until $console_string appears on the Linux console
+#
+# eg.
+# break_on_console "Freeing unused kernel memory:"
+# break_on_console "buildroot login:"
+
+proc break_on_console { console_string } {
+ mysim trigger set console "$console_string" "just_stop"
+}
+
+proc clear_console_break { console_string } {
+ mysim trigger clear console "$console_string"
+}
+
+proc wr { start stop } {
+ mysim trigger set memory system w $start $stop 0 "just_stop"
+}
+
+proc c { } {
+ mysim go
+}
+
+proc i { pc { t -1 } { c -1 } { p -1 } } {
+ global target_t
+ global target_c
+ global target_p
+
+ if { $t == -1 } { set t $target_t }
+ if { $c == -1 } { set c $target_c }
+ if { $p == -1 } { set p $target_p }
+
+ set pc_laddr [mysim cpu $p:$c:$t util itranslate $pc]
+ set inst [mysim cpu $p:$c:$t memory display $pc_laddr 4]
+ set disasm [mysim cpu $p:$c:$t util ppc_disasm $inst $pc]
+ return "\[$p:$c:$t\]: $pc ($pc_laddr) Enc:$inst : $disasm"
+}
+
+proc ipc { { t -1 } { c -1 } { p -1 } } {
+ global target_t
+ global target_c
+ global target_p
+
+ if { $t == -1 } { set t $target_t }
+ if { $c == -1 } { set c $target_c }
+ if { $p == -1 } { set p $target_p }
+
+ set pc [mysim cpu $p:$c:$t display spr pc]
+ i $pc $t $c $p
+}
+
+proc ipca { } {
+ set cpus [myconf query cpus]
+ set threads [myconf query processor/number_of_threads]
+
+ for { set i 0 } { $i < $cpus } { incr i 1 } {
+ for { set j 0 } { $j < $threads } { incr j 1 } {
+ puts [ipc $j $i]
+ }
+ }
+}
+
+proc pa { spr } {
+ set cpus [myconf query cpus]
+ set threads [myconf query processor/number_of_threads]
+
+ for { set i 0 } { $i < $cpus } { incr i 1 } {
+ for { set j 0 } { $j < $threads } { incr j 1 } {
+ set val [mysim cpu $i thread $j display spr $spr]
+ puts "CPU: $i THREAD: $j SPR $spr = $val"
+ }
+ }
+}
+
+proc s { {nr 1} } {
+ for { set i 0 } { $i < $nr } { incr i 1 } {
+ mysim step 1
+ ipca
+ }
+}
+
+proc S { {nr 1} } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ for { set i 0 } { $i < $nr } { incr i 1 } {
+ mysim cpu $p:$c:$t step 1
+ puts [ipc]
+ }
+}
+
+proc z { count } {
+ while { $count > 0 } {
+ s
+ incr count -1
+ }
+}
+
+proc sample_pc { sample count } {
+ while { $count > 0 } {
+ mysim cycle $sample
+ ipc
+ incr count -1
+ }
+}
+
+proc e2p { ea } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ set pa [ mysim cpu $p:$c:$t util dtranslate $ea ]
+ puts "$pa"
+}
+
+proc x { pa { size 8 } } {
+ set val [ mysim memory display $pa $size ]
+ puts "$pa : $val"
+}
+
+proc it { ea } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ mysim cpu $p:$c:$t util itranslate $ea
+}
+proc dt { ea } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ mysim cpu $p:$c:$t util dtranslate $ea
+}
+
+proc ex { ea { size 8 } } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ set pa [ mysim cpu $p:$c:$t util dtranslate $ea ]
+ set val [ mysim memory display $pa $size ]
+ puts "$pa : $val"
+}
+
+proc di { location { count 16 } } {
+ set addr [expr $location & 0xfffffffffffffff0]
+ disasm_mem mysim $addr $count
+}
+
+proc hexdump { location count } {
+ set addr [expr $location & 0xfffffffffffffff0]
+ set top [expr $addr + ($count * 15)]
+ for { set i $addr } { $i < $top } { incr i 16 } {
+ set val [expr $i + (4 * 0)]
+ set val0 [format "%08x" [mysim memory display $val 4]]
+ set val [expr $i + (4 * 1)]
+ set val1 [format "%08x" [mysim memory display $val 4]]
+ set val [expr $i + (4 * 2)]
+ set val2 [format "%08x" [mysim memory display $val 4]]
+ set val [expr $i + (4 * 3)]
+ set val3 [format "%08x" [mysim memory display $val 4]]
+
+ set ascii ""
+ for { set j 0 } { $j < 16 } { incr j } {
+ set byte [get_char [expr $i + $j]]
+ if { $byte < 0x20 || $byte >= 127} {
+ set c "."
+ } else {
+ set c [format %c $byte]
+ }
+ set ascii [string cat "$ascii" "$c"]
+ }
+
+ set loc [format "0x%016x" $i]
+ puts "$loc: $val0 $val1 $val2 $val3 $ascii"
+ }
+}
+
+proc get_char { addr } {
+ return [expr [mysim memory display "$addr" 1]]
+}
+
+proc p_str { addr { limit 0 } } {
+ set addr_limit 0xfffffffffffffffff
+ if { $limit > 0 } { set addr_limit [expr $limit + $addr] }
+ set s ""
+
+ for {} { [get_char "$addr"] != 0} { incr addr 1 } {
+ # memory display returns hex values with a leading 0x
+ set c [format %c [get_char "$addr"]]
+ set s [string cat "$s" "$c"]
+ if { $addr == $addr_limit } { break }
+ }
+
+ puts "$s"
+}
+
+proc slbv {} {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ puts [mysim cpu $p:$c:$t display slb valid]
+}
+
+proc regs { { t -1 } { c -1 } { p -1 }} {
+ global target_t
+ global target_c
+ global target_p
+
+ if { $t == -1 } { set t $target_t }
+ if { $c == -1 } { set c $target_c }
+ if { $p == -1 } { set p $target_p }
+
+ puts "GPRS:"
+ puts [mysim cpu $p:$c:$t display gprs]
+}
+
+proc tlbv {} {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ puts "$p:$c:$t:TLB: ----------------------"
+ puts [mysim cpu $p:$c:$t display tlb valid]
+}
+
+proc exc { { i SystemReset } } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ puts "$p:$c:$t:EXCEPTION:$i"
+ puts [mysim cpu $p:$c:$t interrupt $i]
+}
+
+proc just_stop { args } {
+ simstop
+ ipca
+}
+
+proc st { count } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ set sp [mysim cpu $p:$c:$t display gpr 1]
+ puts "SP: $sp"
+ ipc
+ set lr [mysim cpu $p:$c:$t display spr lr]
+ i $lr
+ while { $count > 0 } {
+ set sp [mysim cpu $p:$c:$t util itranslate $sp]
+ set lr [mysim memory display [expr $sp++16] 8]
+ i $lr
+ set sp [mysim memory display $sp 8]
+
+ incr count -1
+ }
+}
+
+proc mywatch { } {
+ while { [mysim memory display 0x700 8] != 0 } {
+ mysim cycle 1
+ }
+ puts "condition occurred "
+ ipc
+}
+
+#
+# force gdb to attach
+#
+proc gdb { { timeout 0 } } {
+ mysim set fast off
+ mysim debugger wait $timeout
+}
+
+proc egdb { { timeout 0 }} {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ set srr0 [mysim cpu $p:$c:$t display spr srr0]
+ set srr1 [mysim cpu $p:$c:$t display spr srr1]
+ mysim cpu $p:$c:$t set spr pc $srr0
+ mysim cpu $p:$c:$t set spr msr $srr1
+ gdb $timeout
+}
+
+proc mem_display_64_le { addr } {
+ set data 0
+ for {set i 0} {$i < 8} {incr i} {
+ set data [ expr $data << 8 ]
+ set l [ mysim memory display [ expr $addr+7-$i ] 1 ]
+ set data [ expr $data | $l ]
+ }
+ return [format 0x%X $data]
+}
+
+proc mem_display_64 { addr le } {
+ if { $le } {
+ return [ mem_display_64_le $addr ]
+ }
+ # mysim memory display is big endian
+ return [ mysim memory display $addr 8 ]
+}
+
+proc bt { {sp 0} } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ set lr [mysim cpu $p:$c:$t display spr pc]
+ set sym [addr2func $lr]
+ puts "pc:\t\t\t\t$lr\t$sym"
+ if { $sp == 0 } {
+ set sp [mysim cpu $p:$c:$t display gpr 1]
+ }
+ set lr [mysim cpu $p:$c:$t display spr lr]
+ set sym [addr2func $lr]
+ puts "lr:\t\t\t\t$lr\t$sym"
+
+ set msr [mysim cpu $p:$c:$t display spr msr]
+ set le [ expr $msr & 1 ]
+
+ # Limit to 200 in case of an infinite loop
+ for {set i 0} {$i < 200} {incr i} {
+ set pa [ mysim cpu $p:$c:$t util dtranslate $sp ]
+ set bc [ mem_display_64 $pa $le ]
+ set lr [ mem_display_64 [ expr $pa + 16 ] $le ]
+ set sym [addr2func $lr]
+ puts "stack:$pa \t$lr\t$sym"
+ if { $bc == 0 } { break }
+ set sp $bc
+ }
+ puts ""
+}
+
+proc ton { } {mysim mode turbo }
+proc toff { } {mysim mode simple }
+
+proc don { opt } {
+ simdebug set $opt 1
+}
+
+proc doff { opt } {
+ simdebug set $opt 0
+}
+
+# skisym and linsym return the address of a symbol, looked up from
+# the relevant System.map or skiboot.map file.
+proc linsym { name } {
+ global linux_symbol_map
+
+ # create a regexp that matches the symbol name
+ set base {([[:xdigit:]]*) (.)}
+ set exp [concat $base " $name\$"]
+ set ret ""
+
+ foreach {line addr type} [regexp -line -inline $exp $linux_symbol_map] {
+ set ret "0x$addr"
+ }
+
+ return $ret
+}
+
+# skisym factors in skiboot's load address
+proc skisym { name } {
+ global skiboot_symbol_map
+ global mconf
+
+ set base {([[:xdigit:]]*) (.)}
+ set exp [concat $base " $name\$"]
+ set ret ""
+
+ foreach {line addr type} [regexp -line -inline $exp $skiboot_symbol_map] {
+ set actual_addr [expr "0x$addr" + $mconf(boot_load)]
+ set ret [format "0x%.16x" $actual_addr]
+ }
+
+ return $ret
+}
+
+proc addr2func { addr } {
+ global skiboot_symbol_list
+ global linux_symbol_list
+ global user_symbol_list
+ global mconf
+
+ set prevname ""
+ set preva "0"
+
+ if { [ info exists linux_symbol_list ] && "$addr" >= 0xc000000000000000} {
+ foreach line $linux_symbol_list {
+ lassign $line a type name
+ if { "0x$a" > $addr } {
+ set o [format "0x%x" [expr $addr - "0x$preva"]]
+ return "$prevname+$o"
+ }
+ set prevname $name
+ set preva $a
+ }
+ }
+ # Assume skiboot is less that 4MB big
+ if { [ info exists skiboot_symbol_list ] &&
+ "$addr" > $mconf(boot_load) && "$addr" < [expr $mconf(boot_load) + 4194304] } {
+ set mapaddr [expr $addr - $mconf(boot_load)]
+
+ foreach line $skiboot_symbol_list {
+ lassign $line a type name
+ if { "0x$a" > $mapaddr } {
+ set o [format "0x%x" [expr $mapaddr - "0x$preva"]]
+ return "$prevname+$o"
+ }
+ set prevname $name
+ set preva $a
+ }
+ }
+ if { [ info exists user_symbol_list ] } {
+ foreach line $user_symbol_list {
+ lassign $line a type name
+ if { "0x$a" > $addr } {
+ set o [format "0x%x" [expr $addr - "0x$preva"]]
+ return "$prevname+$o"
+ }
+ set prevname $name
+ set preva $a
+ }
+ }
+ return "+$addr"
+}
+
+proc current_insn { { t -1 } { c -1 } { p -1 }} {
+ global target_t
+ global target_c
+ global target_p
+
+ if { $t == -1 } { set t $target_t }
+ if { $c == -1 } { set c $target_c }
+ if { $p == -1 } { set p $target_p }
+
+ set pc [mysim cpu $p:$c:$t display spr pc]
+ set pc_laddr [mysim cpu $p:$c:$t util itranslate $pc]
+ set inst [mysim cpu $p:$c:$t memory display $pc_laddr 4]
+ set disasm [mysim cpu $p:$c:$t util ppc_disasm $inst $pc]
+ return $disasm
+}
+
+set SRR1 0
+set DSISR 0
+set DAR 0
+
+proc sreset_trigger { args } {
+ global SRR1
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ mysim trigger clear pc 0x100
+ mysim trigger clear pc 0x104
+ set s [expr [mysim cpu $p:$c:$t display spr srr1] & ~0x00000000003c0002]
+ set SRR1 [expr $SRR1 | $s]
+ mysim cpu $p:$c:$t set spr srr1 $SRR1
+}
+
+proc exc_sreset { } {
+ global SRR1
+ global DSISR
+ global DAR
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ # In case of recoverable MCE, idle wakeup always sets RI, others get
+ # RI from current environment. For unrecoverable, RI would always be
+ # clear by hardware.
+ if { [current_insn] in { "stop" "nap" "sleep" "winkle" } } {
+ set msr_ri 0x2
+ set SRR1_powersave [expr (0x2 << (63-47))]
+ } else {
+ set msr_ri [expr [mysim cpu $p:$c:$t display spr msr] & 0x2]
+ set SRR1_powersave 0
+ }
+
+ # reason system reset
+ set SRR1_reason 0x4
+
+ set SRR1 [expr 0x0 | $msr_ri | $SRR1_powersave]
+ set SRR1 [expr $SRR1 | ((($SRR1_reason >> 3) & 0x1) << (63-42))]
+ set SRR1 [expr $SRR1 | ((($SRR1_reason >> 2) & 0x1) << (63-43))]
+ set SRR1 [expr $SRR1 | ((($SRR1_reason >> 1) & 0x1) << (63-44))]
+ set SRR1 [expr $SRR1 | ((($SRR1_reason >> 0) & 0x1) << (63-45))]
+
+ if { [current_insn] in { "stop" "nap" "sleep" "winkle" } } {
+ # mambo has a quirk that interrupts from idle wake immediately
+ # and go over current instruction.
+ mysim trigger set pc 0x100 "sreset_trigger"
+ mysim trigger set pc 0x104 "sreset_trigger"
+ mysim cpu $p:$c:$t interrupt SystemReset
+ } else {
+ mysim trigger set pc 0x100 "sreset_trigger"
+ mysim trigger set pc 0x104 "sreset_trigger"
+ mysim cpu $p:$c:$t interrupt SystemReset
+ }
+
+ # sleep and sometimes other types of interrupts do not trigger 0x100
+ if { [expr [mysim cpu $p:$c:$t display spr pc] == 0x100 ] } {
+ sreset_trigger
+ }
+ if { [expr [mysim cpu $p:$c:$t display spr pc] == 0x104 ] } {
+ sreset_trigger
+ }
+}
+
+proc mce_trigger { args } {
+ global SRR1
+ global DSISR
+ global DAR
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ mysim trigger clear pc 0x200
+ mysim trigger clear pc 0x204
+
+ set s [expr [mysim cpu 0 display spr srr1] & ~0x00000000801f0002]
+ set SRR1 [expr $SRR1 | $s]
+ mysim cpu $p:$c:$t set spr srr1 $SRR1
+ mysim cpu $p:$c:$t set spr dsisr $DSISR
+ mysim cpu $p:$c:$t set spr dar $DAR ; list
+}
+
+#
+# Inject a machine check. Recoverable MCE types can be forced to unrecoverable
+# by clearing MSR_RI bit from SRR1 (which hardware may do).
+# If d_side is 0, then cause goes into SRR1. Otherwise it gets put into DSISR.
+# DAR is hardcoded to always 0xdeadbeefdeadbeef
+#
+# Default with no arguments is a recoverable i-side TLB multi-hit
+# Other options:
+# d_side=1 dsisr=0x80 - recoverable d-side SLB multi-hit
+# d_side=1 dsisr=0x8000 - ue error on instruction fetch
+# d_side=0 cause=0xd - unrecoverable i-side async store timeout (POWER9 only)
+# d_side=0 cause=0x1 - unrecoverable i-side ifetch
+#
+proc exc_mce { { d_side 0 } { cause 0x5 } { recoverable 1 } } {
+ global SRR1
+ global DSISR
+ global DAR
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+# puts "INJECTING MCE"
+
+ # In case of recoverable MCE, idle wakeup always sets RI, others get
+ # RI from current environment. For unrecoverable, RI would always be
+ # clear by hardware.
+ if { [current_insn] in { "stop" "nap" "sleep" "winkle" } } {
+ set msr_ri 0x2
+ set SRR1_powersave [expr (0x2 << (63-47))]
+ } else {
+ set msr_ri [expr [mysim cpu $p:$c:$t display spr msr] & 0x2]
+ set SRR1_powersave 0
+ }
+
+ if { !$recoverable } {
+ set msr_ri 0x0
+ }
+
+ if { $d_side } {
+ set is_dside 1
+ set SRR1_mc_cause 0x0
+ set DSISR $cause
+ set DAR 0xdeadbeefdeadbeef
+ } else {
+ set is_dside 0
+ set SRR1_mc_cause $cause
+ set DSISR 0x0
+ set DAR 0x0
+ }
+
+ set SRR1 [expr 0x0 | $msr_ri | $SRR1_powersave]
+
+ set SRR1 [expr $SRR1 | ($is_dside << (63-42))]
+ set SRR1 [expr $SRR1 | ((($SRR1_mc_cause >> 3) & 0x1) << (63-36))]
+ set SRR1 [expr $SRR1 | ((($SRR1_mc_cause >> 2) & 0x1) << (63-43))]
+ set SRR1 [expr $SRR1 | ((($SRR1_mc_cause >> 1) & 0x1) << (63-44))]
+ set SRR1 [expr $SRR1 | ((($SRR1_mc_cause >> 0) & 0x1) << (63-45))]
+
+ if { [current_insn] in { "stop" "nap" "sleep" "winkle" } } {
+ # mambo has a quirk that interrupts from idle wake immediately
+ # and go over current instruction.
+ mysim trigger set pc 0x200 "mce_trigger"
+ mysim trigger set pc 0x204 "mce_trigger"
+ mysim cpu $p:$c:$t interrupt MachineCheck
+ } else {
+ mysim trigger set pc 0x200 "mce_trigger"
+ mysim trigger set pc 0x204 "mce_trigger"
+ mysim cpu $p:$c:$t interrupt MachineCheck
+ }
+
+ # sleep and sometimes other types of interrupts do not trigger 0x200
+ if { [expr [mysim cpu $p:$c:$t display spr pc] == 0x200 ] } {
+ mce_trigger
+ }
+ if { [expr [mysim cpu $p:$c:$t display spr pc] == 0x204 ] } {
+ mce_trigger
+ }
+}
+
+set R1 0
+
+# Avoid stopping if we re-enter the same code. Wait until r1 matches.
+# This helps stepping over exceptions or function calls etc.
+proc stop_stack_match { args } {
+ global R1
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ set r1 [mysim cpu $p:$c:$t display gpr 1]
+ if { $R1 == $r1 } {
+ simstop
+ ipca
+ }
+}
+
+# inject default recoverable MCE and step over it. Useful for testing whether
+# code copes with taking an interleaving MCE.
+proc inject_mce { } {
+ global R1
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ set R1 [mysim cpu $p:$c:$t display gpr 1]
+ set pc [mysim cpu $p:$c:$t display spr pc]
+ mysim trigger set pc $pc "stop_stack_match"
+ exc_mce
+ c
+ mysim trigger clear pc $pc ; list
+}
+
+#
+# We've stopped at addr and we need to inject the mce and continue
+#
+proc trigger_mce_ue_addr {args} {
+ set addr [lindex [lindex $args 0] 1]
+ mysim trigger clear memory system rw $addr $addr
+ exc_mce 0x1 0x8000 0x1
+}
+
+proc inject_mce_ue_on_addr {addr} {
+ mysim trigger set memory system rw $addr $addr 1 "trigger_mce_ue_addr"
+}
+
+# inject and step over one instruction, and repeat.
+proc inject_mce_step { {nr 1} } {
+ for { set i 0 } { $i < $nr } { incr i 1 } {
+ inject_mce
+ s
+ }
+}
+
+# inject if RI is set and step over one instruction, and repeat.
+proc inject_mce_step_ri { {nr 1} } {
+ upvar #0 target_t t
+ upvar #0 target_c c
+ upvar #0 target_p p
+
+ set reserve_inject 1
+ set reserve_inject_skip 0
+ set reserve_counter 0
+
+ for { set i 0 } { $i < $nr } { incr i 1 } {
+ if { [expr [mysim cpu $p:$c:$t display spr msr] & 0x2] } {
+ # inject_mce
+ if { [mysim cpu $p:$c:$t display reservation] in { "none" } } {
+ inject_mce
+ mysim cpu $p:$c:$t set reservation none
+ if { $reserve_inject_skip } {
+ set reserve_inject 1
+ set reserve_inject_skip 0
+ }
+ } else {
+ if { $reserve_inject } {
+ inject_mce
+ mysim cpu $p:$c:$t set reservation none
+ set reserve_inject 0
+ } else {
+ set reserve_inject_skip 1
+ set reserve_counter [ expr $reserve_counter + 1 ]
+ if { $reserve_counter > 30 } {
+ mysim cpu $p:$c:$t set reservation none
+ }
+ }
+ }
+ }
+ s
+ }
+}
diff --git a/roms/skiboot/external/mambo/qtrace_utils.tcl b/roms/skiboot/external/mambo/qtrace_utils.tcl
new file mode 100644
index 000000000..bb20b31d3
--- /dev/null
+++ b/roms/skiboot/external/mambo/qtrace_utils.tcl
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+if { [file exists $env(LIB_DIR)/perf/qtrace.tcl] == 1} {
+ if { [catch {source $env(LIB_DIR)/perf/qtrace.tcl} issue ] } {
+ puts "QTrace not available: $issue"
+ }
+
+ proc start_qtrace { { qtfile qtrace.qt } } {
+ QTrace::Initialize p9 mysim
+ QTrace::Start $qtfile mysim
+ }
+
+ proc stop_qtrace { } {
+ QTrace::Stop mysim
+ }
+}
diff --git a/roms/skiboot/external/mambo/skiboot.tcl b/roms/skiboot/external/mambo/skiboot.tcl
new file mode 100644
index 000000000..0ecb55a77
--- /dev/null
+++ b/roms/skiboot/external/mambo/skiboot.tcl
@@ -0,0 +1,732 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# scripts to run skiboot (and a payload) with Mambo (otherwise known as the
+# POWER[89] Functional Simulator)
+#
+# Copyright 2014-2019 IBM Corp.
+
+# need to get images path defined early
+source $env(LIB_DIR)/ppc/util.tcl
+if { [file exists qtrace_utils.tcl] } then {
+ source qtrace_utils.tcl
+}
+
+#
+# Call tclreadline's Loop to move to friendlier
+# commandline if one exists
+#
+proc readline { } {
+ set readline [catch { package require tclreadline }]
+ if { $readline == 0 } {
+ ::tclreadline::Loop
+ }
+}
+
+proc mconfig { name env_name def } {
+ global mconf
+ global env
+
+ if { [info exists env($env_name)] } { set mconf($name) $env($env_name) }
+ if { ![info exists mconf($name)] } { set mconf($name) $def }
+}
+
+mconfig cpus CPUS 1
+mconfig threads THREADS 1
+mconfig memory MEM_SIZE 4G
+
+# Create multiple memory nodes? This will create a MEM_SIZE region
+# on each chip (CPUS above).
+mconfig numa MAMBO_NUMA 0
+
+# Should we stop on an illeagal instruction
+mconfig stop_on_ill MAMBO_STOP_ON_ILL false
+
+# Location of application binary to load
+mconfig boot_image SKIBOOT ../../skiboot.lid
+
+# Boot: Memory location to load boot_image, for binary or vmlinux
+mconfig boot_load MAMBO_BOOT_LOAD 0x30000000
+
+# Boot: Value of PC after loading, for binary or vmlinux
+mconfig boot_pc MAMBO_BOOT_PC 0x30000010
+
+# Payload: Allow for a Linux style ramdisk/initrd
+if { ![info exists env(SKIBOOT_ZIMAGE)] } {
+ error "Please set SKIBOOT_ZIMAGE to the path of your zImage.epapr"
+}
+mconfig payload PAYLOAD $env(SKIBOOT_ZIMAGE)
+
+mconfig linux_cmdline LINUX_CMDLINE ""
+
+# Paylod: Memory location for a Linux style ramdisk/initrd
+mconfig payload_addr PAYLOAD_ADDR 0x20000000;
+
+# FW: Where should ePAPR Flat Devtree Binary be loaded
+mconfig epapr_dt_addr EPAPR_DT_ADDR 0x1f00000;# place at 31M
+
+# Disk: Location of file to use a bogus disk 0
+mconfig rootdisk ROOTDISK none
+
+# Disk: File to use for re COW file: none or <file>
+mconfig rootdisk_cow MAMBO_ROOTDISK_COW none
+
+# Disk: COW method to use
+mconfig rootdisk_cow_method MAMBO_ROOTDISK_COW_METHOD newcow
+
+# Disk: COW hash size
+mconfig rootdisk_cow_hash MAMBO_ROOTDISK_COW_HASH 1024
+
+# Net: What type of networking: none, phea, bogus
+mconfig net MAMBO_NET none
+
+# Net: What MAC address to use
+mconfig net_mac MAMBO_NET_MAC 00:11:22:33:44:55
+
+# Net: What is the name of the tap device
+mconfig net_tapdev MAMBO_NET_TAPDEV "tap0"
+
+# Enable (default) or disable the "speculation-policy-favor-security" setting,
+# set to 0 to disable. When enabled it causes Linux's RFI flush to be enabled.
+mconfig speculation_policy_favor_security MAMBO_SPECULATION_POLICY_FAVOR_SECURITY 1
+
+# These values ~= P9N DD2.3, except for fw_count_cache_flush_assist=0 because it
+# exercises more kernel code.
+# See https://github.com/open-power/hostboot/blob/7ce2a9daac0ccf759376929b2ec40bbbc7ca3398/src/usr/hdat/hdatiplparms.H#L520
+mconfig needs_l1d_flush_msr_hv MAMBO_NEEDS_L1D_FLUSH_MSR_HV 1
+mconfig needs_l1d_flush_msr_pr MAMBO_NEEDS_L1D_FLUSH_MSR_PR 1
+mconfig fw_l1d_thread_split MAMBO_FW_L1D_THREAD_SPLIT 1
+mconfig needs_spec_barrier MAMBO_NEEDS_SPEC_BARRIER 1
+mconfig fw_bcctrl_serialized MAMBO_FW_BCCTRL_SERIALIZED 0
+mconfig fw_count_cache_disabled MAMBO_FW_COUNT_CACHE_DISABLED 0
+mconfig needs_count_cache_flush MAMBO_NEEDS_COUNT_CACHE_FLUSH 1
+mconfig fw_count_cache_flush_assist MAMBO_COUNT_CACHE_FLUSH_ASSIST 0
+mconfig inst_spec_barrier_ori31 MAMBO_INST_SPEC_BARRIER_ORI31 1
+mconfig inst_l1d_flush_trig2 MAMBO_INST_L1D_FLUSH_TRIG2 1
+mconfig inst_l1d_flush_ori30 MAMBO_INST_L1D_FLUSH_ORI30 0
+
+#
+# Create machine config
+#
+set default_config [display default_configure]
+define dup $default_config myconf
+myconf config cpus $mconf(cpus)
+myconf config processor/number_of_threads $mconf(threads)
+myconf config memory_size $mconf(memory)
+myconf config processor_option/ATTN_STOP true
+myconf config processor_option/stop_on_illegal_instruction $mconf(stop_on_ill)
+myconf config UART/0/enabled false
+myconf config SimpleUART/enabled false
+myconf config enable_rtas_support false
+myconf config processor/cpu_frequency 512M
+myconf config processor/timebase_frequency 1/1
+myconf config enable_pseries_nvram false
+myconf config machine_option/NO_RAM TRUE
+myconf config machine_option/NO_ROM TRUE
+myconf config machine_option/MEMORY_OVERFLOW FALSE
+
+if { $default_config == "PEGASUS" } {
+ # We need to be DD2 or greater on p8 for the HILE HID bit.
+ myconf config processor/initial/PVR 0x4b0201
+
+ if { $mconf(numa) } {
+ myconf config memory_region_id_shift 35
+ }
+}
+
+if { $default_config == "P9" } {
+ # PVR configured for POWER9 DD2.3 Scale out 24 Core (ie SMT4)
+ myconf config processor/initial/PVR 0x4e1203
+ myconf config processor/initial/SIM_CTRL1 0x4228301710000000
+
+ if { $mconf(numa) } {
+ myconf config memory_region_id_shift 45
+ }
+}
+
+if { $default_config == "P10" } {
+ # PVR configured for POWER10 DD1.0
+ myconf config processor/initial/PVR 0x800100
+ myconf config processor/initial/SIM_CTRL1 0xc228100400000000
+
+ if { $mconf(numa) } {
+ myconf config memory_region_id_shift 44
+ }
+}
+
+
+if { $mconf(numa) } {
+ myconf config memory_regions $mconf(cpus)
+}
+
+if { [info exists env(SKIBOOT_SIMCONF)] } {
+ source $env(SKIBOOT_SIMCONF)
+}
+
+define machine myconf mysim
+
+#
+# Include various utilities
+#
+
+source $env(LIB_DIR)/common/epapr.tcl
+if {![info exists of::encode_compat]} {
+ source $env(LIB_DIR)/common/openfirmware_utils.tcl
+}
+
+# Only source mambo_utils.tcl if it exists in the current directory. That
+# allows running mambo in another directory to the one skiboot.tcl is in.
+if { [file exists mambo_utils.tcl] } then {
+ source mambo_utils.tcl
+
+ if { [info exists env(USER_MAP)] } {
+ global user_symbol_map user_symbol_list
+
+ set fp [open $env(USER_MAP) r]
+ set user_symbol_map [read $fp]
+ set user_symbol_list [split $user_symbol_map "\n"]
+ close $fp
+ }
+
+ if { [info exists env(VMLINUX_MAP)] } {
+ global linux_symbol_map linux_symbol_list
+
+ set fp [open $env(VMLINUX_MAP) r]
+ set linux_symbol_map [read $fp]
+ set linux_symbol_list [split $linux_symbol_map "\n"]
+ close $fp
+ }
+
+ if { [info exists env(SKIBOOT_MAP)] } {
+ global skiboot_symbol_map skiboot_symbol_list
+
+ set fp [open $env(SKIBOOT_MAP) r]
+ set skiboot_symbol_map [read $fp]
+ set skiboot_symbol_list [split $skiboot_symbol_map "\n"]
+ close $fp
+ }
+}
+
+#
+# Instanciate xscom
+#
+
+set xscom_base 0x1A0000000000
+mysim xscom create $xscom_base
+
+# Setup bogus IO
+
+if { $mconf(rootdisk) != "none" } {
+ # Now load the bogus disk image
+ switch $mconf(rootdisk_cow) {
+ none {
+ mysim bogus disk init 0 $mconf(rootdisk) rw
+ puts "bogusdisk initialized for $mconf(rootdisk)"
+ }
+ default {
+ mysim bogus disk init 0 $mconf(rootdisk) \
+ $mconf(rootdisk_cow_method) \
+ $mconf(rootdisk_cow) $mconf(rootdisk_cow_hash)
+ }
+ }
+}
+switch $mconf(net) {
+ none {
+ puts "No network support selected"
+ }
+ bogus - bogusnet {
+ mysim bogus net init 0 $mconf(net_mac) $mconf(net_tapdev)
+ }
+ default {
+ error "Bad net \[none | bogus]: $mconf(net)"
+ }
+}
+
+# Device tree fixups
+
+set root_node [mysim of find_device "/"]
+
+mysim of addprop $root_node string "epapr-version" "ePAPR-1.0"
+mysim of setprop $root_node "compatible" "ibm,powernv"
+
+set cpus_node [mysim of find_device "/cpus"]
+mysim of addprop $cpus_node int "#address-cells" 1
+mysim of addprop $cpus_node int "#size-cells" 0
+
+set mem0_node [mysim of find_device "/memory@0"]
+mysim of addprop $mem0_node int "ibm,chip-id" 0
+
+set xscom_node [ mysim of addchild $root_node xscom [format %x $xscom_base]]
+set reg [list $xscom_base 0x10000000]
+mysim of addprop $xscom_node array64 "reg" reg
+mysim of addprop $xscom_node empty "scom-controller" ""
+mysim of addprop $xscom_node int "ibm,chip-id" 0
+mysim of addprop $xscom_node int "#address-cells" 1
+mysim of addprop $xscom_node int "#size-cells" 1
+set compat [list]
+lappend compat "ibm,xscom"
+lappend compat "ibm,power8-xscom"
+set compat [of::encode_compat $compat]
+mysim of addprop $xscom_node byte_array "compatible" $compat
+
+set chosen_node [mysim of find_device /chosen]
+set base_addr [list $mconf(payload_addr)]
+mysim of addprop $chosen_node array64 "kernel-base-address" base_addr
+
+# Load any initramfs
+set cpio_start 0x80000000
+set cpio_end $cpio_start
+set cpio_size 0
+if { [info exists env(SKIBOOT_INITRD)] } {
+
+ set cpios [split $env(SKIBOOT_INITRD) ","]
+
+ foreach cpio_file $cpios {
+ set cpio_file [string trim $cpio_file]
+ set cpio_size [file size $cpio_file]
+ mysim mcm 0 memory fread $cpio_end $cpio_size $cpio_file
+ set cpio_end [expr $cpio_end + $cpio_size]
+ # Linux requires cpios are 4 byte aligned
+ set cpio_end [expr $cpio_end + 3 & 0xfffffffffffffffc]
+ }
+
+ mysim of addprop $chosen_node int "linux,initrd-start" $cpio_start
+ mysim of addprop $chosen_node int "linux,initrd-end" $cpio_end
+}
+
+# Map persistent memory disks
+proc pmem_node_add { root start size } {
+ set start_hex [format %x $start]
+ set node [mysim of addchild $root "pmem@$start_hex" ""]
+ set reg [list [expr $start >> 32] [expr $start & 0xffffffff] [expr $size >> 32] [expr $size & 0xffffffff] ]
+ mysim of addprop $node array "reg" reg
+ mysim of addprop $node string "compatible" "pmem-region"
+ mysim of addprop $node empty "volatile" "1"
+ mysim of addprop $node int "ibm,chip-id" 0
+ return [expr $start + $size]
+}
+
+set pmem_files ""
+if { [info exists env(PMEM_DISK)] } {
+ set pmem_files [split $env(PMEM_DISK) ","]
+}
+set pmem_sizes ""
+if { [info exists env(PMEM_VOLATILE)] } {
+ set pmem_sizes [split $env(PMEM_VOLATILE) ","]
+}
+set pmem_modes ""
+if { [info exists env(PMEM_MODE)] } {
+ set pmem_modes [split $env(PMEM_MODE) ","]
+}
+set pmem_root [mysim of addchild $root_node "pmem" ""]
+mysim of addprop $pmem_root int "#address-cells" 2
+mysim of addprop $pmem_root int "#size-cells" 2
+mysim of addprop $pmem_root empty "ranges" ""
+# Start above where XICS normally is at 0x1A0000000000
+set pmem_start [expr 0x20000000000]
+set pmem_file_ix 0
+foreach pmem_file $pmem_files { # PMEM_DISK
+ set pmem_file [string trim $pmem_file]
+ set pmem_size [file size $pmem_file]
+ if { [expr [llength $pmem_modes] > $pmem_file_ix] } {
+ set pmem_mode [lindex $pmem_modes $pmem_file_ix]
+ } else {
+ set pmem_mode "rw"
+ }
+ if {[catch {mysim memory mmap $pmem_start $pmem_size $pmem_file $pmem_mode}]} {
+ puts "ERROR: pmem: 'mysim mmap' command needs newer mambo"
+ exit
+ }
+ set pmem_start [pmem_node_add $pmem_root $pmem_start $pmem_size]
+ set pmem_file_ix [expr $pmem_file_ix + 1]
+}
+foreach pmem_size $pmem_sizes { # PMEM_VOLATILE
+ set pmem_start [pmem_node_add $pmem_root $pmem_start $pmem_size]
+}
+
+
+# Default NVRAM is blank and will be formatted by Skiboot if no file is provided
+set fake_nvram_start $cpio_end
+set fake_nvram_size 0x40000
+# Load any fake NVRAM file if provided
+if { [info exists env(SKIBOOT_NVRAM)] } {
+ # Set up and write NVRAM file
+ set fake_nvram_file $env(SKIBOOT_NVRAM)
+ set fake_nvram_size [file size $fake_nvram_file]
+ mysim mcm 0 memory fread $fake_nvram_start $fake_nvram_size $fake_nvram_file
+}
+
+# Add device tree entry for NVRAM
+set reserved_memory [mysim of addchild $root_node "reserved-memory" ""]
+mysim of addprop $reserved_memory int "#size-cells" 2
+mysim of addprop $reserved_memory int "#address-cells" 2
+mysim of addprop $reserved_memory empty "ranges" ""
+
+set cvc_code_start [expr $fake_nvram_start + $fake_nvram_size]
+set cvc_code_end $cvc_code_start
+set cvc_code_size 0
+
+if { [info exists env(SKIBOOT_CVC_CODE)] } {
+ set cvc_file $env(SKIBOOT_CVC_CODE)
+
+ set cvc_code_size [file size $cvc_file]
+ mysim mcm 0 memory fread $cvc_code_start $cvc_code_size $cvc_file
+ set cvc_code_end [expr $cvc_code_start + $cvc_code_size]
+
+ # Set up Device Tree for Container Verification Code
+ set hb [mysim of addchild $root_node "ibm,hostboot" ""]
+ set hb_reserved_memory [mysim of addchild $hb "reserved-memory" ""]
+ mysim of addprop $hb_reserved_memory int "#address-cells" 2
+ mysim of addprop $hb_reserved_memory int "#size-cells" 2
+
+ set hb_cvc_code_node [mysim of addchild $hb_reserved_memory "ibm,secure-crypt-algo-code" [format %x $cvc_code_start]]
+ set reg [list $cvc_code_start $cvc_code_size]
+ mysim of addprop $hb_cvc_code_node array64 "reg" reg
+ mysim of addprop $hb_cvc_code_node empty "name" "ibm,secure-crypt-algo-code"
+
+ set cvc_code_node [mysim of addchild $reserved_memory "ibm,secure-crypt-algo-code" [format %x $cvc_code_start]]
+ set reg [list $cvc_code_start $cvc_code_size]
+ mysim of addprop $cvc_code_node array64 "reg" reg
+ mysim of addprop $cvc_code_node empty "name" "ibm,secure-crypt-algo-code"
+}
+
+set initramfs_res [mysim of addchild $reserved_memory "initramfs" ""]
+set reg [list $cpio_start $cpio_size ]
+mysim of addprop $initramfs_res array64 "reg" reg
+mysim of addprop $initramfs_res empty "name" "initramfs"
+
+set fake_nvram_node [mysim of addchild $reserved_memory "ibm,fake-nvram" ""]
+set reg [list $fake_nvram_start $fake_nvram_size ]
+mysim of addprop $fake_nvram_node array64 "reg" reg
+mysim of addprop $fake_nvram_node empty "name" "ibm,fake-nvram"
+
+set opal_node [mysim of addchild $root_node "ibm,opal" ""]
+
+# Allow P9/P10 to use all idle states
+if { $default_config == "P9" || $default_config == "P10" } {
+ set power_mgt_node [mysim of addchild $opal_node "power-mgt" ""]
+ mysim of addprop $power_mgt_node int "ibm,enabled-stop-levels" 0xffffffff
+}
+
+proc add_feature_node { parent name { value 1 } } {
+ if { $value != 1 } {
+ set value "disabled"
+ } else {
+ set value "enabled"
+ }
+ set np [mysim of addchild $parent $name ""]
+ mysim of addprop $np empty $value ""
+}
+
+set np [mysim of addchild $opal_node "fw-features" ""]
+add_feature_node $np "speculation-policy-favor-security" $mconf(speculation_policy_favor_security)
+add_feature_node $np "needs-l1d-flush-msr-hv-1-to-0" $mconf(needs_l1d_flush_msr_hv)
+add_feature_node $np "needs-l1d-flush-msr-pr-0-to-1" $mconf(needs_l1d_flush_msr_pr)
+add_feature_node $np "fw-l1d-thread-split" $mconf(fw_l1d_thread_split)
+add_feature_node $np "needs-spec-barrier-for-bound-checks" $mconf(needs_spec_barrier)
+add_feature_node $np "fw-bcctrl-serialized" $mconf(fw_bcctrl_serialized)
+add_feature_node $np "fw-count-cache-disabled" $mconf(fw_count_cache_disabled)
+add_feature_node $np "needs-count-cache-flush-on-context-switch" $mconf(needs_count_cache_flush)
+add_feature_node $np "fw-count-cache-flush-bcctr2,0,0" $mconf(fw_count_cache_flush_assist)
+add_feature_node $np "inst-spec-barrier-ori31,31,0" $mconf(inst_spec_barrier_ori31)
+add_feature_node $np "inst-l1d-flush-trig2" $mconf(inst_l1d_flush_trig2)
+add_feature_node $np "inst-l1d-flush-ori30,30,0" $mconf(inst_l1d_flush_ori30)
+
+
+# Init CPUs
+set pir 0
+for { set c 0 } { $c < $mconf(cpus) } { incr c } {
+ set cpu_node [mysim of find_device "/cpus/PowerPC@$pir"]
+ mysim of addprop $cpu_node int "ibm,pir" $pir
+ set reg [list 0x0000001c00000028 0xffffffffffffffff]
+ mysim of addprop $cpu_node array64 "ibm,processor-segment-sizes" reg
+
+ mysim of addprop $cpu_node int "ibm,chip-id" $c
+
+ # Create a chip node to tell skiboot to create another chip for this CPU.
+ # This bubbles up to Linux which will then see a new chip (aka nid).
+ # For chip 0 the xscom node above has already definied chip 0, so skip it.
+ if { $c > 0 } {
+ set node [mysim of addchild $root_node "mambo-chip" [format %x $c]]
+ mysim of addprop $node int "ibm,chip-id" $c
+ mysim of addprop $node string "compatible" "ibm,mambo-chip"
+
+ if { $mconf(numa) } {
+ set shift [myconf query memory_region_id_shift]
+ set addr [format %lx [expr (1 << $shift) * $c]]
+ set node [mysim of find_device "/memory@$addr"]
+ mysim of addprop $node int "ibm,chip-id" $c
+ }
+ }
+
+ set reg {}
+ lappend reg 0x0000000c 0x00000010 0x00000018 0x00000022
+ mysim of addprop $cpu_node array "ibm,processor-page-sizes" reg
+
+ set reg {}
+ lappend reg 0x0c 0x000 3 0x0c 0x0000 ;# 4K seg 4k pages
+ lappend reg 0x10 0x0007 ;# 4K seg 64k pages
+ lappend reg 0x18 0x0038 ;# 4K seg 16M pages
+ lappend reg 0x10 0x110 2 0x10 0x0001 ;# 64K seg 64k pages
+ lappend reg 0x18 0x0008 ;# 64K seg 16M pages
+ lappend reg 0x18 0x100 1 0x18 0x0000 ;# 16M seg 16M pages
+ lappend reg 0x22 0x120 1 0x22 0x0003 ;# 16G seg 16G pages
+ mysim of addprop $cpu_node array "ibm,segment-page-sizes" reg
+
+ if { $default_config == "P9" || $default_config == "P10" } {
+ # Set actual page size encodings
+ set reg {}
+ # 4K pages
+ lappend reg 0x0000000c
+ # 64K pages
+ lappend reg 0xa0000010
+ # 2M pages
+ lappend reg 0x20000015
+ # 1G pages
+ lappend reg 0x4000001e
+ mysim of addprop $cpu_node array "ibm,processor-radix-AP-encodings" reg
+
+ set reg {}
+ # POWER9 PAPR defines upto bytes 62-63
+ # POWER10 PAPR defines upto byte 64-65
+ # header + bytes 0-5
+ if { $default_config == "P9" } {
+ lappend reg 0x4000f63fc70080c0
+ } else {
+ lappend reg 0x4200f63fc70080c0
+ }
+ # bytes 6-13
+ lappend reg 0x8000000000000000
+ # bytes 14-21
+ lappend reg 0x0000800080008000
+ # bytes 22-29 22/23=TM
+ lappend reg 0x0000800080008000
+ # bytes 30-37
+ lappend reg 0x80008000C0008000
+ # bytes 38-45 40/41=radix
+ lappend reg 0x8000800080008000
+ # bytes 46-55
+ lappend reg 0x8000800080008000
+ # bytes 54-61 58/59=seg tbl
+ lappend reg 0x8000800080008000
+ # bytes 62-69 64/65=DAWR1(P10 only)
+ if { $default_config == "P9" } {
+ lappend reg 0x8000000000000000
+ } else {
+ lappend reg 0x8000800000000000
+ }
+ mysim of addprop $cpu_node array64 "ibm,pa-features" reg
+ } else {
+ set reg {}
+ lappend reg 0x6000f63fc70080c0
+ mysim of addprop $cpu_node array64 "ibm,pa-features" reg
+ }
+
+ set irqreg [list]
+ for { set t 0 } { $t < $mconf(threads) } { incr t } {
+ mysim mcm 0 cpu $c thread $t set spr pc $mconf(boot_pc)
+ mysim mcm 0 cpu $c thread $t set gpr 3 $mconf(epapr_dt_addr)
+ mysim mcm 0 cpu $c thread $t config_on
+ mysim mcm 0 cpu $c thread $t set spr pir $pir
+ lappend irqreg $pir
+ incr pir
+ }
+ mysim of addprop $cpu_node array "ibm,ppc-interrupt-server#s" irqreg
+}
+
+#Add In-Memory Collection Counter nodes
+if { $default_config == "P9" || $default_config == "P10" } {
+ #Add the base node "imc-counters"
+ set imc_c [mysim of addchild $root_node "imc-counters" ""]
+ mysim of addprop $imc_c string "compatible" "ibm,opal-in-memory-counters"
+ mysim of addprop $imc_c int "#address-cells" 1
+ mysim of addprop $imc_c int "#size-cells" 1
+ mysim of addprop $imc_c int "version-id" 1
+
+ #Add a common mcs event node
+ set mcs_et [mysim of addchild $imc_c "nest-mcs-events" ""]
+ mysim of addprop $mcs_et int "#address-cells" 1
+ mysim of addprop $mcs_et int "#size-cells" 1
+
+ #Add a event
+ set et [mysim of addchild $mcs_et event [format %x 0]]
+ mysim of addprop $et string "event-name" "64B_RD_OR_WR_DISP_PORT01"
+ mysim of addprop $et string "unit" "MiB/s"
+ mysim of addprop $et string "scale" "4"
+ mysim of addprop $et int "reg" 0
+
+ #Add a event
+ set et [mysim of addchild $mcs_et event [format %x 1]]
+ mysim of addprop $et string "event-name" "64B_WR_DISP_PORT01"
+ mysim of addprop $et string "unit" "MiB/s"
+ mysim of addprop $et int "reg" 40
+
+ #Add a event
+ set et [mysim of addchild $mcs_et event [format %x 2]]
+ mysim of addprop $et string "event-name" "64B_RD_DISP_PORT01"
+ mysim of addprop $et string "scale" "100"
+ mysim of addprop $et int "reg" 64
+
+ #Add a event
+ set et [mysim of addchild $mcs_et event [format %x 3]]
+ mysim of addprop $et string "event-name" "64B_XX_DISP_PORT01"
+ mysim of addprop $et int "reg" 32
+
+ #Add a mcs device node
+ set mcs_01 [mysim of addchild $imc_c "mcs01" ""]
+ mysim of addprop $mcs_01 string "compatible" "ibm,imc-counters"
+ mysim of addprop $mcs_01 string "events-prefix" "PM_MCS01_"
+ mysim of addprop $mcs_01 int "reg" 65536
+ mysim of addprop $mcs_01 int "size" 262144
+ mysim of addprop $mcs_01 int "offset" 1572864
+ mysim of addprop $mcs_01 int "events" $mcs_et
+ mysim of addprop $mcs_01 int "type" 16
+ mysim of addprop $mcs_01 string "unit" "KiB/s"
+ mysim of addprop $mcs_01 string "scale" "8"
+
+ #Add a common core event node
+ set ct_et [mysim of addchild $imc_c "core-thread-events" ""]
+ mysim of addprop $ct_et int "#address-cells" 1
+ mysim of addprop $ct_et int "#size-cells" 1
+
+ #Add a event
+ set cet [mysim of addchild $ct_et event [format %x 200]]
+ mysim of addprop $cet string "event-name" "0THRD_NON_IDLE_PCYC"
+ mysim of addprop $cet string "desc" "The number of processor cycles when all threads are idle"
+ mysim of addprop $cet int "reg" 200
+
+ #Add a core device node
+ set core [mysim of addchild $imc_c "core" ""]
+ mysim of addprop $core string "compatible" "ibm,imc-counters"
+ mysim of addprop $core string "events-prefix" "CPM_"
+ mysim of addprop $core int "reg" 24
+ mysim of addprop $core int "size" 8192
+ mysim of addprop $core string "scale" "512"
+ mysim of addprop $core int "events" $ct_et
+ mysim of addprop $core int "type" 4
+
+ #Add a thread device node
+ set thread [mysim of addchild $imc_c "thread" ""]
+ mysim of addprop $thread string "compatible" "ibm,imc-counters"
+ mysim of addprop $thread string "events-prefix" "CPM_"
+ mysim of addprop $thread int "reg" 24
+ mysim of addprop $thread int "size" 8192
+ mysim of addprop $thread string "scale" "512"
+ mysim of addprop $thread int "events" $ct_et
+ mysim of addprop $thread int "type" 1
+
+ #Add a common trace event node
+ set tr_et [mysim of addchild $imc_c "trace-events" ""]
+ mysim of addprop $tr_et int "#address-cells" 1
+ mysim of addprop $tr_et int "#size-cells" 1
+
+ #Add an event
+ set tr [mysim of addchild $tr_et event [format 10200000]]
+ mysim of addprop $tr string "event-name" "cycles"
+ mysim of addprop $tr string "desc" "Reference cycles"
+ mysim of addprop $tr int "reg" 0x10200000
+
+ #Add a trace device node
+ set trace [mysim of addchild $imc_c "trace" ""]
+ mysim of addprop $trace string "compatible" "ibm,imc-counters"
+ mysim of addprop $trace string "events-prefix" "trace_"
+ mysim of addprop $trace int "reg" 0
+ mysim of addprop $trace int "size" 262144
+ mysim of addprop $trace int "events" $tr_et
+ mysim of addprop $trace int "type" 2
+
+}
+
+mconfig enable_stb SKIBOOT_ENABLE_MAMBO_STB 0
+
+if { [info exists env(SKIBOOT_ENABLE_MAMBO_STB)] } {
+ set stb_node [ mysim of addchild $root_node "ibm,secureboot" "" ]
+
+ # For P8 we still use the softrom emulation
+ if { $default_config == "PEGASUS" || ! [info exists env(SKIBOOT_CVC_CODE)] } {
+ mysim of addprop $stb_node string "compatible" "ibm,secureboot-v1-softrom"
+ } else {
+ # on P9 we can use the real CVC
+ mysim of addprop $stb_node string "compatible" "ibm,secureboot-v2"
+ }
+# mysim of addprop $stb_node string "secure-enabled" ""
+ mysim of addprop $stb_node string "trusted-enabled" ""
+ mysim of addprop $stb_node string "hash-algo" "sha512"
+ mysim of addprop $stb_node int "hw-key-hash-size" 64
+ set hw_key_hash {}
+ lappend hw_key_hash 0x40d487ff
+ lappend hw_key_hash 0x7380ed6a
+ lappend hw_key_hash 0xd54775d5
+ lappend hw_key_hash 0x795fea0d
+ lappend hw_key_hash 0xe2f541fe
+ lappend hw_key_hash 0xa9db06b8
+ lappend hw_key_hash 0x466a42a3
+ lappend hw_key_hash 0x20e65f75
+ lappend hw_key_hash 0xb4866546
+ lappend hw_key_hash 0x0017d907
+ lappend hw_key_hash 0x515dc2a5
+ lappend hw_key_hash 0xf9fc5095
+ lappend hw_key_hash 0x4d6ee0c9
+ lappend hw_key_hash 0xb67d219d
+ lappend hw_key_hash 0xfb708535
+ lappend hw_key_hash 0x1d01d6d1
+ mysim of addprop $stb_node array "hw-key-hash" hw_key_hash
+
+ if { $default_config != "PEGASUS" && [info exists env(SKIBOOT_CVC_CODE)] } {
+ set cvc_node [ mysim of addchild $stb_node "ibm,cvc" "" ]
+ mysim of addprop $cvc_node string "compatible" "ibm,container-verification-code"
+ mysim of addprop $cvc_node int "memory-region" $hb_cvc_code_node
+
+ # I'm sure hardcoding these addresses will *never* cause us a problem...
+ set sha_node [ mysim of addchild $cvc_node "ibm,cvc-service" [format %x 0x40]]
+ mysim of addprop $sha_node string "name" "ibm,cvc-service"
+ mysim of addprop $sha_node string "compatible" "ibm,cvc-sha512"
+ mysim of addprop $sha_node int "reg" 0x40
+ mysim of addprop $sha_node int "version" 1
+
+ set verify_node [ mysim of addchild $cvc_node "ibm,cvc-service" [format %x 0x50]]
+ mysim of addprop $verify_node string "name" "ibm,cvc-service"
+ mysim of addprop $verify_node string "compatible" "ibm,cvc-verify"
+ mysim of addprop $verify_node int "reg" 0x50
+ mysim of addprop $verify_node int "version" 1
+ }
+}
+
+# Kernel command line args, appended to any from the device tree
+# e.g.: of::set_bootargs "xmon"
+#
+# Can be set from the environment by setting LINUX_CMDLINE.
+of::set_bootargs $mconf(linux_cmdline)
+
+# Load images
+
+set boot_size [file size $mconf(boot_image)]
+mysim memory fread $mconf(boot_load) $boot_size $mconf(boot_image)
+
+set payload_size [file size $mconf(payload)]
+mysim memory fread $mconf(payload_addr) $payload_size $mconf(payload)
+
+if { $payload_size > [expr $mconf(boot_load) - $mconf(payload_addr)] } {
+ error "vmlinux is too large, consider adjusting PAYLOAD_ADDR"
+}
+
+# Flatten it
+epapr::of2dtb mysim $mconf(epapr_dt_addr)
+
+# Set run speed
+mysim mode fastest
+
+if { [info exists env(GDB_SERVER)] } {
+ mysim debugger wait $env(GDB_SERVER)
+}
+
+if { [info exists env(SKIBOOT_AUTORUN)] } {
+ if [catch { mysim go }] {
+ readline
+ }
+} else {
+ readline
+}
+
+if { [info exists env(SKIBOOT_AUTORUN)] && $env(SKIBOOT_AUTORUN) == 2 } {
+ quit
+}
diff --git a/roms/skiboot/external/memboot/Makefile b/roms/skiboot/external/memboot/Makefile
new file mode 100644
index 000000000..da17b32d9
--- /dev/null
+++ b/roms/skiboot/external/memboot/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+all: memboot
+
+CFLAGS=-O2 -Wall
+
+memboot: memboot.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+.PHONY: clean
+clean:
+ rm -rf memboot
+
+.PHONY: distclean
+distclean: clean
+ rm -rf *.c~ *.h~ *.i *.s Makefile~
diff --git a/roms/skiboot/external/memboot/README b/roms/skiboot/external/memboot/README
new file mode 100644
index 000000000..20158edc5
--- /dev/null
+++ b/roms/skiboot/external/memboot/README
@@ -0,0 +1,24 @@
+memboot
+=======
+
+This is a small utility designed to be run on the ASPEED BMC used in
+some OpenPower systems. It allows you to boot the P8 host from the
+main system memory of the BMC rather than from the flash memory.
+
+This is mainly useful for firmware development as it makes it possible
+to try new firmware images without first having to write the new image
+to flash memory which can be a lengthy process.
+
+Building:
+---------
+
+CC=<arm toolchain> make
+
+Usage:
+-----
+
+./memboot <flash image> - Will cause the P8 host to boot from BMC
+ system memory at next power on.
+
+./memboot - Will boot the system from flash memory at next
+ power on.
diff --git a/roms/skiboot/external/memboot/memboot.c b/roms/skiboot/external/memboot/memboot.c
new file mode 100644
index 000000000..8298aed2c
--- /dev/null
+++ b/roms/skiboot/external/memboot/memboot.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * The old way of booting a temporary f/w image.
+ * These days, use mboxd on a BMC.
+ *
+ * Copyright 2013-2015 IBM Corp.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+/* Where to put the firmware image if booting from memory */
+#define MEM_IMG_BASE (0x5c000000)
+
+/* Start of flash memory if booting from flash */
+#define FLASH_IMG_BASE (0x30000000)
+
+/* LPC registers */
+#define LPC_BASE 0x1e789000
+#define LPC_HICR6 0x80
+#define LPC_HICR7 0x88
+#define LPC_HICR8 0x8c
+#define LPC_SCR0SIO 0x170
+
+#define MEMBOOT_SIO_VERSION_FLAG 0x42
+#define MEMBOOT_SIO_FLAG (0x10 << 8)
+
+uint32_t readl(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint32_t *)addr;
+}
+
+void writel(uint32_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint32_t *)addr = val;
+}
+
+void copy_flash_img(int mem_fd, int flash_fd, unsigned int size)
+{
+ static void *memimg, *fwimg;
+ size_t pagesize = getpagesize();
+
+ memimg = mmap(NULL, ((size/pagesize)+1)*pagesize,
+ PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, MEM_IMG_BASE);
+ if (memimg == MAP_FAILED) {
+ perror("Unable to map image destination memory");
+ exit(1);
+ }
+
+ fwimg = mmap(NULL,size, PROT_READ, MAP_SHARED, flash_fd, 0);
+ if (fwimg == MAP_FAILED) {
+ perror("Unable to open image source memory");
+ exit(1);
+ }
+
+ /* Copy boot image */
+ memcpy(memimg, fwimg, size);
+}
+
+void boot_firmware_image(int mem_fd, char *filename)
+{
+ int fw_fd;
+ struct stat st;
+
+ fw_fd = open(filename, O_RDONLY);
+ if (fw_fd < 0) {
+ perror("Unable to open flash image\n");
+ exit(1);
+ }
+
+ if (stat(filename, &st)) {
+ perror("Unable to determine size of firmware image");
+ exit(1);
+ }
+
+ if (st.st_size > 32*1024*1024) {
+ fprintf(stderr, "Flash too large (> 32MB)");
+ exit(1);
+ }
+
+ copy_flash_img(mem_fd, fw_fd, st.st_size);
+ close(fw_fd);
+}
+
+int main(int argc, char *argv[])
+{
+ int mem_fd;
+ void *lpcreg;
+ uint32_t lpc_scr0sio_val;
+ uint32_t lpc_hicr7_val = (FLASH_IMG_BASE | 0xe00);
+
+ if (argc > 2) {
+ printf("Usage: %s <flash image>\n", argv[0]);
+ exit(1);
+ }
+
+ mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
+ if (mem_fd < 0) {
+ perror("Unable to open /dev/mem");
+ exit(1);
+ }
+
+ lpcreg = mmap(NULL, getpagesize(),
+ PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, LPC_BASE);
+ if (lpcreg == MAP_FAILED) {
+ perror("Unable to map LPC register memory");
+ exit(1);
+ }
+
+ lpc_scr0sio_val = readl(lpcreg+LPC_SCR0SIO);
+ lpc_scr0sio_val &= ~0xff;
+ lpc_scr0sio_val |= MEMBOOT_SIO_VERSION_FLAG;
+ lpc_scr0sio_val &= ~MEMBOOT_SIO_FLAG;
+
+ if (argc == 2) {
+ boot_firmware_image(mem_fd, argv[1]);
+ lpc_hicr7_val = (MEM_IMG_BASE | 0xe00);
+
+ /* Set the boot mode scratch register to indicate a memboot */
+ lpc_scr0sio_val |= MEMBOOT_SIO_FLAG;
+ printf("Booting from memory after power cycle\n");
+ }
+
+ if (readl(lpcreg + LPC_HICR7) != lpc_hicr7_val) {
+ printf("Resetting LPC_HICR7 to 0x%x\n", lpc_hicr7_val);
+ writel(lpc_hicr7_val, lpcreg+LPC_HICR7);
+ }
+
+ /* Set the magic value */
+ writel(0x42, lpcreg+LPC_SCR0SIO);
+
+ writel(lpc_scr0sio_val, lpcreg+LPC_SCR0SIO);
+ printf("LPC_HICR7 = 0x%x\n", lpc_hicr7_val);
+ return 0;
+}
diff --git a/roms/skiboot/external/npu/run_procedure.sh b/roms/skiboot/external/npu/run_procedure.sh
new file mode 100755
index 000000000..e5df327e0
--- /dev/null
+++ b/roms/skiboot/external/npu/run_procedure.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Run an NPU Hardware procedure
+#
+# Copyright 2016 IBM Corp.
+
+
+function usage() {
+ echo -e "$0: run a NPU hardware procedure (requires root)\n"
+ echo -e "Usage: $0 <PCI bdfn> <procedure number>\n"
+ echo -e "Example: $0 0008:00:01.1 1"
+ echo -e "Procedures are documented in skiboot/doc/nvlink.rst"
+}
+
+function check_root() {
+ if [ "$(id -u)" != "0" ]; then
+ echo -e "Error: $0 must be run as root\n" 1>&2
+ exit 1
+ fi
+}
+
+function check_args() {
+ if [ "$#" -eq 0 ]; then
+ usage
+ exit 1
+ fi
+
+ if [ "$#" -gt 2 ]; then
+ echo -e "Error: too many arguments\n" 1>&2
+ usage
+ exit 1
+ fi
+
+ if [[ "$1" == "-h" || "$1" == "--help" ]]; then
+ usage
+ exit 0
+ fi
+
+ if ! [ "$2" -eq "$2" ] 2>/dev/null; then
+ echo -e "Procedure must be a decimal number\n" 1>&2
+ usage
+ exit 1
+ fi
+
+ if [[ "$2" -lt "0" || "$2" -gt "12" ]]; then
+ echo -e "Invalid procedure number\n" 1>&2
+ usage
+ exit 2
+ fi
+
+ pci_check=$(lspci -s $1)
+ if [[ $? -ne 0 || $pci_check == "" ]]; then
+ echo -e "Invalid PCI device\n" 1>&2
+ usage
+ exit 2
+ fi
+}
+
+function run_procedure() {
+ # Convert procedure number into hex
+ procedure=$(echo "obase=16; $2" | bc)
+
+ # Check the status register to make sure we can run a procedure
+ status=$(setpci -s $1 0x84.L)
+ if [[ $status == 8000000* ]]; then
+ echo "Procedure in progress, try again." 1>&2
+ echo "If that doesn't work, use procedure 0 to abort." 1>&2
+ exit 3
+ fi
+
+ # Start the procedure
+ setpci -s $1 0x88.L=0x0000000$procedure >/dev/null
+ if [ $? -ne 0 ]; then
+ echo "Control register write failed!" 1>&2
+ exit 3
+ fi
+
+ iterations=1
+ while [[ $(setpci -s $1 0x84.L) == 8000000* ]]; do
+ ((iterations++))
+ done
+
+ # Check again, procedure should be finished
+ status=$(setpci -s $1 0x84.L)
+
+ echo "Done in $iterations iteration(s)!"
+
+ if [[ $status == 40000000 ]]; then
+ echo "Procedure completed successfully."
+ exit 0
+ elif [[ $status == 40000001 ]]; then
+ echo "Transient failure, try again." 1>&2
+ exit 4
+ elif [[ $status == 40000002 ]]; then
+ echo "Permanent failure, reboot required?" 1>&2
+ exit 5
+ elif [[ $status == 40000003 ]]; then
+ echo "Procedure aborted." 1>&2
+ exit 6
+ elif [[ $status == 40000004 ]]; then
+ echo "Unsupported procedure." 1>&2
+ exit 7
+ fi
+}
+
+check_args "$@"
+check_root
+run_procedure "$1" "$2"
diff --git a/roms/skiboot/external/opal-prd/.gitignore b/roms/skiboot/external/opal-prd/.gitignore
new file mode 100644
index 000000000..d98511f93
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/.gitignore
@@ -0,0 +1,5 @@
+opal-prd
+/ccan
+/libflash
+/test/test_pnor
+common \ No newline at end of file
diff --git a/roms/skiboot/external/opal-prd/Makefile b/roms/skiboot/external/opal-prd/Makefile
new file mode 100644
index 000000000..fb9402f8f
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/Makefile
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: Apache-2.0
+CFLAGS += -m64 -Werror -Wall -g2 -ggdb
+LDFLAGS += -m64
+ASFLAGS = -m64
+CPPFLAGS += -I. -I../../include -I../../
+
+prefix = /usr/local/
+sbindir = $(prefix)/sbin
+datadir = $(prefix)/share
+mandir = $(datadir)/man
+
+all: links arch_links | opal-prd
+
+GET_ARCH = ../../external/common/get_arch.sh
+include ../../external/common/rules.mk
+
+LIBFLASH_OBJS = libflash-blocklevel.o libflash-libffs.o \
+ libflash-libflash.o libflash-ecc.o \
+ libflash-file.o
+
+OBJS = opal-prd.o thunk.o pnor.o i2c.o module.o version.o \
+ $(LIBFLASH_OBJS) common-arch_flash.o
+
+OPAL_PRD_VERSION ?= $(shell ../../make_version.sh opal-prd)
+
+ifdef KERNEL_DIR
+links += asm/opal-prd.h
+endif
+
+asm/opal-prd.h:
+ $(Q_MKDIR)mkdir -p asm
+ $(Q_LN)ln -sfr $(KERNEL_DIR)/arch/powerpc/include/uapi/asm/opal-prd.h \
+ asm/opal-prd.h
+
+%.o: %.c
+ $(Q_CC)$(COMPILE.c) $< -o $@
+
+$(LIBFLASH_OBJS): libflash-%.o : libflash/%.c
+ $(Q_CC)$(COMPILE.c) $< -o $@
+
+%.o: %.S
+ $(Q_CC)$(COMPILE.S) $< -o $@
+
+opal-prd: $(OBJS)
+ $(Q_LINK)$(LINK.o) -o $@ $^
+
+version.c: ../../make_version.sh .version
+ @(if [ "a$(OPAL_PRD_VERSION)" = "a" ]; then \
+ echo "#error You need to set OPAL_PRD_VERSION environment variable" > $@ ;\
+ else \
+ echo "const char version[] = \"$(OPAL_PRD_VERSION)\";" ;\
+ fi) > $@
+
+.PHONY: VERSION-always
+.version: VERSION-always
+ @echo $(OPAL_PRD_VERSION) > $@.tmp
+ @cmp -s $@ $@.tmp || cp $@.tmp $@
+ @rm -f $@.tmp
+
+test: links test/test_pnor
+
+test/test_pnor: test/test_pnor.o pnor.o $(LIBFLASH_OBJS) common-arch_flash.o
+ $(Q_LINK)$(LINK.o) -o $@ $^
+
+install: all
+ install -D opal-prd $(DESTDIR)$(sbindir)/opal-prd
+ install -D -m 0644 opal-prd.8 $(DESTDIR)$(mandir)/man8/opal-prd.8
+
+clean:
+ $(RM) *.[odsa] opal-prd
+ $(RM) test/*.[odsa] test/test_pnor
+
+distclean: clean
+ $(RM) -f $(LINKS) asm
+ $(RM) -f libflash ccan version.c .version common
diff --git a/roms/skiboot/external/opal-prd/config.h b/roms/skiboot/external/opal-prd/config.h
new file mode 100644
index 000000000..e7f377353
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/config.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * For CCAN
+ *
+ * Copyright 2015 IBM Corp.
+ */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF 1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN 0
+#define HAVE_LITTLE_ENDIAN 1
+#else
+#define HAVE_BIG_ENDIAN 1
+#define HAVE_LITTLE_ENDIAN 0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
diff --git a/roms/skiboot/external/opal-prd/hostboot-interface.h b/roms/skiboot/external/opal-prd/hostboot-interface.h
new file mode 100644
index 000000000..59f9ff444
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/hostboot-interface.h
@@ -0,0 +1,759 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Hostboot runtime interface
+ *
+ * Derived from src/include/runtime/interface.h in Hostboot
+ *
+ * Copyright 2013-2018 IBM Corp.
+ */
+#include <stdint.h>
+
+#define HOSTBOOT_RUNTIME_INTERFACE_VERSION 0x9002
+
+/** Memory error types defined for memory_error() interface. */
+enum MemoryError_t
+{
+ /** Hardware has reported a solid memory CE that is
+ * correctable, but continues to report errors on subsequent
+ * reads. A second CE on that cache line will result in memory
+ * UE. Therefore, it is advised to migrate off of the address
+ * range as soon as possible. */
+ MEMORY_ERROR_CE = 0,
+
+ /** Hardware has reported an uncorrectable error in memory
+ * (memory UE, channel failure, etc). The hypervisor should
+ * migrate any partitions off this address range as soon as
+ * possible. Note that these kind of errors will most likely
+ * result in partition failures. It is advised that the
+ * hypervisor waits some time for PRD to handle hardware
+ * attentions so that the hypervisor will know all areas of
+ * memory that are impacted by the failure. */
+ MEMORY_ERROR_UE = 1,
+
+ /** Firmware has predictively requested service on a part in the memory
+ * subsystem. The partitions may not have been affected, but it is
+ * advised to migrate off of the address range as soon as possible to
+ * avoid potential partition outages. */
+ MEMORY_ERROR_PREDICTIVE = 2,
+};
+
+/** Capability sets, for get_interface_capabilities */
+#define HBRT_CAPS_SET0_COMMON 0
+#define HBRT_CAPS_SET1_OPAL 1
+#define HBRT_CAPS_SET2_PHYP 2
+
+/* Capability flags */
+
+/**
+ * xscom_read and xscom_write return proper return codes on error.
+ * Previous implementations may have incorrectly ignored failures.
+ */
+#define HBRT_CAPS_OPAL_HAS_XSCOM_RC (1ul << 0)
+
+/**
+ * OPAL supports wakeup interface
+ */
+#define HBRT_CAPS_OPAL_HAS_WAKEUP_SUPPORT (1ul << 1)
+
+/**
+ * OPAL supports '2=clear all previous forces' argument
+ */
+#define HBRT_CAPS_OPAL_HAS_WAKEUP_CLEAR (1ul << 2)
+
+/********************/
+
+
+/**
+ * Load types for the load_pm_complex() interface
+ * HBRT_PM_LOAD: initial load of all lids/sections from scratch,
+ * preserve nothing
+ * HBRT_PM_RELOAD: concurrent reload of all lids/sections,
+ * but preserve runtime updates
+ */
+#define HBRT_PM_LOAD 0
+#define HBRT_PM_RELOAD 1
+
+/** Common return codes for scom_read(), scom_write(). */
+#define HBRT_RC_RANGE__SCOM 0x1000
+
+/* RC for a piberr is equal to 0x1000 plus the pib error value,
+ made into a negative */
+#define HBRT_RC_PIBERR_MASK (0x00000000u - 0x00001007u) /* 0xFFFF_EFF9 */
+
+#define HBRT_RC_PIBERR_001_BUSY (0x00000000u - 0x00001001u) /* 0xFFFF_EFFF */
+#define HBRT_RC_PIBERR_010_OFFLINE (0x00000000u - 0x00001002u) /* 0xFFFF_EFFE */
+#define HBRT_RC_PIBERR_011_PGOOD (0x00000000u - 0x00001003u) /* 0xFFFF_EFFD */
+#define HBRT_RC_PIBERR_100_INVALIDADDR (0x00000000u - 0x00001004u) /* 0xFFFF_EFFC */
+#define HBRT_RC_PIBERR_101_CLOCKERR (0x00000000u - 0x00001005u) /* 0xFFFF_EFFB */
+#define HBRT_RC_PIBERR_110_PARITYERR (0x00000000u - 0x00001006u) /* 0xFFFF_EFFA */
+#define HBRT_RC_PIBERR_111_TIMEOUT (0x00000000u - 0x00001007u) /* 0xFFFF_EFF9 */
+
+/* Memory channel failure caused an error out to buffer chip. */
+#define HBRT_RC_CHANNEL_FAILURE (0x00000000u - 0x00001008u) /* 0xFFFF_EFF8 */
+
+/* Any host-specific RCs will be this value or bigger */
+#define HBRT_RC_NEXT_OPEN_RC (0x00000000u - 0x00001009u) /* 0xFFFF_EFF7 */
+
+/********************/
+
+
+/** Common return codes for firmware_request(). -0x2000 */
+#define HBRT_RC_RANGE__FIRMWARE_REQUEST 0x2000
+
+/* FSP failed due to a a reset/reload. Only applicable when
+ * hostInterfaces::hbrt_fw_msg::io_type is set to
+ * HBRT_FW_MSG_HBRT_FSP_REQ
+ */
+#define HBRT_RC_FSPDEAD -8193 //0x2001
+
+/********************/
+
+
+/** Common return codes for wakeup(). -0x3000 */
+#define HBRT_RC_RANGE__WAKEUP 0x3000
+
+/* Wakeup was rejected because core was in checkstop statte */
+#define HBRT_RC_WAKEUP_INVALID_ON_CORE_XSTOP -12289 /* -0x3001 */
+
+/********************/
+
+
+/* FSP failed due to a a reset/reload. Only applicable when
+ * hostInterfaces::hbrt_fw_msg::io_type is set to
+ * HBRT_FW_MSG_HBRT_FSP_REQ
+ */
+#define HBRT_RC_FSPDEAD -8193 //0x2001
+
+/********************/
+
+
+
+struct host_interfaces {
+ /** Interface version. */
+ uint64_t interface_version;
+
+ /** Put a string to the console. */
+ void (*puts)(const char*);
+ /** Critical failure in runtime execution. */
+ void (*assert)(void);
+
+ /** OPTIONAL. Hint to environment that the page may be executed. */
+ int (*set_page_execute)(void*);
+
+ /** malloc */
+ void *(*malloc)(size_t);
+ /** free */
+ void (*free)(void*);
+ /** realloc */
+ void *(*realloc)(void*, size_t);
+
+ /**
+ * @brief Send a PEL to the FSP
+ * @param[in] plid Platform Log identifier
+ * @param[in] data size in bytes
+ * @param[in] pointer to data
+ * @return 0 on success else error code
+ * @platform FSP
+ */
+ int (*send_error_log)(uint32_t,uint32_t,void *);
+
+ /**
+ * @brief Scan communication read
+ * @param[in] chip_id (based on devtree defn)
+ * @param[in] address
+ * @param[in] pointer to 8-byte data buffer
+ * @return 0 on success else return code
+ * @platform FSP,OpenPOWER
+ */
+ int (*scom_read)(uint64_t, uint64_t, void*);
+
+ /**
+ * @brief Scan communication write
+ * @param[in] chip_id (based on devtree defn)
+ * @param[in] address
+ * @param[in] pointer to 8-byte data buffer
+ * @return 0 on success else return code
+ * @platform FSP,OpenPOWER
+ */
+ int (*scom_write)(uint64_t, uint64_t, const void *);
+
+ /**
+ * @brief Load a LID from PNOR, FSP, etc.
+ *
+ * @param[in] LID number.
+ * @param[out] Allocated buffer for LID.
+ * @param[out] Size of LID (in bytes).
+ *
+ * @return 0 on success, else RC.
+ * @platform FSP
+ */
+ int (*lid_load)(uint32_t lid, void **buf, size_t *len);
+
+ /**
+ * @brief Release memory from previously loaded LID.
+ *
+ * @param[in] Allocated buffer for LID to release.
+ *
+ * @return 0 on success, else RC.
+ * @platform FSP
+ */
+ int (*lid_unload)(void *buf);
+
+ /**
+ * @brief Get the address of a reserved memory region by its devtree
+ * name.
+ *
+ * @param[in] Devtree name (ex. "ibm,hbrt-vpd-image")
+ * @param[in] Devtree instance
+ * @return physical address of region (or NULL).
+ * @platform FSP,OpenPOWER
+ */
+ uint64_t (*get_reserved_mem)(const char *name, uint32_t instance);
+
+ /**
+ * @brief Force a core to be awake, or clear the force
+ * @param[in] i_core Core to wake up (pid)
+ * @param[in] i_mode 0=force awake
+ * 1=clear force
+ * 2=clear all previous forces
+ * @return rc non-zero on error
+ * @platform FSP
+ */
+ int (*wakeup)( uint32_t i_core, uint32_t i_mode );
+
+ /**
+ * @brief Delay/sleep for at least the time given
+ *
+ * The sleep time must be normalised; i_nano_seconds should be between
+ * 0 and 999999999.
+ *
+ * @param[in] seconds
+ * @param[in] nano seconds
+ * @platform FSP,OpenPOWER
+ */
+ void (*nanosleep)(uint64_t i_seconds, uint64_t i_nano_seconds);
+
+ /**
+ * @brief Report an OCC error to the host
+ * @param[in] Failing status that identifies the nature of the fail
+ * @param[in] Identifier that specifies the failing part
+ * @platform FSP
+ */
+ void (*report_occ_failure)( uint64_t i_status, uint64_t i_partId );
+
+ /**
+ * @brief Reads the clock value from a POSIX clock.
+ * @param[in] i_clkId - The clock ID to read.
+ * @param[out] o_tp - The timespec struct to store the clock value in.
+ *
+ * @return 0 or -(errno).
+ * @retval 0 - SUCCESS.
+ * @retval -EINVAL - Invalid clock requested.
+ * @retval -EFAULT - NULL ptr given for timespec struct.
+ *
+ * @platform OpenPOWER
+ */
+ int (*clock_gettime)( clockid_t i_clkId, struct timespec* o_tp );
+
+ /**
+ * @brief Read Pnor
+ * @param[in] i_proc: processor Id
+ * @param[in] i_partitionName: name of the partition to read
+ * @param[in] i_offset: offset within the partition
+ * @param[out] o_data: pointer to the data read
+ * @param[in] i_sizeBytes: size of data to read
+ * @retval rc - number of bytes read, or non-zero on error
+ * @platform OpenPOWER
+ */
+ int (*pnor_read) ( uint32_t i_proc, const char* i_partitionName,
+ uint64_t i_offset, void* o_data, size_t i_sizeBytes );
+
+ /**
+ * @brief Write to Pnor
+ * @param[in] i_proc: processor Id
+ * @param[in] i_partitionName: name of the partition to write
+ * @param[in] i_offset: offset within the partition
+ * @param[in] i_data: pointer to the data to write
+ * @param[in] i_sizeBytes: size of data to write
+ * @retval rc - number of bytes written, or non-zero on error
+ * @platform OpenPOWER
+ */
+ int (*pnor_write) ( uint32_t i_proc, const char* i_partitionName,
+ uint64_t i_offset, void* i_data, size_t i_sizeBytes );
+
+
+ /**
+ * i2c master description: chip, engine and port packed into
+ * a single 64-bit argument
+ *
+ * ---------------------------------------------------
+ * | chip | reserved | eng | port |
+ * | (32) | (16) | (8) | (8) |
+ * ---------------------------------------------------
+ */
+#define HBRT_I2C_MASTER_CHIP_SHIFT 32
+#define HBRT_I2C_MASTER_CHIP_MASK (0xfffffffful << 32)
+#define HBRT_I2C_MASTER_ENGINE_SHIFT 8
+#define HBRT_I2C_MASTER_ENGINE_MASK (0xfful << 8)
+#define HBRT_I2C_MASTER_PORT_SHIFT 0
+#define HBRT_I2C_MASTER_PORT_MASK (0xfful)
+
+ /**
+ * @brief Read data from an i2c device
+ * @param[in] i_master - Chip/engine/port of i2c bus
+ * @param[in] i_devAddr - I2C address of device
+ * @param[in] i_offsetSize - Length of offset (in bytes)
+ * @param[in] i_offset - Offset within device to read
+ * @param[in] i_length - Number of bytes to read
+ * @param[out] o_data - Data that was read
+ * @return 0 on success else return code
+ * @platform OpenPOWER
+ */
+ int (*i2c_read)( uint64_t i_master, uint16_t i_devAddr,
+ uint32_t i_offsetSize, uint32_t i_offset,
+ uint32_t i_length, void* o_data );
+
+ /**
+ * @brief Write data to an i2c device
+ * @param[in] i_master - Chip/engine/port of i2c bus
+ * @param[in] i_devAddr - I2C address of device
+ * @param[in] i_offsetSize - Length of offset (in bytes)
+ * @param[in] i_offset - Offset within device to write
+ * @param[in] i_length - Number of bytes to write
+ * @param[in] Data to write
+ * @return 0 on success else return code
+ * @platform OpenPOWER
+ */
+ int (*i2c_write)( uint64_t i_master, uint16_t i_devAddr,
+ uint32_t i_offsetSize, uint32_t i_offset,
+ uint32_t i_length, void* i_data );
+
+ /**
+ * Perform an IPMI transaction
+ * @param[in] netfn The IPMI netfn byte
+ * @param[in] cmd The IPMI cmd byte
+ * @param[in] tx_buf The IPMI packet to send to the host
+ * @param[in] tx_size The number of bytes, to send
+ * @param[in] rx_buf A buffer to be populated with the IPMI
+ * response.
+ * @param[inout] rx_size The allocated size of the rx buffer on
+ * input, updated to the size of the response on output.
+ * This should always begin with the IPMI completion
+ * code.
+ */
+ int (*ipmi_msg)(uint8_t netfn, uint8_t cmd,
+ void *tx_buf, size_t tx_size,
+ void *rx_buf, size_t *rx_size);
+
+
+ /**
+ * @brief Hardware has reported a memory error. This function requests
+ * the hypervisor to remove the all addresses within the address range
+ * given (including endpoints) from the available memory space.
+ *
+ * It is understood that the hypervisor may not be able to immediately
+ * deallocate the memory because it may be in use by a partition.
+ * Therefore, the hypervisor should cache all requests and deallocate
+ * the memory once it has been freed.
+ *
+ * @param i_startAddr The beginning address of the range.
+ * @param i_endAddr The end address of the range.
+ * @param i_errorType See enum MemoryError_t.
+ *
+ * @return 0 if the request is successfully received. Any value other
+ * than 0 on failure. The hypervisor should cache the request and
+ * return immediately. It should not wait for the request to be
+ * applied. See note above.
+ */
+ int (*memory_error)( uint64_t i_startAddr, uint64_t i_endAddr,
+ enum MemoryError_t i_errorType );
+
+ /**
+ * @brief Query the prd infrastructure for interface capabilities.
+ * @param[in] i_set The set of capabilites to retrieve
+ *
+ * @return a bitmask containing the relevant HBRT_CAPS_* for
+ * this implementation and the specified set.
+ */
+ uint64_t (*get_interface_capabilities)(uint64_t i_set);
+
+ /**
+ * @brief Map a physical address space into usable memory
+ * @note Repeated calls to map the same memory should not return an
+ * error
+ * @param[in] i_physMem Physical address
+ * @param[in] i_bytes Number of bytes to map in
+ * @return NULL on error, else pointer to usable memory
+ * @platform FSP, OpenPOWER
+ */
+ void* (*map_phys_mem)(uint64_t i_physMem, size_t i_bytes);
+
+ /**
+ * @brief Unmap a physical address space from usable memory
+ * @param[in] i_ptr Previously mapped pointer
+ * @return 0 on success, else RC
+ * @platform FSP, OpenPOWER
+ */
+ int (*unmap_phys_mem)(void* i_ptr);
+
+ /**
+ * @brief Modify the SCOM restore section of the HCODE image with the
+ * given register data
+ *
+ * @note The Hypervisor should perform the following actions:
+ * - insert the data into the HCODE image (p9_stop_api)
+ *
+ * @pre HBRT is responsible for enabling special wakeup on the
+ * associated core(s) before calling this interface
+ *
+ * @param i_chipId processor chip ID
+ * plus ID type, always proc (0x0)
+ * @param i_section runtime section to update
+ * (passthru to pore_gen_scom)
+ * @param i_operation type of operation to perform
+ * (passthru to pore_gen_scom)
+ * @param i_scomAddr fully qualified scom address
+ * @param i_scomData data for operation
+ *
+ * @return 0 if the request is successfully received.
+ * Any value other than 0 on failure.
+ * @platform FSP, OpenPOWER
+ */
+ int (*hcode_scom_update)(uint64_t i_chipId,
+ uint32_t i_section,
+ uint32_t i_operation,
+ uint64_t i_scomAddr,
+ uint64_t i_scomData);
+
+ /**
+ * @brief Send a request to firmware, and receive a response
+ * @details
+ * req_len bytes are sent to runtime firmware, and resp_len
+ * bytes received in response.
+ *
+ * Both req and resp are allocated by the caller. If resp_len
+ * is not large enough to contain the full response, an error
+ * is returned.
+ *
+ * @param[in] i_reqLen length of request data
+ * @param[in] i_req request data
+ * @param[inout] o_respLen in: size of request data buffer
+ * out: length of request data
+ * @param[in] o_resp response data
+ * @return 0 on success, else RC
+ * @platform FSP, OpenPOWER
+ */
+ int (*firmware_request)(uint64_t i_reqLen, void *i_req,
+ uint64_t *o_respLen, void *o_resp);
+
+ /* Reserve some space for future growth. */
+ void (*reserved[27])(void);
+};
+
+struct runtime_interfaces {
+ /** Interface version. */
+ uint64_t interface_version;
+
+ /**
+ * @brief Execute CxxTests that may be contained in the image.
+ *
+ * @param[in] - Pointer to CxxTestStats structure for results reporting.
+ */
+ void (*cxxtestExecute)(void *);
+
+ /**
+ * @brief Get a list of lids numbers of the lids known to HostBoot
+ *
+ * @param[out] o_num - the number of lids in the list
+ * @return a pointer to the list
+ * @platform FSP
+ */
+ const uint32_t * (*get_lid_list)(size_t * o_num);
+
+ /**
+ * @brief Load OCC Image and common data into mainstore, also setup OCC
+ * BARSs
+ *
+ * @param[in] i_homer_addr_phys - The physical mainstore address of the
+ * start of the HOMER image
+ * @param[in] i_homer_addr_va - Virtual memory address of the HOMER
+ * image
+ * @param[in] i_common_addr_phys - The physical mainstore address
+ * of the OCC common area.
+ * @param[in] i_common_addr_va - Virtual memory address of the common
+ * area
+ * @param[in] i_chip - The HW chip id (XSCOM chip ID)
+ * @return 0 on success else return code
+ * @platform FSP
+ */
+ int (*occ_load)(uint64_t i_homer_addr_phys,
+ uint64_t i_homer_addr_va,
+ uint64_t i_common_addr_phys,
+ uint64_t i_common_addr_va,
+ uint64_t i_chip);
+
+ /**
+ * @brief Start OCC on all chips, by module
+ *
+ * @param[in] i_chip - Array of functional HW chip ids
+ * @Note The caller must include a complete modules worth of chips
+ * @param[in] i_num_chips - Number of chips in the array
+ * @return 0 on success else return code
+ * @platform FSP
+ */
+ int (*occ_start)(uint64_t* i_chip, size_t i_num_chips);
+
+ /**
+ * @brief Stop OCC hold OCCs in reset
+ *
+ * @param[in] i_chip - Array of functional HW chip ids
+ * @Note The caller must include a complete modules worth of chips
+ * @param[in] i_num_chips - Number of chips in the array
+ * @return 0 on success else return code
+ * @platform FSP
+ */
+ int (*occ_stop)(uint64_t* i_chip, size_t i_num_chips);
+
+ /**
+ * @brief Notify HTMGT that an OCC has an error to report
+ *
+ * @details When an OCC has encountered an error that it wants to
+ * be reported, this interface will be called to trigger
+ * HTMGT to collect and commit the error.
+ *
+ * @param[i] i_chipId - Id of processor with failing OCC
+ * @platform OpenPower
+ */
+ void (*process_occ_error) (uint64_t i_chipId);
+
+ /**
+ * @brief Enable chip attentions
+ *
+ * @return 0 on success else return code
+ * @platform OpenPower
+ */
+ int (*enable_attns)(void);
+
+ /**
+ * @brief Disable chip attentions
+ *
+ * @return 0 on success else return code
+ * @platform OpenPower
+ */
+ int (*disable_attns)(void);
+
+ /**
+ * @brief handle chip attentions
+ *
+ * @param[in] i_proc - processor chip id at attention XSCOM chip id
+ * based on devtree defn
+ * @param[in] i_ipollStatus - processor chip Ipoll status
+ * @param[in] i_ipollMask - processor chip Ipoll mask
+ * @return 0 on success else return code
+ * @platform OpenPower
+ */
+ int (*handle_attns)(uint64_t i_proc, uint64_t i_ipollStatus,
+ uint64_t i_ipollMask);
+
+ /**
+ * @brief Notify HTMGT that an OCC has failed and needs to be reset
+ *
+ * @details When BMC detects an OCC failure that requires a reset,
+ * this interface will be called to trigger the OCC reset. HTMGT
+ * maintains a reset count and if there are additional resets
+ * available, the OCCs get reset/reloaded. If the recovery attempts
+ * have been exhauseted or the OCC fails to go active, an unrecoverable
+ * error will be logged and the system will remain in safe mode.
+ *
+ * @param[in] i_chipId ChipID which identifies the OCC reporting an
+ * error
+ * @platform OpenPOWER
+ */
+ void (*process_occ_reset)(uint64_t i_chipId);
+
+ /**
+ * @brief Change the OCC state
+ *
+ * @details This is a blocking call that will change the OCC state.
+ * The OCCs will only actuate (update processor frequency/ voltages)
+ * when in Active state. The OCC will only be monitoring/observing
+ * when in Observation state.
+ *
+ * @note When the OCCs are initially started, the state will
+ * default to Active. If the state is changed to Observation, that
+ * state will be retained until the next IPL. (If the OCC would get
+ * reset, it would return to the last requested state)
+ *
+ * @param[in] i_occActivation set to true to move OCC to Active state
+ * or false to move OCC to Observation state
+ *
+ * @return 0 on success, or return code if the state did not change.
+ * @platform OpenPower
+ */
+ int (*enable_occ_actuation)(bool i_occActivation);
+
+ /**
+ * @brief Apply a set of attribute overrides
+ *
+ * @param[in] pointer to binary override data
+ * @param[in] length of override data (bytes)
+ * @returns 0 on success, or return code if the command failed
+ *
+ * @platform OpenPower
+ */
+ int (*apply_attr_override)(uint8_t *i_data, size_t size);
+
+ /**
+ * @brief Send a pass-through command to HTMGT
+ *
+ * @details This is a blocking call that will send a command to
+ * HTMGT.
+ *
+ * @note If o_rspLength is returned with a non-zero value, the
+ * data at the o_rspData should be dumped to stdout in a
+ * hex dump format.
+ * @note The maximum response data returned will be 4096 bytes
+ *
+ * @param[in] i_cmdLength number of bytes in pass-thru command data
+ * @param[in] *i_cmdData pointer to pass-thru command data
+ * @param[out] *o_rspLength pointer to number of bytes returned in
+ * o_rspData
+ * @param[out] *o_rspData pointer to a 4096 byte buffer that will
+ * contain the response data from the command
+ *
+ * @returns 0 on success, or return code if the command failed
+ * @platform OpenPower
+ */
+ int (*mfg_htmgt_pass_thru)(uint16_t i_cmdLength, uint8_t *i_cmdData,
+ uint16_t *o_rspLength, uint8_t *o_rspData);
+
+ /**
+ * @brief Execute an arbitrary command inside hostboot runtime
+ * @param[in] Number of arguments (standard C args)
+ * @param[in] Array of argument values (standard C args)
+ * @param[out] Response message (NULL terminated), memory allocated
+ * by hbrt, if o_outString is NULL then no response will
+ * be sent
+ * @return 0 on success, else error code
+ */
+ int (*run_command)(int argc, const char **argv, char **o_outString);
+
+ /**
+ * @brief Verify integrity of a secure container
+ * @param[in] i_pContainer Pointer to a valid secure container,
+ * Must not be NULL. Container is assumed to be stripped of any
+ * ECC and must start with a valid secure header (which contains
+ * the container size information)
+ * @param[in] i_pHwKeyHash Pointer to a valid hardware keys' hash.
+ * Must not be NULL.
+ * @param[in] i_hwKeyHashSize Size of the hardware keys' hash.
+ * A value which incorrectly states the size of the hardware keys'
+ * hash will be detected as a verification error or worse, an
+ * illegal memory access. Must not be 0.
+ * @note If secureboot is compiled out, the function pointer will be
+ * set to NULL. If caller's secureboot support is compiled in and
+ * secureboot is enabled by policy, then caller should treat a NULL
+ * pointer as a verification failure.
+ * @return Integer error code indicating success or failure
+ * @retval 0 Container verified correctly
+ * @retval !0 API error or otherwise failed to verify container
+ * @platform FSP, OpenPOWER
+ */
+ int (*verify_container)(const void *i_pContainer,
+ const void *i_pHwKeyHash,
+ size_t i_hwKeyHashSize);
+
+ /**
+ * @brief SBE message passing
+ *
+ * @details
+ * This is a blocking call that will pass an SBE message
+ * with a pass-through command through HBRT to code that
+ * will process the command and provide a response.
+ *
+ * @param[in] i_procChipId Chip ID of the processor whose SBE is
+ * passing the message and sent the interrupt
+ *
+ * @return 0 on success, or return code if the command failed
+ * @platform FSP, OpenPOWER
+ */
+ int (*sbe_message_passing)(uint32_t i_procChipId);
+
+ /**
+ * @brief Load OCC/HCODE images into mainstore
+ *
+ * @param[in] i_chip the HW chip id (XSCOM chip ID)
+ * @param[in] i_homer_addr the physical mainstore address of the
+ * start of the HOMER image,
+ * @param[in] i_occ_common_addr the physical mainstore address of the
+ * OCC common area, 8MB, used for
+ * OCC-OCC communication (1 per node)
+ * @param[in] i_mode selects initial load vs concurrent
+ * reloads
+ * HBRT_PM_LOAD:
+ * load all lids/sections from scratch,
+ * preserve nothing
+ * HBRT_PM_RELOAD:
+ * reload all lids/sections,
+ * but preserve runtime updates
+ * @return 0 on success else return code
+ * @platform FSP, OpenPOWER
+ */
+ int (*load_pm_complex)(uint64_t i_chip,
+ uint64_t i_homer_addr,
+ uint64_t i_occ_common_addr,
+ uint32_t i_mode);
+
+ /**
+ * @brief Start OCC/HCODE on the specified chip
+ * @param[in] i_chip the HW chip id
+ * @return 0 on success else return code
+ * @platform FSP, OpenPOWER
+ */
+ int (*start_pm_complex)(uint64_t i_chip);
+
+ /**
+ * @brief Reset OCC/HCODE on the specified chip
+ * @param[in] i_chip the HW chip id
+ * @return 0 on success else return code
+ * @platform FSP, OpenPOWER
+ */
+ int (*reset_pm_complex)(uint64_t i_chip);
+
+ /**
+ * @brief Query the IPOLL event mask supported by HBRT
+ *
+ * @details This call allows the wrapper application to query
+ * the ipoll event mask to set when the HBRT instance is running. Bits
+ * that are *set* in this bitmask represent events that will be
+ * forwarded to the handle_attn() callback.
+ *
+ * @return The IPOLL event bits to enable during HBRT execution
+ * @platform FSP, OpenPOWER
+ */
+ uint64_t (*get_ipoll_events)(void);
+
+ /**
+ * @brief Receive an async notification from firmware
+ * @param[in] i_len length of notification data
+ * @param[in] i_data notification data
+ * @platform FSP, OpenPOWER
+ */
+ void (*firmware_notify)(uint64_t len, void *data);
+
+ /**
+ * @brief Prepare for HBRT concurrent code update
+ *
+ * @details This call allows the Host to inform HBRT that a concurrent
+ * code update has been initiated. HBRT then prepares updated targeting
+ * data for use by the updated HBRT code.
+ *
+ * @return 0 on success else return code
+ * @platform FSP
+ */
+ int (*prepare_hbrt_update)( void );
+
+
+ /* Reserve some space for future growth. */
+ void (*reserved[21])(void);
+};
diff --git a/roms/skiboot/external/opal-prd/i2c.c b/roms/skiboot/external/opal-prd/i2c.c
new file mode 100644
index 000000000..f4c7f27a4
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/i2c.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * I2C operations for opal-prd
+ *
+ * Copyright 2013-2018 IBM Corp.
+ */
+
+#define _GNU_SOURCE /* for aspritnf */
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <ccan/list/list.h>
+
+#include "opal-prd.h"
+#include "module.h"
+#include "i2c.h"
+
+struct i2c_bus {
+ uint32_t chip_id;
+ uint8_t engine;
+ uint8_t port;
+ const char *devpath;
+ int fd;
+ struct list_node link;
+};
+
+static struct list_head bus_list = LIST_HEAD_INIT(bus_list);
+
+static int i2c_get_dev(uint32_t chip, uint8_t eng, uint8_t port, uint16_t dev)
+{
+ struct i2c_bus *b, *bus = NULL;
+
+ list_for_each(&bus_list, b, link) {
+ if (b->chip_id == chip && b->engine == eng && b->port == port) {
+ bus = b;
+ break;
+ }
+ }
+ if (!bus) {
+ pr_log(LOG_WARNING, "I2C: Bus %08x/%d/%d not found",
+ chip, eng, port);
+ return -1;
+ }
+ if (bus->fd < 0) {
+ bus->fd = open(bus->devpath, O_RDWR);
+ if (bus->fd < 0) {
+ pr_log(LOG_ERR, "I2C: Failed to open %s: %m",
+ bus->devpath);
+ return -1;
+ }
+ }
+
+ /* XXX We could use the I2C_SLAVE ioctl to check if the device
+ * is currently in use by a kernel driver...
+ */
+
+ return bus->fd;
+}
+
+int i2c_read(uint32_t chip_id, uint8_t engine, uint8_t port,
+ uint16_t device, uint32_t offset_size, uint32_t offset,
+ uint32_t length, void* data)
+{
+ struct i2c_rdwr_ioctl_data ioargs;
+ struct i2c_msg msgs[2];
+ uint8_t obuf[4];
+ int fd, i, midx = 0;
+
+ if (offset_size > 4) {
+ pr_log(LOG_ERR, "I2C: Invalid write offset_size %d",
+ offset_size);
+ return -1;
+ }
+ fd = i2c_get_dev(chip_id, engine, port, device);
+ if (fd == -1)
+ return -1;
+
+ /* If we have an offset, build a message for it */
+ if (offset_size) {
+ /* The offset has a variable size so let's handle this properly
+ * as it has to be laid out in memory MSB first
+ */
+ for (i = 0; i < offset_size; i++)
+ obuf[i] = offset >> (8 * (offset_size - i - 1));
+ msgs[0].addr = device;
+ msgs[0].flags = 0;
+ msgs[0].buf = obuf;
+ msgs[0].len = offset_size;
+ midx = 1;
+ }
+
+ /* Build the message for the data portion */
+ msgs[midx].addr = device;
+ msgs[midx].flags = I2C_M_RD;
+ msgs[midx].buf = data;
+ msgs[midx].len = length;
+ midx++;
+
+ ioargs.msgs = msgs;
+ ioargs.nmsgs = midx;
+ if (ioctl(fd, I2C_RDWR, &ioargs) < 0) {
+ pr_log(LOG_ERR, "I2C: Read error: %m");
+ return -1;
+ }
+ pr_debug("I2C: Read from %08x:%d:%d@%02x+0x%x %d bytes ok",
+ chip_id, engine, port, device, offset_size ? offset : 0, length);
+
+ return 0;
+}
+
+int i2c_write(uint32_t chip_id, uint8_t engine, uint8_t port,
+ uint16_t device, uint32_t offset_size, uint32_t offset,
+ uint32_t length, void* data)
+{
+ struct i2c_rdwr_ioctl_data ioargs;
+ struct i2c_msg msg;
+ int fd, size, i, rc;
+ uint8_t *buf;
+
+ if (offset_size > 4) {
+ pr_log(LOG_ERR, "I2C: Invalid write offset_size %d",
+ offset_size);
+ return -1;
+ }
+ fd = i2c_get_dev(chip_id, engine, port, device);
+ if (fd == -1)
+ return -1;
+
+ /* Not all kernel driver versions support breaking up a write into
+ * two components (offset, data), so we coalesce them first and
+ * issue a single write. The offset is laid out in BE format.
+ */
+ size = offset_size + length;
+ buf = malloc(size);
+ if (!buf) {
+ pr_log(LOG_ERR, "I2C: Out of memory");
+ return -1;
+ }
+
+ /* The offset has a variable size so let's handle this properly
+ * as it has to be laid out in memory MSB first
+ */
+ for (i = 0; i < offset_size; i++)
+ buf[i] = offset >> (8 * (offset_size - i - 1));
+
+ /* Copy the remaining data */
+ memcpy(buf + offset_size, data, length);
+
+ /* Build the message */
+ msg.addr = device;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = size;
+ ioargs.msgs = &msg;
+ ioargs.nmsgs = 1;
+ rc = ioctl(fd, I2C_RDWR, &ioargs);
+ free(buf);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "I2C: Write error: %m");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void i2c_add_bus(uint32_t chip, uint32_t engine, uint32_t port,
+ const char *devname)
+{
+ struct i2c_bus *b = malloc(sizeof(struct i2c_bus));
+ char *dn;
+
+ if (asprintf(&dn, "/dev/%s", devname) < 0) {
+ pr_log(LOG_ERR, "I2C: Error creating devpath for %s: %m",
+ devname);
+ free(b);
+ return;
+ }
+
+ memset(b, 0, sizeof(*b));
+ b->chip_id = chip;
+ b->engine = engine;
+ b->port = port;
+ b->devpath = dn;
+ b->fd = -1;
+ list_add(&bus_list, &b->link);
+}
+
+void i2c_init(void)
+{
+#define SYSFS "/sys" /* XXX Find it ? */
+ DIR *devsdir;
+ struct dirent *devent;
+ char dpath[PATH_MAX];
+ char busname[256];
+ char *s;
+ FILE *f;
+ unsigned int chip, engine, port;
+
+ /* Ensure i2c-dev is loaded */
+ insert_module("i2c-dev");
+
+ /* Get directory of i2c char devs in sysfs */
+ devsdir = opendir(SYSFS "/class/i2c-dev");
+ if (!devsdir) {
+ pr_log(LOG_ERR, "I2C: Error opening "
+ SYSFS "/class/i2c-dev: %m");
+ return;
+ }
+ while ((devent = readdir(devsdir)) != NULL) {
+ if (!strcmp(devent->d_name, "."))
+ continue;
+ if (!strcmp(devent->d_name, ".."))
+ continue;
+
+ /* Get bus name */
+ sprintf(dpath, SYSFS "/class/i2c-dev/%s/name", devent->d_name);
+ f = fopen(dpath, "r");
+ if (!f) {
+ pr_log(LOG_NOTICE, "I2C: Can't open %s: %m, skipping.",
+ dpath);
+ continue;
+ }
+ s = fgets(busname, sizeof(busname), f);
+ fclose(f);
+ if (!s) {
+ pr_log(LOG_NOTICE, "Failed to read %s, skipping.",
+ dpath);
+ continue;
+ }
+
+ /* Is this a P8 or Centaur i2c bus ? No -> move on */
+ if (strncmp(s, "p8_", 3) == 0)
+ sscanf(s, "p8_%x_e%dp%d", &chip, &engine, &port);
+ else if (strncmp(s, "cen_", 4) == 0)
+ sscanf(s, "cen_%x_e%dp%d", &chip, &engine, &port);
+ else
+ continue;
+
+ pr_log(LOG_INFO, "I2C: Found Chip: %08x engine %d port %d",
+ chip, engine, port);
+ i2c_add_bus(chip, engine, port, devent->d_name);
+ }
+ closedir(devsdir);
+}
+
diff --git a/roms/skiboot/external/opal-prd/i2c.h b/roms/skiboot/external/opal-prd/i2c.h
new file mode 100644
index 000000000..ee73f906e
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/i2c.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015 IBM Corp */
+
+#ifndef __I2C_H
+#define __I2C_H
+
+int i2c_read(uint32_t chip_id, uint8_t engine, uint8_t port,
+ uint16_t device, uint32_t offset_size, uint32_t offset,
+ uint32_t length, void* data);
+
+int i2c_write(uint32_t chip_id, uint8_t engine, uint8_t port,
+ uint16_t device, uint32_t offset_size, uint32_t offset,
+ uint32_t length, void* data);
+
+void i2c_init(void);
+
+#endif /* __I2c_H */
diff --git a/roms/skiboot/external/opal-prd/module.c b/roms/skiboot/external/opal-prd/module.c
new file mode 100644
index 000000000..9c9c0af84
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/module.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Load kernel modules needed for opal-prd
+ *
+ * Copyright 2015 IBM Corp.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "module.h"
+#include "opal-prd.h"
+
+int insert_module(const char *module)
+{
+ int status;
+ pid_t pid;
+
+ pid = fork();
+ if (!pid) {
+ execlp("modprobe", "modprobe", module, NULL);
+ err(EXIT_FAILURE, "Failed to run modprobe");
+ }
+
+ pid = waitpid(pid, &status, 0);
+ if (pid < 0) {
+ pr_log(LOG_ERR, "KMOD: waitpid failed for "
+ "modprobe process: %m");
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ pr_log(LOG_WARNING, "KMOD: modprobe %s: process didn't "
+ "exit cleanly", module);
+ return -1;
+ }
+
+ if (WEXITSTATUS(status) != 0) {
+ pr_log(LOG_WARNING, "KMOD: modprobe %s failed, status %d",
+ module, WEXITSTATUS(status));
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/roms/skiboot/external/opal-prd/module.h b/roms/skiboot/external/opal-prd/module.h
new file mode 100644
index 000000000..2771bf0e8
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/module.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015 IBM Corp. */
+
+#ifndef MODULES_H
+#define MODULES_H
+
+int insert_module(const char *module);
+
+#endif /* MODULES_H */
+
diff --git a/roms/skiboot/external/opal-prd/opal-prd.8 b/roms/skiboot/external/opal-prd/opal-prd.8
new file mode 100644
index 000000000..72027f5c5
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/opal-prd.8
@@ -0,0 +1,78 @@
+.TH opal-prd 8 ""
+.SH NAME
+opal-prd \- Processor recovery diagnostics daemon for OpenPower hardware
+.SH SYNOPSIS
+.SY opal\-prd
+.OP \-\-debug
+.OP \-\-file <hbrt\-image>
+.OP \-\-pnor <device>
+.OP daemon
+.
+.SY opal\-prd
+.I <command>
+.OP arguments
+.YS
+.SH DESCRIPTION
+\fBopal-prd\fP is a daemon that listens for hardware diagnostic events (by
+listening on the \fI/dev/opal-prd\fP device), and executes firmware-provided
+executable code to handle these events. Only one instance of the daemon
+can be running at a time.
+
+.PP
+If no arguments are provided, or the \fIdaemon\fP command is used, then
+the PRD daemon will be started and will listen for incoming hardware events.
+Generally, this will be run from init as a background service, and not
+be run as a user or with user interaction.
+
+.PP
+\fIopal-prd\fP will log to syslog, using the LOG_DAEMON facility. Messages will
+use the string "opal-prd" for their syslog ident.
+
+.PP
+For debugging, run the daemon with the \fI--debug\fP and \fI--stdio\fP
+options. This will log to stdout (instead of syslog), and enable extra
+debugging information.
+
+.PP
+A running opal-prd daemon will also listen for control messages from
+the user; these are sent using the same \fIopal-prd\fP executable, run
+with the <command> argument:
+
+.RS
+ opal-prd <command> [arguments]
+.RE
+
+.PP
+Note that the daemon must be running in the background here, as a separate
+process.
+
+.PP
+Currently, there's one command available, 'occ', for controlling the
+on-chip-controllers. That has 3 possible sub-commands: \fIreset\fP,
+\fIenable\fP, and \fIdisable\fP.
+
+.SH OPTIONS
+.TP
+\fB\-\-debug\fR
+verbose logging for debug information
+.TP
+\fB\-\-pnor\fR DEVICE
+use PNOR MTD device
+.TP
+\fB\-\-file\fR FILE
+use FILE for hostboot runtime code (instead of code
+exported by firmware)
+.TP
+\fB\-\-stdio\fR
+log to stdio, instead of syslog
+
+.SH FILES
+.PD 0
+.B /dev/opal-prd
+.br
+.B /run/opal-prd-control
+.br
+.PD
+
+.SH "SEE ALSO"
+syslog(3)
diff --git a/roms/skiboot/external/opal-prd/opal-prd.c b/roms/skiboot/external/opal-prd/opal-prd.c
new file mode 100644
index 000000000..1c610da4c
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/opal-prd.c
@@ -0,0 +1,2799 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * OPAL Processor Runtime Diagnostics (PRD)
+ * Runs Hostboot RunTime (HBRT) code in a userspace wrapper
+ *
+ * Firmware in userspace? Brilliant!
+ *
+ * Copyright 2014-2019 IBM Corp.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <time.h>
+#include <poll.h>
+#include <signal.h>
+#include <dirent.h>
+
+#include <endian.h>
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <linux/ipmi.h>
+#include <linux/limits.h>
+
+#include <asm/opal-prd.h>
+
+#include <opal-api.h>
+#include <types.h>
+
+#include <ccan/list/list.h>
+
+#include "opal-prd.h"
+#include "hostboot-interface.h"
+#include "module.h"
+#include "pnor.h"
+#include "i2c.h"
+
+struct prd_range {
+ const char *name;
+ uint64_t physaddr;
+ uint64_t size;
+ void *buf;
+ bool multiple;
+ uint32_t instance;
+};
+
+struct prd_msgq_item {
+ struct list_node list;
+ struct opal_prd_msg msg;
+};
+
+struct opal_prd_ctx {
+ int fd;
+ int socket;
+ struct opal_prd_info info;
+ struct prd_range *ranges;
+ int n_ranges;
+ bool fw_range_instances;
+ long page_size;
+ void *code_addr;
+ size_t code_size;
+ bool debug;
+ struct pnor pnor;
+ char *hbrt_file_name;
+ bool use_syslog;
+ bool expert_mode;
+ struct list_head msgq;
+ struct opal_prd_msg *msg;
+ size_t msg_alloc_len;
+ void (*vlog)(int, const char *, va_list);
+};
+
+enum control_msg_type {
+ CONTROL_MSG_ENABLE_OCCS = 0x00,
+ CONTROL_MSG_DISABLE_OCCS = 0x01,
+ CONTROL_MSG_TEMP_OCC_RESET = 0x02,
+ CONTROL_MSG_TEMP_OCC_ERROR = 0x03,
+ CONTROL_MSG_ATTR_OVERRIDE = 0x04,
+ CONTROL_MSG_HTMGT_PASSTHRU = 0x05,
+ CONTROL_MSG_RUN_CMD = 0x30,
+};
+
+struct control_msg {
+ enum control_msg_type type;
+ int response;
+ union {
+ struct {
+ unsigned int argc;
+ } run_cmd;
+ struct {
+ uint64_t chip;
+ } occ_reset;
+ struct {
+ uint64_t chip;
+ } occ_error;
+ };
+ unsigned int data_len;
+ unsigned char data[];
+
+};
+
+#define MAX_CONTROL_MSG_BUF 4096
+
+static struct opal_prd_ctx *ctx;
+
+static const char *opal_prd_devnode = "/dev/opal-prd";
+static const char *opal_prd_socket = "/run/opal-prd-control";
+static const char *hbrt_code_region_name = "hbrt-code-image";
+static const char *hbrt_code_region_name_ibm = "ibm,hbrt-code-image";
+static const int opal_prd_version = 1;
+static uint64_t opal_prd_ipoll = 0xf000000000000000;
+
+static const int max_msgq_len = 16;
+
+static const char *ipmi_devnode = "/dev/ipmi0";
+static const int ipmi_timeout_ms = 5000;
+
+static const char *devicetree_base =
+ "/sys/firmware/devicetree/base";
+
+/* Memory error handling */
+static const char *mem_offline_soft =
+ "/sys/devices/system/memory/soft_offline_page";
+static const char *mem_offline_hard =
+ "/sys/devices/system/memory/hard_offline_page";
+
+#define ADDR_STRING_SZ 20 /* Hold %16lx */
+
+/* This is the "real" HBRT call table for calling into HBRT as
+ * provided by it. It will be used by the assembly thunk
+ */
+struct runtime_interfaces *hservice_runtime;
+struct runtime_interfaces hservice_runtime_fixed;
+
+/* This is the callback table provided by assembly code */
+extern struct host_interfaces hinterface;
+
+/* Create opd to call hostservice init */
+struct func_desc {
+ void *addr;
+ void *toc;
+} hbrt_entry;
+
+static int nr_chips;
+static u64 chips[256];
+
+static int read_prd_msg(struct opal_prd_ctx *ctx);
+
+static struct prd_range *find_range(const char *name, uint32_t instance)
+{
+ struct prd_range *range;
+ unsigned int i;
+
+ for (i = 0; i < ctx->n_ranges; i++) {
+ range = &ctx->ranges[i];
+
+ if (strcmp(range->name, name))
+ continue;
+
+ if (range->multiple && range->instance != instance)
+ continue;
+
+ return range;
+ }
+
+ return NULL;
+}
+
+static void pr_log_stdio(int priority, const char *fmt, va_list ap)
+{
+ if (!ctx->debug && priority >= LOG_DEBUG)
+ return;
+
+ vprintf(fmt, ap);
+ printf("\n");
+
+ if (ctx->debug)
+ fflush(stdout);
+}
+
+/* standard logging prefixes:
+ * HBRT: Messages from hostboot runtime code
+ * FW: Interactions with OPAL firmware
+ * IMAGE: HBRT image loading
+ * MEM: Memory failure interface
+ * SCOM: Chip SCOM interface
+ * IPMI: IPMI interface
+ * PNOR: PNOR interface
+ * I2C: i2c interface
+ * PM: PM/OCC interface
+ * CTRL: User-triggered control events
+ * KMOD: Kernel module functions
+ */
+
+void pr_log(int priority, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ ctx->vlog(priority, fmt, ap);
+ va_end(ap);
+}
+
+static void pr_log_nocall(const char *name)
+{
+ pr_log(LOG_WARNING, "HBRT: Call %s not provided", name);
+}
+
+static void hexdump(const uint8_t *data, uint32_t len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ printf("\n");
+ else if(i % 4 == 0)
+ printf(" ");
+
+ printf("%02x", data[i]);
+ }
+
+ printf("\n");
+}
+
+static void pr_log_daemon_init(void)
+{
+ if (ctx->use_syslog) {
+ openlog("opal-prd", LOG_NDELAY, LOG_DAEMON);
+ ctx->vlog = vsyslog;
+ }
+}
+
+/* Check service processor type */
+static bool is_fsp_system(void)
+{
+ bool fsp_system = true;
+ char *path;
+ int rc;
+
+ rc = asprintf(&path, "%s/fsps", devicetree_base);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: error creating '/fsps' path %m");
+ return false;
+ }
+
+ if (access(path, F_OK))
+ fsp_system = false;
+
+ free(path);
+ return fsp_system;
+}
+
+/**
+ * ABI check that we can't perform at build-time: we want to ensure that the
+ * layout of struct host_interfaces matches that defined in the thunk.
+ */
+static void check_abi(void)
+{
+ extern unsigned char __hinterface_start, __hinterface_pad,
+ __hinterface_end;
+
+ /* ensure our struct size matches the thunk definition */
+ assert((&__hinterface_end - &__hinterface_start)
+ == sizeof(struct host_interfaces));
+
+ /* ensure the padding layout is as expected */
+ assert((void *)&__hinterface_start == (void *)&hinterface);
+ assert((void *)&__hinterface_pad == (void *)&hinterface.reserved);
+}
+
+/* HBRT init wrappers */
+extern struct runtime_interfaces *call_hbrt_init(struct host_interfaces *);
+
+/* hservice Call wrappers */
+
+extern void call_cxxtestExecute(void *);
+extern int call_handle_attns(uint64_t i_proc,
+ uint64_t i_ipollStatus,
+ uint64_t i_ipollMask);
+extern void call_process_occ_error (uint64_t i_chipId);
+extern int call_enable_attns(void);
+extern int call_enable_occ_actuation(bool i_occActivation);
+extern void call_process_occ_reset(uint64_t i_chipId);
+extern int call_mfg_htmgt_pass_thru(uint16_t i_cmdLength, uint8_t *i_cmdData,
+ uint16_t *o_rspLength, uint8_t *o_rspData);
+extern int call_apply_attr_override(uint8_t *i_data, size_t size);
+extern int call_run_command(int argc, const char **argv, char **o_outString);
+extern int call_sbe_message_passing(uint32_t i_chipId);
+extern uint64_t call_get_ipoll_events(void);
+extern int call_firmware_notify(uint64_t len, void *data);
+extern int call_reset_pm_complex(uint64_t chip);
+extern int call_load_pm_complex(u64 chip, u64 homer, u64 occ_common, u32 mode);
+extern int call_start_pm_complex(u64 chip);
+
+void hservice_puts(const char *str)
+{
+ int priority = LOG_INFO;
+
+ /* Interpret the 2-character ERR_MRK/FAIL_MRK/WARN_MRK prefixes that
+ * may be present on HBRT log messages, and bump the log priority as
+ * appropriate.
+ */
+ if (strlen(str) >= 2 && str[1] == '>') {
+ switch (str[0]) {
+ case 'E':
+ case 'F':
+ priority = LOG_ERR;
+ break;
+ case 'W':
+ priority = LOG_WARNING;
+ break;
+ }
+ }
+
+ pr_log(priority, "HBRT: %s", str);
+}
+
+void hservice_assert(void)
+{
+ pr_log(LOG_ERR, "HBRT: Failed assertion! exiting.");
+ exit(EXIT_FAILURE);
+}
+
+void *hservice_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+void hservice_free(void *ptr)
+{
+ free(ptr);
+}
+
+void *hservice_realloc(void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+int hservice_scom_read(uint64_t chip_id, uint64_t addr, void *buf)
+{
+ int rc;
+ struct opal_prd_scom scom;
+
+ scom.chip = chip_id;
+ scom.addr = addr;
+
+ rc = ioctl(ctx->fd, OPAL_PRD_SCOM_READ, &scom);
+ if (rc) {
+ pr_log(LOG_ERR, "SCOM: ioctl read(chip 0x%lx, addr 0x%lx) "
+ "failed: %m", chip_id, addr);
+ return 0;
+ }
+ rc = (int)scom.rc;
+
+ pr_debug("SCOM: read: chip 0x%lx, addr 0x%lx, val 0x%lx, rc %d",
+ chip_id, addr, scom.data, rc);
+
+ *(uint64_t *)buf = htobe64(scom.data);
+
+ return rc;
+}
+
+int hservice_scom_write(uint64_t chip_id, uint64_t addr,
+ const void *buf)
+{
+ int rc;
+ struct opal_prd_scom scom;
+
+ scom.chip = chip_id;
+ scom.addr = addr;
+ scom.data = be64toh(*(uint64_t *)buf);
+
+ rc = ioctl(ctx->fd, OPAL_PRD_SCOM_WRITE, &scom);
+ if (rc) {
+ pr_log(LOG_ERR, "SCOM: ioctl write(chip 0x%lx, addr 0x%lx) "
+ "failed: %m", chip_id, addr);
+ return 0;
+ }
+ rc = (int)scom.rc;
+
+ pr_debug("SCOM: write: chip 0x%lx, addr 0x%lx, val 0x%lx, rc %d",
+ chip_id, addr, scom.data, rc);
+
+ return rc;
+}
+
+uint64_t hservice_get_reserved_mem(const char *name, uint32_t instance)
+{
+ struct prd_range *range;
+
+ pr_debug("IMAGE: hservice_get_reserved_mem: %s, %d", name, instance);
+
+ range = find_range(name, instance);
+ if (!range) {
+ pr_log(LOG_WARNING, "IMAGE: get_reserved_mem: "
+ "no such range %s", name);
+ return 0;
+ }
+
+ if (!range->buf) {
+ uint64_t align_physaddr, offset;
+
+ pr_debug("IMAGE: Mapping 0x%016lx 0x%08lx %s[%d]",
+ range->physaddr, range->size,
+ range->name, range->instance);
+
+ align_physaddr = range->physaddr & ~(ctx->page_size-1);
+ offset = range->physaddr & (ctx->page_size-1);
+ range->buf = mmap(NULL, range->size, PROT_WRITE | PROT_READ,
+ MAP_SHARED, ctx->fd, align_physaddr);
+
+ if (range->buf == MAP_FAILED)
+ pr_log(LOG_ERR,
+ "IMAGE: mmap of %s[%d](0x%016lx) failed: %m",
+ name, instance, range->physaddr);
+ else
+ range->buf += offset;
+ }
+
+ if (range->buf == MAP_FAILED) {
+ pr_log(LOG_WARNING,
+ "IMAGE: get_reserved_mem: %s[%d] has no vaddr",
+ name, instance);
+ return 0;
+ }
+
+ pr_debug(
+ "IMAGE: hservice_get_reserved_mem: %s[%d](0x%016lx) address %p",
+ name, range->instance, range->physaddr,
+ range->buf);
+
+ return (uint64_t)range->buf;
+}
+
+void hservice_nanosleep(uint64_t i_seconds, uint64_t i_nano_seconds)
+{
+ const struct timespec ns = {
+ .tv_sec = i_seconds,
+ .tv_nsec = i_nano_seconds
+ };
+
+ nanosleep(&ns, NULL);
+}
+
+int hservice_set_page_execute(void *addr)
+{
+ /* HBRT calls this on the pages that are already being executed,
+ * nothing to do here */
+ return -1;
+}
+
+int hservice_clock_gettime(clockid_t i_clkId, struct timespec *o_tp)
+{
+ struct timespec tmp;
+ int rc;
+
+ rc = clock_gettime(i_clkId, &tmp);
+ if (rc)
+ return rc;
+
+ o_tp->tv_sec = htobe64(tmp.tv_sec);
+ o_tp->tv_nsec = htobe64(tmp.tv_nsec);
+
+ return 0;
+}
+
+int hservice_pnor_read(uint32_t i_proc, const char* i_partitionName,
+ uint64_t i_offset, void* o_data, size_t i_sizeBytes)
+{
+ return pnor_operation(&ctx->pnor, i_partitionName, i_offset, o_data,
+ i_sizeBytes, PNOR_OP_READ);
+}
+
+int hservice_pnor_write(uint32_t i_proc, const char* i_partitionName,
+ uint64_t i_offset, void* o_data, size_t i_sizeBytes)
+{
+ return pnor_operation(&ctx->pnor, i_partitionName, i_offset, o_data,
+ i_sizeBytes, PNOR_OP_WRITE);
+}
+
+int hservice_i2c_read(uint64_t i_master, uint16_t i_devAddr,
+ uint32_t i_offsetSize, uint32_t i_offset,
+ uint32_t i_length, void* o_data)
+{
+ uint32_t chip_id;
+ uint8_t engine, port;
+
+ chip_id = (i_master & HBRT_I2C_MASTER_CHIP_MASK) >>
+ HBRT_I2C_MASTER_CHIP_SHIFT;
+ engine = (i_master & HBRT_I2C_MASTER_ENGINE_MASK) >>
+ HBRT_I2C_MASTER_ENGINE_SHIFT;
+ port = (i_master & HBRT_I2C_MASTER_PORT_MASK) >>
+ HBRT_I2C_MASTER_PORT_SHIFT;
+ return i2c_read(chip_id, engine, port, i_devAddr, i_offsetSize,
+ i_offset, i_length, o_data);
+}
+
+int hservice_i2c_write(uint64_t i_master, uint16_t i_devAddr,
+ uint32_t i_offsetSize, uint32_t i_offset,
+ uint32_t i_length, void* i_data)
+{
+ uint32_t chip_id;
+ uint8_t engine, port;
+
+ chip_id = (i_master & HBRT_I2C_MASTER_CHIP_MASK) >>
+ HBRT_I2C_MASTER_CHIP_SHIFT;
+ engine = (i_master & HBRT_I2C_MASTER_ENGINE_MASK) >>
+ HBRT_I2C_MASTER_ENGINE_SHIFT;
+ port = (i_master & HBRT_I2C_MASTER_PORT_MASK) >>
+ HBRT_I2C_MASTER_PORT_SHIFT;
+ return i2c_write(chip_id, engine, port, i_devAddr, i_offsetSize,
+ i_offset, i_length, i_data);
+}
+
+int hservice_wakeup(u32 core, u32 mode)
+{
+ struct opal_prd_msg msg;
+
+ msg.hdr.type = OPAL_PRD_MSG_TYPE_CORE_SPECIAL_WAKEUP;
+ msg.hdr.size = htobe16(sizeof(msg));
+ msg.spl_wakeup.core = htobe32(core);
+ msg.spl_wakeup.mode = htobe32(mode);
+
+ if (write(ctx->fd, &msg, sizeof(msg)) != sizeof(msg)) {
+ pr_log(LOG_ERR, "FW: Failed to send CORE_SPECIAL_WAKEUP msg %x : %m\n",
+ core);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void pnor_load_module(struct opal_prd_ctx *ctx)
+{
+ insert_module("powernv_flash");
+}
+
+static void ipmi_init(struct opal_prd_ctx *ctx)
+{
+ insert_module("ipmi_devintf");
+}
+
+static int ipmi_send(int fd, uint8_t netfn, uint8_t cmd, long seq,
+ uint8_t *buf, size_t len)
+{
+ struct ipmi_system_interface_addr addr;
+ struct ipmi_req req;
+ int rc;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ addr.channel = IPMI_BMC_CHANNEL;
+
+ memset(&req, 0, sizeof(req));
+ req.addr = (unsigned char *)&addr;
+ req.addr_len = sizeof(addr);
+
+ req.msgid = seq;
+ req.msg.netfn = netfn;
+ req.msg.cmd = cmd;
+ req.msg.data = buf;
+ req.msg.data_len = len;
+
+ rc = ioctl(fd, IPMICTL_SEND_COMMAND, &req);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+static int ipmi_recv(int fd, uint8_t *netfn, uint8_t *cmd, long *seq,
+ uint8_t *buf, size_t *len)
+{
+ struct ipmi_recv recv;
+ struct ipmi_addr addr;
+ int rc;
+
+ recv.addr = (unsigned char *)&addr;
+ recv.addr_len = sizeof(addr);
+ recv.msg.data = buf;
+ recv.msg.data_len = *len;
+
+ rc = ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv);
+ if (rc < 0 && errno != EMSGSIZE) {
+ pr_log(LOG_WARNING, "IPMI: recv (%zd bytes) failed: %m", *len);
+ return -1;
+ } else if (rc < 0 && errno == EMSGSIZE) {
+ pr_log(LOG_NOTICE, "IPMI: truncated message (netfn %d, cmd %d, "
+ "size %zd), continuing anyway",
+ recv.msg.netfn, recv.msg.cmd, *len);
+ }
+
+ *netfn = recv.msg.netfn;
+ *cmd = recv.msg.cmd;
+ *seq = recv.msgid;
+ *len = recv.msg.data_len;
+
+ return 0;
+}
+
+int hservice_ipmi_msg(uint8_t netfn, uint8_t cmd,
+ void *tx_buf, size_t tx_size,
+ void *rx_buf, size_t *rx_size)
+{
+ struct timeval start, now, delta;
+ struct pollfd pollfds[1];
+ static long seq;
+ size_t size;
+ int rc, fd;
+
+ size = be64toh(*rx_size);
+
+ fd = open(ipmi_devnode, O_RDWR);
+ if (fd < 0) {
+ pr_log(LOG_WARNING, "IPMI: Failed to open IPMI device %s: %m",
+ ipmi_devnode);
+ return -1;
+ }
+
+ seq++;
+ pr_debug("IPMI: sending %zd bytes (netfn 0x%02x, cmd 0x%02x)",
+ tx_size, netfn, cmd);
+
+ rc = ipmi_send(fd, netfn, cmd, seq, tx_buf, tx_size);
+ if (rc) {
+ pr_log(LOG_WARNING, "IPMI: send failed");
+ goto out;
+ }
+
+ gettimeofday(&start, NULL);
+
+ pollfds[0].fd = fd;
+ pollfds[0].events = POLLIN;
+
+ for (;;) {
+ long rx_seq;
+ int timeout;
+
+ gettimeofday(&now, NULL);
+ timersub(&now, &start, &delta);
+ timeout = ipmi_timeout_ms - ((delta.tv_sec * 1000) +
+ (delta.tv_usec / 1000));
+ if (timeout < 0)
+ timeout = 0;
+
+ rc = poll(pollfds, 1, timeout);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "IPMI: poll(%s) failed: %m",
+ ipmi_devnode);
+ break;
+ }
+
+ if (rc == 0) {
+ pr_log(LOG_WARNING, "IPMI: response timeout (>%dms)",
+ ipmi_timeout_ms);
+ rc = -1;
+ break;
+ }
+
+ rc = ipmi_recv(fd, &netfn, &cmd, &rx_seq, rx_buf, &size);
+ if (rc)
+ break;
+
+ if (seq != rx_seq) {
+ pr_log(LOG_NOTICE, "IPMI: out-of-sequence reply: %ld, "
+ "expected %ld. Dropping message.",
+ rx_seq, seq);
+ continue;
+ }
+
+ pr_debug("IPMI: received %zd bytes", tx_size);
+ *rx_size = be64toh(size);
+ rc = 0;
+ break;
+ }
+
+out:
+ close(fd);
+ return rc;
+}
+
+static int memory_error_worker(const char *sysfsfile, const char *type,
+ uint64_t i_start_addr, uint64_t i_endAddr)
+{
+ int memfd, rc, n, ret = 0;
+ char buf[ADDR_STRING_SZ];
+ uint64_t addr;
+
+ memfd = open(sysfsfile, O_WRONLY);
+ if (memfd < 0) {
+ pr_log(LOG_CRIT, "MEM: Failed to offline memory! "
+ "Unable to open sysfs node %s: %m", sysfsfile);
+ return -1;
+ }
+
+ for (addr = i_start_addr; addr <= i_endAddr; addr += ctx->page_size) {
+ n = snprintf(buf, ADDR_STRING_SZ, "0x%lx", addr);
+ rc = write(memfd, buf, n);
+ if (rc != n) {
+ pr_log(LOG_CRIT, "MEM: Failed to offline memory! "
+ "page addr: %016lx type: %s: %m",
+ addr, type);
+ ret = 1;
+ }
+ }
+ pr_log(LOG_CRIT, "MEM: Offlined %016lx,%016lx, type %s: %m\n",
+ i_start_addr, addr, type);
+
+ close(memfd);
+ return ret;
+}
+
+int hservice_memory_error(uint64_t i_start_addr, uint64_t i_endAddr,
+ enum MemoryError_t i_errorType)
+{
+ const char *sysfsfile, *typestr;
+ pid_t pid;
+
+ switch(i_errorType) {
+ case MEMORY_ERROR_CE:
+ sysfsfile = mem_offline_soft;
+ typestr = "correctable";
+ break;
+ case MEMORY_ERROR_UE:
+ sysfsfile = mem_offline_hard;
+ typestr = "uncorrectable";
+ break;
+ default:
+ pr_log(LOG_WARNING, "MEM: Invalid memory error type %d",
+ i_errorType);
+ return -1;
+ }
+
+ pr_log(LOG_ERR, "MEM: Memory error: range %016lx-%016lx, type: %s",
+ i_start_addr, i_endAddr, typestr);
+
+ /*
+ * HBRT expects the memory offlining process to happen in the background
+ * after the notification is delivered.
+ */
+ pid = fork();
+ if (pid > 0)
+ exit(memory_error_worker(sysfsfile, typestr, i_start_addr, i_endAddr));
+
+ if (pid < 0) {
+ perror("MEM: unable to fork worker to offline memory!\n");
+ return -1;
+ }
+
+ pr_log(LOG_INFO, "MEM: forked off %d to handle mem error\n", pid);
+ return 0;
+}
+
+uint64_t hservice_get_interface_capabilities(uint64_t set)
+{
+ if (set == HBRT_CAPS_SET1_OPAL)
+ return HBRT_CAPS_OPAL_HAS_XSCOM_RC ||
+ HBRT_CAPS_OPAL_HAS_WAKEUP_SUPPORT;
+
+ return 0;
+}
+
+uint64_t hservice_firmware_request(uint64_t req_len, void *req,
+ uint64_t *resp_lenp, void *resp)
+{
+ struct opal_prd_msg *msg = ctx->msg;
+ uint64_t resp_len;
+ size_t size;
+ int rc, n;
+
+ resp_len = be64_to_cpu(*resp_lenp);
+
+ pr_log(LOG_DEBUG,
+ "HBRT: firmware request: %lu bytes req, %lu bytes resp",
+ req_len, resp_len);
+
+ /* sanity check for potential overflows */
+ if (req_len > 0xffff || resp_len > 0xffff)
+ return -1;
+
+ size = sizeof(msg->hdr) + sizeof(msg->token) +
+ sizeof(msg->fw_req) + req_len;
+
+ /* we need the entire message to fit within the 2-byte size field */
+ if (size > 0xffff)
+ return -1;
+
+ /* variable sized message, so we may need to expand our buffer */
+ if (size > ctx->msg_alloc_len) {
+ msg = realloc(ctx->msg, size);
+ if (!msg) {
+ pr_log(LOG_ERR,
+ "FW: failed to expand message buffer: %m");
+ return -1;
+ }
+ ctx->msg = msg;
+ ctx->msg_alloc_len = size;
+ }
+
+ memset(msg, 0, size);
+
+ /* construct request message... */
+ msg->hdr.type = OPAL_PRD_MSG_TYPE_FIRMWARE_REQUEST;
+ msg->hdr.size = htobe16(size);
+ msg->fw_req.req_len = htobe64(req_len);
+ msg->fw_req.resp_len = htobe64(resp_len);
+ memcpy(msg->fw_req.data, req, req_len);
+
+ hexdump((void *)msg, size);
+
+ /* ... and send to firmware */
+ rc = write(ctx->fd, msg, size);
+ if (rc != size) {
+ pr_log(LOG_WARNING,
+ "FW: Failed to send FIRMWARE_REQUEST message: %m");
+ return -1;
+ }
+
+ /* We have an "inner" poll loop here, as we want to ensure that the
+ * next entry into HBRT is the return from this function. So, only
+ * read from the prd fd, and queue anything that isn't a response
+ * to this request
+ */
+ n = 0;
+ for (;;) {
+ struct prd_msgq_item *item;
+
+ rc = read_prd_msg(ctx);
+ if (rc)
+ return -1;
+
+ msg = ctx->msg;
+ if (msg->hdr.type == OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE) {
+ size = be64toh(msg->fw_resp.len);
+ if (size > resp_len)
+ return -1;
+
+ /* success! a valid response that fits into HBRT's
+ * resp buffer */
+ memcpy(resp, msg->fw_resp.data, size);
+ *resp_lenp = htobe64(size);
+ return 0;
+ }
+
+ /* not a response? queue up for later consumption */
+ if (++n > max_msgq_len) {
+ pr_log(LOG_ERR,
+ "FW: too many messages queued (%d) while "
+ "waiting for FIRMWARE_RESPONSE", n);
+ return -1;
+ }
+ size = be16toh(msg->hdr.size);
+ item = malloc(sizeof(*item) + size);
+ memcpy(&item->msg, msg, size);
+ list_add_tail(&ctx->msgq, &item->list);
+ }
+}
+
+int hservices_init(struct opal_prd_ctx *ctx, void *code)
+{
+ uint64_t *s, *d;
+ int i, sz;
+
+ pr_debug("IMAGE: code address: %p", code);
+
+ /* We enter at 0x100 into the image. */
+ /* Load func desc in BE since we reverse it in thunk */
+
+ hbrt_entry.addr = (void *)htobe64((unsigned long)code + 0x100);
+ hbrt_entry.toc = 0; /* No toc for init entry point */
+
+ if (memcmp(code, "HBRTVERS", 8) != 0) {
+ pr_log(LOG_ERR, "IMAGE: Bad signature for "
+ "ibm,hbrt-code-image! exiting");
+ return -1;
+ }
+
+ pr_debug("IMAGE: calling ibm,hbrt_init()");
+ hservice_runtime = call_hbrt_init(&hinterface);
+ if (!hservice_runtime) {
+ pr_log(LOG_ERR, "IMAGE: hbrt_init failed, exiting");
+ return -1;
+ }
+
+ pr_log(LOG_NOTICE, "IMAGE: hbrt_init complete, version %016lx",
+ hservice_runtime->interface_version);
+
+ sz = sizeof(struct runtime_interfaces)/sizeof(uint64_t);
+ s = (uint64_t *)hservice_runtime;
+ d = (uint64_t *)&hservice_runtime_fixed;
+ /* Byte swap the function pointers */
+ for (i = 0; i < sz; i++)
+ d[i] = be64toh(s[i]);
+
+ return 0;
+}
+
+static void fixup_hinterface_table(void)
+{
+ uint64_t *t64;
+ unsigned int i, sz;
+
+ /* Swap interface version */
+ hinterface.interface_version =
+ htobe64(hinterface.interface_version);
+
+ /* Swap OPDs */
+ sz = sizeof(struct host_interfaces) / sizeof(uint64_t);
+ t64 = (uint64_t *)&hinterface;
+ for (i = 1; i < sz; i++) {
+ uint64_t *opd = (uint64_t *)t64[i];
+ if (!opd)
+ continue;
+ t64[i] = htobe64(t64[i]);
+ opd[0] = htobe64(opd[0]);
+ opd[1] = htobe64(opd[1]);
+ opd[2] = htobe64(opd[2]);
+ }
+}
+
+static int map_hbrt_file(struct opal_prd_ctx *ctx, const char *name)
+{
+ struct stat statbuf;
+ int fd, rc;
+ void *buf;
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0) {
+ pr_log(LOG_ERR, "IMAGE: HBRT file open(%s) failed: %m", name);
+ return -1;
+ }
+
+ rc = fstat(fd, &statbuf);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "IMAGE: HBRT file fstat(%s) failed: %m", name);
+ close(fd);
+ return -1;
+ }
+
+ buf = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE, fd, 0);
+ close(fd);
+
+ if (buf == MAP_FAILED) {
+ pr_log(LOG_ERR, "IMAGE: HBRT file mmap(%s, 0x%zx) failed: %m",
+ name, statbuf.st_size);
+ return -1;
+ }
+
+ ctx->code_addr = buf;
+ ctx->code_size = statbuf.st_size;
+ return -0;
+}
+
+static int map_hbrt_physmem(struct opal_prd_ctx *ctx, const char *name)
+{
+ struct prd_range *range;
+ int rc;
+ void *buf;
+ void *ro_buf;
+
+ range = find_range(name, 0);
+ if (!range) {
+ pr_log(LOG_ERR, "IMAGE: can't find code region %s", name);
+ return -1;
+ }
+
+ ro_buf = mmap(NULL, range->size, PROT_READ,
+ MAP_PRIVATE, ctx->fd, range->physaddr);
+ if (ro_buf == MAP_FAILED) {
+ pr_log(LOG_ERR, "IMAGE: mmap(range:%s, "
+ "phys:0x%016lx, size:0x%016lx) failed: %m",
+ name, range->physaddr, range->size);
+ return -1;
+ }
+
+ buf = mmap(NULL, range->size, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1 , 0);
+ if (buf == MAP_FAILED) {
+ pr_log(LOG_ERR, "IMAGE: anon mmap(size:0x%016lx) failed: %m",
+ range->size);
+ return -1;
+ }
+
+ memcpy(buf, ro_buf, range->size);
+
+ rc = munmap(ro_buf, range->size);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "IMAGE: munmap("
+ "phys:0x%016lx, size:0x%016lx) failed: %m",
+ range->physaddr, range->size);
+ return -1;
+ }
+
+ /*
+ * FIXME: We shouldn't be mapping the memory as RWX, but HBRT appears to
+ * require the ability to write into the image at runtime.
+ */
+ rc = mprotect(buf, range->size, PROT_READ | PROT_WRITE | PROT_EXEC);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "IMAGE: mprotect(phys:%p, "
+ "size:0x%016lx, rwx) failed: %m",
+ buf, range->size);
+ return -1;
+ }
+
+ ctx->code_addr = buf;
+ ctx->code_size = range->size;
+ return 0;
+}
+
+static void dump_hbrt_map(struct opal_prd_ctx *ctx)
+{
+ const char *dump_name = "hbrt.bin";
+ int fd, rc;
+
+ if (!ctx->debug)
+ return;
+
+ fd = open(dump_name, O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ pr_log(LOG_NOTICE, "IMAGE: couldn't debug image %s for writing",
+ dump_name);
+ return;
+ }
+
+ rc = ftruncate(fd, 0);
+ if (rc < 0) {
+ pr_log(LOG_NOTICE, "IMAGE: couldn't truncate image %s for writing",
+ dump_name);
+ return;
+ }
+ rc = write(fd, ctx->code_addr, ctx->code_size);
+ close(fd);
+
+ if (rc != ctx->code_size)
+ pr_log(LOG_NOTICE, "IMAGE: write to %s failed: %m", dump_name);
+ else
+ pr_debug("IMAGE: dumped HBRT binary to %s", dump_name);
+}
+
+static int open_and_read(const char *path, void **bufp, int *lenp)
+{
+ struct stat statbuf;
+ int fd, rc, bytes;
+ void *buf;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ rc = fstat(fd, &statbuf);
+ if (rc) {
+ close(fd);
+ return -1;
+ }
+
+ buf = malloc(statbuf.st_size);
+ if (!buf) {
+ close(fd);
+ return -1;
+ }
+
+ for (rc = bytes = 0; bytes < statbuf.st_size; bytes += rc) {
+ rc = read(fd, buf + bytes, statbuf.st_size - bytes);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ } else if (rc == 0)
+ break;
+ }
+
+ if (bytes == statbuf.st_size)
+ rc = 0;
+
+ if (rc == 0) {
+ if (lenp)
+ *lenp = bytes;
+ if (bufp)
+ *bufp = buf;
+ } else {
+ free(buf);
+ }
+
+ close(fd);
+
+ return rc == 0 ? 0 : -1;
+}
+
+static int prd_init_one_range(struct opal_prd_ctx *ctx, const char *path,
+ struct dirent *dirent)
+{
+ char *label_path, *reg_path, *instance_path;
+ struct prd_range *range;
+ int label_len, len, rc;
+ __be64 *reg;
+ char *label;
+ void *buf;
+
+ rc = asprintf(&label_path, "%s/%s/ibm,prd-label", path, dirent->d_name);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: error creating 'ibm,prd-label' path "
+ "node: %m");
+ return -1;
+ }
+ rc = asprintf(&instance_path, "%s/%s/ibm,prd-instance",
+ path, dirent->d_name);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: error creating 'ibm,prd-instance' path "
+ "node: %m");
+ return -1;
+ }
+ rc = asprintf(&reg_path, "%s/%s/reg", path, dirent->d_name);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: error creating 'reg' path "
+ " node: %m");
+ return -1;
+ }
+
+ reg = NULL;
+ label = NULL;
+ rc = -1;
+
+ rc = open_and_read(label_path, &buf, &label_len);
+ if (rc)
+ goto out_free;
+
+ label = buf;
+
+ if (label[label_len-1] != '\0')
+ pr_log(LOG_INFO, "FW: node %s has invalid ibm,prd-label - "
+ "not nul-terminated",
+ dirent->d_name);
+
+ rc = open_and_read(reg_path, &buf, &len);
+ if (rc)
+ goto out_free;
+
+ reg = buf;
+
+ if (len != 2 * sizeof(*reg)) {
+ pr_log(LOG_ERR, "FW: node %s has invalid 'reg' size: %d",
+ dirent->d_name, len);
+ goto out_free;
+ }
+
+
+ ctx->ranges = realloc(ctx->ranges, ++ctx->n_ranges * sizeof(*range));
+ range = &ctx->ranges[ctx->n_ranges - 1];
+ range->name = strndup(label, label_len);
+ range->physaddr = be64toh(reg[0]);
+ range->size = be64toh(reg[1]);
+ range->buf = NULL;
+ range->multiple = false;
+ range->instance = 0;
+
+ /* optional instance */
+ rc = open_and_read(instance_path, &buf, &len);
+ if (!rc && len == sizeof(uint32_t)) {
+ range->multiple = true;
+ range->instance = be32toh(*(uint32_t *)buf);
+ ctx->fw_range_instances = true;
+ }
+ rc = 0;
+
+out_free:
+ free(reg);
+ free(label);
+ free(instance_path);
+ free(reg_path);
+ free(label_path);
+ return rc;
+}
+
+static int compare_ranges(const void *ap, const void *bp)
+{
+ const struct prd_range *a = ap, *b = bp;
+ int rc;
+
+ rc = strcmp(a->name, b->name);
+ if (rc)
+ return rc;
+
+ if (a->physaddr < b->physaddr)
+ return -1;
+ else if (a->physaddr > b->physaddr)
+ return 1;
+
+ return 0;
+}
+
+static void assign_range_instances(struct opal_prd_ctx *ctx)
+{
+ int i;
+
+ if (!ctx->n_ranges)
+ return;
+
+ ctx->ranges[0].multiple = false;
+ ctx->ranges[0].instance = 0;
+
+ for (i = 1; i < ctx->n_ranges; i++) {
+ struct prd_range *cur, *prev;
+
+ cur = &ctx->ranges[i];
+ prev = &ctx->ranges[i-1];
+
+ if (!strcmp(cur->name, prev->name)) {
+ prev->multiple = true;
+ cur->multiple = true;
+ cur->instance = prev->instance + 1;
+ } else {
+ cur->multiple = false;
+ cur->instance = 0;
+ }
+ }
+}
+
+static void print_ranges(struct opal_prd_ctx *ctx)
+{
+ int i;
+
+ if (ctx->n_ranges == 0)
+ pr_log(LOG_INFO, "FW: No PRD ranges");
+
+ pr_log(LOG_DEBUG, "FW: %d PRD ranges, instances assigned by %s",
+ ctx->n_ranges,
+ ctx->fw_range_instances ? "firmware" : "userspace");
+
+ for (i = 0; i < ctx->n_ranges; i++) {
+ struct prd_range *range = &ctx->ranges[i];
+ char instance_str[20];
+
+ if (range->multiple)
+ snprintf(instance_str, sizeof(instance_str),
+ " [%d]", range->instance);
+ else
+ instance_str[0] = '\0';
+
+ pr_log(LOG_DEBUG, "FW: %016lx-%016lx %s%s", range->physaddr,
+ range->physaddr + range->size - 1,
+ range->name,
+ instance_str);
+ }
+}
+
+static int chip_init(void)
+{
+ struct dirent *dirent;
+ char *path;
+ DIR *dir;
+ __be32 *chipid;
+ void *buf;
+ int rc, len, i;
+
+ dir = opendir(devicetree_base);
+ if (!dir) {
+ pr_log(LOG_ERR, "FW: Can't open %s", devicetree_base);
+ return -1;
+ }
+
+ for (;;) {
+ dirent = readdir(dir);
+ if (!dirent)
+ break;
+
+ if (strncmp("xscom", dirent->d_name, 5))
+ continue;
+
+ rc = asprintf(&path, "%s/%s/ibm,chip-id", devicetree_base,
+ dirent->d_name);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: Failed to create chip-id path");
+ return -1;
+ }
+
+ rc = open_and_read(path, &buf, &len);
+ if (rc) {
+ pr_log(LOG_ERR, "FW; Failed to read chipid");
+ return -1;
+ }
+ chipid = buf;
+ chips[nr_chips++] = be32toh(*chipid);
+ }
+
+ pr_log(LOG_DEBUG, "FW: Chip init");
+ for (i = 0; i < nr_chips; i++)
+ pr_log(LOG_DEBUG, "FW: Chip 0x%lx", chips[i]);
+
+ return 0;
+}
+
+static int prd_init_ranges(struct opal_prd_ctx *ctx)
+{
+ struct dirent *dirent;
+ char *path;
+ DIR *dir;
+ int rc;
+
+ rc = asprintf(&path, "%s/reserved-memory", devicetree_base);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: error creating 'reserved-memory' path "
+ "node: %m");
+ return -1;
+ }
+
+ rc = -1;
+
+ dir = opendir(path);
+ if (!dir) {
+ pr_log(LOG_ERR, "FW: can't open reserved-memory device-tree "
+ "node: %m");
+ goto out_free;
+ }
+
+ for (;;) {
+ dirent = readdir(dir);
+ if (!dirent)
+ break;
+
+ prd_init_one_range(ctx, path, dirent);
+ }
+
+ rc = 0;
+ /* sort ranges and assign instance numbers for duplicates (if the
+ * firmware doesn't number instances for us) */
+ qsort(ctx->ranges, ctx->n_ranges, sizeof(struct prd_range),
+ compare_ranges);
+
+ if (!ctx->fw_range_instances)
+ assign_range_instances(ctx);
+
+ print_ranges(ctx);
+
+out_free:
+ free(path);
+ closedir(dir);
+ return rc;
+}
+
+bool find_string(const char *buffer, size_t len, const char *s)
+{
+ const char *c, *end;
+
+ if (!buffer)
+ return false;
+ c = buffer;
+ end = c + len;
+
+ while (c < end) {
+ if (!strcasecmp(s, c))
+ return true;
+ c += strlen(c) + 1;
+ }
+ return false;
+}
+
+static int is_prd_supported(void)
+{
+ char *path;
+ int rc;
+ int len;
+ char *buf;
+
+ rc = asprintf(&path, "%s/ibm,opal/diagnostics/compatible",
+ devicetree_base);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: error creating 'compatible' node path: %m");
+ return -1;
+ }
+
+ rc = open_and_read(path, (void *) &buf, &len);
+ if (rc)
+ goto out_free;
+
+ if (buf[len - 1] != '\0')
+ pr_log(LOG_INFO, "FW: node %s is not nul-terminated", path);
+
+ rc = find_string(buf, len, "ibm,opal-prd") ? 0 : -1;
+
+ free(buf);
+out_free:
+ free(path);
+ return rc;
+}
+
+static int prd_init(struct opal_prd_ctx *ctx)
+{
+ int rc;
+
+ ctx->page_size = sysconf(_SC_PAGE_SIZE);
+
+ /* set up the device, and do our get_info ioctl */
+ ctx->fd = open(opal_prd_devnode, O_RDWR);
+ if (ctx->fd < 0) {
+ pr_log(LOG_ERR, "FW: Can't open PRD device %s: %m",
+ opal_prd_devnode);
+ return -1;
+ }
+
+ rc = ioctl(ctx->fd, OPAL_PRD_GET_INFO, &ctx->info);
+ if (rc) {
+ pr_log(LOG_ERR, "FW: Can't query PRD information: %m");
+ return -1;
+ }
+
+ rc = prd_init_ranges(ctx);
+ if (rc) {
+ pr_log(LOG_ERR, "FW: can't parse PRD memory information");
+ return -1;
+ }
+
+ rc = chip_init();
+ if (rc)
+ pr_log(LOG_ERR, "FW: Failed to initialize chip IDs");
+
+ return 0;
+}
+
+static int handle_msg_attn(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg)
+{
+ uint64_t proc, ipoll_mask, ipoll_status;
+ int rc;
+
+ proc = be64toh(msg->attn.proc);
+ ipoll_status = be64toh(msg->attn.ipoll_status);
+ ipoll_mask = be64toh(msg->attn.ipoll_mask);
+
+ if (!hservice_runtime->handle_attns) {
+ pr_log_nocall("handle_attns");
+ return -1;
+ }
+
+ rc = call_handle_attns(proc, ipoll_status, ipoll_mask);
+ if (rc) {
+ pr_log(LOG_ERR, "HBRT: handle_attns(%lx,%lx,%lx) failed, rc %d",
+ proc, ipoll_status, ipoll_mask, rc);
+ return -1;
+ }
+
+ /* send the response */
+ msg->hdr.type = OPAL_PRD_MSG_TYPE_ATTN_ACK;
+ msg->hdr.size = htobe16(sizeof(*msg));
+ msg->attn_ack.proc = htobe64(proc);
+ msg->attn_ack.ipoll_ack = htobe64(ipoll_status);
+ rc = write(ctx->fd, msg, sizeof(*msg));
+
+ if (rc != sizeof(*msg)) {
+ pr_log(LOG_WARNING, "FW: Failed to send ATTN_ACK message: %m");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int handle_msg_occ_error(struct opal_prd_ctx *ctx,
+ struct opal_prd_msg *msg)
+{
+ uint32_t proc;
+
+ proc = be64toh(msg->occ_error.chip);
+
+ pr_debug("FW: firmware signaled OCC error for proc 0x%x", proc);
+
+ if (!hservice_runtime->process_occ_error) {
+ pr_log_nocall("process_occ_error");
+ return -1;
+ }
+
+ call_process_occ_error(proc);
+ return 0;
+}
+
+static int pm_complex_load_start(void)
+{
+ struct prd_range *range;
+ u64 homer, occ_common;
+ int rc = -1, i;
+
+ if (!hservice_runtime->load_pm_complex) {
+ pr_log_nocall("load_pm_complex");
+ return rc;
+ }
+
+ if (!hservice_runtime->start_pm_complex) {
+ pr_log_nocall("start_pm_complex");
+ return rc;
+ }
+
+ range = find_range("ibm,occ-common-area", 0);
+ if (!range) {
+ range = find_range("occ-common-area", 0);
+ if (!range) {
+ pr_log(LOG_ERR, "PM: occ-common-area not found");
+ return rc;
+ }
+ }
+ occ_common = range->physaddr;
+
+ for (i = 0; i < nr_chips; i++) {
+ range = find_range("ibm,homer-image", chips[i]);
+ if (!range) {
+ range = find_range("homer-image", chips[i]);
+ if (!range) {
+ pr_log(LOG_ERR, "PM: homer-image not found 0x%lx",
+ chips[i]);
+ return -1;
+ }
+ }
+ homer = range->physaddr;
+
+ pr_debug("PM: calling load_pm_complex(0x%lx, 0x%lx, 0x%lx, LOAD)",
+ chips[i], homer, occ_common);
+ rc = call_load_pm_complex(chips[i], homer, occ_common, 0);
+ if (rc) {
+ pr_log(LOG_ERR, "PM: Failed load_pm_complex(0x%lx) %m",
+ chips[i]);
+ return rc;
+ }
+ }
+
+ for (i = 0; i < nr_chips; i++) {
+ pr_debug("PM: calling start_pm_complex(0x%lx)", chips[i]);
+ rc = call_start_pm_complex(chips[i]);
+ if (rc) {
+ pr_log(LOG_ERR, "PM: Failed start_pm_complex(0x%lx): %m",
+ chips[i]);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int pm_complex_reset(uint64_t chip)
+{
+ int rc;
+
+ /*
+ * FSP system -> reset_pm_complex
+ * BMC system -> process_occ_reset
+ */
+ if (is_fsp_system()) {
+ int i;
+
+ if (!hservice_runtime->reset_pm_complex) {
+ pr_log_nocall("reset_pm_complex");
+ return -1;
+ }
+
+ for (i = 0; i < nr_chips; i++) {
+ pr_debug("PM: calling pm_complex_reset(%ld)", chips[i]);
+ rc = call_reset_pm_complex(chip);
+ if (rc) {
+ pr_log(LOG_ERR, "PM: Failed pm_complex_reset(%ld): %m",
+ chips[i]);
+ return rc;
+ }
+ }
+
+ rc = pm_complex_load_start();
+ } else {
+ if (!hservice_runtime->process_occ_reset) {
+ pr_log_nocall("process_occ_reset");
+ return -1;
+ }
+
+ pr_debug("PM: calling process_occ_reset(%ld)", chip);
+ call_process_occ_reset(chip);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int handle_msg_occ_reset(struct opal_prd_ctx *ctx,
+ struct opal_prd_msg *msg)
+{
+ uint32_t proc;
+ int rc;
+
+ proc = be64toh(msg->occ_reset.chip);
+
+ pr_debug("FW: firmware requested OCC reset for proc 0x%x", proc);
+
+ rc = pm_complex_reset(proc);
+
+ return rc;
+}
+
+static int handle_msg_firmware_notify(struct opal_prd_ctx *ctx,
+ struct opal_prd_msg *msg)
+{
+ uint64_t len;
+ void *buf;
+
+ len = be64toh(msg->fw_notify.len);
+ buf = msg->fw_notify.data;
+
+ pr_debug("FW: firmware notification, %ld bytes", len);
+
+ if (!hservice_runtime->firmware_notify) {
+ pr_log_nocall("firmware_notify");
+ return -1;
+ }
+
+ call_firmware_notify(len, buf);
+
+ return 0;
+}
+
+static int handle_msg_sbe_passthrough(struct opal_prd_ctx *ctx,
+ struct opal_prd_msg *msg)
+{
+ uint32_t proc;
+ int rc;
+
+ proc = be64toh(msg->sbe_passthrough.chip);
+
+ pr_debug("FW: firmware sent SBE pass through command for proc 0x%x\n",
+ proc);
+
+ if (!hservice_runtime->sbe_message_passing) {
+ pr_log_nocall("sbe_message_passing");
+ return -1;
+ }
+
+ rc = call_sbe_message_passing(proc);
+ return rc;
+}
+
+static int handle_msg_fsp_occ_reset(struct opal_prd_msg *msg)
+{
+ struct opal_prd_msg omsg;
+ int rc = -1, i;
+
+ pr_debug("FW: FSP requested OCC reset");
+
+ if (!hservice_runtime->reset_pm_complex) {
+ pr_log_nocall("reset_pm_complex");
+ return rc;
+ }
+
+ for (i = 0; i < nr_chips; i++) {
+ pr_debug("PM: calling pm_complex_reset(0x%lx)", chips[i]);
+ rc = call_reset_pm_complex(chips[i]);
+ if (rc) {
+ pr_log(LOG_ERR, "PM: Failed pm_complex_reset(0x%lx) %m",
+ chips[i]);
+ break;
+ }
+ }
+
+ omsg.hdr.type = OPAL_PRD_MSG_TYPE_FSP_OCC_RESET_STATUS;
+ omsg.hdr.size = htobe16(sizeof(omsg));
+ omsg.fsp_occ_reset_status.chip = msg->occ_reset.chip;
+ omsg.fsp_occ_reset_status.status = htobe64(rc);
+
+ if (write(ctx->fd, &omsg, sizeof(omsg)) != sizeof(omsg)) {
+ pr_log(LOG_ERR, "FW: Failed to send FSP_OCC_RESET_STATUS msg: %m");
+ return -1;
+ }
+
+ return rc;
+}
+
+static int handle_msg_fsp_occ_load_start(struct opal_prd_msg *msg)
+{
+ struct opal_prd_msg omsg;
+ int rc;
+
+ pr_debug("FW: FSP requested OCC load/start");
+ rc = pm_complex_load_start();
+
+ omsg.hdr.type = OPAL_PRD_MSG_TYPE_FSP_OCC_LOAD_START_STATUS;
+ omsg.hdr.size = htobe16(sizeof(omsg));
+ omsg.fsp_occ_reset_status.chip = msg->occ_reset.chip;
+ omsg.fsp_occ_reset_status.status = htobe64(rc);
+
+ if (write(ctx->fd, &omsg, sizeof(omsg)) != sizeof(omsg)) {
+ pr_log(LOG_ERR, "FW: Failed to send FSP_OCC_LOAD_START_STATUS msg: %m");
+ return -1;
+ }
+
+ return rc;
+}
+
+static int handle_prd_msg(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg)
+{
+ int rc = -1;
+
+ switch (msg->hdr.type) {
+ case OPAL_PRD_MSG_TYPE_ATTN:
+ rc = handle_msg_attn(ctx, msg);
+ break;
+ case OPAL_PRD_MSG_TYPE_OCC_RESET:
+ rc = handle_msg_occ_reset(ctx, msg);
+ break;
+ case OPAL_PRD_MSG_TYPE_OCC_ERROR:
+ rc = handle_msg_occ_error(ctx, msg);
+ break;
+ case OPAL_PRD_MSG_TYPE_FIRMWARE_NOTIFY:
+ rc = handle_msg_firmware_notify(ctx, msg);
+ break;
+ case OPAL_PRD_MSG_TYPE_SBE_PASSTHROUGH:
+ rc = handle_msg_sbe_passthrough(ctx, msg);
+ break;
+ case OPAL_PRD_MSG_TYPE_FSP_OCC_RESET:
+ rc = handle_msg_fsp_occ_reset(msg);
+ break;
+ case OPAL_PRD_MSG_TYPE_FSP_OCC_LOAD_START:
+ rc = handle_msg_fsp_occ_load_start(msg);
+ break;
+ default:
+ pr_log(LOG_WARNING, "Invalid incoming message type 0x%x",
+ msg->hdr.type);
+ }
+
+ return rc;
+}
+
+#define list_for_each_pop(h, i, type, member) \
+ for (i = list_pop((h), type, member); \
+ i; \
+ i = list_pop((h), type, member))
+
+
+static int process_msgq(struct opal_prd_ctx *ctx)
+{
+ struct prd_msgq_item *item;
+
+ list_for_each_pop(&ctx->msgq, item, struct prd_msgq_item, list) {
+ handle_prd_msg(ctx, &item->msg);
+ free(item);
+ }
+
+ return 0;
+}
+
+static int read_prd_msg(struct opal_prd_ctx *ctx)
+{
+ struct opal_prd_msg *msg;
+ int size;
+ int rc;
+
+ msg = ctx->msg;
+
+ rc = read(ctx->fd, msg, ctx->msg_alloc_len);
+ if (rc < 0 && errno == EAGAIN)
+ return -1;
+
+ /* we need at least enough for the message header... */
+ if (rc < 0) {
+ pr_log(LOG_WARNING, "FW: error reading from firmware: %m");
+ return -1;
+ }
+
+ if (rc < sizeof(msg->hdr)) {
+ pr_log(LOG_WARNING, "FW: short message read from firmware");
+ return -1;
+ }
+
+ /* ... and for the reported message size to be sane */
+ size = htobe16(msg->hdr.size);
+ if (size < sizeof(msg->hdr)) {
+ pr_log(LOG_ERR, "FW: Mismatched message size "
+ "between opal-prd and firmware "
+ "(%d from FW, %zd expected)",
+ size, sizeof(msg->hdr));
+ return -1;
+ }
+
+ /* expand our message buffer if necessary... */
+ if (size > ctx->msg_alloc_len) {
+ msg = realloc(ctx->msg, size);
+ if (!msg) {
+ pr_log(LOG_ERR,
+ "FW: Can't expand PRD message buffer: %m");
+ return -1;
+ }
+ ctx->msg = msg;
+ ctx->msg_alloc_len = size;
+ }
+
+ /* ... and complete the read */
+ if (size > rc) {
+ size_t pos;
+
+ for (pos = rc; pos < size;) {
+ rc = read(ctx->fd, msg + pos, size - pos);
+
+ if (rc < 0 && errno == EAGAIN)
+ continue;
+
+ if (rc <= 0) {
+ pr_log(LOG_WARNING,
+ "FW: error reading from firmware: %m");
+ return -1;
+ }
+
+ pos += rc;
+ }
+ }
+
+ return 0;
+}
+
+static void handle_prd_control_occ_error(struct control_msg *send_msg,
+ struct control_msg *recv_msg)
+{
+ uint64_t chip;
+
+ if (!hservice_runtime->process_occ_error) {
+ pr_log_nocall("process_occ_error");
+ return;
+ }
+
+ chip = recv_msg->occ_error.chip;
+
+ pr_debug("CTRL: calling process_occ_error(%lu)", chip);
+ call_process_occ_error(chip);
+
+ send_msg->data_len = 0;
+ send_msg->response = 0;
+}
+
+static void handle_prd_control_occ_reset(struct control_msg *send_msg,
+ struct control_msg *msg)
+{
+ struct opal_prd_msg omsg;
+ uint64_t chip;
+ int rc;
+
+ /* notify OPAL of the impending reset */
+ memset(&omsg, 0, sizeof(omsg));
+ omsg.hdr.type = OPAL_PRD_MSG_TYPE_OCC_RESET_NOTIFY;
+ omsg.hdr.size = htobe16(sizeof(omsg));
+ rc = write(ctx->fd, &omsg, sizeof(omsg));
+ if (rc != sizeof(omsg))
+ pr_log(LOG_WARNING, "FW: Failed to send OCC_RESET message: %m");
+
+ chip = msg->occ_reset.chip;
+
+ /* do reset */
+ pr_debug("CTRL: Calling OCC reset on chip %ld", chip);
+ pm_complex_reset(chip);
+
+ send_msg->data_len = 0;
+ send_msg->response = 0;
+}
+
+static void handle_prd_control_occ_actuation(struct control_msg *msg,
+ bool enable)
+{
+ if (!hservice_runtime->enable_occ_actuation) {
+ pr_log_nocall("enable_occ_actuation");
+ return;
+ }
+
+ pr_debug("CTRL: calling enable_occ_actuation(%s)",
+ enable ? "true" : "false");
+ msg->data_len = 0;
+ msg->response = call_enable_occ_actuation(enable);
+}
+
+static void handle_prd_control_attr_override(struct control_msg *send_msg,
+ struct control_msg *recv_msg)
+{
+ if (!hservice_runtime->apply_attr_override) {
+ pr_log_nocall("apply_attr_override");
+ return;
+ }
+
+ pr_debug("CTRL: calling apply_attr_override");
+ send_msg->response = call_apply_attr_override(
+ recv_msg->data, recv_msg->data_len);
+ send_msg->data_len = 0;
+}
+
+static void handle_prd_control_htmgt_passthru(struct control_msg *send_msg,
+ struct control_msg *recv_msg)
+{
+ uint16_t rsp_len;
+
+ if (!hservice_runtime->mfg_htmgt_pass_thru) {
+ pr_log_nocall("mfg_htmgt_pass_thru");
+ return;
+ }
+
+ pr_debug("CTRL: calling mfg_htmgt_pass_thru");
+ send_msg->response = call_mfg_htmgt_pass_thru(recv_msg->data_len,
+ recv_msg->data, &rsp_len,
+ send_msg->data);
+ send_msg->data_len = be16toh(rsp_len);
+ if (send_msg->data_len > MAX_CONTROL_MSG_BUF) {
+ pr_log(LOG_ERR, "CTRL: response buffer overrun, data len: %d",
+ send_msg->data_len);
+ send_msg->data_len = MAX_CONTROL_MSG_BUF;
+ }
+}
+
+static void handle_prd_control_run_cmd(struct control_msg *send_msg,
+ struct control_msg *recv_msg)
+{
+ char *runcmd_output, *s;
+ const char **argv;
+ int i, argc;
+ size_t size;
+
+ if (!hservice_runtime->run_command) {
+ pr_log_nocall("run_command");
+ return;
+ }
+
+ argc = recv_msg->run_cmd.argc;
+ pr_debug("CTRL: run_command, argc:%d\n", argc);
+
+ argv = malloc(argc * sizeof(*argv));
+ if (!argv) {
+ pr_log(LOG_ERR, "CTRL: argv buffer malloc failed: %m");
+ return;
+ }
+
+ s = (char *)recv_msg->data;
+ size = 0;
+ for (i = 0; i < argc; i++) {
+ argv[i] = (char *)htobe64((uint64_t)&s[size]);
+ size += (strlen(&s[size]) + 1);
+ }
+
+ /* Call HBRT */
+ send_msg->response = call_run_command(argc, argv, &runcmd_output);
+ runcmd_output = (char *)be64toh((uint64_t)runcmd_output);
+ free(argv);
+
+ s = (char *)send_msg->data;
+ if (runcmd_output) {
+ size = strlen(runcmd_output);
+ if (size >= MAX_CONTROL_MSG_BUF) {
+ pr_log(LOG_WARNING, "CTRL: output message truncated");
+ runcmd_output[MAX_CONTROL_MSG_BUF] = '\0';
+ size = MAX_CONTROL_MSG_BUF;
+ }
+
+ strcpy(s, runcmd_output);
+ send_msg->data_len = size + 1;
+ free(runcmd_output);
+ } else {
+ strcpy(s, "Null");
+ send_msg->data_len = strlen("Null") + 1;
+ }
+}
+
+static void handle_prd_control(struct opal_prd_ctx *ctx, int fd)
+{
+ struct control_msg msg, *recv_msg, *send_msg;
+ bool enabled = false;
+ int rc, size;
+
+ /* Default reply, in the error path */
+ send_msg = &msg;
+
+ /* Peek into the socket to ascertain the size of the available data */
+ rc = recv(fd, &msg, sizeof(msg), MSG_PEEK);
+ if (rc != sizeof(msg)) {
+ pr_log(LOG_WARNING, "CTRL: failed to receive control "
+ "message: %m");
+ msg.response = -1;
+ msg.data_len = 0;
+ goto out_send;
+ }
+
+ size = sizeof(*recv_msg) + msg.data_len;
+
+ /* Default reply, in the error path */
+ msg.data_len = 0;
+ msg.response = -1;
+
+ recv_msg = malloc(size);
+ if (!recv_msg) {
+ pr_log(LOG_ERR, "CTRL: message buffer malloc failed: %m");
+ goto out_send;
+ }
+
+ rc = recv(fd, recv_msg, size, MSG_TRUNC);
+ if (rc != size) {
+ pr_log(LOG_WARNING, "CTRL: failed to receive control "
+ "message: %m");
+ goto out_free_recv;
+ }
+
+ send_msg = malloc(sizeof(*send_msg) + MAX_CONTROL_MSG_BUF);
+ if (!send_msg) {
+ pr_log(LOG_ERR, "CTRL: message buffer malloc failed: %m");
+ send_msg = &msg;
+ goto out_free_recv;
+ }
+
+ send_msg->type = recv_msg->type;
+ send_msg->response = -1;
+ switch (recv_msg->type) {
+ case CONTROL_MSG_ENABLE_OCCS:
+ enabled = true;
+ /* fall through */
+ case CONTROL_MSG_DISABLE_OCCS:
+ handle_prd_control_occ_actuation(send_msg, enabled);
+ break;
+ case CONTROL_MSG_TEMP_OCC_RESET:
+ handle_prd_control_occ_reset(send_msg, recv_msg);
+ break;
+ case CONTROL_MSG_TEMP_OCC_ERROR:
+ handle_prd_control_occ_error(send_msg, recv_msg);
+ break;
+ case CONTROL_MSG_ATTR_OVERRIDE:
+ handle_prd_control_attr_override(send_msg, recv_msg);
+ break;
+ case CONTROL_MSG_HTMGT_PASSTHRU:
+ handle_prd_control_htmgt_passthru(send_msg, recv_msg);
+ break;
+ case CONTROL_MSG_RUN_CMD:
+ handle_prd_control_run_cmd(send_msg, recv_msg);
+ break;
+ default:
+ pr_log(LOG_WARNING, "CTRL: Unknown control message action %d",
+ recv_msg->type);
+ send_msg->data_len = 0;
+ break;
+ }
+
+out_free_recv:
+ free(recv_msg);
+out_send:
+ size = sizeof(*send_msg) + send_msg->data_len;
+ rc = send(fd, send_msg, size, MSG_DONTWAIT | MSG_NOSIGNAL);
+ if (rc && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EPIPE))
+ pr_debug("CTRL: control send() returned %d, ignoring failure",
+ rc);
+ else if (rc != size)
+ pr_log(LOG_NOTICE, "CTRL: Failed to send control response: %m");
+
+ if (send_msg != &msg)
+ free(send_msg);
+}
+
+static int run_attn_loop(struct opal_prd_ctx *ctx)
+{
+ struct pollfd pollfds[2];
+ struct opal_prd_msg msg;
+ int rc, fd;
+
+ if (hservice_runtime->enable_attns) {
+ pr_debug("HBRT: calling enable_attns");
+ rc = call_enable_attns();
+ if (rc) {
+ pr_log(LOG_ERR, "HBRT: enable_attns() failed, "
+ "aborting");
+ return -1;
+ }
+ }
+
+ if (hservice_runtime->get_ipoll_events) {
+ pr_debug("HBRT: calling get_ipoll_events");
+ opal_prd_ipoll = call_get_ipoll_events();
+ }
+
+ pr_debug("HBRT: enabling IPOLL events 0x%016lx", opal_prd_ipoll);
+
+ /* send init message, to unmask interrupts */
+ msg.hdr.type = OPAL_PRD_MSG_TYPE_INIT;
+ msg.hdr.size = htobe16(sizeof(msg));
+ msg.init.version = htobe64(opal_prd_version);
+ msg.init.ipoll = htobe64(opal_prd_ipoll);
+
+ pr_debug("FW: writing init message");
+ rc = write(ctx->fd, &msg, sizeof(msg));
+ if (rc != sizeof(msg)) {
+ pr_log(LOG_ERR, "FW: Init message failed: %m. Aborting.");
+ return -1;
+ }
+
+ pollfds[0].fd = ctx->fd;
+ pollfds[0].events = POLLIN | POLLERR;
+ pollfds[1].fd = ctx->socket;
+ pollfds[1].events = POLLIN | POLLERR;
+
+ for (;;) {
+ /* run through any pending messages */
+ process_msgq(ctx);
+
+ rc = poll(pollfds, 2, -1);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: event poll failed: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!rc)
+ continue;
+
+ if (pollfds[0].revents & POLLIN) {
+ rc = read_prd_msg(ctx);
+ if (!rc)
+ handle_prd_msg(ctx, ctx->msg);
+ }
+
+ if (pollfds[1].revents & POLLIN) {
+ fd = accept(ctx->socket, NULL, NULL);
+ if (fd < 0) {
+ pr_log(LOG_NOTICE, "CTRL: accept failed: %m");
+ continue;
+ }
+ handle_prd_control(ctx, fd);
+ close(fd);
+ }
+ }
+
+ return 0;
+}
+
+static int init_control_socket(struct opal_prd_ctx *ctx)
+{
+ struct sockaddr_un addr;
+ int fd, rc;
+
+ unlink(opal_prd_socket);
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, opal_prd_socket);
+
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (fd < 0) {
+ pr_log(LOG_WARNING, "CTRL: Can't open control socket %s: %m",
+ opal_prd_socket);
+ return -1;
+ }
+
+ rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (rc) {
+ pr_log(LOG_WARNING, "CTRL: Can't bind control socket %s: %m",
+ opal_prd_socket);
+ close(fd);
+ return -1;
+ }
+
+ rc = listen(fd, 0);
+ if (rc) {
+ pr_log(LOG_WARNING, "CTRL: Can't listen on "
+ "control socket %s: %m", opal_prd_socket);
+ close(fd);
+ return -1;
+ }
+
+ pr_log(LOG_INFO, "CTRL: Listening on control socket %s",
+ opal_prd_socket);
+
+ ctx->socket = fd;
+ return 0;
+}
+
+static struct sigaction sigchild_action = {
+ .sa_flags = SA_NOCLDWAIT | SA_RESTART,
+ .sa_handler = SIG_DFL,
+};
+
+static int run_prd_daemon(struct opal_prd_ctx *ctx)
+{
+ char *opal_msg_path;
+ void *buf;
+ int rc, len;
+
+ /* log to syslog */
+ pr_log_daemon_init();
+
+ pr_debug("CTRL: Starting PRD daemon\n");
+
+ ctx->fd = -1;
+ ctx->socket = -1;
+
+ /*
+ * Set up our message buffer. Use opal-msg-size device tree
+ * property to get message buffer size.
+ */
+ rc = asprintf(&opal_msg_path,
+ "%s/ibm,opal/opal-msg-size", devicetree_base);
+ if (rc > 0) {
+ rc = open_and_read(opal_msg_path, &buf, &len);
+ if (rc == 0) {
+ ctx->msg_alloc_len = be32toh(*(__be32 *)buf);
+ free(buf);
+ }
+
+ free(opal_msg_path);
+ }
+
+ if (ctx->msg_alloc_len == 0)
+ ctx->msg_alloc_len = sizeof(*ctx->msg);
+
+ ctx->msg = malloc(ctx->msg_alloc_len);
+ if (!ctx->msg) {
+ pr_log(LOG_ERR, "FW: Can't allocate PRD message buffer: %m");
+ return -1;
+ }
+ memset(ctx->msg, 0, ctx->msg_alloc_len);
+
+ list_head_init(&ctx->msgq);
+
+ i2c_init();
+
+#ifdef DEBUG_I2C
+ {
+ uint8_t foo[128];
+ int i;
+
+ rc = i2c_read(0, 1, 2, 0x50, 2, 0x10, 128, foo);
+ pr_debug("I2C: read rc: %d", rc);
+ for (i = 0; i < sizeof(foo); i += 8) {
+ pr_debug("I2C: %02x %02x %02x %02x %02x %02x %02x %02x",
+ foo[i + 0], foo[i + 1], foo[i + 2], foo[i + 3],
+ foo[i + 4], foo[i + 5], foo[i + 6], foo[i + 7]);
+ }
+ }
+#endif
+ rc = init_control_socket(ctx);
+ if (rc) {
+ pr_log(LOG_WARNING, "CTRL: Error initialising PRD control: %m");
+ goto out_close;
+ }
+
+
+ rc = prd_init(ctx);
+ if (rc) {
+ pr_log(LOG_ERR, "FW: Error initialising PRD channel");
+ goto out_close;
+ }
+
+ if (ctx->hbrt_file_name) {
+ rc = map_hbrt_file(ctx, ctx->hbrt_file_name);
+ if (rc) {
+ pr_log(LOG_ERR, "IMAGE: Can't access hbrt file %s",
+ ctx->hbrt_file_name);
+ goto out_close;
+ }
+ } else {
+ rc = map_hbrt_physmem(ctx, hbrt_code_region_name);
+ if (rc) {
+ /* Fallback to old style ibm,prd-label */
+ rc = map_hbrt_physmem(ctx, hbrt_code_region_name_ibm);
+ if (rc) {
+ pr_log(LOG_ERR, "IMAGE: Can't access hbrt "
+ "physical memory");
+ goto out_close;
+ }
+ }
+ dump_hbrt_map(ctx);
+ }
+
+ pr_debug("IMAGE: hbrt map at %p, size 0x%zx",
+ ctx->code_addr, ctx->code_size);
+
+ fixup_hinterface_table();
+
+ if (!is_fsp_system()) {
+ pnor_load_module(ctx);
+
+ rc = pnor_init(&ctx->pnor);
+ if (rc) {
+ pr_log(LOG_ERR, "PNOR: Failed to open pnor: %m");
+ goto out_close;
+ }
+ } else {
+ /* Disable PNOR function pointers */
+ hinterface.pnor_read = NULL;
+ hinterface.pnor_write = NULL;
+ }
+
+ ipmi_init(ctx);
+
+ pr_debug("HBRT: calling hservices_init");
+ rc = hservices_init(ctx, ctx->code_addr);
+ if (rc) {
+ pr_log(LOG_ERR, "HBRT: Can't initialise HBRT");
+ goto out_close;
+ }
+ pr_debug("HBRT: hservices_init done");
+
+ /* Test a scom */
+ if (ctx->debug) {
+ uint64_t val;
+ pr_debug("SCOM: trying scom read");
+ fflush(stdout);
+ hservice_scom_read(0x00, 0xf000f, &val);
+ pr_debug("SCOM: f00f: %lx", be64toh(val));
+ }
+
+ /*
+ * Setup the SIGCHLD handler to automatically reap the worker threads
+ * we use for memory offlining. We can't do this earlier since the
+ * modprobe helper spawns workers and wants to check their exit status
+ * with waitpid(). Auto-reaping breaks that so enable it just before
+ * entering the attn loop.
+ *
+ * We also setup system call restarting on SIGCHLD since opal-prd
+ * doesn't make any real attempt to handle blocking functions exiting
+ * due to EINTR.
+ */
+ if (sigaction(SIGCHLD, &sigchild_action, NULL)) {
+ pr_log(LOG_ERR, "CTRL: Failed to register signal handler %m\n");
+ return -1;
+ }
+
+ run_attn_loop(ctx);
+ rc = 0;
+
+out_close:
+ pr_debug("CTRL: stopping PRD daemon\n");
+ pnor_close(&ctx->pnor);
+ if (ctx->fd != -1)
+ close(ctx->fd);
+ if (ctx->socket != -1)
+ close(ctx->socket);
+ if (ctx->msg)
+ free(ctx->msg);
+ return rc;
+}
+
+static int send_prd_control(struct control_msg *send_msg,
+ struct control_msg **recv_msg)
+{
+ struct sockaddr_un addr;
+ struct control_msg *msg;
+ int sd, rc, size;
+
+ sd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (!sd) {
+ pr_log(LOG_ERR, "CTRL: Failed to create control socket: %m");
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, opal_prd_socket);
+
+ rc = connect(sd, (struct sockaddr *)&addr, sizeof(addr));
+ if (rc) {
+ pr_log(LOG_ERR, "CTRL: Failed to connect to prd daemon: %m");
+ goto out_close;
+ }
+
+ size = sizeof(*send_msg) + send_msg->data_len;
+ rc = send(sd, send_msg, size, 0);
+ if (rc != size) {
+ pr_log(LOG_ERR, "CTRL: Failed to send control message: %m");
+ rc = -1;
+ goto out_close;
+ }
+
+ size = sizeof(*msg) + MAX_CONTROL_MSG_BUF;
+ msg = malloc(size);
+ if (!msg) {
+ pr_log(LOG_ERR, "CTRL: msg buffer malloc failed: %m");
+ rc = -1;
+ goto out_close;
+ }
+
+ *recv_msg = msg;
+
+ /* wait for our reply */
+ rc = recv(sd, msg, size, 0);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "CTRL: Failed to receive control message: %m");
+ goto out_close;
+
+ } else if (rc != (sizeof(*msg) + msg->data_len)) {
+ pr_log(LOG_WARNING, "CTRL: Short read from control socket");
+ rc = -1;
+ goto out_close;
+ }
+
+ rc = msg->response;
+
+out_close:
+ close(sd);
+ return rc;
+}
+
+static int send_occ_control(struct opal_prd_ctx *ctx, int argc, char *argv[])
+{
+ struct control_msg send_msg, *recv_msg = NULL;
+ unsigned long chip = 0;
+ const char *op;
+ int rc;
+
+ assert(argc >= 1);
+ op = argv[0];
+
+ /* some commands accept a 'chip' argument, so parse it here */
+ if (argc > 1) {
+ char *arg, *end;
+ arg = argv[1];
+ chip = strtoul(arg, &end, 0);
+ if (end == arg) {
+ pr_log(LOG_ERR, "CTRL: invalid argument %s", arg);
+ return -1;
+ }
+ }
+
+ memset(&send_msg, 0, sizeof(send_msg));
+
+ if (!strcmp(op, "enable"))
+ send_msg.type = CONTROL_MSG_ENABLE_OCCS;
+ else if (!strcmp(op, "disable"))
+ send_msg.type = CONTROL_MSG_DISABLE_OCCS;
+
+ else if (!strcmp(op, "reset")) {
+ send_msg.type = CONTROL_MSG_TEMP_OCC_RESET;
+ send_msg.occ_reset.chip = (uint64_t)chip;
+
+ } else if (!strcmp(op, "process-error")) {
+ send_msg.type = CONTROL_MSG_TEMP_OCC_ERROR;
+ send_msg.occ_error.chip = (uint64_t)chip;
+ } else {
+ pr_log(LOG_ERR, "CTRL: Invalid OCC action '%s'", op);
+ return -1;
+ }
+
+ rc = send_prd_control(&send_msg, &recv_msg);
+ if (recv_msg) {
+ if (recv_msg->response || ctx->debug)
+ pr_debug("CTRL: OCC action %s returned status %d", op,
+ recv_msg->response);
+ free(recv_msg);
+ }
+
+ return rc;
+}
+
+static int send_attr_override(struct opal_prd_ctx *ctx, uint32_t argc,
+ char *argv[])
+{
+ struct control_msg *send_msg, *recv_msg = NULL;
+ struct stat statbuf;
+ size_t sz;
+ FILE *fd;
+ int rc;
+
+ rc = stat(argv[0], &statbuf);
+ if (rc) {
+ pr_log(LOG_ERR, "CTRL: stat() failed on the file: %m");
+ return -1;
+ }
+
+ send_msg = malloc(sizeof(*send_msg) + statbuf.st_size);
+ if (!send_msg) {
+ pr_log(LOG_ERR, "CTRL: msg buffer malloc failed: %m");
+ return -1;
+ }
+
+ send_msg->type = CONTROL_MSG_ATTR_OVERRIDE;
+ send_msg->data_len = statbuf.st_size;
+
+ fd = fopen(argv[0], "r");
+ if (!fd) {
+ pr_log(LOG_NOTICE, "CTRL: can't open %s: %m", argv[0]);
+ rc = -1;
+ goto out_free;
+ }
+
+ sz = fread(send_msg->data, 1, send_msg->data_len, fd);
+ fclose(fd);
+ if (sz != statbuf.st_size) {
+ pr_log(LOG_ERR, "CTRL: short read from the file");
+ rc = -1;
+ goto out_free;
+ }
+
+ rc = send_prd_control(send_msg, &recv_msg);
+ if (recv_msg) {
+ if (recv_msg->response || ctx->debug)
+ pr_debug("CTRL: attribute override returned status %d",
+ recv_msg->response);
+ free(recv_msg);
+ }
+
+out_free:
+ free(send_msg);
+ return rc;
+}
+
+static int send_htmgt_passthru(struct opal_prd_ctx *ctx, int argc, char *argv[])
+{
+ struct control_msg *send_msg, *recv_msg = NULL;
+ int rc, i;
+
+ if (!ctx->expert_mode) {
+ pr_log(LOG_WARNING, "CTRL: need to be in expert mode");
+ return -1;
+ }
+
+ send_msg = malloc(sizeof(*send_msg) + argc);
+ if (!send_msg) {
+ pr_log(LOG_ERR, "CTRL: message buffer malloc failed: %m");
+ return -1;
+ }
+
+ send_msg->type = CONTROL_MSG_HTMGT_PASSTHRU;
+ send_msg->data_len = argc;
+
+ if (ctx->debug)
+ pr_debug("CTRL: HTMGT passthru arguments:");
+
+ for (i = 0; i < argc; i++) {
+ if (ctx->debug)
+ pr_debug("argv[%d] = %s", i, argv[i]);
+
+ sscanf(argv[i], "%hhx", &send_msg->data[i]);
+ }
+
+ rc = send_prd_control(send_msg, &recv_msg);
+ free(send_msg);
+
+ if (recv_msg) {
+ if (recv_msg->response || ctx->debug)
+ pr_debug("CTRL: HTMGT passthru returned status %d",
+ recv_msg->response);
+ if (recv_msg->response == 0 && recv_msg->data_len)
+ hexdump(recv_msg->data, recv_msg->data_len);
+
+ free(recv_msg);
+ }
+
+ return rc;
+}
+
+static int send_run_command(struct opal_prd_ctx *ctx, int argc, char *argv[])
+{
+ struct control_msg *send_msg, *recv_msg = NULL;
+ uint32_t size = 0;
+ int rc, i;
+ char *s;
+
+ if (!ctx->expert_mode) {
+ pr_log(LOG_WARNING, "CTRL: need to be in expert mode");
+ return -1;
+ }
+
+ if (ctx->debug) {
+ pr_debug("CTRL: run command arguments:");
+ for (i=0; i < argc; i++)
+ pr_debug("argv[%d] = %s", i, argv[i]);
+ }
+
+ for (i = 0; i < argc; i++)
+ size += (strlen(argv[i]) + 1);
+
+ send_msg = malloc(sizeof(*send_msg) + size);
+ if (!send_msg) {
+ pr_log(LOG_ERR, "CTRL: msg buffer malloc failed: %m");
+ return -1;
+ }
+
+ /* Setup message */
+ send_msg->type = CONTROL_MSG_RUN_CMD;
+ send_msg->run_cmd.argc = argc;
+ send_msg->data_len = size;
+ s = (char *)send_msg->data;
+ for (i = 0; i < argc; i++) {
+ strcpy(s, argv[i]);
+ s = s + strlen(argv[i]) + 1;
+ }
+
+ rc = send_prd_control(send_msg, &recv_msg);
+ free(send_msg);
+ if (recv_msg) {
+ if (!rc)
+ pr_log(LOG_INFO, "Received: %s", recv_msg->data);
+
+ if (recv_msg->response || ctx->debug)
+ pr_debug("CTRL: run command returned status %d",
+ recv_msg->response);
+ free(recv_msg);
+ }
+
+ return rc;
+}
+
+static void usage(const char *progname)
+{
+ printf("Usage:\n");
+ printf("\t%s [--debug] [--file <hbrt-image>] [--pnor <device>]\n",
+ progname);
+ printf("\t%s occ <enable|disable|reset [chip]>\n", progname);
+ printf("\t%s pm-complex reset [chip]>\n", progname);
+ printf("\t%s htmgt-passthru <bytes...>\n", progname);
+ printf("\t%s override <FILE>\n", progname);
+ printf("\t%s run [arg 0] [arg 1]..[arg n]\n", progname);
+ printf("\n");
+ printf("Options:\n"
+"\t--debug verbose logging for debug information\n"
+"\t--pnor DEVICE use PNOR MTD device\n"
+"\t--file FILE use FILE for hostboot runtime code (instead of code\n"
+"\t exported by firmware)\n"
+"\t--stdio log to stdio, instead of syslog\n");
+}
+
+static void print_version(void)
+{
+ extern const char version[];
+ printf("opal-prd %s\n", version);
+}
+
+static struct option opal_diag_options[] = {
+ {"file", required_argument, NULL, 'f'},
+ {"pnor", required_argument, NULL, 'p'},
+ {"debug", no_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {"stdio", no_argument, NULL, 's'},
+ {"expert-mode", no_argument, NULL, 'e'},
+ { 0 },
+};
+
+enum action {
+ ACTION_RUN_DAEMON,
+ ACTION_OCC_CONTROL,
+ ACTION_ATTR_OVERRIDE,
+ ACTION_HTMGT_PASSTHRU,
+ ACTION_RUN_COMMAND,
+};
+
+static int parse_action(const char *str, enum action *action)
+{
+ int rc;
+
+ if (!strcmp(str, "occ")) {
+ *action = ACTION_OCC_CONTROL;
+ rc = 0;
+
+ if (is_fsp_system()) {
+ pr_log(LOG_ERR, "CTRL: occ commands are not "
+ "supported on this system");
+ rc = -1;
+ }
+ } else if (!strcmp(str, "pm-complex")) {
+ *action = ACTION_OCC_CONTROL;
+ rc = 0;
+
+ if (!is_fsp_system()) {
+ pr_log(LOG_ERR, "CTRL: pm-complex commands are not "
+ "supported on this system");
+ rc = -1;
+ }
+ } else if (!strcmp(str, "daemon")) {
+ *action = ACTION_RUN_DAEMON;
+ rc = 0;
+ } else if (!strcmp(str, "override")) {
+ *action = ACTION_ATTR_OVERRIDE;
+ rc = 0;
+ } else if (!strcmp(str, "htmgt-passthru")) {
+ *action = ACTION_HTMGT_PASSTHRU;
+ rc = 0;
+ } else if (!strcmp(str, "run")) {
+ *action = ACTION_RUN_COMMAND;
+ return 0;
+ } else {
+ pr_log(LOG_ERR, "CTRL: unknown argument '%s'", str);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ struct opal_prd_ctx _ctx;
+ enum action action;
+ int rc;
+
+ check_abi();
+
+ ctx = &_ctx;
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->vlog = pr_log_stdio;
+ ctx->use_syslog = true;
+
+ /* Parse options */
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, "f:p:dhse", opal_diag_options, NULL);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'f':
+ ctx->hbrt_file_name = optarg;
+ break;
+ case 'd':
+ ctx->debug = true;
+ break;
+ case 'p':
+ ctx->pnor.path = strndup(optarg, PATH_MAX);
+ break;
+ case 's':
+ ctx->use_syslog = false;
+ break;
+ case 'h':
+ usage(argv[0]);
+ return EXIT_SUCCESS;
+ case 'e':
+ ctx->expert_mode = true;
+ break;
+ case 'v':
+ print_version();
+ return EXIT_SUCCESS;
+ case '?':
+ default:
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind < argc) {
+ rc = parse_action(argv[optind], &action);
+ if (rc)
+ return EXIT_FAILURE;
+ optind++;
+ } else {
+ action = ACTION_RUN_DAEMON;
+ }
+
+ if (is_prd_supported() < 0) {
+ pr_log(LOG_ERR, "CTRL: PowerNV OPAL runtime diagnostic "
+ "is not supported on this system");
+ return -1;
+ }
+
+ switch (action) {
+ case ACTION_RUN_DAEMON:
+ rc = run_prd_daemon(ctx);
+ break;
+ case ACTION_OCC_CONTROL:
+ if (optind >= argc) {
+ pr_log(LOG_ERR, "CTRL: occ command requires "
+ "an argument");
+ return EXIT_FAILURE;
+ }
+
+ rc = send_occ_control(ctx, argc - optind, &argv[optind]);
+ break;
+ case ACTION_ATTR_OVERRIDE:
+ if (optind >= argc) {
+ pr_log(LOG_ERR, "CTRL: attribute override command "
+ "requires an argument");
+ return EXIT_FAILURE;
+ }
+
+ rc = send_attr_override(ctx, argc - optind, &argv[optind]);
+ break;
+ case ACTION_HTMGT_PASSTHRU:
+ if (optind >= argc) {
+ pr_log(LOG_ERR, "CTRL: htmgt passthru requires at least "
+ "one argument");
+ return EXIT_FAILURE;
+ }
+
+ rc = send_htmgt_passthru(ctx, argc - optind, &argv[optind]);
+ break;
+ case ACTION_RUN_COMMAND:
+ if (optind >= argc) {
+ pr_log(LOG_ERR, "CTRL: run command requires "
+ "argument(s)");
+ return EXIT_FAILURE;
+ }
+
+ rc = send_run_command(ctx, argc - optind, &argv[optind]);
+ break;
+ default:
+ break;
+ }
+
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/roms/skiboot/external/opal-prd/opal-prd.h b/roms/skiboot/external/opal-prd/opal-prd.h
new file mode 100644
index 000000000..606317d74
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/opal-prd.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015 IBM Corp. */
+
+#ifndef OPAL_PRD_H
+#define OPAL_PRD_H
+
+#include <syslog.h>
+
+#define pr_debug(fmt, ...) pr_log(LOG_DEBUG, fmt, ## __VA_ARGS__)
+
+void pr_log(int priority, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+
+#endif /* OPAL_PRD_H */
+
diff --git a/roms/skiboot/external/opal-prd/opal-prd.service b/roms/skiboot/external/opal-prd/opal-prd.service
new file mode 100644
index 000000000..dce0dd262
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/opal-prd.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=OPAL PRD daemon
+ConditionVirtualization=false
+ConditionPathExists=/sys/firmware/devicetree/base/ibm,opal/diagnostics
+
+[Service]
+ExecStart=/usr/sbin/opal-prd
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roms/skiboot/external/opal-prd/pnor.c b/roms/skiboot/external/opal-prd/pnor.c
new file mode 100644
index 000000000..b2da7134c
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/pnor.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * PNOR Access (/dev/mtd) for opal-prd
+ *
+ * Copyright 2013-2017 IBM Corp.
+ */
+
+#include <libflash/libffs.h>
+#include <common/arch_flash.h>
+
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+
+#include "pnor.h"
+#include "opal-prd.h"
+
+#define FDT_FLASH_PATH "/proc/device-tree/chosen/ibm,system-flash"
+
+bool pnor_available(struct pnor *pnor)
+{
+ /* --pnor is specified */
+ if (pnor->path) {
+ if (access(pnor->path, R_OK | W_OK) == 0)
+ return true;
+
+ pr_log(LOG_ERR, "PNOR: Does not have permission to read pnor: %m");
+ return false;
+ }
+
+ if (access(FDT_FLASH_PATH, R_OK) == 0)
+ return true;
+
+ return false;
+}
+
+int pnor_init(struct pnor *pnor)
+{
+ int rc;
+
+ if (!pnor)
+ return -1;
+
+ rc = arch_flash_init(&(pnor->bl), pnor->path, false);
+ if (rc) {
+ pr_log(LOG_ERR, "PNOR: Flash init failed");
+ return -1;
+ }
+
+ rc = blocklevel_get_info(pnor->bl, NULL, &(pnor->size), &(pnor->erasesize));
+ if (rc) {
+ pr_log(LOG_ERR, "PNOR: blocklevel_get_info() failed. Can't use PNOR");
+ goto out;
+ }
+
+ rc = ffs_init(0, pnor->size, pnor->bl, &pnor->ffsh, 0);
+ if (rc) {
+ pr_log(LOG_ERR, "PNOR: Failed to open pnor partition table");
+ goto out;
+ }
+
+ return 0;
+out:
+ arch_flash_close(pnor->bl, pnor->path);
+ pnor->bl = NULL;
+ return -1;
+}
+
+void pnor_close(struct pnor *pnor)
+{
+ if (!pnor)
+ return;
+
+ if (pnor->ffsh)
+ ffs_close(pnor->ffsh);
+
+ if (pnor->bl)
+ arch_flash_close(pnor->bl, pnor->path);
+
+ if (pnor->path)
+ free(pnor->path);
+}
+
+void dump_parts(struct ffs_handle *ffs) {
+ int i, rc;
+ uint32_t start, size, act_size;
+ char *name;
+
+ pr_debug("PNOR: %10s %8s %8s %8s",
+ "name", "start", "size", "act_size");
+ for (i = 0; ; i++) {
+ rc = ffs_part_info(ffs, i, &name, &start,
+ &size, &act_size, NULL);
+ if (rc)
+ break;
+ pr_debug("PNOR: %10s %08x %08x %08x",
+ name, start, size, act_size);
+ free(name);
+ }
+}
+
+static int mtd_write(struct pnor *pnor, void *data, uint64_t offset,
+ size_t len)
+{
+ int rc;
+
+ if (len > pnor->size || offset > pnor->size ||
+ len + offset > pnor->size)
+ return -ERANGE;
+
+ rc = blocklevel_smart_write(pnor->bl, offset, data, len);
+ if (rc)
+ return -errno;
+
+ return len;
+}
+
+static int mtd_read(struct pnor *pnor, void *data, uint64_t offset,
+ size_t len)
+{
+ int rc;
+
+ if (len > pnor->size || offset > pnor->size ||
+ len + offset > pnor->size)
+ return -ERANGE;
+
+ rc = blocklevel_read(pnor->bl, offset, data, len);
+ if (rc)
+ return -errno;
+
+ return len;
+}
+
+/* Similar to read(2), this performs partial operations where the number of
+ * bytes read/written may be less than size.
+ *
+ * Returns number of bytes written, or a negative value on failure. */
+int pnor_operation(struct pnor *pnor, const char *name, uint64_t offset,
+ void *data, size_t requested_size, enum pnor_op op)
+{
+ int rc;
+ uint32_t pstart, psize, idx;
+ int size;
+
+ if (!pnor->ffsh) {
+ pr_log(LOG_ERR, "PNOR: ffs not initialised");
+ return -EBUSY;
+ }
+
+ rc = ffs_lookup_part(pnor->ffsh, name, &idx);
+ if (rc) {
+ pr_log(LOG_WARNING, "PNOR: no partiton named '%s'", name);
+ return -ENOENT;
+ }
+
+ ffs_part_info(pnor->ffsh, idx, NULL, &pstart, &psize, NULL, NULL);
+ if (rc) {
+ pr_log(LOG_ERR, "PNOR: unable to fetch partition info for %s",
+ name);
+ return -ENOENT;
+ }
+
+ if (offset > psize) {
+ pr_log(LOG_WARNING, "PNOR: partition %s(size 0x%x) "
+ "offset (0x%lx) out of bounds",
+ name, psize, offset);
+ return -ERANGE;
+ }
+
+ /* Large requests are trimmed */
+ if (requested_size > psize)
+ size = psize;
+ else
+ size = requested_size;
+
+ if (size + offset > psize)
+ size = psize - offset;
+
+ if (size < 0) {
+ pr_log(LOG_WARNING, "PNOR: partition %s(size 0x%x) "
+ "read size (0x%zx) and offset (0x%lx) "
+ "out of bounds",
+ name, psize, requested_size, offset);
+ return -ERANGE;
+ }
+
+ switch (op) {
+ case PNOR_OP_READ:
+ rc = mtd_read(pnor, data, pstart + offset, size);
+ break;
+ case PNOR_OP_WRITE:
+ rc = mtd_write(pnor, data, pstart + offset, size);
+ break;
+ default:
+ rc = -EIO;
+ pr_log(LOG_ERR, "PNOR: Invalid operation");
+ goto out;
+ }
+
+ if (rc < 0)
+ pr_log(LOG_ERR, "PNOR: MTD operation failed");
+ else if (rc != size)
+ pr_log(LOG_WARNING, "PNOR: mtd operation "
+ "returned %d, expected %d",
+ rc, size);
+
+out:
+ return rc;
+}
diff --git a/roms/skiboot/external/opal-prd/pnor.h b/roms/skiboot/external/opal-prd/pnor.h
new file mode 100644
index 000000000..aaaf9c487
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/pnor.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015-2017 IBM Corp */
+
+#ifndef PNOR_H
+#define PNOR_H
+
+#include <libflash/libffs.h>
+#include <libflash/blocklevel.h>
+
+struct pnor {
+ char *path;
+ struct ffs_handle *ffsh;
+ uint64_t size;
+ uint32_t erasesize;
+ struct blocklevel_device *bl;
+};
+
+enum pnor_op {
+ PNOR_OP_READ,
+ PNOR_OP_WRITE,
+};
+
+extern int pnor_operation(struct pnor *pnor, const char *name,
+ uint64_t offset, void *data, size_t size,
+ enum pnor_op);
+
+extern int pnor_init(struct pnor *pnor);
+extern void pnor_close(struct pnor *pnor);
+extern bool pnor_available(struct pnor *pnor);
+
+#endif /*PNOR_H*/
diff --git a/roms/skiboot/external/opal-prd/test/test_pnor.c b/roms/skiboot/external/opal-prd/test/test_pnor.c
new file mode 100644
index 000000000..bc7234ecf
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/test/test_pnor.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2015 IBM Corp. */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <linux/limits.h>
+
+#include <libflash/libffs.h>
+#include <pnor.h>
+
+extern void dump_parts(struct ffs_handle *ffs);
+
+void pr_log(int priority, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+int main(int argc, char **argv)
+{
+ struct pnor pnor;
+ int rc;
+
+ if (argc != 2) {
+ printf("usage: %s [pnor file]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+
+ pnor.path = strndup(argv[1], PATH_MAX);
+
+ rc = pnor_init(&pnor);
+ assert(rc);
+
+ dump_parts(pnor.ffsh);
+
+ pnor_close(&pnor);
+
+ return 0;
+}
diff --git a/roms/skiboot/external/opal-prd/test/test_pnor_ops.c b/roms/skiboot/external/opal-prd/test/test_pnor_ops.c
new file mode 100644
index 000000000..913f2ecf7
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/test/test_pnor_ops.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015-2016 IBM Corp */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#undef ioctl
+#define ioctl(d, req, arg) test_ioctl(d, req, arg)
+
+int test_ioctl(int fd, int req, void *arg)
+{
+ if (req == MEMERASE) {
+ uint8_t *buf;
+ struct erase_info_user *erase = arg;
+
+ buf = malloc(erase->length);
+ memset(buf, 'E', erase->length);
+
+ lseek(fd, erase->start, SEEK_SET);
+ write(fd, buf, erase->length);
+
+ free(buf);
+ }
+
+ return 0;
+}
+
+#include "../pnor.c"
+
+bool compare_data(int fd, const uint8_t *check)
+{
+ uint8_t buf[16];
+ int offset = 0;
+ int bytes_read;
+ int i;
+
+ lseek(fd, 0, SEEK_SET);
+
+ do {
+ bytes_read = read(fd, buf, sizeof(buf));
+ i = 0;
+ while (i < bytes_read)
+ if (buf[i++] != check[offset++])
+ return false;
+ } while (bytes_read == sizeof(buf));
+
+out:
+ lseek(fd, 0, SEEK_SET);
+
+ return true;
+}
+
+void print_buf(uint8_t *buf, size_t len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ printf("\n%06x : ", i);
+
+ printf("%c ", buf[i]);
+ }
+ printf("\n");
+}
+
+void print_file(int fd)
+{
+ uint8_t buf[16];
+ int offset = 0;
+ int bytes_read;
+ int i;
+
+ lseek(fd, 0, SEEK_SET);
+
+ do {
+ bytes_read = read(fd, buf, sizeof(buf));
+ if (bytes_read == 0)
+ break;
+ printf ("%06x : ", offset);
+ for (i = 0; i < bytes_read; ++i)
+ printf("%c ", buf[i]);
+ printf("\n");
+ offset += bytes_read;
+ } while (bytes_read == sizeof(buf));
+
+ lseek(fd, 0, SEEK_SET);
+}
+
+const uint8_t empty[32] = {
+ 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E',
+ 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E',
+ 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E',
+ 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E'};
+
+const uint8_t test_one[32] = {
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'E',
+ 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E'};
+
+const uint8_t test_three[32] = {
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'E',
+ 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M'};
+
+int main(int argc, char **argv)
+{
+ int fd, i, rc;
+ struct pnor pnor;
+ uint8_t data[24];
+ char filename[24];
+
+ strcpy(filename, "/tmp/pnor-XXXXXX");
+
+ fd = mkstemp(filename);
+ if (fd < 0) {
+ perror("mkstemp");
+ return EXIT_FAILURE;
+ }
+ /* So the file disappears when we exit */
+ unlink(filename);
+
+ /* E for empty */
+ memset(data, 'E', sizeof(data));
+ for (i = 0; i < 2; i++)
+ write(fd, data, 16);
+
+ /* Adjust this if making the file smaller */
+ pnor.size = 32;
+
+ /* This is fake. Make it smaller than the size */
+ pnor.erasesize = 4;
+
+ printf("Write: ");
+ memset(data, 'A', sizeof(data));
+ rc = mtd_write(&pnor, fd, data, 0, 23);
+ if (rc == 23 && compare_data(fd, test_one))
+ printf("PASS\n");
+ else
+ printf("FAIL: %d\n", rc);
+
+ printf("Read: ");
+ memset(data, '0', sizeof(data));
+ rc = mtd_read(&pnor, fd, data, 7, 24);
+ if (rc == 24 && !memcmp(data, &test_one[7], 24))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Write with offset: ");
+ memset(data, 'M', sizeof(data));
+ rc = mtd_write(&pnor, fd, data, 24, 8);
+ if (rc == 8 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Write size past the end: ");
+ rc = mtd_write(&pnor, fd, data, 0, 64);
+ if (rc == -1 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL: %d\n", rc);
+
+ printf("Write size past the end with offset: ");
+ rc = mtd_write(&pnor, fd, data, 24, 24);
+ if (rc == -1 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Write with offset past the end: ");
+ rc = mtd_write(&pnor, fd, data, 64, 12);
+ if (rc == -1 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Zero sized write: ");
+ rc = mtd_write(&pnor, fd, data, 0, 0);
+ if (rc == 0 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Zero sized write with offset: ");
+ rc = mtd_write(&pnor, fd, data, 12, 0);
+ if (rc == 0 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Read size past the end: ");
+ rc = mtd_read(&pnor, fd, data, 0, 64);
+ if (rc != 0 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+
+ printf("Read size past the end with offset: ");
+ rc = mtd_read(&pnor, fd, data, 24, 24);
+ if (rc != 0 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Read with offset past the end: ");
+ rc = mtd_read(&pnor, fd, data, 64, 12);
+ if (rc != 0 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Zero sized read: ");
+ rc = mtd_read(&pnor, fd, data, 0, 0);
+ if (rc == 0 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ printf("Zero sized read with offset: ");
+ rc = mtd_read(&pnor, fd, data, 12, 0);
+ if (rc == 0 && compare_data(fd, test_three))
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
+
+ return 0;
+}
diff --git a/roms/skiboot/external/opal-prd/thunk.S b/roms/skiboot/external/opal-prd/thunk.S
new file mode 100644
index 000000000..46355c033
--- /dev/null
+++ b/roms/skiboot/external/opal-prd/thunk.S
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015-2017 IBM Corp */
+
+#include <endian.h>
+#include <asm/unistd.h>
+
+#ifndef __NR_switch_endian
+#define __NR_switch_endian 363
+#endif
+
+/* a constant to use in the SI field of a little-endian D-form instruction */
+#define le_si16(x) (((x & 0xff) << 24) | ((x & 0xff00) << 8))
+
+ .text
+
+ /*
+ * Call into a HBRT BE function
+ * Func desc (opd) will be in BE
+ * Use ldbrx to load from opd
+ */
+
+call_be:
+
+ /* Before we switch, we need to perform some ABI
+ * conversion. We are currently running LE with the
+ * new ABI v2. The GPR content is the same, we do
+ * need save/restore and adjust r2. At this point r11
+ * contain the OPD
+ */
+ nop
+ nop
+
+ /* We first create a stack frame compatible with BE, we
+ * do a big one just in case... we save LR into our caller's
+ * frame and r2 in our own frame. This is a BE formatted
+ * frame so we store it as 40(r1), not 24(r1)
+ */
+ stdu %r1,-128(%r1)
+ mflr %r0
+ std %r0,(128 + 16)(%r1)
+ std %r2,40(%r1)
+
+ /* Grab the target r2 and function pointer */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ ldbrx %r0, 0, %r11
+ li %r2, 8
+ ldbrx %r2, %r2, %r11
+#else
+ ld %r0,0(%r11)
+ ld %r2,8(%r11)
+#endif
+
+ mtlr %r0
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ /* Switch to the "other endian" */
+ li %r0,__NR_switch_endian
+ sc
+
+ /* Branch to LR */
+ .long 0x2100804e /* (byteswapped blrl) */
+
+ /* Switch endian back */
+ .long 0x00000038 | le_si16(__NR_switch_endian)
+ /* byteswapped li %r0,__NR_switch_endian */
+ .long 0x02000044 /* byteswapped sc */
+#else
+ bctrl
+#endif
+ /* Recover our r2, LR, undo stack frame ... */
+ ld %r2,40(%r1)
+ ld %r0,(128+16)(%r1)
+ addi %r1,%r1,128
+ mtlr %r0
+ blr
+
+#define CALL_THUNK(name, idx) \
+ .globl call_##name ;\
+call_##name: ;\
+ ld %r11,hservice_runtime_fixed@got(%r2) ;\
+ ld %r11,(idx * 8)(%r11) ;\
+ b call_be
+
+ /* Instanciate call to HBRT thunks */
+ CALL_THUNK(cxxtestExecute, 1)
+ CALL_THUNK(get_lid_list, 2)
+ CALL_THUNK(occ_load, 3)
+ CALL_THUNK(occ_start, 4)
+ CALL_THUNK(occ_stop, 5)
+ CALL_THUNK(process_occ_error, 6)
+ CALL_THUNK(enable_attns, 7)
+ CALL_THUNK(disable_attns, 8)
+ CALL_THUNK(handle_attns, 9)
+ CALL_THUNK(process_occ_reset, 10)
+ CALL_THUNK(enable_occ_actuation, 11)
+ CALL_THUNK(apply_attr_override, 12)
+ CALL_THUNK(mfg_htmgt_pass_thru, 13)
+ CALL_THUNK(run_command, 14)
+ CALL_THUNK(verify_container, 15)
+ CALL_THUNK(sbe_message_passing, 16)
+ CALL_THUNK(load_pm_complex, 17)
+ CALL_THUNK(start_pm_complex, 18)
+ CALL_THUNK(reset_pm_complex, 19)
+ CALL_THUNK(get_ipoll_events, 20)
+ CALL_THUNK(firmware_notify, 21)
+ CALL_THUNK(prepare_hbrt_update, 22)
+
+ .globl call_hbrt_init
+call_hbrt_init:
+ ld %r11,hbrt_entry@got(%r2)
+ b call_be
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ /* Callback from HBRT, stack conversion and call into C code,
+ * we arrive here from the thunk macro with r11 containing the
+ * target function and r2 already set from the OPD.
+ */
+call_le:
+ /* Create a LE stack frame, save LR */
+ stdu %r1,-32(%r1)
+ mflr %r0
+ std %r0,(32+16)(%r1)
+
+ /* Branch to original function */
+ mtlr %r12
+ blrl
+
+ /* Restore stack and LR */
+ ld %r0,(32+16)(%r1)
+ addi %r1,%r1,32
+ mtlr %r0
+
+ /* Switch endian back to BE */
+ li %r0,__NR_switch_endian
+ sc
+
+ /* Return to BE */
+ .long 0x2000804e /* byteswapped blr */
+
+ /* Callback from HBRT. There is one entry point per function.
+ *
+ * We assume the proper r2 is already set via the OPD, so we grab our
+ * target function pointer in r12 and jump to call_le
+ */
+#define CALLBACK_THUNK(name) \
+ .pushsection ".text","ax" ;\
+ .globl name##_thunk ;\
+name##_thunk: ;\
+ .long 0x00000038 | le_si16(__NR_switch_endian) ;\
+ /* byteswapped li %r0,__NR_switch_endian */ ;\
+ .long 0x02000044 /* byteswapped sc */ ;\
+ ld %r12,name@got(%r2) ;\
+ b call_le ;\
+ .popsection ;\
+ .pushsection ".data.thunk_opd","aw" ;\
+1: .llong name##_thunk, .TOC., 0 ;\
+ .popsection ;\
+ .llong 1b
+#else /* __BYTE_ORDER == __LITTLE_ENDIAN */
+#define CALLBACK_THUNK(name) \
+ .llong name
+#endif
+
+#define DISABLED_THUNK(name) .llong 0x0
+
+ /* Here's the callback table generation. It creates the table and
+ * all the thunks for all the callbacks from HBRT to us
+ */
+ .data
+ .globl hinterface
+ .globl __hinterface_start
+__hinterface_start:
+hinterface:
+ /* HBRT interface version */
+ .llong 1
+
+ /* Callout pointers */
+ CALLBACK_THUNK(hservice_puts)
+ CALLBACK_THUNK(hservice_assert)
+ CALLBACK_THUNK(hservice_set_page_execute)
+ CALLBACK_THUNK(hservice_malloc)
+ CALLBACK_THUNK(hservice_free)
+ CALLBACK_THUNK(hservice_realloc)
+ DISABLED_THUNK(hservice_send_error_log)
+ CALLBACK_THUNK(hservice_scom_read)
+ CALLBACK_THUNK(hservice_scom_write)
+ DISABLED_THUNK(hservice_lid_load)
+ DISABLED_THUNK(hservice_lid_unload)
+ CALLBACK_THUNK(hservice_get_reserved_mem)
+ CALLBACK_THUNK(hservice_wakeup)
+ CALLBACK_THUNK(hservice_nanosleep)
+ DISABLED_THUNK(hservice_report_occ_failure)
+ CALLBACK_THUNK(hservice_clock_gettime)
+ CALLBACK_THUNK(hservice_pnor_read)
+ CALLBACK_THUNK(hservice_pnor_write)
+ CALLBACK_THUNK(hservice_i2c_read)
+ CALLBACK_THUNK(hservice_i2c_write)
+ CALLBACK_THUNK(hservice_ipmi_msg)
+ CALLBACK_THUNK(hservice_memory_error)
+ CALLBACK_THUNK(hservice_get_interface_capabilities)
+ DISABLED_THUNK(hservice_map_phys_mem)
+ DISABLED_THUNK(hservice_unmap_phys_mem)
+ DISABLED_THUNK(hservice_hcode_scom_update)
+ CALLBACK_THUNK(hservice_firmware_request)
+.globl __hinterface_pad
+__hinterface_pad:
+ /* Reserved space for future growth */
+ .space 27*8,0
+.globl __hinterface_end
+__hinterface_end:
+ /* Eye catcher for debugging */
+ .llong 0xdeadbeef
+
diff --git a/roms/skiboot/external/pci-scripts/phberr.py b/roms/skiboot/external/pci-scripts/phberr.py
new file mode 100755
index 000000000..5f295fdc9
--- /dev/null
+++ b/roms/skiboot/external/pci-scripts/phberr.py
@@ -0,0 +1,658 @@
+#!/usr/bin/env python3
+
+import sys
+import ppc
+import re
+
+# Mnemonic PHB_ESR - Address Offset 0x0C80 - phbErrorStatusRegister
+phb_esr_bits = [
+ (0, "ETU/RSB Request Address Error"),
+ (1, "Fundamental A Request Address Error"),
+ (2, "Fundamental A Request Size/Alignment Error"),
+ (3, "Fundamental A PCI CFG Addr/Size Error"),
+ (4, "Fundamental A IODA Table Access Error"),
+ (5, "Fundamental A Internal Registers Parity Error"),
+ (6, "PHB Error Registers Request Address Error"),
+ (7, "PHB Error Registers Request Size/Alignment Error"),
+ (8, "Fundamental B Request Address Error"),
+ (9, "Fundamental B Request Size/Alignment Error"),
+ (10, "Fundamental B Internal Registers Parity Error"),
+ (11, "Internal Bus Logic Bad PCIE Macro Request Address"),
+ (12, "Debug Request Address Error"),
+ (13, "Debug Request Size/Alignment Error"),
+ (14, "Debug Internal Registers Parity Error"),
+ (15, "Internal Bus Logic State Machine One-Hot Error"),
+ (16, "UV Page Request Address Error"),
+ (17, "UV Page Request Size/Alignment Error"),
+ (18, "UV Page Internal Registers Parity Error"),
+ (20, "RXE_ARB OR Error Status"),
+ (21, "RXE_MRG OR Error Status"),
+ (22, "RXE_TCE OR Error Status"),
+ (23, "TXE OR Error Status"),
+ (24, "pcie_etu_regb_err_inf"),
+ (25, "pcie_etu_regb_err_erc"),
+ (26, "pcie_etu_regb_err_fat"),
+ (27, "bus_regs_req_wr_data_p_e"),
+ (28, "SCOM HV Indirect Access Error"),
+ (29, "SCOM UV Indirect Access Error"),
+ (30, "SCOM Internal Registers Parity Error"),
+ (31, "SCOM Satellite Finite State Machine Error"),
+]
+
+# Mnemonic TXE_ESR - Address Offset 0x0D00 - txeFirstErrorStatus
+txe_esr_bits = [
+ (0, "AIB Command Invalid"),
+ (2, "AIB Address Decode Error"),
+ (3, "AIB Size Invalid"),
+ (4, "AIB Cmd Ctrls Parity Error"),
+ (5, "AIB Data Ctrls Parity Error"),
+ (8, "AIB Alignment Error"),
+ (9, "AIB Cmd Bus Parity Error"),
+ (10, "AIB Data Bus UE ECC Error"),
+ (11, "AIB Data Ctrls Sequence Error"),
+ (12, "AIB Data Bus CE ECC Error"),
+ (13, "TCE Rd Response DAT_ERR Indication"),
+ (14, "AIB Command Credits Error"),
+ (15, "AIB Data Credits Error"),
+ (16, "BLIF Controls Parity Error"),
+ (17, "CFG Write Error CA or UR response"),
+ (18, "BLIF Forward Progress Timeout"),
+ (19, "MMIO RD Pending Error"),
+ (20, "MMIO WR Pending Error"),
+ (21, "MMIO CFG Pending Error"),
+ (22, "MMIO Write DAT_ERR Indication"),
+ (23, "CI Store Data Fifo Error"),
+ (24, "CFG Enable Error, RRB"),
+ (25, "CFG Size Error"),
+ (26, "CFG Bus Address Error"),
+ (27, "CFG Link Down Error"),
+ (28, "PAPR TXE Injection Error Triggered"),
+ (29, "CFG Write Request Timeout"),
+ (30, "PAPR TXE Injection Error Triggered"),
+ (36, "CI Trigger Buffer ECC Correctable Error"),
+ (37, "CI Trigger Buffer ECC Uncorrectable Error"),
+ (38, "CI Trigger Buffer Stage Data Parity Error"),
+ (40, "MMIO BAR Table (MBT) Parity Error"),
+ (42, "MMIO Domain Table (MDT) ECC Correctable Error"),
+ (43, "MMIO Domain Table (MDT) ECC Uncorrectable Error"),
+ (44, "MMIO Domain Table (MDT) Stage Parity Error"),
+ (45, "MMIO Domain Table (MDT) Stage Valid Error"),
+ (46, "AIB Data Special Uncorrectable Error (SUE)"),
+ (47, "MMIO Domain Table (MDT)"),
+ (48, "P2P Store Data Fifo Error"),
+ (49, "EPAT Table Parity Error"),
+ (50, "MMIO Cmd Parity Error"),
+ (51, "BLIF1 Reg Parity Error"),
+ (52, "P2P1 Reg Parity Error"),
+ (53, "P2P WR Pending Error"),
+ (54, "CRW Onehot Error"),
+ (55, "CRW Pending Error"),
+ (56, "RRB Parity Error"),
+ (57, "RRB Size/Alignment Error"),
+ (58, "s_bad_addr_e_q"),
+ (59, "s_req_size_align_e_q"),
+]
+
+# Mnemonic RXE_ARB_ESR - Address Offset 0x0D80 - phbRxeArbErrorStatus
+rxe_arb_bits = [
+ (0, "BLIF Inbound CA Completion Error"),
+ (1, "BLIF Inbound UR Completion Error"),
+ (2, "MSI Size Error"),
+ (3, "MSI Address Alignment Error"),
+ (5, "BLIF Inbound Header ECC Correctable (CE)"),
+ (6, "BLIF Inbound Header ECC Uncorrectable (UE)"),
+ (7, "ARB Stage Valid Error"),
+ (8, "TCE Tag Release Unused"),
+ (9, "TCE Tag Used, Not Free"),
+ (10, "ARB MMIO Buffer Overflow"),
+ (11, "ARB MMIO Buffer Underflow"),
+ (12, "ARB MMIO Internal Parity Error"),
+ (13, "ARB DMA Buffer Overflow"),
+ (14, "ARB DMA Buffer Underflow"),
+ (15, "ARB DMA Internal Parity Error"),
+ (16, "BLIF Header Control Bits Parity Error"),
+ (17, "BLIF Data Control Bits Parity Error"),
+ (18, "BLIF Unsupported Request (UR) Error"),
+ (19, "BLIF Completion Timeout Error"),
+ (20, "SEID Table ECC Correctable (CE)"),
+ (21, "SEID Table ECC Uncorrectable (UE)"),
+ (22, "NBW Size Error"),
+ (23, "DEC IODA Table Fatal Error"),
+ (24, "TLP Poisoned Error"),
+ (25, "MIST ECC Correctable Error"),
+ (26, "IODA TVT Entry Invalid"),
+ (27, "MSI PE# Mismatch"),
+ (28, "IODA TVT Address"),
+ (29, "TVT ECC Correctable Error"),
+ (30, "TVT ECC Uncorrectable Error"),
+ (31, "MIST ECC Uncorrectable Error"),
+ (32, "PELT-V BAR Disabled Error"),
+ (33, "IODA Table Parity Error"),
+ (34, "PCT Timeout"),
+ (35, "PCT Unexpected Completion"),
+ (36, "PCT Parity Error"),
+ (37, "DEC Stage Valid Error"),
+ (38, "DEC Stage Parity Error"),
+ (39, "PAPR Inbound Injection Error Triggered"),
+ (40, "DMA/MSI: RTE PE Number"),
+ (41, "RTT BAR Disabled Error"),
+ (42, "RTC Internal Parity Error"),
+ (43, "RTC Queue Overflow"),
+ (44, "RTC Queue Underflow"),
+ (45, "RTC Stage Valid Error"),
+ (46, "RTC RCAM Bad State Error"),
+ (47, "RTC RCAM Multiple Hit Error"),
+ (48, "RRB Parity Error"),
+ (49, "RRB request Size / Alignment Error"),
+ (50, "s_bad_addr_e_q"),
+ (51, "s_req_size_align_e_q"),
+ (54, "Discontiguous DMA Write Fragmentation"),
+ (55, "LIST Table Parity Error"),
+ (56, "LKP PEST Data Queue Error"),
+ (57, "PCIE Fatal Error Message Received"),
+ (58, "PCIE Nonfatal Error Message Received"),
+ (59, "PCIE Correctable Error Message Received"),
+]
+
+#Mnemonic RXE_MRG_ESR - Address Offset 0x0E00, phbRxeMrgErrorStatus
+rxe_mrg_bits = [
+ (8, "MRG TMB Allocation Error"),
+ (9, "MRG TMB Response Invalid"),
+ (10, "MRG TMB Response Ready Error"),
+ (11, "MRG MMIO Queue Overflow Error"),
+ (12, "MRG MMIO Queue Underflow Error"),
+ (13, "MRG MMIO Internal Parity Error"),
+ (14, "MRG DMA Queue Overflow Error"),
+ (15, "MRG DMA Queue Underflow Error"),
+ (16, "MRG DMA Internal Parity Error"),
+ (17, "MRG Migration Register Table"),
+ (18, "MRG Migration Register Table"),
+ (20, "s_bad_addr_e_q"),
+ (21, "s_req_size_align_e_q"),
+ (22, "RRB Parity Error"),
+ (23, "RRB request Size / Alignment Error"),
+ (24, "DSP AIB TX Timeout Error"),
+ (25, "Reserved (vA4.1)"),
+ (26, "DSP AIB TX CMD Credit Parity Error"),
+ (28, "DSP AIB TX DAT Credit Parity Error"),
+ (30, "DSP Command Credit Overflow Error"),
+ (31, "DSP Command Credit Underflow Error"),
+ (32, "DSP Command Credit Parity Error"),
+ (33, "DSP Data Credit Overflow Error"),
+ (34, "DSP Data Credit Underflow Error"),
+ (35, "DSP Data Credit Parity Error"),
+ (36, "DSP Completion State Machine One-Hot Error"),
+ (37, "DSP Write Thread State Machine One-Hot Error"),
+ (38, "DSP DMA Secure Address Error (vA4.2)"),
+ (39, "DSP MSI Interrupt Notification Secure Address"),
+ (40, "DSP TREQ ECC Correctable Error"),
+ (41, "DSP TREQ ECC Uncorrectable Error"),
+ (42, "DSP MMIO Queue Overflow Error"),
+ (43, "DSP MMIO Queue Underflow Error"),
+ (44, "DSP MMIO Internal Parity Error"),
+ (45, "DSP DMA Queue Overflow Error"),
+ (46, "DSP DMA Queue Underflow Error"),
+ (47, "DSP DMA Internal Parity Error"),
+ (48, "DSP Read Thread State Machine One-Hot Error"),
+ (49, "DSP Table State Machine One-Hot Error"),
+ (50, "DSP NBW State Machine One-Hot Error"),
+ (51, "DSP TSM PEST BAR Disabled Error"),
+ (56, "IPD ECC Correctable Error"),
+ (57, "IPD ECC Uncorrectable Error"),
+ (58, "ICPLD ECC Correctable Error"),
+ (59, "ICPLD ECC Uncorrectable Error"),
+ (60, "NBWD ECC Correctable Error"),
+ (61, "NBWD ECC Uncorrectable Error"),
+ (63, "pb_etu_ai_rx_raise_fence"),
+]
+
+
+# Mnemonic RXE_TCE_ESR - Address Offset 0x0E80 - phbRxeTceErrorStatus
+rxe_tce_bits = [
+ (0, "TCE CMP Internal Parity Error"),
+ (1, "TCE Request Page Access Error"),
+ (2, "TCE Response Page Access Error"),
+ (3, "TCE CMP Queue Overflow"),
+ (4, "TCE CMP Queue Underflow"),
+ (5, "TCE Secure Address Error"),
+ (6, "TCE Cache Bad State Error"),
+ (7, "TCE Cache Multi-Way Hit Error"),
+ (8, "TCE Request Timeout Error"),
+ (9, "TCE TCR ECC Correctable Error"),
+ (10, "TCE TCR ECC Uncorrectable Error"),
+ (11, "TCE TDR ECC Correctable Error"),
+ (12, "TCE TDR ECC Uncorrectable Error"),
+ (13, "TCE Unexpected Response Error"),
+ (14, "RRB Parity Error"),
+ (15, "RRB request Size / Alignment Error"),
+ (16, "TCE RES Internal Parity Error"),
+ (17, "s_bad_addr_e_q"),
+ (18, "s_req_size_align_e_q"),
+ (19, "TCE RES Queue Overflow"),
+ (20, "TCE RES Queue Underflow"),
+ (21, "TCE Response Data Parity Error"),
+ (22, "TCE TCLB CAM Bad State Error"),
+ (23, "TCE TCLB CAM Multi-Hit Error"),
+ (24, "TCE Kill Internal Parity Error"),
+ (25, "TCE THASH Array ECC Correctable Error"),
+ (26, "TCE THASH Array ECC Uncorrectable Error"),
+ (27, "TCE TCLB TDAT ECC Correctable Error"),
+ (28, "TCE TCLB TDAT ECC Uncorrectable Error"),
+ (29, "TCE Kill State Machine One-Hot Error"),
+ (30, "TCE Kill Queue Overflow"),
+ (31, "TCE Kill Queue Underflow"),
+ (32, "TCE Request Secure Address Register"),
+ (33, "TCE Response Secure Address Register"),
+]
+
+
+#Mnemonic PBL_ESR - Address Offset 0x1900 - phbPblErrorStatus
+pbl_esr_bits = [
+ (0, "pb_err_p_fe_tlif_rx_par_e Parity error detected on TLIF Receive interface."),
+ (1, "pb_err_p_fe_tlif_tx_par_e Parity error detected on TLIF Transmit interface."),
+ (2, "pb_err_p_fe_blif_out_par_e"),
+ (3, "pb_err_p_fe_blif_in_par_e"),
+ (4, "pb_err_p_fe_int_par_e"),
+ (5, "pb_err_p_fe_toc_cred_e"),
+ (6, "pb_err_p_fe_ocf_par_e"),
+ (7, "pb_err_p_fe_ocf_prot_e"),
+ (12, "pb_err_p_fe_pct_erq_overflow_e"),
+ (13, "pb_err_p_fe_pct_erq_underflow_e"),
+ (14, "pb_err_p_fe_pct_onp_tags_rls_unused_e"),
+ (15, "pb_err_p_fe_pct_onp_tags_used_notfree_e"),
+ (16, "pb_err_p_fe_pct_onp_tags_used_unexp_e"),
+ (17, "pb_err_p_fe_bct_onp_tags_rls_unused_e"),
+ (18, "pb_err_p_fe_bct_onp_tags_used_notfree_e"),
+ (19, "pb_err_p_fe_ib_bct_rd_inv"),
+ (20, "pb_err_p_fe_ob_buffer_overflow_e"),
+ (21, "pb_err_p_fe_ob_buffer_underflow_e"),
+ (22, "pb_err_p_fe_ib_buffer_overflow_e"),
+ (23, "pb_err_p_fe_ib_buffer_underflow_e"),
+ (24, "pb_err_p_fe_ib_d_ecc_ue"),
+ (25, "pb_err_p_fe_ib_h_ecc_ue"),
+ (26, "pb_err_p_fe_ob_d_ecc_ue"),
+ (27, "pb_err_p_fe_ob_h_ecc_ue"),
+ (28, "pb_err_p_fe_ocf_ecc_ue"),
+ (32, "pb_err_p_fe_tx_pst_discard_e"),
+ (33, "pb_err_p_inf_tx_npst_discard_e"),
+ (34, "pb_err_p_fe_nbw_tlp_e"),
+ (36, "pb_err_p_fe_pci_rcv_cpl_ca_e"),
+ (37, "pb_err_p_fe_pci_rcv_cpl_crs_e"),
+ (38, "pb_err_p_fe_pci_rcv_cpl_rsvd_e"),
+ (39, "pb_err_p_fe_pci_rcv_cpl_ur_e"),
+ (40, "pb_err_p_fe_pci_rcv_ecrc_e"),
+ (41, "pb_err_p_fe_pci_rcv_malf_tlp_e"),
+ (42, "pb_err_p_fe_pci_rcv_overflow_e"),
+ (43, "pb_err_p_fe_pci_rcv_poisoned_tlp_e"),
+ (44, "pb_err_p_fe_pci_rcv_unexp_cpl_e"),
+ (45, "pb_err_p_fe_pci_rcv_unsup_req_e"),
+ (46, "pb_err_p_fe_pci_sig_cpl_abort_e"),
+ (47, "pb_err_p_fe_pci_sig_cpl_timeout_e"),
+ (48, "pb_err_p_fe_pci_sig_poisoned_tlp_e"),
+ (52, "pb_err_p_inf_out_trans_to_pst_e"),
+ (53, "pb_err_p_inf_out_trans_to_npst_e"),
+ (54, "pb_err_p_inf_out_trans_to_cpl_e"),
+ (56, "pb_err_p_inf_ib_d_ecc_ce"),
+ (57, "pb_err_p_inf_ib_h_ecc_ce"),
+ (58, "pb_err_p_inf_ob_d_ecc_ce"),
+ (59, "pb_err_p_inf_ob_h_ecc_ce"),
+ (60, "pb_err_p_inf_ocf_ecc_ce"),
+ (62, "PBL Bad Register Address Error"),
+ (63, "PBL Register Parity Error"),
+]
+
+# Mnemonic REGB_ESR - Address Offset 0x1C00 - phbRegbErrorStatus
+regb_esr_bits = [
+ (0, "REGB Internal Register Parity Error"),
+ (1, "PBL Internal Register Parity Error"),
+ (2, "Invalid Address Decode Error"),
+ (3, "Register Access Invalid Address+Size Error"),
+ (5, "Register State Machine or Other Internal Error"),
+ (6, "PCI CFG Core Registers Parity Error"),
+ (7, "Register access to CFG core while in reset error."),
+ (8, "PCIE Link Down"),
+ (9, "PCIE Link Up"),
+ (10, "PCIE Link Auto Bandwidth Event Status"),
+ (11, "PCIE Link BW Management Event Status"),
+ (25, "PBL Error Trap: INF Error"),
+ (26, "PBL Error Trap: ERC Error"),
+ (27, "PBL Error Trap: FAT Error"),
+ (28, "tldlpo_dl_mon_rxreceivererror(0)"),
+ (29, "tldlpo_dl_mon_rxreceivererror(1)"),
+ (30, "tldlpo_dl_mon_rxreceivererror(2)"),
+ (32, "DL_EC08_BADDLLP"),
+ (33, "DL_EC08_BADTLP"),
+ (34, "DL_EC08_DLLPE"),
+ (35, "DL_EC08_RECEIVERERROR"),
+ (36, "DL_EC08_ REPLAYROLLOVER"),
+ (37, "DL_EC08_REPLAYTIMEOUT"),
+ (39, "DL_INTERNALERROR"),
+ (40, "DL_LB_ERROR"),
+ (41, "DL_RX_MALFORMED"),
+ (42, "DL_RX_NULLIFY"),
+ (43, "DL_RX_OVERFLOW"),
+ (44, "DL_TX_CORRERROR"),
+ (45, "DL_TX_UNCORRERROR"),
+ (46, "TL_EC08_FCPE"),
+ (48, "Replay ECC Correctable Error (CE)"),
+ (49, "Replay ECC UnCorrectable Error (UE)"),
+ (50, "Bad DLLP Error Count Saturated"),
+ (51, "Bad TLP Error Count Saturated"),
+ (52, "Receiver Error Count Saturated"),
+ (53, "DLLPE Error Count Saturated"),
+ (58, "pbl_ptl_dl_al_rx_initcredit_p_e"),
+ (59, "pbl_ptl_dl_al_rx_updatecredit_p_e"),
+ (60, "PTL Core DLIF Protocol Error"),
+ (61, "PTL Core TLIF Protocol Error"),
+ (62, "PTL Core Internal Parity Error"),
+]
+
+# FIXME: use the long desc
+nfir_bits = [
+ (0, "bar_pe"), # One of the BARs or BAR Mask Register parity error.
+ (1, "nonbar_pe"), # Any non-BAR parity error.
+ (2, "PB_to_PEC_ce"), # ECC correctable error off of outbound SMP interconnect.
+ (3, "PB_to_PEC_ue"), # ECC uncorrectable error off of outbound SMP interconnect.
+ (4, "PB_to_PEC_sue"), # ECC special uncorrectable error off of outbound SMP interconnect
+ (5, "ary_ecc_ce"), # ECC correctable error on an internal array.
+ (6, "ary_ecc_ue"), # ECC uncorrectable error on an internal array.
+ (7, "ary_ecc_sue"), # ECC special uncorrectable error on an internal array.
+ (8, "register_array_pe"), # Parity error on an internal register file.
+ (9, "pb_interface_pe"), # Parity error on the PB interface (address/aTag/tTag/rTAG).
+ (10, "pb_data_hang_errors"), # Any SMP interconnect data hang poll error (only checked for CI stores).
+ (11, "pb_hang_errors"), # Any SMP interconnect command hang error (domestic address range).
+ (12, "rd_are_errors"), # SMP interconnect address error (ARE) detected by a DMA read.
+ (13, "nonrd_are_errors"), # SMP interconnect address error detected by a DMA write or an interrupt engine.
+ (14, "pci_hang_error"), # PBCQ detected that the PCI load, store, EOI, or DMA read response did not make forward progress.
+ (15, "pci_clock_error"), # PBCQ has detected that the PCI clock has stopped.
+ (16, "PFIR_freeze"), # This is the freeze signal from the PFIR freeze output.
+ (17, "hw_errors"), # Any miscellaneous hardware error.
+ (18, "UnsolicitiedPBData"), # The PEC received data with an rTAG matching a queue that was not expecting data or too much data was received.
+ (19, "UnExpectedCResp"), # PEC received an unexpected combined response.
+ (20, "InvalidCResp"), # PEC received an invalid combined response.
+ (21, "PBUnsupportedSize"), # PEC received a CI load/store that hits a BAR but is an unsupported size or address alignment.
+]
+
+pfir_bits = [
+ (0, "register_pe"), # PBAIB register parity error.
+ (1, "hardware_error"), # Hardware error.
+ (2, "AIB_intf_error"), # AIB interface error.
+ (3, "ETU_Reset_error"), # ETU reset error.
+ (4, "PEC_scom_error"), # Common PEC SCOM error.
+ (5, "scomfir_error0"), # SCOM Error bit 0
+ (6, "scomfir_error1"), # SCOM Error bit 1
+]
+
+class PHBError:
+ reg_bits = {
+ "NEST FIR": nfir_bits,
+ "PCI FIR": pfir_bits,
+ "phbErrorStatus": phb_esr_bits,
+ "phbTxeErrorStatus": txe_esr_bits,
+ "phbRxeArbErrorStatus": rxe_arb_bits,
+ "phbRxeMrgErrorStatus": rxe_mrg_bits,
+ "phbRxeTceErrorStatus": rxe_tce_bits,
+ "phbRegbErrorStatus": regb_esr_bits,
+ "phbPblErrorStatus": pbl_esr_bits,
+ }
+
+ def __str__(self):
+ s = ""
+ for k, v in self.regs.items():
+ s += "{:30s} - {:#018x} - {}\n".format(k, v, ppc.setbits(v))
+ return s
+
+ def __init__(self, timestamp = 0):
+ self.timestamp = timestamp
+ self.pest = []
+ self.regs = {}
+
+ # NB: Value is a str, FIXME: Work out how to use python's type annotations
+ def set_reg(self, reg, value):
+ reg = reg.replace(" ", "")
+ if not self.regs.get(reg):
+ self.regs[reg] = value
+ return True
+ return False
+
+ def get_reg(self, reg):
+ reg = reg.replace(" ", "")
+ v = self.regs.get(reg)
+ if v:
+ return v
+ return 0
+
+ # NB: pest entries should be inserted in sort order, but it might be a good
+ # idea to explicitly sort them by PE number
+ def set_pest(self, pe, pesta, pestb):
+ self.pest.append((pe, pesta, pestb))
+
+ def get_pest(self, pe_number):
+ for pe, a, b in self.pest:
+ if pe == pe_number:
+ return (a, b)
+ return None
+
+ def header(self):
+ return self.timestamp
+
+ # TODO: move the formatting out of here and into the main loop
+ def show_errs(self):
+ out = ""
+ for reg_name,reg_bits in self.reg_bits.items():
+ reg_value = self.get_reg(reg_name)
+ parts = reg_name.split("Error");
+ if len(parts) > 1:
+ first_name = "{:s}FirstError{:s}".format(parts[0], parts[1])
+ first_value = self.get_reg(first_name)
+
+ # skiboot spells it wrong, so check Frst too
+ if first_value == 0:
+ frst_name = "{:s}FrstError{:s}".format(parts[0], parts[1])
+ first_value = self.get_reg(frst_name)
+ else:
+ first_value = 0
+
+ if reg_value == 0:
+ continue
+ out += "{} = {:016x}:\n".format(reg_name, reg_value);
+
+ for bit in reg_bits:
+ if ppc.ppcbit(bit[0]) & reg_value:
+ bang = "!" if (ppc.ppcbit(bit[0]) & reg_value & first_value) == ppc.ppcbit(bit[0]) else ""
+ out += "{:s}\t{:2d} - {}\n".format(bang, bit[0], bit[1])
+ out += "\n"
+
+ if len(self.pest) == 0:
+ return out
+
+ out += "PEST entries:\n"
+ for pe, pesta, pestb in self.pest:
+ out += "\tPEST[{:03x}] = {:016x} {:016x}\n".format(pe, pesta, pestb)
+
+ return out
+
+
+
+def parse_opal_log(log_text):
+ # Patterns to match:
+ #
+ # [ 938.249526636,3] PHB#0030[8:0]: NEST FIR WOF=0000800000000000
+ # [ 938.250657886,3] PHB#0030[8:0]: slotStatus = 00402000
+ # [ 938.254305278,3] PHB#0030[8:0]: PEST[511] = 3740002a01000000 0000000000000000
+ #
+ phblog_re = re.compile("" +
+ "^\[\s*[\d.,]+] " + # skiboot log header
+ "(PHB#....\[.:.]):" + # PHB name
+ "\s+" + # whitespace between the PHB and register name
+ "([^:=]+)" + # register name, NB: this might have some trailing WS
+ "=\s*" + # the '=' seperating name and value, along with the whitespace
+ "([a-fA-F\d ]+)") # register value(s)
+
+ # this alone isn't really sufficent. There's a few cases that can cause a register
+ # dump to be generated (e.g. when the link is retrained we do a reg dump)
+ new_log_marker = re.compile("" +
+ "^\[ [\d.,]+] " +
+ "(PHB#....\[.:.]): " +
+ "PHB Freeze/Fence detected !")
+
+ # Store the current register set for each PHB. Keep in mind that we can have register
+ # dumps from different PHBs being interleaved in the register log.
+ current = {}
+
+ # list discovered error logs
+ error_logs = []
+
+ # Match things and split them on a per-PHB basis. We can get multiple PHB error logs
+ # printed interleaved in the skiboot log if there are multiple PHBs frozen.
+ for l in log_text.split("\n"):
+ m = new_log_marker.match(l)
+ if not m:
+ m = phblog_re.match(l)
+ if not m:
+ continue
+
+ match = m.groups()
+ phb = match[0]
+
+ # new log marker, save the current log and create a new one to store register values in
+ log = current.get(phb)
+ if not log:
+ current[phb] = PHBError(l);
+ elif len(match) == 1:
+ error_logs.append(current[phb])
+ current[phb] = PHBError(l) # create a new log object
+ log = current[phb]
+
+ if len(match) > 1:
+ if match[1].find("PEST") >= 0: # PEST entry
+ # NB: unlike .match() .search() scans the whole string
+ m = re.search("PEST\[([\da-fA-F]+)] = ([\da-fA-F]+) ([\da-fA-F]+)", l)
+ pe, pesta, pestb = [int(i, 16) for i in m.groups()]
+ current[phb].set_pest(pe, pesta, pestb)
+ else: # Normal register
+ name = match[1].strip()
+ value = int(match[2].strip(), 16)
+
+ ok = current[phb].set_reg(name, value)
+
+ # If we have duplicate registers then we're in a new log context
+ # so stash the current one and init a new one.
+ if not ok:
+ error_logs.append(current[phb])
+ current[phb] = PHBError(l)
+ current[phb].set_reg(name, value)
+
+ # save all the logs we're still processing
+ for k,v in current.items():
+ error_logs.append(v)
+
+ return error_logs
+
+
+'''
+Mar 25 10:01:49 localhost kernel: PHB4 PHB#48 Diag-data (Version: 1)
+Mar 25 10:01:49 localhost kernel: brdgCtl: 00000002
+Mar 25 10:01:49 localhost kernel: RootSts: 00010020 00402000 a1030008 00100107 00002000
+Mar 25 10:01:49 localhost kernel: RootErrSts: 00000000 00000000 00000001
+Mar 25 10:01:49 localhost kernel: PhbSts: 0000001c00000000 0000001c00000000
+Mar 25 10:01:49 localhost kernel: Lem: 0000000100280000 0000000000000000 0000000100000000
+Mar 25 10:01:49 localhost kernel: PhbErr: 0000088000000000 0000008000000000 2148000098000240 a008400000000000
+Mar 25 10:01:49 localhost kernel: RxeArbErr: 4000200000000000 0000200000000000 02409fde30000000 0000000000000000
+Mar 25 10:01:49 localhost kernel: PblErr: 0000000001000000 0000000001000000 0000000000000000 0000000000000000
+Mar 25 10:01:49 localhost kernel: PcieDlp: 0000000000000000 0000000000000000 ffff000000000000
+Mar 25 10:01:49 localhost kernel: RegbErr: 0000004a10000800 0000000810000000 8800003c00000000 0000000007011000
+Mar 25 10:01:49 localhost kernel: PE[1fd] A/B: a440002a05000000 8000000000000000
+'''
+
+def parse_kernel_log(log_text):
+ reg8 = "([0-9a-fA-F]{8})"
+ reg16 = "([0-9a-fA-F]{16})"
+
+ # TODO: pick up the AER stuff the kernel logs too?
+ # NB: The register names used for set_reg are the skiboot register names, not the kernel.
+ # TODO: check these for completeness / accuracy. I might have missed something
+ register_patterns = [
+ (re.compile("brdgCtl: {}" .format(reg8)), "brdgCtl"),
+ (re.compile("RootSts: {} {} {} {} {}".format(reg8, reg8, reg8, reg8, reg8)),
+ 'deviceStatus', 'slotStatus', 'linkStatus', 'devCmdStatus', 'devSecStatus'),
+ (re.compile("RootErrSts: {} {} {}" .format(reg8, reg8, reg8)),
+ 'rootErrorStatus', 'uncorrErrorStatus', 'corrErrorStatus'),
+ (re.compile("PhbSts: {} {}" .format(reg16, reg16)), "phbPlssr", "phbCsr"),
+ (re.compile("nFir: {} {} {}" .format(reg16, reg16, reg16)), "nFir", "nFirMask", "nFirWOF"),
+ (re.compile("Lem: {} {} {}" .format(reg16, reg16, reg16)), "lemFir", "lemErrorMask", "lemWOF"),
+ (re.compile("PhbErr: {} {} {} {}" .format(reg16, reg16, reg16, reg16)),
+ "phbErrorStatus", "phbFirstErrorStatus", "phbErrorLog0", "phbErrorLog1"),
+ (re.compile("PhbTxeErr: {} {} {} {}" .format(reg16, reg16, reg16, reg16)),
+ "phbPhbTxeErrorStatus", "phbPhbTxeFirstErrorStatus", "phbPhbTxeErrorLog0", "phbTxeErrorLog1"),
+ (re.compile("RxeArbErr: {} {} {} {}" .format(reg16, reg16, reg16, reg16)),
+ "phbRxeArbErrorStatus", "phbRxeArbFirstErrorStatus", "phbRxeArbErrorLog0", "phbRxeArbErrorLog1"),
+ (re.compile("RxeMrgErr: {} {} {} {}" .format(reg16, reg16, reg16, reg16)),
+ "phbRxeMrgErrorStatus", "phbRxeMrgFirstErrorStatus", "phbRxeMrgErrorLog0", "phbRxeMrgErrorLog1"),
+ (re.compile("RxeTceErr: {} {} {} {}" .format(reg16, reg16, reg16, reg16)),
+ "phbRxeTceErrorStatus", "phbRxeTceFirstErrorStatus", "phbRxeTceErrorLog0", "phbRxeTceErrorLog1"),
+ (re.compile("PblErr: {} {} {} {}" .format(reg16, reg16, reg16, reg16)),
+ "phbPblErrorStatus", "phbPblFirstErrorStatus", "phbPblErrorLog0", "phbPblErrorLog1"),
+ (re.compile("PcieDlp: {} {} {}" .format(reg16, reg16, reg16)),
+ "phbPcieDlpErrorLog1", "phbPcieDlpErrorLog2", "phbPcieDlpErrorStatus"),
+ (re.compile("RegbErr: {} {} {} {}" .format(reg16, reg16, reg16, reg16)),
+ "phbRegbErrorStatus", "phbRegbFirstErrorStatus", "phbRegbErrorLog0", "phbRegbErrorLog1"),
+ ]
+
+ header_pattern = re.compile("PHB4 PHB#[0-9]+ Diag-data") # match header
+ pe_pattern = re.compile("PE\[{}\] A/B: {} {}".format("([ 0-9a-fA-F]{3})", reg16, reg16)) # the PE number is three hex digits
+
+ logs = []
+ log = PHBError("");
+
+ # pretty nasty but since interpreting the kernel logs requires context I
+ # don't have any better ideas
+ for l in log_text.split("\n"):
+ m = header_pattern.search(l)
+ if m: # start a new log
+ logs.append(log)
+ log = PHBError(l)
+ continue
+
+ for p,*names in register_patterns:
+ m = p.search(l)
+ if not m:
+ continue
+ for name, val in zip(names, m.groups()):
+ log.set_reg(name, int(val, 16))
+ break
+
+ m = pe_pattern.search(l)
+ if m:
+ pe = int(m.groups()[0], 16)
+ pesta = int(m.groups()[1], 16)
+ pestb = int(m.groups()[2], 16)
+ log.set_pest(pe, pesta, pestb)
+
+ logs.append(log)
+
+ return logs
+
+def main(argv):
+ if len(argv) < 2:
+ print("Usage: {} <log file>".format(argv[0]));
+ return
+
+ try:
+ log_text = open(argv[1]).read();
+ except Exception as err:
+ print(err)
+ sys.exit(1)
+
+ logs = parse_opal_log(log_text);
+ logs.extend(parse_kernel_log(log_text))
+
+ for err in logs:
+ print("==== PHB Register dump found ====")
+ print("")
+ print(err.header())
+ print("")
+ print(err.show_errs())
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/roms/skiboot/external/pci-scripts/ppc.py b/roms/skiboot/external/pci-scripts/ppc.py
new file mode 100644
index 000000000..ae04e4b1b
--- /dev/null
+++ b/roms/skiboot/external/pci-scripts/ppc.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python -i
+
+# Just some helper functions to convert PPC bits (in the docs) to integer
+# values we can actually use in code.
+
+def ppcbit(i):
+ return 1 << (63 - i)
+
+def ppcmask(a,b):
+ mask = 0
+ for i in range(a, b + 1):
+ mask += ppcbit(i)
+ return mask
+
+def ppcfield(a, b, v):
+ return (v & ppcmask(a,b)) >> (63 - b)
+
+def ppcbit32(i):
+ return 1 << (31 - i)
+
+def ppcmask32(a,b):
+ mask = 0
+ for i in range(a, b + 1):
+ mask += ppcbit32(i)
+ return mask
+
+def ppcfield32(a, b, v):
+ return (v & ppcmask32(a,b)) >> (31 - b)
diff --git a/roms/skiboot/external/pflash/.gitignore b/roms/skiboot/external/pflash/.gitignore
new file mode 100644
index 000000000..223b639e8
--- /dev/null
+++ b/roms/skiboot/external/pflash/.gitignore
@@ -0,0 +1,6 @@
+ccan
+common
+libflash
+make_version.sh
+pflash
+test/test.sh
diff --git a/roms/skiboot/external/pflash/Makefile b/roms/skiboot/external/pflash/Makefile
new file mode 100644
index 000000000..2918b7c3d
--- /dev/null
+++ b/roms/skiboot/external/pflash/Makefile
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: Apache-2.0
+include rules.mk
+GET_ARCH := ../../external/common/get_arch.sh
+include ../../external/common/rules.mk
+
+all: links arch_links $(EXE)
+
+.PHONY: coverage
+coverage: CFLAGS += -fprofile-arcs -ftest-coverage
+coverage: check
+
+#Rebuild version.o so that the the version always matches
+#what the test suite will get from ./make_version.sh
+check: version.o all
+ $(MAKE) -C ../ffspart
+ @ln -sf ../../make_version.sh make_version.sh
+ @ln -sf ../../test/test.sh test/test.sh
+ @test/test-pflash
+
+.PHONY: VERSION-always
+.version: VERSION-always
+ @echo $(PFLASH_VERSION) > $@.tmp
+ @cmp -s $@ $@.tmp || cp $@.tmp $@
+ @rm -f $@.tmp
+
+install: all $(INSTALLDEPS)
+ install -D pflash $(DESTDIR)$(sbindir)/pflash
+ install -D -m 0644 pflash.1 $(DESTDIR)$(mandir)/man1/pflash.1
+
+.PHONY: dist
+#File is named $(PFLASH_VERSION).tar because the expectation is that pflash-
+#is always at the start of the verion. This remains consistent with skiboot
+#version strings
+dist: links .version
+ find -L ../pflash/ -iname '*.[ch]' -print0 | xargs -0 tar -rhf $(PFLASH_VERSION).tar
+ tar --transform 's/Makefile.dist/Makefile/' -rhf $(PFLASH_VERSION).tar \
+ ../pflash/Makefile.dist ../pflash/rules.mk \
+ ../pflash/.version ../pflash/make_version.sh \
+ ../pflash/common/*
+
+.PHONY: clean
+clean: arch_clean
+ rm -f $(OBJS) $(EXE) *.o *.d
+.PHONY: distclean
+distclean: clean
+ rm -f *.c~ *.h~ *.sh~ Makefile~ config.mk~ libflash/*.c~ libflash/*.h~
+ rm -f libflash ccan version.c .version .version.tmp
+ rm -f common io.h
diff --git a/roms/skiboot/external/pflash/Makefile.dist b/roms/skiboot/external/pflash/Makefile.dist
new file mode 100644
index 000000000..85e41fa64
--- /dev/null
+++ b/roms/skiboot/external/pflash/Makefile.dist
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+include rules.mk
+GET_ARCH = common/get_arch.sh
+include common/rules.mk
+
+all: $(EXE)
+
+clean:
+ rm -f $(OBJS) *.o
+distclean: clean
+ rm -f $(EXE)
diff --git a/roms/skiboot/external/pflash/TODO b/roms/skiboot/external/pflash/TODO
new file mode 100644
index 000000000..a632c3891
--- /dev/null
+++ b/roms/skiboot/external/pflash/TODO
@@ -0,0 +1,5 @@
+- PCI backend for host
+- Use proper GPIO APIs on ARM
+- Use IPMI for lock/unlock on host
+- Timeouts and flashing errors handling
+- Lock handling
diff --git a/roms/skiboot/external/pflash/build-all-arch.sh b/roms/skiboot/external/pflash/build-all-arch.sh
new file mode 100755
index 000000000..14364334a
--- /dev/null
+++ b/roms/skiboot/external/pflash/build-all-arch.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Script to build all pflash backends
+#
+# Copyright 2015 IBM Corp.
+#
+# pflash has three different backends that are used on powerpc, arm (BMC) and
+# x86 (file-backed). In order to test for regressions when touching shared code
+# such as libflash.
+#
+# Defaults to the cross compilers available under Ubuntu. You can set the
+# environment variables arm_cc, amd64_cc, ppc64le_cc for other distributions.
+#
+# installing on x86:
+# apt-get install gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc
+#
+
+arm_cc=${arm_cc:-arm-linux-gnueabi-}
+amd64_cc=${amd64_cc:-x86_64-linux-gnu-}
+ppc64le_cc=${ppc64le_cc:-powerpc64le-linux-gnu-}
+
+echo "Building for ARM..."
+make clean && make distclean
+CROSS_COMPILE=${arm_cc} make || { echo "ARM build failed"; exit 1; }
+
+echo "Building for x86..."
+make clean && make distclean
+CROSS_COMPILE=${amd64_cc} make || { echo "x86 build failed"; exit 1; }
+
+echo "Building for ppc64le..."
+make clean && make distclean
+CROSS_COMPILE=${ppc64le_cc} make || { echo "ppc64le build failed"; exit 1; }
+
+make clean && make distclean
diff --git a/roms/skiboot/external/pflash/config.h b/roms/skiboot/external/pflash/config.h
new file mode 100644
index 000000000..34da2eb7c
--- /dev/null
+++ b/roms/skiboot/external/pflash/config.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * For CCAN
+ *
+ * Copyright 2014 IBM Corp.
+ */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF 1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN 0
+#define HAVE_LITTLE_ENDIAN 1
+#else
+#define HAVE_BIG_ENDIAN 1
+#define HAVE_LITTLE_ENDIAN 0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
diff --git a/roms/skiboot/external/pflash/pflash.1 b/roms/skiboot/external/pflash/pflash.1
new file mode 100644
index 000000000..5db8d4609
--- /dev/null
+++ b/roms/skiboot/external/pflash/pflash.1
@@ -0,0 +1,96 @@
+.TH pflash "1" "September 2016"
+.SH NAME
+pflash \- manual page for Open-Power Flash tool
+.SH SYNOPSIS
+\fBpflash\fP [\fI\,options\/\fP] \fI\,commands\/\fP...
+.SH DESCRIPTION
+pflash is a tool to access the flash modules
+on such systems and update the OpenPower firmware.
+.SS Options
+.TP
+\fB\-a\fP \fI\,address\/\fP, \fB\-\-address\fP=\fI\,address\/\fP
+Specify the start address for erasing, reading or flashing
+.TP
+\fB\-s\fP \fI\,size\/\fP, \fB\-\-size\fP=\fI\,size\/\fP
+Specify the size in bytes for erasing, reading or flashing
+.TP
+\fB\-P\fP \fI\,part_name\/\fP, \fB\-\-partition\fP=\fI\,part_name\/\fP
+Specify the partition whose content is to be erased
+programmed or read. This is an alternative to \fB\-a\fP and \fB\-s\fP
+if both \fB\-P\fP and \fB\-s\fP are specified, the smallest of the
+two will be used
+.TP
+\fB\-f\fP, \fB\-\-force\fP
+Don't ask for confirmation before erasing or flashing
+.TP
+\fB\-d\fP, \fB\-\-dummy\fP
+Don't write to flash
+.TP
+\fB\-m\fP, \fB\-\-mtd\fP
+Avoid accessing the flash directly if the BMC supports it.
+This will access the flash through the kernel MTD layer and
+not the flash directly
+.TP
+\fB\-b\fP, \fB\-\-bmc\fP
+Target BMC flash instead of host flash.
+Note: This carries a high chance of bricking your BMC if you
+don't know what you're doing. Consider \fB\-\-mtd\fP to be safe(r)
+.TP
+\fB\-F\fP \fI\,filename\/\fP, \fB\-\-flash\-file\fP \fI\,filename
+Target filename instead of actual flash.
+.TP
+\fB\-S\fP, \fB\-\-side\fP
+Side of the flash on which to operate, 0 (default) or 1
+.TP
+\fB\-T\fP, \fB\-\-toc\fP
+libffs TOC on which to operate, defaults to 0.
+leading 0x is required for interpretation of a hex value
+.SS
+Commands:
+.TP
+\fB\-4\fP, \fB\-\-enable\-4B\fP
+Switch the flash and controller to 4\-bytes address
+mode (no confirmation needed).
+.TP
+\fB\-3\fP, \fB\-\-disable\-4B\fP
+Switch the flash and controller to 3\-bytes address
+mode (no confirmation needed).
+.TP
+\fB\-r\fP \fI\,file\/\fP, \fB\-\-read\fP=\fI\,file\/\fP
+Read flash content from address into file, use \fB\-s\fP
+to specify the size to read (or it will use the source
+file size if used in conjunction with \fB\-p\fP and \fB\-s\fP is not
+specified). When using \fB\-r\fP together with \fB\-e\fP or \fB\-p\fP, the
+read will be performed first
+.TP
+\fB\-E\fP, \fB\-\-erase\-all\fP
+Erase entire flash chip (Not supported on all chips/controllers)
+.TP
+\fB\-e\fP, \fB\-\-erase\fP
+Erase the specified region. If size or address are not
+specified, but '\-\-program' is used, then the file
+size will be used (rounded to an erase block) and the
+address defaults to 0.
+.TP
+\fB\-p\fP \fI\,file\/\fP, \fB\-\-program\fP=\fI\,file\/\fP
+Will program the file to flash. If the address is not
+specified, it will use 0. If the size is not specified
+it will use the file size. Otherwise it will limit to
+the specified size (whatever is smaller). If used in
+conjunction with any erase command, the erase will
+take place first.
+.TP
+\fB\-t\fP, \fB\-\-tune\fP
+Just tune the flash controller & access size
+(Implicit for all other operations)
+.TP
+\fB\-c\fP \fB\-\-clear\fP
+Used to ECC clear a partition of the flash
+Must be used in conjunction with \fB\-P\fP. Will erase the
+partition and then set all the ECC bits as they should be
+.TP
+\fB\-i\fP, \fB\-\-info\fP
+Display some information about the flash.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+This message.
diff --git a/roms/skiboot/external/pflash/pflash.c b/roms/skiboot/external/pflash/pflash.c
new file mode 100644
index 000000000..8aa2acb1d
--- /dev/null
+++ b/roms/skiboot/external/pflash/pflash.c
@@ -0,0 +1,1230 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Display progress bars, while also writing whole or part
+ * of flash.
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <inttypes.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/blocklevel.h>
+#include <libflash/ecc.h>
+#include <common/arch_flash.h>
+#include "progress.h"
+
+#define __aligned(x) __attribute__((aligned(x)))
+
+struct flash_details {
+ struct blocklevel_device *bl;
+ int need_relock;
+ const char *name;
+ uint64_t toc;
+ uint64_t total_size;
+ uint32_t erase_granule;
+ bool mark_ecc;
+};
+
+/* Full pflash version number (possibly includes gitid). */
+extern const char version[];
+
+const char *flashfilename = NULL;
+static bool must_confirm = true;
+static bool dummy_run;
+static bool bmc_flash;
+
+#define FILE_BUF_SIZE 0x10000
+static uint8_t file_buf[FILE_BUF_SIZE] __aligned(0x1000);
+
+static bool check_confirm(void)
+{
+ char yes[8], *p;
+
+ if (!must_confirm)
+ return true;
+
+ printf("WARNING ! This will modify your %s flash chip content !\n",
+ bmc_flash ? "BMC" : "HOST");
+ printf("Enter \"yes\" to confirm:");
+ memset(yes, 0, sizeof(yes));
+ if (!fgets(yes, 7, stdin))
+ return false;
+ p = strchr(yes, 10);
+ if (p)
+ *p = 0;
+ p = strchr(yes, 13);
+ if (p)
+ *p = 0;
+ if (strcmp(yes, "yes")) {
+ printf("Operation cancelled !\n");
+ return false;
+ }
+ must_confirm = false;
+ return true;
+}
+
+static uint32_t print_ffs_info(struct ffs_handle *ffsh, uint32_t toc)
+{
+ struct ffs_entry *ent;
+ uint32_t next_toc = toc;
+ int rc;
+ int i;
+
+ printf("\n");
+ printf("TOC@0x%08x Partitions:\n", toc);
+ printf("-----------\n");
+
+ for (i = 0;; i++) {
+ uint32_t start, size, act, end;
+ struct ffs_entry_user user;
+ char *name = NULL, *flags;
+
+ rc = ffs_part_info(ffsh, i, &name, &start, &size, &act, NULL);
+ if (rc == FFS_ERR_PART_NOT_FOUND)
+ break;
+
+ ent = ffs_entry_get(ffsh, i);
+ if (rc || !ent) {
+ fprintf(stderr, "Error %d scanning partitions\n",
+ !ent ? FFS_ERR_PART_NOT_FOUND : rc);
+ goto out;
+ }
+
+ user = ffs_entry_user_get(ent);
+ ffs_entry_put(ent);
+ flags = ffs_entry_user_to_string(&user);
+ if (!flags)
+ goto out;
+
+ end = start + size;
+ printf("ID=%02d %15s 0x%08x..0x%08x (actual=0x%08x) [%s]\n",
+ i, name, start, end, act, flags);
+
+ if (strcmp(name, "OTHER_SIDE") == 0)
+ next_toc = start;
+
+ free(flags);
+out:
+ free(name);
+ }
+
+ return next_toc;
+}
+
+static struct ffs_handle *open_ffs(struct flash_details *flash)
+{
+ struct ffs_handle *ffsh;
+ int rc;
+
+ rc = ffs_init(flash->toc, flash->total_size,
+ flash->bl, &ffsh, flash->mark_ecc);
+ if (rc) {
+ fprintf(stderr, "Error %d opening ffs !\n", rc);
+ if (flash->toc) {
+ fprintf(stderr, "You specified 0x%" PRIx64 " as the libffs TOC\n"
+ "Looks like it doesn't exist\n", flash->toc);
+ return NULL;
+ }
+ }
+
+ return ffsh;
+}
+
+static void print_flash_info(struct flash_details *flash)
+{
+ struct ffs_handle *ffsh;
+ uint32_t next_toc;
+ uint32_t toc;
+
+ printf("Flash info:\n");
+ printf("-----------\n");
+ printf("Name = %s\n", flash->name);
+ printf("Total size = %" PRIu64 "MB\t Flags E:ECC, P:PRESERVED, R:READONLY, "
+ "B:BACKUP\n", flash->total_size >> 20);
+ printf("Erase granule = %2d%-13sF:REPROVISION, V:VOLATILE, C:CLEARECC\n",
+ flash->erase_granule >> 10, "KB");
+
+ if (bmc_flash)
+ return;
+
+ toc = flash->toc;
+
+ ffsh = open_ffs(flash);
+ if (!ffsh)
+ return;
+
+ next_toc = print_ffs_info(ffsh, toc);
+ ffs_close(ffsh);
+ while(next_toc != toc) {
+ struct ffs_handle *next_ffsh;
+
+ flash->toc = next_toc;
+ next_ffsh = open_ffs(flash);
+ if (!next_ffsh)
+ break;
+ next_toc = print_ffs_info(next_ffsh, next_toc);
+ ffs_close(next_ffsh);
+ }
+ flash->toc = toc;
+}
+
+static struct ffs_handle *open_partition(struct flash_details *flash,
+ const char *name, uint32_t *index)
+{
+ struct ffs_handle *ffsh;
+ int rc;
+
+ ffsh = open_ffs(flash);
+ if (!ffsh)
+ return NULL;
+
+ if (!name)
+ /* Just open the FFS */
+ return ffsh;
+
+ /* Find partition */
+ rc = ffs_lookup_part(ffsh, name, index);
+ if (rc == FFS_ERR_PART_NOT_FOUND) {
+ fprintf(stderr, "Partition '%s' not found !\n", name);
+ goto out;
+ }
+ if (rc) {
+ fprintf(stderr, "Error %d looking for partition '%s' !\n",
+ rc, name);
+ goto out;
+ }
+ return ffsh;
+out:
+ ffs_close(ffsh);
+ return NULL;
+}
+
+static struct ffs_handle *lookup_partition_at_toc(struct flash_details *flash,
+ const char *name, uint32_t *index)
+{
+ return open_partition(flash, name, index);
+}
+
+static struct ffs_handle *lookup_partition_at_side(struct flash_details *flash,
+ int side, const char *name, uint32_t *index)
+{
+ uint32_t toc = 0;
+ int rc;
+
+ if (side == 1) {
+ struct ffs_handle *ffsh;
+ uint32_t side_index;
+
+ ffsh = open_partition(flash, "OTHER_SIDE", &side_index);
+ if (!ffsh)
+ return NULL;
+
+ /* Just need to know where it starts */
+ rc = ffs_part_info(ffsh, side_index, NULL, &toc, NULL, NULL, NULL);
+ ffs_close(ffsh);
+ if (rc)
+ return NULL;
+ }
+
+ flash->toc = toc;
+ return lookup_partition_at_toc(flash, name, index);
+}
+
+static int erase_chip(struct flash_details *flash)
+{
+ bool confirm;
+ int rc;
+ uint64_t pos;
+
+ printf("About to erase chip !\n");
+ confirm = check_confirm();
+ if (!confirm)
+ return 1;
+
+ printf("Erasing... (may take a while)\n");
+ fflush(stdout);
+
+ if (dummy_run) {
+ printf("skipped (dummy)\n");
+ return 1;
+ }
+
+ /*
+ * We could use arch_flash_erase_chip() here BUT everyone really
+ * likes the progress bars.
+ * Lets do an erase block at a time erase then...
+ */
+ progress_init(flash->total_size);
+ for (pos = 0; pos < flash->total_size; pos += flash->erase_granule) {
+ rc = blocklevel_erase(flash->bl, pos, flash->erase_granule);
+ if (rc)
+ break;
+ progress_tick(pos);
+ }
+ progress_end();
+ if (rc) {
+ fprintf(stderr, "Error %d erasing chip\n", rc);
+ return rc;
+ }
+
+ printf("done !\n");
+ return 0;
+}
+
+static int erase_range(struct flash_details *flash,
+ uint32_t start, uint32_t size, bool will_program,
+ struct ffs_handle *ffsh, int ffs_index)
+{
+ uint32_t done = 0, erase_mask = flash->erase_granule - 1;
+ struct ffs_entry *toc;
+ bool confirm;
+ int rc;
+
+ printf("About to erase 0x%08x..0x%08x !\n", start, start + size);
+ confirm = check_confirm();
+ if (!confirm)
+ return 1;
+
+ if (dummy_run) {
+ printf("skipped (dummy)\n");
+ return 1;
+ }
+
+ printf("Erasing...\n");
+ /*
+ * blocklevel_smart_erase() can do the entire thing in one call
+ * BUT everyone really likes progress bars so break stuff up
+ */
+ progress_init(size);
+ if (start & erase_mask) {
+ /*
+ * Align to next erase block, or just do the entire
+ * thing if we fit within one erase block
+ */
+ uint32_t first_size = MIN(size, (flash->erase_granule - (start & erase_mask)));
+
+ rc = blocklevel_smart_erase(flash->bl, start, first_size);
+ if (rc) {
+ fprintf(stderr, "Failed to blocklevel_smart_erase(): %d\n", rc);
+ return 1;
+ }
+ size -= first_size;
+ done = first_size;
+ start += first_size;
+ }
+ progress_tick(done);
+ while (size & ~(erase_mask)) {
+ rc = blocklevel_smart_erase(flash->bl, start, flash->erase_granule);
+ if (rc) {
+ fprintf(stderr, "Failed to blocklevel_smart_erase(): %d\n", rc);
+ return 1;
+ }
+ start += flash->erase_granule;
+ size -= flash->erase_granule;
+ done += flash->erase_granule;
+ progress_tick(done);
+ }
+ if (size) {
+ rc = blocklevel_smart_erase(flash->bl, start, size);
+ if (rc) {
+ fprintf(stderr, "Failed to blocklevel_smart_erase(): %d\n", rc);
+ return 1;
+ }
+ done += size;
+ progress_tick(done);
+ }
+ progress_end();
+
+ if (!ffsh)
+ return 0;
+
+ /* If this is a flash partition, mark it empty if we aren't
+ * going to program over it as well
+ */
+ toc = ffs_entry_get(ffsh, 0);
+ if (toc) {
+ struct ffs_entry_user user;
+ bool rw_toc;
+
+ user = ffs_entry_user_get(toc);
+ rw_toc = !(user.miscflags & FFS_MISCFLAGS_READONLY);
+ if (ffs_index >= 0 && !will_program && rw_toc) {
+ printf("Updating actual size in partition header...\n");
+ ffs_update_act_size(ffsh, ffs_index, 0);
+ }
+ }
+
+ return 0;
+}
+
+static int set_ecc(struct flash_details *flash, uint32_t start, uint32_t size)
+{
+ uint32_t i = start + 8;
+ uint8_t ecc = 0;
+ bool confirm;
+ int rc;
+
+ printf("About to erase and set ECC bits in region 0x%08x to 0x%08x\n", start, start + size);
+ confirm = check_confirm();
+ if (!confirm)
+ return 1;
+
+ rc = erase_range(flash, start, size, true, NULL, 0);
+ if (rc) {
+ fprintf(stderr, "Couldn't erase region to mark with ECC\n");
+ return rc;
+ }
+
+ printf("Programming ECC bits...\n");
+ progress_init(size);
+ while (i < start + size) {
+ rc = blocklevel_write(flash->bl, i, &ecc, sizeof(ecc));
+ if (rc) {
+ fprintf(stderr, "\nError setting ECC byte at 0x%08x\n", i);
+ return rc;
+ }
+ i += 9;
+ progress_tick(i - start);
+ }
+ progress_end();
+ return 0;
+}
+
+static int program_file(struct blocklevel_device *bl,
+ const char *file, uint32_t start, uint32_t size,
+ struct ffs_handle *ffsh, int ffs_index)
+{
+ uint32_t actual_size = 0;
+ struct ffs_entry *toc;
+ int fd, rc = 0;
+ bool confirm;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ perror("Failed to open file");
+ return 1;
+ }
+ printf("About to program \"%s\" at 0x%08x..0x%08x !\n",
+ file, start, start + size);
+ confirm = check_confirm();
+ if (!confirm) {
+ rc = 1;
+ goto out;
+ }
+
+ if (dummy_run) {
+ printf("skipped (dummy)\n");
+ rc = 1;
+ goto out;
+ }
+
+ printf("Programming & Verifying...\n");
+ progress_init(size);
+ while(size) {
+ ssize_t len;
+
+ len = read(fd, file_buf, FILE_BUF_SIZE);
+ if (len < 0) {
+ perror("Error reading file");
+ rc = 1;
+ goto out;
+ }
+ if (len == 0)
+ break;
+ if (len > size)
+ len = size;
+ size -= len;
+ actual_size += len;
+ rc = blocklevel_write(bl, start, file_buf, len);
+ if (rc) {
+ if (rc == FLASH_ERR_VERIFY_FAILURE)
+ fprintf(stderr, "Verification failed for"
+ " chunk at 0x%08x\n", start);
+ else
+ fprintf(stderr, "Flash write error %d for"
+ " chunk at 0x%08x\n", rc, start);
+ goto out;
+ }
+ start += len;
+ progress_tick(actual_size);
+ }
+ progress_end();
+
+ if (!ffsh)
+ goto out;
+
+ /* If this is a flash partition, adjust its size */
+ toc = ffs_entry_get(ffsh, 0);
+ if (toc) {
+ struct ffs_entry_user user;
+ bool rw_toc;
+
+ user = ffs_entry_user_get(toc);
+ rw_toc = !(user.miscflags & FFS_MISCFLAGS_READONLY);
+ if (ffs_index >= 0 && rw_toc) {
+ printf("Updating actual size in partition header...\n");
+ ffs_update_act_size(ffsh, ffs_index, actual_size);
+ }
+ }
+out:
+ close(fd);
+ return rc;
+}
+
+static int do_read_file(struct blocklevel_device *bl, const char *file,
+ uint32_t start, uint32_t size, uint32_t skip_size)
+{
+ int fd, rc = 0;
+ uint32_t done = 0;
+
+ fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 00666);
+ if (fd == -1) {
+ perror("Failed to open file");
+ return 1;
+ }
+ start += skip_size;
+ size -= skip_size;
+
+ printf("Reading to \"%s\" from 0x%08x..0x%08x !\n",
+ file, start, start + size);
+
+ progress_init(size);
+ while(size) {
+ ssize_t len;
+
+ len = size > FILE_BUF_SIZE ? FILE_BUF_SIZE : size;
+ rc = blocklevel_read(bl, start, file_buf, len);
+ if (rc) {
+ fprintf(stderr, "Flash read error %d for"
+ " chunk at 0x%08x\n", rc, start);
+ break;
+ }
+ rc = write(fd, file_buf, len);
+ /*
+ * zero isn't strictly an error.
+ * Treat it as such so we can be sure we'lre always
+ * making forward progress.
+ */
+ if (rc <= 0) {
+ perror("Error writing file");
+ break;
+ }
+ start += rc;
+ size -= rc;
+ done += rc;
+ progress_tick(done);
+ }
+ progress_end();
+ close(fd);
+ return size ? rc : 0;
+}
+
+static int enable_4B_addresses(struct blocklevel_device *bl)
+{
+ int rc;
+
+ printf("Switching to 4-bytes address mode\n");
+
+ rc = arch_flash_4b_mode(bl, true);
+ if (rc) {
+ if (rc == -1) {
+ fprintf(stderr, "Switching address mode not available on this architecture\n");
+ } else {
+ fprintf(stderr, "Error %d enabling 4b mode\n", rc);
+ }
+ }
+
+ return rc;
+}
+
+static int disable_4B_addresses(struct blocklevel_device *bl)
+{
+ int rc;
+
+ printf("Switching to 3-bytes address mode\n");
+
+ rc = arch_flash_4b_mode(bl, false);
+ if (rc) {
+ if (rc == -1) {
+ fprintf(stderr, "Switching address mode not available on this architecture\n");
+ } else {
+ fprintf(stderr, "Error %d enabling 4b mode\n", rc);
+ }
+ }
+
+ return rc;
+}
+
+static void print_partition_detail(struct ffs_handle *ffsh, uint32_t part_id)
+{
+ uint32_t start, size, act, end;
+ char *ent_name = NULL, *flags;
+ struct ffs_entry *ent;
+ int rc, l;
+
+ rc = ffs_part_info(ffsh, part_id, &ent_name, &start, &size,
+ &act, NULL);
+ if (rc) {
+ fprintf(stderr, "Partition with ID %d doesn't exist error: %d\n",
+ part_id, rc);
+ goto out;
+ }
+
+ ent = ffs_entry_get(ffsh, part_id);
+ if (!ent) {
+ rc = FFS_ERR_PART_NOT_FOUND;
+ fprintf(stderr, "Couldn't open partition entry\n");
+ goto out;
+ }
+
+ printf("Detailed partition information\n");
+ end = start + size;
+ printf("Name:\n");
+ printf("%s (ID=%02d)\n\n", ent_name, part_id);
+ printf("%-10s %-10s %-10s\n", "Start", "End", "Actual");
+ printf("0x%08x 0x%08x 0x%08x\n\n", start, end, act);
+
+ printf("Flags:\n");
+
+ l = asprintf(&flags, "%s%s%s%s%s%s%s", has_ecc(ent) ? "ECC [E]\n" : "",
+ has_flag(ent, FFS_MISCFLAGS_PRESERVED) ? "PRESERVED [P]\n" : "",
+ has_flag(ent, FFS_MISCFLAGS_READONLY) ? "READONLY [R]\n" : "",
+ has_flag(ent, FFS_MISCFLAGS_BACKUP) ? "BACKUP [B]\n" : "",
+ has_flag(ent, FFS_MISCFLAGS_REPROVISION) ?
+ "REPROVISION [F]\n" : "",
+ has_flag(ent, FFS_MISCFLAGS_VOLATILE) ? "VOLATILE [V]\n" : "",
+ has_flag(ent, FFS_MISCFLAGS_CLEARECC) ? "CLEARECC [C]\n" : "");
+ ffs_entry_put(ent);
+ if (l < 0) {
+ fprintf(stderr, "Memory allocation failure printing flags!\n");
+ goto out;
+ }
+
+ printf("%s", flags);
+ free(flags);
+
+out:
+ free(ent_name);
+}
+
+static void print_version(void)
+{
+ printf("Open-Power Flash tool %s\n", version);
+}
+
+static void print_help(const char *pname)
+{
+ printf("Usage: %s [options] commands...\n\n", pname);
+ printf(" Options:\n");
+ printf("\t-a address, --address=address\n");
+ printf("\t\tSpecify the start address for erasing, reading\n");
+ printf("\t\tor flashing\n\n");
+ printf("\t-s size, --size=size\n");
+ printf("\t\tSpecify the size in bytes for erasing, reading\n");
+ printf("\t\tor flashing\n\n");
+ printf("\t-P part_name, --partition=part_name\n");
+ printf("\t\tSpecify the partition whose content is to be erased\n");
+ printf("\t\tprogrammed or read. This is an alternative to -a and -s\n");
+ printf("\t\tif both -P and -s are specified, the smallest of the\n");
+ printf("\t\ttwo will be used\n\n");
+ printf("\t-f, --force\n");
+ printf("\t\tDon't ask for confirmation before erasing or flashing\n\n");
+ printf("\t-d, --dummy\n");
+ printf("\t\tDon't write to flash\n\n");
+ printf("\t--direct\n");
+ printf("\t\tBypass all safety provided to you by the kernel driver\n");
+ printf("\t\tand use the flash driver built into pflash.\n");
+ printf("\t\tIf you have mtd devices and you use this command, the\n");
+ printf("\t\tsystem may become unstable.\n");
+ printf("\t\tIf you are reading this sentence then this flag is not\n");
+ printf("\t\twhat you want! Using this feature without knowing\n");
+ printf("\t\twhat it does can and likely will result in a bricked\n");
+ printf("\t\tmachine\n\n");
+ printf("\t-b, --bmc\n");
+ printf("\t\tTarget BMC flash instead of host flash.\n");
+ printf("\t\tNote: This carries a high chance of bricking your BMC if you\n");
+ printf("\t\tdon't know what you're doing. Consider --mtd to be safe(r)\n\n");
+ printf("\t-F filename, --flash-file filename\n");
+ printf("\t\tTarget filename instead of actual flash.\n\n");
+ printf("\t-S, --side\n");
+ printf("\t\tSide of the flash on which to operate, 0 (default) or 1\n\n");
+ printf("\t--skip=N\n");
+ printf("\t\tSkip N number of bytes from the start when reading\n\n");
+ printf("\t-T, --toc\n");
+ printf("\t\tlibffs TOC on which to operate, defaults to 0.\n");
+ printf("\t\tleading 0x is required for interpretation of a hex value\n\n");
+ printf("\t-g\n");
+ printf("\t\tEnable verbose libflash debugging\n\n");
+ printf(" Commands:\n");
+ printf("\t-4, --enable-4B\n");
+ printf("\t\tSwitch the flash and controller to 4-bytes address\n");
+ printf("\t\tmode (no confirmation needed).\n\n");
+ printf("\t-3, --disable-4B\n");
+ printf("\t\tSwitch the flash and controller to 3-bytes address\n");
+ printf("\t\tmode (no confirmation needed).\n\n");
+ printf("\t-r file, --read=file\n");
+ printf("\t\tRead flash content from address into file, use -s\n");
+ printf("\t\tto specify the size to read (or it will use the source\n");
+ printf("\t\tfile size if used in conjunction with -p and -s is not\n");
+ printf("\t\tspecified). When using -r together with -e or -p, the\n");
+ printf("\t\tread will be performed first\n\n");
+ printf("\t-E, --erase-all\n");
+ printf("\t\tErase entire flash chip\n");
+ printf("\t\t(Not supported on all chips/controllers)\n\n");
+ printf("\t-e, --erase\n");
+ printf("\t\tErase the specified region. If size or address are not\n");
+ printf("\t\tspecified, but \'--program\' is used, then the file\n");
+ printf("\t\tsize will be used (rounded to an erase block) and the\n");
+ printf("\t\taddress defaults to 0.\n\n");
+ printf("\t-p file, --program=file\n");
+ printf("\t\tWill program the file to flash. If the address is not\n");
+ printf("\t\tspecified, it will use 0. If the size is not specified\n");
+ printf("\t\tit will use the file size. Otherwise it will limit to\n");
+ printf("\t\tthe specified size (whatever is smaller). If used in\n");
+ printf("\t\tconjunction with any erase command, the erase will\n");
+ printf("\t\ttake place first.\n\n");
+ printf("\t-t, --tune\n");
+ printf("\t\tJust tune the flash controller & access size\n");
+ printf("\t\tMust be used in conjuction with --direct\n");
+ printf("\t\t(Implicit for all other operations)\n\n");
+ printf("\t-c --clear\n");
+ printf("\t\tUsed to ECC clear a partition of the flash\n");
+ printf("\t\tMust be used in conjunction with -P. Will erase the\n");
+ printf("\t\tpartition and then set all the ECC bits as they should be\n\n");
+ printf("\t-9 --ecc\n");
+ printf("\t\tEncode/Decode ECC where specified in the FFS header.\n");
+ printf("\t\tThis 9 byte ECC method is used for some OpenPOWER\n");
+ printf("\t\tpartitions.\n");
+ printf("\t-i, --info\n");
+ printf("\t\tDisplay some information about the flash.\n\n");
+ printf("\t--detail\n");
+ printf("\t\tDisplays detailed info about a particular partition.\n");
+ printf("\t\tAccepts a numeric partition or can be used in conjuction\n");
+ printf("\t\twith the -P flag.\n\n");
+ printf("\t-h, --help\n");
+ printf("\t\tThis message.\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+ const char *pname = argv[0];
+ struct flash_details flash = { 0 };
+ static struct ffs_handle *ffsh = NULL;
+ uint32_t ffs_index;
+ uint32_t address = 0, read_size = 0, detail_id = UINT_MAX;
+ uint32_t write_size = 0, write_size_minus_ecc = 0;
+ bool erase = false, do_clear = false;
+ bool program = false, erase_all = false, info = false, do_read = false;
+ bool enable_4B = false, disable_4B = false;
+ bool show_help = false, show_version = false;
+ bool no_action = false, tune = false;
+ char *write_file = NULL, *read_file = NULL, *part_name = NULL;
+ bool ffs_toc_seen = false, direct = false, print_detail = false;
+ int flash_side = 0, skip_size = 0;
+ int rc = 0;
+
+ while(1) {
+ struct option long_opts[] = {
+ {"address", required_argument, NULL, 'a'},
+ {"size", required_argument, NULL, 's'},
+ {"partition", required_argument, NULL, 'P'},
+ {"bmc", no_argument, NULL, 'b'},
+ {"direct", no_argument, NULL, 'D'},
+ {"enable-4B", no_argument, NULL, '4'},
+ {"disable-4B", no_argument, NULL, '3'},
+ {"read", required_argument, NULL, 'r'},
+ {"erase-all", no_argument, NULL, 'E'},
+ {"erase", no_argument, NULL, 'e'},
+ {"program", required_argument, NULL, 'p'},
+ {"force", no_argument, NULL, 'f'},
+ {"flash-file", required_argument, NULL, 'F'},
+ {"info", no_argument, NULL, 'i'},
+ {"detail", optional_argument, NULL, 'm'},
+ {"tune", no_argument, NULL, 't'},
+ {"dummy", no_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {"debug", no_argument, NULL, 'g'},
+ {"side", required_argument, NULL, 'S'},
+ {"skip", required_argument, NULL, 'k'},
+ {"toc", required_argument, NULL, 'T'},
+ {"clear", no_argument, NULL, 'c'},
+ {"ecc", no_argument, NULL, '9'},
+ {NULL, 0, NULL, 0 }
+ };
+ int c, oidx = 0;
+
+ c = getopt_long(argc, argv, "+:a:s:P:r:43Eep:fdihvbtgS:T:c9F:",
+ long_opts, &oidx);
+ if (c == -1)
+ break;
+ switch(c) {
+ char *endptr;
+
+ case 'a':
+ address = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ rc = 1;
+ no_action = true;
+ }
+ break;
+ case 's':
+ read_size = write_size = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ rc = 1;
+ no_action = true;
+ }
+ break;
+ case 'P':
+ free(part_name);
+ part_name = strdup(optarg);
+ break;
+ case '4':
+ enable_4B = true;
+ break;
+ case '3':
+ disable_4B = true;
+ break;
+ case 'r':
+ if (!optarg)
+ break;
+ do_read = true;
+ free(read_file);
+ read_file = strdup(optarg);
+ break;
+ case 'E':
+ erase_all = erase = true;
+ break;
+ case 'e':
+ erase = true;
+ break;
+ case 'D':
+ direct = true;
+ break;
+ case 'p':
+ if (!optarg)
+ break;
+ program = true;
+ free(write_file);
+ write_file = strdup(optarg);
+ break;
+ case 'f':
+ must_confirm = false;
+ break;
+ case 'F':
+ flashfilename = optarg;
+ break;
+ case 'd':
+ must_confirm = false;
+ dummy_run = true;
+ break;
+ case 'i':
+ info = true;
+ break;
+ case 'b':
+ bmc_flash = true;
+ break;
+ case 't':
+ tune = true;
+ break;
+ case 'v':
+ show_version = true;
+ break;
+ case 'h':
+ show_help = show_version = true;
+ break;
+ case 'g':
+ libflash_debug = true;
+ break;
+ case 'S':
+ flash_side = atoi(optarg);
+ break;
+ case 'k':
+ skip_size = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ rc = 1;
+ no_action = true;
+ }
+ break;
+ case 'T':
+ if (!optarg)
+ break;
+ ffs_toc_seen = true;
+ flash.toc = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ rc = 1;
+ no_action = true;
+ }
+ break;
+ case 'c':
+ do_clear = true;
+ break;
+ case 'm':
+ print_detail = true;
+ if (optarg) {
+ detail_id = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ rc = 1;
+ no_action = true;
+ }
+ }
+ break;
+ case '9':
+ flash.mark_ecc = true;
+ break;
+ case ':':
+ fprintf(stderr, "Unrecognised option \"%s\" to '%c'\n", optarg, optopt);
+ no_action = true;
+ break;
+ case '?':
+ fprintf(stderr, "Unrecognised option '%c'\n", optopt);
+ no_action = true;
+ break;
+ default:
+ fprintf(stderr , "Encountered unknown error parsing options\n");
+ no_action = true;
+ }
+ }
+
+ if (optind < argc) {
+ /*
+ * It appears not everything passed to pflash was an option, best to
+ * not continue
+ */
+ while (optind < argc)
+ fprintf(stderr, "Unrecognised option or argument \"%s\"\n", argv[optind++]);
+
+ no_action = true;
+ }
+
+ /* Check if we need to access the flash at all (which will
+ * also tune them as a side effect
+ */
+ no_action = no_action || (!erase && !program && !info && !do_read &&
+ !enable_4B && !disable_4B && !tune && !do_clear && !print_detail);
+
+ /* Nothing to do, if we didn't already, print usage */
+ if (no_action && !show_version)
+ show_help = show_version = true;
+
+ if (show_version)
+ print_version();
+ if (show_help)
+ print_help(pname);
+
+ if (no_action)
+ goto out;
+
+ /* --enable-4B and --disable-4B are mutually exclusive */
+ if (enable_4B && disable_4B) {
+ fprintf(stderr, "--enable-4B and --disable-4B are mutually"
+ " exclusive !\n");
+ rc = 1;
+ goto out;
+ }
+
+ /* 4B not supported on BMC flash */
+ if (enable_4B && bmc_flash) {
+ fprintf(stderr, "--enable-4B not supported on BMC flash !\n");
+ rc = 1;
+ goto out;;
+ }
+
+ /* partitions not supported on BMC flash */
+ if (part_name && bmc_flash) {
+ fprintf(stderr, "--partition not supported on BMC flash !\n");
+ rc = 1;
+ goto out;
+ }
+
+ if (print_detail && ((detail_id == UINT_MAX && !part_name)
+ || (detail_id != UINT_MAX && part_name))) {
+ fprintf(stderr, "--detail requires either a partition id or\n");
+ fprintf(stderr, "a partition name with -P\n");
+ }
+
+ /* part-name and erase-all make no sense together */
+ if (part_name && erase_all) {
+ fprintf(stderr, "--partition and --erase-all are mutually"
+ " exclusive !\n");
+ rc = 1;
+ goto out;
+ }
+
+ /* Read command should always come with a file */
+ if (do_read && !read_file) {
+ fprintf(stderr, "Read with no file specified !\n");
+ rc = 1;
+ goto out;
+ }
+
+ /* Skip only supported on read */
+ if (skip_size && !do_read) {
+ fprintf(stderr, "--skip requires a --read command !\n");
+ rc = 1;
+ goto out;
+ }
+
+ /* Program command should always come with a file */
+ if (program && !write_file) {
+ fprintf(stderr, "Program with no file specified !\n");
+ rc = 1;
+ goto out;
+ }
+
+ /* If both partition and address specified, error out */
+ if (address && part_name) {
+ fprintf(stderr, "Specify partition or address, not both !\n");
+ rc = 1;
+ goto out;
+ }
+
+ if (do_clear && !part_name) {
+ fprintf(stderr, "--clear only supported on a partition name\n");
+ rc = 1;
+ goto out;
+ }
+
+ /* Explicitly only support two sides */
+ if (flash_side != 0 && flash_side != 1) {
+ fprintf(stderr, "Unexpected value for --side '%d'\n", flash_side);
+ rc = 1;
+ goto out;
+ }
+
+ if (ffs_toc_seen && flash_side) {
+ fprintf(stderr, "--toc and --side are exclusive");
+ rc = 1;
+ goto out;
+ }
+
+ if (flashfilename && bmc_flash) {
+ fprintf(stderr, "Filename or bmc flash but not both\n");
+ rc = 1;
+ goto out;
+ }
+
+ if (flashfilename && direct) {
+ fprintf(stderr, "Filename or direct access but not both\n");
+ rc = 1;
+ goto out;
+ }
+
+ if (tune && !direct) {
+ fprintf(stderr, "It doesn't make sense to --tune without --direct\n");
+ rc = 1;
+ goto out;
+ }
+
+ if (direct) {
+ /* If -t is passed, then print a nice message */
+ if (tune)
+ printf("Flash and controller tuned\n");
+
+ if (arch_flash_access(NULL, bmc_flash ? BMC_DIRECT : PNOR_DIRECT) == ACCESS_INVAL) {
+ fprintf(stderr, "Can't access %s flash directly on this architecture\n",
+ bmc_flash ? "BMC" : "PNOR");
+ rc = 1;
+ goto out;
+ }
+ } else if (!flashfilename) {
+ if (arch_flash_access(NULL, bmc_flash ? BMC_MTD : PNOR_MTD) == ACCESS_INVAL) {
+ fprintf(stderr, "Can't access %s flash through MTD on this system\n",
+ bmc_flash ? "BMC" : "PNOR");
+ rc = 1;
+ goto out;
+ }
+ }
+
+ if (arch_flash_init(&flash.bl, flashfilename, true)) {
+ fprintf(stderr, "Couldn't initialise architecture flash structures\n");
+ rc = 1;
+ goto out;
+ }
+
+ rc = blocklevel_get_info(flash.bl, &flash.name,
+ &flash.total_size, &flash.erase_granule);
+ if (rc) {
+ fprintf(stderr, "Error %d getting flash info\n", rc);
+ rc = 1;
+ goto close;
+ }
+
+ /* If file specified but not size, get size from file */
+ if (write_file && !write_size) {
+ struct stat stbuf;
+
+ if (stat(write_file, &stbuf)) {
+ perror("Failed to get file size");
+ rc = 1;
+ goto close;
+ }
+ write_size = stbuf.st_size;
+ }
+
+ /* Only take ECC into account under some conditions later */
+ write_size_minus_ecc = write_size;
+
+ /* If read specified and no read_size, use flash size */
+ if (do_read && !read_size && !part_name)
+ read_size = flash.total_size;
+
+ /* We have a partition, adjust read/write size if needed */
+ if (part_name || print_detail) {
+ uint32_t pstart, pmaxsz, pactsize;
+ bool ecc, confirm;
+
+ if (ffs_toc_seen)
+ ffsh = lookup_partition_at_toc(&flash,
+ part_name, &ffs_index);
+ else
+ ffsh = lookup_partition_at_side(&flash, flash_side,
+ part_name, &ffs_index);
+ if (!ffsh)
+ goto close;
+
+ if (!part_name)
+ ffs_index = detail_id;
+
+ rc = ffs_part_info(ffsh, ffs_index, NULL,
+ &pstart, &pmaxsz, &pactsize, &ecc);
+ if (rc) {
+ fprintf(stderr,"Failed to get partition info\n");
+ goto close;
+ }
+
+ if (!ecc && do_clear) {
+ fprintf(stderr, "The partition on which to do --clear "
+ "does not have ECC, are you sure?\n");
+ confirm = check_confirm();
+ if (!confirm) {
+ rc = 1;
+ goto close;
+ }
+ /* Still confirm later on */
+ must_confirm = true;
+ }
+
+ /* Read size is obtained from partition "actual" size */
+ if (!read_size)
+ read_size = pactsize;
+ /* If we're decoding ecc and partition is ECC'd, then adjust */
+ if (ecc && flash.mark_ecc)
+ read_size = ecc_buffer_size_minus_ecc(read_size);
+
+ /* Write size is max size of partition */
+ if (!write_size)
+ write_size = pmaxsz;
+
+ /* But write size can take into account ECC as well */
+ if (ecc && flash.mark_ecc)
+ write_size_minus_ecc = ecc_buffer_size_minus_ecc(write_size);
+ else
+ write_size_minus_ecc = write_size;
+
+ /* Crop write size to partition size if --force was passed */
+ if ((write_size_minus_ecc > pmaxsz) && !must_confirm) {
+ printf("WARNING: Size (%d bytes) larger than partition"
+ " (%d bytes), cropping to fit\n",
+ write_size, pmaxsz);
+ write_size = pmaxsz;
+ } else if (write_size_minus_ecc > pmaxsz) {
+ printf("ERROR: Size (%d bytes) larger than partition"
+ " (%d bytes). Use --force to force\n",
+ write_size, pmaxsz);
+ goto close;
+ }
+
+ /* Set address */
+ address = pstart;
+ } else if (erase) {
+ if ((address | write_size) & (flash.erase_granule - 1)) {
+ if (must_confirm) {
+ printf("ERROR: Erase at 0x%08x for 0x%08x isn't erase block aligned\n",
+ address, write_size);
+ printf("Use --force to force\n");
+ goto close;
+ } else {
+ printf("WARNING: Erase at 0x%08x for 0x%08x isn't erase block aligned\n",
+ address, write_size);
+ }
+ }
+ }
+
+ /* Process commands */
+
+ /* Both enable and disable can't be set (we've checked) */
+ if (enable_4B)
+ rc = enable_4B_addresses(flash.bl);
+ if (disable_4B)
+ rc = disable_4B_addresses(flash.bl);
+ if (rc)
+ goto close;
+
+ if (info) {
+ /*
+ * Don't pass through modfied TOC value if the modification was done
+ * because of --size, but still respect if it came from --toc (we
+ * assume the user knows what they're doing in that case)
+ */
+ print_flash_info(&flash);
+ }
+
+ if (print_detail)
+ print_partition_detail(ffsh, ffs_index);
+
+ /* Unlock flash (PNOR only) */
+ if ((erase || program || do_clear) && !bmc_flash && !flashfilename) {
+ flash.need_relock = arch_flash_set_wrprotect(flash.bl, false);
+ if (flash.need_relock == -1) {
+ fprintf(stderr, "Architecture doesn't support write protection on flash\n");
+ flash.need_relock = 0;
+ goto close;
+ }
+ }
+ rc = 0;
+ if (do_read)
+ rc = do_read_file(flash.bl, read_file, address, read_size, skip_size);
+ if (!rc && erase_all)
+ rc = erase_chip(&flash);
+ else if (!rc && erase)
+ rc = erase_range(&flash, address, write_size,
+ program, ffsh, ffs_index);
+ if (!rc && program)
+ rc = program_file(flash.bl, write_file, address, write_size_minus_ecc,
+ ffsh, ffs_index);
+ if (!rc && do_clear)
+ rc = set_ecc(&flash, address, write_size);
+
+close:
+ if (flash.need_relock)
+ arch_flash_set_wrprotect(flash.bl, 1);
+ arch_flash_close(flash.bl, flashfilename);
+ if (ffsh)
+ ffs_close(ffsh);
+out:
+ free(part_name);
+ free(read_file);
+ free(write_file);
+
+ return rc;
+}
diff --git a/roms/skiboot/external/pflash/progress.c b/roms/skiboot/external/pflash/progress.c
new file mode 100644
index 000000000..b98838473
--- /dev/null
+++ b/roms/skiboot/external/pflash/progress.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * The most important part of pflash, the progress bars
+ *
+ * Copyright 2014-2017 IBM Corp.
+ */
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "progress.h"
+
+static uint64_t progress_max;
+static uint64_t progress_pcent;
+static uint64_t progress_n_upd;
+static time_t progress_prevsec;
+static struct timespec progress_start;
+
+#define PROGRESS_CHARS 50
+
+void progress_init(uint64_t count)
+{
+ unsigned int i;
+
+ progress_max = count;
+ progress_pcent = 0;
+ progress_n_upd = ULONG_MAX;
+ progress_prevsec = ULONG_MAX;
+
+ printf("\r[");
+ for (i = 0; i < PROGRESS_CHARS; i++)
+ printf(" ");
+ printf("] 0%%");
+ fflush(stdout);
+ clock_gettime(CLOCK_MONOTONIC, &progress_start);}
+
+void progress_tick(uint64_t cur)
+{
+ unsigned int i, pos;
+ struct timespec now;
+ uint64_t pcent;
+ double sec;
+
+ pcent = (cur * 100) / progress_max;
+ if (progress_pcent == pcent && cur < progress_n_upd &&
+ cur < progress_max)
+ return;
+ progress_pcent = pcent;
+ pos = (pcent * PROGRESS_CHARS) / 101;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ printf("\r[");
+ for (i = 0; i <= pos; i++)
+ printf("=");
+ for (; i < PROGRESS_CHARS; i++)
+ printf(" ");
+ printf("] %" PRIu64 "%%", pcent);
+
+ sec = difftime(now.tv_sec, progress_start.tv_sec);
+ if (sec >= 5 && pcent > 0) {
+ uint64_t persec = cur / sec;
+ uint64_t rem_sec;
+
+ if (!persec)
+ persec = 1;
+ progress_n_upd = cur + persec;
+ rem_sec = ((sec * 100) + (pcent / 2)) / pcent - sec;
+ if (rem_sec > progress_prevsec)
+ rem_sec = progress_prevsec;
+ progress_prevsec = rem_sec;
+ if (rem_sec < 60)
+ printf(" ETA:%" PRIu64 "s ", rem_sec);
+ else {
+ printf(" ETA:%" PRIu64 ":%02" PRIu64 ":%02" PRIu64 " ",
+ rem_sec / 3600,
+ (rem_sec / 60) % 60,
+ rem_sec % 60);
+ }
+ }
+
+ fflush(stdout);
+}
+
+void progress_end(void)
+{
+ printf("\n");
+}
diff --git a/roms/skiboot/external/pflash/progress.h b/roms/skiboot/external/pflash/progress.h
new file mode 100644
index 000000000..eb390d542
--- /dev/null
+++ b/roms/skiboot/external/pflash/progress.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright 2014-2017 IBM Corp.
+ */
+
+#ifndef __PROGRESS_H
+#define __PROGRESS_H
+
+#include <inttypes.h>
+
+void progress_init(uint64_t count);
+void progress_tick(uint64_t cur);
+void progress_end(void);
+
+#endif /* __PROGRESS_H */
diff --git a/roms/skiboot/external/pflash/rules.mk b/roms/skiboot/external/pflash/rules.mk
new file mode 100644
index 000000000..1d1b60486
--- /dev/null
+++ b/roms/skiboot/external/pflash/rules.mk
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+.DEFAULT_GOAL := all
+
+override CFLAGS += -O2 -Wall -I.
+LIBFLASH_FILES := libflash.c libffs.c ecc.c blocklevel.c file.c
+LIBFLASH_OBJS := $(addprefix libflash-, $(LIBFLASH_FILES:.c=.o))
+LIBFLASH_SRC := $(addprefix libflash/,$(LIBFLASH_FILES))
+CCAN_FILES := list.c
+CCAN_OBJS := $(addprefix ccan-list-, $(CCAN_FILES:.c=.o))
+CCAN_SRC := $(addprefix ccan/list/,$(CCAN_FILES))
+PFLASH_OBJS := pflash.o progress.o version.o common-arch_flash.o
+OBJS := $(PFLASH_OBJS) $(LIBFLASH_OBJS) $(CCAN_OBJS)
+EXE := pflash
+sbindir = $(prefix)/sbin
+datadir = $(prefix)/share
+mandir = $(datadir)/man
+
+PFLASH_VERSION ?= $(shell ../../make_version.sh $(EXE))
+LINKAGE ?= static
+
+ifeq ($(LINKAGE),dynamic)
+include ../shared/rules.mk
+SHARED := ../shared/$(SHARED_NAME)
+OBJS := $(PFLASH_OBJS) $(SHARED)
+INSTALLDEPS += install-shared
+
+install-shared:
+ $(MAKE) -C ../shared install prefix=$(prefix)
+
+$(SHARED):
+ $(MAKE) -C ../shared
+endif
+
+version.c: .version
+ @(if [ "a$(PFLASH_VERSION)" = "a" ]; then \
+ echo "#error You need to set PFLASH_VERSION environment variable" > $@ ;\
+ else \
+ echo "const char version[] = \"$(PFLASH_VERSION)\";" ;\
+ fi) > $@
+
+%.o : %.c | links
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(LIBFLASH_SRC): | links
+
+$(CCAN_SRC): | links
+
+$(LIBFLASH_OBJS): libflash-%.o : libflash/%.c | links
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(CCAN_OBJS): ccan-list-%.o: ccan/list/%.c | links
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(EXE): $(OBJS)
+ $(Q_CC)$(CC) $(LDFLAGS) $(CFLAGS) $^ -lrt -o $@
+
diff --git a/roms/skiboot/external/pflash/test/files/01-info.ffs b/roms/skiboot/external/pflash/test/files/01-info.ffs
new file mode 100644
index 000000000..41cbb68cb
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/files/01-info.ffs
@@ -0,0 +1,6 @@
+ONE,0x00003000,0x00001000,EV,,/dev/zero
+TWO,0x00004000,0x00001000,EF,,/dev/zero
+THREE,0x00005000,0x00001000,EF,,/dev/zero
+FOUR,0x00006000,0x00001000,EF,,/dev/zero
+FIVE,0x00007000,0x1000,L,,/dev/zero
+SIX,0x00008000,0x1000,C,,/dev/zero
diff --git a/roms/skiboot/external/pflash/test/files/02-erase.ffs b/roms/skiboot/external/pflash/test/files/02-erase.ffs
new file mode 100644
index 000000000..ff64d4e69
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/files/02-erase.ffs
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,/dev/urandom
+TWO,0x00000400,0x00000100,EF,,/dev/urandom
+THREE,0x00000500,0x00000100,EF,,/dev/urandom
+FOUR,0x00000600,0x00000100,EF,,/dev/urandom
diff --git a/roms/skiboot/external/pflash/test/files/03-erase-parts.ffs b/roms/skiboot/external/pflash/test/files/03-erase-parts.ffs
new file mode 100644
index 000000000..ff64d4e69
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/files/03-erase-parts.ffs
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,/dev/urandom
+TWO,0x00000400,0x00000100,EF,,/dev/urandom
+THREE,0x00000500,0x00000100,EF,,/dev/urandom
+FOUR,0x00000600,0x00000100,EF,,/dev/urandom
diff --git a/roms/skiboot/external/pflash/test/files/04-program-rand.ffs b/roms/skiboot/external/pflash/test/files/04-program-rand.ffs
new file mode 100644
index 000000000..ff64d4e69
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/files/04-program-rand.ffs
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,/dev/urandom
+TWO,0x00000400,0x00000100,EF,,/dev/urandom
+THREE,0x00000500,0x00000100,EF,,/dev/urandom
+FOUR,0x00000600,0x00000100,EF,,/dev/urandom
diff --git a/roms/skiboot/external/pflash/test/files/05-bad-numbers.ffs b/roms/skiboot/external/pflash/test/files/05-bad-numbers.ffs
new file mode 100644
index 000000000..ff64d4e69
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/files/05-bad-numbers.ffs
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,/dev/urandom
+TWO,0x00000400,0x00000100,EF,,/dev/urandom
+THREE,0x00000500,0x00000100,EF,,/dev/urandom
+FOUR,0x00000600,0x00000100,EF,,/dev/urandom
diff --git a/roms/skiboot/external/pflash/test/files/06-miscprint.ffs b/roms/skiboot/external/pflash/test/files/06-miscprint.ffs
new file mode 100644
index 000000000..d7e91b0e1
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/files/06-miscprint.ffs
@@ -0,0 +1,6 @@
+PRESERVED,0x003000,0x1000,P,,/dev/zero
+READONLY,0x0004000,0x1000,R,,/dev/zero
+REPROVISION,0x5000,0x1000,F,,/dev/zero
+BACKUP,0x000006000,0x1000,B,,/dev/zero
+VOLATILE,0x000007000,0x1000,V,,/dev/zero
+CLEARECC,0x000008000,0x1000,C,,/dev/zero
diff --git a/roms/skiboot/external/pflash/test/make-check-test b/roms/skiboot/external/pflash/test/make-check-test
new file mode 100755
index 000000000..7f0094860
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/make-check-test
@@ -0,0 +1 @@
+make -C external/pflash/ check
diff --git a/roms/skiboot/external/pflash/test/results/00-usage.err b/roms/skiboot/external/pflash/test/results/00-usage.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/00-usage.err
diff --git a/roms/skiboot/external/pflash/test/results/00-usage.out b/roms/skiboot/external/pflash/test/results/00-usage.out
new file mode 100644
index 000000000..a3721ca8e
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/00-usage.out
@@ -0,0 +1,114 @@
+Open-Power Flash tool VERSION
+Usage: ./pflash [options] commands...
+
+ Options:
+ -a address, --address=address
+ Specify the start address for erasing, reading
+ or flashing
+
+ -s size, --size=size
+ Specify the size in bytes for erasing, reading
+ or flashing
+
+ -P part_name, --partition=part_name
+ Specify the partition whose content is to be erased
+ programmed or read. This is an alternative to -a and -s
+ if both -P and -s are specified, the smallest of the
+ two will be used
+
+ -f, --force
+ Don't ask for confirmation before erasing or flashing
+
+ -d, --dummy
+ Don't write to flash
+
+ --direct
+ Bypass all safety provided to you by the kernel driver
+ and use the flash driver built into pflash.
+ If you have mtd devices and you use this command, the
+ system may become unstable.
+ If you are reading this sentence then this flag is not
+ what you want! Using this feature without knowing
+ what it does can and likely will result in a bricked
+ machine
+
+ -b, --bmc
+ Target BMC flash instead of host flash.
+ Note: This carries a high chance of bricking your BMC if you
+ don't know what you're doing. Consider --mtd to be safe(r)
+
+ -F filename, --flash-file filename
+ Target filename instead of actual flash.
+
+ -S, --side
+ Side of the flash on which to operate, 0 (default) or 1
+
+ --skip=N
+ Skip N number of bytes from the start when reading
+
+ -T, --toc
+ libffs TOC on which to operate, defaults to 0.
+ leading 0x is required for interpretation of a hex value
+
+ -g
+ Enable verbose libflash debugging
+
+ Commands:
+ -4, --enable-4B
+ Switch the flash and controller to 4-bytes address
+ mode (no confirmation needed).
+
+ -3, --disable-4B
+ Switch the flash and controller to 3-bytes address
+ mode (no confirmation needed).
+
+ -r file, --read=file
+ Read flash content from address into file, use -s
+ to specify the size to read (or it will use the source
+ file size if used in conjunction with -p and -s is not
+ specified). When using -r together with -e or -p, the
+ read will be performed first
+
+ -E, --erase-all
+ Erase entire flash chip
+ (Not supported on all chips/controllers)
+
+ -e, --erase
+ Erase the specified region. If size or address are not
+ specified, but '--program' is used, then the file
+ size will be used (rounded to an erase block) and the
+ address defaults to 0.
+
+ -p file, --program=file
+ Will program the file to flash. If the address is not
+ specified, it will use 0. If the size is not specified
+ it will use the file size. Otherwise it will limit to
+ the specified size (whatever is smaller). If used in
+ conjunction with any erase command, the erase will
+ take place first.
+
+ -t, --tune
+ Just tune the flash controller & access size
+ Must be used in conjuction with --direct
+ (Implicit for all other operations)
+
+ -c --clear
+ Used to ECC clear a partition of the flash
+ Must be used in conjunction with -P. Will erase the
+ partition and then set all the ECC bits as they should be
+
+ -9 --ecc
+ Encode/Decode ECC where specified in the FFS header.
+ This 9 byte ECC method is used for some OpenPOWER
+ partitions.
+ -i, --info
+ Display some information about the flash.
+
+ --detail
+ Displays detailed info about a particular partition.
+ Accepts a numeric partition or can be used in conjuction
+ with the -P flag.
+
+ -h, --help
+ This message.
+
diff --git a/roms/skiboot/external/pflash/test/results/01-info.err b/roms/skiboot/external/pflash/test/results/01-info.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/01-info.err
diff --git a/roms/skiboot/external/pflash/test/results/01-info.out b/roms/skiboot/external/pflash/test/results/01-info.out
new file mode 100644
index 000000000..6dfa9216f
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/01-info.out
@@ -0,0 +1,15 @@
+Flash info:
+-----------
+Name = FILE
+Total size = 0MB Flags E:ECC, P:PRESERVED, R:READONLY, B:BACKUP
+Erase granule = 0KB F:REPROVISION, V:VOLATILE, C:CLEARECC
+
+TOC@0x00000000 Partitions:
+-----------
+ID=00 part 0x00000000..0x00001000 (actual=0x00001000) [----------]
+ID=01 ONE 0x00003000..0x00004000 (actual=0x00001000) [E--------V]
+ID=02 TWO 0x00004000..0x00005000 (actual=0x00001000) [E-----F---]
+ID=03 THREE 0x00005000..0x00006000 (actual=0x00001000) [E-----F---]
+ID=04 FOUR 0x00006000..0x00007000 (actual=0x00001000) [E-----F---]
+ID=05 FIVE 0x00007000..0x00008000 (actual=0x00001000) [-L--------]
+ID=06 SIX 0x00008000..0x00009000 (actual=0x00001000) [--------C-]
diff --git a/roms/skiboot/external/pflash/test/results/02-erase.err b/roms/skiboot/external/pflash/test/results/02-erase.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/02-erase.err
diff --git a/roms/skiboot/external/pflash/test/results/02-erase.out b/roms/skiboot/external/pflash/test/results/02-erase.out
new file mode 100644
index 000000000..7d251a70c
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/02-erase.out
@@ -0,0 +1,105 @@
+About to erase chip !
+WARNING ! This will modify your HOST flash chip content !
+Enter "yes" to confirm:Erasing... (may take a while)
+
+[ ] 0%
+[= ] 1%
+[= ] 2%
+[== ] 3%
+[== ] 4%
+[=== ] 5%
+[=== ] 6%
+[==== ] 7%
+[==== ] 8%
+[===== ] 9%
+[===== ] 10%
+[====== ] 11%
+[====== ] 12%
+[======= ] 13%
+[======= ] 14%
+[======== ] 15%
+[======== ] 16%
+[========= ] 17%
+[========= ] 18%
+[========== ] 19%
+[========== ] 20%
+[=========== ] 21%
+[=========== ] 22%
+[============ ] 23%
+[============ ] 24%
+[============= ] 25%
+[============= ] 26%
+[============== ] 27%
+[============== ] 28%
+[=============== ] 29%
+[=============== ] 30%
+[================ ] 31%
+[================ ] 32%
+[================= ] 33%
+[================= ] 34%
+[================== ] 35%
+[================== ] 36%
+[=================== ] 37%
+[=================== ] 38%
+[==================== ] 39%
+[==================== ] 40%
+[===================== ] 41%
+[===================== ] 42%
+[====================== ] 43%
+[====================== ] 44%
+[======================= ] 45%
+[======================= ] 46%
+[======================== ] 47%
+[======================== ] 48%
+[========================= ] 49%
+[========================= ] 50%
+[========================== ] 51%
+[========================== ] 52%
+[=========================== ] 53%
+[=========================== ] 54%
+[============================ ] 55%
+[============================ ] 56%
+[============================= ] 57%
+[============================= ] 58%
+[============================== ] 59%
+[============================== ] 60%
+[=============================== ] 61%
+[=============================== ] 62%
+[================================ ] 63%
+[================================ ] 64%
+[================================= ] 65%
+[================================= ] 66%
+[================================== ] 67%
+[================================== ] 68%
+[=================================== ] 69%
+[=================================== ] 70%
+[==================================== ] 71%
+[==================================== ] 72%
+[===================================== ] 73%
+[===================================== ] 74%
+[====================================== ] 75%
+[====================================== ] 76%
+[======================================= ] 77%
+[======================================= ] 78%
+[======================================== ] 79%
+[======================================== ] 80%
+[========================================= ] 81%
+[========================================= ] 82%
+[========================================== ] 83%
+[========================================== ] 84%
+[=========================================== ] 85%
+[=========================================== ] 86%
+[============================================ ] 87%
+[============================================ ] 88%
+[============================================= ] 89%
+[============================================= ] 90%
+[============================================== ] 91%
+[============================================== ] 92%
+[=============================================== ] 93%
+[=============================================== ] 94%
+[================================================ ] 95%
+[================================================ ] 96%
+[================================================= ] 97%
+[================================================= ] 98%
+[==================================================] 99%
+done !
diff --git a/roms/skiboot/external/pflash/test/results/03-erase-parts.err b/roms/skiboot/external/pflash/test/results/03-erase-parts.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/03-erase-parts.err
diff --git a/roms/skiboot/external/pflash/test/results/03-erase-parts.out b/roms/skiboot/external/pflash/test/results/03-erase-parts.out
new file mode 100644
index 000000000..28f0b23d1
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/03-erase-parts.out
@@ -0,0 +1,106 @@
+About to erase 0x00000300..0x00000400 !
+WARNING ! This will modify your HOST flash chip content !
+Enter "yes" to confirm:Erasing...
+
+[ ] 0%
+[= ] 1%
+[= ] 2%
+[== ] 3%
+[== ] 4%
+[=== ] 5%
+[=== ] 6%
+[==== ] 7%
+[==== ] 8%
+[===== ] 9%
+[===== ] 10%
+[====== ] 11%
+[====== ] 12%
+[======= ] 13%
+[======= ] 14%
+[======== ] 15%
+[======== ] 16%
+[========= ] 17%
+[========= ] 18%
+[========== ] 19%
+[========== ] 20%
+[=========== ] 21%
+[=========== ] 22%
+[============ ] 23%
+[============ ] 24%
+[============= ] 25%
+[============= ] 26%
+[============== ] 27%
+[============== ] 28%
+[=============== ] 29%
+[=============== ] 30%
+[================ ] 31%
+[================ ] 32%
+[================= ] 33%
+[================= ] 34%
+[================== ] 35%
+[================== ] 36%
+[=================== ] 37%
+[=================== ] 38%
+[==================== ] 39%
+[==================== ] 40%
+[===================== ] 41%
+[===================== ] 42%
+[====================== ] 43%
+[====================== ] 44%
+[======================= ] 45%
+[======================= ] 46%
+[======================== ] 47%
+[======================== ] 48%
+[========================= ] 49%
+[========================= ] 50%
+[========================== ] 51%
+[========================== ] 52%
+[=========================== ] 53%
+[=========================== ] 54%
+[============================ ] 55%
+[============================ ] 56%
+[============================= ] 57%
+[============================= ] 58%
+[============================== ] 59%
+[============================== ] 60%
+[=============================== ] 61%
+[=============================== ] 62%
+[================================ ] 63%
+[================================ ] 64%
+[================================= ] 65%
+[================================= ] 66%
+[================================== ] 67%
+[================================== ] 68%
+[=================================== ] 69%
+[=================================== ] 70%
+[==================================== ] 71%
+[==================================== ] 72%
+[===================================== ] 73%
+[===================================== ] 74%
+[====================================== ] 75%
+[====================================== ] 76%
+[======================================= ] 77%
+[======================================= ] 78%
+[======================================== ] 79%
+[======================================== ] 80%
+[========================================= ] 81%
+[========================================= ] 82%
+[========================================== ] 83%
+[========================================== ] 84%
+[=========================================== ] 85%
+[=========================================== ] 86%
+[============================================ ] 87%
+[============================================ ] 88%
+[============================================= ] 89%
+[============================================= ] 90%
+[============================================== ] 91%
+[============================================== ] 92%
+[=============================================== ] 93%
+[=============================================== ] 94%
+[================================================ ] 95%
+[================================================ ] 96%
+[================================================= ] 97%
+[================================================= ] 98%
+[==================================================] 99%
+[==================================================] 100%
+Updating actual size in partition header...
diff --git a/roms/skiboot/external/pflash/test/results/04-program-rand.err b/roms/skiboot/external/pflash/test/results/04-program-rand.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/04-program-rand.err
diff --git a/roms/skiboot/external/pflash/test/results/04-program-rand.out b/roms/skiboot/external/pflash/test/results/04-program-rand.out
new file mode 100644
index 000000000..b0df327cd
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/04-program-rand.out
@@ -0,0 +1,111 @@
+About to erase 0x00000300..0x00000400 !
+WARNING ! This will modify your HOST flash chip content !
+Enter "yes" to confirm:Erasing...
+
+[ ] 0%
+[= ] 1%
+[= ] 2%
+[== ] 3%
+[== ] 4%
+[=== ] 5%
+[=== ] 6%
+[==== ] 7%
+[==== ] 8%
+[===== ] 9%
+[===== ] 10%
+[====== ] 11%
+[====== ] 12%
+[======= ] 13%
+[======= ] 14%
+[======== ] 15%
+[======== ] 16%
+[========= ] 17%
+[========= ] 18%
+[========== ] 19%
+[========== ] 20%
+[=========== ] 21%
+[=========== ] 22%
+[============ ] 23%
+[============ ] 24%
+[============= ] 25%
+[============= ] 26%
+[============== ] 27%
+[============== ] 28%
+[=============== ] 29%
+[=============== ] 30%
+[================ ] 31%
+[================ ] 32%
+[================= ] 33%
+[================= ] 34%
+[================== ] 35%
+[================== ] 36%
+[=================== ] 37%
+[=================== ] 38%
+[==================== ] 39%
+[==================== ] 40%
+[===================== ] 41%
+[===================== ] 42%
+[====================== ] 43%
+[====================== ] 44%
+[======================= ] 45%
+[======================= ] 46%
+[======================== ] 47%
+[======================== ] 48%
+[========================= ] 49%
+[========================= ] 50%
+[========================== ] 51%
+[========================== ] 52%
+[=========================== ] 53%
+[=========================== ] 54%
+[============================ ] 55%
+[============================ ] 56%
+[============================= ] 57%
+[============================= ] 58%
+[============================== ] 59%
+[============================== ] 60%
+[=============================== ] 61%
+[=============================== ] 62%
+[================================ ] 63%
+[================================ ] 64%
+[================================= ] 65%
+[================================= ] 66%
+[================================== ] 67%
+[================================== ] 68%
+[=================================== ] 69%
+[=================================== ] 70%
+[==================================== ] 71%
+[==================================== ] 72%
+[===================================== ] 73%
+[===================================== ] 74%
+[====================================== ] 75%
+[====================================== ] 76%
+[======================================= ] 77%
+[======================================= ] 78%
+[======================================== ] 79%
+[======================================== ] 80%
+[========================================= ] 81%
+[========================================= ] 82%
+[========================================== ] 83%
+[========================================== ] 84%
+[=========================================== ] 85%
+[=========================================== ] 86%
+[============================================ ] 87%
+[============================================ ] 88%
+[============================================= ] 89%
+[============================================= ] 90%
+[============================================== ] 91%
+[============================================== ] 92%
+[=============================================== ] 93%
+[=============================================== ] 94%
+[================================================ ] 95%
+[================================================ ] 96%
+[================================================= ] 97%
+[================================================= ] 98%
+[==================================================] 99%
+[==================================================] 100%
+About to program "FILE" at 0x00000300..0x00000400 !
+Programming & Verifying...
+
+[ ] 0%
+[==================================================] 100%
+Updating actual size in partition header...
diff --git a/roms/skiboot/external/pflash/test/results/05-bad-numbers.err b/roms/skiboot/external/pflash/test/results/05-bad-numbers.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/05-bad-numbers.err
diff --git a/roms/skiboot/external/pflash/test/results/05-bad-numbers.out b/roms/skiboot/external/pflash/test/results/05-bad-numbers.out
new file mode 100644
index 000000000..2a76c28c0
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/05-bad-numbers.out
@@ -0,0 +1,224 @@
+Open-Power Flash tool VERSION
+Usage: ./pflash [options] commands...
+
+ Options:
+ -a address, --address=address
+ Specify the start address for erasing, reading
+ or flashing
+
+ -s size, --size=size
+ Specify the size in bytes for erasing, reading
+ or flashing
+
+ -P part_name, --partition=part_name
+ Specify the partition whose content is to be erased
+ programmed or read. This is an alternative to -a and -s
+ if both -P and -s are specified, the smallest of the
+ two will be used
+
+ -f, --force
+ Don't ask for confirmation before erasing or flashing
+
+ -d, --dummy
+ Don't write to flash
+
+ --direct
+ Bypass all safety provided to you by the kernel driver
+ and use the flash driver built into pflash.
+ If you have mtd devices and you use this command, the
+ system may become unstable.
+ If you are reading this sentence then this flag is not
+ what you want! Using this feature without knowing
+ what it does can and likely will result in a bricked
+ machine
+
+ -b, --bmc
+ Target BMC flash instead of host flash.
+ Note: This carries a high chance of bricking your BMC if you
+ don't know what you're doing. Consider --mtd to be safe(r)
+
+ -F filename, --flash-file filename
+ Target filename instead of actual flash.
+
+ -S, --side
+ Side of the flash on which to operate, 0 (default) or 1
+
+ --skip=N
+ Skip N number of bytes from the start when reading
+
+ -T, --toc
+ libffs TOC on which to operate, defaults to 0.
+ leading 0x is required for interpretation of a hex value
+
+ -g
+ Enable verbose libflash debugging
+
+ Commands:
+ -4, --enable-4B
+ Switch the flash and controller to 4-bytes address
+ mode (no confirmation needed).
+
+ -3, --disable-4B
+ Switch the flash and controller to 3-bytes address
+ mode (no confirmation needed).
+
+ -r file, --read=file
+ Read flash content from address into file, use -s
+ to specify the size to read (or it will use the source
+ file size if used in conjunction with -p and -s is not
+ specified). When using -r together with -e or -p, the
+ read will be performed first
+
+ -E, --erase-all
+ Erase entire flash chip
+ (Not supported on all chips/controllers)
+
+ -e, --erase
+ Erase the specified region. If size or address are not
+ specified, but '--program' is used, then the file
+ size will be used (rounded to an erase block) and the
+ address defaults to 0.
+
+ -p file, --program=file
+ Will program the file to flash. If the address is not
+ specified, it will use 0. If the size is not specified
+ it will use the file size. Otherwise it will limit to
+ the specified size (whatever is smaller). If used in
+ conjunction with any erase command, the erase will
+ take place first.
+
+ -t, --tune
+ Just tune the flash controller & access size
+ Must be used in conjuction with --direct
+ (Implicit for all other operations)
+
+ -c --clear
+ Used to ECC clear a partition of the flash
+ Must be used in conjunction with -P. Will erase the
+ partition and then set all the ECC bits as they should be
+
+ -9 --ecc
+ Encode/Decode ECC where specified in the FFS header.
+ This 9 byte ECC method is used for some OpenPOWER
+ partitions.
+ -i, --info
+ Display some information about the flash.
+
+ --detail
+ Displays detailed info about a particular partition.
+ Accepts a numeric partition or can be used in conjuction
+ with the -P flag.
+
+ -h, --help
+ This message.
+
+About to erase 0x00000300..0x00000400 !
+WARNING ! This will modify your HOST flash chip content !
+Enter "yes" to confirm:Erasing...
+
+[ ] 0%
+[= ] 1%
+[= ] 2%
+[== ] 3%
+[== ] 4%
+[=== ] 5%
+[=== ] 6%
+[==== ] 7%
+[==== ] 8%
+[===== ] 9%
+[===== ] 10%
+[====== ] 11%
+[====== ] 12%
+[======= ] 13%
+[======= ] 14%
+[======== ] 15%
+[======== ] 16%
+[========= ] 17%
+[========= ] 18%
+[========== ] 19%
+[========== ] 20%
+[=========== ] 21%
+[=========== ] 22%
+[============ ] 23%
+[============ ] 24%
+[============= ] 25%
+[============= ] 26%
+[============== ] 27%
+[============== ] 28%
+[=============== ] 29%
+[=============== ] 30%
+[================ ] 31%
+[================ ] 32%
+[================= ] 33%
+[================= ] 34%
+[================== ] 35%
+[================== ] 36%
+[=================== ] 37%
+[=================== ] 38%
+[==================== ] 39%
+[==================== ] 40%
+[===================== ] 41%
+[===================== ] 42%
+[====================== ] 43%
+[====================== ] 44%
+[======================= ] 45%
+[======================= ] 46%
+[======================== ] 47%
+[======================== ] 48%
+[========================= ] 49%
+[========================= ] 50%
+[========================== ] 51%
+[========================== ] 52%
+[=========================== ] 53%
+[=========================== ] 54%
+[============================ ] 55%
+[============================ ] 56%
+[============================= ] 57%
+[============================= ] 58%
+[============================== ] 59%
+[============================== ] 60%
+[=============================== ] 61%
+[=============================== ] 62%
+[================================ ] 63%
+[================================ ] 64%
+[================================= ] 65%
+[================================= ] 66%
+[================================== ] 67%
+[================================== ] 68%
+[=================================== ] 69%
+[=================================== ] 70%
+[==================================== ] 71%
+[==================================== ] 72%
+[===================================== ] 73%
+[===================================== ] 74%
+[====================================== ] 75%
+[====================================== ] 76%
+[======================================= ] 77%
+[======================================= ] 78%
+[======================================== ] 79%
+[======================================== ] 80%
+[========================================= ] 81%
+[========================================= ] 82%
+[========================================== ] 83%
+[========================================== ] 84%
+[=========================================== ] 85%
+[=========================================== ] 86%
+[============================================ ] 87%
+[============================================ ] 88%
+[============================================= ] 89%
+[============================================= ] 90%
+[============================================== ] 91%
+[============================================== ] 92%
+[=============================================== ] 93%
+[=============================================== ] 94%
+[================================================ ] 95%
+[================================================ ] 96%
+[================================================= ] 97%
+[================================================= ] 98%
+[==================================================] 99%
+[==================================================] 100%
+About to program "FILE" at 0x00000300..0x00000400 !
+Programming & Verifying...
+
+[ ] 0%
+[==================================================] 100%
diff --git a/roms/skiboot/external/pflash/test/results/06-miscprint.err b/roms/skiboot/external/pflash/test/results/06-miscprint.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/06-miscprint.err
diff --git a/roms/skiboot/external/pflash/test/results/06-miscprint.out b/roms/skiboot/external/pflash/test/results/06-miscprint.out
new file mode 100644
index 000000000..58a6b889c
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/results/06-miscprint.out
@@ -0,0 +1,54 @@
+Detailed partition information
+Name:
+PRESERVED (ID=01)
+
+Start End Actual
+0x00003000 0x00004000 0x00001000
+
+Flags:
+PRESERVED [P]
+Detailed partition information
+Name:
+READONLY (ID=02)
+
+Start End Actual
+0x00004000 0x00005000 0x00001000
+
+Flags:
+READONLY [R]
+Detailed partition information
+Name:
+REPROVISION (ID=03)
+
+Start End Actual
+0x00005000 0x00006000 0x00001000
+
+Flags:
+REPROVISION [F]
+Detailed partition information
+Name:
+BACKUP (ID=04)
+
+Start End Actual
+0x00006000 0x00007000 0x00000000
+
+Flags:
+BACKUP [B]
+Detailed partition information
+Name:
+VOLATILE (ID=05)
+
+Start End Actual
+0x00007000 0x00008000 0x00001000
+
+Flags:
+VOLATILE [V]
+Detailed partition information
+Name:
+CLEARECC (ID=06)
+
+Start End Actual
+0x00008000 0x00009000 0x00001000
+
+Flags:
+CLEARECC [C]
diff --git a/roms/skiboot/external/pflash/test/test-pflash b/roms/skiboot/external/pflash/test/test-pflash
new file mode 100755
index 000000000..dd775bda3
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/test-pflash
@@ -0,0 +1,59 @@
+#! /bin/sh
+
+. test/test.sh
+
+get_part_start() {
+ #We want to fail the test if the caller passed garbage
+ if [ "$#" -ne 2 ] || ! grep -q "$2" "$1" ; then
+ fail_test;
+ fi
+ grep "$2" "$1" | cut -f2 -d, | xargs printf "%d";
+}
+
+get_part_len() {
+ #We want to fail the test if the caller passed garbage
+ if [ "$#" -ne 2 ] || ! grep -q "$2" "$1" ; then
+ fail_test;
+ fi
+ grep "$2" "$1" | cut -f3 -d, | xargs printf "%d";
+}
+
+get_part_end() {
+ start=$(get_part_start "$1" "$2");
+ len=$(get_part_len "$1" "$2");
+ expr "$start" \+ "$len";
+}
+
+cmp_with_ff() {
+ if [ "$#" -ne 3 ] ; then
+ fail_test;
+ fi
+ file="$1";
+ start="$2";
+ len="$3";
+
+ blank=$(mktemp --tmpdir="$DATA_DIR" blank.pnorXXXXXX);
+ dd status=none if=/dev/zero bs="$len" count=1 | tr '\000' '\377' > "$blank"
+ cmp --bytes="$len" --ignore-initial="$start:0" "$file" "$blank";
+ if [ "$?" -ne 0 ] ; then
+ fail_test;
+ fi
+ #fail_test; will trigger a cleanup straight away
+ rm $blank;
+}
+
+#The reason for it is that this way there is a completely independant
+#way of calculating checksums so if checksums fail, we can be
+#confident its because libflash/libffs changed
+update_checksum() {
+ if [ "$#" -ne 2 ] ; then
+ fail_test;
+ fi
+ file=$1;
+ part=$2;
+ dd if="$file" bs=1 skip="$part" count=124 status=none | perl -e 'use integer; binmode STDIN; binmode STDOUT; my $result=0; while (read STDIN, $word, 4) { $result = $result ^ unpack("N", $word); } print pack("N",$result)' | dd of="$file" seek="$(expr $part \+ 124)" bs=1 count=4 status=none conv=notrunc
+}
+
+run_tests "test/tests/*" "test/results" "test/files"
+
+
diff --git a/roms/skiboot/external/pflash/test/tests/00-usage b/roms/skiboot/external/pflash/test/tests/00-usage
new file mode 100644
index 000000000..4c4ba2aa5
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/tests/00-usage
@@ -0,0 +1,13 @@
+#! /bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+run_binary "./pflash" "-h"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "pflash"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/pflash/test/tests/01-info b/roms/skiboot/external/pflash/test/tests/01-info
new file mode 100644
index 000000000..583d275c4
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/tests/01-info
@@ -0,0 +1,23 @@
+#! /bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+touch "$DATA_DIR/$CUR_TEST.pnor"
+
+# Don't record the output of ffspart
+../ffspart/ffspart -s 0x1000 -c 10 -i "$DATA_DIR/$CUR_TEST.ffs" \
+ -p "$DATA_DIR/$CUR_TEST.pnor" 2>&1 >/dev/null
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+run_binary "./pflash" "-F $DATA_DIR/$CUR_TEST.pnor --info"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+#--info will print the name of the file which will change between runs
+sed -i "s|$DATA_DIR/$CUR_TEST.pnor|FILE|" "$STDOUT_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/pflash/test/tests/02-erase b/roms/skiboot/external/pflash/test/tests/02-erase
new file mode 100644
index 000000000..f1db99266
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/tests/02-erase
@@ -0,0 +1,27 @@
+#! /bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+touch "$DATA_DIR/$CUR_TEST.pnor"
+
+# Don't record the output of ffspart
+../ffspart/ffspart -s 0x100 -c 10 -i "$DATA_DIR/$CUR_TEST.ffs" \
+ -p "$DATA_DIR/$CUR_TEST.pnor" 2>&1 >/dev/null
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+yes yes | run_binary "./pflash" "-F $DATA_DIR/$CUR_TEST.pnor -E"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+cmp_with_ff "$DATA_DIR/$CUR_TEST.pnor" 0 \
+ "$(stat --printf="%s" "$DATA_DIR/$CUR_TEST.pnor")"
+
+# The test infrastructure will clean up but lets not chew unnecessarily
+# though disk space
+rm "$DATA_DIR/$CUR_TEST.pnor"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/pflash/test/tests/03-erase-parts b/roms/skiboot/external/pflash/test/tests/03-erase-parts
new file mode 100644
index 000000000..91b0a145e
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/tests/03-erase-parts
@@ -0,0 +1,56 @@
+#! /bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+touch "$DATA_DIR/$CUR_TEST.pnor"
+
+# Don't record the output of ffspart
+../ffspart/ffspart -s 0x100 -c 10 -i "$DATA_DIR/$CUR_TEST.ffs" \
+ -p "$DATA_DIR/$CUR_TEST.pnor" 2>&1 >/dev/null
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+cp "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+#Let us all take a second to appreciate the fragility of this.
+#The reason we need is that pflash -e will set the actual size of the
+#partition to zero, which we'll do here. Examination determined that
+#it will be at byte 216 in the file
+dd if=/dev/zero of="$DATA_DIR/$CUR_TEST.bk" \
+ bs=1 seek=216 count=4 conv=notrunc status=none
+
+#176 Should be where the FFS header for partition ONE starts
+update_checksum "$DATA_DIR/$CUR_TEST.bk" "176";
+
+yes yes | run_binary "./pflash" "-F $DATA_DIR/$CUR_TEST.pnor -e -P ONE"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+one_start=$(get_part_start "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+one_len=$(get_part_len "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+one_end=$(get_part_end "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+cmp_with_ff "$DATA_DIR/$CUR_TEST.pnor" "$one_start" "$one_len"
+
+cmp --bytes="$one_start" "$DATA_DIR/$CUR_TEST.pnor" \
+ "$DATA_DIR/$CUR_TEST.bk";
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+cmp --ignore-initial="$one_end" \
+ --bytes="$(expr $(stat --printf="%s" "$DATA_DIR/$CUR_TEST.pnor") - "$one_end")" \
+ "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+# The test infrastructure will clean up but lets no chew unnecessarily
+# though disk space
+rm "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/pflash/test/tests/04-program-rand b/roms/skiboot/external/pflash/test/tests/04-program-rand
new file mode 100644
index 000000000..fc8abb648
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/tests/04-program-rand
@@ -0,0 +1,55 @@
+#! /bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+touch "$DATA_DIR/$CUR_TEST.pnor"
+
+# Don't record the output of ffspart
+../ffspart/ffspart -s 0x100 -c 10 -i "$DATA_DIR/$CUR_TEST.ffs" \
+ -p "$DATA_DIR/$CUR_TEST.pnor" 2>&1 >/dev/null
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+cp "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+one_len=$(get_part_len "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+one_start=$(get_part_start "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+one_end=$(get_part_end "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+dd if=/dev/urandom bs="$one_len" count=1 of="$DATA_DIR/random" status=none
+
+yes yes | run_binary "./pflash" \
+ "-F $DATA_DIR/$CUR_TEST.pnor -e -P ONE -p $DATA_DIR/random"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+cmp --ignore-initial="$one_start:0" --bytes="$one_len" \
+ "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/random"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+cmp --bytes="$one_start" "$DATA_DIR/$CUR_TEST.pnor" \
+ "$DATA_DIR/$CUR_TEST.bk";
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+cmp --ignore-initial="$one_end" \
+ --bytes="$(expr $(stat --printf="%s" "$DATA_DIR/$CUR_TEST.pnor") - "$one_end")" \
+ "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+sed -i "s|$DATA_DIR/random|FILE|" "$STDOUT_OUT"
+
+# The test infrastructure will clean up but lets no chew unnecessarily
+# though disk space
+rm "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk" "$DATA_DIR/random"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/pflash/test/tests/05-bad-numbers b/roms/skiboot/external/pflash/test/tests/05-bad-numbers
new file mode 100644
index 000000000..03cc39ebf
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/tests/05-bad-numbers
@@ -0,0 +1,66 @@
+#! /bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+touch "$DATA_DIR/$CUR_TEST.pnor"
+
+# Don't record the output of ffspart
+../ffspart/ffspart -s 0x100 -c 10 -i "$DATA_DIR/$CUR_TEST.ffs" \
+ -p "$DATA_DIR/$CUR_TEST.pnor" 2>&1 >/dev/null
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+cp "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+one_len=$(get_part_len "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+one_start=$(get_part_start "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+one_end=$(get_part_end "$DATA_DIR/$CUR_TEST.ffs" "ONE");
+dd if=/dev/urandom bs="$one_len" count=1 of="$DATA_DIR/random" status=none
+
+#This should error out
+run_binary "./pflash" \
+ "-F $DATA_DIR/$CUR_TEST.pnor -e -a NOT_A_NUMBER -s ALSO_NOT_A_NUMBER"
+if [ "$?" -eq 0 ] ; then
+ fail_test;
+fi
+
+one_start_decimal=$(printf "%d" $one_start);
+one_len_decimal=$(printf "%d" $one_len);
+yes yes | run_binary "./pflash" \
+ "-F $DATA_DIR/$CUR_TEST.pnor -e -a $one_start_decimal \
+ -s $one_len_decimal -p $DATA_DIR/random"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+cmp --ignore-initial="$one_start:0" --bytes="$one_len" \
+ "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/random"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+cmp --bytes="$one_start" "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk";
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+cmp --ignore-initial="$one_end" \
+ --bytes="$(expr $(stat --printf="%s" "$DATA_DIR/$CUR_TEST.pnor") - "$one_end")" \
+ "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+sed -i "s|$DATA_DIR/random|FILE|" "$STDOUT_OUT"
+
+# The test infrastructure will clean up but lets no chew unnecessarily
+# though disk space
+rm "$DATA_DIR/$CUR_TEST.pnor" "$DATA_DIR/$CUR_TEST.bk" "$DATA_DIR/random"
+
+strip_version_from_result "pflash"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/pflash/test/tests/06-miscprint b/roms/skiboot/external/pflash/test/tests/06-miscprint
new file mode 100644
index 000000000..bee84afc5
--- /dev/null
+++ b/roms/skiboot/external/pflash/test/tests/06-miscprint
@@ -0,0 +1,46 @@
+#! /bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+touch "$DATA_DIR/$CUR_TEST.pnor"
+
+# Don't record the output of ffspart
+../ffspart/ffspart -s 0x1000 -c 10 -i "$DATA_DIR/$CUR_TEST.ffs" \
+ -p "$DATA_DIR/$CUR_TEST.pnor" 2>&1 >/dev/null
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+run_binary "./pflash" "--detail=1 -F $DATA_DIR/$CUR_TEST.pnor"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+run_binary "./pflash" "--detail=2 -F $DATA_DIR/$CUR_TEST.pnor"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+run_binary "./pflash" "--detail=3 -F $DATA_DIR/$CUR_TEST.pnor"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+run_binary "./pflash" "--detail=4 -F $DATA_DIR/$CUR_TEST.pnor"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+run_binary "./pflash" "--detail=5 -F $DATA_DIR/$CUR_TEST.pnor"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+run_binary "./pflash" "--detail=6 -F $DATA_DIR/$CUR_TEST.pnor"
+if [ "$?" -ne 0 ] ; then
+ fail_test;
+fi
+
+# The test infrastructure will clean up but lets no chew unnecessarily
+# though disk space
+rm "$DATA_DIR/$CUR_TEST.pnor"
+
+strip_version_from_result "pflash"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/read_esel.sh b/roms/skiboot/external/read_esel.sh
new file mode 100644
index 000000000..ee4c66ed4
--- /dev/null
+++ b/roms/skiboot/external/read_esel.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+# Copyright 2013-2014 IBM Corp.
+
+set -e
+
+BMC_HOST=$1
+RECORD_ID=$2
+
+BMC_USER=admin
+BMC_PASS=admin
+
+if [ -z "$BMC_HOST" -o -z "$RECORD_ID" ]; then
+ echo "Usage: $0 <bmc hostname> <record id>"
+ echo "Example: $0 bmc 0xa > pel.bin"
+ echo ''
+ echo 'Record ids can be found using ipmitool with the "sel list" command. Records with'
+ echo 'a description of "OEM record df" contain extended SEL information (in PEL'
+ echo 'format) which can be extracted with this tool.'
+ exit -1
+fi
+
+# Convert a number into 2 hex-bytes in little-endian order
+function conv_le {
+ echo $(for i in $(printf %04x $1 | grep -o .. | tac); do echo -n "0x$i "; done)
+}
+
+function conv_native {
+ echo -n "0x${2}${1}"
+}
+
+record=$(conv_le $2)
+offset=0
+progress=0
+
+while [ $progress = 0 ]; do
+ result=$(ipmitool -H ${BMC_HOST} -I lan -U ${BMC_USER} -P ${BMC_PASS} raw 0x32 0xf1 ${record} $(conv_le ${offset}))
+ len=$(conv_native $(echo ${result} | cut -d " " -f 1-2))
+ progress=$(($(echo ${result} | cut -d " " -f 3)))
+ data="$data "$(echo -n ${result} | cut -d " " -f 6-)
+ offset=$(($offset + ${#data}/3))
+done
+
+echo -n ${data} | cut -d " " -f 1-$(($len)) | xxd -r -p
diff --git a/roms/skiboot/external/shared/Makefile b/roms/skiboot/external/shared/Makefile
new file mode 100644
index 000000000..9ea4adc51
--- /dev/null
+++ b/roms/skiboot/external/shared/Makefile
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+.DEFAULT_GOAL := all
+GET_ARCH := ../../external/common/get_arch.sh
+include ../../external/common/rules.mk
+include rules.mk
+
+PREFIX ?= /usr/local/
+LIBDIR := $(PREFIX)/lib
+INCDIR := $(PREFIX)/include/libflash
+
+ifneq ($(ARCH), ARCH_ARM)
+CFLAGS += -m64
+endif
+CFLAGS += -Werror -Wall -g2 -ggdb -I. -fPIC
+
+LIBFLASH_OBJS := libflash-file.o libflash-libflash.o libflash-libffs.o \
+ libflash-ecc.o libflash-blocklevel.o
+ARCHFLASH_OBJS := common-arch_flash.o
+OBJS := $(LIBFLASH_OBJS) $(ARCHFLASH_OBJS)
+
+LIBFLASH_H := libflash/file.h libflash/libflash.h libflash/libffs.h \
+ libflash/ffs.h libflash/ecc.h libflash/blocklevel.h libflash/errors.h
+ARCHFLASH_H := common/arch_flash.h
+
+LIBFLASH_FILES := libflash.c libffs.c ecc.c blocklevel.c file.c
+LIBFLASH_SRC := $(addprefix libflash/,$(LIBFLASH_FILES))
+
+$(LIBFLASH_SRC): | links
+
+$(LIBFLASH_OBJS) : libflash-%.o : libflash/%.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+clean:
+ rm -f $(OBJS) common-*.o *.so*
+
+distclean: clean
+ rm -f ccan libflash common
+
+all: links arch_links $(OBJS)
+ $(CC) -shared -Wl,-soname,libflash.so -o $(SHARED_NAME) $(OBJS)
+
+install-lib: all
+ install -D -m 0755 $(SHARED_NAME) $(DESTDIR)$(LIBDIR)/$(SHARED_NAME)
+ ln -sf $(SHARED_NAME) $(DESTDIR)$(LIBDIR)/libflash.so
+
+install-dev: links arch_links
+ mkdir -p $(DESTDIR)$(INCDIR)
+ install -m 0644 $(LIBFLASH_H) $(ARCHFLASH_H) $(DESTDIR)$(INCDIR)
+
+install: install-lib install-dev
+
+uninstall:
+ rm -f $(DESTDIR)$(LIBDIR)/libflash*
+ rm -rf $(DESTDIR)$(INCDIR)
diff --git a/roms/skiboot/external/shared/config.h b/roms/skiboot/external/shared/config.h
new file mode 100644
index 000000000..f78e30e08
--- /dev/null
+++ b/roms/skiboot/external/shared/config.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * For CCAN
+ *
+ * Copyright 2016 IBM Corp.
+ */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF 1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN 0
+#define HAVE_LITTLE_ENDIAN 1
+#else
+#define HAVE_BIG_ENDIAN 1
+#define HAVE_LITTLE_ENDIAN 0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
diff --git a/roms/skiboot/external/shared/rules.mk b/roms/skiboot/external/shared/rules.mk
new file mode 100644
index 000000000..a3d83d166
--- /dev/null
+++ b/roms/skiboot/external/shared/rules.mk
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+# NOTE: shared lib versioning has *NOTHING* to do with software package version
+#
+# If source code has changed, revision++
+# if any interfaces have been added, removed, or changed, current++, revision=0
+# if any interfaces have been added, age++
+# if any interfaces have been removed or changed, age=0
+#
+# i.e. AGE refers to backwards compatibility.
+# e.g. If we start with 5.3.0 and we're releasing new version:
+# - bug fix no api change: 5.4.0
+# - new API call, backwards compatible otherwise: 6.0.1
+# - API removed, or changed (i.e. not backwards compat): 6.0.0
+
+LIBFLASH_VERSION_CURRENT=5
+LIBFLASH_VERSION_REVISION=3
+LIBFLASH_VERSION_AGE=0
+
+SHARED_NAME=libflash.so.${LIBFLASH_VERSION_CURRENT}.${LIBFLASH_VERSION_REVISION}.${LIBFLASH_VERSION_AGE}
diff --git a/roms/skiboot/external/test/test.sh b/roms/skiboot/external/test/test.sh
new file mode 100755
index 000000000..2bf1e89a6
--- /dev/null
+++ b/roms/skiboot/external/test/test.sh
@@ -0,0 +1,101 @@
+#! /bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Copyright 2013-2017 IBM Corp.
+
+run_binary() {
+ if [ -x "$1" ] ; then
+ $VALGRIND "$1" $2 2>> $STDERR_OUT 1>> $STDOUT_OUT
+ else
+ echo "Fatal error, cannot execute binary '$1'. Did you make?";
+ exit 1;
+ fi
+}
+
+fail_test() {
+ echo "$0 ($CUR_TEST): test failed";
+ echo "Test directory preserved:"
+ echo " DATA_DIR = $DATA_DIR"
+ echo " STDOUT = $STDOUT_OUT"
+ echo " STDERR = $STDERR_OUT"
+ exit ${1:-1};
+}
+
+pass_test() {
+ /bin/true;
+}
+
+strip_version_from_result() {
+ VERSION=$(./make_version.sh $1)
+ sed -i "s/${VERSION}/VERSION/;s/^Open-Power \(.*\) tool .*/Open-Power \\1 tool VERSION/" $STDERR_OUT
+ sed -i "s/${VERSION}/VERSION/;s/^Open-Power \(.*\) tool .*/Open-Power \\1 tool VERSION/" $STDOUT_OUT
+}
+
+diff_with_result() {
+ # Explicitly diff a file with an arbitrary result file
+ if [ "$#" -eq 1 ] ; then
+ if ! diff -u "$RESULT" "$1" ; then
+ fail_test;
+ fi
+ # Otherwise just diff result.out with stdout and result.err with stderr
+ else
+ #Strip carriage returns, useful for pflash which does fancy
+ #'progress bars'. The main reason for this is is that email
+ #doesn't barf at really really long lines
+ if ! cat "$STDOUT_OUT" | tr '\r' '\n' | \
+ diff -u "${RESULT}.out" - ; then
+ fail_test;
+ fi
+ if ! cat "$STDERR_OUT" | tr '\r' '\n' | \
+ diff -u "${RESULT}.err" - ; then
+ fail_test;
+ fi
+ fi
+}
+
+run_tests() {
+ if [ $# -lt 2 ] ; then
+ echo "Usage run_tests test_dir result_dir [data_dir]";
+ exit 1;
+ fi
+
+ all_tests="$1";
+ res_path="$2";
+
+ if [ ! -d "$res_path" ] ; then
+ echo "Result path isn't a valid directory";
+ exit 1;
+ fi
+
+ export DATA_DIR=$(mktemp --tmpdir -d external-test-datadir.XXXXXX);
+ export STDERR_OUT="$DATA_DIR/stderr"
+ export STDOUT_OUT="$DATA_DIR/stdout"
+ if [ $# -eq 3 ] ; then
+ cp -r $3/* "$DATA_DIR"
+ fi
+
+
+ for the_test in $all_tests; do
+ export CUR_TEST=$(basename $the_test)
+ export RESULT="$res_path/$CUR_TEST"
+ echo "running $the_test"
+
+ . "$the_test";
+ R="$?"
+ if [ "$R" -ne 0 ] ; then
+ fail_test "$R";
+ fi
+ #reset for next test
+ > "$STDERR_OUT";
+ > "$STDOUT_OUT";
+ done
+
+ rm -rf $STDERR_OUT;
+ rm -rf $STDOUT_OUT;
+ rm -rf $DATA_DIR;
+
+ echo "$0 tests passed"
+
+ exit 0;
+}
+
diff --git a/roms/skiboot/external/trace/Makefile b/roms/skiboot/external/trace/Makefile
new file mode 100644
index 000000000..737397bca
--- /dev/null
+++ b/roms/skiboot/external/trace/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+HOSTEND=$(shell uname -m | sed -e 's/^i.*86$$/LITTLE/' -e 's/^x86.*/LITTLE/' -e 's/^ppc64le/LITTLE/' -e 's/^ppc.*/BIG/')
+CFLAGS=-g -Wall -DHAVE_$(HOSTEND)_ENDIAN -I../../include -I../../
+
+dump_trace: dump_trace.c trace.c ../../ccan/heap/heap.c
+
+clean:
+ rm -f dump_trace *.o
diff --git a/roms/skiboot/external/trace/dump_trace.c b/roms/skiboot/external/trace/dump_trace.c
new file mode 100644
index 000000000..f54dfa081
--- /dev/null
+++ b/roms/skiboot/external/trace/dump_trace.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Dump the content of an OPAL trace
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <trace.h>
+#include <err.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "../../ccan/endian/endian.h"
+#include "../../ccan/short_types/short_types.h"
+#include "../../ccan/heap/heap.h"
+#include "trace.h"
+
+
+struct trace_entry {
+ int index;
+ union trace t;
+ struct list_node link;
+};
+
+static int follow;
+static long poll_msecs;
+
+static void *ezalloc(size_t size)
+{
+ void *p;
+
+ p = calloc(size, 1);
+ if (!p)
+ err(1, "Allocating memory");
+ return p;
+}
+
+#define TB_HZ 512000000ul
+
+static void display_header(const struct trace_hdr *h)
+{
+ static u64 prev_ts;
+ u64 ts = be64_to_cpu(h->timestamp);
+
+ printf("[%5lu.%09lu,%d] (+%8lx) [%03x] : ",
+ ts / TB_HZ, /* match the usual skiboot log header */
+ ts % TB_HZ,
+ h->type, /* hey why not */
+ prev_ts ? (ts - prev_ts) % TB_HZ : 0, be16_to_cpu(h->cpu));
+ prev_ts = ts;
+}
+
+static void dump_fsp_event(struct trace_fsp_event *t)
+{
+ printf("FSP_EVT [st=%d] ", be16_to_cpu(t->fsp_state));
+
+ switch(be16_to_cpu(t->event)) {
+ case TRACE_FSP_EVT_LINK_DOWN:
+ printf("LINK DOWN");
+ break;
+ case TRACE_FSP_EVT_DISR_CHG:
+ printf("DISR CHANGE (0x%08x)", be32_to_cpu(t->data[0]));
+ break;
+ case TRACE_FSP_EVT_SOFT_RR:
+ printf("SOFT R&R (DISR=0x%08x)", be32_to_cpu(t->data[0]));
+ break;
+ case TRACE_FSP_EVT_RR_COMPL:
+ printf("R&R COMPLETE");
+ break;
+ case TRACE_FSP_EVT_HDES_CHG:
+ printf("HDES CHANGE (0x%08x)", be32_to_cpu(t->data[0]));
+ break;
+ case TRACE_FSP_EVT_POLL_IRQ:
+ printf("%s HDIR=%08x CTL=%08x PSI_IRQ=%d",
+ t->data[0] ? "IRQ " : "POLL", be32_to_cpu(t->data[1]),
+ be32_to_cpu(t->data[2]), be32_to_cpu(t->data[3]));
+ break;
+ default:
+ printf("Unknown %d (d: %08x %08x %08x %08x)",
+ be16_to_cpu(t->event), be32_to_cpu(t->data[0]),
+ be32_to_cpu(t->data[1]), be32_to_cpu(t->data[2]),
+ be32_to_cpu(t->data[3]));
+ }
+ printf("\n");
+}
+
+static void dump_opal_call(struct trace_opal *t)
+{
+ unsigned int i, n;
+
+ printf("OPAL CALL %"PRIu64, be64_to_cpu(t->token));
+ printf(" LR=0x%016"PRIx64" SP=0x%016"PRIx64,
+ be64_to_cpu(t->lr), be64_to_cpu(t->sp));
+ n = (t->hdr.len_div_8 * 8 - offsetof(union trace, opal.r3_to_11))
+ / sizeof(u64);
+ for (i = 0; i < n; i++)
+ printf(" R%u=0x%016"PRIx64,
+ i+3, be64_to_cpu(t->r3_to_11[i]));
+ printf("\n");
+}
+
+static void dump_fsp_msg(struct trace_fsp_msg *t)
+{
+ unsigned int i;
+
+ printf("FSP_MSG: CMD %u SEQ %u MOD %u SUB %u DLEN %u %s [",
+ be32_to_cpu(t->word0) & 0xFFFF,
+ be32_to_cpu(t->word0) >> 16,
+ be32_to_cpu(t->word1) >> 8,
+ be32_to_cpu(t->word1) & 0xFF,
+ t->dlen,
+ t->dir == TRACE_FSP_MSG_IN ? "IN" :
+ (t->dir == TRACE_FSP_MSG_OUT ? "OUT" : "UNKNOWN"));
+
+ for (i = 0; i < t->dlen; i++)
+ printf("%s%02x", i ? " " : "", t->data[i]);
+ printf("]\n");
+}
+
+static void dump_uart(struct trace_uart *t)
+{
+ switch(t->ctx) {
+ case TRACE_UART_CTX_IRQ:
+ printf(": IRQ IRQEN=%d IN_CNT=%d\n",
+ !t->irq_state, be16_to_cpu(t->in_count));
+ break;
+ case TRACE_UART_CTX_POLL:
+ printf(": POLL IRQEN=%d IN_CNT=%d\n",
+ !t->irq_state, be16_to_cpu(t->in_count));
+ break;
+ case TRACE_UART_CTX_READ:
+ printf(": READ IRQEN=%d IN_CNT=%d READ=%d\n",
+ !t->irq_state, be16_to_cpu(t->in_count), t->cnt);
+ break;
+ default:
+ printf(": ???? IRQEN=%d IN_CNT=%d\n",
+ !t->irq_state, be16_to_cpu(t->in_count));
+ break;
+ }
+}
+
+static void dump_i2c(struct trace_i2c *t)
+{
+ uint16_t type = be16_to_cpu(t->type);
+
+ printf("I2C: bus: %d dev: %02x len: %x ",
+ be16_to_cpu(t->bus),
+ be16_to_cpu(t->i2c_addr),
+ be16_to_cpu(t->size)
+ );
+
+ switch (type & 0x3) {
+ case 0:
+ printf("read");
+ break;
+ case 1:
+ printf("write");
+ break;
+ case 2:
+ printf("smbus read from %x", be16_to_cpu(t->smbus_reg));
+ break;
+ case 3:
+ printf("smbus write to %x", be16_to_cpu(t->smbus_reg));
+ break;
+ default:
+ printf("u wot?");
+ }
+
+ printf(", rc = %hd\n", (int16_t) be16_to_cpu(t->rc));
+}
+
+static void load_traces(struct trace_reader *trs, int count)
+{
+ struct trace_entry *te;
+ union trace t;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ while (trace_get(&t, &trs[i])) {
+ te = ezalloc(sizeof(struct trace_entry));
+ memcpy(&te->t, &t, sizeof(union trace));
+ te->index = i;
+ list_add_tail(&trs[i].traces, &te->link);
+ }
+ }
+}
+
+static void print_trace(union trace *t)
+{
+ display_header(&t->hdr);
+ switch (t->hdr.type) {
+ case TRACE_REPEAT:
+ printf("REPEATS: %u times\n",
+ be16_to_cpu(t->repeat.num));
+ break;
+ case TRACE_OVERFLOW:
+ printf("**OVERFLOW**: %"PRIu64" bytes missed\n",
+ be64_to_cpu(t->overflow.bytes_missed));
+ break;
+ case TRACE_OPAL:
+ dump_opal_call(&t->opal);
+ break;
+ case TRACE_FSP_MSG:
+ dump_fsp_msg(&t->fsp_msg);
+ break;
+ case TRACE_FSP_EVENT:
+ dump_fsp_event(&t->fsp_evt);
+ break;
+ case TRACE_UART:
+ dump_uart(&t->uart);
+ break;
+ case TRACE_I2C:
+ dump_i2c(&t->i2c);
+ break;
+ default:
+ printf("UNKNOWN(%u) CPU %u length %u\n",
+ t->hdr.type, be16_to_cpu(t->hdr.cpu),
+ t->hdr.len_div_8 * 8);
+ }
+}
+
+/* Gives a min heap */
+bool earlier_entry(const void *va, const void *vb)
+{
+ struct trace_entry *a, *b;
+
+ a = (struct trace_entry *) va;
+ b = (struct trace_entry *) vb;
+
+ if (!a)
+ return false;
+ if (!b)
+ return true;
+ return be64_to_cpu(a->t.hdr.timestamp) < be64_to_cpu(b->t.hdr.timestamp);
+}
+
+static void display_traces(struct trace_reader *trs, int count)
+{
+ struct trace_entry *current, *next;
+ struct heap *h;
+ int i;
+
+ h = heap_init(earlier_entry);
+ if (!h)
+ err(1, "Allocating memory");
+
+ for (i = 0; i < count; i++) {
+ current = list_pop(&trs[i].traces, struct trace_entry, link);
+ /* no need to add empty ones */
+ if (current)
+ heap_push(h, current);
+ }
+
+ while (h->len) {
+ current = heap_pop(h);
+ if (!current)
+ break;
+
+ print_trace(&current->t);
+
+ next = list_pop(&trs[current->index].traces, struct trace_entry,
+ link);
+ heap_push(h, next);
+ free(current);
+ }
+ heap_free(h);
+}
+
+
+/* Can't poll for 0 msec, so use 0 to signify failure */
+static long get_mseconds(char *s)
+{
+ char *end;
+ long ms;
+
+ errno = 0;
+ ms = strtol(s, &end, 10);
+ if (errno || *end || ms < 0)
+ return 0;
+ return ms;
+}
+
+static void usage(void)
+{
+ errx(1, "Usage: dump_trace [-f [-s msecs]] file...");
+}
+
+int main(int argc, char *argv[])
+{
+ struct trace_reader *trs;
+ struct trace_info *ti;
+ bool no_mmap = false;
+ struct stat sb;
+ int fd, opt, i;
+
+ poll_msecs = 1000;
+ while ((opt = getopt(argc, argv, "fs:")) != -1) {
+ switch (opt) {
+ case 'f':
+ follow++;
+ break;
+ case 's':
+ poll_msecs = get_mseconds(optarg);
+ if (follow && poll_msecs)
+ break;
+ /* fallthru */
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ trs = ezalloc(sizeof(struct trace_reader) * argc);
+
+ for (i = 0; i < argc; i++) {
+ fd = open(argv[i], O_RDONLY);
+ if (fd < 0)
+ err(1, "Opening %s", argv[i]);
+
+ if (fstat(fd, &sb) < 0)
+ err(1, "Stating %s", argv[1]);
+
+ ti = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (ti == MAP_FAILED) {
+ no_mmap = true;
+
+ ti = ezalloc(sb.st_size);
+ if (!ti)
+ err(1, "allocating memory for %s", argv[i]);
+
+ if (read(fd, ti, sb.st_size) == -1)
+ err(1, "reading from %s", argv[i]);
+ }
+
+ trs[i].tb = &ti->tb;
+ list_head_init(&trs[i].traces);
+ }
+
+ if (no_mmap) {
+ fprintf(stderr, "disabling follow mode: can't mmap() OPAL export files\n");
+ follow = 0;
+ }
+
+ do {
+ load_traces(trs, argc);
+ display_traces(trs, argc);
+ if (follow)
+ usleep(poll_msecs * 1000);
+ } while (follow);
+
+ return 0;
+}
diff --git a/roms/skiboot/external/trace/trace.c b/roms/skiboot/external/trace/trace.c
new file mode 100644
index 000000000..97cec7d9d
--- /dev/null
+++ b/roms/skiboot/external/trace/trace.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * This example code shows how to read from the trace buffer.
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <external/trace/trace.h>
+#include "../ccan/endian/endian.h"
+#include "../ccan/short_types/short_types.h"
+#include "trace.h"
+#include <trace_types.h>
+#include <errno.h>
+
+#if defined(__powerpc__) || defined(__powerpc64__)
+#define rmb() lwsync()
+#else
+#define rmb()
+#endif
+
+bool trace_empty(const struct trace_reader *tr)
+{
+ const struct trace_repeat *rep;
+
+ if (tr->rpos == be64_to_cpu(tr->tb->end))
+ return true;
+
+ /*
+ * If we have a single element only, and it's a repeat buffer
+ * we've already seen every repeat for (yet which may be
+ * incremented in future), we're also empty.
+ */
+ rep = (void *)tr->tb->buf + tr->rpos % be64_to_cpu(tr->tb->buf_size);
+ if (be64_to_cpu(tr->tb->end) != tr->rpos + sizeof(*rep))
+ return false;
+
+ if (rep->type != TRACE_REPEAT)
+ return false;
+
+ if (be16_to_cpu(rep->num) != tr->last_repeat)
+ return false;
+
+ return true;
+}
+
+/* You can't read in parallel, so some locking required in caller. */
+bool trace_get(union trace *t, struct trace_reader *tr)
+{
+ u64 start, rpos;
+ size_t len;
+
+ len = sizeof(*t) < be32_to_cpu(tr->tb->max_size) ? sizeof(*t) :
+ be32_to_cpu(tr->tb->max_size);
+
+ if (trace_empty(tr))
+ return false;
+
+again:
+ /*
+ * The actual buffer is slightly larger than tbsize, so this
+ * memcpy is always valid.
+ */
+ memcpy(t, tr->tb->buf + tr->rpos % be64_to_cpu(tr->tb->buf_size), len);
+
+ rmb(); /* read barrier, so we read tr->tb->start after copying record. */
+
+ start = be64_to_cpu(tr->tb->start);
+ rpos = tr->rpos;
+
+ /* Now, was that overwritten? */
+ if (rpos < start) {
+ /* Create overflow record. */
+ t->overflow.unused64 = 0;
+ t->overflow.type = TRACE_OVERFLOW;
+ t->overflow.len_div_8 = sizeof(t->overflow) / 8;
+ t->overflow.bytes_missed = cpu_to_be64(start - rpos);
+ tr->rpos = start;
+ return true;
+ }
+
+ /* Repeat entries need special handling */
+ if (t->hdr.type == TRACE_REPEAT) {
+ u32 num = be16_to_cpu(t->repeat.num);
+
+ /* In case we've read some already... */
+ t->repeat.num = cpu_to_be16(num - tr->last_repeat);
+
+ /* Record how many repeats we saw this time. */
+ tr->last_repeat = num;
+
+ /* Don't report an empty repeat buffer. */
+ if (t->repeat.num == 0) {
+ /*
+ * This can't be the last buffer, otherwise
+ * trace_empty would have returned true.
+ */
+ assert(be64_to_cpu(tr->tb->end) >
+ rpos + t->hdr.len_div_8 * 8);
+ /* Skip to next entry. */
+ tr->rpos = rpos + t->hdr.len_div_8 * 8;
+ tr->last_repeat = 0;
+ goto again;
+ }
+ } else {
+ tr->last_repeat = 0;
+ tr->rpos = rpos + t->hdr.len_div_8 * 8;
+ }
+
+ return true;
+}
diff --git a/roms/skiboot/external/trace/trace.h b/roms/skiboot/external/trace/trace.h
new file mode 100644
index 000000000..1a5a138ae
--- /dev/null
+++ b/roms/skiboot/external/trace/trace.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright 2013-2019 IBM Corp.
+ */
+#ifndef E_TRACE_H
+#define E_TRACE_H
+
+#include <stdbool.h>
+#include <types.h>
+#include <trace.h>
+#include <trace_types.h>
+
+struct trace_reader {
+ /* This is where the reader is up to. */
+ u64 rpos;
+ /* If the last one we read was a repeat, this shows how many. */
+ u32 last_repeat;
+ struct list_head traces;
+ struct tracebuf *tb;
+};
+
+/* Is this tracebuf empty? */
+bool trace_empty(const struct trace_reader *tr);
+
+/* Get the next trace from this buffer (false if empty). */
+bool trace_get(union trace *t, struct trace_reader *tr);
+
+#endif
diff --git a/roms/skiboot/external/xscom-utils/.gitignore b/roms/skiboot/external/xscom-utils/.gitignore
new file mode 100644
index 000000000..43e219f9c
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/.gitignore
@@ -0,0 +1,3 @@
+getscom
+getsram
+putscom
diff --git a/roms/skiboot/external/xscom-utils/Makefile b/roms/skiboot/external/xscom-utils/Makefile
new file mode 100644
index 000000000..69ce87a72
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/Makefile
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+CC = $(CROSS_COMPILE)gcc
+
+XSCOM_VERSION ?= $(shell ../../make_version.sh xscom-utils)
+CFLAGS += -O2 -g -Wall -m64
+
+prefix = /usr/local/
+sbindir = $(prefix)/sbin
+datadir = $(prefix)/share
+mandir = $(datadir)/man
+
+%.o: %.c
+ $(Q_CC)$(COMPILE.c) $< -o $@
+
+# Use make V=1 for a verbose build.
+ifndef V
+ Q_CC= @echo ' CC ' $@;
+ Q_LINK= @echo ' LINK ' $@;
+ Q_LN= @echo ' LN ' $@;
+ Q_MKDIR=@echo ' MKDIR ' $@;
+endif
+
+all: getscom putscom getsram
+
+getscom: getscom.o xscom.o version.o
+ $(Q_LINK)$(LINK.o) -o $@ $^
+
+getsram: getsram.o xscom.o sram.o version.o
+ $(Q_LINK)$(LINK.o) -o $@ $^
+
+putscom: putscom.o xscom.o version.o
+ $(Q_LINK)$(LINK.o) -o $@ $^
+
+install: all
+ install -D getscom $(DESTDIR)$(sbindir)/getscom
+ install -D putscom $(DESTDIR)$(sbindir)/putscom
+ install -D getsram $(DESTDIR)$(sbindir)/getsram
+ install -D -m 0644 getscom.1 $(DESTDIR)$(mandir)/man1/getscom.1
+ install -D -m 0644 putscom.1 $(DESTDIR)$(mandir)/man1/putscom.1
+ install -D -m 0644 getsram.1 $(DESTDIR)$(mandir)/man1/getsram.1
+
+.PHONY: clean
+clean:
+ rm -rf *.[od] getscom putscom getsram
+
+.PHONY: distclean
+distclean: clean
+ rm -rf *.c~ *.h~ *.i *.s Makefile~ .version version.c
+
+version.c: ../../make_version.sh .version
+ @(if [ "a$(XSCOM_VERSION)" = "a" ]; then \
+ echo "#error You need to set XSCOM_VERSION environment variable" > $@ ;\
+ else \
+ echo "const char version[] = \"$(XSCOM_VERSION)\";" ;\
+ fi) > $@
+
+.PHONY: VERSION-always
+.version: VERSION-always
+ @echo $(XSCOM_VERSION) > $@.tmp
+ @cmp -s $@ $@.tmp || cp $@.tmp $@
+ @rm -f $@.tmp
diff --git a/roms/skiboot/external/xscom-utils/adu_scoms.py b/roms/skiboot/external/xscom-utils/adu_scoms.py
new file mode 100755
index 000000000..e90634190
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/adu_scoms.py
@@ -0,0 +1,300 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Python library for in-band SCom access
+# (based on xscom-utils from OPAL firmware)
+#
+# Copyright 2018 IBM Corp.
+
+import os, sys, struct, getopt
+
+class XSCom(object):
+ def __init__(self):
+ self.name = "xscom"
+ self.base = "/sys/kernel/debug/powerpc/scom/"
+ self.enabled = False
+ self.setup = False
+ self.chips = []
+ self.dirs = []
+ self.key_val_bin = {}
+ self.file = "/access"
+
+ if os.path.exists(self.base):
+ self.enabled = True
+
+ if not self.scan_chips():
+ raise ValueError
+
+ def scan_chips(self):
+ if not self.enabled:
+ print("Not supported")
+ return False
+
+ for i in os.listdir(self.base):
+ if os.path.isdir(self.base+i):
+ self.dirs.append(i)
+ self.chips.append(int(i,16))
+
+ for i in self.dirs:
+ try:
+ b = open(self.base+i+self.file, "rb+")
+ self.key_val_bin[int(i,16)] = b
+ except:
+ print("Count not open"+self.base+i+self.file)
+ return False
+
+ self.setup = True
+ return True
+
+ def is_supported(self):
+ return self.enabled
+
+ def get_chip_ids(self):
+ return list(self.key_val_bin.keys())
+
+ def mangle_addr(self, addr):
+ tmp = (addr & 0xf000000000000000) >> 4
+ addr = (addr & 0x00ffffffffffffff)
+ addr = addr | tmp
+ return (addr << 3)
+
+ def xscom_read(self, chip_id, addr):
+ if not isinstance(chip_id, int) or not isinstance(addr, int):
+ print("xscom_read: Input paramater type mismatch")
+ return -1
+
+ if chip_id not in self.key_val_bin:
+ print("Invalid Chip id")
+ return -1
+
+ saddr = self.mangle_addr(addr)
+ fd = self.key_val_bin.get(chip_id)
+ fd.seek(saddr, 0)
+ return struct.unpack('Q',fd.read(8))[0]
+
+ def xscom_read_spl(self, chip_id, addr):
+ if not isinstance(chip_id, int) or not isinstance(addr, int):
+ print("xscom_read: Input paramater type mismatch")
+ return -1
+
+ if chip_id not in self.key_val_bin:
+ print("Invalid Chip id")
+ return -1
+
+ saddr = self.mangle_addr(addr)
+ fd = self.key_val_bin.get(chip_id)
+ fd.seek(saddr, 0)
+ val = struct.unpack('Q',fd.read(8))[0]
+ fd.close()
+ try:
+ b = open(self.key_val_path.get(chip_id), "rb+")
+ except:
+ print("Reopen failed")
+ return val
+ self.key_val_bin[chip_id] = b
+ return val
+
+ def xscom_write(self, chip_id, addr, val):
+ if chip_id not in self.key_val_bin:
+ print("Invalid Chip id")
+ return -1
+
+ c = struct.pack('Q',val)
+ saddr = self.mangle_addr(addr)
+ fd = self.key_val_bin.get(chip_id)
+
+ try:
+ fd.seek(saddr, 0)
+ fd.write(c)
+ # write again just to be sure
+ fd.seek(saddr, 0)
+ fd.write(c)
+ except:
+ print("Write() error")
+ return -1
+
+ def xscom_read_ex(self, ex_target_id, addr):
+ if not isinstance(ex_target_id, int) or not isinstance(addr, int):
+ print("xscom_read_ex: Input paramater type mismatch")
+ return -1
+
+ chip_id = ex_target_id >> 4
+ addr |= (ex_target_id & 0xf) << 24;
+ return self.xscom_read(chip_id, addr, val);
+
+ def xscom_write_ex(self, ex_target_id, addr, val):
+ chip_id = ex_target_id >> 4
+ addr |= (ex_target_id & 0xf) << 24;
+ return self.xscom_write(chip_id, addr, val)
+
+class GetSCom(object):
+ def __init__(self):
+ self.name = "getscom"
+ self.backend = XSCom()
+ self.listchip = False
+ self.chip_id = 0
+ self.chips = False
+ self.addr = 0
+ self.flg_addr = False
+
+ if not self.backend.is_supported():
+ print("In-Band SCom not supported Exiting....")
+ raise ValueError
+
+ def set_chip(self, chip_id):
+ self.chip_id = chip_id
+ self.chips = True
+
+ def set_addr(self, scom_addr):
+ self.addr = scom_addr
+ self.flg_addr = True
+
+ def print_usage(self):
+ print("usage: getscom [-c|--chip chip-id] addr")
+ print(" getscom -l|--list-chips")
+ print(" getscom -h|--help")
+ sys.exit(0)
+
+
+ def chip_info(self, chip_id):
+ val = self.backend.xscom_read(chip_id, 0xf000f)
+ if val < 0:
+ print("Error in scom read")
+ raise ValueError
+
+ c_id = val >> 44
+ id = c_id & 0xff
+ if id == 0xef:
+ name = "P8E (Murano) processor"
+ elif id == 0xea:
+ name = "P8 (Venice) processor"
+ elif id == 0xd3:
+ name = "P8NVL (Naples) processor"
+ elif id == 0xd1:
+ name = "P9 (Nimbus) processor"
+ elif id == 0xd4:
+ name = "P9 (Cumulus) processor"
+ elif id == 0xd9:
+ name = "P9P (Axone) processor"
+ elif id == 0xda:
+ name = "P10 processor"
+ elif id == 0xe9:
+ name = "Centaur memory buffer"
+ else:
+ name = "Unknown ID 0x%x"%id
+
+ print(("%08x | DD%s.%s | %s"%(chip_id, ((c_id >> 16) & 0xf), ((c_id >> 8) & 0xf), name)))
+
+ def parse_args(self):
+ try:
+ optlist, sys.argv = getopt.getopt(sys.argv[1:], "lhc:", ["chip", "list-chips", "help"])
+ except getopt.GetoptError as err:
+ print(str(err))
+ self.print_usage()
+ sys.exit(0)
+
+ if len(optlist) == 0:
+ self.print_usage()
+ sys.exit(0)
+
+ for opt, arg in optlist:
+ if opt in [ "-h", "--help"]:
+ self.print_usage()
+ sys.exit(0)
+
+ elif opt in [ "-l", "--list-chips"]:
+ self.listchip = True
+
+ elif opt in ["-c", "--chip"]:
+ self.chip_id = int(arg, 16)
+ self.chips = True
+
+ if sys.argv:
+ self.addr = int(sys.argv.pop(), 16)
+ self.flg_addr = True
+
+ if self.listchip:
+ print("Chip ID | Rev | Chip type")
+ print("---------|-------|-----------")
+ for i in self.backend.get_chip_ids():
+ self.chip_info(i)
+
+ sys.exit(0)
+
+ def run_command(self):
+ if self.chips and self.flg_addr:
+ print(hex(self.backend.xscom_read(self.chip_id, self.addr)))
+
+ def list_chips(self):
+ print("Chip ID | Rev | Chip type")
+ print("---------|-------|-----------")
+ for i in self.backend.get_chip_ids():
+ self.chip_info(i)
+
+ raise ValueError
+
+ def execute(self, chip_id, addr):
+ return self.backend.xscom_read(chip_id, addr)
+
+ def execute_spl(self, chip_id, addr):
+ return self.backend.xscom_read_spl(chip_id, addr)
+
+class PutSCom(object):
+ def __init__(self):
+ self.name = "putscom"
+ self.backend = XSCom()
+ self.chip_id = 0
+ self.chips = False
+ self.addr = 0
+ self.value = 0
+
+ if not self.backend.is_supported():
+ print("In-Band SCom not supported Exiting....")
+ raise ValueError
+
+ def set_addr(self, addr):
+ self.addr = addr
+
+ def set_value(self, value):
+ self.value = value
+
+ def print_usage(self):
+ print("usage: putscom [-c|--chip chip-id] addr value")
+ print(" putscom -h|--help")
+ sys.exit(0)
+
+ def parse_args(self):
+ try:
+ optlist, sys.argv = getopt.getopt(sys.argv[1:], "hc:", ["chip", "help"])
+ except getopt.GetoptError as err:
+ print(str(err))
+ self.print_usage()
+ sys.exit(0)
+
+ if len(optlist) == 0:
+ self.print_usage()
+ sys.exit(0)
+
+ for opt, arg in optlist:
+ if opt in [ "-h", "--help"]:
+ self.print_usage()
+ sys.exit(0)
+
+ elif opt in ["-c", "--chip"]:
+ self.chip_id = int(arg, 16)
+ self.chips = True
+
+ if sys.argv:
+ self.value = int(sys.argv.pop(), 16)
+ self.addr = int(sys.argv.pop(), 16)
+
+ if self.chips:
+ self.backend.xscom_write(self.chip_id, self.addr, self.value)
+
+ def run_command(self):
+ if self.chips:
+ self.backend.xscom_write(self.chip_id, self.addr, self.value)
+
+ def execute(self, chip_id, addr, value):
+ self.backend.xscom_write(chip_id, addr, value)
+
diff --git a/roms/skiboot/external/xscom-utils/getscom.1 b/roms/skiboot/external/xscom-utils/getscom.1
new file mode 100644
index 000000000..295b99d56
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/getscom.1
@@ -0,0 +1,23 @@
+.TH XSCOM "1" "September 2016"
+.SH NAME
+getscom \- part of xscom utils
+.SH SYNOPIS
+.TP
+\fBgetscom\fP [\-c | \-\-chip \fIchip\-id\fP] \fIaddr\fP
+.TP
+\fBgetscom\fP [\-l | \-\-list\-chips]
+.TP
+\fBgetscom\fP [\-v | \-\-version]
+.SH DESCRIPTION
+\fBgetscom\fP utility provides an interface to query the
+registers of the different chipsets of an OpenPower system.
+.SS Options
+.TP
+\fB\-c|\-\-chip-id\fP \fIchip-id\fP
+Specify chipset where to read register at \fIaddr\fP
+.TP
+\fB\-l|\-\-list\-chips\fP
+List the chipsets found on the system
+.TP
+\fB\-v|\-\-version\fP
+Display version of the tool
diff --git a/roms/skiboot/external/xscom-utils/getscom.c b/roms/skiboot/external/xscom-utils/getscom.c
new file mode 100644
index 000000000..67596e618
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/getscom.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * getscom
+ *
+ * Copyright 2014-2017 IBM Corp.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "xscom.h"
+
+static void print_usage(int code)
+{
+ printf("usage: getscom [-c|--chip chip-id] [-b|--list-bits] addr\n");
+ printf(" getscom -l|--list-chips\n");
+ printf(" getscom -v|--version\n");
+ printf("\n");
+ printf(" NB: --list-bits shows which PPC bits are set\n");
+ exit(code);
+}
+
+static void print_chip_info(uint32_t chip_id)
+{
+ uint64_t f000f, cfam_id;
+ const char *name;
+ char uname_buf[64];
+ int rc;
+
+ rc = xscom_read(chip_id, 0xf000f, &f000f);
+ if (rc)
+ return;
+
+ cfam_id = f000f >> 44;
+
+ switch(cfam_id & 0xff) {
+ case 0xef:
+ name = "P8E (Murano) processor";
+ break;
+ case 0xea:
+ name = "P8 (Venice) processor";
+ break;
+ case 0xd3:
+ name = "P8NVL (Naples) processor";
+ break;
+ case 0xd1:
+ name = "P9 (Nimbus) processor";
+ break;
+ case 0xd4:
+ name = "P9 (Cumulus) processor";
+ break;
+ case 0xd9:
+ name = "P9P (Axone) processor";
+ break;
+ case 0xda:
+ name = "P10 processor";
+ break;
+ case 0xe9:
+ name = "Centaur memory buffer";
+ break;
+ default:
+ snprintf(uname_buf, sizeof(uname_buf), "Unknown ID 0x%02lx",
+ cfam_id & 0xff);
+ name = uname_buf;
+ }
+
+ printf("%08x | DD%lx.%lx | %s\n",
+ chip_id, (cfam_id >> 16) & 0xf, (cfam_id >> 8) & 0xf, name);
+}
+
+extern const char version[];
+
+int main(int argc, char *argv[])
+{
+ uint64_t val, addr = -1ull;
+ uint32_t def_chip, chip_id = 0xffffffff;
+ bool list_chips = false;
+ bool no_work = false;
+ bool list_bits = false;
+ int rc;
+
+ while(1) {
+ static struct option long_opts[] = {
+ {"chip", required_argument, NULL, 'c'},
+ {"list-chips", no_argument, NULL, 'l'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {"list-bits", no_argument, NULL, 'b'},
+ };
+ int c, oidx = 0;
+
+ c = getopt_long(argc, argv, "-c:bhlv", long_opts, &oidx);
+ if (c == EOF)
+ break;
+ switch(c) {
+ case 1:
+ addr = strtoull(optarg, NULL, 16);
+ break;
+ case 'c':
+ chip_id = strtoul(optarg, NULL, 16);
+ break;
+ case 'h':
+ print_usage(0);
+ break;
+ case 'l':
+ list_chips = true;
+ break;
+ case 'b':
+ list_bits = true;
+ break;
+ case 'v':
+ printf("xscom utils version %s\n", version);
+ exit(0);
+ default:
+ exit(1);
+ }
+ }
+
+ if (addr == -1ull)
+ no_work = true;
+ if (no_work && !list_chips) {
+ fprintf(stderr, "Invalid or missing address\n");
+ print_usage(1);
+ }
+
+ def_chip = xscom_init();
+ if (def_chip == 0xffffffff) {
+ fprintf(stderr, "No valid XSCOM chip found\n");
+ exit(1);
+ }
+ if (list_chips) {
+ printf("Chip ID | Rev | Chip type\n");
+ printf("---------|-------|--------\n");
+ xscom_for_each_chip(print_chip_info);
+ }
+ if (no_work)
+ return 0;
+ if (chip_id == 0xffffffff)
+ chip_id = def_chip;
+
+ rc = xscom_read(chip_id, addr, &val);
+ if (rc) {
+ fprintf(stderr,"Error %d reading XSCOM\n", rc);
+ exit(1);
+ }
+
+ printf("%016" PRIx64, val);
+
+ if (list_bits) {
+ int i;
+
+ printf(" - set: ");
+
+ for (i = 0; i < 64; i++)
+ if (val & PPC_BIT(i))
+ printf("%d ", i);
+ }
+
+ putchar('\n');
+
+ return 0;
+}
+
diff --git a/roms/skiboot/external/xscom-utils/getsram.1 b/roms/skiboot/external/xscom-utils/getsram.1
new file mode 100644
index 000000000..19b297414
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/getsram.1
@@ -0,0 +1,23 @@
+.TH XSCOM "1" "January 2018"
+.SH NAME
+getsram \- part of xscom utils
+.SH SYNOPIS
+.TP
+\fBgetsram\fP [\-c | \-\-chip \fIchip\-id\fP] \fIaddr\fP
+.TP
+\fBgetsram\fP [\-n | \-\-occ\-channel \fIchan\fP]
+.TP
+\fBgetsram\fP [\-v | \-\-version]
+.SH DESCRIPTION
+\fBgetsram\fP utility provides an interface to query the
+OCC RAM of an OpenPower system.
+.SS Options
+.TP
+\fB\-c|\-\-chip-id\fP \fIchip-id\fP
+Specify chipset where to read register at \fIaddr\fP
+.TP
+\fB\-n|\-\-occ\-channel\fP
+Specify the channel : 0, 1, 2 or 3
+.TP
+\fB\-v|\-\-version\fP
+Display version of the tool
diff --git a/roms/skiboot/external/xscom-utils/getsram.c b/roms/skiboot/external/xscom-utils/getsram.c
new file mode 100644
index 000000000..7d9c4bfac
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/getsram.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Read SRAM
+ *
+ * Copyright 2014-2018 IBM Corp.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "xscom.h"
+#include "sram.h"
+
+static void print_usage(int code)
+{
+ printf("usage: getsram [opts] addr\n");
+ printf(" -c|--chip <chip-id>\n");
+ printf(" -l|--length <size to read>\n");
+ printf(" -n|--occ-channel <chan>\n");
+ printf(" -f|--file <filename>\n");
+ printf(" -v|--version\n");
+ exit(code);
+}
+
+extern const char version[];
+
+int main(int argc, char *argv[])
+{
+ uint64_t val, addr = -1ull, length = 8;
+ uint32_t def_chip, chip_id = 0xffffffff;
+ int rc;
+ int occ_channel = 0;
+ char *filename = NULL;
+ FILE *f = stdout;
+
+ while(1) {
+ static struct option long_opts[] = {
+ {"chip", required_argument, NULL, 'c'},
+ {"occ-channel", required_argument, NULL, 'n'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {"length", required_argument, NULL, 'l'},
+ {"file", required_argument, NULL, 'f'},
+ };
+ int c, oidx = 0;
+
+ c = getopt_long(argc, argv, "-c:n:hl:vf:", long_opts, &oidx);
+ if (c == EOF)
+ break;
+ switch(c) {
+ case 1:
+ addr = strtoull(optarg, NULL, 16);
+ break;
+ case 'c':
+ chip_id = strtoul(optarg, NULL, 16);
+ break;
+ case 'n':
+ occ_channel = strtoul(optarg, NULL, 0);
+ if (occ_channel < 0 || occ_channel > 3) {
+ fprintf(stderr, "occ-channel out of range 0 <= c <= 3\n");
+ exit(1);
+ }
+ break;
+ case 'h':
+ print_usage(0);
+ break;
+ case 'v':
+ printf("xscom utils version %s\n", version);
+ exit(0);
+ case 'f':
+ filename = optarg;
+ break;
+ case 'l':
+ length = strtoul(optarg, NULL, 0);
+ length = (length + 7) & ~0x7; /* round up to an eight byte interval */
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ if (addr == -1ull) {
+ fprintf(stderr, "Invalid or missing address\n");
+ print_usage(1);
+ }
+
+ def_chip = xscom_init();
+ if (def_chip == 0xffffffff) {
+ fprintf(stderr, "No valid XSCOM chip found\n");
+ exit(1);
+ }
+ if (chip_id == 0xffffffff)
+ chip_id = def_chip;
+
+ if (filename) {
+ f = fopen(filename, "wb");
+ if (!f) {
+ fprintf(stderr, "unable to open %s for writing\n", filename);
+ exit(1);
+ }
+ }
+
+ rc = 0;
+ while (length) {
+ rc = sram_read(chip_id, occ_channel, addr, &val);
+ if (rc)
+ break;
+
+ if (f) {
+ int i;
+
+ /* make sure we write it out big endian */
+ for (i = 1; i <= 8; i++)
+ fputc((val >> (64 - i * 8)) & 0xff, f);
+ } else {
+ printf("OCC%d: %" PRIx64 "\n", occ_channel, val);
+ }
+
+ length -= 8;
+ addr += 8;
+ }
+
+ if (rc) {
+ fprintf(stderr,"Error %d reading XSCOM\n", rc);
+ exit(1);
+ }
+ return 0;
+}
diff --git a/roms/skiboot/external/xscom-utils/putscom.1 b/roms/skiboot/external/xscom-utils/putscom.1
new file mode 100644
index 000000000..9c329fc27
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/putscom.1
@@ -0,0 +1,18 @@
+.TH XSCOM "1" "September 2016"
+.SH NAME
+putscom \- part of xscom utils
+.SH SYNOPIS
+.TP
+\fBputscom\fP [\-c | \-\-chip \fIchip\-id\fP] \fIaddr\fP \fIvalue\fP
+.TP
+\fBputscom\fP [\-v | \-\-version]
+.SH DESCRIPTION
+\fBputscom\fP utility provides an interface to modify the
+registers of the different chipsets of an OpenPower system.
+.SS Options
+.TP
+\fB\-c|\-\-chip-id\fP \fIchip\-id\fP
+Specify chipset where to modify register at \fIaddr\fP with \fIvalue\fP
+.TP
+\fB\-v|\-\-version\fP
+Display version of the tool
diff --git a/roms/skiboot/external/xscom-utils/putscom.c b/roms/skiboot/external/xscom-utils/putscom.c
new file mode 100644
index 000000000..b942eeb58
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/putscom.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * getscom
+ *
+ * Copyright 2014-2017 IBM Corp.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "xscom.h"
+
+static void print_usage(int code)
+{
+ printf("usage: putscom [-c|--chip chip-id] [-b|--list-bits] addr value\n");
+ printf(" putscom -v|--version\n");
+ printf("\n");
+ printf(" NB: --list-bits shows which PPC bits are set\n");
+ exit(code);
+ exit(code);
+}
+
+extern const char version[];
+
+int main(int argc, char *argv[])
+{
+ uint64_t val = -1ull, addr = -1ull;
+ uint32_t def_chip, chip_id = 0xffffffff;
+ bool got_addr = false, got_val = false;
+ bool list_bits = false;
+ int rc;
+
+ while(1) {
+ static struct option long_opts[] = {
+ {"chip", required_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ };
+ int c, oidx = 0;
+
+ c = getopt_long(argc, argv, "-c:bhv", long_opts, &oidx);
+ if (c == EOF)
+ break;
+ switch(c) {
+ case 1:
+ if (!got_addr) {
+ addr = strtoull(optarg, NULL, 16);
+ got_addr = true;
+ break;
+ }
+ val = strtoull(optarg, NULL, 16);
+ got_val = true;
+ break;
+ case 'c':
+ chip_id = strtoul(optarg, NULL, 16);
+ break;
+ case 'b':
+ list_bits = true;
+ break;
+ case 'v':
+ printf("xscom utils version %s\n", version);
+ exit(0);
+ case 'h':
+ print_usage(0);
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ if (!got_addr || !got_val) {
+ fprintf(stderr, "Invalid or missing address/value\n");
+ print_usage(1);
+ }
+
+ def_chip = xscom_init();
+ if (def_chip == 0xffffffff) {
+ fprintf(stderr, "No valid XSCOM chip found\n");
+ exit(1);
+ }
+ if (chip_id == 0xffffffff)
+ chip_id = def_chip;
+
+ rc = xscom_write(chip_id, addr, val);
+ if (rc) {
+ fprintf(stderr,"Error %d writing XSCOM\n", rc);
+ exit(1);
+ }
+ if (xscom_readable(addr)) {
+ rc = xscom_read(chip_id, addr, &val);
+ if (rc) {
+ fprintf(stderr,"Error %d reading XSCOM\n", rc);
+ exit(1);
+ }
+ }
+
+ printf("%016" PRIx64, val);
+ if (list_bits) {
+ int i;
+
+ printf(" - set: ");
+
+ for (i = 0; i < 64; i++)
+ if (val & PPC_BIT(i))
+ printf("%d ", i);
+ }
+
+ putchar('\n');
+ return 0;
+}
+
diff --git a/roms/skiboot/external/xscom-utils/sram.c b/roms/skiboot/external/xscom-utils/sram.c
new file mode 100644
index 000000000..efe08d8e7
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/sram.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2014-2019 IBM Corp. */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "xscom.h"
+
+#define DBG(fmt...) do { if (verbose) printf(fmt); } while(0)
+#define ERR(fmt...) do { fprintf(stderr, fmt); } while(0)
+
+#define OCB_PIB_BASE_P8 0x0006B000
+#define OCB_PIB_BASE_P9 0x0006D000
+
+#define OCBCSR0 0x11
+#define OCBCSR0_AND 0x12
+#define OCBCSR0_OR 0x13
+#define OCB_STREAM_MODE PPC_BIT(4)
+#define OCB_STREAM_TYPE PPC_BIT(5)
+#define OCBAR0 0x10
+#define OCBDR0 0x15
+
+#define PVR_TYPE_P8E 0x004b /* Murano */
+#define PVR_TYPE_P8 0x004d /* Venice */
+#define PVR_TYPE_P8NVL 0x004c /* Naples */
+#define PVR_TYPE_P9 0x004e
+#define PVR_TYPE_P9P 0x004f /* Axone */
+#define PVR_TYPE_P10 0x0080
+
+#ifdef __powerpc__
+static uint64_t get_xscom_base(void)
+{
+ unsigned int pvr;
+
+ asm volatile("mfpvr %0" : "=r" (pvr));
+
+ switch (pvr >> 16) {
+ case PVR_TYPE_P9:
+ case PVR_TYPE_P9P:
+ case PVR_TYPE_P10: /* P10 OCB_PIB OCC Control Register is same for P9 and P10 */
+ return OCB_PIB_BASE_P9;
+
+ case PVR_TYPE_P8E:
+ case PVR_TYPE_P8:
+ case PVR_TYPE_P8NVL:
+ return OCB_PIB_BASE_P8;
+ }
+
+ ERR("Unknown processor, exiting\n");
+ exit(1);
+ return 0;
+}
+#else
+/* Just so it compiles on x86 */
+static uint64_t get_xscom_base(void) { return 0; }
+#endif
+
+int sram_read(uint32_t chip_id, int chan, uint32_t addr, uint64_t *val)
+{
+ uint64_t sdat, base = get_xscom_base();
+ uint32_t coff = chan * 0x20;
+ int rc;
+
+ /* Read for debug purposes */
+ rc = xscom_read(chip_id, base + OCBCSR0 + coff, &sdat);
+ if (rc) {
+ ERR("xscom OCBCSR0 read error %d\n", rc);
+ return -1;
+ }
+
+ /* Create an AND mask to clear bit 4 and 5 and poke the AND register */
+ sdat = ~(OCB_STREAM_MODE | OCB_STREAM_TYPE);
+ rc = xscom_write(chip_id, base + OCBCSR0_AND + coff, sdat);
+ if (rc) {
+ ERR("xscom OCBCSR0_AND write error %d\n", rc);
+ return -1;
+ }
+
+ sdat = ((uint64_t)addr) << 32;
+ rc = xscom_write(chip_id, base + OCBAR0 + coff, sdat);
+ if (rc) {
+ ERR("xscom OCBAR0 write error %d\n", rc);
+ return -1;
+ }
+
+ rc = xscom_read(chip_id, base + OCBDR0 + coff, val);
+ if (rc) {
+ ERR("xscom OCBDR0 read error %d\n", rc);
+ return -1;
+ }
+ return 0;
+}
+
+int sram_write(uint32_t chip_id, int chan, uint32_t addr, uint64_t val)
+{
+ uint64_t sdat, base = get_xscom_base();
+ uint32_t coff = chan * 0x20;
+ int rc;
+
+#if 0
+ if (dummy) {
+ printf("[dummy] write chip %d OCC sram 0x%08x = %016lx\n",
+ chip_id, addr, val);
+ return 0;
+ }
+#endif
+
+ /* Read for debug purposes */
+ rc = xscom_read(chip_id, base + OCBCSR0 + coff, &sdat);
+ if (rc) {
+ ERR("xscom OCBCSR0 read error %d\n", rc);
+ return -1;
+ }
+
+ /* Create an AND mask to clear bit 4 and 5 and poke the AND register */
+ sdat = ~(OCB_STREAM_MODE | OCB_STREAM_TYPE);
+ rc = xscom_write(chip_id, base + OCBCSR0_AND + coff, sdat);
+ if (rc) {
+ ERR("xscom OCBCSR0_AND write error %d\n", rc);
+ return -1;
+ }
+
+ sdat = ((uint64_t)addr) << 32;
+ rc = xscom_write(chip_id, base + OCBAR0 + coff, sdat);
+ if (rc) {
+ ERR("xscom OCBAR0 write error %d\n", rc);
+ return -1;
+ }
+
+ rc = xscom_write(chip_id, base + OCBDR0 + coff, val);
+ if (rc) {
+ ERR("xscom OCBDR0 write error %d\n", rc);
+ return -1;
+ }
+ return 0;
+}
diff --git a/roms/skiboot/external/xscom-utils/sram.h b/roms/skiboot/external/xscom-utils/sram.h
new file mode 100644
index 000000000..75d9155c4
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/sram.h
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2014-2016 IBM Corp. */
+
+#ifndef __SRAM_H
+#define __SRAM_H
+
+#include <stdint.h>
+
+extern int sram_read(uint32_t chip_id, int chan, uint64_t addr, uint64_t *val);
+extern int sram_write(uint32_t chip_id, int chan, uint64_t addr, uint64_t val);
+
+extern void sram_for_each_chip(void (*cb)(uint32_t chip_id));
+
+#endif /* __SRAM_H */
diff --git a/roms/skiboot/external/xscom-utils/xscom.c b/roms/skiboot/external/xscom-utils/xscom.c
new file mode 100644
index 000000000..031f9e193
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/xscom.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Do XSCOMs through linux debugfs interface
+ *
+ * Copyright 2014-2017 IBM Corp.
+ */
+
+#define _LARGEFILE64_SOURCE
+#include <sys/mman.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "xscom.h"
+
+#define XSCOM_BASE_PATH "/sys/kernel/debug/powerpc/scom"
+
+struct xscom_chip {
+ struct xscom_chip *next;
+ uint32_t chip_id;
+ int fd;
+};
+static struct xscom_chip *xscom_chips;
+
+void xscom_for_each_chip(void (*cb)(uint32_t chip_id))
+{
+ struct xscom_chip *c;
+
+ for (c = xscom_chips; c; c = c->next)
+ cb(c->chip_id);
+}
+
+static uint32_t xscom_add_chip(const char *base_path, const char *dname)
+{
+ char nbuf[strlen(base_path) + strlen(dname) + 16];
+ struct xscom_chip *chip;
+ int fd;
+
+ snprintf(nbuf, sizeof(nbuf), "%s/%s/access", base_path, dname);
+ fd = open(nbuf, O_RDWR);
+ if (fd < 0) {
+ perror("Failed to open SCOM access file");
+ exit(1);
+ }
+
+ chip = malloc(sizeof(*chip));
+ assert(chip);
+ memset(chip, 0, sizeof(*chip));
+ chip->fd = fd;
+ chip->chip_id = strtoul(dname, NULL, 16);
+ chip->next = xscom_chips;
+ xscom_chips = chip;
+
+ return chip->chip_id;
+}
+
+static bool xscom_check_dirname(const char *n)
+{
+ while(*n) {
+ char c = toupper(*(n++));
+
+ if ((c < 'A' || c > 'Z') &&
+ (c < '0' || c > '9'))
+ return false;
+ }
+ return true;
+}
+
+static uint32_t xscom_scan_chips(const char *base_path)
+{
+ int i, nfiles;
+ struct dirent **filelist;
+ uint32_t lower = 0xffffffff;
+
+ nfiles = scandir(base_path, &filelist, NULL, alphasort);
+ if (nfiles < 0) {
+ perror("Error accessing sysfs scom directory");
+ exit(1);
+ }
+ if (nfiles == 0) {
+ fprintf(stderr, "No SCOM dir found in sysfs\n");
+ exit(1);
+ }
+
+ for (i = 0; i < nfiles; i++) {
+ struct dirent *d = filelist[i];
+ uint32_t id;
+
+ if (d->d_type != DT_DIR)
+ continue;
+ if (!xscom_check_dirname(d->d_name))
+ continue;
+ id = xscom_add_chip(base_path, d->d_name);
+ if (id < lower)
+ lower = id;
+ free(d);
+ }
+
+ free(filelist);
+ return lower;
+}
+
+static struct xscom_chip *xscom_find_chip(uint32_t chip_id)
+{
+ struct xscom_chip *c;
+
+ for (c = xscom_chips; c; c = c->next)
+ if (c->chip_id == chip_id)
+ return c;
+ return NULL;
+}
+
+static uint64_t xscom_mangle_addr(uint64_t addr)
+{
+ uint64_t tmp;
+
+ /*
+ * Shift the top 4 bits (indirect mode) down by 4 bits so we
+ * don't lose going through the debugfs interfaces.
+ */
+ tmp = (addr & 0xf000000000000000) >> 4;
+ addr &= 0x00ffffffffffffff;
+ addr |= tmp;
+
+ /* Shift up by 3 for debugfs */
+ return addr << 3;
+}
+
+int xscom_read(uint32_t chip_id, uint64_t addr, uint64_t *val)
+{
+ struct xscom_chip *c = xscom_find_chip(chip_id);
+ int rc;
+
+ if (!c)
+ return -ENODEV;
+ addr = xscom_mangle_addr(addr);
+ lseek64(c->fd, addr, SEEK_SET);
+ rc = read(c->fd, val, 8);
+ if (rc < 0)
+ return -errno;
+ if (rc != 8)
+ return -EIO;
+ return 0;
+}
+
+int xscom_write(uint32_t chip_id, uint64_t addr, uint64_t val)
+{
+ struct xscom_chip *c = xscom_find_chip(chip_id);
+ int rc;
+
+ if (!c)
+ return -ENODEV;
+ addr = xscom_mangle_addr(addr);
+ lseek64(c->fd, addr, SEEK_SET);
+ rc = write(c->fd, &val, 8);
+ if (rc < 0)
+ return -errno;
+ if (rc != 8)
+ return -EIO;
+ return 0;
+}
+
+int xscom_read_ex(uint32_t ex_target_id, uint64_t addr, uint64_t *val)
+{
+ uint32_t chip_id = ex_target_id >> 4;;
+
+ addr |= (ex_target_id & 0xf) << 24;
+
+ /* XXX TODO: Special wakeup ? */
+
+ return xscom_read(chip_id, addr, val);
+}
+
+int xscom_write_ex(uint32_t ex_target_id, uint64_t addr, uint64_t val)
+{
+ uint32_t chip_id = ex_target_id >> 4;;
+
+ addr |= (ex_target_id & 0xf) << 24;
+
+ /* XXX TODO: Special wakeup ? */
+
+ return xscom_write(chip_id, addr, val);
+}
+
+bool xscom_readable(uint64_t addr)
+{
+ /* Top nibble 9 indicates form 1 indirect, which is write only */
+ if (((addr >> 60) & 0xf) == 9)
+ return false;
+ return true;
+}
+
+uint32_t xscom_init(void)
+{
+ return xscom_scan_chips(XSCOM_BASE_PATH);
+}
diff --git a/roms/skiboot/external/xscom-utils/xscom.h b/roms/skiboot/external/xscom-utils/xscom.h
new file mode 100644
index 000000000..05b8f543b
--- /dev/null
+++ b/roms/skiboot/external/xscom-utils/xscom.h
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2014-2017 IBM Corp.
+ */
+
+#ifndef __XSCOM_H
+#define __XSCOM_H
+
+#include <stdint.h>
+
+extern int xscom_read(uint32_t chip_id, uint64_t addr, uint64_t *val);
+extern int xscom_write(uint32_t chip_id, uint64_t addr, uint64_t val);
+
+extern int xscom_read_ex(uint32_t ex_target_id, uint64_t addr, uint64_t *val);
+extern int xscom_write_ex(uint32_t ex_target_id, uint64_t addr, uint64_t val);
+
+extern void xscom_for_each_chip(void (*cb)(uint32_t chip_id));
+
+extern bool xscom_readable(uint64_t addr);
+
+extern uint32_t xscom_init(void);
+
+#ifndef PPC_BIT
+#define PPC_BIT(bit) (0x8000000000000000UL >> (bit))
+#endif
+
+#endif /* __XSCOM_H */