1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! KM (Key Management) driver for WS63.
//!
//! The KM peripheral manages cryptographic keys, providing:
//! - KLAD (Key Loading and Distribution)
//! - Keyslot locking mechanisms
//! - RKP (Root Key Provisioning)
use crate::peripherals::Km;
/// Number of available keyslots.
pub const KEYSLOT_COUNT: u8 = 8;
/// KM driver.
pub struct KmDriver<'d> {
_km: Km<'d>,
}
impl<'d> KmDriver<'d> {
/// Create a new KM driver.
pub fn new(km: Km<'d>) -> Self {
Self { _km: km }
}
fn regs(&self) -> &'static ws63_pac::km::RegisterBlock {
// SAFETY: PAC peripheral pointer is a static physical MMIO address, always valid
unsafe { &*Km::ptr() }
}
/// Enable the KM peripheral.
pub fn enable(&mut self) {
unsafe {
self.regs().kl_com_ctrl().write(|w| w.bits(0x01));
}
}
/// Check if a keyslot is locked (read-only after lock).
///
/// Implements the vendor SDK `inner_kslot_chn_is_locked` sequence:
/// 1. Write `KC_RD_SLOT_NUM` to select the keyslot and type (mcipher)
/// 2. Read `KC_RD_LOCK_STATUS` to get the lock owner
/// 3. Keyslot is locked if `rd_lock_status != 0` (non-zero = some CPU owns the lock)
pub fn is_keyslot_locked(&self, slot: u8) -> bool {
assert!(slot < KEYSLOT_COUNT, "keyslot {} out of range (max {})", slot, KEYSLOT_COUNT);
// Select keyslot to query: mcipher type (bit 15 = 0), slot number in bits [9:0]
unsafe {
self.regs().kc_rd_slot_num().write(|w| {
w.slot_num_cfg().bits(slot as u16);
w.slot_cfg_type().clear_bit()
});
}
// Read lock status: non-zero rd_lock_status field means locked
self.regs().kc_rd_lock_status().read().rd_lock_status().bits() != 0
}
/// Lock a keyslot (prevents further writes).
///
/// Writes to `KC_REECPU_LOCK_CMD` — the REE CPU keyslot lock command register
/// (main application processor = REE). Uses mcipher keyslot type.
///
/// After locking, the keyslot cannot be modified until the next system reset.
pub fn lock_keyslot(&mut self, slot: u8) {
assert!(slot < KEYSLOT_COUNT, "keyslot {} out of range (max {})", slot, KEYSLOT_COUNT);
// Vendor SDK: write key_slot_num (bits [9:0]) and lock_cmd=1 (bit 20),
// flush_hmac_kslot_ind=0 (mcipher), tscipher_ind=0
unsafe {
self.regs().kc_reecpu_lock_cmd().write(|w| {
w.key_slot_num().bits(slot as u16);
w.lock_cmd().set_bit()
});
}
}
/// Get the number of available keyslots.
pub fn keyslot_count(&self) -> u8 {
KEYSLOT_COUNT
}
}
// ── Tests ──────────────────────────────────────────────────────
#[cfg(test)]
mod tests {
#[test]
fn test_keyslot_lock_bit_position() {
// lock_keyslot writes key_slot_num in bits [9:0], lock_cmd=1 at bit 20
let slot: u16 = 3;
let lock_cmd_bit: u32 = 1 << 20;
let val: u32 = (slot as u32) | lock_cmd_bit;
assert_eq!(val & 0x3FF, 3); // key_slot_num = 3 in bits [9:0]
assert_eq!((val >> 20) & 1, 1); // lock_cmd = 1 at bit 20
}
#[test]
fn test_keyslot_lock_status_is_enum_not_bitmask() {
// rd_lock_status is a 3-bit enum (bits [2:0]):
// 0=unlocked, 1=REE, 2=TEE, 4=PCPU, 6=AIDSP
// is_keyslot_locked should check rd_lock_status != 0, NOT (status & (1 << slot))
let unlocked: u8 = 0;
let ree_locked: u8 = 1;
let tee_locked: u8 = 2;
assert_eq!(unlocked != 0, false); // unlocked → not locked
assert_eq!(ree_locked != 0, true); // REE locked → locked
assert_eq!(tee_locked != 0, true); // TEE locked → locked
}
#[test]
fn test_keyslot_count() {
assert_eq!(super::KEYSLOT_COUNT, 8);
}
#[test]
fn test_all_keyslots_lockable() {
// All 8 keyslots (0-7) must produce valid lock values
for slot in 0..8u16 {
let val: u32 = (slot as u32) | (1 << 20);
assert_eq!(val & 0x3FF, slot as u32); // key_slot_num in bits [9:0]
assert_eq!((val >> 20) & 1, 1); // lock_cmd at bit 20
assert!(slot < super::KEYSLOT_COUNT as u16); // within valid range
}
}
}