Skip to content

Commit 9d2e83d

Browse files
committed
[SVD] Improvements to loading file
- Improve error messages to include what exactly caused the parsing to fail - Relaxed the parsing validation to allow for more files to be parsed - Added a form dialog to improve discoverability of load settings We still need to relax optional fields failing to parse if they have no child, as that is considered an error, even with no validation.
1 parent 6e870bc commit 9d2e83d

File tree

1 file changed

+112
-10
lines changed

1 file changed

+112
-10
lines changed

plugins/svd/src/lib.rs

Lines changed: 112 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,139 @@ use crate::mapper::DeviceMapper;
55
use crate::settings::LoadSettings;
66
use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt};
77
use binaryninja::command::Command;
8+
use binaryninja::interaction::{Form, FormInputField};
89
use binaryninja::logger::Logger;
910
use binaryninja::workflow::{activity, Activity, AnalysisContext, Workflow};
1011
use log::LevelFilter;
12+
use std::path::PathBuf;
13+
use svd_parser::ValidateLevel;
14+
15+
pub struct LoadFileField;
16+
17+
impl LoadFileField {
18+
pub fn field() -> FormInputField {
19+
FormInputField::OpenFileName {
20+
prompt: "File Path".to_string(),
21+
// TODO: This is called extension but is really a filter.
22+
extension: Some("*.svd".to_string()),
23+
default: None,
24+
value: None,
25+
}
26+
}
27+
28+
pub fn from_form(form: &Form) -> Option<PathBuf> {
29+
let field = form.get_field_with_name("File Path")?;
30+
let field_value = field.try_value_string()?;
31+
Some(PathBuf::from(field_value))
32+
}
33+
}
34+
35+
pub struct AddCommentsField;
36+
37+
impl AddCommentsField {
38+
pub fn field(default: bool) -> FormInputField {
39+
FormInputField::Checkbox {
40+
prompt: "Add Comments".to_string(),
41+
default: Some(default),
42+
value: false,
43+
}
44+
}
45+
46+
pub fn from_form(form: &Form) -> Option<bool> {
47+
let field = form.get_field_with_name("Add Comments")?;
48+
let field_value = field.try_value_int()?;
49+
match field_value {
50+
1 => Some(true),
51+
_ => Some(false),
52+
}
53+
}
54+
}
55+
56+
pub struct AddBitfieldsField;
57+
58+
impl AddBitfieldsField {
59+
pub fn field(default: bool) -> FormInputField {
60+
FormInputField::Checkbox {
61+
prompt: "Add Bitfields".to_string(),
62+
default: Some(default),
63+
value: false,
64+
}
65+
}
66+
67+
pub fn from_form(form: &Form) -> Option<bool> {
68+
let field = form.get_field_with_name("Add Bitfields")?;
69+
let field_value = field.try_value_int()?;
70+
match field_value {
71+
1 => Some(true),
72+
_ => Some(false),
73+
}
74+
}
75+
}
76+
77+
pub struct AddMemoryRegionsField;
78+
79+
impl AddMemoryRegionsField {
80+
pub fn field(default: bool) -> FormInputField {
81+
FormInputField::Checkbox {
82+
prompt: "Add Memory Regions".to_string(),
83+
default: Some(default),
84+
value: false,
85+
}
86+
}
87+
88+
pub fn from_form(form: &Form) -> Option<bool> {
89+
let field = form.get_field_with_name("Add Memory Regions")?;
90+
let field_value = field.try_value_int()?;
91+
match field_value {
92+
1 => Some(true),
93+
_ => Some(false),
94+
}
95+
}
96+
}
1197

1298
struct LoadSVDFile;
1399

14100
impl Command for LoadSVDFile {
15101
fn action(&self, view: &BinaryView) {
16-
let Some(file) =
17-
binaryninja::interaction::get_open_filename_input("Select a .svd file", "*.svd")
18-
else {
102+
let mut form = Form::new("Load SVD File");
103+
let mut load_settings = LoadSettings::from_view_settings(view);
104+
form.add_field(LoadFileField::field());
105+
form.add_field(AddCommentsField::field(load_settings.add_comments));
106+
form.add_field(AddBitfieldsField::field(load_settings.add_bitfields));
107+
form.add_field(AddMemoryRegionsField::field(
108+
load_settings.add_backing_regions,
109+
));
110+
if !form.prompt() {
111+
return;
112+
}
113+
let Some(file_path) = LoadFileField::from_form(&form) else {
19114
return;
20115
};
116+
load_settings.add_comments = AddCommentsField::from_form(&form).unwrap_or(true);
117+
load_settings.add_bitfields = AddBitfieldsField::from_form(&form).unwrap_or(true);
118+
load_settings.add_backing_regions = AddMemoryRegionsField::from_form(&form).unwrap_or(true);
21119

22-
let file_content = match std::fs::read_to_string(&file) {
120+
let file_content = match std::fs::read_to_string(&file_path) {
23121
Ok(content) => content,
24122
Err(e) => {
25-
log::error!("Failed to read file: {}", e);
123+
log::error!("Failed to read file: {:?}", e);
26124
return;
27125
}
28126
};
29127

30-
match svd_parser::parse(&file_content) {
128+
// Disabling validation since vendors are lazy and don't follow the spec.
129+
let mut config = svd_parser::Config::default();
130+
config.validate_level = ValidateLevel::Disabled;
131+
match svd_parser::parse_with_config(&file_content, &config) {
31132
Ok(device) => {
32133
// We have a supported svd device. map it!
33-
let load_settings = LoadSettings::from_view_settings(view);
34134
let address_size = view.address_size();
35135
let mapper = DeviceMapper::new(load_settings, address_size, device);
36136
mapper.map_to_view(view);
37137
view.update_analysis();
38138
}
39139
Err(e) => {
40-
log::error!("Failed to parse SVD file: {}", e);
140+
log::error!("Failed to parse SVD file: {:?}", e);
41141
}
42142
}
43143
}
@@ -95,14 +195,16 @@ fn plugin_init() -> Result<(), ()> {
95195
return;
96196
}
97197
};
98-
match svd_parser::parse(&file_content) {
198+
let mut config = svd_parser::Config::default();
199+
config.validate_level = ValidateLevel::Disabled;
200+
match svd_parser::parse_with_config(&file_content, &config) {
99201
Ok(device) => {
100202
let address_size = view.address_size();
101203
let mapper = DeviceMapper::new(load_settings, address_size, device);
102204
mapper.map_to_view(&view);
103205
}
104206
Err(e) => {
105-
log::error!("Failed to parse SVD file: {}", e);
207+
log::error!("Failed to parse SVD file: {:?}", e);
106208
}
107209
}
108210
};

0 commit comments

Comments
 (0)