Esempio n. 1
0
def guard_pages(obj_space, cspaces, elfs, options, **_):
    '''Introduce a guard page around each stack and IPC buffer. Note that the
    templates should have ensured a three page region for each stack in order to
    enable this.'''

    for group, space in cspaces.items():
        cnode = space.cnode
        for index, tcb in [(k, v.referent) for (k, v) in cnode.slots.items() \
                if v is not None and isinstance(v.referent, TCB)]:

            perspective = Perspective(group=group, tcb=tcb.name)

            if perspective['pool']:
                # This TCB is part of the (cap allocator's) TCB pool.
                continue

            elf_name = perspective['elf_name']

            # Find the page directory.
            pd = None
            pd_name = perspective['pd']
            pds = [x for x in obj_space.spec.objs if x.name == pd_name]
            if len(pds) > 1:
                raise Exception('Multiple PDs found for group %s' % group)
            elif len(pds) == 1:
                pd, = pds
                tcb['vspace'] = Cap(pd)
            # If no PD was found we were probably just not passed any ELF files
            # in this pass.

            elf = elfs.get(elf_name)

            if pd and elf:

                ipc_symbol = perspective['ipc_buffer_symbol']

                # Find the IPC buffer's preceding guard page's virtual address.
                assert get_symbol_size(elf, ipc_symbol) == PAGE_SIZE * 3
                pre_guard = get_symbol_vaddr(elf, ipc_symbol)

                # Relate this virtual address to a PT.
                pt_index = page_table_index(get_elf_arch(elf), pre_guard,
                    options.hyp)
                if pt_index not in pd:
                    raise Exception('IPC buffer region of TCB %s in ' \
                        'group %s does not appear to be backed by a frame' \
                        % (tcb.name, group))
                pt = pd[pt_index].referent

                # Continue on to infer the page.
                p_index = page_index(get_elf_arch(elf), pre_guard, options.hyp)
                if p_index not in pt:
                    raise Exception('IPC buffer region of TCB %s in ' \
                        'group %s does not appear to be backed by a frame' \
                        % (tcb.name, group))

                # Delete the page.
                frame = pt[p_index].referent
                del pt[p_index]
                obj_space.remove(frame)

                # Now do the same for the following guard page. We do this
                # calculation separately just in case the region crosses a PT
                # boundary and the two guard pages are in separate PTs.

                post_guard = pre_guard + 2 * PAGE_SIZE

                pt_index = page_table_index(get_elf_arch(elf), post_guard,
                    options.hyp)
                if pt_index not in pd:
                    raise Exception('IPC buffer region of TCB %s in ' \
                        'group %s does not appear to be backed by a frame' \
                        % (tcb.name, group))
                pt = pd[pt_index].referent

                p_index = page_index(get_elf_arch(elf), post_guard, options.hyp)
                if p_index not in pt:
                    raise Exception('IPC buffer region of TCB %s in ' \
                        'group %s does not appear to be backed by a frame' \
                        % (tcb.name, group))

                frame = pt[p_index].referent
                del pt[p_index]
                obj_space.remove(frame)

                # Now we do the same thing for the preceding guard page of the
                # thread's stack...

                stack_symbol = perspective['stack_symbol']

                pre_guard = get_symbol_vaddr(elf, stack_symbol)

                pt_index = page_table_index(get_elf_arch(elf), pre_guard,
                    options.hyp)
                if pt_index not in pd:
                    raise Exception('stack region of TCB %s in ' \
                        'group %s does not appear to be backed by a frame' \
                        % (tcb.name, group))
                pt = pd[pt_index].referent

                p_index = page_index(get_elf_arch(elf), pre_guard, options.hyp)
                if p_index not in pt:
                    raise Exception('stack region of TCB %s in ' \
                        'group %s does not appear to be backed by a frame' \
                        % (tcb.name, group))

                frame = pt[p_index].referent
                del pt[p_index]
                obj_space.remove(frame)

                # ...and the following guard page.

                stack_region_size = get_symbol_size(elf, stack_symbol)
                assert stack_region_size % PAGE_SIZE == 0, \
                    'stack region is not page-aligned'
                assert stack_region_size >= 3 * PAGE_SIZE, \
                    'stack region has no room for guard pages'
                post_guard = pre_guard + stack_region_size - PAGE_SIZE

                pt_index = page_table_index(get_elf_arch(elf), post_guard,
                    options.hyp)
                if pt_index not in pd:
                    raise Exception('stack region of TCB %s in ' \
                        'group %s does not appear to be backed by a frame' \
                        % (tcb.name, group))
                pt = pd[pt_index].referent

                p_index = page_index(get_elf_arch(elf), post_guard, options.hyp)
                if p_index not in pt:
                    raise Exception('stack region of TCB %s in ' \
                        'group %s does not appear to be backed by a frame' \
                        % (tcb.name, group))

                frame = pt[p_index].referent
                del pt[p_index]
                obj_space.remove(frame)
