blob: a38d9cb64aeec3f18bd952786c3dba93dc24de54 [file] [log] [blame]
Chris Frantzd2585452021-11-05 12:18:41 -07001// Copyright lowRISC contributors.
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::{ensure, Result};
6use erased_serde::Serialize;
7use std::any::Any;
Jon Flatleycb2775f2022-05-02 15:31:01 -04008use std::convert::TryInto;
Jon Flatley17097802022-05-05 12:11:31 -04009use std::fs::File;
10use std::io::Write;
Chris Frantzd2585452021-11-05 12:18:41 -070011use std::path::PathBuf;
12use structopt::StructOpt;
13
14use opentitanlib::app::command::CommandDispatch;
15use opentitanlib::app::TransportWrapper;
16
Jon Flatleye0f76bf2022-04-28 11:55:35 -040017use opentitanlib::image::image::{self, ImageAssembler};
18use opentitanlib::image::manifest_def::ManifestDef;
Chris Frantzd2585452021-11-05 12:18:41 -070019use opentitanlib::util::parse_int::ParseInt;
20
21/// Bootstrap the target device.
22#[derive(Debug, StructOpt)]
23pub struct AssembleCommand {
24 #[structopt(
25 short,
26 long,
27 parse(try_from_str=usize::from_str),
28 default_value="1048576",
29 help="The size of the image to assemble"
30 )]
31 size: usize,
32 #[structopt(
33 short,
34 long,
35 parse(try_from_str),
36 default_value = "true",
37 help = "Whether or not the assembled image is mirrored"
38 )]
39 mirror: bool,
40 #[structopt(short, long, help = "Filename to write the assembled image to")]
41 output: PathBuf,
42 #[structopt(
43 name = "FILE",
44 min_values(1),
45 help = "One or more filename@offset specifiers to assemble into an image"
46 )]
47 filename: Vec<String>,
48}
49
50impl CommandDispatch for AssembleCommand {
51 fn run(
52 &self,
53 _context: &dyn Any,
54 _transport: &TransportWrapper,
55 ) -> Result<Option<Box<dyn Serialize>>> {
56 // The `min_values` structopt attribute should take care of this, but it doesn't.
57 ensure!(
58 !self.filename.is_empty(),
59 "You must supply at least one filename"
60 );
61 let mut image = ImageAssembler::with_params(self.size, self.mirror);
62 image.parse(&self.filename)?;
63 let content = image.assemble()?;
64 std::fs::write(&self.output, &content)?;
65 Ok(None)
66 }
67}
68
Jon Flatleye0f76bf2022-04-28 11:55:35 -040069/// Manifest show command.
70#[derive(Debug, StructOpt)]
71pub struct ManifestShowCommand {
72 #[structopt(name = "IMAGE", help = "Filename for the image to display")]
73 image: PathBuf,
74}
75
76impl CommandDispatch for ManifestShowCommand {
77 fn run(
78 &self,
79 _context: &dyn Any,
80 _transport: &TransportWrapper,
81 ) -> Result<Option<Box<dyn Serialize>>> {
Jon Flatleycb2775f2022-05-02 15:31:01 -040082 let image = image::Image::read_from_file(&self.image)?;
83 let manifest_def: ManifestDef = image.borrow_manifest()?.try_into()?;
84 Ok(Some(Box::new(manifest_def)))
Jon Flatleye0f76bf2022-04-28 11:55:35 -040085 }
86}
87
88/// Manifest update command.
89#[derive(Debug, StructOpt)]
90pub struct ManifestUpdateCommand {
91 #[structopt(name = "IMAGE", help = "Filename for the image to update")]
92 image: PathBuf,
93 #[structopt(
94 short,
95 long,
96 help = "Filename for an HJSON configuration specifying manifest fields"
97 )]
98 hjson_file: Option<PathBuf>,
99 #[structopt(short, long, help = "Filename for a signature file")]
100 signature_file: Option<PathBuf>,
101 #[structopt(
102 short,
103 long,
104 help = "Filename to write the output to instead of updating the input file"
105 )]
106 output: Option<PathBuf>,
107}
108
109impl CommandDispatch for ManifestUpdateCommand {
110 fn run(
111 &self,
112 _context: &dyn Any,
113 _transport: &TransportWrapper,
114 ) -> Result<Option<Box<dyn Serialize>>> {
115 let mut image = image::Image::read_from_file(&self.image)?;
116
117 self.hjson_file.as_ref().map(|hjson| -> Result<()> {
118 let def = ManifestDef::read_from_file(&hjson)?;
119 image.overwrite_manifest(def)
120 });
121
122 // TODO: Add signature update.
123
124 image.write_to_file(&self.output.as_ref().unwrap_or(&self.image))?;
125
126 Ok(None)
127 }
128}
129
130/// Manifest verify command.
131#[derive(Debug, StructOpt)]
132pub struct ManifestVerifyCommand {
133 #[structopt(name = "IMAGE", help = "Filename for the image to verify")]
134 image: PathBuf,
135}
136
137impl CommandDispatch for ManifestVerifyCommand {
138 fn run(
139 &self,
140 _context: &dyn Any,
141 _transport: &TransportWrapper,
142 ) -> Result<Option<Box<dyn Serialize>>> {
143 Ok(None)
144 }
145}
146
147/// Compute digest command.
148#[derive(Debug, StructOpt)]
149pub struct DigestCommand {
150 #[structopt(
151 name = "IMAGE",
152 help = "Filename for the image to calculate the digest for"
153 )]
154 image: PathBuf,
Jon Flatley17097802022-05-05 12:11:31 -0400155 #[structopt(short, long, help = "Filename for an output bin file")]
156 bin: Option<PathBuf>,
157}
158
159/// Response format for the digest command.
160#[derive(serde::Serialize)]
161pub struct DigestResponse {
162 pub digest: String,
Jon Flatleye0f76bf2022-04-28 11:55:35 -0400163}
164
165impl CommandDispatch for DigestCommand {
166 fn run(
167 &self,
168 _context: &dyn Any,
169 _transport: &TransportWrapper,
170 ) -> Result<Option<Box<dyn Serialize>>> {
Jon Flatley17097802022-05-05 12:11:31 -0400171 let image = image::Image::read_from_file(&self.image)?;
172 let digest = image.compute_digest();
173 if let Some(bin) = &self.bin {
174 let mut file = File::create(bin)?;
175 file.write(&digest)?;
176 }
177 Ok(Some(Box::new(DigestResponse {
178 digest: format!("{}", hex::encode(&digest)),
179 })))
Jon Flatleye0f76bf2022-04-28 11:55:35 -0400180 }
181}
182
183#[derive(Debug, StructOpt, CommandDispatch)]
184/// Manifest manipulation commands.
185pub enum ManifestCommand {
186 Show(ManifestShowCommand),
187 Update(ManifestUpdateCommand),
188 Verify(ManifestVerifyCommand),
189}
190
Chris Frantzd2585452021-11-05 12:18:41 -0700191#[derive(Debug, StructOpt, CommandDispatch)]
192/// Image manipulation commands.
193pub enum Image {
194 Assemble(AssembleCommand),
Jon Flatley17097802022-05-05 12:11:31 -0400195 Manifest(ManifestCommand),
196 Digest(DigestCommand),
Chris Frantzd2585452021-11-05 12:18:41 -0700197}