diff options
Diffstat (limited to 'roms/skiboot/external')
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 Binary files differnew file mode 100644 index 000000000..e00fa5c0e --- /dev/null +++ b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.out 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 Binary files differnew file mode 100644 index 000000000..617c4ef44 --- /dev/null +++ b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.out 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 Binary files differnew file mode 100644 index 000000000..43b9583b3 --- /dev/null +++ b/roms/skiboot/external/ffspart/test/files/16-create-blank.out 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 Binary files differnew file mode 100644 index 000000000..e00fa5c0e --- /dev/null +++ b/roms/skiboot/external/ffspart/test/files/17-toc.out 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 Binary files differnew file mode 100644 index 000000000..c6cc6b8c2 --- /dev/null +++ b/roms/skiboot/external/gard/test/files/blank.bin diff --git a/roms/skiboot/external/gard/test/files/data-p9.bin b/roms/skiboot/external/gard/test/files/data-p9.bin Binary files differnew file mode 100644 index 000000000..f2141657a --- /dev/null +++ b/roms/skiboot/external/gard/test/files/data-p9.bin diff --git a/roms/skiboot/external/gard/test/files/data1.bin b/roms/skiboot/external/gard/test/files/data1.bin Binary files differnew file mode 100644 index 000000000..aa5bf1437 --- /dev/null +++ b/roms/skiboot/external/gard/test/files/data1.bin 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 Binary files differnew file mode 100644 index 000000000..8d4c7e704 --- /dev/null +++ b/roms/skiboot/external/mambo/cvc.bin 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(®_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(¤t->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 */ |