Hi, I'm writing an OS in rust and need to deal with the different address types, Physical, Virtual and Ports. Generally the physical and virtual addresses are fairly easy to handle with a generic read<T>
/ write<T>
function that cast the raw pointer to the correct type. IO ports on the other hand generally only accept i8
, i16
, i32
and i64
and you need to use specific instructions in
, out
instructions, and load up the respective CPU registers. An example of what I tried (and that shows this) is:
pub trait PortIO<T: RegisterType> {
fn read(&self) -> Result<T, ErrorCode>;
fn write(&self, value: T) -> Result<(), ErrorCode>;
}
#[cfg(target_arch = "x86_64")]
impl PortIO<i8> for Address {
fn read(&self) -> Result<i8, ErrorCode> {
let addr = self.value()?;
let mut value;
unsafe {
asm!(
"in al, dx",
in("rdx") addr,
out("al") value
)
}
Ok(value)
}
fn write(&self, value: i8) -> Result<(), ErrorCode> {
let addr = self.value()?;
Ok(unsafe {
asm!(
"out dx, al",
in("rdx") addr,
in("al") value,
)
})
}
}
Given the exact combinations of instructions for different data sizes, I have to implement specific i8
, i16
, i32
and i64
versions for Port-Mapped devices. This is where I am comming unstuck, I can't work out how to limit the available read() and write() functions to the address subject to the address type. I.e. my starting point was:
pub struct Address<T: RegisterType> {
pub fn read<T>(&self) -> Result<T, ErrorCode> {...}
pub fn write<T>(&self) -> Result<T, ErrorCode> {...}
}
Obviously I can't concurrently implement read<T>
and say read<i8>
etc. I tried a few things using the Factory design pattern, but that does expect a fixed interface, and even with a variable interface, still not sure it can be done with rust.
Does anyone have suggestions how to achieve this generic / transparent address design, or should I park this in the not-possible spot?