Skip to content

Commit

Permalink
Validate the firmware when packing and include manifest in the update
Browse files Browse the repository at this point in the history
  • Loading branch information
sylane committed Nov 13, 2024
1 parent c228b02 commit a816271
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 10 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ and this project adheres to
- The deploy command now generate a MANIFEST files that contains information
about the deployed software in a term file that can be read with
file:consult/1.
- The pack command now validates the firmware by creating an image, writing the
firmware and trying to mount it. If the firmware contains a software build
manifest, it will also be included in the update manifest.

## [2.7.1] - 2024-10-11

Expand Down
3 changes: 2 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
bbmustache,
hackney,
edifa,
grisp_update_packager
%FIXME: Revert to HEX package when released
{grisp_update_packager, {git, "https://github.com/grisp/grisp_update_packager.git", {branch, "main"}}}
]}.
{project_plugins, [rebar3_ex_doc]}.
{hex, [{doc, ex_doc}]}.
Expand Down
13 changes: 7 additions & 6 deletions rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.12.0">>},1},
{<<"edifa">>,{pkg,<<"edifa">>,<<"1.0.0">>},0},
{<<"erlexec">>,{pkg,<<"erlexec">>,<<"2.0.7">>},1},
{<<"grisp_update_packager">>,{pkg,<<"grisp_update_packager">>,<<"1.0.0">>},0},
{<<"grisp_update_packager">>,
{git,"https://github.com/grisp/grisp_update_packager.git",
{ref,"ce9de02d36a8b3c8596c72ce60b380386df19818"}},
0},
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.20.1">>},0},
{<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},1},
{<<"mapz">>,{pkg,<<"mapz">>,<<"2.4.0">>},0},
Expand All @@ -14,14 +17,13 @@
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.7">>},1},
{<<"termseal">>,{pkg,<<"termseal">>,<<"0.1.1">>},1},
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1},
{<<"uuid">>,{pkg,<<"uuid_erl">>,<<"2.0.7">>},1}]}.
{<<"uuid">>,{pkg,<<"uuid_erl">>,<<"2.0.4">>},1}]}.
[
{pkg_hash,[
{<<"bbmustache">>, <<"0CABDCE0DB9FE6D3318131174B9F2B351328A4C0AFBEB3E6E99BB0E02E9B621D">>},
{<<"certifi">>, <<"2D1CCA2EC95F59643862AF91F001478C9863C2AC9CB6E2F89780BFD8DE987329">>},
{<<"edifa">>, <<"0F1A01A0C79B7135F334B3FCEEB624F0574C5ED3E4554B06C8664AADA6A339C8">>},
{<<"erlexec">>, <<"76D0BC7487929741B5BB9F74DA2AF5DAF1492134733CF9A05C7AAA278B6934C5">>},
{<<"grisp_update_packager">>, <<"0532CCD0955398FAC4E1DE90FE85DB941CA609A2F4E066CFFE01ECE41DCCE119">>},
{<<"hackney">>, <<"8D97AEC62DDDDD757D128BFD1DF6C5861093419F8F7A4223823537BAD5D064E2">>},
{<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>},
{<<"mapz">>, <<"77A8E38B69BAB16C5D3EBD44E6C619F8AF1F1598B0CAAE301D266605A0865756">>},
Expand All @@ -32,13 +34,12 @@
{<<"ssl_verify_fun">>, <<"354C321CF377240C7B8716899E182CE4890C5938111A1296ADD3EC74CF1715DF">>},
{<<"termseal">>, <<"C9D93D4FF638EE99F9377D3438FC7AD132D2901EBBAF10C54F8DEA1D7E24D61C">>},
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>},
{<<"uuid">>, <<"B2078D2CC814F53AFA52D36C91E08962C7E7373585C623F4C0EA6DFB04B2AF94">>}]},
{<<"uuid">>, <<"77C3E3EE1E1701A2856CE945846D7CEB71931C60633A305D0B0FEAE03B2B3B5C">>}]},
{pkg_hash_ext,[
{<<"bbmustache">>, <<"688B33A4D5CC2D51F575ADF0B3683FC40A38314A2F150906EDCFC77F5B577B3B">>},
{<<"certifi">>, <<"EE68D85DF22E554040CDB4BE100F33873AC6051387BAF6A8F6CE82272340FF1C">>},
{<<"edifa">>, <<"A1E010561E7D236A24C668D95626BE2BFE082ED0331CE1E6798BE0CD43F59A7B">>},
{<<"erlexec">>, <<"AF2DD940BB8E32F5AA40A65CB455DCAA18F5334FD3507E9BFD14A021E9630897">>},
{<<"grisp_update_packager">>, <<"47BFDF6FADBED4B8342205A812198CF913E0223A98A775CAAE5D2FB5D5CF751C">>},
{<<"hackney">>, <<"FE9094E5F1A2A2C0A7D10918FEE36BFEC0EC2A979994CFF8CFE8058CD9AF38E3">>},
{<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>},
{<<"mapz">>, <<"4B68DF5CF0522E0D6545DF7B681BC052865CDB78405AD4CC9C55FE45EE7B25BE">>},
Expand All @@ -49,5 +50,5 @@
{<<"ssl_verify_fun">>, <<"FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8">>},
{<<"termseal">>, <<"466280936214AF1894FC431642E83341B7D13580A3F3485820A2D300C5CAEB49">>},
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>},
{<<"uuid">>, <<"4E4C5CA3461DC47C5E157ED42AA3981A053B7A186792AF972A27B14A9489324E">>}]}
{<<"uuid">>, <<"7A4CCD1C151D9B88B4383FA802BCCF9BCB3754B7F53D7CAA164D51A14A6652E4">>}]}
].
91 changes: 88 additions & 3 deletions src/grisp_tools_pack.erl
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,15 @@ package(State) ->
grisp_tools_util:weave(State, [
fun expand_bootloader/1,
fun expand_system/1,
fun create_image/1,
fun create_partitions/1,
fun copy_firmware/1,
fun extract_manifest/1,
fun close_image/1,
fun build_package/1,
fun cleanup/1
], [
fun close_image/1,
fun cleanup/1
]).

