Skip to content

Commit 1e0ef55

Browse files
committed
Merge branch 'wip-internal-use-area'
Add support for FRU Internal Use Area
2 parents aebe814 + e2624ff commit 1e0ef55

5 files changed

Lines changed: 88 additions & 9 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pip3 install git+https://github.com/MicroTCA-Tech-Lab/frugy
2020
$ frugy --help
2121
usage: frugy [-h] [--version] [-o OUTPUT] [-w] [-r] [-d]
2222
[-e EEPROM_SIZE] [-s SET] [-t] [-b] [-c] [-l [LIST]]
23-
[-v VERBOSITY]
23+
[-v VERBOSITY] [--internal-area-size SIZE]
2424
[srcfile]
2525
2626
FRU Generator YAML

frugy/areas.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# #
1111
###########################################################################
1212

13-
from frugy.types import FruAreaVersioned, FruAreaSized, FixedField, StringField, StringField, bin2hex_helper, CustomStringArray
13+
from frugy.types import FruAreaInternalUse, FruAreaVersioned, FruAreaSized, FixedField, StringField, StringField, bin2hex_helper, CustomStringArray, BytearrayField
1414
from frugy.fru_registry import FruRecordType, rec_register
1515
from datetime import datetime, timedelta
1616
import logging
@@ -152,3 +152,12 @@ class ProductInfo(FruAreaSized):
152152
('fru_file_id', StringField),
153153
('custom_info_fields', CustomStringArray),
154154
]
155+
156+
157+
@ipmi_area
158+
class InternalUse(FruAreaInternalUse):
159+
''' Platform Management FRU Information Storage Definition, Table 9-1 '''
160+
161+
_schema = [
162+
('data', BytearrayField),
163+
]

frugy/cli.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from frugy.__init__ import __version__
2121
from frugy.fru import Fru
2222
from frugy.fru_registry import FruRecordType, rec_enumerate, rec_lookup_by_name, rec_info, schema_entry_info
23-
from frugy.types import FruAreaChecksummed
23+
from frugy.types import FruAreaChecksummed, FruAreaInternalUse
2424
from frugy.multirecords import MultirecordEntry
2525

2626

