opentitanlib/backend/
mod.rs1use anyhow::Result;
6use clap::Args;
7use std::path::{Path, PathBuf};
8use thiserror::Error;
9
10use crate::app::config::process_config_file;
11use crate::app::{TransportWrapper, TransportWrapperBuilder};
12use crate::transport::{EmptyTransport, Transport};
13use crate::util::parse_int::ParseInt;
14
15mod chip_whisperer;
16mod ftdi;
17mod hyperdebug;
18mod proxy;
19mod ti50emulator;
20mod verilator;
21
22pub trait Backend {
23 type Opts: Args;
25
26 fn create_transport(common: &BackendOpts, opts: &Self::Opts) -> Result<Box<dyn Transport>>;
28}
29
30#[doc(hidden)]
31pub use inventory::submit;
32
33#[doc(hidden)]
34pub struct InterfaceRegistration {
35 pub name: &'static str,
36 pub augment_args: fn(clap::Command) -> clap::Command,
37 pub create_transport: fn(&BackendOpts, &clap::ArgMatches) -> Result<Box<dyn Transport>>,
38 pub config: Option<&'static str>,
39}
40inventory::collect!(InterfaceRegistration);
41
42#[macro_export]
43macro_rules! define_interface {
44 ($name: literal, $backend: ty) => {
45 $crate::backend::define_interface!($name, $backend, None);
46 };
47 ($name: literal, $backend: ty, $config: literal) => {
48 $crate::backend::define_interface!($name, $backend, Some($config));
49 };
50 ($name: literal, $backend: ty, $config: expr) => {
51 $crate::backend::submit!($crate::backend::InterfaceRegistration {
52 name: $name,
53 augment_args: |command| {
54 let id = <<$backend as $crate::backend::Backend>::Opts as clap::Args>::group_id();
55
56 if command.get_groups().any(|x| Some(x.get_id()) == id.as_ref()) {
58 return command;
59 }
60
61 <<$backend as $crate::backend::Backend>::Opts as clap::Args>::augment_args(command)
62 },
63 create_transport: |common, arg_matches| {
64 let opts = <<$backend as $crate::backend::Backend>::Opts as clap::FromArgMatches>::from_arg_matches(arg_matches)?;
65 <$backend as $crate::backend::Backend>::create_transport(common, &opts)
66 },
67 config: $config,
68 });
69 }
70}
71pub use crate::define_interface;
72
73struct TransportSpecificOpts {
74 matches: clap::ArgMatches,
75}
76
77impl std::fmt::Debug for TransportSpecificOpts {
78 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 fmt.debug_struct("TransportSpecificOpts").finish()
80 }
81}
82
83impl clap::FromArgMatches for TransportSpecificOpts {
84 fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
85 Ok(Self {
86 matches: matches.clone(),
87 })
88 }
89
90 fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> {
91 unreachable!()
92 }
93}
94
95impl Args for TransportSpecificOpts {
96 fn augment_args(mut cmd: clap::Command) -> clap::Command {
97 for reg in inventory::iter::<InterfaceRegistration> {
98 cmd = (reg.augment_args)(cmd);
99 }
100 cmd
101 }
102
103 fn augment_args_for_update(_: clap::Command) -> clap::Command {
104 unreachable!()
105 }
106}
107
108#[derive(Debug, Args)]
109pub struct BackendOpts {
110 #[arg(long, default_value = "")]
112 pub interface: String,
113
114 #[arg(long)]
119 pub disable_dft_on_reset: bool,
120
121 #[arg(long, value_parser = u16::from_str)]
123 pub usb_vid: Option<u16>,
124 #[arg(long, value_parser = u16::from_str)]
126 pub usb_pid: Option<u16>,
127 #[arg(long)]
129 pub usb_serial: Option<String>,
130
131 #[command(flatten)]
132 transport_specific_opts: TransportSpecificOpts,
133
134 #[arg(long, num_args = 1)]
136 pub conf: Vec<PathBuf>,
137
138 #[arg(long)]
143 pub openocd_adapter_config: Option<PathBuf>,
144}
145
146#[derive(Error, Debug)]
147pub enum Error {
148 #[error("Unknown interface {0}")]
149 UnknownInterface(String),
150}
151
152pub fn create(args: &BackendOpts) -> Result<TransportWrapper> {
154 let interface = args.interface.as_str();
155 let mut env = TransportWrapperBuilder::new(interface.to_string(), args.disable_dft_on_reset);
156
157 for conf_file in &args.conf {
158 process_config_file(&mut env, conf_file.as_ref())?
159 }
160
161 let interface = env.get_interface();
162
163 let (backend, default_conf) = 'done: {
164 for reg in inventory::iter::<InterfaceRegistration> {
165 if interface == reg.name {
166 let transport =
167 (reg.create_transport)(args, &args.transport_specific_opts.matches)?;
168 break 'done (transport, reg.config.map(Path::new));
169 }
170 }
171
172 Err(Error::UnknownInterface(interface.to_string()))?
173 };
174
175 if args.conf.is_empty()
176 && let Some(conf_file) = default_conf
177 {
178 process_config_file(&mut env, conf_file)?
179 }
180 env.set_openocd_adapter_config(&args.openocd_adapter_config);
181 env.build(backend)
182}
183
184pub fn create_empty_transport() -> Result<Box<dyn Transport>> {
185 Ok(Box::new(EmptyTransport))
186}
187
188struct EmptyBackend;
189
190impl Backend for EmptyBackend {
191 type Opts = ();
192
193 fn create_transport(_: &BackendOpts, _: &()) -> Result<Box<dyn Transport>> {
194 Ok(Box::new(EmptyTransport))
195 }
196}
197
198define_interface!("", EmptyBackend);