Esempio n. 2
0
def collapse_shared_frames(ast, obj_space, elfs, options, **_):
    """Find regions in virtual address spaces that are intended to be backed by
    shared frames and adjust the capability distribution to reflect this."""

    if not elfs:
        # If we haven't been passed any ELF files this step is not relevant yet.
        return

    assembly = find_assembly(ast)

    # We want to track the frame objects backing shared regions with a dict
    # keyed on the name of the connection linking the regions.
    shared_frames = {}

    for i in (x for x in assembly.composition.instances
            if not x.type.hardware):

        perspective = Perspective(instance=i.name, group=i.address_space)

        elf_name = perspective['elf_name']
        assert elf_name in elfs
        elf = elfs[elf_name]

        # Find this instance's page directory.
        pd_name = perspective['pd']
        pds = [x for x in obj_space.spec.objs if x.name == pd_name]
        assert len(pds) == 1
        pd, = pds

        large_frame_uid = 0

        for d in i.type.dataports:

            # Find the connection that associates this dataport with another.
            connections = [x for x in assembly.composition.connections if \
                ((x.from_instance == i and x.from_interface == d) or \
                (x.to_instance == i and x.to_interface == d))]
            if len(connections) == 0:
                # This dataport is unconnected.
                continue
            #assert len(connections) == 1
            conn_name = connections[0].name

            if connections[0].from_instance == i and \
                    connections[0].from_interface == d:
                direction = 'from'
            else:
                assert connections[0].to_instance == i
                assert connections[0].to_interface == d
                direction = 'to'

            # Reverse the logic in the Makefile template.
            p = Perspective(instance=i.name, dataport=d.name)
            sym = p['dataport_symbol']

            vaddr = get_symbol_vaddr(elf, sym)
            assert vaddr is not None, 'failed to find dataport symbol \'%s\'' \
                ' in ELF %s' % (sym, elf_name)
            assert vaddr != 0
            assert vaddr % PAGE_SIZE == 0, 'dataport not page-aligned'
            sz = get_symbol_size(elf, sym)
            assert sz != 0

            arch = get_elf_arch(elf)

            # Infer the page table(s) and page(s) that back this region.
            pts, p_indices = zip(*[\
                (pd[page_table_index(arch, v, options.hyp)].referent,
                 page_index(arch, v, options.hyp)) \
                for v in xrange(vaddr, vaddr + sz, PAGE_SIZE)])

            # Determine the rights this mapping should have. We use these to
            # recreate the mapping below. Technically we may not need to
            # recreate this mapping if it's already correct, but do it anyway
            # for simplicity.
            # FIXME: stop hard coding this name mangling.
            rights_setting = assembly.configuration[conn_name].get('%s_access' % direction)
            if rights_setting is not None and \
                    re.match(r'^"R?W?(G|X)?"$', rights_setting):
                read = 'R' in rights_setting
                write = 'W' in rights_setting
                execute = 'X' in rights_setting or 'G' in rights_setting
            else:
                # default
                read = True
                write = True
                execute = False

            # Check if the dataport is connected *TO* a hardware component.
            if connections[0].to_instance.type.hardware:
                p = Perspective(to_interface=connections[0].to_interface.name)
                hardware_attribute = p['hardware_attribute']
                conf = assembly.configuration[connections[0].to_instance.name].get(hardware_attribute)
                assert conf is not None
                paddr, size = conf.strip('"').split(':')
                # Round up the MMIO size to PAGE_SIZE
                paddr = int(paddr, 0)
                size = int(size, 0)

                instance_name = connections[0].to_instance.name

                if size == 0:
                    raise Exception('Hardware dataport %s.%s has zero size!' % (instance_name,
                        connections[0].to_interface.name))

                # determine the size of a large frame, and the type of kernel
                # object that will be used, both of which depend on the architecture
                if get_elf_arch(elf) == 'ARM':
                    large_size = 1024 * 1024
                    large_object_type = seL4_ARM_SectionObject
                else:
                    large_size = 4 * 1024 * 1024
                    large_object_type = seL4_IA32_4M

                # Check if MMIO start and end is aligned to page table coverage.
                # This will indicate that we should use pagetable-sized pages
                # to back the device region to be consistent with the kernel.
                if paddr % large_size == 0 and size % large_size == 0:

                    # number of page tables backing device memory
                    n_pts = size / large_size

                    # index of first page table in page directory backing the device memory
                    base_pt_index = page_table_index(get_elf_arch(elf), vaddr)
                    pt_indices = xrange(base_pt_index, base_pt_index + n_pts)

                    # loop over all the page table indices and replace the page tables
                    # with large frames
                    for count, pt_index in enumerate(pt_indices):

                        # look up the page table at the current index
                        pt = pd[pt_index].referent

                        name = 'large_frame_%s_%d' % (instance_name, large_frame_uid)
                        large_frame_uid += 1

                        frame_paddr = paddr + large_size * count

                        # allocate a new large frame
                        frame = obj_space.alloc(large_object_type, name, paddr=frame_paddr)

                        # insert the frame cap into the page directory
                        frame_cap = Cap(frame, read, write, execute)
                        frame_cap.set_cached(False)
                        pd[pt_index] = frame_cap

                        # remove all the small frames from the spec
                        for p_index in pt:
                            small_frame = pt[p_index].referent
                            obj_space.remove(small_frame)

                        # remove the page table from the spec
                        obj_space.remove(pt)

                else:
                    # If the MMIO start and end are not aligned to page table coverage,
                    # loop over all the frames and set their paddrs based on the
                    # paddr in the spec.
                    for idx in xrange(0, (size + PAGE_SIZE - 1) / PAGE_SIZE):
                        try:
                            frame_obj = pts[idx][p_indices[idx]].referent
                        except IndexError:
                            raise Exception('MMIO attributes specify device ' \
                                'memory that is larger than the dataport it is ' \
                                'associated with')
                        frame_obj.paddr = paddr + PAGE_SIZE * idx
                        cap = Cap(frame_obj, read, write, execute)
                        cap.set_cached(False)
                        pts[idx].slots[p_indices[idx]] = cap
                        obj_space.relabel(conn_name, frame_obj)

                continue

            shm_keys = []
            for c in connections:
                shm_keys.append('%s_%s' % (c.from_instance.name, c.from_interface.name))
                shm_keys.append('%s_%s' % (c.to_instance.name, c.to_interface.name))

            mapped = [x for x in shm_keys if x in shared_frames]
            if mapped:
                # We've already encountered the other side of this dataport.

                # The region had better be the same size in all address spaces.
                for key in mapped:
                    assert len(shared_frames[key]) == sz / PAGE_SIZE

            # This is the first side of this dataport.

            # Save all the frames backing this region.
            for key in shm_keys:
                if mapped:
                    shared_frames[key] = shared_frames[mapped[0]]
                else:
                    shared_frames[key] = [pt[p_index].referent \
                        for (pt, p_index) in zip(pts, p_indices)]

            # Overwrite the caps backing this region with caps to the shared
            # frames.
            for j, f in enumerate(shared_frames[shm_keys[0]]):
                existing = pts[j].slots[p_indices[j]].referent
                if existing != f:
                    # We're actually modifying this mapping. Delete the
                    # unneeded frame.
                    obj_space.remove(existing)
                pts[j].slots[p_indices[j]] = Cap(f, read, write, execute)
                obj_space.relabel(conn_name, f)
