|
| 1 | +diff --git a/boards/nordic/nrf52_components/src/startup.rs b/boards/nordic/nrf52_components/src/startup.rs |
| 2 | +index 7b3ea5a59..87a566550 100644 |
| 3 | +--- a/boards/nordic/nrf52_components/src/startup.rs |
| 4 | ++++ b/boards/nordic/nrf52_components/src/startup.rs |
| 5 | +@@ -36,6 +36,13 @@ impl<'a> Component for NrfStartupComponent<'a> { |
| 6 | + type StaticInput = (); |
| 7 | + type Output = (); |
| 8 | + unsafe fn finalize(self, _s: Self::StaticInput) -> Self::Output { |
| 9 | ++ // Disable APPROTECT in software. This is required as of newer nRF52 |
| 10 | ++ // hardware revisions. See |
| 11 | ++ // https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/working-with-the-nrf52-series-improved-approtect. |
| 12 | ++ // If run on older HW revisions this function will do nothing. |
| 13 | ++ let approtect = nrf52::approtect::Approtect::new(); |
| 14 | ++ approtect.sw_disable_approtect(); |
| 15 | ++ |
| 16 | + // Make non-volatile memory writable and activate the reset button |
| 17 | + let uicr = nrf52::uicr::Uicr::new(); |
| 18 | + |
| 19 | +@@ -56,6 +63,12 @@ impl<'a> Component for NrfStartupComponent<'a> { |
| 20 | + // Avoid killing the DFU bootloader if present |
| 21 | + let (dfu_start_addr, dfu_settings_addr) = uicr.get_dfu_params(); |
| 22 | + |
| 23 | ++ // On new nRF52 variants we need to ensure that the APPROTECT field in UICR is |
| 24 | ++ // set to `HwDisable`. |
| 25 | ++ if uicr.is_ap_protect_enabled() { |
| 26 | ++ erase_uicr = true; |
| 27 | ++ } |
| 28 | ++ |
| 29 | + if erase_uicr { |
| 30 | + self.nvmc.erase_uicr(); |
| 31 | + } |
| 32 | +@@ -102,6 +115,12 @@ impl<'a> Component for NrfStartupComponent<'a> { |
| 33 | + needs_soft_reset = true; |
| 34 | + } |
| 35 | + |
| 36 | ++ if uicr.is_ap_protect_enabled() { |
| 37 | ++ uicr.disable_ap_protect(); |
| 38 | ++ while !self.nvmc.is_ready() {} |
| 39 | ++ needs_soft_reset = true; |
| 40 | ++ } |
| 41 | ++ |
| 42 | + // Any modification of UICR needs a soft reset for the changes to be taken into account. |
| 43 | + if needs_soft_reset { |
| 44 | + cortexm4::scb::reset(); |
| 45 | +diff --git a/chips/nrf52/src/approtect.rs b/chips/nrf52/src/approtect.rs |
| 46 | +new file mode 100644 |
| 47 | +index 000000000..5900e10ec |
| 48 | +--- /dev/null |
| 49 | ++++ b/chips/nrf52/src/approtect.rs |
| 50 | +@@ -0,0 +1,107 @@ |
| 51 | ++// Licensed under the Apache License, Version 2.0 or the MIT License. |
| 52 | ++// SPDX-License-Identifier: Apache-2.0 OR MIT |
| 53 | ++// Copyright Tock Contributors 2023. |
| 54 | ++ |
| 55 | ++//! Access port protection |
| 56 | ++//! |
| 57 | ++//! <https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fdif.html&cp=5_0_0_3_7_1&anchor=register.DISABLE> |
| 58 | ++//! |
| 59 | ++//! The logic around APPROTECT was changed in newer revisions of the nRF52 |
| 60 | ++//! series chips (Oct 2021) and later which requires more careful disabling of |
| 61 | ++//! the access port (JTAG), both in the UICR register and in a software written |
| 62 | ++//! register. This module enables the kernel to disable the protection on boot. |
| 63 | ++//! |
| 64 | ++//! Example code to disable the APPROTECT protection in software: |
| 65 | ++//! |
| 66 | ++//! ```rust,ignore |
| 67 | ++//! let approtect = nrf52::approtect::Approtect::new(); |
| 68 | ++//! approtect.sw_disable_approtect(); |
| 69 | ++//! ``` |
| 70 | ++ |
| 71 | ++use crate::ficr; |
| 72 | ++use kernel::utilities::registers::interfaces::Writeable; |
| 73 | ++use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite}; |
| 74 | ++use kernel::utilities::StaticRef; |
| 75 | ++ |
| 76 | ++const APPROTECT_BASE: StaticRef<ApprotectRegisters> = |
| 77 | ++ unsafe { StaticRef::new(0x40000000 as *const ApprotectRegisters) }; |
| 78 | ++ |
| 79 | ++register_structs! { |
| 80 | ++ ApprotectRegisters { |
| 81 | ++ (0x000 => _reserved0), |
| 82 | ++ (0x550 => forceprotect: ReadWrite<u32, Forceprotect::Register>), |
| 83 | ++ (0x554 => _reserved1), |
| 84 | ++ (0x558 => disable: ReadWrite<u32, Disable::Register>), |
| 85 | ++ (0x55c => @END), |
| 86 | ++ } |
| 87 | ++} |
| 88 | ++ |
| 89 | ++register_bitfields! [u32, |
| 90 | ++ Forceprotect [ |
| 91 | ++ FORCEPROTECT OFFSET(0) NUMBITS(8) [ |
| 92 | ++ FORCE = 0 |
| 93 | ++ ] |
| 94 | ++ ], |
| 95 | ++ /// Access port protection |
| 96 | ++ Disable [ |
| 97 | ++ DISABLE OFFSET(0) NUMBITS(8) [ |
| 98 | ++ SWDISABLE = 0x5a |
| 99 | ++ ] |
| 100 | ++ ] |
| 101 | ++]; |
| 102 | ++ |
| 103 | ++pub struct Approtect { |
| 104 | ++ registers: StaticRef<ApprotectRegisters>, |
| 105 | ++} |
| 106 | ++ |
| 107 | ++impl Approtect { |
| 108 | ++ pub const fn new() -> Approtect { |
| 109 | ++ Approtect { |
| 110 | ++ registers: APPROTECT_BASE, |
| 111 | ++ } |
| 112 | ++ } |
| 113 | ++ |
| 114 | ++ /// Software disable the Access Port Protection mechanism. |
| 115 | ++ /// |
| 116 | ++ /// On newer variants of the nRF52, to enable JTAG, APPROTECT must be |
| 117 | ++ /// disabled both in the UICR register (hardware) and in this register |
| 118 | ++ /// (software). For older variants this is just a no-op. |
| 119 | ++ /// |
| 120 | ++ /// - <https://devzone.nordicsemi.com/f/nordic-q-a/96590/how-to-disable-approtect-permanently-dfu-is-needed> |
| 121 | ++ /// - <https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/working-with-the-nrf52-series-improved-approtect> |
| 122 | ++ pub fn sw_disable_approtect(&self) { |
| 123 | ++ let factory_config = ficr::Ficr::new(); |
| 124 | ++ match factory_config.variant() { |
| 125 | ++ ficr::Variant::AAF0 | ficr::Variant::Unspecified => { |
| 126 | ++ // Newer revisions of the chip require setting the APPROTECT |
| 127 | ++ // software register to `SwDisable`. We assume that an unspecified |
| 128 | ++ // version means that it is new and the FICR module hasn't been |
| 129 | ++ // updated to recognize it. |
| 130 | ++ self.registers.disable.write(Disable::DISABLE::SWDISABLE); |
| 131 | ++ } |
| 132 | ++ |
| 133 | ++ // Exhaustively list variants here to produce compiler error on |
| 134 | ++ // adding a new variant, which would otherwise not match the above |
| 135 | ++ // condition. |
| 136 | ++ ficr::Variant::AAA0 |
| 137 | ++ | ficr::Variant::AAAA |
| 138 | ++ | ficr::Variant::AAAB |
| 139 | ++ | ficr::Variant::AAB0 |
| 140 | ++ | ficr::Variant::AABA |
| 141 | ++ | ficr::Variant::AABB |
| 142 | ++ | ficr::Variant::AAC0 |
| 143 | ++ | ficr::Variant::AACA |
| 144 | ++ | ficr::Variant::AACB |
| 145 | ++ | ficr::Variant::AAD0 |
| 146 | ++ | ficr::Variant::AAD1 |
| 147 | ++ | ficr::Variant::AADA |
| 148 | ++ | ficr::Variant::AAE0 |
| 149 | ++ | ficr::Variant::AAEA |
| 150 | ++ | ficr::Variant::ABBA |
| 151 | ++ | ficr::Variant::BAAA |
| 152 | ++ | ficr::Variant::CAAA => { |
| 153 | ++ // All other revisions don't need this. |
| 154 | ++ } |
| 155 | ++ } |
| 156 | ++ } |
| 157 | ++} |
| 158 | +diff --git a/chips/nrf52/src/ficr.rs b/chips/nrf52/src/ficr.rs |
| 159 | +index ddd6a4093..00e37bd37 100644 |
| 160 | +--- a/chips/nrf52/src/ficr.rs |
| 161 | ++++ b/chips/nrf52/src/ficr.rs |
| 162 | +@@ -167,8 +167,16 @@ register_bitfields! [u32, |
| 163 | + ABBA = 0x41424241, |
| 164 | + /// AAD0 |
| 165 | + AAD0 = 0x41414430, |
| 166 | ++ /// AAD1 |
| 167 | ++ AAD1 = 0x41414431, |
| 168 | ++ /// AADA |
| 169 | ++ AADA = 0x41414441, |
| 170 | + /// AAE0 |
| 171 | + AAE0 = 0x41414530, |
| 172 | ++ /// AAEA |
| 173 | ++ AAEA = 0x41414541, |
| 174 | ++ /// AAF0 |
| 175 | ++ AAF0 = 0x41414630, |
| 176 | + /// BAAA |
| 177 | + BAAA = 0x42414141, |
| 178 | + /// CAAA |
| 179 | +@@ -234,7 +242,7 @@ register_bitfields! [u32, |
| 180 | + /// Variant describes part variant, hardware version, and production configuration. |
| 181 | + #[derive(PartialEq, Debug)] |
| 182 | + #[repr(u32)] |
| 183 | +-enum Variant { |
| 184 | ++pub(crate) enum Variant { |
| 185 | + AAA0 = 0x41414130, |
| 186 | + AAAA = 0x41414141, |
| 187 | + AAAB = 0x41414142, |
| 188 | +@@ -244,9 +252,13 @@ enum Variant { |
| 189 | + AAC0 = 0x41414330, |
| 190 | + AACA = 0x41414341, |
| 191 | + AACB = 0x41414342, |
| 192 | +- ABBA = 0x41424241, |
| 193 | + AAD0 = 0x41414430, |
| 194 | ++ AAD1 = 0x41414431, |
| 195 | ++ AADA = 0x41414441, |
| 196 | + AAE0 = 0x41414530, |
| 197 | ++ AAEA = 0x41414541, |
| 198 | ++ AAF0 = 0x41414630, |
| 199 | ++ ABBA = 0x41424241, |
| 200 | + BAAA = 0x42414141, |
| 201 | + CAAA = 0x43414141, |
| 202 | + Unspecified = 0xffffffff, |
| 203 | +@@ -306,7 +318,7 @@ pub struct Ficr { |
| 204 | + } |
| 205 | + |
| 206 | + impl Ficr { |
| 207 | +- const fn new() -> Ficr { |
| 208 | ++ pub(crate) const fn new() -> Ficr { |
| 209 | + Ficr { |
| 210 | + registers: FICR_BASE, |
| 211 | + } |
| 212 | +@@ -321,7 +333,9 @@ impl Ficr { |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | +- fn variant(&self) -> Variant { |
| 217 | ++ pub(crate) fn variant(&self) -> Variant { |
| 218 | ++ // If you update this, make sure to update |
| 219 | ++ // `has_updated_approtect_logic()` as well. |
| 220 | + match self.registers.info_variant.get() { |
| 221 | + 0x41414130 => Variant::AAA0, |
| 222 | + 0x41414141 => Variant::AAAA, |
| 223 | +@@ -334,13 +348,32 @@ impl Ficr { |
| 224 | + 0x41414342 => Variant::AACB, |
| 225 | + 0x41424241 => Variant::ABBA, |
| 226 | + 0x41414430 => Variant::AAD0, |
| 227 | ++ 0x41414431 => Variant::AAD1, |
| 228 | ++ 0x41414441 => Variant::AADA, |
| 229 | + 0x41414530 => Variant::AAE0, |
| 230 | ++ 0x41414541 => Variant::AAEA, |
| 231 | ++ 0x41414630 => Variant::AAF0, |
| 232 | + 0x42414141 => Variant::BAAA, |
| 233 | + 0x43414141 => Variant::CAAA, |
| 234 | + _ => Variant::Unspecified, |
| 235 | + } |
| 236 | + } |
| 237 | + |
| 238 | ++ /// Returns if this variant of the nRF52 has the updated APPROTECT logic. |
| 239 | ++ /// This changed occurred towards the end of 2021 with chips becoming widely |
| 240 | ++ /// available/used in 2023. |
| 241 | ++ /// |
| 242 | ++ /// See <https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/working-with-the-nrf52-series-improved-approtect>. |
| 243 | ++ /// for more information. |
| 244 | ++ pub(crate) fn has_updated_approtect_logic(&self) -> bool { |
| 245 | ++ // We assume that an unspecified version means that it is new and this |
| 246 | ++ // module hasn't been updated to recognize it. |
| 247 | ++ match self.variant() { |
| 248 | ++ Variant::AAF0 | Variant::Unspecified => true, |
| 249 | ++ _ => false, |
| 250 | ++ } |
| 251 | ++ } |
| 252 | ++ |
| 253 | + fn package(&self) -> Package { |
| 254 | + match self.registers.info_package.get() { |
| 255 | + 0x2000 => Package::QF, |
| 256 | +diff --git a/chips/nrf52/src/lib.rs b/chips/nrf52/src/lib.rs |
| 257 | +index 5ea4263c7..5293a7c63 100644 |
| 258 | +--- a/chips/nrf52/src/lib.rs |
| 259 | ++++ b/chips/nrf52/src/lib.rs |
| 260 | +@@ -4,6 +4,7 @@ |
| 261 | + |
| 262 | + pub mod acomp; |
| 263 | + pub mod adc; |
| 264 | ++pub mod approtect; |
| 265 | + pub mod ble_radio; |
| 266 | + pub mod chip; |
| 267 | + pub mod clock; |
| 268 | +diff --git a/chips/nrf52/src/uicr.rs b/chips/nrf52/src/uicr.rs |
| 269 | +index 3086394aa..cfaa6ba60 100644 |
| 270 | +--- a/chips/nrf52/src/uicr.rs |
| 271 | ++++ b/chips/nrf52/src/uicr.rs |
| 272 | +@@ -1,5 +1,6 @@ |
| 273 | + //! User information configuration registers |
| 274 | + |
| 275 | ++use crate::ficr; |
| 276 | + use enum_primitive::cast::FromPrimitive; |
| 277 | + use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable}; |
| 278 | + use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite}; |
| 279 | +@@ -60,7 +61,9 @@ register_bitfields! [u32, |
| 280 | + /// Enable |
| 281 | + ENABLED = 0x00, |
| 282 | + /// Disable |
| 283 | +- DISABLED = 0xff |
| 284 | ++ DISABLED = 0xff, |
| 285 | ++ /// Disable for later nRF52 variants |
| 286 | ++ HWDISABLE = 0x5a |
| 287 | + ] |
| 288 | + ], |
| 289 | + |
| 290 | +@@ -207,14 +210,39 @@ impl Uicr { |
| 291 | + } |
| 292 | + |
| 293 | + pub fn is_ap_protect_enabled(&self) -> bool { |
| 294 | +- // Here we compare to DISABLED value because any other value should enable the protection. |
| 295 | +- !self |
| 296 | +- .registers |
| 297 | +- .approtect |
| 298 | +- .matches_all(ApProtect::PALL::DISABLED) |
| 299 | ++ // We need to understand the variant of this nRF52 chip to correctly |
| 300 | ++ // implement this function. Newer versions use a different value to |
| 301 | ++ // indicate disabled. |
| 302 | ++ let factory_config = ficr::Ficr::new(); |
| 303 | ++ let disabled_val = if factory_config.has_updated_approtect_logic() { |
| 304 | ++ ApProtect::PALL::HWDISABLE |
| 305 | ++ } else { |
| 306 | ++ ApProtect::PALL::DISABLED |
| 307 | ++ }; |
| 308 | ++ |
| 309 | ++ // Here we compare to the correct DISABLED value because any other value |
| 310 | ++ // should enable the protection. |
| 311 | ++ !self.registers.approtect.matches_all(disabled_val) |
| 312 | + } |
| 313 | + |
| 314 | + pub fn set_ap_protect(&self) { |
| 315 | + self.registers.approtect.write(ApProtect::PALL::ENABLED); |
| 316 | + } |
| 317 | ++ |
| 318 | ++ /// Disable the access port protection in the UICR register. This is stored |
| 319 | ++ /// in flash and is persistent. This behavior can also be accomplished |
| 320 | ++ /// outside of tock by running `nrfjprog --recover`. |
| 321 | ++ pub fn disable_ap_protect(&self) { |
| 322 | ++ // We need to understand the variant of this nRF52 chip to correctly |
| 323 | ++ // implement this function. |
| 324 | ++ let factory_config = ficr::Ficr::new(); |
| 325 | ++ if factory_config.has_updated_approtect_logic() { |
| 326 | ++ // Newer revisions of the chip require setting the APPROTECT |
| 327 | ++ // register to `HwDisable`. |
| 328 | ++ self.registers.approtect.write(ApProtect::PALL::HWDISABLE); |
| 329 | ++ } else { |
| 330 | ++ // All other revisions just use normal disable. |
| 331 | ++ self.registers.approtect.write(ApProtect::PALL::DISABLED); |
| 332 | ++ } |
| 333 | ++ } |
| 334 | + } |
| 335 | +-- |
| 336 | +2.39.5 |
0 commit comments