Nothing to see here, move along meow
1use crate::cap::cnode;
2use crate::cap::frame_table::{self, FRAME_TABLE};
3use crate::cap::object::ObjectTag;
4use crate::cap::pool::POOL;
5use crate::cap::table::Rights;
6use crate::error::KernelError;
7use crate::pci::DEVICE_TABLE;
8use crate::proc::PROCESSES;
9use crate::proc::context::CpuContext;
10use lancer_core::object_layout::{FrameObject, PciDeviceObject};
11
12use super::{SyscallResult, try_syscall};
13
14pub fn sys_iommu_map(ctx: &mut CpuContext) {
15 let pci_cap_addr = ctx.rdi;
16 let frame_cap_addr = ctx.rsi;
17 let pid = crate::arch::syscall::current_pid();
18
19 let (pci_cap, frame_cap) = {
20 let ptable = PROCESSES.lock();
21 let pool = POOL.lock_after(&ptable);
22 let pci = try_syscall!(
23 ctx,
24 cnode::resolve_caller_validate(
25 pid,
26 pci_cap_addr,
27 ObjectTag::PciDevice,
28 Rights::WRITE,
29 &ptable,
30 &pool,
31 )
32 );
33 let frame = try_syscall!(
34 ctx,
35 cnode::resolve_caller_validate(
36 pid,
37 frame_cap_addr,
38 ObjectTag::Frame,
39 Rights::WRITE,
40 &ptable,
41 &pool,
42 )
43 );
44 (pci, frame)
45 };
46
47 let device_table_idx = {
48 let pool = POOL.lock();
49 match pool.read_as::<PciDeviceObject>(pci_cap.phys(), pci_cap.generation()) {
50 Ok(d) => d.device_table_idx,
51 Err(e) => {
52 ctx.rax = SyscallResult::error(e).raw();
53 return;
54 }
55 }
56 };
57
58 let (bus, devfn) = {
59 let dev_table = DEVICE_TABLE.lock();
60 match dev_table.get(device_table_idx as usize) {
61 Some(d) => (d.bus, d.devfn()),
62 None => {
63 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
64 return;
65 }
66 }
67 };
68
69 let (frame_phys, ft_idx) = {
70 let pool = POOL.lock();
71 match pool.read_as::<FrameObject>(frame_cap.phys(), frame_cap.generation()) {
72 Ok(f) => (f.phys_addr, f.frame_table_idx),
73 Err(e) => {
74 ctx.rax = SyscallResult::error(e).raw();
75 return;
76 }
77 }
78 };
79
80 if ft_idx == lancer_core::header::NONE_SENTINEL as u16 {
81 ctx.rax = SyscallResult::error(KernelError::ResourceExhausted).raw();
82 return;
83 }
84
85 if !crate::iommu::is_available() {
86 ctx.rax = SyscallResult::error(KernelError::NotInitialized).raw();
87 return;
88 }
89
90 let hhdm_offset = crate::mem::addr::hhdm_offset();
91 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
92
93 let (iova, domain_id) = {
94 let mut devices = crate::iommu::DMA_DEVICES.lock();
95 let dev = match devices
96 .iter_mut()
97 .find(|d| d.bus == bus && d.devfn == devfn)
98 {
99 Some(d) => d,
100 None => {
101 ctx.rax = SyscallResult::error(KernelError::NotFound).raw();
102 return;
103 }
104 };
105
106 let iova = match dev.iova_alloc.allocate(1) {
107 Some(base) => base,
108 None => {
109 ctx.rax = SyscallResult::error(KernelError::ResourceExhausted).raw();
110 return;
111 }
112 };
113
114 match crate::iommu::tables::map_iova_to_phys(
115 &dev.dma_pt,
116 iova,
117 x86_64::PhysAddr::new(frame_phys),
118 &mut allocator,
119 hhdm_offset,
120 ) {
121 Ok(()) => {}
122 Err(e) => {
123 let _ = dev.iova_alloc.free(iova, 1);
124 ctx.rax = SyscallResult::error(e).raw();
125 return;
126 }
127 }
128
129 (iova, dev.domain_id)
130 };
131
132 if let Err(e) = crate::iommu::invalidate_iotlb_domain(domain_id) {
133 crate::kprintln!(
134 "[IOMMU] IOTLB invalidation warning during iommu_map: {:?}",
135 e
136 );
137 }
138
139 let mut ft = FRAME_TABLE.lock();
140 let entry = ft.get_mut(ft_idx);
141 match entry.iommu_mapping() {
142 Some(_) => {
143 drop(ft);
144 crate::iommu::unmap_frame_iommu(bus, devfn, iova);
145 ctx.rax = SyscallResult::error(KernelError::AlreadyExists).raw();
146 }
147 None => {
148 entry.set_iommu(frame_table::IommuMapping { bus, devfn, iova });
149 ctx.rax = SyscallResult::success(iova).raw();
150 }
151 }
152}
153
154pub fn sys_iommu_unmap(ctx: &mut CpuContext) {
155 let pci_cap_addr = ctx.rdi;
156 let frame_cap_addr = ctx.rsi;
157 let pid = crate::arch::syscall::current_pid();
158
159 let (pci_cap, frame_cap) = {
160 let ptable = PROCESSES.lock();
161 let pool = POOL.lock_after(&ptable);
162 let pci = try_syscall!(
163 ctx,
164 cnode::resolve_caller_validate(
165 pid,
166 pci_cap_addr,
167 ObjectTag::PciDevice,
168 Rights::WRITE,
169 &ptable,
170 &pool,
171 )
172 );
173 let frame = try_syscall!(
174 ctx,
175 cnode::resolve_caller_validate(
176 pid,
177 frame_cap_addr,
178 ObjectTag::Frame,
179 Rights::WRITE,
180 &ptable,
181 &pool,
182 )
183 );
184 (pci, frame)
185 };
186
187 let device_table_idx = {
188 let pool = POOL.lock();
189 match pool.read_as::<PciDeviceObject>(pci_cap.phys(), pci_cap.generation()) {
190 Ok(d) => d.device_table_idx,
191 Err(e) => {
192 ctx.rax = SyscallResult::error(e).raw();
193 return;
194 }
195 }
196 };
197
198 let (bus, devfn) = {
199 let dev_table = DEVICE_TABLE.lock();
200 match dev_table.get(device_table_idx as usize) {
201 Some(d) => (d.bus, d.devfn()),
202 None => {
203 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
204 return;
205 }
206 }
207 };
208
209 let ft_idx = {
210 let pool = POOL.lock();
211 match pool.read_as::<FrameObject>(frame_cap.phys(), frame_cap.generation()) {
212 Ok(f) => f.frame_table_idx,
213 Err(e) => {
214 ctx.rax = SyscallResult::error(e).raw();
215 return;
216 }
217 }
218 };
219 if ft_idx == lancer_core::header::NONE_SENTINEL as u16 {
220 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
221 return;
222 }
223 let mapping = {
224 let mut ft = FRAME_TABLE.lock();
225 let entry = ft.get_mut(ft_idx);
226 match entry.iommu_mapping() {
227 Some(m) if m.bus == bus && m.devfn == devfn => {
228 let m_copy = frame_table::IommuMapping {
229 bus: m.bus,
230 devfn: m.devfn,
231 iova: m.iova,
232 };
233 entry.clear_iommu();
234 m_copy
235 }
236 Some(_) => {
237 ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw();
238 return;
239 }
240 None => {
241 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
242 return;
243 }
244 }
245 };
246
247 crate::iommu::unmap_frame_iommu(bus, devfn, mapping.iova);
248 ctx.rax = SyscallResult::ok().raw();
249}
250
251pub fn sys_iommu_setup_device(ctx: &mut CpuContext) {
252 let pid = crate::arch::syscall::current_pid();
253 if pid != crate::types::Pid::new(0) {
254 ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw();
255 return;
256 }
257
258 let pci_cap_addr = ctx.rdi;
259 let target_proc_addr = ctx.rsi;
260
261 let (device_table_idx, target_pid) = {
262 let ptable = PROCESSES.lock();
263 let pool = POOL.lock_after(&ptable);
264 let pci_cap = try_syscall!(
265 ctx,
266 cnode::resolve_caller_validate(
267 pid,
268 pci_cap_addr,
269 ObjectTag::PciDevice,
270 Rights::WRITE,
271 &ptable,
272 &pool,
273 )
274 );
275 let proc_cap = try_syscall!(
276 ctx,
277 cnode::resolve_caller_validate(
278 pid,
279 target_proc_addr,
280 ObjectTag::Process,
281 Rights::READ,
282 &ptable,
283 &pool,
284 )
285 );
286 let idx = match pool.read_as::<PciDeviceObject>(pci_cap.phys(), pci_cap.generation()) {
287 Ok(d) => d.device_table_idx,
288 Err(e) => {
289 ctx.rax = SyscallResult::error(e).raw();
290 return;
291 }
292 };
293 let tpid = match pool.read_as::<lancer_core::object_layout::ProcessObject>(
294 proc_cap.phys(),
295 proc_cap.generation(),
296 ) {
297 Ok(p) => match crate::types::Pid::try_new(p.pid) {
298 Some(pid) => pid,
299 None => {
300 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
301 return;
302 }
303 },
304 Err(e) => {
305 ctx.rax = SyscallResult::error(e).raw();
306 return;
307 }
308 };
309 (idx, tpid)
310 };
311
312 if !crate::iommu::is_available() {
313 ctx.rax = SyscallResult::ok().raw();
314 return;
315 }
316
317 let hhdm_offset = crate::mem::addr::hhdm_offset();
318 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
319
320 ctx.rax = match crate::iommu::setup_device_context(
321 device_table_idx,
322 target_pid,
323 &mut allocator,
324 hhdm_offset,
325 ) {
326 Ok(()) => SyscallResult::ok().raw(),
327 Err(e) => SyscallResult::error(e).raw(),
328 };
329}