1use std::{
5 collections::{hash_map::Entry, HashMap},
6 sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
7};
8
9use error::LuwenError;
10use all_smi_luwen_core::Arch;
11use all_smi_luwen_if::{FnDriver, FnOptions};
12use all_smi_ttkmd_if::{PciError, PossibleTlbAllocation};
13
14mod detect;
15pub mod error;
16mod wormhole;
17
18use wormhole::ethernet::{self, EthCommCoord};
19
20pub use detect::{
21 detect_chips, detect_chips_fallible, detect_chips_silent, detect_local_chips, start_detect,
22};
23pub use all_smi_ttkmd_if::{DmaBuffer, DmaConfig, PciDevice, Tlb};
24
25#[derive(Clone)]
26pub struct ExtendedPciDeviceWrapper {
27 inner: Arc<RwLock<ExtendedPciDevice>>,
28}
29
30impl ExtendedPciDeviceWrapper {
31 pub fn borrow_mut(&self) -> RwLockWriteGuard<ExtendedPciDevice> {
32 self.inner.as_ref().write().unwrap()
33 }
34
35 pub fn borrow(&self) -> RwLockReadGuard<ExtendedPciDevice> {
36 self.inner.as_ref().read().unwrap()
37 }
38}
39
40pub struct ExtendedPciDevice {
41 pub device: PciDevice,
42
43 pub harvested_rows: u32,
44 pub grid_size_x: u8,
45 pub grid_size_y: u8,
46
47 pub eth_x: u8,
48 pub eth_y: u8,
49 pub command_q_addr: u32,
50 pub fake_block: bool,
51
52 pub default_tlb: PossibleTlbAllocation,
53
54 pub ethernet_dma_buffer: HashMap<(u8, u8), DmaBuffer>,
55}
56
57impl ExtendedPciDevice {
58 pub fn open(pci_interface: usize) -> Result<ExtendedPciDeviceWrapper, all_smi_ttkmd_if::PciError> {
59 let device = PciDevice::open(pci_interface)?;
60
61 let (grid_size_x, grid_size_y) = match device.arch {
62 all_smi_luwen_core::Arch::Grayskull => (13, 12),
63 all_smi_luwen_core::Arch::Wormhole => (10, 12),
64 all_smi_luwen_core::Arch::Blackhole => (17, 12),
65 };
66
67 let default_tlb;
68
69 if device.arch != Arch::Grayskull && device.driver_version >= 2 {
71 let size = match device.arch {
72 Arch::Wormhole => 1 << 24, Arch::Blackhole => 1 << 21, _ => {
75 return Err(PciError::TlbAllocationError(
76 "Unsupported architecture for TLB allocation".to_string(),
77 ))
78 }
79 };
80
81 if let Ok(tlb) = device.allocate_tlb(size) {
82 default_tlb = PossibleTlbAllocation::Allocation(tlb);
83 } else {
84 return Err(PciError::TlbAllocationError(
87 "Failed to find a free tlb".to_string(),
88 ));
89 }
90 } else {
91 default_tlb = PossibleTlbAllocation::Hardcoded(match device.arch {
93 all_smi_luwen_core::Arch::Grayskull | all_smi_luwen_core::Arch::Wormhole => 184,
94 all_smi_luwen_core::Arch::Blackhole => 190,
95 });
96 }
97
98 Ok(ExtendedPciDeviceWrapper {
99 inner: Arc::new(RwLock::new(ExtendedPciDevice {
100 harvested_rows: 0,
101 grid_size_x,
102 grid_size_y,
103 eth_x: 4,
104 eth_y: 6,
105 command_q_addr: 0,
106 fake_block: false,
107
108 default_tlb,
109
110 device,
111
112 ethernet_dma_buffer: HashMap::with_capacity(16),
113 })),
114 })
115 }
116
117 pub fn read_block(&mut self, addr: u32, data: &mut [u8]) -> Result<(), PciError> {
118 self.device.read_block(addr, data)
119 }
120
121 pub fn write_block(&mut self, addr: u32, data: &[u8]) -> Result<(), PciError> {
122 self.device.write_block(addr, data)
123 }
124}
125
126pub fn comms_callback(
127 ud: &ExtendedPciDeviceWrapper,
128 op: FnOptions,
129) -> Result<(), Box<dyn std::error::Error>> {
130 Ok(comms_callback_inner(ud, op)?)
131}
132
133pub fn comms_callback_inner(
134 ud: &ExtendedPciDeviceWrapper,
135 op: FnOptions,
136) -> Result<(), LuwenError> {
137 match op {
138 FnOptions::Driver(op) => match op {
139 FnDriver::DeviceInfo(info) => {
140 let borrow = ud.borrow();
141 if !info.is_null() {
142 unsafe {
143 *info = Some(all_smi_luwen_if::DeviceInfo {
144 bus: borrow.device.physical.pci_bus,
145 slot: borrow.device.physical.slot,
146 function: borrow.device.physical.pci_function,
147 domain: borrow.device.physical.pci_domain,
148
149 interface_id: borrow.device.id as u32,
150
151 vendor: borrow.device.physical.vendor_id,
152 device_id: borrow.device.physical.device_id,
153 board_id: borrow.device.physical.subsystem_id,
154 bar_size: borrow.device.pci_bar.as_ref().map(|v| v.bar_size_bytes),
155 });
156 }
157 }
158 }
159 },
160 FnOptions::Axi(op) => match op {
161 all_smi_luwen_if::FnAxi::Read { addr, data, len } => {
162 if len > 0 {
163 if len <= 4 {
164 let output = ud.borrow_mut().device.read32(addr)?;
165 let output = output.to_le_bytes();
166 unsafe {
167 data.copy_from_nonoverlapping(output.as_ptr(), len as usize);
168 }
169 } else {
170 unsafe {
171 ud.borrow_mut().read_block(
172 addr,
173 std::slice::from_raw_parts_mut(data, len as usize),
174 )?
175 };
176 }
177 }
178 }
179 all_smi_luwen_if::FnAxi::Write { addr, data, len } => {
180 if len > 0 {
181 if len <= 4 {
183 let to_write = if len == 4 {
184 let slice = unsafe { std::slice::from_raw_parts(data, len as usize) };
185 u32::from_le_bytes(slice.try_into().unwrap())
186 } else {
187 let value = ud.borrow_mut().device.read32(addr)?;
190 let mut value = value.to_le_bytes();
191 unsafe {
192 value
193 .as_mut_ptr()
194 .copy_from_nonoverlapping(data, len as usize);
195 }
196
197 u32::from_le_bytes(value)
198 };
199
200 ud.borrow_mut().device.write32(addr, to_write)?;
201 } else {
202 unsafe {
203 ud.borrow_mut()
204 .write_block(addr, std::slice::from_raw_parts(data, len as usize))?
205 };
206 }
207 }
208 }
209 },
210 FnOptions::Noc(op) => match op {
211 all_smi_luwen_if::FnNoc::Read {
212 noc_id,
213 x,
214 y,
215 addr,
216 data,
217 len,
218 } => {
219 let mut reader = ud.borrow_mut();
220 let reader: &mut ExtendedPciDevice = &mut reader;
221
222 reader.device.noc_read(
223 &reader.default_tlb,
224 Tlb {
225 local_offset: addr,
226 x_end: x as u8,
227 y_end: y as u8,
228 noc_sel: noc_id,
229 mcast: false,
230 ..Default::default()
231 },
232 unsafe { std::slice::from_raw_parts_mut(data, len as usize) },
233 )?;
234 }
235 all_smi_luwen_if::FnNoc::Write {
236 noc_id,
237 x,
238 y,
239 addr,
240 data,
241 len,
242 } => {
243 let mut writer = ud.borrow_mut();
244 let writer: &mut ExtendedPciDevice = &mut writer;
245
246 writer.device.noc_write(
247 &writer.default_tlb,
248 Tlb {
249 local_offset: addr,
250 x_end: x as u8,
251 y_end: y as u8,
252 noc_sel: noc_id,
253 mcast: false,
254 ..Default::default()
255 },
256 unsafe { std::slice::from_raw_parts(data, len as usize) },
257 )?;
258 }
259 all_smi_luwen_if::FnNoc::Broadcast {
260 noc_id,
261 addr,
262 data,
263 len,
264 } => {
265 let mut writer = ud.borrow_mut();
266 let writer: &mut ExtendedPciDevice = &mut writer;
267
268 let (x_start, y_start) = match writer.device.arch {
269 all_smi_luwen_core::Arch::Grayskull => (0, 0),
270 all_smi_luwen_core::Arch::Wormhole => (1, 0),
271 all_smi_luwen_core::Arch::Blackhole => (0, 1),
272 };
273
274 writer.device.noc_write(
275 &writer.default_tlb,
276 Tlb {
277 local_offset: addr,
278 x_start,
279 y_start,
280 x_end: writer.grid_size_x - 1,
281 y_end: writer.grid_size_y - 1,
282 noc_sel: noc_id,
283 mcast: true,
284 ..Default::default()
285 },
286 unsafe { std::slice::from_raw_parts(data, len as usize) },
287 )?;
288 }
289 all_smi_luwen_if::FnNoc::Multicast {
290 noc_id,
291 start_x,
292 start_y,
293 end_x,
294 end_y,
295 addr,
296 data,
297 len,
298 } => {
299 let mut writer = ud.borrow_mut();
300 let writer: &mut ExtendedPciDevice = &mut writer;
301
302 let (min_start_x, min_start_y) = match writer.device.arch {
303 all_smi_luwen_core::Arch::Grayskull => (0, 0),
304 all_smi_luwen_core::Arch::Wormhole => (1, 0),
305 all_smi_luwen_core::Arch::Blackhole => (0, 1),
306 };
307
308 let (start_x, start_y) = (start_x.max(min_start_x), start_y.max(min_start_y));
309
310 writer.device.noc_write(
311 &writer.default_tlb,
312 Tlb {
313 local_offset: addr,
314 x_start: start_x,
315 y_start: start_y,
316 x_end: end_x,
317 y_end: end_y,
318 noc_sel: noc_id,
319 mcast: true,
320 ..Default::default()
321 },
322 unsafe { std::slice::from_raw_parts(data, len as usize) },
323 )?;
324 }
325 },
326 FnOptions::Eth(op) => match op.rw {
327 all_smi_luwen_if::FnNoc::Read {
328 noc_id,
329 x,
330 y,
331 addr,
332 data,
333 len,
334 } => {
335 let mut borrow = ud.borrow_mut();
336 let borrow: &mut ExtendedPciDevice = &mut borrow;
337
338 let eth_x = borrow.eth_x;
339 let eth_y = borrow.eth_y;
340
341 let command_q_addr = borrow.device.noc_read32(
342 &borrow.default_tlb,
343 Tlb {
344 local_offset: 0x170,
345 noc_sel: 0,
346 x_end: eth_x,
347 y_end: eth_y,
348 ..Default::default()
349 },
350 )?;
351 let fake_block = borrow.fake_block;
352
353 let default_tlb = &mut borrow.default_tlb;
354 let read32 = |borrow: &mut PciDevice, addr| {
355 borrow.noc_read32(
356 default_tlb,
357 Tlb {
358 local_offset: addr,
359 noc_sel: 0,
360 x_end: eth_x,
361 y_end: eth_y,
362 ..Default::default()
363 },
364 )
365 };
366
367 let write32 = |borrow: &mut PciDevice, addr, data| {
368 borrow.noc_write32(
369 default_tlb,
370 Tlb {
371 local_offset: addr,
372 noc_sel: 0,
373 x_end: eth_x,
374 y_end: eth_y,
375 ..Default::default()
376 },
377 data,
378 )
379 };
380
381 let dma_buffer = {
382 let key = (eth_x, eth_y);
383 if let Entry::Vacant(e) = borrow.ethernet_dma_buffer.entry(key) {
384 e.insert(borrow.device.allocate_dma_buffer(1 << 20)?);
386 }
387
388 unsafe { borrow.ethernet_dma_buffer.get_mut(&key).unwrap_unchecked() }
390 };
391
392 ethernet::fixup_queues(&mut borrow.device, read32, write32, command_q_addr)?;
393
394 if len <= 4 {
395 let value = ethernet::eth_read32(
396 &mut borrow.device,
397 read32,
398 write32,
399 command_q_addr,
400 EthCommCoord {
401 coord: op.addr,
402 noc_id,
403 noc_x: x as u8,
404 noc_y: y as u8,
405 offset: addr,
406 },
407 std::time::Duration::from_secs(5 * 60),
408 )?;
409
410 let sl = unsafe { std::slice::from_raw_parts_mut(data, len as usize) };
411 let vl = value.to_le_bytes();
412
413 for (s, v) in sl.iter_mut().zip(vl.iter()) {
414 *s = *v;
415 }
416 } else {
417 ethernet::block_read(
418 &mut borrow.device,
419 read32,
420 write32,
421 dma_buffer,
422 command_q_addr,
423 std::time::Duration::from_secs(5 * 60),
424 fake_block,
425 EthCommCoord {
426 coord: op.addr,
427 noc_id,
428 noc_x: x as u8,
429 noc_y: y as u8,
430 offset: addr,
431 },
432 unsafe { std::slice::from_raw_parts_mut(data, len as usize) },
433 )?;
434 }
435 }
436 all_smi_luwen_if::FnNoc::Write {
437 noc_id,
438 x,
439 y,
440 addr,
441 data,
442 len,
443 } => {
444 let mut borrow = ud.borrow_mut();
445 let borrow: &mut ExtendedPciDevice = &mut borrow;
446
447 let eth_x = borrow.eth_x;
448 let eth_y = borrow.eth_y;
449
450 let command_q_addr = borrow.device.noc_read32(
451 &borrow.default_tlb,
452 Tlb {
453 local_offset: 0x170,
454 noc_sel: 0,
455 x_end: eth_x,
456 y_end: eth_y,
457 ..Default::default()
458 },
459 )?;
460 let fake_block = borrow.fake_block;
461
462 let default_tlb = &borrow.default_tlb;
463 let read32 = |borrow: &mut PciDevice, addr| {
464 borrow.noc_read32(
465 default_tlb,
466 Tlb {
467 local_offset: addr,
468 noc_sel: 0,
469 x_end: eth_x,
470 y_end: eth_y,
471 ..Default::default()
472 },
473 )
474 };
475
476 let write32 = |borrow: &mut PciDevice, addr, data| {
477 borrow.noc_write32(
478 default_tlb,
479 Tlb {
480 local_offset: addr,
481 noc_sel: 0,
482 x_end: eth_x,
483 y_end: eth_y,
484 ..Default::default()
485 },
486 data,
487 )
488 };
489
490 let dma_buffer = {
491 let key = (eth_x, eth_y);
492 if let Entry::Vacant(e) = borrow.ethernet_dma_buffer.entry(key) {
493 e.insert(borrow.device.allocate_dma_buffer(1 << 20)?);
495 }
496
497 unsafe { borrow.ethernet_dma_buffer.get_mut(&key).unwrap_unchecked() }
499 };
500
501 ethernet::fixup_queues(&mut borrow.device, read32, write32, command_q_addr)?;
502
503 if len <= 4 {
504 let sl = unsafe { std::slice::from_raw_parts(data, len as usize) };
505 let mut value = 0u32;
506 for s in sl.iter().rev() {
507 value <<= 8;
508 value |= *s as u32;
509 }
510
511 ethernet::eth_write32(
512 &mut borrow.device,
513 read32,
514 write32,
515 command_q_addr,
516 EthCommCoord {
517 coord: op.addr,
518 noc_id,
519 noc_x: x as u8,
520 noc_y: y as u8,
521 offset: addr,
522 },
523 std::time::Duration::from_secs(5 * 60),
524 value,
525 )?;
526 } else {
527 ethernet::block_write(
528 &mut borrow.device,
529 read32,
530 write32,
531 dma_buffer,
532 command_q_addr,
533 std::time::Duration::from_secs(5 * 60),
534 fake_block,
535 EthCommCoord {
536 coord: op.addr,
537 noc_id,
538 noc_x: x as u8,
539 noc_y: y as u8,
540 offset: addr,
541 },
542 unsafe { std::slice::from_raw_parts(data, len as usize) },
543 )?;
544 }
545 }
546 all_smi_luwen_if::FnNoc::Broadcast {
547 noc_id,
548 addr,
549 data,
550 len,
551 } => {
552 todo!("Tried to do an ethernet broadcast which is not supported, noc_id: {}, addr: {:#x}, data: {:p}, len: {:x}", noc_id, addr, data, len);
553 }
554 all_smi_luwen_if::FnNoc::Multicast {
555 noc_id,
556 start_x,
557 start_y,
558 end_x,
559 end_y,
560 addr,
561 data,
562 len,
563 } => {
564 todo!("Tried to do an ethernet multicast which is not supported, noc_id: {}, start: ({}, {}), end: ({}, {}), addr: {:#x}, data: {:p}, len: {:x}", noc_id, start_x, start_y, end_x, end_y, addr, data, len);
565 }
566 },
567 }
568
569 Ok(())
570}
571
572pub fn open(interface_id: usize) -> Result<all_smi_luwen_if::chip::Chip, LuwenError> {
573 let ud = ExtendedPciDevice::open(interface_id)?;
574
575 let arch = ud.borrow().device.arch;
576
577 Ok(all_smi_luwen_if::chip::Chip::open(
578 arch,
579 all_smi_luwen_if::CallbackStorage::new(comms_callback, ud.clone()),
580 )?)
581}