summaryrefslogtreecommitdiff
path: root/cxmanage_api/image.py
blob: 8f3011a27dfb0f99fbd0a40b840ecb2798e75dbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"""Calxeda: image.py"""


# Copyright (c) 2012-2013, Calxeda Inc.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Calxeda Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.


import os
import subprocess

from cxmanage_api import temp_file
from cxmanage_api.simg import create_simg, has_simg
from cxmanage_api.simg import valid_simg, get_simg_contents
from cxmanage_api.cx_exceptions import InvalidImageError


class Image(object):
    """An Image consists of: an image type, a filename, and SIMG header info.

    >>> from cxmanage_api.image import Image
    >>> img = Image(filename='spi_highbank.bin', image_type='PACAKGE')

    :param filename: Path to the image.
    :type filename: string
    :param image_type: Type of image. [CDB, BOOT_LOG, SOC_ELF]
    :type image_type: string
    :param simg: Path to the simg file.
    :type simg: string
    :param daddr: The daddr field in the SIMG Header.
    :type daddr: integer
    :param skip_crc32: Flag to skip (or not) CRC32 checking.
    :type skip_crc32: boolean
    :param version: Image version.
    :type version: string

    :raises ValueError: If the image file does not exist.
    :raises InvalidImageError: If the file is NOT a valid image.

    """

    # pylint: disable=R0913
    def __init__(self, filename, image_type, simg=None, daddr=None,
                  skip_crc32=False, version=None):
        """Default constructor for the Image class."""
        self.filename = filename
        self.type = image_type
        self.daddr = daddr
        self.skip_crc32 = skip_crc32
        self.version = version

        if (not os.path.exists(filename)):
            raise ValueError("File %s does not exist" % filename)

        if (simg == None):
            contents = open(filename).read()
            self.simg = has_simg(contents)
        else:
            self.simg = simg

        if (not self.verify()):
            raise InvalidImageError("%s is not a valid %s image" %
                                    (filename, image_type))

    def render_to_simg(self, priority, daddr):
        """Creates a SIMG file.

        >>> img.render_to_simg(priority=1, daddr=0)
        >>> 'spi_highbank.bin'

        :param priority: SIMG header priority value.
        :type priority: integer
        :param daddr: SIMG daddr field value.
        :type daddr: integer

        :returns: The file name of the image.
        :rtype: string

        :raises InvalidImageError: If the SIMG image is not valid.

        """
        filename = self.filename
        # Create new image if necessary
        if (not self.simg):
            contents = open(filename).read()
            # Figure out daddr
            if (self.daddr != None):
                daddr = self.daddr
            # Create simg
            align = (self.type in ["CDB", "BOOT_LOG"])
            simg = create_simg(contents, priority=priority, daddr=daddr,
                    skip_crc32=self.skip_crc32, align=align,
                    version=self.version)
            filename = temp_file()
            with open(filename, "w") as file_:
                file_.write(simg)

        # Make sure the simg was built correctly
        if (not valid_simg(open(filename).read())):
            raise InvalidImageError("%s is not a valid SIMG" %
                    os.path.basename(self.filename))

        return filename

    def size(self):
        """Return the full size of this image (as an SIMG)

        >>> img.size()
        2174976

        :returns: The size of the image file in bytes.
        :rtype: integer

        """
        if (self.simg):
            return os.path.getsize(self.filename)
        else:
            contents = open(self.filename).read()
            align = (self.type in ["CDB", "BOOT_LOG"])
            simg = create_simg(contents, skip_crc32=True, align=align)
            return len(simg)

    def verify(self):
        """Returns true if the image is valid, false otherwise.

        >>> img.verify()
        True

        :returns: Whether or not the image file is valid.
        :rtype: boolean

        """
        if (self.type == "SOC_ELF" and not self.simg):
            try:
                file_process = subprocess.Popen(["file", self.filename],
                                                stdout=subprocess.PIPE)
                file_type = file_process.communicate()[0].split()[1]

                if (file_type != "ELF"):
                    return False
            except OSError:
                # "file" tool wasn't found, just continue without it
                # typically located: /usr/bin/file
                pass

        if (self.type in ["CDB", "BOOT_LOG"]):
            # Look for "CDBH"
            contents = open(self.filename).read()
            if (self.simg):
                contents = get_simg_contents(contents)
            if (contents[:4] != "CDBH"):
                return False
        return True


# End of file: ./image.py