137d9df22SJakub Kicinski# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 23aacf828SJakub Kicinski 33aacf828SJakub Kicinskiimport collections 45c6674f6SJakub Kicinskiimport importlib 53aacf828SJakub Kicinskiimport os 63aacf828SJakub Kicinskiimport yaml 73aacf828SJakub Kicinski 83aacf828SJakub Kicinski 95c6674f6SJakub Kicinski# To be loaded dynamically as needed 105c6674f6SJakub Kicinskijsonschema = None 115c6674f6SJakub Kicinski 125c6674f6SJakub Kicinski 133aacf828SJakub Kicinskiclass SpecElement: 143aacf828SJakub Kicinski """Netlink spec element. 153aacf828SJakub Kicinski 163aacf828SJakub Kicinski Abstract element of the Netlink spec. Implements the dictionary interface 173aacf828SJakub Kicinski for access to the raw spec. Supports iterative resolution of dependencies 183aacf828SJakub Kicinski across elements and class inheritance levels. The elements of the spec 193aacf828SJakub Kicinski may refer to each other, and although loops should be very rare, having 203aacf828SJakub Kicinski to maintain correct ordering of instantiation is painful, so the resolve() 213aacf828SJakub Kicinski method should be used to perform parts of init which require access to 223aacf828SJakub Kicinski other parts of the spec. 233aacf828SJakub Kicinski 243aacf828SJakub Kicinski Attributes: 253aacf828SJakub Kicinski yaml raw spec as loaded from the spec file 263aacf828SJakub Kicinski family back reference to the full family 273aacf828SJakub Kicinski 283aacf828SJakub Kicinski name name of the entity as listed in the spec (optional) 293aacf828SJakub Kicinski ident_name name which can be safely used as identifier in code (optional) 303aacf828SJakub Kicinski """ 313aacf828SJakub Kicinski def __init__(self, family, yaml): 323aacf828SJakub Kicinski self.yaml = yaml 333aacf828SJakub Kicinski self.family = family 343aacf828SJakub Kicinski 353aacf828SJakub Kicinski if 'name' in self.yaml: 363aacf828SJakub Kicinski self.name = self.yaml['name'] 373aacf828SJakub Kicinski self.ident_name = self.name.replace('-', '_') 383aacf828SJakub Kicinski 393aacf828SJakub Kicinski self._super_resolved = False 403aacf828SJakub Kicinski family.add_unresolved(self) 413aacf828SJakub Kicinski 423aacf828SJakub Kicinski def __getitem__(self, key): 433aacf828SJakub Kicinski return self.yaml[key] 443aacf828SJakub Kicinski 453aacf828SJakub Kicinski def __contains__(self, key): 463aacf828SJakub Kicinski return key in self.yaml 473aacf828SJakub Kicinski 483aacf828SJakub Kicinski def get(self, key, default=None): 493aacf828SJakub Kicinski return self.yaml.get(key, default) 503aacf828SJakub Kicinski 513aacf828SJakub Kicinski def resolve_up(self, up): 523aacf828SJakub Kicinski if not self._super_resolved: 533aacf828SJakub Kicinski up.resolve() 543aacf828SJakub Kicinski self._super_resolved = True 553aacf828SJakub Kicinski 563aacf828SJakub Kicinski def resolve(self): 573aacf828SJakub Kicinski pass 583aacf828SJakub Kicinski 593aacf828SJakub Kicinski 606517a60bSJakub Kicinskiclass SpecEnumEntry(SpecElement): 616517a60bSJakub Kicinski """ Entry within an enum declared in the Netlink spec. 626517a60bSJakub Kicinski 636517a60bSJakub Kicinski Attributes: 646517a60bSJakub Kicinski doc documentation string 656517a60bSJakub Kicinski enum_set back reference to the enum 666517a60bSJakub Kicinski value numerical value of this enum (use accessors in most situations!) 676517a60bSJakub Kicinski 686517a60bSJakub Kicinski Methods: 696517a60bSJakub Kicinski raw_value raw value, i.e. the id in the enum, unlike user value which is a mask for flags 706517a60bSJakub Kicinski user_value user value, same as raw value for enums, for flags it's the mask 716517a60bSJakub Kicinski """ 726517a60bSJakub Kicinski def __init__(self, enum_set, yaml, prev, value_start): 736517a60bSJakub Kicinski if isinstance(yaml, str): 746517a60bSJakub Kicinski yaml = {'name': yaml} 756517a60bSJakub Kicinski super().__init__(enum_set.family, yaml) 766517a60bSJakub Kicinski 776517a60bSJakub Kicinski self.doc = yaml.get('doc', '') 786517a60bSJakub Kicinski self.enum_set = enum_set 796517a60bSJakub Kicinski 806517a60bSJakub Kicinski if 'value' in yaml: 816517a60bSJakub Kicinski self.value = yaml['value'] 826517a60bSJakub Kicinski elif prev: 836517a60bSJakub Kicinski self.value = prev.value + 1 846517a60bSJakub Kicinski else: 856517a60bSJakub Kicinski self.value = value_start 866517a60bSJakub Kicinski 876517a60bSJakub Kicinski def has_doc(self): 886517a60bSJakub Kicinski return bool(self.doc) 896517a60bSJakub Kicinski 906517a60bSJakub Kicinski def raw_value(self): 916517a60bSJakub Kicinski return self.value 926517a60bSJakub Kicinski 934c6170d1SJakub Kicinski def user_value(self, as_flags=None): 944c6170d1SJakub Kicinski if self.enum_set['type'] == 'flags' or as_flags: 956517a60bSJakub Kicinski return 1 << self.value 966517a60bSJakub Kicinski else: 976517a60bSJakub Kicinski return self.value 986517a60bSJakub Kicinski 996517a60bSJakub Kicinski 1006517a60bSJakub Kicinskiclass SpecEnumSet(SpecElement): 1016517a60bSJakub Kicinski """ Enum type 1026517a60bSJakub Kicinski 1036517a60bSJakub Kicinski Represents an enumeration (list of numerical constants) 1046517a60bSJakub Kicinski as declared in the "definitions" section of the spec. 1056517a60bSJakub Kicinski 1066517a60bSJakub Kicinski Attributes: 1076517a60bSJakub Kicinski type enum or flags 1086517a60bSJakub Kicinski entries entries by name 109c311aaa7SJakub Kicinski entries_by_val entries by value 1106517a60bSJakub Kicinski Methods: 1116517a60bSJakub Kicinski get_mask for flags compute the mask of all defined values 1126517a60bSJakub Kicinski """ 1136517a60bSJakub Kicinski def __init__(self, family, yaml): 1146517a60bSJakub Kicinski super().__init__(family, yaml) 1156517a60bSJakub Kicinski 1166517a60bSJakub Kicinski self.type = yaml['type'] 1176517a60bSJakub Kicinski 1186517a60bSJakub Kicinski prev_entry = None 1196517a60bSJakub Kicinski value_start = self.yaml.get('value-start', 0) 1206517a60bSJakub Kicinski self.entries = dict() 121c311aaa7SJakub Kicinski self.entries_by_val = dict() 1226517a60bSJakub Kicinski for entry in self.yaml['entries']: 1236517a60bSJakub Kicinski e = self.new_entry(entry, prev_entry, value_start) 1246517a60bSJakub Kicinski self.entries[e.name] = e 125c311aaa7SJakub Kicinski self.entries_by_val[e.raw_value()] = e 1266517a60bSJakub Kicinski prev_entry = e 1276517a60bSJakub Kicinski 1286517a60bSJakub Kicinski def new_entry(self, entry, prev_entry, value_start): 1296517a60bSJakub Kicinski return SpecEnumEntry(self, entry, prev_entry, value_start) 1306517a60bSJakub Kicinski 1316517a60bSJakub Kicinski def has_doc(self): 1326517a60bSJakub Kicinski if 'doc' in self.yaml: 1336517a60bSJakub Kicinski return True 1346517a60bSJakub Kicinski for entry in self.entries.values(): 1356517a60bSJakub Kicinski if entry.has_doc(): 1366517a60bSJakub Kicinski return True 1376517a60bSJakub Kicinski return False 1386517a60bSJakub Kicinski 1394c6170d1SJakub Kicinski def get_mask(self, as_flags=None): 1406517a60bSJakub Kicinski mask = 0 141bf51d277SLorenzo Bianconi for e in self.entries.values(): 1424c6170d1SJakub Kicinski mask += e.user_value(as_flags) 1436517a60bSJakub Kicinski return mask 1446517a60bSJakub Kicinski 1456517a60bSJakub Kicinski 1463aacf828SJakub Kicinskiclass SpecAttr(SpecElement): 1477b4434a8SAlessandro Marcolini """ Single Netlink attribute type 1483aacf828SJakub Kicinski 1493aacf828SJakub Kicinski Represents a single attribute type within an attr space. 1503aacf828SJakub Kicinski 1513aacf828SJakub Kicinski Attributes: 1527d4caf54SJakub Kicinski type string, attribute type 1533aacf828SJakub Kicinski value numerical ID when serialized 1543aacf828SJakub Kicinski attr_set Attribute Set containing this attr 155b423c3c8SDonald Hunter is_multi bool, attr may repeat multiple times 15626071913SDonald Hunter struct_name string, name of struct definition 157b423c3c8SDonald Hunter sub_type string, name of sub type 158d8eea68dSDonald Hunter len integer, optional byte length of binary types 159d8eea68dSDonald Hunter display_hint string, hint to help choose format specifier 160d8eea68dSDonald Hunter when displaying the value 1611769e2beSDonald Hunter sub_message string, name of sub message type 1621769e2beSDonald Hunter selector string, name of attribute used to select 1631769e2beSDonald Hunter sub-message type 1647d4caf54SJakub Kicinski 1657d4caf54SJakub Kicinski is_auto_scalar bool, attr is a variable-size scalar 1663aacf828SJakub Kicinski """ 1673aacf828SJakub Kicinski def __init__(self, family, attr_set, yaml, value): 1683aacf828SJakub Kicinski super().__init__(family, yaml) 1693aacf828SJakub Kicinski 1707d4caf54SJakub Kicinski self.type = yaml['type'] 1713aacf828SJakub Kicinski self.value = value 1723aacf828SJakub Kicinski self.attr_set = attr_set 1733aacf828SJakub Kicinski self.is_multi = yaml.get('multi-attr', False) 17426071913SDonald Hunter self.struct_name = yaml.get('struct') 175b423c3c8SDonald Hunter self.sub_type = yaml.get('sub-type') 1769f7cc57fSStanislav Fomichev self.byte_order = yaml.get('byte-order') 177d8eea68dSDonald Hunter self.len = yaml.get('len') 178d8eea68dSDonald Hunter self.display_hint = yaml.get('display-hint') 1791769e2beSDonald Hunter self.sub_message = yaml.get('sub-message') 1801769e2beSDonald Hunter self.selector = yaml.get('selector') 1813aacf828SJakub Kicinski 1827d4caf54SJakub Kicinski self.is_auto_scalar = self.type == "sint" or self.type == "uint" 1837d4caf54SJakub Kicinski 1843aacf828SJakub Kicinski 1853aacf828SJakub Kicinskiclass SpecAttrSet(SpecElement): 1863aacf828SJakub Kicinski """ Netlink Attribute Set class. 1873aacf828SJakub Kicinski 1883aacf828SJakub Kicinski Represents a ID space of attributes within Netlink. 1893aacf828SJakub Kicinski 1903aacf828SJakub Kicinski Note that unlike other elements, which expose contents of the raw spec 1913aacf828SJakub Kicinski via the dictionary interface Attribute Set exposes attributes by name. 1923aacf828SJakub Kicinski 1933aacf828SJakub Kicinski Attributes: 1943aacf828SJakub Kicinski attrs ordered dict of all attributes (indexed by name) 1953aacf828SJakub Kicinski attrs_by_val ordered dict of all attributes (indexed by value) 1963aacf828SJakub Kicinski subset_of parent set if this is a subset, otherwise None 1973aacf828SJakub Kicinski """ 1983aacf828SJakub Kicinski def __init__(self, family, yaml): 1993aacf828SJakub Kicinski super().__init__(family, yaml) 2003aacf828SJakub Kicinski 2013aacf828SJakub Kicinski self.subset_of = self.yaml.get('subset-of', None) 2023aacf828SJakub Kicinski 2033aacf828SJakub Kicinski self.attrs = collections.OrderedDict() 2043aacf828SJakub Kicinski self.attrs_by_val = collections.OrderedDict() 2053aacf828SJakub Kicinski 2067cf93538SJakub Kicinski if self.subset_of is None: 207ad4fafcdSJakub Kicinski val = 1 2083aacf828SJakub Kicinski for elem in self.yaml['attributes']: 2093aacf828SJakub Kicinski if 'value' in elem: 2103aacf828SJakub Kicinski val = elem['value'] 2113aacf828SJakub Kicinski 2123aacf828SJakub Kicinski attr = self.new_attr(elem, val) 2133aacf828SJakub Kicinski self.attrs[attr.name] = attr 2143aacf828SJakub Kicinski self.attrs_by_val[attr.value] = attr 2153aacf828SJakub Kicinski val += 1 2167cf93538SJakub Kicinski else: 2177cf93538SJakub Kicinski real_set = family.attr_sets[self.subset_of] 2187cf93538SJakub Kicinski for elem in self.yaml['attributes']: 2197cf93538SJakub Kicinski attr = real_set[elem['name']] 2207cf93538SJakub Kicinski self.attrs[attr.name] = attr 2217cf93538SJakub Kicinski self.attrs_by_val[attr.value] = attr 2223aacf828SJakub Kicinski 2233aacf828SJakub Kicinski def new_attr(self, elem, value): 2243aacf828SJakub Kicinski return SpecAttr(self.family, self, elem, value) 2253aacf828SJakub Kicinski 2263aacf828SJakub Kicinski def __getitem__(self, key): 2273aacf828SJakub Kicinski return self.attrs[key] 2283aacf828SJakub Kicinski 2293aacf828SJakub Kicinski def __contains__(self, key): 2303aacf828SJakub Kicinski return key in self.attrs 2313aacf828SJakub Kicinski 2323aacf828SJakub Kicinski def __iter__(self): 2333aacf828SJakub Kicinski yield from self.attrs 2343aacf828SJakub Kicinski 2353aacf828SJakub Kicinski def items(self): 2363aacf828SJakub Kicinski return self.attrs.items() 2373aacf828SJakub Kicinski 2383aacf828SJakub Kicinski 239bec0b7a2SDonald Hunterclass SpecStructMember(SpecElement): 240bec0b7a2SDonald Hunter """Struct member attribute 241bec0b7a2SDonald Hunter 242bec0b7a2SDonald Hunter Represents a single struct member attribute. 243bec0b7a2SDonald Hunter 244bec0b7a2SDonald Hunter Attributes: 245bec0b7a2SDonald Hunter type string, type of the member attribute 246bddd2e56SDonald Hunter byte_order string or None for native byte order 247313a7a80SDonald Hunter enum string, name of the enum definition 248d8eea68dSDonald Hunter len integer, optional byte length of binary types 249d8eea68dSDonald Hunter display_hint string, hint to help choose format specifier 250d8eea68dSDonald Hunter when displaying the value 251bf08f32cSDonald Hunter struct string, name of nested struct type 252bec0b7a2SDonald Hunter """ 253bec0b7a2SDonald Hunter def __init__(self, family, yaml): 254bec0b7a2SDonald Hunter super().__init__(family, yaml) 255bec0b7a2SDonald Hunter self.type = yaml['type'] 256bddd2e56SDonald Hunter self.byte_order = yaml.get('byte-order') 257313a7a80SDonald Hunter self.enum = yaml.get('enum') 258d8eea68dSDonald Hunter self.len = yaml.get('len') 259d8eea68dSDonald Hunter self.display_hint = yaml.get('display-hint') 260bf08f32cSDonald Hunter self.struct = yaml.get('struct') 261bec0b7a2SDonald Hunter 262bec0b7a2SDonald Hunter 263bec0b7a2SDonald Hunterclass SpecStruct(SpecElement): 264bec0b7a2SDonald Hunter """Netlink struct type 265bec0b7a2SDonald Hunter 266bec0b7a2SDonald Hunter Represents a C struct definition. 267bec0b7a2SDonald Hunter 268bec0b7a2SDonald Hunter Attributes: 269bec0b7a2SDonald Hunter members ordered list of struct members 270bec0b7a2SDonald Hunter """ 271bec0b7a2SDonald Hunter def __init__(self, family, yaml): 272bec0b7a2SDonald Hunter super().__init__(family, yaml) 273bec0b7a2SDonald Hunter 274bec0b7a2SDonald Hunter self.members = [] 275bec0b7a2SDonald Hunter for member in yaml.get('members', []): 276bec0b7a2SDonald Hunter self.members.append(self.new_member(family, member)) 277bec0b7a2SDonald Hunter 278bec0b7a2SDonald Hunter def new_member(self, family, elem): 279bec0b7a2SDonald Hunter return SpecStructMember(family, elem) 280bec0b7a2SDonald Hunter 281bec0b7a2SDonald Hunter def __iter__(self): 282bec0b7a2SDonald Hunter yield from self.members 283bec0b7a2SDonald Hunter 284bec0b7a2SDonald Hunter def items(self): 285bec0b7a2SDonald Hunter return self.members.items() 286bec0b7a2SDonald Hunter 287bec0b7a2SDonald Hunter 2881769e2beSDonald Hunterclass SpecSubMessage(SpecElement): 2891769e2beSDonald Hunter """ Netlink sub-message definition 2901769e2beSDonald Hunter 2911769e2beSDonald Hunter Represents a set of sub-message formats for polymorphic nlattrs 2921769e2beSDonald Hunter that contain type-specific sub messages. 2931769e2beSDonald Hunter 2941769e2beSDonald Hunter Attributes: 2951769e2beSDonald Hunter name string, name of sub-message definition 2961769e2beSDonald Hunter formats dict of sub-message formats indexed by match value 2971769e2beSDonald Hunter """ 2981769e2beSDonald Hunter def __init__(self, family, yaml): 2991769e2beSDonald Hunter super().__init__(family, yaml) 3001769e2beSDonald Hunter 3011769e2beSDonald Hunter self.formats = collections.OrderedDict() 3021769e2beSDonald Hunter for elem in self.yaml['formats']: 3031769e2beSDonald Hunter format = self.new_format(family, elem) 3041769e2beSDonald Hunter self.formats[format.value] = format 3051769e2beSDonald Hunter 3061769e2beSDonald Hunter def new_format(self, family, format): 3071769e2beSDonald Hunter return SpecSubMessageFormat(family, format) 3081769e2beSDonald Hunter 3091769e2beSDonald Hunter 3101769e2beSDonald Hunterclass SpecSubMessageFormat(SpecElement): 3117b4434a8SAlessandro Marcolini """ Netlink sub-message format definition 3121769e2beSDonald Hunter 3137b4434a8SAlessandro Marcolini Represents a single format for a sub-message. 3141769e2beSDonald Hunter 3151769e2beSDonald Hunter Attributes: 3161769e2beSDonald Hunter value attribute value to match against type selector 3171769e2beSDonald Hunter fixed_header string, name of fixed header, or None 3181769e2beSDonald Hunter attr_set string, name of attribute set, or None 3191769e2beSDonald Hunter """ 3201769e2beSDonald Hunter def __init__(self, family, yaml): 3211769e2beSDonald Hunter super().__init__(family, yaml) 3221769e2beSDonald Hunter 3231769e2beSDonald Hunter self.value = yaml.get('value') 3241769e2beSDonald Hunter self.fixed_header = yaml.get('fixed-header') 3251769e2beSDonald Hunter self.attr_set = yaml.get('attribute-set') 3261769e2beSDonald Hunter 3271769e2beSDonald Hunter 3283aacf828SJakub Kicinskiclass SpecOperation(SpecElement): 3293aacf828SJakub Kicinski """Netlink Operation 3303aacf828SJakub Kicinski 3313aacf828SJakub Kicinski Information about a single Netlink operation. 3323aacf828SJakub Kicinski 3333aacf828SJakub Kicinski Attributes: 3343aacf828SJakub Kicinski value numerical ID when serialized, None if req/rsp values differ 3353aacf828SJakub Kicinski 3363aacf828SJakub Kicinski req_value numerical ID when serialized, user -> kernel 3373aacf828SJakub Kicinski rsp_value numerical ID when serialized, user <- kernel 338*3e51f2cbSJakub Kicinski modes supported operation modes (do, dump, event etc.) 3393aacf828SJakub Kicinski is_call bool, whether the operation is a call 3403aacf828SJakub Kicinski is_async bool, whether the operation is a notification 3413aacf828SJakub Kicinski is_resv bool, whether the operation does not exist (it's just a reserved ID) 3423aacf828SJakub Kicinski attr_set attribute set name 343f036d936SDonald Hunter fixed_header string, optional name of fixed header struct 3443aacf828SJakub Kicinski 3453aacf828SJakub Kicinski yaml raw spec as loaded from the spec file 3463aacf828SJakub Kicinski """ 3473aacf828SJakub Kicinski def __init__(self, family, yaml, req_value, rsp_value): 3483aacf828SJakub Kicinski super().__init__(family, yaml) 3493aacf828SJakub Kicinski 3503aacf828SJakub Kicinski self.value = req_value if req_value == rsp_value else None 3513aacf828SJakub Kicinski self.req_value = req_value 3523aacf828SJakub Kicinski self.rsp_value = rsp_value 3533aacf828SJakub Kicinski 354*3e51f2cbSJakub Kicinski self.modes = yaml.keys() & {'do', 'dump', 'event', 'notify'} 3553aacf828SJakub Kicinski self.is_call = 'do' in yaml or 'dump' in yaml 3563aacf828SJakub Kicinski self.is_async = 'notify' in yaml or 'event' in yaml 3573aacf828SJakub Kicinski self.is_resv = not self.is_async and not self.is_call 358f036d936SDonald Hunter self.fixed_header = self.yaml.get('fixed-header', family.fixed_header) 3593aacf828SJakub Kicinski 3603aacf828SJakub Kicinski # Added by resolve: 3613aacf828SJakub Kicinski self.attr_set = None 3623aacf828SJakub Kicinski delattr(self, "attr_set") 3633aacf828SJakub Kicinski 3643aacf828SJakub Kicinski def resolve(self): 3653aacf828SJakub Kicinski self.resolve_up(super()) 3663aacf828SJakub Kicinski 3673aacf828SJakub Kicinski if 'attribute-set' in self.yaml: 3683aacf828SJakub Kicinski attr_set_name = self.yaml['attribute-set'] 3693aacf828SJakub Kicinski elif 'notify' in self.yaml: 3703aacf828SJakub Kicinski msg = self.family.msgs[self.yaml['notify']] 3713aacf828SJakub Kicinski attr_set_name = msg['attribute-set'] 3723aacf828SJakub Kicinski elif self.is_resv: 3733aacf828SJakub Kicinski attr_set_name = '' 3743aacf828SJakub Kicinski else: 3753aacf828SJakub Kicinski raise Exception(f"Can't resolve attribute set for op '{self.name}'") 3763aacf828SJakub Kicinski if attr_set_name: 3773aacf828SJakub Kicinski self.attr_set = self.family.attr_sets[attr_set_name] 3783aacf828SJakub Kicinski 3793aacf828SJakub Kicinski 38088901b96SDonald Hunterclass SpecMcastGroup(SpecElement): 38188901b96SDonald Hunter """Netlink Multicast Group 38288901b96SDonald Hunter 38388901b96SDonald Hunter Information about a multicast group. 38488901b96SDonald Hunter 38588901b96SDonald Hunter Value is only used for classic netlink families that use the 38688901b96SDonald Hunter netlink-raw schema. Genetlink families use dynamic ID allocation 38788901b96SDonald Hunter where the ids of multicast groups get resolved at runtime. Value 38888901b96SDonald Hunter will be None for genetlink families. 38988901b96SDonald Hunter 39088901b96SDonald Hunter Attributes: 39188901b96SDonald Hunter name name of the mulitcast group 39288901b96SDonald Hunter value integer id of this multicast group for netlink-raw or None 39388901b96SDonald Hunter yaml raw spec as loaded from the spec file 39488901b96SDonald Hunter """ 39588901b96SDonald Hunter def __init__(self, family, yaml): 39688901b96SDonald Hunter super().__init__(family, yaml) 39788901b96SDonald Hunter self.value = self.yaml.get('value') 39888901b96SDonald Hunter 39988901b96SDonald Hunter 4003aacf828SJakub Kicinskiclass SpecFamily(SpecElement): 4013aacf828SJakub Kicinski """ Netlink Family Spec class. 4023aacf828SJakub Kicinski 4033aacf828SJakub Kicinski Netlink family information loaded from a spec (e.g. in YAML). 4043aacf828SJakub Kicinski Takes care of unfolding implicit information which can be skipped 4053aacf828SJakub Kicinski in the spec itself for brevity. 4063aacf828SJakub Kicinski 4073aacf828SJakub Kicinski The class can be used like a dictionary to access the raw spec 4083aacf828SJakub Kicinski elements but that's usually a bad idea. 4093aacf828SJakub Kicinski 4103aacf828SJakub Kicinski Attributes: 4113aacf828SJakub Kicinski proto protocol type (e.g. genetlink) 412ff6db4b5SJakub Kicinski msg_id_model enum-model for operations (unified, directional etc.) 413cfab77c0SJakub Kicinski license spec license (loaded from an SPDX tag on the spec) 4143aacf828SJakub Kicinski 4153aacf828SJakub Kicinski attr_sets dict of attribute sets 4163aacf828SJakub Kicinski msgs dict of all messages (index by name) 4171769e2beSDonald Hunter sub_msgs dict of all sub messages (index by name) 4183aacf828SJakub Kicinski ops dict of all valid requests / responses 419ced15688SJakub Kicinski ntfs dict of all async events 4206517a60bSJakub Kicinski consts dict of all constants/enums 421f036d936SDonald Hunter fixed_header string, optional name of family default fixed header struct 42288901b96SDonald Hunter mcast_groups dict of all multicast groups (index by name) 423ba980f8dSJakub Kicinski kernel_family dict of kernel family attributes 4243aacf828SJakub Kicinski """ 425008bcd68SJakub Kicinski def __init__(self, spec_path, schema_path=None, exclude_ops=None): 4263aacf828SJakub Kicinski with open(spec_path, "r") as stream: 427cfab77c0SJakub Kicinski prefix = '# SPDX-License-Identifier: ' 428cfab77c0SJakub Kicinski first = stream.readline().strip() 429cfab77c0SJakub Kicinski if not first.startswith(prefix): 430cfab77c0SJakub Kicinski raise Exception('SPDX license tag required in the spec') 431cfab77c0SJakub Kicinski self.license = first[len(prefix):] 432cfab77c0SJakub Kicinski 433cfab77c0SJakub Kicinski stream.seek(0) 4343aacf828SJakub Kicinski spec = yaml.safe_load(stream) 4353aacf828SJakub Kicinski 4363aacf828SJakub Kicinski self._resolution_list = [] 4373aacf828SJakub Kicinski 4383aacf828SJakub Kicinski super().__init__(self, spec) 4393aacf828SJakub Kicinski 440008bcd68SJakub Kicinski self._exclude_ops = exclude_ops if exclude_ops else [] 441008bcd68SJakub Kicinski 4423aacf828SJakub Kicinski self.proto = self.yaml.get('protocol', 'genetlink') 443ff6db4b5SJakub Kicinski self.msg_id_model = self.yaml['operations'].get('enum-model', 'unified') 4443aacf828SJakub Kicinski 4453aacf828SJakub Kicinski if schema_path is None: 4463aacf828SJakub Kicinski schema_path = os.path.dirname(os.path.dirname(spec_path)) + f'/{self.proto}.yaml' 4473aacf828SJakub Kicinski if schema_path: 4485c6674f6SJakub Kicinski global jsonschema 4495c6674f6SJakub Kicinski 4503aacf828SJakub Kicinski with open(schema_path, "r") as stream: 4513aacf828SJakub Kicinski schema = yaml.safe_load(stream) 4523aacf828SJakub Kicinski 4535c6674f6SJakub Kicinski if jsonschema is None: 4545c6674f6SJakub Kicinski jsonschema = importlib.import_module("jsonschema") 4555c6674f6SJakub Kicinski 4563aacf828SJakub Kicinski jsonschema.validate(self.yaml, schema) 4573aacf828SJakub Kicinski 4583aacf828SJakub Kicinski self.attr_sets = collections.OrderedDict() 4591769e2beSDonald Hunter self.sub_msgs = collections.OrderedDict() 4603aacf828SJakub Kicinski self.msgs = collections.OrderedDict() 4613aacf828SJakub Kicinski self.req_by_value = collections.OrderedDict() 4623aacf828SJakub Kicinski self.rsp_by_value = collections.OrderedDict() 4633aacf828SJakub Kicinski self.ops = collections.OrderedDict() 464ced15688SJakub Kicinski self.ntfs = collections.OrderedDict() 4656517a60bSJakub Kicinski self.consts = collections.OrderedDict() 46688901b96SDonald Hunter self.mcast_groups = collections.OrderedDict() 467ba980f8dSJakub Kicinski self.kernel_family = collections.OrderedDict(self.yaml.get('kernel-family', {})) 4683aacf828SJakub Kicinski 4693aacf828SJakub Kicinski last_exception = None 4703aacf828SJakub Kicinski while len(self._resolution_list) > 0: 4713aacf828SJakub Kicinski resolved = [] 4723aacf828SJakub Kicinski unresolved = self._resolution_list 4733aacf828SJakub Kicinski self._resolution_list = [] 4743aacf828SJakub Kicinski 4753aacf828SJakub Kicinski for elem in unresolved: 4763aacf828SJakub Kicinski try: 4773aacf828SJakub Kicinski elem.resolve() 4783aacf828SJakub Kicinski except (KeyError, AttributeError) as e: 4793aacf828SJakub Kicinski self._resolution_list.append(elem) 4803aacf828SJakub Kicinski last_exception = e 4813aacf828SJakub Kicinski continue 4823aacf828SJakub Kicinski 4833aacf828SJakub Kicinski resolved.append(elem) 4843aacf828SJakub Kicinski 4853aacf828SJakub Kicinski if len(resolved) == 0: 486b9d3a3e4SJakub Kicinski raise last_exception 4873aacf828SJakub Kicinski 4886517a60bSJakub Kicinski def new_enum(self, elem): 4896517a60bSJakub Kicinski return SpecEnumSet(self, elem) 4906517a60bSJakub Kicinski 4913aacf828SJakub Kicinski def new_attr_set(self, elem): 4923aacf828SJakub Kicinski return SpecAttrSet(self, elem) 4933aacf828SJakub Kicinski 494bec0b7a2SDonald Hunter def new_struct(self, elem): 495bec0b7a2SDonald Hunter return SpecStruct(self, elem) 496bec0b7a2SDonald Hunter 4971769e2beSDonald Hunter def new_sub_message(self, elem): 4981769e2beSDonald Hunter return SpecSubMessage(self, elem); 4991769e2beSDonald Hunter 5003aacf828SJakub Kicinski def new_operation(self, elem, req_val, rsp_val): 5013aacf828SJakub Kicinski return SpecOperation(self, elem, req_val, rsp_val) 5023aacf828SJakub Kicinski 50388901b96SDonald Hunter def new_mcast_group(self, elem): 50488901b96SDonald Hunter return SpecMcastGroup(self, elem) 50588901b96SDonald Hunter 5063aacf828SJakub Kicinski def add_unresolved(self, elem): 5073aacf828SJakub Kicinski self._resolution_list.append(elem) 5083aacf828SJakub Kicinski 5093aacf828SJakub Kicinski def _dictify_ops_unified(self): 510f036d936SDonald Hunter self.fixed_header = self.yaml['operations'].get('fixed-header') 511ad4fafcdSJakub Kicinski val = 1 5123aacf828SJakub Kicinski for elem in self.yaml['operations']['list']: 5133aacf828SJakub Kicinski if 'value' in elem: 5143aacf828SJakub Kicinski val = elem['value'] 5153aacf828SJakub Kicinski 5163aacf828SJakub Kicinski op = self.new_operation(elem, val, val) 5173aacf828SJakub Kicinski val += 1 5183aacf828SJakub Kicinski 5193aacf828SJakub Kicinski self.msgs[op.name] = op 5203aacf828SJakub Kicinski 5213aacf828SJakub Kicinski def _dictify_ops_directional(self): 522f036d936SDonald Hunter self.fixed_header = self.yaml['operations'].get('fixed-header') 523ad4fafcdSJakub Kicinski req_val = rsp_val = 1 5243aacf828SJakub Kicinski for elem in self.yaml['operations']['list']: 5256da3424fSJakub Kicinski if 'notify' in elem or 'event' in elem: 5263aacf828SJakub Kicinski if 'value' in elem: 5273aacf828SJakub Kicinski rsp_val = elem['value'] 5283aacf828SJakub Kicinski req_val_next = req_val 5293aacf828SJakub Kicinski rsp_val_next = rsp_val + 1 5303aacf828SJakub Kicinski req_val = None 5313aacf828SJakub Kicinski elif 'do' in elem or 'dump' in elem: 5323aacf828SJakub Kicinski mode = elem['do'] if 'do' in elem else elem['dump'] 5333aacf828SJakub Kicinski 5343aacf828SJakub Kicinski v = mode.get('request', {}).get('value', None) 5353aacf828SJakub Kicinski if v: 5363aacf828SJakub Kicinski req_val = v 5373aacf828SJakub Kicinski v = mode.get('reply', {}).get('value', None) 5383aacf828SJakub Kicinski if v: 5393aacf828SJakub Kicinski rsp_val = v 5403aacf828SJakub Kicinski 5413aacf828SJakub Kicinski rsp_inc = 1 if 'reply' in mode else 0 5423aacf828SJakub Kicinski req_val_next = req_val + 1 5433aacf828SJakub Kicinski rsp_val_next = rsp_val + rsp_inc 5443aacf828SJakub Kicinski else: 5453aacf828SJakub Kicinski raise Exception("Can't parse directional ops") 5463aacf828SJakub Kicinski 5479858bfc2SJakub Kicinski if req_val == req_val_next: 5489858bfc2SJakub Kicinski req_val = None 5499858bfc2SJakub Kicinski if rsp_val == rsp_val_next: 5509858bfc2SJakub Kicinski rsp_val = None 551008bcd68SJakub Kicinski 552008bcd68SJakub Kicinski skip = False 553008bcd68SJakub Kicinski for exclude in self._exclude_ops: 554008bcd68SJakub Kicinski skip |= bool(exclude.match(elem['name'])) 555008bcd68SJakub Kicinski if not skip: 5563aacf828SJakub Kicinski op = self.new_operation(elem, req_val, rsp_val) 557008bcd68SJakub Kicinski 5583aacf828SJakub Kicinski req_val = req_val_next 5593aacf828SJakub Kicinski rsp_val = rsp_val_next 5603aacf828SJakub Kicinski 5613aacf828SJakub Kicinski self.msgs[op.name] = op 5623aacf828SJakub Kicinski 563f3d07b02SStanislav Fomichev def find_operation(self, name): 564f3d07b02SStanislav Fomichev """ 565f3d07b02SStanislav Fomichev For a given operation name, find and return operation spec. 566f3d07b02SStanislav Fomichev """ 567f3d07b02SStanislav Fomichev for op in self.yaml['operations']['list']: 568f3d07b02SStanislav Fomichev if name == op['name']: 569f3d07b02SStanislav Fomichev return op 570f3d07b02SStanislav Fomichev return None 571f3d07b02SStanislav Fomichev 5723aacf828SJakub Kicinski def resolve(self): 5733aacf828SJakub Kicinski self.resolve_up(super()) 5743aacf828SJakub Kicinski 575054abb51SJakub Kicinski definitions = self.yaml.get('definitions', []) 576054abb51SJakub Kicinski for elem in definitions: 5776517a60bSJakub Kicinski if elem['type'] == 'enum' or elem['type'] == 'flags': 5786517a60bSJakub Kicinski self.consts[elem['name']] = self.new_enum(elem) 579bec0b7a2SDonald Hunter elif elem['type'] == 'struct': 580bec0b7a2SDonald Hunter self.consts[elem['name']] = self.new_struct(elem) 5816517a60bSJakub Kicinski else: 5826517a60bSJakub Kicinski self.consts[elem['name']] = elem 5836517a60bSJakub Kicinski 5843aacf828SJakub Kicinski for elem in self.yaml['attribute-sets']: 5853aacf828SJakub Kicinski attr_set = self.new_attr_set(elem) 5863aacf828SJakub Kicinski self.attr_sets[elem['name']] = attr_set 5873aacf828SJakub Kicinski 5881769e2beSDonald Hunter for elem in self.yaml.get('sub-messages', []): 5891769e2beSDonald Hunter sub_message = self.new_sub_message(elem) 5901769e2beSDonald Hunter self.sub_msgs[sub_message.name] = sub_message 5911769e2beSDonald Hunter 592ff6db4b5SJakub Kicinski if self.msg_id_model == 'unified': 5933aacf828SJakub Kicinski self._dictify_ops_unified() 594ff6db4b5SJakub Kicinski elif self.msg_id_model == 'directional': 5953aacf828SJakub Kicinski self._dictify_ops_directional() 5963aacf828SJakub Kicinski 5973aacf828SJakub Kicinski for op in self.msgs.values(): 5983aacf828SJakub Kicinski if op.req_value is not None: 5993aacf828SJakub Kicinski self.req_by_value[op.req_value] = op 6003aacf828SJakub Kicinski if op.rsp_value is not None: 6013aacf828SJakub Kicinski self.rsp_by_value[op.rsp_value] = op 6023aacf828SJakub Kicinski if not op.is_async and 'attribute-set' in op: 6033aacf828SJakub Kicinski self.ops[op.name] = op 604ced15688SJakub Kicinski elif op.is_async: 605ced15688SJakub Kicinski self.ntfs[op.name] = op 60688901b96SDonald Hunter 60788901b96SDonald Hunter mcgs = self.yaml.get('mcast-groups') 60888901b96SDonald Hunter if mcgs: 60988901b96SDonald Hunter for elem in mcgs['list']: 61088901b96SDonald Hunter mcg = self.new_mcast_group(elem) 61188901b96SDonald Hunter self.mcast_groups[elem['name']] = mcg 612