Skip to content

Commit 4d384c5

Browse files
bors[bot]gkelly
andauthored
Merge #120
120: Enforce register value constraints r=burrbull a=gkelly Enforce register size, reset, and mask value agreement. If the provided reset value or mask extend past the register size, or the provided reset value would conflict with the mask, then fail parsing. --- I saw that there are some tests from CMSIS that are disabled because of constraint violations due to a check like this. There are also a couple of ATSAMD51 tests (at least) that will fail because of this that are currently enabled. Co-authored-by: Garret Kelly <[email protected]>
2 parents 1e8b49a + f23e29d commit 4d384c5

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

src/error.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! This module defines error types and messages for SVD parsing and encoding
33
44
pub use anyhow::{Context, Result};
5+
use core::u32;
56
use once_cell::sync::Lazy;
67
use regex::Regex;
78
use xmltree::Element;
@@ -74,6 +75,16 @@ pub enum NameError {
7475
Invalid(String, String),
7576
}
7677

78+
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
79+
pub enum ResetValueError {
80+
#[error("Reset value 0x{0:x} doesn't fit in {1} bits")]
81+
ValueTooLarge(u32, u32),
82+
#[error("Reset value 0x{0:x} conflicts with mask '{1}'")]
83+
MaskConflict(u32, u32),
84+
#[error("Mask value 0x{0:x} doesn't fit in {1} bits")]
85+
MaskTooLarge(u32, u32),
86+
}
87+
7788
pub(crate) fn check_name(name: &str, tag: &str) -> Result<()> {
7889
static PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new("^[_A-Za-z0-9]*$").unwrap());
7990
if PATTERN.is_match(name) {
@@ -93,3 +104,56 @@ pub(crate) fn check_dimable_name(name: &str, tag: &str) -> Result<()> {
93104
Err(NameError::Invalid(name.to_string(), tag.to_string()).into())
94105
}
95106
}
107+
108+
pub(crate) fn check_reset_value(
109+
size: Option<u32>,
110+
value: Option<u32>,
111+
mask: Option<u32>,
112+
) -> Result<()> {
113+
const MAX_BITS: u32 = u32::MAX.count_ones();
114+
115+
if let (Some(size), Some(value)) = (size, value) {
116+
if MAX_BITS - value.leading_zeros() > size {
117+
return Err(ResetValueError::ValueTooLarge(value, size).into());
118+
}
119+
}
120+
if let (Some(size), Some(mask)) = (size, mask) {
121+
if MAX_BITS - mask.leading_zeros() > size {
122+
return Err(ResetValueError::MaskTooLarge(mask, size).into());
123+
}
124+
}
125+
if let (Some(value), Some(mask)) = (value, mask) {
126+
if value & mask != value {
127+
return Err(ResetValueError::MaskConflict(value, mask).into());
128+
}
129+
}
130+
131+
Ok(())
132+
}
133+
134+
#[cfg(test)]
135+
mod tests {
136+
use crate::error::check_reset_value;
137+
138+
#[test]
139+
fn test_check_reset_value() {
140+
check_reset_value(None, None, None).unwrap();
141+
check_reset_value(Some(8), None, None).unwrap();
142+
check_reset_value(Some(8), None, Some(0xff)).unwrap();
143+
check_reset_value(Some(32), Some(0xfaceface), None).unwrap();
144+
check_reset_value(Some(32), Some(0xfaceface), Some(0xffffffff)).unwrap();
145+
146+
assert!(
147+
check_reset_value(Some(8), None, Some(0x100)).is_err(),
148+
"mask shouldn't fit in size"
149+
);
150+
assert!(
151+
check_reset_value(Some(1), Some(0x02), None).is_err(),
152+
"reset value shouldn't fit in field"
153+
);
154+
assert!(
155+
check_reset_value(Some(8), Some(0x80), Some(0x01)).is_err(),
156+
"value should conflict with mask"
157+
);
158+
}
159+
}

src/svd/registerproperties.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ impl Parse for RegisterProperties {
5151
p.reset_value = parse::optional::<u32>("resetValue", tree)?;
5252
p.reset_mask = parse::optional::<u32>("resetMask", tree)?;
5353
p.access = parse::optional::<Access>("access", tree)?;
54+
check_reset_value(p.size, p.reset_value, p.reset_mask)?;
5455
Ok(p)
5556
}
5657
}
@@ -94,7 +95,7 @@ mod tests {
9495
<mock>
9596
<size>0xaabbccdd</size>
9697
<resetValue>0x11223344</resetValue>
97-
<resetMask>0x00000000</resetMask>
98+
<resetMask>0xffffffff</resetMask>
9899
<access>read-only</access>
99100
</mock>
100101
",
@@ -103,7 +104,7 @@ mod tests {
103104
let mut expected = RegisterProperties::default();
104105
expected.size = Some(0xaabbccdd);
105106
expected.reset_value = Some(0x11223344);
106-
expected.reset_mask = Some(0x00000000);
107+
expected.reset_mask = Some(0xffffffff);
107108
expected.access = Some(Access::ReadOnly);
108109

109110
let tree1 = Element::parse(example.as_bytes()).unwrap();

0 commit comments

Comments
 (0)