read and parse image binary header during firmware upload

This commit is contained in:
Laura Klünder 2023-11-17 19:04:43 +01:00
parent 14e39b2377
commit f89d069ab1
3 changed files with 16 additions and 8 deletions

View file

@ -1,6 +1,7 @@
import re import re
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import IntEnum, unique from enum import IntEnum, unique
from typing import BinaryIO, Self
from c3nav.api.utils import EnumSchemaByNameMixin from c3nav.api.utils import EnumSchemaByNameMixin
from c3nav.mesh.baseformats import (BoolFormat, EnumFormat, FixedHexFormat, FixedStrFormat, SimpleFormat, StructType, from c3nav.mesh.baseformats import (BoolFormat, EnumFormat, FixedHexFormat, FixedStrFormat, SimpleFormat, StructType,
@ -227,7 +228,7 @@ class FirmwareImageFileHeader(StructType):
class FirmwareImageExtendedFileHeader(StructType): class FirmwareImageExtendedFileHeader(StructType):
wp_pin: int = field(metadata={"format": SimpleFormat('B')}) wp_pin: int = field(metadata={"format": SimpleFormat('B')})
drive_settings: int = field(metadata={"format": SimpleFormat('3B')}) drive_settings: int = field(metadata={"format": SimpleFormat('3B')})
chip_id: ChipType = field(metadata={"format": EnumFormat('H')}) chip: ChipType = field(metadata={"format": EnumFormat('H')})
min_chip_rev_old: int = field(metadata={"format": SimpleFormat('B')}) min_chip_rev_old: int = field(metadata={"format": SimpleFormat('B')})
min_chip_rev: tuple[int, int] = field(metadata={"format": ChipRevFormat()}) min_chip_rev: tuple[int, int] = field(metadata={"format": ChipRevFormat()})
max_chip_rev: tuple[int, int] = field(metadata={"format": ChipRevFormat()}) max_chip_rev: tuple[int, int] = field(metadata={"format": ChipRevFormat()})
@ -241,3 +242,8 @@ class FirmwareImage(StructType):
ext_header: FirmwareImageExtendedFileHeader ext_header: FirmwareImageExtendedFileHeader
first_segment_headers: tuple[int, int] = field(metadata={"format": SimpleFormat('2I')}, repr=False) first_segment_headers: tuple[int, int] = field(metadata={"format": SimpleFormat('2I')}, repr=False)
app_desc: FirmwareAppDescription app_desc: FirmwareAppDescription
@classmethod
def from_file(cls, file: BinaryIO) -> Self:
result, data = cls.decode(file.read(FirmwareImage.get_min_size()))
return result

View file

@ -412,8 +412,7 @@ class FirmwareBuild(models.Model):
@cached_property @cached_property
def firmware_image(self) -> FirmwareImage: def firmware_image(self) -> FirmwareImage:
firmware_image, remaining = FirmwareImage.decode(self.binary.open('rb').read()[:FirmwareImage.get_min_size()]) return FirmwareImage.from_file(self.binary.open('rb'))
return firmware_image
class FirmwareBuildBoard(models.Model): class FirmwareBuildBoard(models.Model):

View file

@ -88,8 +88,6 @@ def firmware_project_description(request, firmware_id: int, variant: str):
class UploadFirmwareBuildSchema(Schema): class UploadFirmwareBuildSchema(Schema):
variant: str = APIField(..., example="c3uart") variant: str = APIField(..., example="c3uart")
chip: ChipType = APIField(..., example=ChipType.ESP32_C3.name)
sha256_hash: str = APIField(..., regex=r"^[0-9a-f]{64}$")
boards: list[BoardType] = APIField(..., example=[BoardType.C3NAV_LOCATION_PCB_REV_0_2.name, ]) boards: list[BoardType] = APIField(..., example=[BoardType.C3NAV_LOCATION_PCB_REV_0_2.name, ])
project_description: dict = APIField(..., title='project_description.json contents') project_description: dict = APIField(..., title='project_description.json contents')
uploaded_filename: str = APIField(..., example="firmware.bin") uploaded_filename: str = APIField(..., example="firmware.bin")
@ -143,16 +141,21 @@ def firmware_upload(request, firmware_data: UploadFirmwareSchema, binary_files:
# if sha256_bin_file != build_data.sha256_hash: # if sha256_bin_file != build_data.sha256_hash:
# raise ValueError # raise ValueError
try:
image = FirmwareImage.from_file(binary_files_by_name[build_data.uploaded_filename].open('rb'))
except ValueError:
raise APIRequestValidationFailed(f"Can't parse binary image {build_data.uploaded_filename}")
build = version.builds.create( build = version.builds.create(
variant=build_data.variant, variant=build_data.variant,
chip=build_data.chip, chip=image.ext_header.chip.name,
sha256_hash=build_data.sha256_hash, sha256_hash=image.app_desc.app_elf_sha256,
project_description=build_data.project_description, project_description=build_data.project_description,
binary=binary_files_by_name[build_data.uploaded_filename], binary=binary_files_by_name[build_data.uploaded_filename],
) )
for board in build_data.boards: for board in build_data.boards:
build.firmwarebuildboard_set.create(board=board) build.firmwarebuildboard_set.create(board=board.name)
except IntegrityError: except IntegrityError:
raise APIConflict('Firmware version already exists.') raise APIConflict('Firmware version already exists.')