Esempio n. 3
0
def replace_dma_frames(ast, obj_space, elfs, options, **_):
    '''Locate the DMA pool (a region that needs to have frames whose mappings
    can be reversed) and replace its backing frames with pre-allocated,
    reversible ones.'''

    # TODO: Large parts of this function clagged from collapse_shared_frames; Refactor.

    if not elfs:
        # If we haven't been passed any ELF files this step is not relevant yet.
        return

    assembly = find_assembly(ast)

    for i in (x for x in assembly.composition.instances
            if not x.type.hardware):

        perspective = Perspective(instance=i.name, group=i.address_space)

        elf_name = perspective['elf_name']
        assert elf_name in elfs
        elf = elfs[elf_name]

        # Find this instance's page directory.
        pd_name = perspective['pd']
        pds = filter(lambda x: x.name == pd_name, obj_space.spec.objs)
        assert len(pds) == 1
        pd, = pds

        sym = perspective['dma_pool_symbol']
        base = get_symbol_vaddr(elf, sym)
        if base is None:
            # We don't have a DMA pool.
            continue
        assert base != 0
        sz = get_symbol_size(elf, sym)
        assert sz % PAGE_SIZE == 0 # DMA pool should be page-aligned.

        # Generate a list of the base addresses of the pages we need to
        # replace.
        base_vaddrs = [PAGE_SIZE * x + base for x in
            range(int(sz / PAGE_SIZE))]

        for index, v in enumerate(base_vaddrs):
            # Locate the mapping.
            pt_index = page_table_index(get_elf_arch(elf), v, options.hyp)
            p_index = page_index(get_elf_arch(elf), v, options.hyp)

            # It should contain an existing frame.
            assert pt_index in pd
            pt = pd[pt_index].referent
            assert p_index in pt
            discard_frame = pt[p_index].referent

            # Locate the frame we're going to replace it with. The logic that
            # constructs this object name is in component.template.c. Note that
            # we need to account for the guard-prefix of the instance name
            # introduced by the template context.
            p = Perspective(instance=i.name, group=i.address_space,
                dma_frame_index=index)
            dma_frames = [x for x in obj_space.spec.objs if
                x.name == p['dma_frame_symbol']]
            assert len(dma_frames) == 1
            dma_frame, = dma_frames

            # Replace the existing mapping.
            c = Cap(dma_frame, True, True, False) # RW
            c.set_cached(False)
            pt.slots[p_index] = c

            # We can now remove the old frame as we know it's not referenced
            # anywhere else. TODO: assert this somehow.
            obj_space.remove(discard_frame)
Esempio n. 4
0
def set_tcb_caps(ast, obj_space, cspaces, elfs, options, **_):
    assembly = find_assembly(ast)

    for group, space in cspaces.items():
        cnode = space.cnode
        for index, tcb in [(k, v.referent) for (k, v) in cnode.slots.items() \
                if v is not None and isinstance(v.referent, TCB)]:

            perspective = Perspective(tcb=tcb.name, group=group)

            # Finalise the CNode so that we know what its absolute size will
            # be. Note that we are assuming no further caps will be added to
            # the CNode after this point.
            cnode.finalise_size()

            # Allow the user to override CNode sizes with the 'cnode_size_bits'
            # attribute.
            cnode_size = assembly.configuration[group].get('cnode_size_bits')
            if cnode_size is not None:
                try:
                    if isinstance(cnode_size, str):
                        size = int(cnode_size, 0)
                    else:
                        size = cnode_size
                except ValueError:
                    raise Exception('illegal value for CNode size for %s' % \
                        group)
                if size < cnode.size_bits:
                    raise Exception('%d-bit CNode specified for %s, but this ' \
                        'CSpace needs to be at least %d bits' % \
                        (size, group, cnode.size_bits))
                cnode.size_bits = size

            cspace = Cap(cnode)
            cspace.set_guard_size(32 - cnode.size_bits)
            tcb['cspace'] = cspace

            elf_name = perspective['elf_name']

            pd = None
            pd_name = perspective['pd']
            pds = [x for x in obj_space.spec.objs if x.name == pd_name]
            if len(pds) > 1:
                raise Exception('Multiple PDs found for %s' % group)
            elif len(pds) == 1:
                pd, = pds
                tcb['vspace'] = Cap(pd)
            # If no PD was found we were probably just not passed any ELF files
            # in this pass.

            if perspective['pool']:
                # This TCB is part of the (cap allocator's) TCB pool.
                continue

            elf = elfs.get(elf_name)

            if pd and elf:

                ipc_symbol = perspective['ipc_buffer_symbol']

                # Find the IPC buffer's virtual address.
                assert get_symbol_size(elf, ipc_symbol) == PAGE_SIZE * 3
                ipc_vaddr = get_symbol_vaddr(elf, ipc_symbol) + PAGE_SIZE

                # Relate this virtual address to a PT.
                pt_index = page_table_index(get_elf_arch(elf), ipc_vaddr,
                    options.hyp)
                if pt_index not in pd:
                    raise Exception('IPC buffer of TCB %s in group %s does ' \
                        'not appear to be backed by a frame' % (tcb.name, group))
                pt = pd[pt_index].referent

                # Continue on to infer the physical frame.
                p_index = page_index(get_elf_arch(elf), ipc_vaddr, options.hyp)
                if p_index not in pt:
                    raise Exception('IPC buffer of TCB %s in group %s does ' \
                        'not appear to be backed by a frame' % (tcb.name, group))
                frame = pt[p_index].referent

                tcb['ipc_buffer_slot'] = Cap(frame, True, True, False) # RW
