2008-04-17 19:46:16 -04:00
|
|
|
'''
|
|
|
|
|
This module contains helper classes for structure fields and length expressions.
|
|
|
|
|
'''
|
|
|
|
|
class Field(object):
|
|
|
|
|
'''
|
|
|
|
|
Represents a field of a structure.
|
|
|
|
|
|
|
|
|
|
type is the datatype object for the field.
|
|
|
|
|
field_type is the name of the type (string tuple)
|
|
|
|
|
field_name is the name of the structure field.
|
|
|
|
|
visible is true iff the field should be in the request API.
|
|
|
|
|
wire is true iff the field should be in the request structure.
|
|
|
|
|
auto is true iff the field is on the wire but not in the request API (e.g. opcode)
|
2011-11-27 10:35:06 +00:00
|
|
|
enum is the enum name this field refers to, if any.
|
2008-04-17 19:46:16 -04:00
|
|
|
'''
|
2013-01-14 14:19:45 -08:00
|
|
|
def __init__(self, type, field_type, field_name, visible, wire, auto, enum=None, isfd=False):
|
2008-04-17 19:46:16 -04:00
|
|
|
self.type = type
|
|
|
|
|
self.field_type = field_type
|
|
|
|
|
self.field_name = field_name
|
2011-11-27 10:35:06 +00:00
|
|
|
self.enum = enum
|
2008-04-17 19:46:16 -04:00
|
|
|
self.visible = visible
|
|
|
|
|
self.wire = wire
|
|
|
|
|
self.auto = auto
|
2013-01-14 14:19:45 -08:00
|
|
|
self.isfd = isfd
|
2014-11-02 13:47:07 +01:00
|
|
|
self.parent = None
|
2008-04-17 19:46:16 -04:00
|
|
|
|
automatic alignment checker / manual offsets
The verification algorithm basically traverses the protocol-description
of a protocol entity by recursively processing all fields and their
types.
A start-align consists of two numbers:
* the alignment: This is a power of 2, and guarantees that the
address (or protocol position) minus the offset can be divided
by this number.
* the offset: how many bytes the current position is after a
position that can be divided by the alignment.
The algorithm starts with the start-alignment and computes the alignment
of each field from the start-align of the previous field as follows:
* if the previous field is a primitive type then the offset is increased
by the size of the primitive type module the alignment.
If the alignment or offset are incompatible with the primitive type,
then an error is reported.
* if the previous field is a complex type, then it is processed recursively.
* if the previous field is an alignment-pad, then the alignment is
adjusted accordingly, as to be expected by the alignment-pad.
Start-aligns can also be set manually with the xml-element
"required_start_align" which has two attributes: "align" and "offset"
If "offset" is omitted, it is assumed to 0.
All toplevel protocol entities default to 4-byte start-alignment with offset 0.
All non-toplevel complex entities, such as structs, switch, case, ...
do not have a default alignment.
If no alignment is explicitly specified for them, their alignment
is computed by their usage in other entities.
In that case, if they are used with aligments that violate the
alignment requirements of some of their fields, an error is issued.
If they are used with an alignment with non-zero offset,
a warning is issued, which recommends to specify the required_start_align
explicitly. (Reason: I don't want non-zero offsets to be silently
computed automatically. These non-zero offsets have to be reviewed
by a human and specified explicitely to record that this was reviewed
by a human)
If the required_start_align is explicitly specified for an entity
then an error will be issued if it is used with an aligment that's
incompatible with the explicitly specified alignment.
If an entity is used in different contexts with different start-aligns
then those start-aligns are combined to an align which is compatible
with all aligns.
E.g. (align 4, offset 0) and (align 4, offset 2) are combined
to (align 2, offset 0).
Error reports include the relevant context for a misalignment.
For non-toplevel entities this includes the entity-usage stack.
There is some complexity in the algorithm for reducing the size
of the error-reports to include only the relevant info.
This alignment verifier is also a prerequisite for getting
rid of implicit alignment altogether.
(This will then simplify the generated code and make it faster.)
Signed-off-by: Christian Linhart <chris@demorecorder.com>
2015-11-01 18:26:33 +01:00
|
|
|
def __str__(self):
|
|
|
|
|
field_string = "Field"
|
|
|
|
|
if self.field_name is None:
|
|
|
|
|
if self.field_type is not None:
|
|
|
|
|
field_string += " with type " + str(self.type)
|
|
|
|
|
else:
|
|
|
|
|
field_string += " \"" + self.field_name + "\""
|
|
|
|
|
if self.parent is not None:
|
|
|
|
|
field_string += " in " + str(self.parent)
|
|
|
|
|
|
|
|
|
|
return field_string
|
2008-04-17 19:46:16 -04:00
|
|
|
|
|
|
|
|
class Expression(object):
|
|
|
|
|
'''
|
|
|
|
|
Represents a mathematical expression for a list length or exprfield.
|
|
|
|
|
|
|
|
|
|
Public fields:
|
2010-05-13 21:05:58 +00:00
|
|
|
op is the operation (text +,*,/,<<,~) or None.
|
2008-04-17 19:46:16 -04:00
|
|
|
lhs and rhs are the sub-Expressions if op is set.
|
|
|
|
|
lenfield_name is the name of the length field, or None for request lists.
|
|
|
|
|
lenfield is the Field object for the length field, or None.
|
|
|
|
|
bitfield is True if the length field is a bitmask instead of a number.
|
|
|
|
|
nmemb is the fixed size (value)of the expression, or None
|
|
|
|
|
'''
|
|
|
|
|
def __init__(self, elt, parent):
|
|
|
|
|
self.parent = parent
|
|
|
|
|
|
|
|
|
|
self.nmemb = None
|
|
|
|
|
|
|
|
|
|
self.lenfield_name = None
|
|
|
|
|
self.lenfield_type = None
|
2010-07-13 07:58:24 +02:00
|
|
|
self.lenfield_parent = None
|
2008-04-17 19:46:16 -04:00
|
|
|
self.lenfield = None
|
|
|
|
|
self.lenwire = False
|
|
|
|
|
self.bitfield = False
|
|
|
|
|
|
|
|
|
|
self.op = None
|
|
|
|
|
self.lhs = None
|
|
|
|
|
self.rhs = None
|
|
|
|
|
|
2014-09-04 17:48:56 +02:00
|
|
|
self.contains_listelement_ref = False
|
|
|
|
|
|
2008-04-17 19:46:16 -04:00
|
|
|
if elt.tag == 'list':
|
|
|
|
|
# List going into a request, which has no length field (inferred by server)
|
|
|
|
|
self.lenfield_name = elt.get('name') + '_len'
|
|
|
|
|
self.lenfield_type = 'CARD32'
|
|
|
|
|
|
|
|
|
|
elif elt.tag == 'fieldref':
|
|
|
|
|
# Standard list with a fieldref
|
|
|
|
|
self.lenfield_name = elt.text
|
|
|
|
|
|
2014-09-08 02:29:06 +02:00
|
|
|
elif elt.tag == 'paramref':
|
|
|
|
|
self.lenfield_name = elt.text
|
|
|
|
|
self.lenfield_type = elt.get('type')
|
|
|
|
|
|
2008-04-17 19:46:16 -04:00
|
|
|
elif elt.tag == 'op':
|
|
|
|
|
# Op field. Need to recurse.
|
|
|
|
|
self.op = elt.get('op')
|
|
|
|
|
self.lhs = Expression(list(elt)[0], parent)
|
|
|
|
|
self.rhs = Expression(list(elt)[1], parent)
|
|
|
|
|
|
|
|
|
|
# Hopefully we don't have two separate length fields...
|
|
|
|
|
self.lenfield_name = self.lhs.lenfield_name
|
|
|
|
|
if self.lenfield_name == None:
|
|
|
|
|
self.lenfield_name = self.rhs.lenfield_name
|
|
|
|
|
|
2010-05-13 21:05:58 +00:00
|
|
|
elif elt.tag == 'unop':
|
|
|
|
|
# Op field. Need to recurse.
|
|
|
|
|
self.op = elt.get('op')
|
|
|
|
|
self.rhs = Expression(list(elt)[0], parent)
|
|
|
|
|
|
|
|
|
|
self.lenfield_name = self.rhs.lenfield_name
|
2025-08-02 13:28:41 -07:00
|
|
|
|
2008-04-17 19:46:16 -04:00
|
|
|
elif elt.tag == 'value':
|
|
|
|
|
# Constant expression
|
2010-05-14 04:24:27 +07:00
|
|
|
self.nmemb = int(elt.text, 0)
|
2008-04-17 19:46:16 -04:00
|
|
|
|
2010-07-13 07:58:24 +02:00
|
|
|
elif elt.tag == 'popcount':
|
|
|
|
|
self.op = 'popcount'
|
|
|
|
|
self.rhs = Expression(list(elt)[0], parent)
|
|
|
|
|
self.lenfield_name = self.rhs.lenfield_name
|
|
|
|
|
# xcb_popcount returns 'int' - handle the type in the language-specific part
|
|
|
|
|
|
|
|
|
|
elif elt.tag == 'enumref':
|
|
|
|
|
self.op = 'enumref'
|
|
|
|
|
self.lenfield_name = (elt.get('ref'), elt.text)
|
2025-08-02 13:28:41 -07:00
|
|
|
|
2010-07-13 07:58:24 +02:00
|
|
|
elif elt.tag == 'sumof':
|
|
|
|
|
self.op = 'sumof'
|
|
|
|
|
self.lenfield_name = elt.get('ref')
|
2014-11-02 13:47:29 +01:00
|
|
|
subexpressions = list(elt)
|
|
|
|
|
if len(subexpressions) > 0:
|
|
|
|
|
# sumof with a nested expression which is to be evaluated
|
|
|
|
|
# for each list-element in the context of that list-element.
|
|
|
|
|
# sumof then returns the sum of the results of these evaluations
|
|
|
|
|
self.rhs = Expression(subexpressions[0], parent)
|
2010-07-13 07:58:24 +02:00
|
|
|
|
2014-09-04 17:48:56 +02:00
|
|
|
elif elt.tag == 'listelement-ref':
|
|
|
|
|
# current list element inside iterating expressions such as sumof
|
|
|
|
|
self.op = 'listelement-ref'
|
|
|
|
|
self.contains_listelement_ref = True
|
|
|
|
|
|
2008-04-17 19:46:16 -04:00
|
|
|
else:
|
|
|
|
|
# Notreached
|
2010-05-22 18:04:53 +02:00
|
|
|
raise Exception("undefined tag '%s'" % elt.tag)
|
2008-04-17 19:46:16 -04:00
|
|
|
|
|
|
|
|
def fixed_size(self):
|
|
|
|
|
return self.nmemb != None
|
2010-07-13 07:58:24 +02:00
|
|
|
|
automatic alignment checker / manual offsets
The verification algorithm basically traverses the protocol-description
of a protocol entity by recursively processing all fields and their
types.
A start-align consists of two numbers:
* the alignment: This is a power of 2, and guarantees that the
address (or protocol position) minus the offset can be divided
by this number.
* the offset: how many bytes the current position is after a
position that can be divided by the alignment.
The algorithm starts with the start-alignment and computes the alignment
of each field from the start-align of the previous field as follows:
* if the previous field is a primitive type then the offset is increased
by the size of the primitive type module the alignment.
If the alignment or offset are incompatible with the primitive type,
then an error is reported.
* if the previous field is a complex type, then it is processed recursively.
* if the previous field is an alignment-pad, then the alignment is
adjusted accordingly, as to be expected by the alignment-pad.
Start-aligns can also be set manually with the xml-element
"required_start_align" which has two attributes: "align" and "offset"
If "offset" is omitted, it is assumed to 0.
All toplevel protocol entities default to 4-byte start-alignment with offset 0.
All non-toplevel complex entities, such as structs, switch, case, ...
do not have a default alignment.
If no alignment is explicitly specified for them, their alignment
is computed by their usage in other entities.
In that case, if they are used with aligments that violate the
alignment requirements of some of their fields, an error is issued.
If they are used with an alignment with non-zero offset,
a warning is issued, which recommends to specify the required_start_align
explicitly. (Reason: I don't want non-zero offsets to be silently
computed automatically. These non-zero offsets have to be reviewed
by a human and specified explicitely to record that this was reviewed
by a human)
If the required_start_align is explicitly specified for an entity
then an error will be issued if it is used with an aligment that's
incompatible with the explicitly specified alignment.
If an entity is used in different contexts with different start-aligns
then those start-aligns are combined to an align which is compatible
with all aligns.
E.g. (align 4, offset 0) and (align 4, offset 2) are combined
to (align 2, offset 0).
Error reports include the relevant context for a misalignment.
For non-toplevel entities this includes the entity-usage stack.
There is some complexity in the algorithm for reducing the size
of the error-reports to include only the relevant info.
This alignment verifier is also a prerequisite for getting
rid of implicit alignment altogether.
(This will then simplify the generated code and make it faster.)
Signed-off-by: Christian Linhart <chris@demorecorder.com>
2015-11-01 18:26:33 +01:00
|
|
|
def get_value(self):
|
|
|
|
|
return self.nmemb
|
|
|
|
|
|
|
|
|
|
# if the value of the expression is a guaranteed multiple of a number
|
|
|
|
|
# return this number, else return 1 (which is trivially guaranteed for integers)
|
|
|
|
|
def get_multiple(self):
|
|
|
|
|
multiple = 1
|
|
|
|
|
if self.op == '*':
|
|
|
|
|
if self.lhs.fixed_size():
|
|
|
|
|
multiple *= self.lhs.get_value()
|
|
|
|
|
if self.rhs.fixed_size():
|
|
|
|
|
multiple *= self.rhs.get_value()
|
|
|
|
|
|
|
|
|
|
return multiple
|
|
|
|
|
|
2014-09-04 17:48:56 +02:00
|
|
|
def recursive_resolve_tasks(self, module, parents):
|
|
|
|
|
for subexpr in (self.lhs, self.rhs):
|
|
|
|
|
if subexpr != None:
|
|
|
|
|
subexpr.recursive_resolve_tasks(module, parents)
|
|
|
|
|
self.contains_listelement_ref |= subexpr.contains_listelement_ref
|
|
|
|
|
|
2010-07-13 07:58:24 +02:00
|
|
|
def resolve(self, module, parents):
|
|
|
|
|
if self.op == 'enumref':
|
|
|
|
|
self.lenfield_type = module.get_type(self.lenfield_name[0])
|
|
|
|
|
self.lenfield_name = self.lenfield_name[1]
|
|
|
|
|
elif self.op == 'sumof':
|
|
|
|
|
# need to find the field with lenfield_name
|
2025-08-02 13:28:41 -07:00
|
|
|
for p in reversed(parents):
|
2010-07-13 07:58:24 +02:00
|
|
|
fields = dict([(f.field_name, f) for f in p.fields])
|
|
|
|
|
if self.lenfield_name in fields.keys():
|
2014-08-19 15:55:33 +02:00
|
|
|
if p.is_case_or_bitcase:
|
2025-08-02 13:28:41 -07:00
|
|
|
# switch is the anchestor
|
2010-07-15 01:06:49 +02:00
|
|
|
self.lenfield_parent = p.parents[-1]
|
|
|
|
|
else:
|
|
|
|
|
self.lenfield_parent = p
|
2010-07-13 07:58:24 +02:00
|
|
|
self.lenfield_type = fields[self.lenfield_name].field_type
|
2017-01-20 14:07:01 +01:00
|
|
|
self.lenfield = fields[self.lenfield_name]
|
2010-07-13 07:58:24 +02:00
|
|
|
break
|
2014-09-04 17:48:56 +02:00
|
|
|
|
|
|
|
|
self.recursive_resolve_tasks(module, parents)
|
2025-08-02 13:28:41 -07:00
|
|
|
|