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