Esempio n. 5
0
def collapse_shared_frames(ast, obj_space, cspaces, elfs, options, **_):
    """Find regions in virtual address spaces that are intended to be backed by
    shared frames and adjust the capability distribution to reflect this."""

    if not elfs:
        # If we haven't been passed any ELF files this step is not relevant yet.
        return

    assembly = find_assembly(ast)

    # We want to track the frame objects backing shared regions with a dict
    # keyed on the name of the connection linking the regions.
    shared_frames = {}

    for i in (x for x in assembly.composition.instances
            if not x.type.hardware):

        perspective = Perspective(instance=i.name, group=i.address_space)

        elf_name = perspective['elf_name']
        assert elf_name in elfs
        elf = elfs[elf_name]

        # Find this instance's page directory.
        pd_name = perspective['pd']
        pds = [x for x in obj_space.spec.objs if x.name == pd_name]
        assert len(pds) == 1
        pd, = pds

        for d in i.type.dataports:

            # Find the connection that associates this dataport with another.
            connections = [x for x in assembly.composition.connections if \
                ((x.from_instance == i and x.from_interface == d) or \
                (x.to_instance == i and x.to_interface == d))]
            if len(connections) == 0:
                # This dataport is unconnected.
                continue
            #assert len(connections) == 1
            conn_name = connections[0].name

            if connections[0].from_instance == i and \
                    connections[0].from_interface == d:
                direction = 'from'
            else:
                assert connections[0].to_instance == i
                assert connections[0].to_interface == d
                direction = 'to'

            # Reverse the logic in the Makefile template.
            p = Perspective(instance=i.name, dataport=d.name)
            sym = p['dataport_symbol']

            vaddr = get_symbol_vaddr(elf, sym)
            assert vaddr is not None, 'failed to find dataport symbol \'%s\'' \
                ' in ELF %s' % (sym, elf_name)
            assert vaddr != 0
            assert vaddr % PAGE_SIZE == 0, 'dataport %s not page-aligned' % sym
            sz = get_symbol_size(elf, sym)
            assert sz != 0

            # Infer the page table(s) and page(s) that back this region.
            pts, p_indices = zip(*[\
                (pd[page_table_index(options.architecture, v)].referent,
                 page_index(options.architecture, v)) \
                for v in xrange(vaddr, vaddr + sz, PAGE_SIZE)])

            # Determine the rights this mapping should have. We use these to
            # recreate the mapping below. Technically we may not need to
            # recreate this mapping if it's already correct, but do it anyway
            # for simplicity.
            # FIXME: stop hard coding this name mangling.
            rights_setting = assembly.configuration[conn_name].get('%s_access' % direction)
            if rights_setting is not None and \
                    re.match(r'^"R?W?(G|X)?"$', rights_setting):
                read = 'R' in rights_setting
                write = 'W' in rights_setting
                execute = 'X' in rights_setting or 'G' in rights_setting
            else:
                # default
                read = True
                write = True
                execute = False

            # Check if the dataport is connected *TO* a hardware component.
            if connections[0].to_instance.type.hardware:
                p = Perspective(to_interface=connections[0].to_interface.name)
                hardware_attribute = p['hardware_attribute']
                conf = assembly.configuration[connections[0].to_instance.name].get(hardware_attribute)
                assert conf is not None, "%s.%s not found in configuration" % \
                    (connections[0].to_instance.name, hardware_attribute)
                paddr, size = conf.strip('"').split(':')
                # Round up the MMIO size to PAGE_SIZE
                try:
                    paddr = int(paddr, 0)
                except ValueError:
                    raise Exception("Invalid physical address specified for %s.%s: %s\n" %
                                    (me.to_instance.name, me.to_interface.name, paddr))

                try:
                    size = int(size, 0)
                except ValueError:
                    raise Exception("Invalid size specified for %s.%s: %s\n" %
                                    (me.to_instance.name, me.to_interface.name, size))

                hardware_cached = p['hardware_cached']
                cached = assembly.configuration[connections[0].to_instance.name].get(hardware_cached)
                if cached is None:
                    cached = False
                elif cached.lower() == 'true':
                    cached = True
                elif cached.lower() == 'false':
                    cached = False
                else:
                    raise Exception("Value of %s.%s_cached must be either 'true' or 'false'. Got '%s'." %
                                    (me.to_instance.name, me.to_interface.name, cached))

                instance_name = connections[0].to_instance.name

                if size == 0:
                    raise Exception('Hardware dataport %s.%s has zero size!' % (instance_name,
                        connections[0].to_interface.name))

                # determine the size of a large frame, and the type of kernel
                # object that will be used, both of which depend on the architecture
                if get_elf_arch(elf) == 'ARM':
                    large_size = 1024 * 1024
                    large_object_type = seL4_ARM_SectionObject
                else:
                    large_size = 4 * 1024 * 1024
                    large_object_type = seL4_IA32_4M

                # Check if MMIO start and end is aligned to page table coverage.
                # This will indicate that we should use pagetable-sized pages
                # to back the device region to be consistent with the kernel.
                if paddr % large_size == 0 and size % large_size == 0:

                    # number of page tables backing device memory
                    n_pts = size / large_size

                    # index of first page table in page directory backing the device memory
                    base_pt_index = page_table_index(options.architecture, vaddr)
                    pt_indices = xrange(base_pt_index, base_pt_index + n_pts)

                    # loop over all the page table indices and replace the page tables
                    # with large frames
                    for count, pt_index in enumerate(pt_indices):

                        # look up the page table at the current index
                        pt = pd[pt_index].referent

                        offset = count * large_size
                        frame_paddr = paddr + offset

                        # lookup the frame, already allocated by a template
                        frame_cap = find_hardware_frame_in_cspace(
                                        cspaces[i.address_space],
                                        frame_paddr,
                                        connections[0].to_instance.name,
                                        connections[0].to_interface.name)
                        frame_obj = frame_cap.referent

                        # create a new cap for the frame to use for its mapping
                        mapping_frame_cap = Cap(frame_obj, read, write, execute)
                        mapping_frame_cap.set_cached(cached)

                        # add the mapping to the spec
                        pd[pt_index] = mapping_frame_cap

                        # add the mapping information to the original cap
                        frame_cap.set_mapping(pd, pt_index)

                        # remove all the small frames from the spec
                        for p_index in pt:
                            small_frame = pt[p_index].referent
                            obj_space.remove(small_frame)

                        # remove the page table from the spec
                        obj_space.remove(pt)

                else:
                    # If the MMIO start and end are not aligned to page table coverage,
                    # loop over all the frames and set their paddrs based on the
                    # paddr in the spec.
                    for idx in xrange(0, (size + PAGE_SIZE - 1) / PAGE_SIZE):
                        try:
                            frame_obj = pts[idx][p_indices[idx]].referent
                        except IndexError:
                            raise Exception('MMIO attributes specify device ' \
                                'memory that is larger than the dataport it is ' \
                                'associated with')

                        offset = idx * PAGE_SIZE
                        frame_paddr = paddr + offset

                        # lookup the frame, already allocated by a template
                        frame_cap = find_hardware_frame_in_cspace(
                                        cspaces[i.address_space],
                                        frame_paddr,
                                        connections[0].to_instance.name,
                                        connections[0].to_interface.name)
                        frame_obj = frame_cap.referent

                        # create a new cap for the frame to use for its mapping
                        mapping_frame_cap = Cap(frame_obj, read, write, execute)
                        mapping_frame_cap.set_cached(cached)

                        # add the mapping to the spec
                        pt = pts[idx]
                        slot = p_indices[idx]
                        pt.slots[slot] = mapping_frame_cap

                        # add the mapping information to the original cap
                        frame_cap.set_mapping(pt, slot)

                        obj_space.relabel(conn_name, frame_obj)

                continue

            # If any objects still have names indicating they are part of a hardware
            # dataport, it means that dataport hasn't been given a paddr or size.
            # This indicates an error, and the object name is invalid in capdl,
            # so catch the error here rather than having the capdl translator fail.

            for cap in (v for v in cspaces[i.address_space].cnode.slots.values() if v is not None):

                obj = cap.referent

                match = HARDWARE_FRAME_NAME_PATTERN.match(obj.name)
                assert (match is None or match.group(2) != connections[0].to_instance.name), \
                    "Missing hardware attributes for %s.%s" % (match.group(2), match.group(3))

            shm_keys = []
            for c in connections:
                shm_keys.append('%s_%s' % (c.from_instance.name, c.from_interface.name))
                shm_keys.append('%s_%s' % (c.to_instance.name, c.to_interface.name))

            mapped = [x for x in shm_keys if x in shared_frames]
            if mapped:
                # We've already encountered the other side of this dataport.

                # The region had better be the same size in all address spaces.
                for key in mapped:
                    assert len(shared_frames[key]) == sz / PAGE_SIZE

            # This is the first side of this dataport.

            # Save all the frames backing this region.
            for key in shm_keys:
                if mapped:
                    shared_frames[key] = shared_frames[mapped[0]]
                else:
                    shared_frames[key] = [pt[p_index].referent \
                        for (pt, p_index) in zip(pts, p_indices)]

            # Overwrite the caps backing this region with caps to the shared
            # frames.
            for j, f in enumerate(shared_frames[shm_keys[0]]):
                existing = pts[j].slots[p_indices[j]].referent
                if existing != f:
                    # We're actually modifying this mapping. Delete the
                    # unneeded frame.
                    obj_space.remove(existing)
                pts[j].slots[p_indices[j]] = Cap(f, read, write, execute)
                obj_space.relabel(conn_name, f)
