1use std::{
5 os::{
6 fd::{AsRawFd, RawFd},
7 unix::prelude::FileTypeExt,
8 },
9 sync::Arc,
10};
11
12mod error;
13pub mod ioctl;
14mod kmdif;
15mod pci;
16pub mod tlb;
17
18pub use error::{PciError, PciOpenError};
19use ioctl::{
20 query_mappings, AllocateDmaBuffer, GetDeviceInfo, GetDeviceInfoOut, Mapping, QueryMappings,
21};
22use luwen_core::Arch;
23pub use tlb::{DeviceTlbInfo, Tlb};
24
25impl TryFrom<&GetDeviceInfoOut> for Arch {
26 type Error = u16;
27
28 fn try_from(value: &GetDeviceInfoOut) -> Result<Self, Self::Error> {
29 match value.device_id {
30 0xfaca => Ok(Arch::Grayskull),
31 0x401e => Ok(Arch::Wormhole),
32 0xb140 => Ok(Arch::Blackhole),
33 id => Err(id),
34 }
35 }
36}
37
38pub struct DmaBuffer {
39 pub buffer: memmap2::MmapMut,
40 pub physical_address: u64,
41 pub size: u64,
42}
43
44#[derive(Clone)]
45pub struct DmaConfig {
46 pub csm_pcie_ctrl_dma_request_offset: u32,
48
49 pub arc_misc_cntl_addr: u32,
51
52 pub dma_host_phys_addr_high: u32,
54
55 pub support_64_bit_dma: bool,
56
57 pub use_msi_for_dma: bool,
58
59 pub read_threshold: u32,
60 pub write_threshold: u32,
61}
62
63pub struct PhysicalDevice {
64 pub vendor_id: u16,
65 pub device_id: u16,
66 pub subsystem_vendor_id: u16,
67 pub subsystem_id: u16,
68
69 pub pci_bus: u16,
70 pub slot: u16,
71 pub pci_function: u16,
72 pub pci_domain: u16,
73}
74
75pub struct BarMapping {
76 pub bar_addr: u64,
77 pub bar_size_bytes: u64,
78
79 pub bar0_uc: memmap2::MmapMut,
80 #[allow(dead_code)]
81 pub bar0_uc_size: u64,
82 pub bar0_uc_offset: u64,
83
84 pub bar0_wc: Option<memmap2::MmapMut>,
85 pub bar0_wc_size: u64,
86
87 pub bar1_uc: Option<memmap2::MmapMut>,
88 pub bar1_uc_size: u64,
89
90 pub system_reg_mapping: Option<memmap2::MmapMut>,
91 #[allow(dead_code)]
92 pub system_reg_mapping_size: usize,
93 pub system_reg_start_offset: u32, pub system_reg_offset_adjust: u32, }
96
97#[derive(Debug)]
98pub struct TlbAllocation {
99 pub id: u32,
100 pub uc_mapping: memmap2::MmapMut,
101 pub size: u64,
102}
103
104#[derive(Debug)]
105pub enum PossibleTlbAllocation {
106 Allocation(TlbAllocation),
107 Hardcoded(u32),
108 NoAllocation,
109}
110
111#[allow(dead_code)]
112pub struct PciDevice {
113 pub id: usize,
114
115 pub physical: PhysicalDevice,
116 pub arch: Arch,
117
118 pub read_checking_enabled: bool,
119 pub read_checking_addr: u32,
120
121 next_dma_buf: usize,
122
123 device_fd: std::fs::File,
124 pub driver_version: u32,
125
126 config_space: std::fs::File,
127
128 max_dma_buf_size_log2: u16,
129
130 #[allow(dead_code)]
131 dma_buffer_mappings: Vec<Arc<DmaBuffer>>,
132 completion_flag_buffer: Option<DmaBuffer>,
133 transfer_buffer: Option<DmaBuffer>,
134
135 pub dma_config: Option<DmaConfig>,
136 pub pci_bar: Option<BarMapping>,
137}
138
139fn allocate_dma_buffer(
140 device_id: usize,
141 device_fd: RawFd,
142 max_dma_buf_size_log2: u32,
143 buffer_index: usize,
144 size: u32,
145) -> Result<DmaBuffer, PciError> {
146 let mut allocate_dma_buf = AllocateDmaBuffer::default();
147 allocate_dma_buf.input.requested_size =
148 (size.min(1 << max_dma_buf_size_log2)).max(kmdif::getpagesize().unwrap() as u32);
149 allocate_dma_buf.input.buf_index = buffer_index as u8;
150
151 if let Err(err) = unsafe { ioctl::allocate_dma_buffer(device_fd, &mut allocate_dma_buf) } {
152 return Err(PciError::DmaAllocationFailed {
153 id: device_id,
154 size: allocate_dma_buf.input.requested_size,
155 err,
156 });
157 }
158
159 let map = unsafe {
160 memmap2::MmapOptions::default()
161 .len(allocate_dma_buf.output.size as usize)
162 .offset(allocate_dma_buf.output.mapping_offset)
163 .map_mut(device_fd)
164 };
165 let map = match map {
166 Err(err) => {
167 return Err(PciError::DmaBufferMappingFailed {
168 id: device_id,
169 source: err,
170 })
171 }
172 Ok(map) => map,
173 };
174
175 let output = DmaBuffer {
176 buffer: map,
177 physical_address: allocate_dma_buf.output.physical_address,
178 size: allocate_dma_buf.output.size as u64,
179 };
180
181 Ok(output)
182}
183
184impl PciDevice {
185 fn map_bar(&mut self) -> Result<(), PciOpenError> {
186 let mut mappings = QueryMappings::<8>::default();
187
188 if let Err(erno) = unsafe { query_mappings(self.device_fd.as_raw_fd(), &mut mappings) } {
189 return Err(PciOpenError::IoctlError {
190 name: "query_mappings".to_string(),
191 id: self.id,
192 source: erno,
193 });
194 }
195
196 let mut bar0_uc_mapping = Mapping::default();
197 let mut bar0_wc_mapping = Mapping::default();
198 let mut bar1_uc_mapping = Mapping::default();
199 let mut _bar1_wc_mapping = Mapping::default();
200 let mut bar2_uc_mapping = Mapping::default();
201 let mut _bar2_wc_mapping = Mapping::default();
202
203 for i in 0..mappings.input.output_mapping_count as usize {
204 match kmdif::MappingId::from_u32(mappings.output.mappings[i].mapping_id) {
205 kmdif::MappingId::Resource0Uc => {
206 bar0_uc_mapping = mappings.output.mappings[i];
207 }
208 kmdif::MappingId::Resource0Wc => {
209 bar0_wc_mapping = mappings.output.mappings[i];
210 }
211 kmdif::MappingId::Resource1Uc => {
212 bar1_uc_mapping = mappings.output.mappings[i];
213 }
214 kmdif::MappingId::Resource1Wc => {
215 _bar1_wc_mapping = mappings.output.mappings[i];
216 }
217 kmdif::MappingId::Resource2Uc => {
218 bar2_uc_mapping = mappings.output.mappings[i];
219 }
220 kmdif::MappingId::Resource2Wc => {
221 _bar2_wc_mapping = mappings.output.mappings[i];
222 }
223 kmdif::MappingId::Unused => {
224 }
226 kmdif::MappingId::Unknown(v) => {
227 println!("WARNING: recieved unknown mapping id {v}");
228 }
229 }
230 }
231
232 if bar0_uc_mapping.mapping_id != kmdif::MappingId::Resource0Uc.as_u32() {
233 return Err(PciOpenError::BarMappingError {
234 name: "bar0_uc_mapping".to_string(),
235 id: self.id,
236 });
237 }
238
239 let wc_mapping_size = if self.arch.is_blackhole() {
240 kmdif::BH_BAR0_WC_MAPPING_SIZE
241 } else {
242 kmdif::GS_BAR0_WC_MAPPING_SIZE
243 };
244
245 let mut bar0_wc_size = 0;
250 let mut bar0_wc = None;
251 if bar0_wc_mapping.mapping_id == kmdif::MappingId::Resource0Wc.as_u32() {
252 bar0_wc_size = bar0_wc_mapping.mapping_size.min(wc_mapping_size);
253 let bar0_wc_map = unsafe {
254 memmap2::MmapOptions::default()
255 .len(bar0_wc_size as usize)
256 .offset(bar0_wc_mapping.mapping_base)
257 .map_mut(self.device_fd.as_raw_fd())
258 };
259 match bar0_wc_map {
260 Ok(map) => {
261 bar0_wc = Some(map);
262 }
263 Err(err) => {
264 println!(
265 "WARNING: Failed to map bar0_wc for {} with error {err}",
266 self.id
267 );
268 bar0_wc_size = 0;
269 bar0_wc = None;
270 }
271 }
272 }
273
274 let bar0_uc_size;
275 let bar0_uc_offset;
276 if bar0_wc.is_some() {
277 bar0_uc_size = bar0_uc_mapping.mapping_size.saturating_sub(wc_mapping_size);
278 bar0_uc_offset = wc_mapping_size;
279 } else {
280 bar0_uc_size = bar0_uc_mapping.mapping_size;
282 bar0_uc_offset = 0;
283 }
284
285 let bar0_uc = unsafe {
286 memmap2::MmapOptions::default()
287 .len(bar0_uc_size as usize)
288 .offset(bar0_uc_mapping.mapping_base + bar0_uc_offset)
289 .map_mut(self.device_fd.as_raw_fd())
290 };
291 let bar0_uc = match bar0_uc {
292 Ok(map) => map,
293 Err(err) => {
294 panic!("Failed to map bar0_uc for {} with error {err}", self.id);
295 }
296 };
297
298 let mut system_reg_mapping_size = 0;
305 let mut system_reg_start_offset = 0;
306 let mut system_reg_offset_adjust = 0;
307 let mut system_reg_mapping = None;
308 if self.arch.is_wormhole() {
309 if bar2_uc_mapping.mapping_id != kmdif::MappingId::Resource2Uc.as_u32() {
310 panic!("Device {} has no BAR4 mapping", self.id);
311 }
312
313 system_reg_mapping_size = bar2_uc_mapping.mapping_size as usize;
314 let system_reg = unsafe {
315 memmap2::MmapOptions::default()
316 .len(system_reg_mapping_size)
317 .offset(bar2_uc_mapping.mapping_base)
318 .map_mut(self.device_fd.as_raw_fd())
319 };
320 system_reg_mapping = match system_reg {
321 Ok(map) => Some(map),
322 Err(err) => {
323 panic!(
324 "BAR4 mapping failed for device {} with error {err}",
325 self.id
326 );
327 }
328 };
329
330 system_reg_start_offset = (512 - 16) * 1024 * 1024;
331 system_reg_offset_adjust = (512 - 32) * 1024 * 1024;
332 }
333
334 let mut bar1_uc = None;
335 let mut bar1_uc_size = 0;
336 if self.arch.is_blackhole() {
337 if bar1_uc_mapping.mapping_id != kmdif::MappingId::Resource1Uc.as_u32() {
338 panic!("Device {} has not Bar1 UC mapping", self.id);
339 }
340
341 bar1_uc_size = bar1_uc_mapping.mapping_size;
342 bar1_uc = Some(unsafe {
343 memmap2::MmapOptions::default()
344 .len(bar1_uc_mapping.mapping_size as usize)
345 .offset(bar1_uc_mapping.mapping_base)
346 .map_mut(self.device_fd.as_raw_fd())
347 .expect("Bar1 mapping failed for device {device_id}")
348 });
349 }
350
351 self.pci_bar = Some(BarMapping {
352 bar_addr: pci::read_bar0_base(&self.config_space),
353 bar_size_bytes: bar0_uc_mapping.mapping_size,
354
355 bar0_uc,
356 bar0_uc_size,
357 bar0_uc_offset,
358 bar0_wc,
359 bar0_wc_size,
360 bar1_uc,
361 bar1_uc_size,
362 system_reg_mapping,
363 system_reg_mapping_size,
364 system_reg_start_offset,
365 system_reg_offset_adjust,
366 });
367
368 Ok(())
369 }
370
371 pub fn open(device_id: usize) -> Result<PciDevice, PciOpenError> {
372 let fd = std::fs::OpenOptions::new()
373 .read(true)
374 .write(true)
375 .open(format!("/dev/tenstorrent/{device_id}"));
376 let fd = match fd {
377 Ok(fd) => fd,
378 Err(err) => {
379 return Err(PciOpenError::DeviceOpenFailed {
380 id: device_id,
381 source: err,
382 })
383 }
384 };
385
386 let mut device_info = GetDeviceInfo::default();
387 if let Err(errorno) = unsafe { ioctl::get_device_info(fd.as_raw_fd(), &mut device_info) } {
388 return Err(PciOpenError::IoctlError {
389 name: "get_device_info".to_string(),
390 id: device_id,
391 source: errorno,
392 });
393 }
394
395 let mut driver_info = ioctl::GetDriverInfo::default();
396 if let Err(errorno) = unsafe { ioctl::get_driver_info(fd.as_raw_fd(), &mut driver_info) } {
397 return Err(PciOpenError::IoctlError {
398 name: "get_driver_info".to_string(),
399 id: device_id,
400 source: errorno,
401 });
402 }
403
404 let arch = Arch::try_from(&device_info.output).map_err(|asic_id| {
405 PciOpenError::UnrecognizedDeviceId {
406 pci_id: device_id,
407 device_id: asic_id,
408 }
409 })?;
410
411 let max_dma_buf_size_log2 = device_info.output.max_dma_buf_size_log2;
412
413 let pci_bus = device_info.output.bus_dev_fn >> 8;
414 let slot = ((device_info.output.bus_dev_fn) >> 3) & 0x1f; let pci_function = (device_info.output.bus_dev_fn) & 0x7; let pci_domain = device_info.output.pci_domain;
417
418 let config_space = std::fs::OpenOptions::new()
419 .read(true)
420 .write(false)
421 .open(format!(
422 "/sys/bus/pci/devices/{pci_domain:04x}:{pci_bus:02x}:{slot:02x}.{pci_function:01x}/config"
423 ));
424 let config_space = match config_space {
425 Ok(file) => file,
426 Err(err) => {
427 panic!("Failed to open config space for device {device_id} with error {err}");
428 }
429 };
430
431 let mut device = PciDevice {
432 id: device_id,
433 arch,
434
435 physical: PhysicalDevice {
436 vendor_id: device_info.output.vendor_id,
437 device_id: device_info.output.device_id,
438 subsystem_vendor_id: device_info.output.subsystem_vendor_id,
439 subsystem_id: device_info.output.subsystem_id,
440 pci_bus,
441 slot,
442 pci_function,
443 pci_domain,
444 },
445
446 read_checking_enabled: true,
447 read_checking_addr: if arch.is_blackhole() {
448 kmdif::BH_NOC_NODE_ID_OFFSET
449 } else {
450 kmdif::GS_WH_ARC_SCRATCH6_ADDR
451 },
452
453 next_dma_buf: 0,
454
455 device_fd: fd,
456 driver_version: driver_info.output.driver_version,
457
458 config_space,
459
460 max_dma_buf_size_log2,
461
462 dma_buffer_mappings: vec![],
463 dma_config: None,
464 completion_flag_buffer: None,
465 transfer_buffer: None,
466
467 pci_bar: None,
468 };
469
470 device.allocate_transfer_buffers();
473
474 device.map_bar()?;
476
477 Ok(device)
478 }
479
480 pub fn allocate_transfer_buffers(&mut self) -> bool {
481 if self.transfer_buffer.is_none() {
484 self.transfer_buffer = self
485 .allocate_dma_buffer_range(
486 kmdif::getpagesize().unwrap() as u32,
487 kmdif::MAX_DMA_BYTES,
488 )
489 .ok();
490 }
491
492 if self.transfer_buffer.is_some() && self.completion_flag_buffer.is_none() {
495 self.completion_flag_buffer = self
496 .allocate_dma_buffer(std::mem::size_of::<u64>() as u32)
497 .ok();
498 }
499
500 self.transfer_buffer.is_some() && self.completion_flag_buffer.is_some()
501 }
502
503 pub fn allocate_dma_buffer_range(
504 &mut self,
505 min_size: u32,
506 max_size: u32,
507 ) -> Result<DmaBuffer, PciError> {
508 let page_size = kmdif::getpagesize().unwrap() as u32;
509
510 let mut page_aligned_size = (max_size + page_size - 1) & !(page_size - 1);
511 let min_aligned_page_size = (min_size + page_size - 1) & !(page_size - 1);
512
513 loop {
514 match allocate_dma_buffer(
515 self.id,
516 self.device_fd.as_raw_fd(),
517 self.max_dma_buf_size_log2 as u32,
518 self.next_dma_buf,
519 page_aligned_size,
520 ) {
521 Ok(buf) => {
522 self.next_dma_buf += 1;
523 return Ok(buf);
524 }
525 Err(err) => {
526 if page_aligned_size <= min_aligned_page_size {
527 return Err(err);
528 }
529
530 page_aligned_size = (page_aligned_size / 2).max(min_aligned_page_size);
531 }
532 }
533 }
534 }
535
536 pub fn allocate_dma_buffer(&mut self, size: u32) -> Result<DmaBuffer, PciError> {
537 self.allocate_dma_buffer_range(size, size)
538 }
539
540 pub fn allocate_tlb(&self, size: u64) -> Result<TlbAllocation, PciError> {
541 let mut data = ioctl::AllocateTlb {
542 input: ioctl::AllocateTlbIn {
543 size,
544 ..Default::default()
545 },
546 ..Default::default()
547 };
548 let result =
549 unsafe { ioctl::allocate_tlb(self.device_fd.as_raw_fd(), (&mut data) as *mut _) };
550
551 let uc_mapping = unsafe {
552 memmap2::MmapOptions::default()
553 .len(size as usize)
554 .offset(data.output.mmap_offset_uc)
555 .map_mut(self.device_fd.as_raw_fd())
556 }
557 .map_err(|_err| PciError::TlbAllocationError("Failed to map uc buffer".to_string()))?;
558
559 match result {
560 Ok(rc) => match rc {
561 0 => Ok(TlbAllocation {
562 id: data.output.id,
563 uc_mapping,
564 size,
565 }),
566 errno => Err(PciError::IoctlError(nix::errno::Errno::from_i32(errno))),
567 },
568 Err(errno) => Err(PciError::IoctlError(errno)),
569 }
570 }
571
572 pub fn free_tlb(&self, alloc: TlbAllocation) -> Result<(), PciError> {
573 let id = alloc.id;
574
575 drop(alloc);
577
578 let result = unsafe {
579 ioctl::free_tlb(
580 self.device_fd.as_raw_fd(),
581 (&mut ioctl::FreeTlb {
582 input: ioctl::FreeTlbIn { id },
583 output: ioctl::FreeTlbOut {},
584 }) as *mut _,
585 )
586 };
587
588 match result {
589 Ok(0) => Ok(()),
590 Ok(rc) => Err(PciError::IoctlError(nix::errno::Errno::from_i32(rc))),
591 Err(errno) => Err(PciError::IoctlError(errno)),
592 }
593 }
594
595 pub fn configure_tlb(
596 &self,
597 alloc: &TlbAllocation,
598 config: ioctl::NocTlbConfig,
599 ) -> Result<(), PciError> {
600 let result = unsafe {
601 ioctl::configure_tlb(
602 self.device_fd.as_raw_fd(),
603 (&mut ioctl::ConfigureTlb {
604 input: ioctl::ConfigureTlbIn {
605 id: alloc.id,
606 config,
607 },
608 output: ioctl::ConfigureTlbOut {
609 ..Default::default()
610 },
611 }) as *mut _,
612 )
613 };
614
615 match result {
616 Ok(0) => Ok(()),
617 Ok(rc) => Err(PciError::IoctlError(nix::errno::Errno::from_i32(rc))),
618 Err(errno) => Err(PciError::IoctlError(errno)),
619 }
620 }
621
622 pub fn scan() -> Vec<usize> {
623 let output = std::fs::read_dir("/dev/tenstorrent");
624 let output = match output {
625 Ok(output) => output,
626 Err(err) => {
627 tracing::debug!("When reading /dev/tenstorrent for a scan hit error: {err}");
628 return Vec::new();
629 }
630 };
631
632 let mut output = output
633 .filter_map(|entry| {
634 let entry = entry.ok()?;
635
636 if !entry.file_type().ok()?.is_char_device() {
637 return None;
638 }
639
640 let path = entry.path();
641 let file_name = path.file_name()?.to_str()?;
642 file_name.parse::<usize>().ok()
643 })
644 .collect::<Vec<_>>();
645
646 output.sort();
647
648 output
649 }
650
651 pub fn setup_tlb(
652 &mut self,
653 index: &PossibleTlbAllocation,
654 tlb: Tlb,
655 ) -> Result<(u64, u64), PciError> {
656 match index {
657 PossibleTlbAllocation::Allocation(tlb_allocation) => {
658 let config = ioctl::NocTlbConfig {
659 addr: tlb.local_offset & !(tlb_allocation.size - 1),
660 x_end: tlb.x_end as u16,
661 y_end: tlb.y_end as u16,
662 x_start: tlb.x_start as u16,
663 y_start: tlb.y_start as u16,
664 noc: tlb.noc_sel,
665 mcast: tlb.mcast as u8,
666 ordering: tlb.ordering.into(),
667 linked: tlb.linked as u8,
668 static_vc: tlb.static_vc,
669 ..Default::default()
670 };
671 self.configure_tlb(tlb_allocation, config)?;
672
673 let tlb_offset = tlb.local_offset & (tlb_allocation.size - 1);
676
677 Ok((
678 tlb_offset,
679 tlb_allocation.size - tlb_offset,
681 ))
682 }
683 PossibleTlbAllocation::Hardcoded(index) => tlb::setup_tlb(self, *index, tlb),
684 PossibleTlbAllocation::NoAllocation => {
685 todo!("Need all fallback implementation if a tlb was not otherwise selected")
686 }
687 }
688 }
689
690 pub fn get_tlb(&self, index: &PossibleTlbAllocation) -> Result<Tlb, PciError> {
691 let index = match index {
692 PossibleTlbAllocation::Allocation(tlb_allocation) => tlb_allocation.id,
693 PossibleTlbAllocation::Hardcoded(index) => *index,
694 PossibleTlbAllocation::NoAllocation => {
695 todo!("Need all fallback implementation if a tlb was not otherwise selected")
696 }
697 };
698
699 tlb::get_tlb(self, index)
700 }
701
702 pub fn noc_write(
703 &mut self,
704 index: &PossibleTlbAllocation,
705 mut tlb: Tlb,
706 data: &[u8],
707 ) -> Result<(), PciError> {
708 let mut written = 0;
709 let addr = tlb.local_offset;
710 while written < data.len() {
711 let (offset, size) = self.setup_tlb(index, tlb.clone())?;
712
713 let remaining_data = &data[written..];
714 let chunk = &remaining_data[..(size as usize).min(remaining_data.len())];
715
716 match index {
717 PossibleTlbAllocation::Allocation(tlb_allocation) => unsafe {
718 Self::memcpy_to_device(
719 (tlb_allocation.uc_mapping.as_ptr() as *mut u8).byte_add(offset as usize),
720 chunk,
721 );
722 },
723 PossibleTlbAllocation::Hardcoded(_index) => {
724 self.write_block(offset as u32, chunk)?;
725 }
726 PossibleTlbAllocation::NoAllocation => todo!(),
727 }
728
729 written += chunk.len();
730
731 tlb.local_offset = addr + written as u64;
732 }
733
734 Ok(())
735 }
736
737 pub fn noc_read(
738 &mut self,
739 index: &PossibleTlbAllocation,
740 mut tlb: Tlb,
741 data: &mut [u8],
742 ) -> Result<(), PciError> {
743 let mut read = 0;
744 let addr = tlb.local_offset;
745 while read < data.len() {
746 let (offset, size) = self.setup_tlb(index, tlb.clone())?;
747
748 let remaining_buffer = &mut data[read..];
749 let chunk_len = (size as usize).min(remaining_buffer.len());
750 let chunk = &mut remaining_buffer[..chunk_len];
751
752 match index {
753 PossibleTlbAllocation::Allocation(tlb_allocation) => unsafe {
754 Self::memcpy_from_device(
755 chunk,
756 (tlb_allocation.uc_mapping.as_ptr() as *mut u8).byte_add(offset as usize),
757 );
758 },
759 PossibleTlbAllocation::Hardcoded(_index) => {
760 self.read_block(offset as u32, chunk)?;
761 }
762 PossibleTlbAllocation::NoAllocation => todo!(),
763 }
764
765 read += chunk.len();
766
767 tlb.local_offset = addr + read as u64;
768 }
769
770 Ok(())
771 }
772
773 pub fn noc_write32(
774 &mut self,
775 tlb_index: &PossibleTlbAllocation,
776 tlb: Tlb,
777 data: u32,
778 ) -> Result<(), PciError> {
779 let (offset, size) = self.setup_tlb(tlb_index, tlb)?;
780 assert!(
781 size >= 4,
782 "We have hit the the unlikely case of size being less than 4 bytes; this should not be possible"
783 );
784 match tlb_index {
785 PossibleTlbAllocation::Allocation(tlb_allocation) => self.write32_no_translation(
786 unsafe {
787 (tlb_allocation.uc_mapping.as_ptr() as *mut u8).byte_add(offset as usize)
788 as usize
789 },
790 data,
791 ),
792 PossibleTlbAllocation::Hardcoded(_) => self.write32(offset as u32, data),
793 PossibleTlbAllocation::NoAllocation => todo!(),
794 }
795 }
796
797 pub fn noc_read32(
798 &mut self,
799 tlb_index: &PossibleTlbAllocation,
800 tlb: Tlb,
801 ) -> Result<u32, PciError> {
802 let (offset, size) = self.setup_tlb(tlb_index, tlb)?;
803 assert!(
804 size >= 4,
805 "We have hit the the unlikely case of size being less than 4 bytes; this should not be possible"
806 );
807 match tlb_index {
808 PossibleTlbAllocation::Allocation(tlb_allocation) => {
809 self.read32_no_translation(unsafe {
810 (tlb_allocation.uc_mapping.as_ptr() as *mut u8).byte_add(offset as usize)
811 as usize
812 })
813 }
814 PossibleTlbAllocation::Hardcoded(_) => self.read32(offset as u32),
815 PossibleTlbAllocation::NoAllocation => todo!(),
816 }
817 }
818}
819
820#[derive(Debug, Default, PartialEq)]
821pub struct DriverVersion {
822 pub major: u32,
823 pub minor: u32,
824 pub patch: u32,
825 pub quirk: Option<String>,
826}
827
828impl PartialOrd for DriverVersion {
829 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
830 match self.major.cmp(&other.major) {
831 std::cmp::Ordering::Equal => match self.minor.cmp(&other.minor) {
832 std::cmp::Ordering::Equal => match self.patch.cmp(&other.patch) {
833 std::cmp::Ordering::Equal => {
834 if self.quirk == other.quirk {
835 Some(std::cmp::Ordering::Equal)
836 } else {
837 None
838 }
839 }
840 other => Some(other),
841 },
842 other => Some(other),
843 },
844 other => Some(other),
845 }
846 }
847}
848
849fn driver_version_parse(version: &str) -> DriverVersion {
850 let mut driver_version = DriverVersion::default();
851 let version = version.trim();
852
853 let version = if let Some((a, b)) = version.split_once('-') {
854 driver_version.quirk = Some(b.to_string());
855
856 a
857 } else {
858 version
859 };
860
861 let mut it = version.splitn(3, '.');
862 if let Some(v) = it.next() {
863 if let Ok(v) = v.parse() {
864 driver_version.major = v;
865 }
866 }
867 if let Some(v) = it.next() {
868 if let Ok(v) = v.parse() {
869 driver_version.minor = v;
870 }
871 }
872 if let Some(v) = it.next() {
873 if let Ok(v) = v.parse() {
874 driver_version.patch = v;
875 }
876 }
877
878 driver_version
879}
880
881pub fn get_version() -> Option<DriverVersion> {
882 std::fs::read_to_string("/sys/module/tenstorrent/version")
883 .ok()
884 .map(|version| driver_version_parse(&version))
885}
886#[cfg(test)]
887mod test {
888 use super::*;
889
890 #[test]
891 fn test_get_version() {
892 assert_eq!(
893 DriverVersion::default(),
894 DriverVersion {
895 major: 0,
896 minor: 0,
897 patch: 0,
898 quirk: None
899 },
900 "default driver version did not match expected values"
901 );
902 assert_eq!(
903 driver_version_parse("1"),
904 DriverVersion {
905 major: 1,
906 ..Default::default()
907 }
908 );
909 assert_eq!(
910 driver_version_parse("1.33"),
911 DriverVersion {
912 major: 1,
913 minor: 33,
914 ..Default::default()
915 }
916 );
917 assert_eq!(
918 driver_version_parse("1.33.5"),
919 DriverVersion {
920 major: 1,
921 minor: 33,
922 patch: 5,
923 ..Default::default()
924 }
925 );
926 assert_eq!(
927 driver_version_parse("1.33.5-quirk"),
928 DriverVersion {
929 major: 1,
930 minor: 33,
931 patch: 5,
932 quirk: Some("quirk".to_string())
933 }
934 );
935 assert_eq!(
936 driver_version_parse("1.33-quirk"),
937 DriverVersion {
938 major: 1,
939 minor: 33,
940 quirk: Some("quirk".to_string()),
941 ..Default::default()
942 }
943 );
944 assert_eq!(
945 driver_version_parse("1-quirk"),
946 DriverVersion {
947 major: 1,
948 quirk: Some("quirk".to_string()),
949 ..Default::default()
950 }
951 );
952 assert_eq!(driver_version_parse("bad"), DriverVersion::default());
953 }
954
955 fn verify_noc(device: &mut PciDevice, tlb: PossibleTlbAllocation) {
956 let node_info = match device.arch {
957 Arch::Grayskull => {
958 unimplemented!("Not currently supporting GS for this test\nTo support readback the noc node id from ARC");
959 }
960 Arch::Wormhole => (0, 10, 0xFFFB2002C),
961 Arch::Blackhole => (8, 0, 0x0000000080050044),
962 };
963
964 let node_id = device
965 .noc_read32(
966 &tlb,
967 Tlb {
968 x_end: node_info.0,
969 y_end: node_info.1,
970 noc_sel: 0,
971
972 local_offset: node_info.2,
973
974 ..Default::default()
975 },
976 )
977 .unwrap();
978 let x = node_id & 0x3f;
979 let y = (node_id >> 6) & 0x3f;
980
981 assert!(
982 x == node_info.0 as u32 && y == node_info.1 as u32,
983 "ARC node id didn't match expected ({x}, {y}) != ({}, {})",
984 node_info.0,
985 node_info.1
986 );
987 }
988
989 #[test]
990 #[cfg_attr(
991 any(not(feature = "test_hardware"), feature = "test_grayskull"),
992 ignore = "Requires hardware"
993 )]
994 fn ttkmd_allocate() {
995 let mut device = PciDevice::scan()
996 .into_iter()
997 .map(PciDevice::open)
998 .next()
999 .expect("Expected to have access to 1 pci device")
1000 .unwrap();
1001
1002 let tlb = device.allocate_tlb(1 << 20).unwrap();
1003 verify_noc(&mut device, PossibleTlbAllocation::Allocation(tlb));
1004 }
1005
1006 #[test]
1007 #[cfg_attr(
1008 any(not(feature = "test_hardware"), feature = "test_grayskull"),
1009 ignore = "Requires hardware"
1010 )]
1011 fn ttkmd_no_allocate() {
1012 let mut device = PciDevice::scan()
1013 .into_iter()
1014 .map(PciDevice::open)
1015 .next()
1016 .expect("Expected to have access to 1 pci device")
1017 .unwrap();
1018
1019 verify_noc(&mut device, PossibleTlbAllocation::Hardcoded(1));
1020 }
1021}