1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use crate::processor::elements::registers::*;
use super::capability::*;
use crate::processor::elements::cheri::CheriRVFuncs;

/// Register file for 64-bit RISC-V that can hold tagged 128-bit capabilities.
/// 
/// Implements [RegisterFile<SafeTaggedCap>] for capability-mode access, [RegisterFile<u64>] for integer-mode.
pub struct CheriRV64RegisterFile {
    regs: [SafeTaggedCap; 31],
}
impl CheriRV64RegisterFile {
    pub fn read_u64(&mut self, idx: u8) -> Result<u64, RegisterFileError> {
        <Self as RegisterFile<u64>>::read(self, idx)
    }
    pub fn write_u64(&mut self, idx: u8, val: u64) -> Result<(), RegisterFileError> {
        <Self as RegisterFile<u64>>::write(self, idx, val)
    }
    /// Reads a valid capability from the register file.
    pub fn read_maybe_cap(&mut self, idx: u8) -> Result<SafeTaggedCap, RegisterFileError> {
        <Self as RegisterFile<SafeTaggedCap>>::read(self, idx)
    }
    pub fn write_maybe_cap(&mut self, idx: u8, val: SafeTaggedCap) -> Result<(), RegisterFileError> {
        <Self as RegisterFile<SafeTaggedCap>>::write(self, idx, val)
    }

    pub fn dump(&self) {
        const REGISTER_NAMES: [&str; 32] = [
            "zero", "ra", "sp", "gp",
            "tp", "t0", "t1", "t2",
            "fp", "s1", "a0", "a1",
            "a2", "a3", "a4", "a5",
            "a6", "a7", "s2", "s3",
            "s4", "s5", "s6", "s7",
            "s8", "s9", "s10", "s11",
            "t3", "t4", "t5", "t6"
        ];

        println!("x{} = {} = 0x{:016x}", 0, REGISTER_NAMES[0], 0);
        for i in 1..32 {
            match self.regs[i - 1] {
                SafeTaggedCap::RawData{ top, bot } => {
                    println!("x{} = {} = 0x{:08x}{:08x}", i, REGISTER_NAMES[i], top, bot);
                }
                SafeTaggedCap::ValidCap(cap) => {
                    println!("x{} = {} = {:x?}", i, REGISTER_NAMES[i], cap);
                }
            };
        }
    }
    pub fn reset(&mut self) {
        // TR-951 $3.6.1 
        // If the arch-specific approach is to extend existing integer registers
        // to also hold tagged capabilties, can instead init to hold untagged values:
        // Unset Tag bit, offset=0, base=0, length=2^XLEN(?), otype=2^(XLEN)-1(??)
        // Right now, initing them to 0
        self.regs = [SafeTaggedCap::RawData{top: 0, bot: 0}; 31];
    }
}
impl RegisterFile<SafeTaggedCap> for CheriRV64RegisterFile {
    fn read(&mut self, idx: u8) -> Result<SafeTaggedCap, RegisterFileError> {
        let val = match idx {
            0    => Ok(SafeTaggedCap::RawData{top: 0, bot: 0}),
            1..=31 => Ok(self.regs[(idx - 1) as usize]),
            _ => Err(RegisterFileError::InvalidIndex(idx))
        }?;

        Ok(val)
    }
    fn write(&mut self, idx: u8, val: SafeTaggedCap) -> Result<(), RegisterFileError> {
        match idx {
            0    => Ok(()),
            1..=31 => {
                self.regs[(idx - 1) as usize] = val;
                Ok(())
            },
            _ => Err(RegisterFileError::InvalidIndex(idx))
        }?;

        Ok(())
    }
}
/// Interface used by normal RV32 instructions
impl RegisterFile<u64> for CheriRV64RegisterFile {
    fn read(&mut self, idx: u8) -> Result<u64, RegisterFileError> {
        let val = match idx {
            0    => Ok(0),
            1..=31 => match self.regs[(idx - 1) as usize] {
                // Return just the bottom part of raw data - the top is capability metadata
                SafeTaggedCap::RawData{top: _, bot} => Ok(bot),
                SafeTaggedCap::ValidCap(cap) => Ok(cap.address())
            },
            _ => Err(RegisterFileError::InvalidIndex(idx))
        }?;

        Ok(val as u64)
    }
    fn write(&mut self, idx: u8, val: u64) -> Result<(), RegisterFileError> {
        // In integer mode, assume writes zero out the top bit and remove tag
        // TR-951$5.3.6 states "the upper XLEN bits and tag bit [...] will be ignored"
        let val = SafeTaggedCap::RawData{top: 0, bot: val};
        match idx {
            0    => Ok(()),
            1..=31 => {
                self.regs[(idx - 1) as usize] = val;
                Ok(())
            },
            _ => Err(RegisterFileError::InvalidIndex(idx))
        }?;

        Ok(())
    }
}
impl Default for CheriRV64RegisterFile {
    fn default() -> Self {
        CheriRV64RegisterFile {
            regs: [SafeTaggedCap::RawData{ top: 0, bot: 0 }; 31],
        }
    }
}

pub struct IntegerModeCheriRV64RegisterFile<'a> {
    base_reg: &'a mut CheriRV64RegisterFile,
    base_cap: Cc128Cap
}
impl<'a> RegisterFile<u64> for IntegerModeCheriRV64RegisterFile<'a> {
    fn read(&mut self, idx: u8) -> Result<u64, RegisterFileError> {
        self.base_reg.read_u64(idx)
    }
    fn write(&mut self, idx: u8, val: u64) -> Result<(), RegisterFileError> {
        self.base_reg.write_u64(idx, val)
    }
}
impl<'a> IntegerModeCheriRV64RegisterFile<'a> {
    pub fn read_ddc_offset_cap(&mut self, idx: u8) -> anyhow::Result<Cc128Cap> {
        let addr = self.read(idx)?;
        let cap = self.base_cap.clone();
        let (success, new_cap) = Cc128::setCapOffset(&cap, addr);
        if !success {
            bail!("Setting offset of DDC failed");
        }
        Ok(new_cap)
    }
    pub fn wrap(base_reg: &'a mut CheriRV64RegisterFile, base_cap: Cc128Cap) -> Self {
        IntegerModeCheriRV64RegisterFile {
            base_reg,
            base_cap
        }
    }
}