1# 2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3# 4# SPDX-License-Identifier: BSD-2-Clause 5# 6 7""" 8Definitions of kernel objects. 9""" 10 11from __future__ import absolute_import, division, print_function, \ 12 unicode_literals 13 14import abc 15import math 16import six 17from functools import total_ordering 18 19from aenum import Enum, Flag, unique, auto, IntEnum 20import logging 21 22# dict of all object sizes, must be registered before using any Object in this file. 23# this dict is indexed by strings matching ObjectType.name 24object_sizes = {} 25 26 27def register_object_sizes(sizes): 28 """Register the object sizes to be used when creating objects in this class""" 29 global object_sizes 30 object_sizes = sizes 31 32 33def get_libsel4_constant(name): 34 global object_sizes 35 try: 36 return object_sizes[name] 37 except KeyError: 38 if not object_sizes: 39 logging.fatal("No object sizes registered!") 40 logging.fatal("No value for key {}".format(name)) 41 42 43def get_object_size_bits(object_type): 44 global object_sizes 45 try: 46 return object_sizes[object_type.name] 47 except KeyError: 48 if not object_sizes: 49 logging.fatal("No object sizes registered!") 50 logging.fatal("No size for object {}".format(object_type.name)) 51 52 53def get_object_size(object_type): 54 size = get_object_size_bits(object_type) 55 if not size: 56 return 0 57 return 1 << size 58 59 60@unique 61class ObjectType(Enum): 62 seL4_UntypedObject = auto() 63 seL4_TCBObject = auto() 64 seL4_EndpointObject = auto() 65 seL4_NotificationObject = auto() 66 seL4_CapTableObject = auto() 67 68 seL4_SmallPageObject = auto() 69 seL4_LargePageObject = auto() 70 seL4_HugePageObject = auto() 71 seL4_ARM_SectionObject = auto() 72 seL4_ARM_SuperSectionObject = auto() 73 seL4_PageTableObject = auto() 74 seL4_PageDirectoryObject = auto() 75 76 seL4_X64_PDPT = auto() 77 seL4_X64_PML4 = auto() 78 seL4_IOPageTableObject = auto() 79 seL4_IA32_IOPort = auto() 80 seL4_IA32_IOSpace = auto() 81 seL4_VCPU = auto() 82 83 seL4_FrameObject = auto() 84 seL4_IRQControl = auto() 85 seL4_IRQHandler = auto() 86 seL4_DomainControl = auto() 87 88 seL4_ASID_Pool = auto() 89 seL4_ASID_Control = auto() 90 91 seL4_SchedContextObject = auto() 92 seL4_SchedControl = auto() 93 seL4_RTReplyObject = auto() 94 seL4_ARM_IOSpace = auto() 95 96 seL4_ARMSID = auto() 97 seL4_ARMCB = auto() 98 99 seL4_AARCH64_PGD = auto() 100 seL4_AARCH64_PUD = auto() 101 102 seL4_Slot = auto() 103 104 # Only used by ASIDTableAllocator. Note: this counts slots, not bytes. 105 seL4_ASID_Table = auto() 106 107 108class ObjectRights(Flag): 109 _order_ = 'seL4_NoRights seL4_CanRead seL4_CanWrite seL4_CanGrant seL4_CanGrantReply seL4_AllRights' 110 seL4_NoRights = 0 111 seL4_CanRead = auto() 112 seL4_CanWrite = auto() 113 seL4_CanGrant = auto() 114 seL4_CanGrantReply = auto() 115 seL4_AllRights = seL4_CanRead | seL4_CanWrite | seL4_CanGrant | seL4_CanGrantReply 116 117 118class ARMIRQMode(IntEnum): 119 seL4_ARM_IRQ_LEVEL = 0 120 seL4_ARM_IRQ_EDGE = 1 121 122 123class Object(six.with_metaclass(abc.ABCMeta, object)): 124 """ 125 Parent of all kernel objects. 126 """ 127 128 def __init__(self, name): 129 self.name = name 130 131 @abc.abstractmethod 132 def get_size_bits(self): 133 pass 134 135 def get_size(self): 136 return 1 << self.get_size_bits() 137 138 def is_container(self): 139 return False 140 141 142class ContainerObject(six.with_metaclass(abc.ABCMeta, Object)): 143 """ 144 Common functionality for all objects that are cap containers, in the sense 145 that they may have child caps. 146 """ 147 148 def __init__(self, name): 149 super(ContainerObject, self).__init__(name) 150 self.slots = {} 151 152 def is_container(self): 153 return True 154 155 def print_contents(self): 156 keys = self.slots.keys() 157 if all(isinstance(k, six.integer_types) for k in keys): 158 def print_slot_index(index): return '0x%x' % index 159 elif all(isinstance(k, six.string_types) for k in keys): 160 def print_slot_index(index): return index 161 else: 162 raise RuntimeError( 163 "Object %s: slot indexes must be either all strings or all integers" % self.name) 164 165 return '%s {\n%s\n}' % (self.name, 166 '\n'.join('%s: %s' % (print_slot_index(index), val) 167 for index, val in sorted(self.slots.items()) 168 if val is not None)) 169 170 def __contains__(self, key): 171 return key in self.slots 172 173 def __delitem__(self, key): 174 del self.slots[key] 175 176 def __getitem__(self, key): 177 return self.slots[key] 178 179 def __setitem__(self, slot, cap): 180 self.slots[slot] = cap 181 182 def __iter__(self): 183 return self.slots.__iter__() 184 185 186class Frame(Object): 187 def __init__(self, name, size=4096, paddr=None, fill=[], **_): 188 super(Frame, self).__init__(name) 189 self.size = size 190 self.paddr = paddr 191 self.fill = fill 192 # check the size is aligned to a power of 2 193 assert(self.size == (1 << self.get_size_bits())) 194 195 def set_fill(self, fill): 196 self.fill = fill 197 198 def get_size_bits(self): 199 return self.size.bit_length() - 1 200 201 def __repr__(self): 202 if self.size % (1024 * 1024) == 0: 203 size = '%dM' % (self.size // 1024 // 1024) 204 elif self.size % 1024 == 0: 205 size = '%dk' % (self.size // 1024) 206 else: 207 size = str(self.size) 208 return '%(name)s = frame (%(size)s%(maybepaddr)s%(maybefill)s)' % { 209 'name': self.name, 210 'size': size, 211 'maybepaddr': (', paddr: 0x%x' % self.paddr) if self.paddr is not None else '', 212 'maybefill': (', fill: [%s]' % ",".join(["{%s}" % f for f in self.fill])), 213 } 214 215 216class PageTable(ContainerObject): 217 def __repr__(self): 218 return '%s = pt' % self.name 219 220 def get_size_bits(self): 221 return get_object_size_bits(ObjectType.seL4_PageTableObject) 222 223 224class PageDirectory(ContainerObject): 225 def __repr__(self): 226 return '%s = pd' % self.name 227 228 def get_size_bits(self): 229 return get_object_size_bits(ObjectType.seL4_PageDirectoryObject) 230 231 232class PDPT(ContainerObject): 233 def __repr__(self): 234 return '%s = pdpt' % self.name 235 236 def get_size_bits(self): 237 return get_object_size_bits(ObjectType.seL4_X64_PDPT) 238 239 240class PML4(ContainerObject): 241 def __repr__(self): 242 return '%s = pml4' % self.name 243 244 def get_size_bits(self): 245 return get_object_size_bits(ObjectType.seL4_X64_PML4) 246 247 248class PUD(ContainerObject): 249 def __repr__(self): 250 return '%s = pud' % self.name 251 252 def get_size_bits(self): 253 return get_object_size_bits(ObjectType.seL4_AARCH64_PUD) 254 255 256class PGD(ContainerObject): 257 def __repr__(self): 258 return '%s = pgd' % self.name 259 260 def get_size_bits(self): 261 return get_object_size_bits(ObjectType.seL4_AARCH64_PGD) 262 263 264class ASIDPool(ContainerObject): 265 def __init__(self, name, asid_high=None): 266 super(ASIDPool, self).__init__(name) 267 self.asid_high = asid_high 268 269 def __repr__(self): 270 s = '%s = asid_pool' % self.name 271 if self.asid_high is not None: 272 s += ' (asid_high: 0x%x)' % self.asid_high 273 return s 274 275 def get_size_bits(self): 276 return get_object_size_bits(ObjectType.seL4_ASID_Pool) 277 278 279def calculate_cnode_size(max_slot): 280 return int(math.floor(math.log(max(max_slot, 2), 2)) + 1) 281 282 283def calculate_size(cnode): 284 max_slot = None 285 try: 286 max_slot = max(cnode.slots.keys()) 287 except ValueError as e: 288 max_slot = 0 289 return calculate_cnode_size(max_slot) 290 291 292class CNode(ContainerObject): 293 def __init__(self, name, size_bits='auto'): 294 super(CNode, self).__init__(name) 295 self.size_bits = size_bits 296 self.update_guard_size_caps = [] 297 298 def finalise_size(self, arch=None): 299 if self.size_bits == 'auto': 300 # Minimum CNode size is 1 bit. Maximum size (28 bits) is not 301 # checked. 302 self.size_bits = calculate_size(self) 303 if arch: 304 for x in self.update_guard_size_caps: 305 x.set_guard_size(arch.word_size_bits() - self.size_bits) 306 307 def get_slot_bits(self): 308 if self.size_bits == 'auto': 309 size_bits = calculate_size(self) 310 else: 311 size_bits = self.size_bits 312 return size_bits 313 314 def __repr__(self): 315 return '%s = cnode (%s bits)' % (self.name, self.get_slot_bits()) 316 317 def get_size_bits(self): 318 return self.get_slot_bits() + get_object_size_bits(ObjectType.seL4_Slot) 319 320 321class Endpoint(Object): 322 def __repr__(self): 323 return '%s = ep' % self.name 324 325 def get_size_bits(self): 326 return get_object_size_bits(ObjectType.seL4_EndpointObject) 327 328 329class Notification(Object): 330 def __repr__(self): 331 return '%s = notification' % self.name 332 333 def get_size_bits(self): 334 return get_object_size_bits(ObjectType.seL4_NotificationObject) 335 336 337class TCB(ContainerObject): 338 def __init__(self, name, ipc_buffer_vaddr=0x0, ip=0x0, sp=0x0, 339 prio=254, max_prio=254, affinity=0, init=None, domain=None, fault_ep_slot=None, resume=True): 340 super(TCB, self).__init__(name) 341 self.addr = ipc_buffer_vaddr 342 self.ip = ip 343 self.sp = sp 344 self.prio = prio 345 self.max_prio = max_prio 346 self.affinity = affinity 347 self.init = init or [] 348 self.domain = domain 349 self.fault_ep_slot = fault_ep_slot 350 self.resume = resume 351 352 def __repr__(self): 353 fields = [ 354 'addr: 0x%(addr)x', 355 'ip: 0x%(ip)x', 356 'sp: 0x%(sp)x', 357 'prio: %(prio)s', 358 'max_prio: %(max_prio)s', 359 'affinity: %(affinity)s', 360 'init: %(init)s' 361 ] 362 if self.fault_ep_slot is not None: 363 fields += ['fault_ep: 0x%(fault_ep_slot)0.8x'] 364 if self.domain is not None: 365 fields += ['dom: %(domain)d'] 366 if self.resume is False: 367 fields += ['resume: %(resume)s'] 368 return ('%(name)s = tcb (' + ','.join(fields) + ')') % self.__dict__ 369 370 def set_affinity(self, affinity): 371 self.affinity = affinity 372 373 def set_fault_ep_slot(self, fault_ep_slot=0, fault_ep=None, badge=0): 374 if fault_ep_slot != 0: 375 self.fault_ep_slot = fault_ep_slot 376 if fault_ep: 377 if badge != 0: 378 fault_ep += " (badge: %d)" % badge 379 self.__setitem__("fault_ep_slot", fault_ep) 380 381 def get_size_bits(self): 382 return get_object_size_bits(ObjectType.seL4_TCBObject) 383 384# untypeds are ordered by their paddr, then size, then name, which makes allocation of objects from an 385# untyped at specific addresses easier. 386@total_ordering 387class Untyped(Object): 388 def __init__(self, name, size_bits=12, paddr=None): 389 super(Untyped, self).__init__(name) 390 self.size_bits = size_bits 391 self.paddr = paddr 392 self.watermark = 0 393 self.children = [] 394 395 def add_child(self, child, paddr=None): 396 """Add a child to this untyped. The child *must* align with the current 397 watermark, and must fit into the untyped""" 398 assert isinstance(child, Object) 399 assert(self.remaining() >= child.get_size()) 400 assert(paddr is None or is_aligned(paddr, child.get_size_bits())) 401 if paddr: 402 assert(self.watermark_paddr() == paddr) 403 self.children.append(child) 404 self.watermark += child.get_size() 405 return self.remaining() 406 407 def remaining(self): 408 """Return the amount of space left that can be retyped in this untyped""" 409 return self.get_size() - self.watermark 410 411 def watermark_paddr(self): 412 """Return the current paddr watermark""" 413 return self.watermark + self.paddr 414 415 def __repr__(self): 416 return '%(name)s = ut (%(size_bits)s bits%(maybepaddr)s) { %(kids)s }' % { 417 'name': self.name, 418 'size_bits': self.size_bits, 419 'maybepaddr': (', paddr: 0x%x' % self.paddr) if self.paddr is not None else '', 420 'kids': ('\n'.join([k.name for k in self.children])) if self.children else '' 421 } 422 423 def get_size_bits(self): 424 return self.size_bits 425 426 def __eq__(self, other): 427 return self.get_size_bits() == other.get_size_bits() and self.paddr == other.paddr and self.name == other.name 428 429 def __lt__(self, other): 430 return (self.paddr, self.size_bits, self.name) < (other.paddr, other.size_bits, other.name) 431 432 def __hash__(self): 433 return hash((self.paddr, self.size_bits, self.name)) 434 435 436class IOPorts(Object): 437 # In the implementation there is no such thing as an IO port object, but it is 438 # simpler to model it here as an actual object. 439 def __init__(self, name, start_port=None, end_port=None): 440 super(IOPorts, self).__init__(name) 441 self.start_port = start_port 442 self.end_port = end_port 443 444 def __repr__(self): 445 return '%(name)s = io_ports (ports:[%(start)s..%(end)s])' % \ 446 {'name': self.name, 447 'start': self.start_port, 448 'end': self.end_port - 1} 449 450 def get_size_bits(self): 451 return None 452 453 454class IODevice(Object): 455 def __init__(self, name, domainID, bus, dev, fun): 456 super(IODevice, self).__init__(name) 457 self.domainID = domainID 458 self.bus = bus 459 self.dev = dev 460 self.fun = fun 461 462 def __repr__(self): 463 return '%s = io_device (domainID: %d, 0x%x:%d.%d)' % (self.name, self.domainID, self.bus, self.dev, self.fun) 464 465 def get_size_bits(self): 466 return None 467 468 469class ARMIODevice(Object): 470 def __init__(self, name, iospace): 471 super(ARMIODevice, self).__init__(name) 472 self.iospace = iospace 473 474 def __repr__(self): 475 return '%s = arm_io_device (iospace: %d)' % (self.name, self.iospace) 476 477 def get_size_bits(self): 478 return None 479 480 481class IOPageTable(ContainerObject): 482 def __init__(self, name, level=1): 483 super(IOPageTable, self).__init__(name) 484 assert level in [1, 2, 3] # Complies with CapDL spec 485 self.level = level 486 487 def __repr__(self): 488 return '%(name)s = io_pt (level: %(level)s)' % self.__dict__ 489 490 def get_size_bits(self): 491 return get_object_size_bits(ObjectType.seL4_IOPageTableObject) 492 493 494class IRQ(ContainerObject): 495 # In the implementation there is no such thing as an IRQ object, but it is 496 # simpler to model it here as an actual object. 497 def __init__(self, name, number=None): 498 super(IRQ, self).__init__(name) 499 self.number = number 500 501 def set_notification(self, notification_cap): 502 assert isinstance(notification_cap.referent, Notification) 503 self[0] = notification_cap 504 505 def get_size_bits(self): 506 return None 507 508 def __repr__(self): 509 # Note, in CapDL this is actually represented as a 0-sized CNode. 510 return '%s = irq' % self.name 511 512 513class IOAPICIRQ(IRQ): 514 def __init__(self, name, vector=None, ioapic=None, pin=None, level=None, polarity=None): 515 super(IOAPICIRQ, self).__init__(name, number=vector) 516 self.ioapic = ioapic 517 self.pin = pin 518 self.level = level 519 self.polarity = polarity 520 521 def __repr__(self): 522 return '%s = ioapic_irq (ioapic_num:%d, ioapic_pin:%d, ioapic_level:%d, ioapic_polarity:%d)' % (self.name, 523 self.ioapic, self.pin, self.level, self.polarity) 524 525 526class MSIIRQ(IRQ): 527 def __init__(self, name, vector=None, handle=None, bus=None, dev=None, fun=None): 528 super(MSIIRQ, self).__init__(name, number=vector) 529 self.handle = handle 530 self.bus = bus 531 self.dev = dev 532 self.fun = fun 533 534 def __repr__(self): 535 return '%s = msi_irq (msi_handle:%d, msi_pci_bus:%d, msi_pci_dev:%d, msi_pci_fun:%d)' % (self.name, 536 self.handle, self.bus, self.dev, self.fun) 537 538 539class ARMIRQ(IRQ): 540 def __init__(self, name, number, trigger=ARMIRQMode.seL4_ARM_IRQ_LEVEL, target=0): 541 super(ARMIRQ, self).__init__(name, number=number) 542 self.trigger = trigger 543 self.target = target 544 545 def __repr__(self): 546 return '%s = arm_irq (trigger:%s, target:%d)' % (self.name, "level" if self.trigger == ARMIRQMode.seL4_ARM_IRQ_LEVEL else "edge", self.target) 547 548 549class VCPU(Object): 550 def __repr__(self): 551 return '%s = vcpu' % self.name 552 553 def get_size_bits(self): 554 return get_object_size_bits(ObjectType.seL4_VCPU) 555 556 557class SC(Object): 558 def __init__(self, name, period=10000, budget=10000, data=0x0, size_bits='auto'): 559 super(SC, self).__init__(name) 560 self.period = period 561 self.budget = budget 562 self.data = data 563 if size_bits == 'auto': 564 size_bits = get_object_size_bits(ObjectType.seL4_SchedContextObject) 565 self.size_bits = size_bits 566 567 def __repr__(self): 568 s = '%(name)s = sc (period: %(period)s, budget: %(budget)s, data: %(data)s, %(size_bits)s bits)' % self.__dict__ 569 return s 570 571 def get_size_bits(self): 572 return self.size_bits 573 574 575class IRQControl(Object): 576 def __init__(self, name): 577 super(IRQControl, self).__init__(name) 578 self.name = 'irq_control' 579 580 def __repr__(self): 581 # no object representation for an IRQControl 582 s = "" 583 return s 584 585 def get_size_bits(self): 586 return None 587 588 589class ASIDControl(Object): 590 def __init__(self, name): 591 super(ASIDControl, self).__init__(name) 592 self.name = 'asid_control' 593 594 def __repr__(self): 595 # no object representation for an ASID Control 596 s = "" 597 return s 598 599 def get_size_bits(self): 600 return None 601 602 603class DomainControl(Object): 604 def __init__(self, name): 605 super(DomainControl, self).__init__(name) 606 self.name = 'domain' 607 608 def __repr__(self): 609 # no object representation for an DomainControl 610 s = "" 611 return s 612 613 def get_size_bits(self): 614 return None 615 616 617class SchedControl(Object): 618 def __init__(self, name, core=0): 619 super(SchedControl, self).__init__(name) 620 self.core = core 621 622 def __repr__(self): 623 # no object representation for a sched control 624 s = "" 625 return s 626 627 def get_size_bits(self): 628 return None 629 630 631class RTReply(Object): 632 def __init__(self, name): 633 super(RTReply, self).__init__(name) 634 635 def __repr__(self): 636 return '%s = rtreply' % self.name 637 638 def get_size_bits(self): 639 return get_object_size_bits(ObjectType.seL4_RTReplyObject) 640 641 642class StreamID(Object): 643 def __init__(self, name): 644 super().__init__(name) 645 646 def __repr__(self): 647 return '%s = streamid' % self.name 648 649 def get_size_bits(self): 650 return None 651 652 653class ContextBank(Object): 654 def __init__(self, name): 655 super().__init__(name) 656 657 # Assignment of context bank numbers will evolve with use case 658 self.bank = 0 659 660 def __repr__(self): 661 s = '%s = contextbank (bank: %d)' % (self.name, self.bank) 662 self.bank += 1 663 return s 664 665 def get_size_bits(self): 666 return None 667 668 669def is_aligned(value, size_bits): 670 """ 671 Return true if value is aligned to the provided alignment 672 """ 673 return (value % (1 << size_bits)) == 0 674