Skip to content

Commit 4e8699c

Browse files
committed
[ot] python/qemu: ot.otp: add support for absorb-able fields
Signed-off-by: Emmanuel Blot <[email protected]>
1 parent 5044f04 commit 4e8699c

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed

python/qemu/ot/otp/map.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ def _generate_partitions(self) -> None:
110110
# assume name & size are always defined for each item
111111
item_name = item['name']
112112
del item['name']
113-
item_size = int(item['size'])
113+
# absorbing items may have no size
114+
item_size = int(item.get('size', 0))
114115
item['size'] = item_size
115116
assert item_name not in items
116117
items[item_name] = item
@@ -187,8 +188,9 @@ def _compute_locations(self) -> None:
187188
if extra_blocks:
188189
part.size += self.BLOCK_SIZE
189190
extra_blocks -= 1
190-
self._log.info('Partition %s size augmented from %u to %u',
191+
self._log.info('Partition %s size augmented from %u to %u bytes',
191192
part.name, psize, part.size)
193+
part.dispatch_absorb()
192194
for part in self._partitions:
193195
part_offset = 0
194196
for part in self._partitions:

python/qemu/ot/otp/partition.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ def is_zeroizable(self) -> bool:
9393
"""Check if the partition supports zeroization."""
9494
return getattr(self, 'zeroizable', False)
9595

96+
@property
97+
def is_absorb(self) -> bool:
98+
"""Check if the partition can use unassigned storage."""
99+
return getattr(self, 'absorb', False)
100+
96101
@property
97102
def is_empty(self) -> bool:
98103
"""Report if the partition is empty."""
@@ -279,6 +284,43 @@ def emit(fmt, *args):
279284
if offset != rpos:
280285
self._log.warning('%s: offset %d, size %d', self.name, offset, rpos)
281286

287+
def dispatch_absorb(self) -> None:
288+
"""Request the partition to resize its fields."""
289+
if not self.is_absorb:
290+
raise RuntimeError('Partition cannot absorb free space')
291+
absorb_fields = {n: v for n, v in self.items.items()
292+
if v.get('absorb', False)}
293+
avail_size = self.size
294+
if self.has_digest:
295+
avail_size -= self.DIGEST_SIZE
296+
if self.is_zeroizable:
297+
avail_size -= self.ZER_SIZE
298+
avail_blocks = avail_size // OtpMap.BLOCK_SIZE
299+
if not absorb_fields:
300+
# a version of OTP where absorb is defined as a partition property
301+
# but not defined for fields. In such a case, it is expected that
302+
# the partition contains a single field.
303+
itemcnt = len(self.items)
304+
if itemcnt != 1:
305+
raise RuntimeError(f'No known absorption method with {itemcnt} '
306+
f'items in partition {self.name}')
307+
# nothing to do here
308+
return
309+
absorb_count = len(absorb_fields)
310+
blk_per_field = avail_blocks // absorb_count
311+
extra_blocks = avail_blocks % absorb_count
312+
self._log.info("%s: %d bytes (%d blocks) to absorb into %d field%s",
313+
self.name, avail_size, avail_blocks, absorb_count,
314+
's' if absorb_count > 1 else '')
315+
for itname, itfield in absorb_fields.items():
316+
fsize = itfield['size']
317+
itfield['size'] += OtpMap.BLOCK_SIZE * blk_per_field
318+
if extra_blocks:
319+
itfield['size'] += OtpMap.BLOCK_SIZE
320+
extra_blocks -= 1
321+
self._log.info('%s.%s size augmented from %u to %u bytes',
322+
self.name, itname, fsize, itfield['size'])
323+
282324
def empty(self) -> None:
283325
"""Empty the partition, including its digest if any."""
284326
self._data = bytes(len(self._data))

0 commit comments

Comments
 (0)