Esempio n. 6
0
def collapse_shared_frames(ast, obj_space, cspaces, elfs, *_):
    """Find regions in virtual address spaces that are intended to be backed by
    shared frames and adjust the capability distribution to reflect this."""

    if not elfs:
        # If we haven't been passed any ELF files this step is not relevant yet.
        return

    assembly = find_assembly(ast)

    settings = \
        assembly.configuration.settings if assembly.configuration is not None \
        else []

    # We want to track the frame objects backing shared regions with a dict
    # keyed on the name of the connection linking the regions.
    shared_frames = {}

    for i in assembly.composition.instances:
        if i.type.hardware:
            continue

        perspective = Perspective(instance=i.name, group=i.address_space)

        elf_name = perspective['elf_name']
        assert elf_name in elfs
        elf = elfs[elf_name]

        # Find this instance's page directory.
        pd_name = perspective['pd']
        pds = filter(lambda x: x.name == pd_name, obj_space.spec.objs)
        assert len(pds) == 1
        pd, = pds

        for d in i.type.dataports:

            # Find the connection that associates this dataport with another.
            connections = filter(lambda x: \
                (x.from_instance == i and x.from_interface == d) or \
                (x.to_instance == i and x.to_interface == d), \
                assembly.composition.connections)
            if len(connections) == 0:
                # This dataport is unconnected.
                continue
            #assert len(connections) == 1
            conn_name = connections[0].name

            if connections[0].from_instance == i and \
                    connections[0].from_interface == d:
                direction = 'from'
            else:
                assert connections[0].to_instance == i
                assert connections[0].to_interface == d
                direction = 'to'

            # Reverse the logic in the Makefile template.
            p = Perspective(instance=i.name, dataport=d.name)
            sym = p['dataport_symbol']

            vaddr = get_symbol_vaddr(elf, sym)
            assert vaddr is not None, 'failed to find dataport symbol \'%s\'' \
                ' in ELF %s' % (sym, elf_name)
            assert vaddr != 0
            sz = get_symbol_size(elf, sym)
            assert sz != 0

            # Infer the page table that backs this vaddr.
            pt_index = page_table_index(get_elf_arch(elf), vaddr)
            assert pt_index in pd
            pt = pd[pt_index].referent

            # Infer the starting page index of this vaddr.
            p_index = page_index(get_elf_arch(elf), vaddr)

            # TODO: If the following assertion fails it means that the shared
            # region crosses a PT boundary (i.e. it is backed by more than one
            # PT). In theory we could support this with a bit more cleverness
            # here.
            assert page_table_index(get_elf_arch(elf), vaddr + sz - 1) == pt_index

            # Determine the rights this mapping should have. We use these to
            # recreate the mapping below. Technically we may not need to
            # recreate this mapping if it's already correct, but do it anyway
            # for simplicity.
            # FIXME: stop hard coding this name mangling.
            rights_setting = filter(lambda x: x.instance == conn_name and \
                x.attribute == '%s_access' % direction, settings)
            if len(rights_setting) == 1 and \
                    re.match(r'^"R?W?(G|X)?"$', rights_setting[0].value):
                read = 'R' in rights_setting[0].value
                write = 'W' in rights_setting[0].value
                execute = 'X' in rights_setting[0].value or \
                    'G' in rights_setting[0].value
            else:
                # default
                read = True
                write = True
                execute = False

            # Check if the dataport is connected *TO* a hardware component.
            if connections[0].to_instance.type.hardware and \
                    assembly.configuration is not None:
                p = Perspective(to_interface=connections[0].to_interface.name)
                hardware_attribute = p['hardware_attribute']
                configurations = filter(lambda x: \
                    (x.instance == connections[0].to_instance.name and \
                     x.attribute == hardware_attribute), \
                    assembly.configuration.settings)
                assert len(configurations) == 1
                paddr, size = configurations[0].value.strip('"').split(':')
                # Round up the MMIO size to PAGE_SIZE
                size = int(size, 16)
                for idx in range(0, (size + PAGE_SIZE - 1) / PAGE_SIZE):
                    frame_obj = pt[p_index + idx].referent
                    frame_obj.paddr = int(paddr, 16) + PAGE_SIZE * idx
                    cap = Cap(frame_obj, read, write, execute)
                    cap.set_cached(False)
                    pt.slots[p_index + idx] = cap
                    obj_space.relabel(conn_name, frame_obj)

                continue

            shm_keys = []
            for c in connections:
                shm_keys.append('%s_%s' % (c.from_instance.name, c.from_interface.name))
                shm_keys.append('%s_%s' % (c.to_instance.name, c.to_interface.name))

            mapped = filter(lambda x: x in shared_frames, shm_keys)
            if mapped:
                # We've already encountered the other side of tnhis dataport.

                # The region had better be the same size in all address spaces.
                for key in mapped:
                    assert len(shared_frames[key]) == sz / PAGE_SIZE

            # This is the first side of this dataport.

            # Save all the frames backing this region.
            for key in shm_keys:
                if mapped:
                    shared_frames[key] = shared_frames[mapped[0]]
                else:
                    shared_frames[key] = \
                        map(lambda x: pt[p_index + x].referent, \
                        range(0, sz / PAGE_SIZE))

            # Overwrite the caps backing this region with caps to the shared
            # frames. Again, note we may not need to do this, but doing it
            # unconditionally is simpler.
            for j in range(0, sz / PAGE_SIZE):
                f = shared_frames[shm_keys[0]][j]
                pt.slots[p_index + j] = Cap(f, read, write, execute)
                obj_space.relabel(conn_name, f)
