##############################################################################
# Australian Public Licence B (OZPLB)
# 
# Version 1-0
# 
# Copyright (c) 2007, Open Kernel Labs, Inc.
# 
# All rights reserved. 
# 
# Developed by: Embedded, Real-time and Operating Systems Program (ERTOS)
#               National ICT Australia
#               http://www.ertos.nicta.com.au
# 
# Permission is granted by National ICT Australia, free of charge, to
# any person obtaining a copy of this software and any associated
# documentation files (the "Software") to deal with the Software without
# restriction, including (without limitation) the rights to use, copy,
# modify, adapt, merge, publish, distribute, communicate to the public,
# sublicense, and/or sell, lend or rent out copies of the Software, and
# to permit persons to whom the Software is furnished to do so, subject
# to the following conditions:
# 
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimers.
# 
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimers in the documentation and/or other materials provided
#       with the distribution.
# 
#     * Neither the name of National ICT Australia, nor the names of its
#       contributors, may be used to endorse or promote products derived
#       from this Software without specific prior written permission.
# 
# EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
# PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS", AND
# NATIONAL ICT AUSTRALIA AND ITS CONTRIBUTORS MAKE NO REPRESENTATIONS,
# WARRANTIES OR CONDITIONS OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS
# REGARDING THE CONTENTS OR ACCURACY OF THE SOFTWARE, OR OF TITLE,
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT,
# THE ABSENCE OF LATENT OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF
# ERRORS, WHETHER OR NOT DISCOVERABLE.
# 
# TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL
# NATIONAL ICT AUSTRALIA OR ITS CONTRIBUTORS BE LIABLE ON ANY LEGAL
# THEORY (INCLUDING, WITHOUT LIMITATION, IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHERWISE) FOR ANY CLAIM, LOSS, DAMAGES OR OTHER
# LIABILITY, INCLUDING (WITHOUT LIMITATION) LOSS OF PRODUCTION OR
# OPERATION TIME, LOSS, DAMAGE OR CORRUPTION OF DATA OR RECORDS; OR LOSS
# OF ANTICIPATED SAVINGS, OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR
# OTHER ECONOMIC LOSS; OR ANY SPECIAL, INCIDENTAL, INDIRECT,
# CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES, ARISING OUT OF OR IN
# CONNECTION WITH THIS LICENCE, THE SOFTWARE OR THE USE OF OR OTHER
# DEALINGS WITH THE SOFTWARE, EVEN IF NATIONAL ICT AUSTRALIA OR ITS
# CONTRIBUTORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH CLAIM, LOSS,
# DAMAGES OR OTHER LIABILITY.
# 
# If applicable legislation implies representations, warranties, or
# conditions, or imposes obligations or liability on National ICT
# Australia or one of its contributors in respect of the Software that
# cannot be wholly or partly excluded, restricted or modified, the
# liability of National ICT Australia or the contributor is limited, to
# the full extent permitted by the applicable legislation, at its
# option, to:
# a.  in the case of goods, any one or more of the following:
# i.  the replacement of the goods or the supply of equivalent goods;
# ii.  the repair of the goods;
# iii. the payment of the cost of replacing the goods or of acquiring
#  equivalent goods;
# iv.  the payment of the cost of having the goods repaired; or
# b.  in the case of services:
# i.  the supplying of the services again; or
# ii.  the payment of the cost of having the services supplied again.
# 
# The construction, validity and performance of this licence is governed
# by the laws in force in New South Wales, Australia.

"""Process segment XML elements."""

import re
from elf.constants import PF_X, PF_W, PF_R, EM_386
from weaver import MergeError
from weaver.ezxml import Element, long_attr, bool_attr, str_attr, size_attr
import weaver.image
import weaver.pools
import weaver
import weaver.machine

# General element
Segment_el = Element("segment",
                     name      = (str_attr, "required"),
                     phys_addr = (long_attr, "optional"),
                     physpool  = (str_attr, "optional"),
                     align     = (size_attr, "optional"),
                     attach    = (str_attr,  "optional"),
                     direct    = (bool_attr, "optional"),
                     pager     = (str_attr, "optional"),
                     protected = (bool_attr, "optional"),
                     cache_policy = (str_attr, "optional"))

Heap_el = Element("heap",
                  size      = (size_attr, "optional"),
                  virt_addr = (long_attr, "optional"),
                  phys_addr = (long_attr, "optional"),
                  physpool  = (str_attr, "optional"),
                  virtpool  = (str_attr, "optional"),
                  align     = (size_attr, "optional"),
                  attach    = (str_attr,  "optional"),
                  direct    = (bool_attr, "optional"),
                  zero      = (bool_attr, "optional"),
                  pager     = (str_attr, "optional"),
                  cache_policy = (str_attr, "optional"))
                  

Patch_el = Element("patch",
                   address = (str_attr, "required"),
                   value   = (long_attr, "required"),
                   bytes   = (long_attr, "optional"))

def start_to_value(start, elf):
    """
    Convert a start value, which may be a number, a text value of a
    number or a symbol, to a number.  ELF is used to resolve the value
    of a symbol.
    """
    # FIXME: depending on the type of start is not so nice
    if start is not None and not isinstance(start, (int, long)):
        if re.match("\s*[+-]?(0x)?[0-9a-fA-F]+$", start):
            start = long(start, 0)
        else:
            sym = elf.find_symbol(start)

            if not sym:
                raise MergeError, "Symbol %s not found" % (start)

            start = sym.get_value()

    return start

def segments_hash(segments_el):
    """Convert a list of segments elements into a hash, indexed by name."""
    shash = {}

    for segment_el in segments_el:
        shash[segment_el.name] = segment_el

    return shash

