blob: 3ae0f0864c33c7254bdc18bfb2e0deaaee7b9767 [file] [log] [blame]
William A. Kennington III1b34b592021-03-02 20:24:57 -08001#!/bin/bash
2# Copyright 2021 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16[ -n "${ipmi_fru_init-}" ] && return
17
18IPMI_FRU_COMMON_HEADER_INTERNAL_OFFSET_IDX=1
19IPMI_FRU_COMMON_HEADER_CHASSIS_OFFSET_IDX=2
20IPMI_FRU_COMMON_HEADER_BOARD_OFFSET_IDX=3
21IPMI_FRU_COMMON_HEADER_PRODUCT_OFFSET_IDX=4
22IPMI_FRU_COMMON_HEADER_MULTI_RECORD_OFFSET_IDX=5
23IPMI_FRU_AREA_HEADER_SIZE_IDX=1
24IPMI_FRU_CHECKSUM_IDX=-1
25
26of_name_to_eeproms() {
27 local names
28 if ! names="$(grep -xl "$1" /sys/bus/i2c/devices/*/of_node/name)"; then
29 echo "Failed to find eeproms with of_name '$1'" >&2
30 return 1
31 fi
32 echo "$names" | sed 's,/of_node/name$,/eeprom,'
33}
34
35of_name_to_eeprom() {
36 local eeproms
37 eeproms="$(of_name_to_eeproms "$1")" || return
38 if (( "$(echo "$eeproms" | wc -l)" != 1 )); then
39 echo "Got more than one eeprom for '$1':" $eeproms >&2
40 return 1
41 fi
42 echo "$eeproms"
43}
44
William A. Kennington IIIa3a8ca92021-10-12 23:18:52 -070045declare -A IPMI_FRU_EEPROM_FILE=()
46
47ipmi_fru_alloc() {
48 local name="$1"
49 local -n ret="$2"
50
51 # Pick the first free index to return as the allocated entry
52 for (( ret = 0; ret < "${#IPMI_FRU_EEPROM_FILE[@]}"; ++ret )); do
53 [ -n "${IPMI_FRU_EEPROM_FILE[@]+1}" ] || break
54 done
55
56 if [[ "$name" =~ ^of-name:(.*)$ || "$name" =~ ^([^:]*)$ ]]; then
57 local ofn="${BASH_REMATCH[1]}"
58 local file
59 file="$(of_name_to_eeprom "$ofn")" || return
60 IPMI_FRU_EEPROM_FILE["$ret"]="$file"
61 else
62 echo "Invalid IPMI FRU eeprom specification: $name" >&2
63 return 1
64 fi
65}
66
67ipmi_fru_free() {
68 unset 'IPMI_FRU_EEPROM_FILE[$1]'
69}
70
William A. Kennington III1b34b592021-03-02 20:24:57 -080071checksum() {
72 local -n checksum_arr="$1"
73 local checksum=0
74 for byte in "${checksum_arr[@]}"; do
75 checksum=$((checksum + byte))
76 done
77 echo $((checksum & 0xff))
78}
79
80fix_checksum() {
81 local -n fix_checksum_arr="$1"
82 old_cksum=${fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]}
83 ((fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]-=$(checksum fix_checksum_arr)))
84 ((fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]&=0xff))
85 printf 'Corrected %s checksum from 0x%02X -> 0x%02X\n' \
86 "$1" "${old_cksum}" "${fix_checksum_arr[$IPMI_FRU_CHECKSUM_IDX]}" >&2
87}
88
89read_bytes() {
William A. Kennington IIIa3a8ca92021-10-12 23:18:52 -070090 local file="${IPMI_FRU_EEPROM_FILE["$1"]-$1}"
William A. Kennington III1b34b592021-03-02 20:24:57 -080091 local offset="$2"
92 local size="$3"
93
94 echo "Reading $file at $offset for $size" >&2
95 dd if="$file" bs=1 count="$size" skip="$offset" 2>/dev/null | \
96 hexdump -v -e '1/1 "%d "'
97}
98
99write_bytes() {
William A. Kennington IIIa3a8ca92021-10-12 23:18:52 -0700100 local file="${IPMI_FRU_EEPROM_FILE["$1"]-$1}"
William A. Kennington III1b34b592021-03-02 20:24:57 -0800101 local offset="$2"
102 local -n bytes_arr="$3"
103
104 local hexstr
105 hexstr="$(printf '\\x%x' "${bytes_arr[@]}")" || return
106 echo "Writing $file at $offset for ${#bytes_arr[@]}" >&2
107 printf "$hexstr" | dd of="$file" bs=1 seek=$offset 2>/dev/null
108}
109
110read_header() {
111 local eeprom="$1"
112 local -n header_arr="$2"
113
114 header_arr=($(read_bytes "$eeprom" 0 8)) || return
115 echo "Checking $eeprom FRU Header version" >&2
116 # FRU header is always version 1
117 (( header_arr[0] == 1 )) || return
118 echo "Checking $eeprom FRU Header checksum" >&2
119 local sum
120 sum="$(checksum header_arr)" || return
121 # Checksums should be valid
122 (( sum == 0 )) || return 10
123}
124
125read_area() {
126 local eeprom="$1"
127 local offset="$2"
128 local -n area_arr="$3"
129 local area_size="${4-0}"
130
131 offset=$((offset*8))
132 area_arr=($(read_bytes "$eeprom" "$offset" 8)) || return
133 echo "Checking $eeprom $offset FRU Area version" >&2
134 # FRU Area is always version 1
135 (( area_arr[0] == 1 )) || return
136 if (( area_size == 0 )); then
137 area_size=${area_arr[$IPMI_FRU_AREA_HEADER_SIZE_IDX]}
138 fi
139 area_arr=($(read_bytes "$eeprom" "$offset" $((area_size*8)))) || return
140 echo "Checking $eeprom $offset FRU Area checksum" >&2
141 local sum
142 sum="$(checksum area_arr)" || return
143 # Checksums should be valid
144 (( sum == 0 )) || return 10
145}
146
147ipmi_fru_init=1
148return 0 2>/dev/null
149echo "ipmi-fru is a library, not executed directly" >&2
150exit 1