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