Esempio n. 7
0
def set_tcb_caps(ast, obj_space, cspaces, elfs, *_):
    for group, space in cspaces.items():
        cnode = space.cnode
        for index, cap in cnode.slots.items():

            if cap is None:
                continue
            tcb = cap.referent
            if not isinstance(tcb, TCB):
                continue

            perspective = Perspective(tcb=tcb.name, group=group)

            # Finalise the CNode so that we know what its absolute size will
            # be. Note that we are assuming no further caps will be added to
            # the CNode after this point.
            cnode.finalise_size()

            cspace = Cap(cnode)
            cspace.set_guard_size(32 - cnode.size_bits)
            tcb['cspace'] = cspace

            elf_name = perspective['elf_name']

            pd = None
            pd_name = perspective['pd']
            pds = filter(lambda x: x.name == pd_name, obj_space.spec.objs)
            if len(pds) > 1:
                raise Exception('Multiple PDs found for %s' % group)
            elif len(pds) == 1:
                pd, = pds
                tcb['vspace'] = Cap(pd)
            # If no PD was found we were probably just not passed any ELF files
            # in this pass.

            if perspective['pool']:
                # This TCB is part of the (cap allocator's) TCB pool.
                continue

            elf = elfs.get(elf_name)

            if pd and elf:

                ipc_symbol = perspective['ipc_buffer_symbol']

                # Find the IPC buffer's virtual address.
                assert get_symbol_size(elf, ipc_symbol) == PAGE_SIZE * 3
                ipc_vaddr = get_symbol_vaddr(elf, ipc_symbol) + PAGE_SIZE

                # Relate this virtual address to a PT.
                pt_index = page_table_index(get_elf_arch(elf), ipc_vaddr)
                if pt_index not in pd:
                    raise Exception('IPC buffer of TCB %s in group %s does ' \
                        'not appear to be backed by a frame' % (tcb.name, group))
                pt = pd[pt_index].referent

                # Continue on to infer the physical frame.
                p_index = page_index(get_elf_arch(elf), ipc_vaddr)
                if p_index not in pt:
                    raise Exception('IPC buffer of TCB %s in group %s does ' \
                        'not appear to be backed by a frame' % (tcb.name, group))
                frame = pt[p_index].referent

                tcb['ipc_buffer_slot'] = Cap(frame, True, True, True) # RWG
