blob: 3ae0f0864c33c7254bdc18bfb2e0deaaee7b9767 [file] [log] [blame]
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[ -n "${ipmi_fru_init-}" ] && return
IPMI_FRU_COMMON_HEADER_INTERNAL_OFFSET_IDX=1
IPMI_FRU_COMMON_HEADER_CHASSIS_OFFSET_IDX=2
IPMI_FRU_COMMON_HEADER_BOARD_OFFSET_IDX=3
IPMI_FRU_COMMON_HEADER_PRODUCT_OFFSET_IDX=4
IPMI_FRU_COMMON_HEADER_MULTI_RECORD_OFFSET_IDX=5
IPMI_FRU_AREA_HEADER_SIZE_IDX=1
IPMI_FRU_CHECKSUM_IDX=-1
of_name_to_eeproms() {
local names
if ! names="$(grep -xl "$1" /sys/bus/i2c/devices/*/of_node/name)"; then
echo "Failed to find eeproms with of_name '$1'" >&2
return 1
fi
echo "$names" | sed 's,/of_node/name$,/eeprom,'
}
of_name_to_eeprom() {
local eeproms
eeproms="$(of_name_to_eeproms "$1")" || return
if (( "$(echo "$eeproms" | wc -l)" != 1 )); then
echo "Got more than one eeprom for '$1':" $eeproms >&2
return 1
fi
echo "$eeproms"
}
declare -A IPMI_FRU_EEPROM_FILE=()
ipmi_fru_alloc() {
local name="$1"
local -n ret="$2"
# 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
done
if [[ "$name" =~ ^of-name:(.*)$ || "$name" =~ ^([^:]*)$ ]]; then
local ofn="${BASH_REMATCH[1]}"
local file
file="$(of_name_to_eeprom "$ofn")" || return
IPMI_FRU_EEPROM_FILE["$ret"]="$file"
else
echo "Invalid IPMI FRU eeprom specification: $name" >&2
return 1
fi
}
ipmi_fru_free() {
unset 'IPMI_FRU_EEPROM_FILE[$1]'
}
checksum() {
local -n checksum_arr="$1"
local checksum=0
for byte in "${checksum_arr[@]}"; do
checksum=$((checksum + byte))
done
echo $((checksum & 0xff))
}
fix_checksum() {
local -n fix_checksum_arr="$1"
old_cksum=${fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]}
((fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]-=$(checksum fix_checksum_arr)))
((fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]&=0xff))
printf 'Corrected %s checksum from 0x%02X -> 0x%02X\n' \
"$1" "${old_cksum}" "${fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]}" >&2
}
read_bytes() {
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 "'
}
write_bytes() {
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
}
read_header() {
local eeprom="$1"
local -n header_arr="$2"
header_arr=($(read_bytes "$eeprom" 0 8)) || return
echo "Checking $eeprom FRU Header version" >&2
# FRU header is always version 1
(( header_arr[0] == 1 )) || return
echo "Checking $eeprom FRU Header checksum" >&2
local sum
sum="$(checksum header_arr)" || return
# Checksums should be valid
(( sum == 0 )) || return 10
}
read_area() {
local eeprom="$1"
local offset="$2"
local -n area_arr="$3"
local area_size="${4-0}"
offset=$((offset*8))
area_arr=($(read_bytes "$eeprom" "$offset" 8)) || return
echo "Checking $eeprom $offset FRU Area version" >&2
# FRU Area is always version 1
(( area_arr[0] == 1 )) || return
if (( area_size == 0 )); then
area_size=${area_arr[$IPMI_FRU_AREA_HEADER_SIZE_IDX]}
fi
area_arr=($(read_bytes "$eeprom" "$offset" $((area_size*8)))) || return
echo "Checking $eeprom $offset FRU Area checksum" >&2
local sum
sum="$(checksum area_arr)" || return
# Checksums should be valid
(( sum == 0 )) || return 10
}
ipmi_fru_init=1
return 0 2>/dev/null
echo "ipmi-fru is a library, not executed directly" >&2
exit 1