device_emulators/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Utilities for implementing device emulators.
5
6#![forbid(unsafe_code)]
7
8/// Performs a device register read as a series of 32-bit reads.
9pub fn read_as_u32_chunks<F, Num>(offset: Num, data: &mut [u8], mut read_u32: F)
10where
11    F: FnMut(Num) -> u32,
12    Num: Into<u64> + TryFrom<u64>,
13    <Num as TryFrom<u64>>::Error: std::fmt::Debug,
14{
15    let offset = offset.into();
16    let mut next_offset = offset;
17    let remaining_data = if offset & 3 != 0 {
18        let val = read_u32((offset & !3).try_into().unwrap());
19        let val = val.to_ne_bytes();
20        let u32_offset = (offset & 3) as usize;
21        let byte_count = std::cmp::min(4 - u32_offset, data.len());
22        data[..byte_count].copy_from_slice(&val[u32_offset..(byte_count + u32_offset)]);
23        next_offset += u64::try_from(byte_count).unwrap();
24        let (_, rem) = data.split_at_mut(byte_count);
25        rem
26    } else {
27        data
28    };
29
30    for next_chunk in remaining_data.chunks_exact_mut(4) {
31        let val = read_u32(next_offset.try_into().unwrap());
32        next_offset += 4;
33        next_chunk.copy_from_slice(&val.to_ne_bytes());
34    }
35    let extra_bytes = remaining_data.chunks_exact_mut(4).into_remainder();
36    if !extra_bytes.is_empty() {
37        let val = read_u32(next_offset.try_into().unwrap());
38        let val = val.to_ne_bytes();
39        extra_bytes.copy_from_slice(&val[..extra_bytes.len()]);
40    }
41}
42
43/// The request type for [`write_as_u32_chunks`].
44pub enum ReadWriteRequestType {
45    /// A read request.
46    Read,
47    /// A write request with the given value.
48    Write(u32),
49}
50
51/// Performs a device register write as a series of 32-bit reads and writes.
52///
53/// NOTE: We read u32 and then write back when we chunk.  Because of this, the borrow checker
54///       requires a single mutable closure that implements both read/write semantics.
55pub fn write_as_u32_chunks<F, Num>(offset: Num, data: &[u8], mut read_write_u32: F)
56where
57    F: FnMut(Num, ReadWriteRequestType) -> Option<u32>,
58    Num: Into<u64> + TryFrom<u64>,
59    <Num as TryFrom<u64>>::Error: std::fmt::Debug,
60{
61    let offset = offset.into();
62    let mut next_offset = offset;
63    let remaining_data = if next_offset & 3 != 0 {
64        let val = read_write_u32(
65            (next_offset & !3).try_into().unwrap(),
66            ReadWriteRequestType::Read,
67        )
68        .expect("Read for ReadWriteFn didn't return u32");
69        let mut val = val.to_ne_bytes();
70        let u32_offset = (next_offset & 3) as usize;
71        let byte_count = std::cmp::min(4 - u32_offset, data.len());
72        val[u32_offset..(byte_count + u32_offset)].copy_from_slice(&data[..byte_count]);
73        next_offset += u64::try_from(byte_count).unwrap();
74        read_write_u32(
75            (offset & !3).try_into().unwrap(),
76            ReadWriteRequestType::Write(u32::from_ne_bytes(val)),
77        );
78        let (_, rem) = data.split_at(byte_count);
79        rem
80    } else {
81        data
82    };
83    for next_chunk in remaining_data.chunks_exact(4) {
84        let val = u32::from_ne_bytes(
85            next_chunk
86                .try_into()
87                .expect("4 byte chunk should convert to u32"),
88        );
89        read_write_u32(
90            next_offset.try_into().unwrap(),
91            ReadWriteRequestType::Write(val),
92        );
93        next_offset += 4;
94    }
95    let extra_bytes = remaining_data.chunks_exact(4).remainder();
96    if !extra_bytes.is_empty() {
97        let val = read_write_u32(next_offset.try_into().unwrap(), ReadWriteRequestType::Read)
98            .expect("Read for ReadWriteFn didn't return u32");
99        let mut val = val.to_ne_bytes();
100        for (i, &extra_data) in extra_bytes.iter().enumerate() {
101            val[i] = extra_data;
102        }
103        let val = u32::from_ne_bytes(val);
104        read_write_u32(
105            next_offset.try_into().unwrap(),
106            ReadWriteRequestType::Write(val),
107        );
108    }
109}