meta-google: ipmi-fru: Direct read support
We can't dynamically bind to the at24 driver if the eeprom is not in
the kernel's device tree. Since this support was intended to be used
for dynamic FRUs, it is broken when the kernel has the entries removed.
Change-Id: I99c774191c22d67e518fe9435b1446b80efb5600
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/meta-google/recipes-google/ipmi/ipmi-fru-sh/lib.sh b/meta-google/recipes-google/ipmi/ipmi-fru-sh/lib.sh
index 940be7f..d02eeab 100644
--- a/meta-google/recipes-google/ipmi/ipmi-fru-sh/lib.sh
+++ b/meta-google/recipes-google/ipmi/ipmi-fru-sh/lib.sh
@@ -23,6 +23,20 @@
IPMI_FRU_AREA_HEADER_SIZE_IDX=1
IPMI_FRU_CHECKSUM_IDX=-1
+offset_1bw() {
+ local addr="$1"
+ local off="$2"
+ local extra="${3-0}"
+ echo w$((1+extra))@$addr $(( off & 0xff ))
+}
+
+offset_2bw() {
+ local addr="$1"
+ local off="$2"
+ local extra="${3-0}"
+ echo w$((2+extra))@$addr $(( (off >> 8) & 0xff )) $(( off & 0xff ))
+}
+
of_name_to_eeproms() {
local names
if ! names="$(grep -xl "$1" /sys/bus/i2c/devices/*/of_node/name)"; then
@@ -42,10 +56,14 @@
echo "$eeproms"
}
+# Each element is a `filename`
declare -A IPMI_FRU_EEPROM_FILE=()
+# Each element is a `bus` + `addr` + `offset_bytes_func`
+declare -A IPMI_FRU_EEPROM_BUSADDR=()
-ipmi_fru_device_to_file() {
+ipmi_fru_device_alloc() {
local fdn="$1"
+ local idx="$2"
local json
json="$(busctl -j call xyz.openbmc_project.FruDevice \
@@ -54,20 +72,21 @@
local jqq='.data[0] | (.BUS.data | tostring) + " " + (.ADDRESS.data | tostring)'
local busaddr
- busaddr="$(echo "$json" | jq -r "$jqq")" || return
+ busaddr=($(echo "$json" | jq -r "$jqq")) || return
# FRU 0 is hardcoded and FruDevice does not report the correct bus for it
# Hardcode a workaround for this specifically known bus
- if [ "$busaddr" = '0 0' ]; then
- echo "/etc/fru/baseboard.fru.bin"
+ if (( busaddr[0] == 0 && busaddr[1] == 0 )); then
+ IPMI_FRU_EEPROM_FILE["$idx"]=/etc/fru/baseboard.fru.bin
else
- local dev="$(printf '%d-%04x' $busaddr)"
- local efile="/sys/bus/i2c/devices/$dev/eeprom"
- # The at24 eeprom driver is not guaranteed to be bound
- if [ ! -e "$efile" ]; then
- echo "$dev" >/sys/bus/i2c/drivers/at24/bind || return
+ local offset_bw=offset_2bw
+ local rsp
+ rsp=$(i2ctransfer -f -y ${busaddr[0]} $(offset_1bw ${busaddr[1]} 0) r1) || return
+ # FRUs never start with 0xff bytes, so we can figure out addressing mode
+ if (( rsp != 0xff )); then
+ offset_bw=offset_1bw
fi
- echo "$efile"
+ IPMI_FRU_EEPROM_BUSADDR["$idx"]="${busaddr[*]} $offset_bw"
fi
}
@@ -77,7 +96,8 @@
# Pick the first free index to return as the allocated entry
for (( ret = 0; ret < "${#IPMI_FRU_EEPROM_FILE[@]}"; ++ret )); do
- [ -n "${IPMI_FRU_EEPROM_FILE[@]+1}" ] || break
+ [ -n "${IPMI_FRU_EEPROM_FILE[@]+1}" ] || \
+ [ -n "${IPMI_FRU_EEPROM_BUSADDR[@]+1}" ]|| break
done
if [[ "$name" =~ ^of-name:(.*)$ || "$name" =~ ^([^:]*)$ ]]; then
@@ -91,13 +111,12 @@
local file
while (( SECONDS - start < 60 )); do
local rc=0
- file="$(ipmi_fru_device_to_file "$fdn")" || rc=$?
+ ipmi_fru_device_alloc "$fdn" "$ret" || rc=$?
(( rc == 0 )) && break
# Immediately return any errors, 80 is special to signify retry
(( rc != 80 )) && return $rc
sleep 1
done
- IPMI_FRU_EEPROM_FILE["$ret"]="$file"
else
echo "Invalid IPMI FRU eeprom specification: $name" >&2
return 1
@@ -106,6 +125,7 @@
ipmi_fru_free() {
unset 'IPMI_FRU_EEPROM_FILE[$1]'
+ unset 'IPMI_FRU_EEPROM_BUSADDR[$1]'
}
checksum() {
@@ -127,24 +147,36 @@
}
read_bytes() {
+ local busaddr=(${IPMI_FRU_EEPROM_BUSADDR["$1"]-})
local file="${IPMI_FRU_EEPROM_FILE["$1"]-$1}"
local offset="$2"
local size="$3"
- echo "Reading $file at $offset for $size" >&2
- dd if="$file" bs=1 count="$size" skip="$offset" 2>/dev/null | \
- hexdump -v -e '1/1 "%d "'
+ if (( "${#busaddr[@]}" > 0)); then
+ echo "Reading ${busaddr[*]} at $offset for $size" >&2
+ i2ctransfer -f -y ${busaddr[0]} $(${busaddr[2]} ${busaddr[1]} $offset) r$size
+ else
+ echo "Reading $file at $offset for $size" >&2
+ dd if="$file" bs=1 count="$size" skip="$offset" 2>/dev/null | \
+ hexdump -v -e '1/1 "%d "'
+ fi
}
write_bytes() {
+ local busaddr=(${IPMI_FRU_EEPROM_BUSADDR["$1"]-})
local file="${IPMI_FRU_EEPROM_FILE["$1"]-$1}"
local offset="$2"
local -n bytes_arr="$3"
- local hexstr
- hexstr="$(printf '\\x%x' "${bytes_arr[@]}")" || return
- echo "Writing $file at $offset for ${#bytes_arr[@]}" >&2
- printf "$hexstr" | dd of="$file" bs=1 seek=$offset 2>/dev/null
+ if (( "${#busaddr[@]}" > 0)); then
+ echo "Writing ${busaddr[*]} at $offset for ${#bytes_arr[@]}" >&2
+ i2ctransfer -f -y ${busaddr[0]} $(${busaddr[2]} ${busaddr[1]} $offset ${#bytes_arr[@]}) ${bytes_arr[@]}
+ else
+ local hexstr
+ hexstr="$(printf '\\x%x' "${bytes_arr[@]}")" || return
+ echo "Writing $file at $offset for ${#bytes_arr[@]}" >&2
+ printf "$hexstr" | dd of="$file" bs=1 seek=$offset 2>/dev/null
+ fi
}
read_header() {