Skip to main content

tsffs/os/windows/
util.rs

1use std::mem::MaybeUninit;
2
3use anyhow::{anyhow, bail, Result};
4use simics::{get_interface, read_byte, Access, ConfObject, ProcessorInfoV2Interface};
5
6use super::paging::{
7    DIR_TABLE_BASE, PAGE_1GB_SHIFT, PAGE_2MB_SHIFT, PAGE_4KB_SHIFT, PDE, PDPTE, PDPTE_LARGE, PML4E,
8    PTE, VIRTUAL_ADDRESS,
9};
10
11/// Read from a virtual address
12pub fn read_virtual<T>(processor: *mut ConfObject, virtual_address: u64) -> Result<T>
13where
14    T: Sized,
15{
16    let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(processor)?;
17
18    let size = std::mem::size_of::<T>();
19
20    let mut t = MaybeUninit::<T>::uninit();
21
22    let memory = processor_info_v2.get_physical_memory()?;
23
24    let contents = (0..size)
25        .map(|i| {
26            processor_info_v2
27                .logical_to_physical(virtual_address + i as u64, Access::Sim_Access_Read)
28                .and_then(|b| read_byte(memory, b.address))
29                .map_err(|e| anyhow!("Failed to read memory: {}", e))
30        })
31        .collect::<Result<Vec<u8>>>()?;
32
33    unsafe {
34        std::ptr::copy_nonoverlapping(contents.as_ptr(), t.as_mut_ptr() as *mut u8, size);
35        Ok(t.assume_init())
36    }
37}
38
39/// Read from a physical address
40pub fn read_physical<T>(processor: *mut ConfObject, physical_address: u64) -> Result<T> {
41    let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(processor)?;
42
43    let size = std::mem::size_of::<T>();
44
45    let mut t = MaybeUninit::<T>::uninit();
46
47    let memory = processor_info_v2.get_physical_memory()?;
48
49    let contents = (0..size)
50        .map(|i| {
51            read_byte(memory, physical_address + i as u64)
52                .map_err(|e| anyhow!("Failed to read memory: {}", e))
53        })
54        .collect::<Result<Vec<u8>>>()?;
55
56    unsafe {
57        std::ptr::copy_nonoverlapping(contents.as_ptr(), t.as_mut_ptr() as *mut u8, size);
58        Ok(t.assume_init())
59    }
60}
61
62/// Read from a virtual address with a specific directory table base
63pub fn read_virtual_dtb<T>(
64    processor: *mut ConfObject,
65    directory_table_base: u64,
66    virtual_address: u64,
67) -> Result<T> {
68    let physical_address = virtual_to_physical(processor, directory_table_base, virtual_address)?;
69    read_physical(processor, physical_address)
70}
71
72/// Translate a virtual to physical address using a directory table base
73pub fn virtual_to_physical(
74    processor: *mut ConfObject,
75    directory_table_base: u64,
76    virtual_address: u64,
77    // build: u32,
78) -> Result<u64> {
79    let virtual_address = VIRTUAL_ADDRESS {
80        All: virtual_address,
81    };
82    let dir_table_base = DIR_TABLE_BASE {
83        All: directory_table_base,
84    };
85    let pml4e = read_physical::<PML4E>(
86        processor,
87        (unsafe { dir_table_base.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT as u64)
88            + (unsafe { virtual_address.Bits }.Pml4Index() * 8),
89    )?;
90
91    if unsafe { pml4e.Bits }.Present() == 0 {
92        bail!("PML4E not present");
93    }
94
95    let pdpte = read_physical::<PDPTE>(
96        processor,
97        (unsafe { pml4e.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT)
98            + (unsafe { virtual_address.Bits }.PdptIndex() * 8),
99    )?;
100
101    if unsafe { pdpte.Bits }.Present() == 0 {
102        bail!("PDPTE not present");
103    }
104
105    if (unsafe { pdpte.All } >> 7) & 1 != 0 {
106        let pdpte_large = PDPTE_LARGE {
107            All: unsafe { pdpte.All },
108        };
109        return Ok(
110            ((unsafe { pdpte_large.Bits }.PhysicalAddress()) << PAGE_1GB_SHIFT)
111                + (unsafe { virtual_address.All } & (!(u64::MAX << PAGE_1GB_SHIFT))),
112        );
113    }
114
115    let pde = read_physical::<PDE>(
116        processor,
117        (unsafe { pdpte.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT)
118            + (unsafe { virtual_address.Bits }.PdIndex() * 8),
119    )?;
120
121    if unsafe { pde.Bits }.Present() == 0 {
122        bail!("PDE not present");
123    }
124
125    if (unsafe { pde.All } >> 7) & 1 != 0 {
126        let pde_large = PDPTE_LARGE {
127            All: unsafe { pde.All },
128        };
129        return Ok(
130            ((unsafe { pde_large.Bits }.PhysicalAddress()) << PAGE_2MB_SHIFT)
131                + (unsafe { virtual_address.All } & (!(u64::MAX << PAGE_2MB_SHIFT))),
132        );
133    }
134
135    let pte = read_physical::<PTE>(
136        processor,
137        (unsafe { pde.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT)
138            + (unsafe { virtual_address.Bits }.PtIndex() * 8),
139    )?;
140
141    if unsafe { pte.Bits }.Present() == 0 {
142        bail!("PTE not present");
143    }
144
145    Ok((unsafe { pte.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT)
146        + unsafe { virtual_address.Bits }.PageIndex())
147}
148
149pub fn read_unicode_string(
150    processor: *mut ConfObject,
151    length: usize,
152    buffer: *const u16,
153) -> Result<String> {
154    if length == 0 || buffer.is_null() {
155        return Ok(String::new());
156    }
157
158    let mut string = Vec::new();
159    let mut address = buffer as u64;
160
161    for _ in 0..length {
162        let character = read_virtual::<u16>(processor, address)?;
163
164        if character == 0 {
165            break;
166        }
167
168        string.push(character);
169        address += 2;
170    }
171
172    String::from_utf16(&string).map_err(|e| anyhow!("Failed to convert string: {}", e))
173}
174
175pub fn read_unicode_string_dtb(
176    processor: *mut ConfObject,
177    length: usize,
178    buffer: *const u16,
179    directory_table_base: u64,
180) -> Result<String> {
181    if length == 0 || buffer.is_null() {
182        return Ok(String::new());
183    }
184    let mut string = Vec::new();
185    let mut address = buffer as u64;
186
187    for _ in 0..length {
188        let character = read_virtual_dtb::<u16>(processor, directory_table_base, address)?;
189
190        if character == 0 {
191            break;
192        }
193
194        string.push(character);
195        address += 2;
196    }
197
198    String::from_utf16(&string).map_err(|e| anyhow!("Failed to convert string: {}", e))
199}
200
201pub fn read_nul_terminated_string(processor: *mut ConfObject, address: u64) -> Result<String> {
202    if address == 0 {
203        return Ok(String::new());
204    }
205    let mut string = String::new();
206    let mut address = address;
207
208    loop {
209        let character = read_virtual::<u8>(processor, address)?;
210
211        if character == 0 {
212            break;
213        }
214
215        string.push(character as char);
216        address += 1;
217    }
218
219    Ok(string)
220}
221
222pub fn read_nul_terminated_string_dtb(
223    processor: *mut ConfObject,
224    address: u64,
225    directory_table_base: u64,
226) -> Result<String> {
227    if address == 0 {
228        return Ok(String::new());
229    }
230    let mut string = String::new();
231    let mut address = address;
232
233    loop {
234        let character = read_virtual_dtb::<u8>(processor, directory_table_base, address)?;
235
236        if character == 0 {
237            break;
238        }
239
240        string.push(character as char);
241        address += 1;
242    }
243
244    Ok(string)
245}