Skip to content

Commit 0bdb3e1

Browse files
WhatAmISupposedToPutHerejannau
authored andcommitted
soc: apple: Add SEP driver.
This is a co-processor in charge of various security-related features on Apple SoCs. This driver only boots the firmware, which is needed to unlock the mic secure disable on certain laptop models. Signed-off-by: Sasha Finkelstein <[email protected]>
1 parent a5043ab commit 0bdb3e1

File tree

3 files changed

+359
-0
lines changed

3 files changed

+359
-0
lines changed

drivers/soc/apple/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ config APPLE_AOP
5858

5959
Say 'y' here if you have an Apple laptop.
6060

61+
config APPLE_SEP
62+
tristate "Apple Secure Element Processor"
63+
depends on ARCH_APPLE || COMPILE_TEST
64+
depends on RUST
65+
select RUST_APPLE_RTKIT
66+
default y if ARCH_APPLE
67+
help
68+
A security co-processor persent on Apple SoCs, controlling transparent
69+
disk encryption, secure boot, HDCP, biometric auth and probably more.
70+
71+
Say 'y' here if you have an Apple SoC.
72+
6173
endmenu
6274

6375
endif

drivers/soc/apple/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ obj-$(CONFIG_APPLE_SART) += apple-sart.o
1010
apple-sart-y = sart.o
1111

1212
obj-$(CONFIG_APPLE_AOP) += aop.o
13+
14+
obj-$(CONFIG_APPLE_SEP) += sep.o

