common/recipes-core/psu-update/files/hexfile.py (148 lines of code) (raw):
# The MIT License (MIT)
# =====================
#
# Copyright (c) 2014 Ryan Sturmer
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# License URL: https://github.com/ryansturmer/hexfile/blob/master/LICENSE.md
# Code URL: https://github.com/ryansturmer/hexfile/blob/master/hexfile/core.py
import itertools
def short(msb,lsb):
return (msb<<8) | lsb
def long(b1, b2, b3, b4):
return (b1<<24) | (b2<<16) | (b3<<8) | b4
class HexFile(object):
def __init__(self, segments, eip, cs, ip):
self.segments = segments
self.eip = eip
self.cs = cs
self.ip = ip
def __getitem__(self, val):
if isinstance(val, slice):
address = val.start
else:
address = val
for segment in self.segments:
if address in segment:
return segment[val]
raise IndexError('No segment contains address 0x%x' % address)
def __len__(self):
return sum(map(len, self.segments))
@property
def size(self):
return len(self)
def __iter__(self):
return itertools.chain(*self.segments)
@staticmethod
def load(filename):
segments = []
eip = None
cs = None
ip = None
with open(filename) as fp:
lines = fp.readlines()
extended_linear_address = 0
extended_segment_address = 0
current_address = 0
end_of_file = False
lineno = 0
for line in lines:
lineno += 1
line = line.strip();
if not line.startswith(':'):
continue
if end_of_file:
raise Exception("Record found after end of file on line %d" % lineno)
bytes = [int(line[i:i+2], 16) for i in range(1,len(line), 2)]
byte_count = bytes[0]
address = short(*bytes[1:3])
record_type = bytes[3]
checksum = bytes[-1]
data = bytes[4:-1]
computed_checksum = ((1 << 8)-(sum(bytes[:-1]) & 0xff)) & 0xff
if(computed_checksum != checksum):
raise Exception("Record checksum doesn't match on line %d" % lineno)
if record_type == 0:
if byte_count == len(data):
current_address = (address + extended_linear_address + extended_segment_address) & 0xffffffff
have_segment = False
for segment in segments:
if segment.end_address == current_address:
segment.data.extend(data)
have_segment = True
break
if not have_segment:
segments.append(Segment(current_address, data))
else:
raise Exception("Data record reported size does not match actual size on line %d" % lineno)
elif record_type == 1:
end_of_file = True
elif record_type == 2:
if byte_count != 2 or len(data) != 2:
raise Exception("Byte count misreported in extended segment address record on line %d" % lineno)
extended_segment_address = short(*data) << 4
elif record_type == 3:
if byte_count != 4 or len(data) != 4:
raise Exception("Byte count misreported in start segment address record on line %d" % lineno)
cs = short(*data[0:2])
ip = short(*data[2:4])
elif record_type == 4:
if byte_count != 2 or len(data) != 2:
raise Exception("Byte count misreported in extended linear address record on line %d" % lineno)
extended_linear_address = short(*data) << 16
elif record_type == 5:
if byte_count != 4 or len(data) != 4:
raise Exception("Byte count misreported in start linear address record on line %d" % lineno)
eip = long(*data)
else:
raise Exception("Unknown record type: %s" % record_type)
return HexFile(segments, eip, cs, ip)
def pretty_string(self, stride=16):
retval = []
for segment in self.segments:
retval.append('Segment @ 0x%08x (%d bytes)' % (segment.start_address, segment.size))
retval.append(segment.pretty_string(stride=stride))
retval.append('')
if self.eip is not None:
retval.append('EIP 0x%x' % self.eip)
if self.cs is not None:
retval.append('CS 0x%x' % self.cs)
if self.ip is not None:
retval.append('IP 0x%x' % self.ip)
return '\n'.join(retval)
def load(filename):
return HexFile.load(filename)
class Segment(object):
def __init__(self, start_address, data = None):
self.start_address = start_address
self.data = data or []
def pretty_string(self, stride=16):
retval = []
addresses = self.addresses
ranges = [addresses[i:i+stride] for i in range(0, self.size, stride)]
for r in ranges:
retval.append('%08x ' % r[0] + ' '.join(['%02x' % self[addr] for addr in r]))
return '\n'.join(retval)
def __str__(self):
return '<%d byte segment @ 0x%08x>' % (self.size, self.start_address)
def __repr__(self):
return str(self)
@property
def end_address(self):
return self.start_address + len(self.data)
@property
def size(self):
return len(self.data)
def __contains__(self, address):
return address >= self.start_address and address < self.end_address
def __getitem__(self, address):
if isinstance(address, slice):
if address.start not in self or address.stop-1 not in self:
raise IndexError('Address out of range for this segment')
else:
d = self.data[address.start-self.start_address:address.stop-self.start_address:address.step]
start_address = address.start + self.start_address
return Segment(start_address, d)
else:
if not address in self:
raise IndexError("Address 0x%x is not in this segment" % address)
return self.data[address-self.start_address]
@property
def addresses(self):
return range(self.start_address, self.end_address)
def __len__(self):
return len(self.data)
def __iter__(self):
return iter(zip(self.addresses,self.data))