Skip to content

Commit 2949dc4

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 fda68e8 commit 2949dc4

File tree

3 files changed

+364
-0
lines changed

3 files changed

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

0 commit comments

Comments
 (0)