ttkmd_if/
lib.rs

1// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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    /// Address in CSM where the DMA request structure resides
47    pub csm_pcie_ctrl_dma_request_offset: u32,
48
49    /// To trigger ARC interrupt
50    pub arc_misc_cntl_addr: u32,
51
52    /// DMA host phys addr high
53    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, // Registers >= this are system regs, use the mapping.
94    pub system_reg_offset_adjust: u32, // This is the offset of the first reg in the system reg mapping.
95}
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                    // println!("WARNING: recieved unused mapping id");
225                }
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        // if disable_wc
246        // bar0_wc_mapping = 0;
247        // bar0_wc_size = 0;
248        // else
249        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            // No WC mapping, map the entire BAR UC.
281            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 bar0_wc = if let Some(bar0_wc) = bar0_wc {
299        //     bar0_wc
300        // } else {
301        //     bar0_uc
302        // };
303
304        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; // The definition of PCI_SLOT from include/uapi/linux/pci.h
415        let pci_function = (device_info.output.bus_dev_fn) & 0x7; // The definition of PCI_FUNC from include/uapi/linux/pci.h
416        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        // To avoid needing a warmup when performing the first dma access, try to allocate the
471        // buffers now.
472        device.allocate_transfer_buffers();
473
474        // If/when the raw bar mapping is removed this will start failing
475        device.map_bar()?;
476
477        Ok(device)
478    }
479
480    pub fn allocate_transfer_buffers(&mut self) -> bool {
481        // Try to allocate the transfer buffer first, if this fails then there is no point in
482        // allocating the completion flag.
483        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 we didn't get the transfer buffer then there is no point in allocating the completion
493        // flag
494        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        // Explicitly unmap, otherwise the ioctl will return EBUSY
576        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                // The tlb addr selects the upper bits of the final noc addrress
674                // therefore the lower bits are the offset into the tlb itself, selected by the lower bits of our size.
675                let tlb_offset = tlb.local_offset & (tlb_allocation.size - 1);
676
677                Ok((
678                    tlb_offset,
679                    // The size must be shrunk by the offset into the tlb of our configured address
680                    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}