Skip to content

Commit 54c2a5a

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 <fnkl.kernel@gmail.com>
1 parent 7fa67c9 commit 54c2a5a

3 files changed

Lines changed: 358 additions & 0 deletions

File tree

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: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
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) -> Result<ShMem> {
61+
let of = dev.as_ref().of_node().ok_or(EIO)?;
62+
let iomem = dma::CoherentAllocation::<u8>::alloc_coherent(dev, SHMEM_SIZE, GFP_KERNEL)?;
63+
64+
let panic_offset = 0x4000;
65+
let panic_size = 0x8000;
66+
memcpy_to_iomem(&iomem, panic_offset, &1u32.to_le_bytes())?;
67+
68+
let lpol_offset = panic_offset + panic_size;
69+
let lpol = of
70+
.find_property(c_str!("local-policy-manifest"))
71+
.ok_or(EIO)?;
72+
memcpy_to_iomem(
73+
&iomem,
74+
lpol_offset,
75+
&(lpol.value().len() as u32).to_le_bytes(),
76+
)?;
77+
memcpy_to_iomem(&iomem, lpol_offset + 4, lpol.value())?;
78+
let lpol_size = align_up(lpol.value().len() + 4, 0x4000);
79+
80+
let ibot_offset = lpol_offset + lpol_size;
81+
let ibot = of.find_property(c_str!("iboot-manifest")).ok_or(EIO)?;
82+
memcpy_to_iomem(
83+
&iomem,
84+
ibot_offset,
85+
&(ibot.value().len() as u32).to_le_bytes(),
86+
)?;
87+
memcpy_to_iomem(&iomem, ibot_offset + 4, ibot.value())?;
88+
let ibot_size = align_up(ibot.value().len() + 4, 0x4000);
89+
90+
memcpy_to_iomem(&iomem, 0, b"CNIP")?;
91+
memcpy_to_iomem(&iomem, 4, &(panic_size as u32).to_le_bytes())?;
92+
memcpy_to_iomem(&iomem, 8, &(panic_offset as u32).to_le_bytes())?;
93+
94+
memcpy_to_iomem(&iomem, 16, b"OPLA")?;
95+
memcpy_to_iomem(&iomem, 16 + 4, &(lpol_size as u32).to_le_bytes())?;
96+
memcpy_to_iomem(&iomem, 16 + 8, &(lpol_offset as u32).to_le_bytes())?;
97+
98+
memcpy_to_iomem(&iomem, 32, b"IPIS")?;
99+
memcpy_to_iomem(&iomem, 32 + 4, &(ibot_size as u32).to_le_bytes())?;
100+
memcpy_to_iomem(&iomem, 32 + 8, &(ibot_offset as u32).to_le_bytes())?;
101+
102+
memcpy_to_iomem(&iomem, 48, b"llun")?;
103+
Ok(iomem)
104+
}
105+
106+
#[pin_data]
107+
struct SepReceiveWork {
108+
data: Arc<SepData>,
109+
msg: Message,
110+
#[pin]
111+
work: Work<SepReceiveWork>,
112+
}
113+
114+
impl_has_work! {
115+
impl HasWork<Self, 0> for SepReceiveWork { self.work }
116+
}
117+
118+
impl SepReceiveWork {
119+
fn new(data: Arc<SepData>, msg: Message) -> Result<Arc<Self>> {
120+
Arc::pin_init(
121+
pin_init!(SepReceiveWork {
122+
data,
123+
msg,
124+
work <- new_work!("SepReceiveWork::work"),
125+
}),
126+
GFP_ATOMIC,
127+
)
128+
}
129+
}
130+
131+
impl WorkItem for SepReceiveWork {
132+
type Pointer = Arc<SepReceiveWork>;
133+
134+
fn run(this: Arc<SepReceiveWork>) {
135+
this.data.process_message(this.msg);
136+
}
137+
}
138+
139+
struct FwRegionParams {
140+
addr: u64,
141+
size: usize,
142+
}
143+
144+
#[pin_data]
145+
struct SepData {
146+
dev: ARef<device::Device>,
147+
#[pin]
148+
mbox: Mutex<Option<Mailbox<SepData>>>,
149+
shmem: ShMem,
150+
region_params: FwRegionParams,
151+
fw_mapped: AtomicBool,
152+
}
153+
154+
impl SepData {
155+
fn new(dev: &platform::Device, region_params: FwRegionParams) -> Result<Arc<SepData>> {
156+
Arc::pin_init(
157+
try_pin_init!(SepData {
158+
shmem: build_shmem(dev)?,
159+
dev: ARef::<device::Device>::from(dev.as_ref()),
160+
mbox <- new_mutex!(None),
161+
region_params,
162+
fw_mapped: AtomicBool::new(false),
163+
}),
164+
GFP_KERNEL,
165+
)
166+
}
167+
fn start(&self) -> Result<()> {
168+
self.mbox.lock().as_ref().unwrap().send(
169+
Message {
170+
msg0: EP_BOOT | (MSG_BOOT_TZ0 << MSG_TYPE_SHIFT),
171+
msg1: 0,
172+
},
173+
false,
174+
)
175+
}
176+
fn load_fw_and_shmem(&self) -> Result<()> {
177+
let fw_addr = unsafe {
178+
let res = bindings::dma_map_resource(
179+
self.dev.as_raw(),
180+
self.region_params.addr,
181+
self.region_params.size,
182+
bindings::dma_data_direction_DMA_TO_DEVICE,
183+
0,
184+
);
185+
if bindings::dma_mapping_error(self.dev.as_raw(), res) != 0 {
186+
dev_err!(self.dev, "Failed to map firmware");
187+
return Err(ENOMEM);
188+
}
189+
self.fw_mapped.store(true, Ordering::Relaxed);
190+
res >> IOVA_SHIFT
191+
};
192+
let guard = self.mbox.lock();
193+
let mbox = guard.as_ref().unwrap();
194+
mbox.send(
195+
Message {
196+
msg0: EP_BOOT | (MSG_BOOT_IMG4 << MSG_TYPE_SHIFT) | (fw_addr << MSG_DATA_SHIFT),
197+
msg1: 0,
198+
},
199+
false,
200+
)?;
201+
let shm_addr = self.shmem.dma_handle() >> IOVA_SHIFT;
202+
mbox.send(
203+
Message {
204+
msg0: EP_SHMEM | (MSG_SET_SHMEM << MSG_TYPE_SHIFT) | (shm_addr << MSG_DATA_SHIFT),
205+
msg1: 0,
206+
},
207+
false,
208+
)?;
209+
Ok(())
210+
}
211+
fn process_boot_msg(&self, msg: Message) {
212+
let ty = (msg.msg0 >> MSG_TYPE_SHIFT) & MSG_TYPE_MASK;
213+
match ty {
214+
MSG_BOOT_TZ0_ACK1 => {}
215+
MSG_BOOT_TZ0_ACK2 => {
216+
let res = self.load_fw_and_shmem();
217+
if let Err(e) = res {
218+
dev_err!(self.dev, "Unable to load firmware: {:?}", e);
219+
}
220+
}
221+
MSG_BOOT_IMG4_ACK => {}
222+
_ => {
223+
dev_err!(self.dev, "Unknown boot message type: {}", ty);
224+
}
225+
}
226+
}
227+
fn process_discover_msg(&self, msg: Message) {
228+
let ty = (msg.msg0 >> MSG_TYPE_SHIFT) & MSG_TYPE_MASK;
229+
//let data = (msg.msg0 >> MSG_DATA_SHIFT) as u32;
230+
//let param = (msg.msg0 >> MSG_PARAM_SHIFT) & MSG_PARAM_MASK;
231+
match ty {
232+
MSG_ADVERTISE_EP => {
233+
/*dev_info!(
234+
self.dev,
235+
"Got endpoint {:?} at {}",
236+
core::str::from_utf8(&data.to_be_bytes()),
237+
param
238+
);*/
239+
}
240+
_ => {
241+
//dev_warn!(self.dev, "Unknown discovery message type: {}", ty);
242+
}
243+
}
244+
}
245+
fn process_message(&self, msg: Message) {
246+
let ep = msg.msg0 & MSG_EP_MASK;
247+
match ep {
248+
EP_BOOT => self.process_boot_msg(msg),
249+
EP_DISCOVER => self.process_discover_msg(msg),
250+
_ => {} // dev_warn!(self.dev, "Message from unknown endpoint: {}", ep),
251+
}
252+
}
253+
fn remove(&self) {
254+
*self.mbox.lock() = None;
255+
if self.fw_mapped.load(Ordering::Relaxed) {
256+
unsafe {
257+
bindings::dma_unmap_resource(
258+
self.dev.as_raw(),
259+
self.region_params.addr,
260+
self.region_params.size,
261+
bindings::dma_data_direction_DMA_TO_DEVICE,
262+
0,
263+
);
264+
}
265+
}
266+
}
267+
}
268+
269+
impl MailCallback for SepData {
270+
type Data = Arc<SepData>;
271+
fn recv_message(data: <Self::Data as ForeignOwnable>::Borrowed<'_>, msg: Message) {
272+
let work = SepReceiveWork::new(data.into(), msg);
273+
if let Ok(work) = work {
274+
let res = workqueue::system().enqueue(work);
275+
if res.is_err() {
276+
dev_err!(
277+
data.dev,
278+
"Unable to schedule work item for message {}",
279+
msg.msg0
280+
);
281+
}
282+
} else {
283+
dev_err!(
284+
data.dev,
285+
"Unable to allocate work item for message {}",
286+
msg.msg0
287+
);
288+
}
289+
}
290+
}
291+
292+
unsafe impl Send for SepData {}
293+
unsafe impl Sync for SepData {}
294+
295+
struct SepDriver(Arc<SepData>);
296+
297+
kernel::of_device_table!(
298+
OF_TABLE,
299+
MODULE_OF_TABLE,
300+
(),
301+
[(of::DeviceId::new(c_str!("apple,sep")), ())]
302+
);
303+
304+
impl platform::Driver for SepDriver {
305+
type IdInfo = ();
306+
307+
const OF_ID_TABLE: Option<of::IdTable<()>> = Some(&OF_TABLE);
308+
309+
fn probe(pdev: &mut platform::Device, _info: Option<&()>) -> Result<Pin<KBox<SepDriver>>> {
310+
let of = pdev.as_ref().of_node().ok_or(EIO)?;
311+
let fw_node = of.parse_phandle(c_str!("memory-region"), 0).ok_or(EIO)?;
312+
let mut reg = [0u64, 0u64];
313+
fw_node
314+
.find_property(c_str!("reg"))
315+
.ok_or(EIO)?
316+
.copy_to_slice(&mut reg)?;
317+
let data = SepData::new(
318+
pdev,
319+
FwRegionParams {
320+
addr: reg[0],
321+
size: reg[1] as usize,
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+
license: "Dual MIT/GPL",
344+
}

0 commit comments

Comments
 (0)