Skip to content

Commit b9523cf

Browse files
committed
Reuse data segments implementation from imxsb project, start implementing smx file parser.
1 parent b47861d commit b9523cf

15 files changed

+1898
-5
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ will exist installation packages later, at this moment use raw sources.
2828
In case of development clone this repo into your PC and install all dependencies:
2929

3030
``` bash
31-
$ git clone https://github.com/molejar/imxsb.git
32-
$ cd imxsb
31+
$ git clone https://github.com/molejar/imxmi.git
32+
$ cd imxmi
3333
$ pip install -r requirements.txt
3434
```
3535

core/config.py

Whitespace-only changes.

core/fs/ext.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright (c) 2019 Martin Olejar
2+
#
3+
# SPDX-License-Identifier: BSD-3-Clause
4+
# The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution
5+
# or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText
6+
7+
8+
########################################################################################################################
9+
# helper functions
10+
########################################################################################################################
11+
12+
def size_fmt(num, use_kibibyte=True):
13+
base, suffix = [(1000., 'B'), (1024., 'iB')][use_kibibyte]
14+
for x in ['B'] + [x + suffix for x in list('kMGTP')]:
15+
if -base < num < base:
16+
break
17+
num /= base
18+
return "{0:3.1f} {1:s}".format(num, x)
19+
20+
21+
########################################################################################################################
22+
# Ext2/3/4 classes
23+
########################################################################################################################
24+
25+
class ExtX(object):
26+
27+
READ_SECTOR_SIZE = 1024
28+
29+
def __init__(self, stream, offset, size):
30+
"""
31+
:param stream:
32+
:param offset:
33+
:param size:
34+
"""
35+
self._io = stream
36+
self._io_offset = offset
37+
self._size = size
38+
39+
def info(self):
40+
nfo = str()
41+
nfo += " " + "-" * 60 + "\n"
42+
nfo += " RootFS Image: {}\n".format(size_fmt(self._size))
43+
nfo += " " + "-" * 60 + "\n"
44+
return nfo
45+
46+
def save_as(self, file_path):
47+
assert isinstance(file_path, str)
48+
49+
file_size = self._size
50+
self._io.seek(self._io_offset)
51+
with open(file_path, "wb") as f:
52+
while file_size > 0:
53+
f.write(self._io.read(self.READ_SECTOR_SIZE if file_size > self.READ_SECTOR_SIZE else file_size))
54+
file_size -= self.READ_SECTOR_SIZE

core/segments/__init__.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
1-
# Copyright (c) 2019 Martin Olejar
1+
# Copyright (c) 2017-2019 Martin Olejar
22
#
33
# SPDX-License-Identifier: BSD-3-Clause
44
# The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution
55
# or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText
6+
7+
from .fdt import DatSegFDT, InitErrorFDT
8+
from .dcd import DatSegDCD, InitErrorDCD
9+
from .imx import DatSegIMX2, DatSegIMX2B, DatSegIMX3, InitErrorIMX
10+
from .raw import DatSegRAW, InitErrorRAW
11+
from .uboot import DatSegUBI, DatSegUBX, DatSegUBT, DatSegUEV, InitErrorUBI, InitErrorUBX, InitErrorUBT, InitErrorUEV
12+
13+
__all__ = [
14+
'DatSegFDT',
15+
'DatSegDCD',
16+
'DatSegIMX2',
17+
'DatSegIMX2B',
18+
'DatSegIMX3',
19+
'DatSegRAW',
20+
'DatSegUBI',
21+
'DatSegUBX',
22+
'DatSegUBT',
23+
'DatSegUEV',
24+
# Errors
25+
'InitErrorFDT',
26+
'InitErrorDCD',
27+
'InitErrorIMX',
28+
'InitErrorRAW',
29+
'InitErrorUBI',
30+
'InitErrorUBX',
31+
'InitErrorUBT'
32+
]