Expand All @@ -106,30 +112,109 @@ expand_system(State = #{system := SysPath})
expand_system(State) ->
State.

build_package(State = #{package := PackageFile}) ->
create_image(State = #{temp_dir := TempDir}) ->
Opts = edifa_opts(State, #{temp_dir => TempDir}),
ImageFile = filename:join(TempDir, "emmc.img"),
case edifa:create(ImageFile, ?GRISP2_IMAGE_SIZE, Opts) of
{ok, Pid, State2} ->
State2#{edifa_pid => Pid};
{error, Reason, State2} ->
event(State2, [{error, Reason}])
end.

create_partitions(State = #{edifa_pid := Pid}) ->
Opts = edifa_opts(State),
case edifa:partition(Pid, mbr, ?GRISP2_PARTITIONS, Opts) of
{ok, [_, _] = Partitions, State2} ->
State2#{partitions => Partitions};
{error, Reason, State2} ->
event(State2, [{error, Reason}])
end.

copy_firmware(State = #{edifa_pid := Pid, system := ExpPath}) ->
Opts = edifa_opts(State, #{
count => ?GRISP2_SYSTEM_SIZE,
seek => ?GRISP2_RESERVED_SIZE
}),
case edifa:write(Pid, ExpPath, Opts) of
{ok, State2} -> State2;
{error, Reason, State2} ->
event(State2, [{error, Reason}])
end.

extract_manifest(State = #{edifa_pid := Pid, partitions := [PartId | _]}) ->
Opts = edifa_opts(State),
case edifa:mount(Pid, PartId, Opts) of
{error, Reason, State2} ->
event(State2, [{error, Reason}]);
{ok, MountPoint, State2} ->
ManifestPath = filename:join(MountPoint, "MANIFEST"),
Manifest = case file:consult(ManifestPath) of
{error, _Reason} -> undefined;
{ok, Term} -> Term
end,
Opts2 = edifa_opts(State2),
case edifa:unmount(Pid, PartId, Opts2) of
{error, Reason, State2} ->
event(State2, [{error, Reason}]);
{ok, State3} ->
event(State3#{manifest => Manifest},
[{manifest, Manifest}])
end
end.

close_image(State = #{edifa_pid := Pid}) ->
case edifa:close(Pid, edifa_opts(State)) of
{ok, State2} -> maps:remove(edifa_pid, State2);
{error, Reason, State2} ->
event(State2, [{error, Reason}])
end.

build_package(State = #{package := PackageFile, manifest := Manifest}) ->
PackagerOpts1 = maps:with([name, version, block_size,
key_file, system, bootloader], State),
PackagerOpts2 = PackagerOpts1#{
tarball => true,
mbr => ?GRISP2_PARTITIONS
mbr => ?GRISP2_PARTITIONS,
manifest => Manifest
},
case grisp_update_packager:package(PackageFile, PackagerOpts2) of
ok -> event(State, [{done, PackageFile}]);
{error, Reason} -> event(State, [{error, Reason}])
end.

cleanup(State) ->
cleanup_temp_dir(State).
cleanup_temp_dir(cleanup_image(State)).

cleanup_temp_dir(State = #{temp_dir := TempDir, cleanup_temp_dir := true}) ->
{_, State2} = shell(State, "rm -rf '~s'", [TempDir]),
State2#{cleanup_temp_dir => false};
cleanup_temp_dir(State) ->
State.

cleanup_image(State = #{edifa_pid := Pid}) ->
case edifa:close(Pid, edifa_opts(State)) of
{ok, State2} -> maps:remove(edifa_pid, State2);
{error, _Reason, State2} -> maps:remove(edifa_pid, State2)
end;
cleanup_image(State) ->
State.


%--- Internal ------------------------------------------------------------------

edifa_opts(State) ->
edifa_opts(State, #{}).

edifa_opts(State, Opts) ->
Opts#{
log_handler => fun edifa_log_hanler/2,
log_state => State
}.

edifa_log_hanler(Event, State) ->
event(State, [Event]).

shell(State, Fmt, Args) ->
Cmd = binary_to_list(iolist_to_binary(io_lib:format(Fmt, Args))),
{{ok, Output}, State2} = grisp_tools_util:shell(State, Cmd),
Expand Down

0 comments on commit a816271

Please sign in to comment.