Esempio n. 8
0
def collapse_shared_frames(ast, obj_space, cspaces, elfs, options, **_):
    """Find regions in virtual address spaces that are intended to be backed by
    shared frames and adjust the capability distribution to reflect this."""

    if not elfs:
        # If we haven't been passed any ELF files this step is not relevant yet.
        return

    assembly = find_assembly(ast)

    # We want to track the frame objects backing shared regions with a dict
    # keyed on the name of the connection linking the regions.
    shared_frames = {}

    for i in (x for x in assembly.composition.instances
            if not x.type.hardware):

        perspective = Perspective(instance=i.name, group=i.address_space)

        elf_name = perspective['elf_name']
        assert elf_name in elfs
        elf = elfs[elf_name]

        # Find this instance's page directory.
        pd_name = perspective['pd']
        pds = [x for x in obj_space.spec.objs if x.name == pd_name]
        assert len(pds) == 1
        pd, = pds

        for d in i.type.dataports:

            # Find the connection that associates this dataport with another.
            connections = [x for x in assembly.composition.connections if \
                ((x.from_instance == i and x.from_interface == d) or \
                (x.to_instance == i and x.to_interface == d))]
            if len(connections) == 0:
                # This dataport is unconnected.
                continue
            #assert len(connections) == 1
            conn_name = connections[0].name

            if connections[0].from_instance == i and \
                    connections[0].from_interface == d:
                direction = 'from'
            else:
                assert connections[0].to_instance == i
                assert connections[0].to_interface == d
                direction = 'to'

            # Reverse the logic in the Makefile template.
            p = Perspective(instance=i.name, dataport=d.name)
            sym = p['dataport_symbol']

            vaddr = get_symbol_vaddr(elf, sym)
            assert vaddr is not None, 'failed to find dataport symbol \'%s\'' \
                ' in ELF %s' % (sym, elf_name)
            assert vaddr != 0
            assert vaddr % PAGE_SIZE == 0, 'dataport %s not page-aligned' % sym
            sz = get_symbol_size(elf, sym)
            assert sz != 0

            # Infer the page table(s) and page(s) that back this region.
            pts, p_indices = zip(*[\
                (pd[page_table_index(options.architecture, v)].referent,
                 page_index(options.architecture, v)) \
                for v in xrange(vaddr, vaddr + sz, PAGE_SIZE)])

            # Determine the rights this mapping should have. We use these to
            # recreate the mapping below. Technically we may not need to
            # recreate this mapping if it's already correct, but do it anyway
            # for simplicity.
            # FIXME: stop hard coding this name mangling.
            rights_setting = assembly.configuration[conn_name].get('%s_access' % direction)
            if rights_setting is not None and \
                    re.match(r'^"R?W?(G|X)?"$', rights_setting):
                read = 'R' in rights_setting
                write = 'W' in rights_setting
                execute = 'X' in rights_setting or 'G' in rights_setting
            else:
                # default
                read = True
                write = True
                execute = False

            # Check if the dataport is connected *TO* a hardware component.
            if connections[0].to_instance.type.hardware:
                p = Perspective(to_interface=connections[0].to_interface.name)
                hardware_attribute = p['hardware_attribute']
                conf = assembly.configuration[connections[0].to_instance.name].get(hardware_attribute)
                assert conf is not None, "%s.%s not found in configuration" % \
                    (connections[0].to_instance.name, hardware_attribute)
                paddr, size = conf.strip('"').split(':')
                # Round up the MMIO size to PAGE_SIZE
                try:
                    paddr = int(paddr, 0)
                except ValueError:
                    raise Exception("Invalid physical address specified for %s.%s: %s\n" %
                                    (me.to_instance.name, me.to_interface.name, paddr))

                try:
                    size = int(size, 0)
                except ValueError:
                    raise Exception("Invalid size specified for %s.%s: %s\n" %
                                    (me.to_instance.name, me.to_interface.name, size))

                hardware_cached = p['hardware_cached']
                cached = assembly.configuration[connections[0].to_instance.name].get(hardware_cached)
                if cached is None:
                    cached = False
                elif cached.lower() == 'true':
                    cached = True
                elif cached.lower() == 'false':
                    cached = False
                else:
                    raise Exception("Value of %s.%s_cached must be either 'true' or 'false'. Got '%s'." %
                                    (me.to_instance.name, me.to_interface.name, cached))

                instance_name = connections[0].to_instance.name

                if size == 0:
                    raise Exception('Hardware dataport %s.%s has zero size!' % (instance_name,
                        connections[0].to_interface.name))

                # determine the size of a large frame, and the type of kernel
                # object that will be used, both of which depend on the architecture
                if get_elf_arch(elf) == 'ARM':
                    large_size = 1024 * 1024
                    large_object_type = seL4_ARM_SectionObject
                else:
                    large_size = 4 * 1024 * 1024
                    large_object_type = seL4_IA32_4M

                # Check if MMIO start and end is aligned to page table coverage.
                # This will indicate that we should use pagetable-sized pages
                # to back the device region to be consistent with the kernel.
                if paddr % large_size == 0 and size % large_size == 0:

                    # number of page tables backing device memory
                    n_pts = size / large_size

                    # index of first page table in page directory backing the device memory
                    base_pt_index = page_table_index(options.architecture, vaddr)
                    pt_indices = xrange(base_pt_index, base_pt_index + n_pts)

                    # loop over all the page table indices and replace the page tables
                    # with large frames
                    for count, pt_index in enumerate(pt_indices):

                        # look up the page table at the current index
                        pt = pd[pt_index].referent

                        offset = count * large_size
                        frame_paddr = paddr + offset

                        # lookup the frame, already allocated by a template
                        frame_cap = find_hardware_frame_in_cspace(
                                        cspaces[i.address_space],
                                        frame_paddr,
                                        connections[0].to_instance.name,
                                        connections[0].to_interface.name)
                        frame_obj = frame_cap.referent

                        # create a new cap for the frame to use for its mapping
                        mapping_frame_cap = Cap(frame_obj, read, write, execute)
                        mapping_frame_cap.set_cached(cached)

                        # add the mapping to the spec
                        pd[pt_index] = mapping_frame_cap

                        # add the mapping information to the original cap
                        frame_cap.set_mapping(pd, pt_index)

                        # remove all the small frames from the spec
                        for p_index in pt:
                            small_frame = pt[p_index].referent
                            obj_space.remove(small_frame)

                        # remove the page table from the spec
                        obj_space.remove(pt)

                else:
                    # If the MMIO start and end are not aligned to page table coverage,
                    # loop over all the frames and set their paddrs based on the
                    # paddr in the spec.
                    for idx in xrange(0, (size + PAGE_SIZE - 1) / PAGE_SIZE):
                        try:
                            frame_obj = pts[idx][p_indices[idx]].referent
                        except IndexError:
                            raise Exception('MMIO attributes specify device ' \
                                'memory that is larger than the dataport it is ' \
                                'associated with')

                        offset = idx * PAGE_SIZE
                        frame_paddr = paddr + offset

                        # lookup the frame, already allocated by a template
                        frame_cap = find_hardware_frame_in_cspace(
                                        cspaces[i.address_space],
                                        frame_paddr,
                                        connections[0].to_instance.name,
                                        connections[0].to_interface.name)
                        frame_obj = frame_cap.referent

                        # create a new cap for the frame to use for its mapping
                        mapping_frame_cap = Cap(frame_obj, read, write, execute)
                        mapping_frame_cap.set_cached(cached)

                        # add the mapping to the spec
                        pt = pts[idx]
                        slot = p_indices[idx]
                        pt.slots[slot] = mapping_frame_cap

                        # add the mapping information to the original cap
                        frame_cap.set_mapping(pt, slot)

                        obj_space.relabel(conn_name, frame_obj)

                continue

            # If any objects still have names indicating they are part of a hardware
            # dataport, it means that dataport hasn't been given a paddr or size.
            # This indicates an error, and the object name is invalid in capdl,
            # so catch the error here rather than having the capdl translator fail.

            for cap in (v for v in cspaces[i.address_space].cnode.slots.values() if v is not None):

                obj = cap.referent

                match = HARDWARE_FRAME_NAME_PATTERN.match(obj.name)
                assert (match is None or match.group(2) != connections[0].to_instance.name), \
                    "Missing hardware attributes for %s.%s" % (match.group(2), match.group(3))

            shm_keys = []
            for c in connections:
                shm_keys.append('%s_%s' % (c.from_instance.name, c.from_interface.name))
                shm_keys.append('%s_%s' % (c.to_instance.name, c.to_interface.name))

            mapped = [x for x in shm_keys if x in shared_frames]
            if mapped:
                # We've already encountered the other side of this dataport.

                # The region had better be the same size in all address spaces.
                for key in mapped:
                    assert len(shared_frames[key]) == sz / PAGE_SIZE

            # This is the first side of this dataport.

            # Save all the frames backing this region.
            for key in shm_keys:
                if mapped:
                    shared_frames[key] = shared_frames[mapped[0]]
                else:
                    shared_frames[key] = [pt[p_index].referent \
                        for (pt, p_index) in zip(pts, p_indices)]

            # Overwrite the caps backing this region with caps to the shared
            # frames.
            for j, f in enumerate(shared_frames[shm_keys[0]]):
                existing = pts[j].slots[p_indices[j]].referent
                if existing != f:
                    # We're actually modifying this mapping. Delete the
                    # unneeded frame.
                    obj_space.remove(existing)
                pts[j].slots[p_indices[j]] = Cap(f, read, write, execute)
                obj_space.relabel(conn_name, f)