blob: bfd5c19561e52b3db9a0b0953f3dfb14fe6fb930 [file] [log] [blame]
#
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: BSD-2-Clause
#
"""
Definitions of kernel objects.
"""
from __future__ import absolute_import, division, print_function, \
unicode_literals
import abc
import math
import six
from functools import total_ordering
from aenum import Enum, Flag, unique, auto, IntEnum
import logging
# dict of all object sizes, must be registered before using any Object in this file.
# this dict is indexed by strings matching ObjectType.name
object_sizes = {}
def register_object_sizes(sizes):
"""Register the object sizes to be used when creating objects in this class"""
global object_sizes
object_sizes = sizes
def get_libsel4_constant(name):
global object_sizes
try:
return object_sizes[name]
except KeyError:
if not object_sizes:
logging.fatal("No object sizes registered!")
logging.fatal("No value for key {}".format(name))
def get_object_size_bits(object_type):
global object_sizes
try:
return object_sizes[object_type.name]
except KeyError:
if not object_sizes:
logging.fatal("No object sizes registered!")
logging.fatal("No size for object {}".format(object_type.name))
def get_object_size(object_type):
size = get_object_size_bits(object_type)
if not size:
return 0
return 1 << size
@unique
class ObjectType(Enum):
seL4_UntypedObject = auto()
seL4_TCBObject = auto()
seL4_EndpointObject = auto()
seL4_NotificationObject = auto()
seL4_CapTableObject = auto()
seL4_SmallPageObject = auto()
seL4_LargePageObject = auto()
seL4_HugePageObject = auto()
seL4_ARM_SectionObject = auto()
seL4_ARM_SuperSectionObject = auto()
seL4_PageTableObject = auto()
seL4_PageDirectoryObject = auto()
seL4_X64_PDPT = auto()
seL4_X64_PML4 = auto()
seL4_IOPageTableObject = auto()
seL4_IA32_IOPort = auto()
seL4_IA32_IOSpace = auto()
seL4_VCPU = auto()
seL4_FrameObject = auto()
seL4_IRQControl = auto()
seL4_IRQHandler = auto()
seL4_DomainControl = auto()
seL4_ASID_Pool = auto()
seL4_ASID_Control = auto()
seL4_SchedContextObject = auto()
seL4_SchedControl = auto()
seL4_RTReplyObject = auto()
seL4_ARM_IOSpace = auto()
seL4_ARMSID = auto()
seL4_ARMCB = auto()
seL4_AARCH64_PGD = auto()
seL4_AARCH64_PUD = auto()
seL4_Slot = auto()
# Only used by ASIDTableAllocator. Note: this counts slots, not bytes.
seL4_ASID_Table = auto()
class ObjectRights(Flag):
_order_ = 'seL4_NoRights seL4_CanRead seL4_CanWrite seL4_CanGrant seL4_CanGrantReply seL4_AllRights'
seL4_NoRights = 0
seL4_CanRead = auto()
seL4_CanWrite = auto()
seL4_CanGrant = auto()
seL4_CanGrantReply = auto()
seL4_AllRights = seL4_CanRead | seL4_CanWrite | seL4_CanGrant | seL4_CanGrantReply
class ARMIRQMode(IntEnum):
seL4_ARM_IRQ_LEVEL = 0
seL4_ARM_IRQ_EDGE = 1
class Object(six.with_metaclass(abc.ABCMeta, object)):
"""
Parent of all kernel objects.
"""
def __init__(self, name):
self.name = name
@abc.abstractmethod
def get_size_bits(self):
pass
def get_size(self):
return 1 << self.get_size_bits()
def is_container(self):
return False
class ContainerObject(six.with_metaclass(abc.ABCMeta, Object)):
"""
Common functionality for all objects that are cap containers, in the sense
that they may have child caps.
"""
def __init__(self, name):
super(ContainerObject, self).__init__(name)
self.slots = {}
def is_container(self):
return True
def print_contents(self):
keys = self.slots.keys()
if all(isinstance(k, six.integer_types) for k in keys):
def print_slot_index(index): return '0x%x' % index
elif all(isinstance(k, six.string_types) for k in keys):
def print_slot_index(index): return index
else:
raise RuntimeError(
"Object %s: slot indexes must be either all strings or all integers" % self.name)
return '%s {\n%s\n}' % (self.name,
'\n'.join('%s: %s' % (print_slot_index(index), val)
for index, val in sorted(self.slots.items())
if val is not None))
def __contains__(self, key):
return key in self.slots
def __delitem__(self, key):
del self.slots[key]
def __getitem__(self, key):
return self.slots[key]
def __setitem__(self, slot, cap):
self.slots[slot] = cap
def __iter__(self):
return self.slots.__iter__()
class Frame(Object):
def __init__(self, name, size=4096, paddr=None, fill=[], **_):
super(Frame, self).__init__(name)
self.size = size
self.paddr = paddr
self.fill = fill
# check the size is aligned to a power of 2
assert(self.size == (1 << self.get_size_bits()))
def set_fill(self, fill):
self.fill = fill
def get_size_bits(self):
return self.size.bit_length() - 1
def __repr__(self):
if self.size % (1024 * 1024) == 0:
size = '%dM' % (self.size // 1024 // 1024)
elif self.size % 1024 == 0:
size = '%dk' % (self.size // 1024)
else:
size = str(self.size)
return '%(name)s = frame (%(size)s%(maybepaddr)s%(maybefill)s)' % {
'name': self.name,
'size': size,
'maybepaddr': (', paddr: 0x%x' % self.paddr) if self.paddr is not None else '',
'maybefill': (', fill: [%s]' % ",".join(["{%s}" % f for f in self.fill])),
}
class PageTable(ContainerObject):
def __repr__(self):
return '%s = pt' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_PageTableObject)
class PageDirectory(ContainerObject):
def __repr__(self):
return '%s = pd' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_PageDirectoryObject)
class PDPT(ContainerObject):
def __repr__(self):
return '%s = pdpt' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_X64_PDPT)
class PML4(ContainerObject):
def __repr__(self):
return '%s = pml4' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_X64_PML4)
class PUD(ContainerObject):
def __repr__(self):
return '%s = pud' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_AARCH64_PUD)
class PGD(ContainerObject):
def __repr__(self):
return '%s = pgd' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_AARCH64_PGD)
class ASIDPool(ContainerObject):
def __init__(self, name, asid_high=None):
super(ASIDPool, self).__init__(name)
self.asid_high = asid_high
def __repr__(self):
s = '%s = asid_pool' % self.name
if self.asid_high is not None:
s += ' (asid_high: 0x%x)' % self.asid_high
return s
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_ASID_Pool)
def calculate_cnode_size(max_slot):
return int(math.floor(math.log(max(max_slot, 2), 2)) + 1)
def calculate_size(cnode):
max_slot = None
try:
max_slot = max(cnode.slots.keys())
except ValueError as e:
max_slot = 0
return calculate_cnode_size(max_slot + cnode.headroom)
class CNode(ContainerObject):
def __init__(self, name, size_bits='auto', headroom=0):
super(CNode, self).__init__(name)
self.size_bits = size_bits
self.headroom = headroom
self.update_guard_size_caps = []
self.has_untyped_memory = False
def finalise_size(self, arch=None):
if self.size_bits == 'auto':
# Minimum CNode size is 1 bit. Maximum size (28 bits) is not
# checked.
self.size_bits = calculate_size(self)
if arch:
for x in self.update_guard_size_caps:
x.set_guard_size(arch.word_size_bits() - self.size_bits)
def get_slot_bits(self):
if self.size_bits == 'auto':
size_bits = calculate_size(self)
else:
size_bits = self.size_bits
return size_bits
def __repr__(self):
return '%s = cnode (%s bits, untyped: %s)' % (
self.name, self.get_slot_bits(), self.has_untyped_memory)
def get_size_bits(self):
return self.get_slot_bits() + get_object_size_bits(ObjectType.seL4_Slot)
class Endpoint(Object):
def __repr__(self):
return '%s = ep' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_EndpointObject)
class Notification(Object):
def __repr__(self):
return '%s = notification' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_NotificationObject)
class TCB(ContainerObject):
def __init__(self, name, ipc_buffer_vaddr=0x0, ip=0x0, sp=0x0,
prio=254, max_prio=254, affinity=0, init=None, domain=None, fault_ep_slot=None, resume=True):
super(TCB, self).__init__(name)
self.addr = ipc_buffer_vaddr
self.ip = ip
self.sp = sp
self.prio = prio
self.max_prio = max_prio
self.affinity = affinity
self.init = init or []
self.domain = domain
self.fault_ep_slot = fault_ep_slot
self.resume = resume
def __repr__(self):
fields = [
'addr: 0x%(addr)x',
'ip: 0x%(ip)x',
'sp: 0x%(sp)x',
'prio: %(prio)s',
'max_prio: %(max_prio)s',
'affinity: %(affinity)s',
'init: %(init)s'
]
if self.fault_ep_slot is not None:
fields += ['fault_ep: 0x%(fault_ep_slot)0.8x']
if self.domain is not None:
fields += ['dom: %(domain)d']
if self.resume is False:
fields += ['resume: %(resume)s']
return ('%(name)s = tcb (' + ','.join(fields) + ')') % self.__dict__
def set_affinity(self, affinity):
self.affinity = affinity
def set_fault_ep_slot(self, fault_ep_slot=0, fault_ep=None, badge=0):
if fault_ep_slot != 0:
self.fault_ep_slot = fault_ep_slot
if fault_ep:
if badge != 0:
fault_ep += " (badge: %d)" % badge
self.__setitem__("fault_ep_slot", fault_ep)
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_TCBObject)
# untypeds are ordered by their paddr, then size, then name, which makes allocation of objects from an
# untyped at specific addresses easier.
@total_ordering
class Untyped(Object):
def __init__(self, name, size_bits=12, paddr=None):
super(Untyped, self).__init__(name)
self.size_bits = size_bits
self.paddr = paddr
self.watermark = 0
self.children = []
def add_child(self, child, paddr=None):
"""Add a child to this untyped. The child *must* align with the current
watermark, and must fit into the untyped"""
assert isinstance(child, Object)
assert(self.remaining() >= child.get_size())
assert(paddr is None or is_aligned(paddr, child.get_size_bits()))
if paddr:
assert(self.watermark_paddr() == paddr)
self.children.append(child)
self.watermark += child.get_size()
return self.remaining()
def remaining(self):
"""Return the amount of space left that can be retyped in this untyped"""
return self.get_size() - self.watermark
def watermark_paddr(self):
"""Return the current paddr watermark"""
return self.watermark + self.paddr
def __repr__(self):
return '%(name)s = ut (%(size_bits)s bits%(maybepaddr)s) { %(kids)s }' % {
'name': self.name,
'size_bits': self.size_bits,
'maybepaddr': (', paddr: 0x%x' % self.paddr) if self.paddr is not None else '',
'kids': ('\n'.join([k.name for k in self.children])) if self.children else ''
}
def get_size_bits(self):
return self.size_bits
def __eq__(self, other):
return self.get_size_bits() == other.get_size_bits() and self.paddr == other.paddr and self.name == other.name
def __lt__(self, other):
return (self.paddr, self.size_bits, self.name) < (other.paddr, other.size_bits, other.name)
def __hash__(self):
return hash((self.paddr, self.size_bits, self.name))
class IOPorts(Object):
# In the implementation there is no such thing as an IO port object, but it is
# simpler to model it here as an actual object.
def __init__(self, name, start_port=None, end_port=None):
super(IOPorts, self).__init__(name)
self.start_port = start_port
self.end_port = end_port
def __repr__(self):
return '%(name)s = io_ports (ports:[%(start)s..%(end)s])' % \
{'name': self.name,
'start': self.start_port,
'end': self.end_port - 1}
def get_size_bits(self):
return None
class IODevice(Object):
def __init__(self, name, domainID, bus, dev, fun):
super(IODevice, self).__init__(name)
self.domainID = domainID
self.bus = bus
self.dev = dev
self.fun = fun
def __repr__(self):
return '%s = io_device (domainID: %d, 0x%x:%d.%d)' % (self.name, self.domainID, self.bus, self.dev, self.fun)
def get_size_bits(self):
return None
class ARMIODevice(Object):
def __init__(self, name, iospace):
super(ARMIODevice, self).__init__(name)
self.iospace = iospace
def __repr__(self):
return '%s = arm_io_device (iospace: %d)' % (self.name, self.iospace)
def get_size_bits(self):
return None
class IOPageTable(ContainerObject):
def __init__(self, name, level=1):
super(IOPageTable, self).__init__(name)
assert level in [1, 2, 3] # Complies with CapDL spec
self.level = level
def __repr__(self):
return '%(name)s = io_pt (level: %(level)s)' % self.__dict__
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_IOPageTableObject)
class IRQ(ContainerObject):
# In the implementation there is no such thing as an IRQ object, but it is
# simpler to model it here as an actual object.
def __init__(self, name, number=None):
super(IRQ, self).__init__(name)
self.number = number
def set_notification(self, notification_cap):
assert isinstance(notification_cap.referent, Notification)
self[0] = notification_cap
def get_size_bits(self):
return None
def __repr__(self):
# Note, in CapDL this is actually represented as a 0-sized CNode.
return '%s = irq' % self.name
class IOAPICIRQ(IRQ):
def __init__(self, name, vector=None, ioapic=None, pin=None, level=None, polarity=None):
super(IOAPICIRQ, self).__init__(name, number=vector)
self.ioapic = ioapic
self.pin = pin
self.level = level
self.polarity = polarity
def __repr__(self):
return '%s = ioapic_irq (ioapic_num:%d, ioapic_pin:%d, ioapic_level:%d, ioapic_polarity:%d)' % (self.name,
self.ioapic, self.pin, self.level, self.polarity)
class MSIIRQ(IRQ):
def __init__(self, name, vector=None, handle=None, bus=None, dev=None, fun=None):
super(MSIIRQ, self).__init__(name, number=vector)
self.handle = handle
self.bus = bus
self.dev = dev
self.fun = fun
def __repr__(self):
return '%s = msi_irq (msi_handle:%d, msi_pci_bus:%d, msi_pci_dev:%d, msi_pci_fun:%d)' % (self.name,
self.handle, self.bus, self.dev, self.fun)
class ARMIRQ(IRQ):
def __init__(self, name, number, trigger=ARMIRQMode.seL4_ARM_IRQ_LEVEL, target=0):
super(ARMIRQ, self).__init__(name, number=number)
self.trigger = trigger
self.target = target
def __repr__(self):
return '%s = arm_irq (trigger:%s, target:%d)' % (self.name, "level" if self.trigger == ARMIRQMode.seL4_ARM_IRQ_LEVEL else "edge", self.target)
class VCPU(Object):
def __repr__(self):
return '%s = vcpu' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_VCPU)
class SC(Object):
def __init__(self, name, period=10000, budget=10000, data=0x0, size_bits='auto'):
super(SC, self).__init__(name)
self.period = period
self.budget = budget
self.data = data
if size_bits == 'auto':
size_bits = get_object_size_bits(ObjectType.seL4_SchedContextObject)
self.size_bits = size_bits
def __repr__(self):
s = '%(name)s = sc (period: %(period)s, budget: %(budget)s, data: %(data)s, %(size_bits)s bits)' % self.__dict__
return s
def get_size_bits(self):
return self.size_bits
class IRQControl(Object):
def __init__(self, name):
super(IRQControl, self).__init__(name)
self.name = 'irq_control'
def __repr__(self):
# no object representation for an IRQControl
s = ""
return s
def get_size_bits(self):
return None
class ASIDControl(Object):
def __init__(self, name):
super(ASIDControl, self).__init__(name)
self.name = 'asid_control'
def __repr__(self):
# no object representation for an ASID Control
s = ""
return s
def get_size_bits(self):
return None
class DomainControl(Object):
def __init__(self, name):
super(DomainControl, self).__init__(name)
self.name = 'domain'
def __repr__(self):
# no object representation for an DomainControl
s = ""
return s
def get_size_bits(self):
return None
class SchedControl(Object):
def __init__(self, name, core=0):
super(SchedControl, self).__init__(name)
self.core = core
def __repr__(self):
# no object representation for a sched control
s = ""
return s
def get_size_bits(self):
return None
class RTReply(Object):
def __init__(self, name):
super(RTReply, self).__init__(name)
def __repr__(self):
return '%s = rtreply' % self.name
def get_size_bits(self):
return get_object_size_bits(ObjectType.seL4_RTReplyObject)
class StreamID(Object):
def __init__(self, name):
super().__init__(name)
def __repr__(self):
return '%s = streamid' % self.name
def get_size_bits(self):
return None
class ContextBank(Object):
def __init__(self, name):
super().__init__(name)
# Assignment of context bank numbers will evolve with use case
self.bank = 0
def __repr__(self):
s = '%s = contextbank (bank: %d)' % (self.name, self.bank)
self.bank += 1
return s
def get_size_bits(self):
return None
def is_aligned(value, size_bits):
"""
Return true if value is aligned to the provided alignment
"""
return (value % (1 << size_bits)) == 0