drivers/soc/apple/sep.rs

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
#![recursion_limit = "2048"]
3+
4+
//! Apple SEP driver
5+
//!
6+
//! Copyright (C) The Asahi Linux Contributors
7+
8+
use core::sync::atomic::{AtomicBool, Ordering};
9+
10+
use kernel::{
11+
bindings, c_str, device, dma, module_platform_driver, new_mutex, of, platform,
12+
prelude::*,
13+
soc::apple::mailbox::{MailCallback, Mailbox, Message},
14+
sync::{Arc, Mutex},
15+
types::{ARef, ForeignOwnable},
16+
workqueue::{self, impl_has_work, new_work, Work, WorkItem},
17+
};
18+
19+
const SHMEM_SIZE: usize = 0x30000;
20+
const MSG_BOOT_TZ0: u64 = 0x5;
21+
const MSG_BOOT_IMG4: u64 = 0x6;
22+
const MSG_SET_SHMEM: u64 = 0x18;
23+
const MSG_BOOT_TZ0_ACK1: u64 = 0x69;
24+
const MSG_BOOT_TZ0_ACK2: u64 = 0xD2;
25+
const MSG_BOOT_IMG4_ACK: u64 = 0x6A;
26+
const MSG_ADVERTISE_EP: u64 = 0;
27+
const EP_DISCOVER: u64 = 0xFD;
28+
const EP_SHMEM: u64 = 0xFE;
29+
const EP_BOOT: u64 = 0xFF;
30+
31+
const MSG_TYPE_SHIFT: u32 = 16;
32+
const MSG_TYPE_MASK: u64 = 0xFF;
33+
//const MSG_PARAM_SHIFT: u32 = 24;
34+
//const MSG_PARAM_MASK: u64 = 0xFF;
35+
36+
const MSG_EP_MASK: u64 = 0xFF;
37+
const MSG_DATA_SHIFT: u32 = 32;
38+
39+
const IOVA_SHIFT: u32 = 0xC;
40+
41+
type ShMem = dma::CoherentAllocation<u8>;
42+
43+
fn align_up(v: usize, a: usize) -> usize {
44+
(v + a - 1) & !(a - 1)
45+
}
46+
47+
fn memcpy_to_iomem(iomem: &ShMem, off: usize, src: &[u8]) -> Result<()> {
48+
// SAFETY:
49+
// as_slice_mut() checks that off and src.len() are whithin iomem's limits.
50+
// memcpy_to_iomem is only called from within probe() ansuring there are no
51+
// concurrent read and write accesses to the same region while the slice is
52+
// alive per as_slice_mut()'s requiremnts.
53+
unsafe {
54+
let target = iomem.as_slice_mut(off, src.len())?;
55+
target.copy_from_slice(src);
56+
}
57+
Ok(())
58+
}
59+
60+
fn build_shmem(dev: &platform::Device<device::Core>) -> Result<ShMem> {
61+
let fwnode = dev.as_ref().fwnode().ok_or(EIO)?;
62+
let iomem =
63+
dma::CoherentAllocation::<u8>::alloc_coherent(dev.as_ref(), SHMEM_SIZE, GFP_KERNEL)?;
64+
65+
let panic_offset = 0x4000;
66+
let panic_size = 0x8000;
67+
memcpy_to_iomem(&iomem, panic_offset, &1u32.to_le_bytes())?;
68+
69+
let lpol_offset = panic_offset + panic_size;
70+
let lpol_prop_name = c_str!("local-policy-manifest");
71+
let lpol_prop_size = fwnode.property_count_elem::<u8>(lpol_prop_name)?;
72+
let lpol = fwnode
73+
.property_read_array_vec(lpol_prop_name, lpol_prop_size)?
74+
.required_by(dev.as_ref())?;
75+
memcpy_to_iomem(&iomem, lpol_offset, &(lpol_prop_size as u32).to_le_bytes())?;
76+
memcpy_to_iomem(&iomem, lpol_offset + 4, &lpol)?;
77+
let lpol_size = align_up(lpol_prop_size + 4, 0x4000);
78+
79+
let ibot_offset = lpol_offset + lpol_size;
80+
let ibot_prop_name = c_str!("iboot-manifest");
81+
let ibot_prop_size = fwnode.property_count_elem::<u8>(ibot_prop_name)?;
82+
let ibot = fwnode
83+
.property_read_array_vec(ibot_prop_name, ibot_prop_size)?
84+
.required_by(dev.as_ref())?;
85+
memcpy_to_iomem(&iomem, ibot_offset, &(ibot_prop_size as u32).to_le_bytes())?;
86+
memcpy_to_iomem(&iomem, ibot_offset + 4, &ibot)?;
87+
let ibot_size = align_up(ibot_prop_size + 4, 0x4000);
88+
89+
memcpy_to_iomem(&iomem, 0, b"CNIP")?;
90+
memcpy_to_iomem(&iomem, 4, &(panic_size as u32).to_le_bytes())?;
91+
memcpy_to_iomem(&iomem, 8, &(panic_offset as u32).to_le_bytes())?;
92+
93+
memcpy_to_iomem(&iomem, 16, b"OPLA")?;
94+
memcpy_to_iomem(&iomem, 16 + 4, &(lpol_size as u32).to_le_bytes())?;
95+
memcpy_to_iomem(&iomem, 16 + 8, &(lpol_offset as u32).to_le_bytes())?;
96+
97+
memcpy_to_iomem(&iomem, 32, b"IPIS")?;
98+
memcpy_to_iomem(&iomem, 32 + 4, &(ibot_size as u32).to_le_bytes())?;
99+
memcpy_to_iomem(&iomem, 32 + 8, &(ibot_offset as u32).to_le_bytes())?;
100+
101+
memcpy_to_iomem(&iomem, 48, b"llun")?;
102+
Ok(iomem)
103+
}
104+
105+
#[pin_data]
106+
struct SepReceiveWork {
107+
data: Arc<SepData>,
108+
msg: Message,
109+
#[pin]
110+
work: Work<SepReceiveWork>,
111+
}
112+
113+
impl_has_work! {
114+
impl HasWork<Self, 0> for SepReceiveWork { self.work }
115+
}
116+
117+
impl SepReceiveWork {
118+
fn new(data: Arc<SepData>, msg: Message) -> Result<Arc<Self>> {
119+
Arc::pin_init(
120+
pin_init!(SepReceiveWork {
121+
data,
122+
msg,
123+
work <- new_work!("SepReceiveWork::work"),
124+
}),
125+
GFP_ATOMIC,
126+
)
127+
}
128+
}
129+
130+
impl WorkItem for SepReceiveWork {
131+
type Pointer = Arc<SepReceiveWork>;
132+
133+
fn run(this: Arc<SepReceiveWork>) {
134+
this.data.process_message(this.msg);
135+
}
136+
}
137+
138+
struct FwRegionParams {
139+
addr: u64,
140+
size: usize,
141+
}
142+
143+
#[pin_data]
144+
struct SepData {
145+
dev: ARef<device::Device>,
146+
#[pin]
147+
mbox: Mutex<Option<Mailbox<SepData>>>,
148+
shmem: ShMem,
149+
region_params: FwRegionParams,
150+
fw_mapped: AtomicBool,
151+
}
152+
153+
impl SepData {
154+
fn new(
155+
dev: &platform::Device<device::Core>,
156+
region_params: FwRegionParams,
157+
) -> Result<Arc<SepData>> {
158+
Arc::pin_init(
159+
try_pin_init!(SepData {
160+
shmem: build_shmem(dev)?,
161+
dev: ARef::<device::Device>::from(dev.as_ref()),
162+
mbox <- new_mutex!(None),
163+
region_params,
164+
fw_mapped: AtomicBool::new(false),
165+
}),
166+
GFP_KERNEL,
167+
)
168+
}
169+
fn start(&self) -> Result<()> {
170+
self.mbox.lock().as_ref().unwrap().send(
171+
Message {
172+
msg0: EP_BOOT | (MSG_BOOT_TZ0 << MSG_TYPE_SHIFT),
173+
msg1: 0,
174+
},
175+
false,
176+
)
177+
}
178+
fn load_fw_and_shmem(&self) -> Result<()> {
179+
let fw_addr = unsafe {
180+
let res = bindings::dma_map_resource(
181+
self.dev.as_raw(),
182+
self.region_params.addr,
183+
self.region_params.size,
184+
bindings::dma_data_direction_DMA_TO_DEVICE,
185+
0,
186+
);
187+
if bindings::dma_mapping_error(self.dev.as_raw(), res) != 0 {
188+
dev_err!(self.dev, "Failed to map firmware");
189+
return Err(ENOMEM);
190+
}
191+
self.fw_mapped.store(true, Ordering::Relaxed);
192+
res >> IOVA_SHIFT
193+
};
194+
let guard = self.mbox.lock();
195+
let mbox = guard.as_ref().unwrap();
196+
mbox.send(
197+
Message {
198+
msg0: EP_BOOT | (MSG_BOOT_IMG4 << MSG_TYPE_SHIFT) | (fw_addr << MSG_DATA_SHIFT),
199+
msg1: 0,
200+
},
201+
false,
202+
)?;
203+
let shm_addr = self.shmem.dma_handle() >> IOVA_SHIFT;
204+
mbox.send(
205+
Message {
206+
msg0: EP_SHMEM | (MSG_SET_SHMEM << MSG_TYPE_SHIFT) | (shm_addr << MSG_DATA_SHIFT),
207+
msg1: 0,
208+
},
209+
false,
210+
)?;
211+
Ok(())
212+
}
213+
fn process_boot_msg(&self, msg: Message) {
214+
let ty = (msg.msg0 >> MSG_TYPE_SHIFT) & MSG_TYPE_MASK;
215+
match ty {
216+
MSG_BOOT_TZ0_ACK1 => {}
217+
MSG_BOOT_TZ0_ACK2 => {
218+
let res = self.load_fw_and_shmem();
219+
if let Err(e) = res {
220+
dev_err!(self.dev, "Unable to load firmware: {:?}", e);
221+
}
222+
}
223+
MSG_BOOT_IMG4_ACK => {}
224+
_ => {
225+
dev_err!(self.dev, "Unknown boot message type: {}", ty);
226+
}
227+
}
228+
}
229+
fn process_discover_msg(&self, msg: Message) {
230+
let ty = (msg.msg0 >> MSG_TYPE_SHIFT) & MSG_TYPE_MASK;
231+
//let data = (msg.msg0 >> MSG_DATA_SHIFT) as u32;
232+
//let param = (msg.msg0 >> MSG_PARAM_SHIFT) & MSG_PARAM_MASK;
233+
match ty {
234+
MSG_ADVERTISE_EP => {
235+
/*dev_info!(
236+
self.dev,
237+
"Got endpoint {:?} at {}",
238+
core::str::from_utf8(&data.to_be_bytes()),
239+
param
240+
);*/
241+
}
242+
_ => {
243+
//dev_warn!(self.dev, "Unknown discovery message type: {}", ty);
244+
}
245+
}
246+
}
247+
fn process_message(&self, msg: Message) {
248+
let ep = msg.msg0 & MSG_EP_MASK;
249+
match ep {
250+
EP_BOOT => self.process_boot_msg(msg),
251+
EP_DISCOVER => self.process_discover_msg(msg),
252+
_ => {} // dev_warn!(self.dev, "Message from unknown endpoint: {}", ep),
253+
}
254+
}
255+
fn remove(&self) {
256+
*self.mbox.lock() = None;
257+
if self.fw_mapped.load(Ordering::Relaxed) {
258+
unsafe {
259+
bindings::dma_unmap_resource(
260+
self.dev.as_raw(),
261+
self.region_params.addr,
262+
self.region_params.size,
263+
bindings::dma_data_direction_DMA_TO_DEVICE,
264+
0,
265+
);
266+
}
267+
}
268+
}
269+
}
270+
271+
impl MailCallback for SepData {
272+
type Data = Arc<SepData>;
273+
fn recv_message(data: <Self::Data as ForeignOwnable>::Borrowed<'_>, msg: Message) {
274+
let work = SepReceiveWork::new(data.into(), msg);
275+
if let Ok(work) = work {
276+
let res = workqueue::system().enqueue(work);
277+
if res.is_err() {
278+
dev_err!(
279+
data.dev,
280+
"Unable to schedule work item for message {}",
281+
msg.msg0
282+
);
283+
}
284+
} else {
285+
dev_err!(
286+
data.dev,
287+
"Unable to allocate work item for message {}",
288+
msg.msg0
289+
);
290+
}
291+
}
292+
}
293+
294+
unsafe impl Send for SepData {}
295+
unsafe impl Sync for SepData {}
296+
297+
struct SepDriver(Arc<SepData>);
298+
299+
kernel::of_device_table!(
300+
OF_TABLE,
301+
MODULE_OF_TABLE,
302+
(),
303+
[(of::DeviceId::new(c_str!("apple,sep")), ())]
304+
);
305+
306+
impl platform::Driver for SepDriver {
307+
type IdInfo = ();
308+
309+
const OF_ID_TABLE: Option<of::IdTable<()>> = Some(&OF_TABLE);
310+
311+
fn probe(
312+
pdev: &platform::Device<device::Core>,
313+
_info: Option<&()>,
314+
) -> Result<Pin<KBox<SepDriver>>> {
315+
let of = pdev.as_ref().of_node().ok_or(EIO)?;
316+
let res = of.reserved_mem_region_to_resource_byname(c_str!("sepfw"))?;
317+
let data = SepData::new(
318+
pdev,
319+
FwRegionParams {
320+
addr: res.start(),
321+
size: res.size().try_into()?,
322+
},
323+
)?;
324+
*data.mbox.lock() = Some(Mailbox::new_byname(
325+
pdev.as_ref(),
326+
c_str!("mbox"),
327+
data.clone(),
328+
)?);
329+
data.start()?;
330+
Ok(KBox::pin(SepDriver(data), GFP_KERNEL)?)
331+
}
332+
}
333+
334+
impl Drop for SepDriver {
335+
fn drop(&mut self) {
336+
self.0.remove();
337+
}
338+
}
339+
340+
module_platform_driver! {
341+
type: SepDriver,
342+
name: "apple_sep",
343+
description: "Secure enclave processor stub driver",
344+
license: "Dual MIT/GPL",
345+
}

0 commit comments

Comments
 (0)