-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UBootEnvContent: add content module for creating a U-Boot environment
This allows creating a U-Boot environment in the image, if U-Boot is configured to load its environment from a region on the sd card/emmc.
- Loading branch information
Showing
5 changed files
with
231 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
embdgen.plugins.content.UBootEnvContent | ||
======================================= | ||
|
||
.. automodule:: embdgen.plugins.content.UBootEnvContent |
17 changes: 17 additions & 0 deletions
17
embdgen-config-yaml/src/embdgen/plugins/config/yaml/validator/StringDict.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# SPDX-License-Identifier: GPL-3.0-only | ||
|
||
from typing import Dict | ||
import strictyaml as y | ||
from strictyaml.parser import YAMLChunk | ||
|
||
class StringDict(y.MapPattern): | ||
RESULT_TYPE = Dict[str, str] | ||
|
||
def __init__(self): | ||
super().__init__(y.Str(), y.Str()) | ||
|
||
def __call__(self, chunk: YAMLChunk) -> y.YAML: | ||
self.validate(chunk) | ||
v = y.YAML(chunk, validator=self) | ||
v._value = {k._value: v._value for k, v in v._value.items()} | ||
return v |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
embdgen-core/src/embdgen/plugins/content/UBootEnvContent.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# SPDX-License-Identifier: GPL-3.0-only | ||
|
||
from io import BufferedIOBase | ||
from pathlib import Path | ||
import struct | ||
from typing import Dict, Optional | ||
import zlib | ||
|
||
from embdgen.core.content import BinaryContent | ||
from embdgen.core.utils.class_factory import Config | ||
|
||
|
||
@Config("vars") | ||
@Config("file") | ||
class UBootEnvContent(BinaryContent): | ||
""" | ||
U-Boot Environment region | ||
The variables passed in using file and vars is merged | ||
with variables defined in vars overwriting variables | ||
defined in the variables file. | ||
""" | ||
CONTENT_TYPE ="uboot_env" | ||
|
||
vars: Optional[Dict[str, str]] = None | ||
"""Variables placed in the environment""" | ||
|
||
_file: Optional[Path] = None | ||
|
||
_data: bytes | ||
|
||
@property | ||
def file(self) -> Optional[Path]: | ||
"""Variable file with key=value pairs""" | ||
return self._file | ||
|
||
@file.setter | ||
def file(self, value: Optional[Path]): | ||
if value and not value.exists(): | ||
raise Exception(f"File {value} does not exist") | ||
self._file = value | ||
|
||
def _parse_file(self) -> Dict[str, str]: | ||
if not self.file: | ||
return {} | ||
out = {} | ||
with self.file.open() as f: | ||
for i, line in enumerate(f): | ||
line = line.strip() | ||
if line.startswith("#"): | ||
continue | ||
parts = line.split("=", 1) | ||
if len(parts) != 2: | ||
raise Exception(f"Invalid entry in U-Boot environment file (line {i + 1})") | ||
out[parts[0].strip()] = parts[1].strip() | ||
return out | ||
|
||
def _merged_vars(self) -> Dict[str, str]: | ||
out: Dict[str, str] = self._parse_file() | ||
|
||
if self.vars: | ||
out.update(self.vars) | ||
|
||
return out | ||
|
||
def prepare(self) -> None: | ||
if self.size.is_undefined: | ||
raise Exception("Size for U-Boot environment must be defined") | ||
|
||
self._data = b"" | ||
|
||
res_vars = self._merged_vars() | ||
for key in sorted(res_vars.keys()): | ||
self._data += f"{key}={res_vars[key]}\0".encode() | ||
|
||
self._data += b"\0" | ||
|
||
if len(self._data) + 4 > self.size.bytes: | ||
overflow = 4 + len(self._data) - self.size.bytes | ||
raise Exception(f"U-Boot environment variables overflow storage area by {overflow} bytes") | ||
|
||
self._data += b"\xFF" * (self.size.bytes - len(self._data) - 4) | ||
self._data = struct.pack("<I", zlib.crc32(self._data)) + self._data | ||
|
||
def do_write(self, file: BufferedIOBase) -> None: | ||
file.write(self._data) | ||
|
||
def __repr__(self) -> str: | ||
return f"{self.__class__.__name__}(...)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
from io import BytesIO | ||
import pytest | ||
from pathlib import Path | ||
|
||
from embdgen.plugins.content.UBootEnvContent import UBootEnvContent | ||
from embdgen.core.utils.SizeType import SizeType | ||
|
||
class TestUBootEnvContent: | ||
def test_assign_invalid_file(self): | ||
with pytest.raises(Exception, match="File /path/i-do-not-exist does not exist"): | ||
UBootEnvContent().file = Path("/path/i-do-not-exist") | ||
|
||
def test_no_size(self): | ||
obj = UBootEnvContent() | ||
with pytest.raises(Exception, match="Size for U-Boot environment must be defined"): | ||
obj.prepare() | ||
|
||
def test_empty(self): | ||
obj = UBootEnvContent() | ||
obj.size = SizeType(1024) | ||
obj.prepare() | ||
|
||
img = BytesIO() | ||
obj.write(img) | ||
buf = img.getbuffer() | ||
assert buf == b"\x97\xdb\x74\x10" + b"\0" + b"\xff" * 1019 | ||
|
||
def test_file(self, tmp_path: Path): | ||
uboot_env = tmp_path / "uboot.env" | ||
uboot_env.write_text( | ||
""" | ||
C=D | ||
A=B | ||
#D=E | ||
Empty= | ||
A=Some other string with spaces | ||
""".strip() | ||
) | ||
obj = UBootEnvContent() | ||
obj.file = uboot_env | ||
obj.size = SizeType(1024) | ||
obj.prepare() | ||
img = BytesIO() | ||
obj.write(img) | ||
buf = img.getbuffer() | ||
data = b"A=Some other string with spaces\0C=D\0Empty=\0\0" | ||
assert buf == b"\x08\x88\x28\xfa" + data + b"\xff" * (1024 - 4 - len(data)) | ||
|
||
def test_file_invalid_line(self, tmp_path: Path): | ||
uboot_env = tmp_path / "uboot.env" | ||
uboot_env.write_text( | ||
""" | ||
C=D | ||
A | ||
#D=E | ||
A=Some other string with spaces | ||
""".strip() | ||
) | ||
obj = UBootEnvContent() | ||
obj.file = uboot_env | ||
obj.size = SizeType(1024) | ||
with pytest.raises(Exception, match=r"Invalid entry in U-Boot environment file \(line 2\)"): | ||
obj.prepare() | ||
|
||
def test_vars(self): | ||
obj = UBootEnvContent() | ||
obj.vars = { | ||
"C": "D", | ||
"A": "Some other string with spaces", | ||
"Empty": "" | ||
} | ||
obj.size = SizeType(1024) | ||
obj.prepare() | ||
img = BytesIO() | ||
obj.write(img) | ||
buf = img.getbuffer() | ||
data = b"A=Some other string with spaces\0C=D\0Empty=\0\0" | ||
assert buf == b"\x08\x88\x28\xfa" + data + b"\xff" * (1024 - 4 - len(data)) | ||
|
||
def test_file_and_vars(self, tmp_path: Path): | ||
uboot_env = tmp_path / "uboot.env" | ||
uboot_env.write_text( | ||
""" | ||
C=D | ||
Some Var = I will be overwritten by vars | ||
""".strip() | ||
) | ||
obj = UBootEnvContent() | ||
obj.file = uboot_env | ||
obj.vars = { | ||
"Some Var": "I overwrite var A from file", | ||
"NewVar": "Additional var in vars" | ||
} | ||
obj.size = SizeType(1024) | ||
obj.prepare() | ||
img = BytesIO() | ||
obj.write(img) | ||
buf: memoryview = img.getbuffer() | ||
data = b"C=D\0NewVar=Additional var in vars\0Some Var=I overwrite var A from file\0\0" | ||
assert buf == b"\x9f\xac\xf1\xb0" + data + b"\xff" * (1024 - 4 - len(data)) | ||
|
||
def test_overflow(self): | ||
obj = UBootEnvContent() | ||
obj.size = SizeType(100) | ||
obj.vars = { | ||
"A": "x" * (100 - 4 - len(b"A=\0\0")) | ||
} | ||
obj.prepare() | ||
|
||
obj.vars = { | ||
"A": "x" * (100 - 4 - len(b"A=\0\0") + 1) | ||
} | ||
with pytest.raises(Exception, match=r"U-Boot environment variables overflow storage area by 1 byte"): | ||
obj.prepare() | ||
|
||
obj.vars = { | ||
"A": "x" * (100 - 4 - len(b"A=\0\0") + 548) | ||
} | ||
with pytest.raises(Exception, match=r"U-Boot environment variables overflow storage area by 548 byte"): | ||
obj.prepare() |