def __init__(self, ph_offset): self.ph_offset = ph_offset self.kconfig = KernelInit() self.objects = None self.kernel_segments = [] self.kernel_heap = None self.kernel_arrays = [] self.segments = [] self.memsections = [] self.zones = [] self.elf = None self.endianess = None self.wordsize = None self.patches = [] self.virt_pool_stack = Image.AttrStack() self.phys_pool_stack = Image.AttrStack() self.pager_stack = Image.AttrStack() self.direct_stack = Image.AttrStack() self.protected_segment = None self.groups = []
class Image: """Representation of the contents of the final image.""" # Different types of segments. # NOTE: PROGRAM and EXTENSION must have the values 1 and 2 to # maintain binary compatibility with the iguana library function # get_elf_info(). PROGRAM = 1 EXTENSION = 2 KERNEL = 3 ROOT_PROGRAM = 4 class Patch: """A Class for describing patches to segments.""" def __init__(self, addr, size, value): self.addr = addr self.size = size self.value = value def get_addr(self): """Return the address to patch.""" return self.addr def get_size(self): """Return the number of bytes to change.""" return self.size def get_value(self): """Return the value to patch in.""" return self.value class AttrStack: """ Class for holding a stack of attribute values. Virtpool, physpool and pagers operate in that way. These stacks differ from regular stacks in that the top of the stack is defined to be the last non-None entry in the list. """ def __init__(self): self.stack = [] self.top = None def __getitem__(self, index): return self.stack[index] def set(self, value): """ Set the stack to only contain the given value. """ assert value is not None self.stack = [value] self.top = 0 assert self.top is None or self.top < len(self.stack) def push(self, value): """ Push an item onto the stack. If the item is not None, then then will become the top of the stack. """ self.stack.append(value) if value is not None: self.top = len(self.stack) - 1 if self.top < 0: self.top = None assert self.top is None or \ (self.top < len(self.stack) and \ self.stack[self.top] is not None) def pop(self): """ Pop on item off the stack. If the item is non-None, then the top of the stack is moved to the last non-None value in the list. """ value = self.stack.pop() if value is not None: # Recalculate the top of the stack. self.top = None i = 0 for item in self.stack: if item is not None: self.top = i i += 1 assert self.top is None or \ self.stack[self.top] is not None assert self.top is None or self.top < len(self.stack) def tos(self): """ Return the item at the top of the stack, or None if there is no such item. """ if self.top is None: return None else: return self.stack[self.top] def __init__(self, ph_offset): self.ph_offset = ph_offset self.kconfig = KernelInit() self.objects = None self.kernel_segments = [] self.kernel_heap = None self.kernel_arrays = [] self.segments = [] self.memsections = [] self.zones = [] self.elf = None self.endianess = None self.wordsize = None self.patches = [] self.virt_pool_stack = Image.AttrStack() self.phys_pool_stack = Image.AttrStack() self.pager_stack = Image.AttrStack() self.direct_stack = Image.AttrStack() self.protected_segment = None self.groups = [] def get_elf(self): """Return the ELF file that will contain the image.""" return self.elf def remove_section_headers(self): self.elf.remove_section_headers() def current_pools(self): """Return the current virtual and physical pools.""" return (self.virt_pool_stack.tos(), self.phys_pool_stack.tos()) def new_attrs(self, namespace, for_segment = False): """ Create a new attribute object. The attributes are initialised with the current values from the attribute stack and the supplied namespace. """ def_direct = False if for_segment: def_direct = self.direct_stack.tos() if namespace is None: path = '/' else: path = namespace.abs_name('.') return ImageAttrs(path = path, virtpool = self.virt_pool_stack.tos(), physpool = self.phys_pool_stack.tos(), pager = self.pager_stack.tos(), direct = def_direct) def set_attrs_stack(self, def_virt = None, def_phys = None, def_pager = None, def_direct = None): """ Prime the attribute stack with initial values. """ if def_virt is not None: self.virt_pool_stack.set(def_virt) if def_phys is not None: self.phys_pool_stack.set(def_phys) if def_pager is not None: self.pager_stack.set(def_pager) if def_direct is not None: self.direct_stack.set(def_direct) def push_attrs(self, virtual = None, physical = None, pager = None, direct = None): """Push values onto the attribute stack.""" self.virt_pool_stack.push(virtual) self.phys_pool_stack.push(physical) self.pager_stack.push(pager) self.direct_stack.push(direct) def pop_attrs(self): """Pop values from the attribute stack.""" self.virt_pool_stack.pop() self.phys_pool_stack.pop() self.pager_stack.pop() self.direct_stack.pop() def prepare(self, machine): """Prepare the ELF file for writing to disk.""" self.elf = self.elf.prepare(self.wordsize, self.endianess) def set_rootserver_stack(self, stack): """Record the root-servers stack pointer.""" self.kconfig.set_rootserver_stack(stack) def write_out_image(self, output_file, machine): """Write out the final ELF file.""" # Record the physical properties of the root server. # Note: Groovy functional programming! root_mappings = [o.root_mappings() for o in self.objects if o.root_mappings() is not None] assert len(root_mappings) > 0 self.kconfig.set_rootserver_mappings(root_mappings) # Record memory descriptors for those objects that need them. for obj in self.objects: descs = obj.make_memdesc() if descs is not None: for desc in descs: self.kconfig.add_mem_descriptor(desc) # Now write out the data. self.kconfig.update_elf(self.elf, machine) #self.elf = self.elf.prepare(self.wordsize, self.endianess) self.elf.to_filename(output_file) def make_single_list(self): """ Place all of the objects into a single list to generate a good layout. Items that will be written to the ELF file are placed together to try and reduce the size of the image. """ # # Approximate proper support for proximity by placing the # kernel heap close to the kernel and memsections with data # close to the segments. # # Proper support should be added to ensure that wombat's # vmlinux memsection is close to the wombat text. # self.objects = [] self.objects.extend(self.kernel_segments) self.objects.extend(self.kernel_arrays) self.objects.append(self.kernel_heap) self.objects.extend(self.segments) self.objects.extend(self.memsections) def layout(self, machine, pools): """Layout the image in memory.""" self.make_single_list() for obj in self.zones: obj.prime(self.virt_pool_stack[0], self.phys_pool_stack[0], pools) for obj in self.groups: obj.layout(self.virt_pool_stack[0], self.phys_pool_stack[0], machine, pools) def apply_patches(self): """Apply registered patches.""" for segment in self.elf.segments: for section in segment.sections: patches = [patch for patch in self.patches if patch.addr >= section.address and patch.addr < section.address + section.get_size()] for patch in patches: offset = patch.addr - section.address if isinstance(patch.value, weaver.image.ImageObject): value = patch.value.attrs.phys_addr else: value = patch.value section.get_data().set_data(offset, value, patch.size, self.endianess) def get_value(self, address, size, endianess=None): """get a value from the image.""" if self.elf.machine == ElfMachine(8): if self.elf.flags & EF_MIPS_ABI_O64: if address & 0x80000000: address |= 0xffffffff00000000L for segment in self.elf.segments: for section in segment.get_sections(): if address > section.address and \ address < (section.address + section.get_size()): offset = address - section.address if endianess is None: endianess = self.elf.endianess return section.get_data().get_data(offset, size, endianess) raise MergeError, "Could not find address %x in Image." % address def set_kernel(self, kernel): """ Record the kernel.""" self.elf = UnpreparedElfFile() self.endianess = kernel.endianess self.wordsize = kernel.wordsize self.elf.elf_type = ET_EXEC self.elf.machine = kernel.machine self.elf.osabi = kernel.osabi self.elf.abiversion = kernel.abiversion self.elf.flags = kernel.flags self.elf.entry_point = kernel.entry_point if self.ph_offset is not None: self.elf.set_ph_offset(self.ph_offset, fixed=True) def patch(self, addr, size, value): """Record the details of a patch to a segment.""" if self.elf.machine == ElfMachine(8): if self.elf.flags & EF_MIPS_ABI_O64: if addr & 0x80000000: addr |= 0xffffffff00000000L self.patches.append(self.Patch(addr, size, value)) def set_kernel_heap(self, attrs, pools): """ Record the details of the kernel heap. """ self.kernel_heap = ImageKernelHeap(attrs, pools) return self.kernel_heap def add_kernel_array(self, attrs, pools): """Record the details of the kernel array.""" array = ImageKernelArray(attrs, pools) self.kernel_arrays.append(array) return array def add_segment(self, segment_index, section_prefix, segment, file_type, attrs, machine, pools): """Create a segment for inclusion in the image.""" if not valid_segment(segment): return None # Remove any pathname components from the prefix. section_prefix = os.path.basename(section_prefix) # Prepare the image for inclusion. new_segment = segment.copy() # Align segments to the page boundary if is safe to do so. # RVCT tends to give very conservative alignment (1 word) to # segments that could be paged aligned. if new_segment.vaddr % machine.min_page_size() == 0 and \ new_segment.align < machine.min_page_size(): new_segment.align = machine.min_page_size() # Rename the sections in the segment, giving each the supplied # prefix if new_segment.has_sections(): for section in new_segment.get_sections(): assert section.link is None sec_name = section.name #strip GNU leading dots in section names if sec_name[0] == ".": sec_name = sec_name[1:] section.name = "%s.%s" % (section_prefix, sec_name) if section_prefix != "kernel": for symbol in section.symbols: symbol.name = "%s-%s" % (section_prefix, symbol.name) self.elf.add_section(section) iseg = ImageSegment(new_segment, segment_index, file_type, attrs, pools) if attrs.protected: if self.protected_segment is not None: raise MergeError, \ 'Only one segment can be declared protected. ' \ 'Found "%s" and "%s".' % \ (self.protected_segment.get_attrs().abs_name(), attrs.abs_name()) self.protected_segment = iseg # Kernel segments need to be at the start of the memory pools # to place them in a different list to keep track of them. if file_type == Image.KERNEL: self.kernel_segments.append(iseg) else: self.segments.append(iseg) self.elf.add_segment(new_segment) return iseg def add_memsection(self, attrs, machine, pools): """ Create a memsection for inclusion in the image. If the data or file attributes of 'attr' are non-None, then a ELF segment will be created, otherwise the memsection will will be included in the address layout process, but will be created at runtime by Iguana server. """ new_segment = None in_image = False if attrs.file is not None or attrs.data is not None: if attrs.file is not None: the_file = open(attrs.file, 'r') data = ByteArray(the_file.read()) the_file.close() else: data = attrs.data if attrs.size is not None and len(data) < attrs.size: data.extend([0] * (attrs.size - len(data))) attrs.size = data.buffer_info()[1] * data.itemsize sect = UnpreparedElfSection(attrs.name, SHT_PROGBITS, attrs.virt_addr, data = data, flags = SHF_WRITE | SHF_ALLOC) self.elf.add_section(sect) new_segment = SectionedElfSegment(PT_LOAD, attrs.virt_addr, attrs.phys_addr, PF_R | PF_W, machine.min_page_size(), sections=[sect]) self.elf.add_segment(new_segment) in_image = True obj = ImageMemsection(new_segment, attrs, pools) # If the memsection has data that goes into the image, then # put it at the front of the list so that it will be near the # code segments. if in_image: self.memsections = [obj] + self.memsections else: self.memsections.append(obj) return obj def add_zone(self, attrs, zone): """Create a zone for inclusion in the image.""" izone = ImageZone(attrs, zone) self.zones.append(izone) return izone def add_group(self, distance, items, error_message = None): """Add an image group.""" # Generate a static group for virtual addresses. virt_group = [i.get_allocator_item(is_virtual = True) for i in items if i.get_allocator_item(is_virtual = True) is not None] if len(virt_group) != 0: group = ImageGroup(distance, virt_group, error_message, is_virtual = True) self.groups.append(group) # Generate a static group for physical addresses. phys_group = [i.get_allocator_item(is_virtual = False) for i in items if i.get_allocator_item(is_virtual = False) is not None] if len(phys_group) != 0: group = ImageGroup(distance, phys_group, error_message, is_virtual = False) self.groups.append(group) def dump(self): """ Print out a virtual and physical memory map of the final image. """ virtual_objects = {} physical_objects = {} for obj in self.objects: if obj.attrs.virt_addr is not None: vbase = obj.attrs.virt_addr vend = vbase + obj.attrs.size - 1 virtual_objects[vbase, vend] = obj.attrs.abs_name() if obj.attrs.phys_addr is not None: pbase = obj.attrs.phys_addr pend = pbase + obj.attrs.size - 1 physical_objects[pbase, pend] = obj.attrs.abs_name() print "VIRTUAL:" for (base, end), name in sorted(virtual_objects.items()): print " <%08x:%08x> %s" % (base, end, name) print "PHYSICAL:" for (base, end), name in sorted(physical_objects.items()): print " <%08x:%08x> %s" % (base, end, name)