core/segments/base.py

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Copyright (c) 2017-2019 Martin Olejar
2+
#
3+
# SPDX-License-Identifier: BSD-3-Clause
4+
# The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution
5+
# or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText
6+
7+
import os
8+
9+
10+
def get_full_path(root, *path_list):
11+
"""
12+
:param root:
13+
:param path:
14+
:return:
15+
"""
16+
ret_path = []
17+
for path in path_list:
18+
file_path = ""
19+
for abs_path in [path, os.path.join(root, path)]:
20+
abs_path = os.path.normpath(abs_path)
21+
if os.path.exists(abs_path):
22+
file_path = abs_path
23+
break
24+
if not file_path:
25+
raise Exception("Path: \"%s\" doesnt exist" % path)
26+
ret_path.append(file_path)
27+
28+
return ret_path
29+
30+
31+
def get_data_segment(db, name):
32+
""" Get data segments by it's name
33+
:param db:
34+
:param name: The name of data segments
35+
:return: return object
36+
"""
37+
assert isinstance(db, list), ""
38+
assert isinstance(name, str), ""
39+
40+
for item in db:
41+
if item.full_name == name.upper():
42+
return item
43+
44+
raise Exception("{} doesn't exist !".format(name))
45+
46+
47+
class DatSegBase(object):
48+
""" Data segments base class """
49+
50+
MARK = 'base'
51+
52+
@property
53+
def loaded(self):
54+
return False if self.data is None else True
55+
56+
@property
57+
def full_name(self):
58+
return '{}.{}'.format(self.name, self.MARK)
59+
60+
def __init__(self, name):
61+
""" Init BaseItem
62+
:param name: Data segments name
63+
:return Data segments object
64+
"""
65+
assert isinstance(name, str)
66+
67+
self.name = name
68+
self.data = None
69+
self.path = None
70+
self.address = None
71+
self.description = ""
72+
73+
def __str__(self):
74+
""" String representation """
75+
return self.info()
76+
77+
def __ne__(self, node):
78+
""" Check data segments inequality """
79+
return not self.__eq__(node)
80+
81+
def info(self):
82+
return self.full_name
83+
84+
def init(self, data):
85+
raise NotImplementedError()
86+
87+
def load(self, db, root_path):
88+
raise NotImplementedError()

core/segments/dcd.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Copyright (c) 2017-2019 Martin Olejar
2+
#
3+
# SPDX-License-Identifier: BSD-3-Clause
4+
# The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution
5+
# or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText
6+
7+
from imx.img import SegDCD
8+
from .base import DatSegBase, get_full_path
9+
10+
11+
class InitErrorDCD(Exception):
12+
"""Thrown when parsing a file fails"""
13+
pass
14+
15+
16+
class DatSegDCD(DatSegBase):
17+
""" Data segments class for Device Configuration Data
18+
19+
<NAME>.dcd:
20+
DESC: srt
21+
ADDR: int
22+
DATA: str or bytes (required)
23+
24+
<NAME>.dcd:
25+
DESC: srt
26+
ADDR: int
27+
FILE: path (required)
28+
"""
29+
30+
MARK = 'dcd'
31+
32+
def __init__(self, name, smx_data=None):
33+
super().__init__(name)
34+
self._txt_data = None
35+
if smx_data is not None:
36+
self.init(smx_data)
37+
38+
def init(self, smx_data):
39+
""" Initialize DCD segments
40+
:param smx_data: ...
41+
"""
42+
assert isinstance(smx_data, dict)
43+
44+
for key, val in smx_data.items():
45+
if not isinstance(key, str):
46+
raise InitErrorDCD("{}: Property name must be a string !".format(self.full_name))
47+
key = key.upper()
48+
if key == 'DESC':
49+
if not isinstance(val, str):
50+
raise InitErrorDCD("{}/DESC: Value must be a string !".format(self.full_name))
51+
self.description = val
52+
elif key == 'ADDR':
53+
if not isinstance(val, int):
54+
try:
55+
self.address = int(val, 0)
56+
except Exception as ex:
57+
raise InitErrorDCD("{}/ADDR: {}".format(self.full_name, str(ex)))
58+
else:
59+
self.address = val
60+
elif key == 'DATA':
61+
if not isinstance(val, str):
62+
raise InitErrorDCD("{}/DATA: Not supported value type !".format(self.full_name))
63+
self._txt_data = val
64+
elif key == 'FILE':
65+
if not isinstance(val, str):
66+
raise InitErrorDCD("{}/FILE: Value must be a string !".format(self.full_name))
67+
self.path = val
68+
else:
69+
raise InitErrorDCD("{}: Not supported property name \"{}\" !".format(self.full_name, key))
70+
71+
if self.path is None and self._txt_data is None:
72+
raise InitErrorDCD("{}: FILE or DATA property must be defined !".format(self.full_name))
73+
74+
def load(self, db, root_path):
75+
""" load DCD segments
76+
:param db: ...
77+
:param root_path: ...
78+
"""
79+
assert isinstance(db, list)
80+
assert isinstance(root_path, str)
81+
82+
if self.path is None:
83+
dcd_obj = SegDCD.parse_txt(self._txt_data)
84+
else:
85+
file_path = get_full_path(root_path, self.path)[0]
86+
if file_path.endswith(".txt"):
87+
with open(file_path, 'r') as f:
88+
dcd_obj = SegDCD.parse_txt(f.read())
89+
else:
90+
with open(file_path, 'rb') as f:
91+
dcd_obj = SegDCD.parse(f.read())
92+
93+
self.data = dcd_obj.export()