@@ -135,6 +135,11 @@ def main():
135135
nargs='?',
136136
help='list supported FRU records or schema of specified record'
137137
)
138+
139+
parser.add_argument('--internal-area-size',
140+
type=int,
141+
help='set fixed size for internal use area (including format byte)'
142+
)
138143
parser.add_argument('-v', '--verbosity',
139144
type=int,
140145
help='set verbosity (0=quiet, 1=info, 2=debug)'
@@ -163,7 +168,7 @@ def main():
163168
parser.print_help(sys.stderr)
164169
sys.exit(1)
165170

166-
if read_mode and (args.eeprom_size is not None or args.set or args.timestamp):
171+
if read_mode and (args.eeprom_size is not None or args.set or args.timestamp or args.internal_area_size):
167172
parser.print_help(sys.stderr)
168173
sys.exit(1)
169174

@@ -173,6 +178,14 @@ def main():
173178
if read_mode and args.ignore_checksum_errors:
174179
FruAreaChecksummed.ignore_checksum_errors = True
175180

181+
if args.internal_area_size and args.internal_area_size % 8 != 0:
182+
print("Internal area size must be a multiple of 8 bytes", file=sys.stderr)
183+
sys.exit(1)
184+
185+
# In read mode, this isn't used, we always use the size of the section in
186+
# the file since there is no length field.
187+
FruAreaInternalUse.internal_area_size = args.internal_area_size
188+
176189
outfile = args.output
177190
if args.dump:
178191
if outfile:

frugy/fru.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
####################################################################################
1313

1414
from frugy import __version__
15-
from frugy.areas import CommonHeader, ChassisInfo, BoardInfo, ProductInfo
15+
from frugy.areas import CommonHeader, ChassisInfo, BoardInfo, ProductInfo, InternalUse
1616
from frugy.multirecords import MultirecordArea
1717
import frugy.multirecords_ipmi
1818
import frugy.multirecords_picmg
@@ -46,6 +46,7 @@ def import_log(msg):
4646

4747
class Fru:
4848
_area_table_lookup = bidict({
49+
'InternalUse': 'internal_use_offs',
4950
'ChassisInfo': 'chassis_info_offs',
5051
'BoardInfo': 'board_info_offs',
5152
'ProductInfo': 'product_info_offs',
@@ -61,6 +62,7 @@ def __init__(self, initdict=None):
6162

6263
def factory(self, cls_name, cls_args=None):
6364
map = {
65+
'InternalUse': InternalUse,
6466
'ChassisInfo': ChassisInfo,
6567
'BoardInfo': BoardInfo,
6668
'ProductInfo': ProductInfo,
@@ -103,13 +105,25 @@ def deserialize(self, input):
103105
import_log.str = ''
104106
self.areas = {}
105107
self.header.deserialize(input)
108+
109+
# The internal record does not have a length field in it,
110+
# and it is just a byte array. So we have to find the start
111+
# of the next record and slice the input on that boundary.
112+
offsets = self.header.to_dict()
113+
if "internal_use_offs" in offsets:
114+
internal_use_start = offsets["internal_use_offs"]
115+
internal_use_end = next(
116+
(o for o in sorted(offsets.values()) if o > internal_use_start), None
117+
)
118+
106119
for k, v in self.header.to_dict().items():
107-
# Ignore "internal use area"
108-
# TODO: Support it as opaque byte array?
109-
if v and k != 'internal_use_offs':
120+
if v:
110121
obj_name = self._area_table_lookup.inverse[k]
111122
obj = self.factory(obj_name)
112-
obj.deserialize(input[v:])
123+
if k == "internal_use_offs" and internal_use_end is not None:
124+
obj.deserialize(input[v:internal_use_end])
125+
else:
126+
obj.deserialize(input[v:])
113127
self.areas[obj_name] = obj
114128

115129
def load_yaml(self, fname):

frugy/types.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,49 @@ def deserialize(self, input: bytearray):
791791
remainder = self._deserialize(input[1:])
792792
return self._verify_epilogue(input, len(input) - len(remainder))
793793

794+
class FruAreaInternalUse(FruAreaBase):
795+
''' FRU Internal use area featuring a version field but no checksum, will
796+
be padded to whatever internal area is specified'''
797+
798+
internal_area_size = None
799+
800+
def __init__(self, initdict=None):
801+
self._format_version = _format_version_default
802+
super().__init__(initdict=initdict)
803+
804+
def _set_format_version(self, val):
805+
self._format_version = val
806+
807+
def _get_format_version(self):
808+
return self._format_version
809+
810+
def _prologue(self) -> bytearray:
811+
''' return data to prepend (format version) '''
812+
return self._format_version.to_bytes(1, 'little')
813+
814+
def _epilogue(self, payload: bytearray) -> bytearray:
815+
''' return data to append, padded of 0xff bytes'''
816+
numPadBytes, _ = _sizeAlign(len(payload), self.internal_area_size or 8)
817+
return b'\xff' * numPadBytes
818+
819+
def size_total(self) -> int:
820+
# payload plus "format version" prologue
821+
total_size_base = self.size_payload() + 1
822+
_, total_size_base = _sizeAlign(total_size_base, self.internal_area_size or 8)
823+
return total_size_base
824+
825+
def serialize(self) -> bytearray:
826+
payload = self._prologue()
827+
payload += self._serialize()
828+
if self.internal_area_size and len(payload) > self.internal_area_size:
829+
raise RuntimeError(f"internal area size ({self.internal_area_size}) too small; need at least {len(payload)} bytes")
830+
return payload + self._epilogue(payload)
831+
832+
def deserialize(self, input: bytearray):
833+
logging.debug(f"bytearray: {input} len: {len(input)}")
834+
self._format_version = input[0]
835+
return self._deserialize(input[1:])
836+
794837

795838
class FruAreaSized(FruAreaVersioned):
796839
''' FRU area featuring version and length fields '''

0 commit comments

Comments
 (0)