def elf_segment_names(elf):
    """Return a hash of elf segment names, indexed by segment number."""
    table = {}

    seg_names_sect = elf.find_section_named(".segment_names")

    if seg_names_sect is not None:
        names = seg_names_sect.get_strings()[1:]

        for i in range(len(names)):
            table[i] = names[i]

    return table

def attach_to_elf_flags(attach):
    """Convert the attach 'rwx' string to ELF flags."""
    flags = 0L

    if attach.find('r') != -1:
        flags |= PF_R

    if attach.find('w') != -1:
        flags |= PF_W

    if attach.find('x') != -1:
        flags |= PF_X

    return flags

def make_pager_attr(pager):
    """Check that the pager value is valid."""
    VALID_PAGERS = ("none", "default", "memload")

    if pager not in VALID_PAGERS:
        raise MergeError, '"%s" is not a recognised pager.  Valid values are %s.' % (pager, VALID_PAGERS)

    if pager is 'none':
        pager = None

    return pager

def collect_patches(elf, patch_els, filename):
    """Process 'patch' elements."""
    for patch_el in patch_els:
        if re.match("\s*[+-]?(0x)?[0-9a-fA-F]+$", patch_el.address): # FIXME OKL
            # convert from a string hex representation to a number 
            patch_el.address = long(patch_el.address, 0)
            # ensure we know how big it is
            if not hasattr(patch_el, "bytes"):
                raise MergeError, \
                      "bytes attribute must be specified if patch address is a number."
        else:
            # look up address of symbol
            name = patch_el.address
            sym = elf.find_symbol(patch_el.address)

            if not sym:
                raise MergeError, \
                      "symbol %s not found in %s" % (patch_el.address, filename)
            patch_el.address = sym.get_value()

            if not hasattr(patch_el, "bytes"):
                # set the size
                patch_el.bytes = sym.get_size()
                if patch_el.bytes == 0:
                    raise MergeError, \
                          "Elf file does not specify size of symbol %s, please specify 'bytes' in this patch element" % name
        
        weaver.image.image.patch(patch_el.address, patch_el.bytes,
                                 patch_el.value)

def collect_elf_segments(elf, file_type, segment_els, section_prefix, namespace):
    """
    Process all of the segment elements in a program/kernel, etc.
    """
    elf_seg_names = elf_segment_names(elf)
    shash = segments_hash(segment_els)
    collected_segments = []

    i           = 0
    last_segment = None # The last segment processed.
    group = []
    
    for segment in elf.get_segments():
        attrs = weaver.image.image.new_attrs(namespace, for_segment = True)
        
        attrs.virt_addr = segment.get_vaddr()
        attrs.attach    = segment.get_flags()
        attrs.elf_flags = segment.get_flags()

        # The kernel is *very* picky about alignment, so use the
        # segment's alignment rules by default.
        if file_type == weaver.image.image.KERNEL:
            attrs.align  = segment.get_align()
            # XXX: The kernel image is generated very weirdly on x86.  These
            # hacks are connected to some other PC99 hacks in tools/build.py.
            if elf.get_machine() == EM_386:
                attrs.align = weaver.machine.machine.min_page_size()
                attrs.phys_addr = segment.get_paddr()

        if elf_seg_names.has_key(i):
            seg_name = elf_seg_names[i]

            attrs.name = seg_name

            if shash.has_key(seg_name):
                segment_el = shash[seg_name]

                attrs.phys_addr = getattr(segment_el, 'phys_addr', attrs.phys_addr)
                attrs.physpool  = getattr(segment_el, 'physpool', attrs.physpool)
                attrs.align     = getattr(segment_el, 'align', attrs.align)
                attrs.pager     = getattr(segment_el, 'pager', attrs.pager)
                attrs.direct    = getattr(segment_el, 'direct', attrs.direct)
                attrs.protected = getattr(segment_el, 'protected', attrs.protected)

                if hasattr(segment_el, 'attach'):
                    attrs.attach = attach_to_elf_flags(segment_el.attach)

                if hasattr(segment_el, 'pager'):
                    attrs.pager = make_pager_attr(segment_el.pager)

                if hasattr(segment_el, 'cache_policy'):
                    attrs.cache_policy = weaver.machine.machine.get_cache_policy(segment_el.cache_policy)
        else:
            seg_name = str(i)
            attrs.name = seg_name

            # QC_MODIFIED
            if shash.has_key(seg_name):
                segment_el = shash[seg_name]

                attrs.phys_addr = getattr(segment_el, 'phys_addr', attrs.phys_addr)
                attrs.physpool  = getattr(segment_el, 'physpool', attrs.physpool)
                attrs.align     = getattr(segment_el, 'align', attrs.align)
                attrs.pager     = getattr(segment_el, 'pager', attrs.pager)
                attrs.direct    = getattr(segment_el, 'direct', attrs.direct)
                attrs.protected = getattr(segment_el, 'protected', attrs.protected)

        s = weaver.image.image.add_segment(segment_index   = i,
                                           segment         = segment,
                                           section_prefix  = section_prefix,
                                           file_type       = file_type,
                                           attrs           = attrs)

        if s is not None:
            collected_segments.append(s)

            # It is possible for different segments to occupy the same
            # page of memory (yes!  really!).  To accommodate this
            # place segments that have the same flags into the same
            # static allocation groups, where sub-page allocation is
            # allowed.
            #
            # Do not set the maximum distance the segments can be
            # apart.  The distance support was originally designed
            # with this code in mind, but with testing it's been shown
            # that it is difficult to get the distance value right.

            if last_segment is not None and \
                   last_segment.get_flags() != segment.get_flags():
                weaver.image.image.add_group(None, group)
                group = []

            group.append(s)

        last_segment = segment

        i = i + 1

    weaver.image.image.add_group(None, group)

    return collected_segments