core/segments/fdt.py

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (c) 2017-2019 Martin Olejar
2+
#
3+
# SPDX-License-Identifier: BSD-3-Clause
4+
# The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution
5+
# or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText
6+
7+
8+
import fdt
9+
from .base import DatSegBase, get_full_path
10+
11+
12+
class InitErrorFDT(Exception):
13+
"""Thrown when parsing a file fails"""
14+
pass
15+
16+
17+
class DatSegFDT(DatSegBase):
18+
""" Data segments class for Device Configuration Data
19+
20+
<NAME>.fdt:
21+
DESC: srt
22+
ADDR: int
23+
FILE: path (required)
24+
MODE: <'disabled' or 'merge'> default ('disabled')
25+
DATA: str
26+
"""
27+
28+
MARK = 'fdt'
29+
30+
def __init__(self, name, smx_data=None):
31+
super().__init__(name)
32+
self._dts_data = None
33+
self._mode = 'disabled'
34+
if smx_data is not None:
35+
self.init(smx_data)
36+
37+
def init(self, smx_data):
38+
""" Initialize FDT segments
39+
:param smx_data: ...
40+
"""
41+
assert isinstance(smx_data, dict)
42+
43+
for key, val in smx_data.items():
44+
if not isinstance(key, str):
45+
raise InitErrorFDT("{}: Property name must be a string !".format(self.full_name))
46+
key = key.upper()
47+
if key == 'DESC':
48+
if not isinstance(val, str):
49+
raise InitErrorFDT("{}/DESC: Value must be a string !".format(self.full_name))
50+
self.description = val
51+
elif key == 'ADDR':
52+
if not isinstance(val, int):
53+
try:
54+
self.address = int(val, 0)
55+
except Exception as ex:
56+
raise InitErrorFDT("{}/ADDR: {}".format(self.full_name, str(ex)))
57+
else:
58+
self.address = val
59+
elif key == 'FILE':
60+
if not isinstance(val, str):
61+
raise InitErrorFDT("{}/FILE: Value must be a string !".format(self.full_name))
62+
self.path = val
63+
elif key == 'DATA':
64+
if not isinstance(val, str):
65+
raise InitErrorFDT("{}/DATA: Value must be a string !".format(self.full_name))
66+
self._dts_data = val
67+
elif key == 'MODE':
68+
if not isinstance(val, str):
69+
raise InitErrorFDT("{}/MODE: Value must be a string !".format(self.full_name))
70+
val = val.lower()
71+
if val not in ('disabled', 'merge'):
72+
raise InitErrorFDT("{}/MODE: Not supported value \"{}\"".format(self.full_name, val))
73+
self._mode = val
74+
else:
75+
raise InitErrorFDT("{}: Not supported property name \"{}\" !".format(self.full_name, key))
76+
77+
if self.path is None:
78+
raise InitErrorFDT("{}: FILE property must be defined !".format(self.full_name))
79+
80+
def load(self, db, root_path):
81+
""" load DCD segments
82+
:param db: ...
83+
:param root_path: ...
84+
"""
85+
assert isinstance(db, list)
86+
assert isinstance(root_path, str)
87+
88+
file_path = get_full_path(root_path, self.path)[0]
89+
if file_path.endswith(".dtb"):
90+
with open(file_path, 'rb') as f:
91+
fdt_obj = fdt.parse_dtb(f.read())
92+
else:
93+
with open(file_path, 'r') as f:
94+
fdt_obj = fdt.parse_dts(f.read())
95+
96+
if self._mode is 'merge':
97+
fdt_obj.merge(fdt.parse_dts(self._dts_data))
98+
99+
if fdt_obj.header.version is None:
100+
fdt_obj.header.version = 17
101+
102+
self.data = fdt_obj.to_dtb()

0 commit comments

Comments
 (0)