Skip to content

Commit bc7368c

Browse files
djmsippatrislav1
authored andcommitted
Add in limited support for Internal Areas
The code as is does not support printing or defining internal areas. This patch adds basic support for doing this. The contents of the internal area are defined by a list of hex numbers as shown below: InternalUse: data: 01 02 03 04 ab cd ef The size of the internal area is set to the recommended 72 by default. It can be changed by using the --internal-area-size command line arg. The data given will be padded with 0xff or truncated to fit this length. Note that byte 0 of the internal area is mandated to be a '1' (see section 9 of the FRU spec), and that is included in the size. When dumping, since there is no length field in the internal area, it just takes the start of the next section (if present) as the size to dump.
1 parent c2737a5 commit bc7368c

5 files changed

Lines changed: 95 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: 16 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 internal area size (including format byte), default is recommended 72 (only valid in write mode)'
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,15 @@ def main():
173178
if read_mode and args.ignore_checksum_errors:
174179
FruAreaChecksummed.ignore_checksum_errors = True
175180

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

frugy/fru.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
###########################################################################
1212

1313
from frugy import __version__
14-
from frugy.areas import CommonHeader, ChassisInfo, BoardInfo, ProductInfo
14+
from frugy.areas import CommonHeader, ChassisInfo, BoardInfo, ProductInfo, InternalUse
1515
from frugy.multirecords import MultirecordArea
1616
import frugy.multirecords_ipmi
1717
import frugy.multirecords_picmg
@@ -44,6 +44,7 @@ def import_log(msg):
4444

4545
class Fru:
4646
_area_table_lookup = bidict({
47+
'InternalUse': 'internal_use_offs',
4748
'ChassisInfo': 'chassis_info_offs',
4849
'BoardInfo': 'board_info_offs',
4950
'ProductInfo': 'product_info_offs',
@@ -59,6 +60,7 @@ def __init__(self, initdict=None):
5960

6061
def factory(self, cls_name, cls_args=None):
6162
map = {
63+
'InternalUse': InternalUse,
6264
'ChassisInfo': ChassisInfo,
6365
'BoardInfo': BoardInfo,
6466
'ProductInfo': ProductInfo,
@@ -101,13 +103,33 @@ def deserialize(self, input):
101103
import_log.str = ''
102104
self.areas = {}
103105
self.header.deserialize(input)
106+
107+
# The internal record does not have a length field in it,
108+
# and it is just a byte array. So we have to find the start
109+
# of the next record and slice the input on that boundary.
110+
end_of_internal = 0;
111+
internal_use_offset = 0
112+
offset_list = []
113+
for k, v in self.header.to_dict().items():
114+
if v and k == 'internal_use_offs':
115+
internal_use_offset = v
116+
offset_list.append(v)
117+
offset_list.sort()
118+
119+
if internal_use_offset != 0:
120+
index = offset_list.index(internal_use_offset)
121+
# Check it isn't the last on the list
122+
if index != len(offset_list) - 1:
123+
end_of_internal = offset_list[index + 1]
124+
104125
for k, v in self.header.to_dict().items():
105-
# Ignore "internal use area"
106-
# TODO: Support it as opaque byte array?
107-
if v and k != 'internal_use_offs':
126+
if v:
108127
obj_name = self._area_table_lookup.inverse[k]
109128
obj = self.factory(obj_name)
110-
obj.deserialize(input[v:])
129+
if k == 'internal_use_offs' and end_of_internal != 0:
130+
obj.deserialize(input[v:end_of_internal])
131+
else:
132+
obj.deserialize(input[v:])
111133
self.areas[obj_name] = obj
112134

113135
def load_yaml(self, fname):

frugy/types.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,47 @@ def deserialize(self, input: bytearray):
694694
remainder = self._deserialize(input[1:])
695695
return self._verify_epilogue(input, len(input) - len(remainder))
696696

697+
class FruAreaInternalUse(FruAreaBase):
698+
''' FRU Internal use area featuring a version field but no checksum, will
699+
be padded to whatever internal area is specified'''
700+
701+
internal_area_size = 72
702+
703+
def __init__(self, initdict=None):
704+
self._format_version = _format_version_default
705+
super().__init__(initdict=initdict)
706+
707+
def _set_format_version(self, val):
708+
self._format_version = val
709+
710+
def _get_format_version(self):
711+
return self._format_version
712+
713+
def _prologue(self) -> bytearray:
714+
''' return data to prepend (format version) '''
715+
return self._format_version.to_bytes(1, 'little')
716+
717+
def _epilogue(self, payload: bytearray) -> bytearray:
718+
''' return data to append, padded of zero bytes'''
719+
numPadBytes, _ = _sizeAlign(len(payload), self.internal_area_size)
720+
return b'\xff' * numPadBytes
721+
722+
def size_payload(self) -> int:
723+
return self.internal_area_size
724+
725+
def size_total(self) -> int:
726+
return self.internal_area_size
727+
728+
def serialize(self) -> bytearray:
729+
payload = self._prologue()
730+
body = self._serialize()
731+
payload += body[:self.internal_area_size - 1]
732+
return payload + self._epilogue(payload)
733+
734+
def deserialize(self, input: bytearray):
735+
self._format_version = input[0]
736+
return self._deserialize(input[1:])
737+
697738

698739
class FruAreaSized(FruAreaVersioned):
699740
''' FRU area featuring version and length fields '''

0 commit comments

Comments
 (0)