use crate::models::CheriExecMode;
use crate::processor::exceptions::IllegalInstructionException::UnsupportedParam;
use crate::processor::isa_mods::*;
use crate::processor::exceptions::{CapabilityException,CapOrRegister};
use crate::processor::elements::cheri::{Cc128,CompressedCapability,Cc128Cap,CheriMemory,CheriAggregateMemory,CheriRVFuncs,SafeTaggedCap};
use crate::processor::elements::cheri::CheriRV64RegisterFile;
use crate::processor::elements::registers::RegisterFile;
pub struct XCheri64Conn<'a> {
pub pcc: Cc128Cap,
pub sreg: &'a mut CheriRV64RegisterFile,
pub memory: &'a mut CheriAggregateMemory,
pub mode: CheriExecMode,
pub ddc: Cc128Cap,
}
pub struct XCheri64 {}
impl XCheri64 {
fn handle_cjalr(&mut self, cd: u8, cs1: u8, offset: u64, conn: XCheri64Conn) -> ProcessorResult<Cc128Cap> {
let cs1_reg = CapOrRegister::Reg(cs1);
let cs1_val = conn.sreg.read_maybe_cap(cs1)?;
match cs1_val {
SafeTaggedCap::RawData{..} => bail!(CapabilityException::TagViolation{ cap: cs1_reg }),
SafeTaggedCap::ValidCap(cs1_val) => {
if cs1_val.is_sealed() && cs1_val.otype() != Cc128::OTYPE_SENTRY {
bail!(CapabilityException::SealViolation{ cap: cs1_reg });
}
let next_pc = conn.pcc.address() + 4;
let (success, link_cap) = Cc128::setCapAddr(&conn.pcc, next_pc);
assert!(success, "Link cap should always be representable.");
assert!(!link_cap.is_sealed(), "Link cap should always be unsealed.");
let link_cap = Cc128::sealCap(&link_cap, Cc128::OTYPE_SENTRY);
assert!(link_cap.tag());
conn.sreg.write_maybe_cap(cd, SafeTaggedCap::from_cap(link_cap))?;
let mut new_pc = cs1_val.address() & (u64::MAX << 1);
new_pc = new_pc.wrapping_add(offset);
let mut new_pcc = Cc128::unsealCap(&cs1_val);
new_pcc.set_address_unchecked(new_pc);
Ok(new_pcc)
}
}
}
}
impl IsaMod<XCheri64Conn<'_>> for XCheri64 {
type Pc = Cc128Cap;
fn will_handle(&self, opcode: Opcode, inst: InstructionBits) -> bool {
use crate::processor::decode::Opcode::*;
match (opcode, inst) {
(Custom2CHERI, _) => true,
(MiscMem, InstructionBits::IType{funct3: 0x2, ..}) => true,
(Store, InstructionBits::SType{funct3: 0x4, ..}) => true,
_ => false
}
}
fn execute(&mut self, opcode: Opcode, inst: InstructionBits, _inst_bits: u32, conn: XCheri64Conn) -> ProcessorResult<Option<Self::Pc>> {
use crate::processor::decode::Opcode::*;
match (opcode, inst) {
(MiscMem, InstructionBits::IType{rd, funct3: 0x2, rs1, imm}) => {
let origin = match conn.mode {
CheriExecMode::Capability => {
let offset = imm.sign_extend_u64();
let mut cap = conn.sreg.read_maybe_cap(rs1)?.to_cap();
cap.set_address_unchecked(cap.address().wrapping_add(offset));
cap
}
CheriExecMode::Integer => {
let reg_rs1 = conn.sreg.read_u64(rs1)?;
let offset = imm.sign_extend_u64().wrapping_add(reg_rs1);
let mut cap = conn.ddc.clone();
cap.set_address_unchecked(cap.address().wrapping_add(offset));
cap
}
};
let loaded_cap = conn.memory.load_maybe_cap(origin)?;
conn.sreg.write_maybe_cap(rd, loaded_cap)?;
},
(Store, InstructionBits::SType{funct3: 0x4, rs1, rs2, imm}) => {
let destination = match conn.mode {
CheriExecMode::Capability => {
let offset = imm.sign_extend_u64();
let mut cap = conn.sreg.read_maybe_cap(rs1)?.to_cap();
cap.set_address_unchecked(cap.address().wrapping_add(offset));
cap
}
CheriExecMode::Integer => {
let reg_rs1 = conn.sreg.read_u64(rs1)?;
let offset = imm.sign_extend_u64().wrapping_add(reg_rs1);
let mut cap = conn.ddc.clone();
cap.set_address_unchecked(cap.address().wrapping_add(offset));
cap
}
};
let cap_to_store = conn.sreg.read_maybe_cap(rs2)?;
conn.memory.store_maybe_cap(destination, cap_to_store)?;
},
(Custom2CHERI, InstructionBits::ROrIType{rd, funct3, rs1, rs2, funct7, imm}) => {
match (funct7, funct3) {
(0x1, 0x0) => {
bail!("Haven't implemented CSpecialRW")
}
(0x8, 0x0) => {
let cs1_reg = CapOrRegister::Reg(rs1);
let cs1_val = conn.sreg.read_maybe_cap(rs1)?.to_cap();
let rs2_val = conn.sreg.read_u64(rs2)?;
let new_base = cs1_val.address();
let new_top = new_base as u128 + rs2_val as u128;
if !cs1_val.tag() {
bail!(CapabilityException::TagViolation{ cap: cs1_reg });
} else if cs1_val.is_sealed() {
bail!(CapabilityException::SealViolation{ cap: cs1_reg });
} else if !Cc128::inCapBounds(&cs1_val, new_base, rs2_val as u128) {
bail!(CapabilityException::LengthViolation{ cap: cs1_reg, base: new_base, top: new_top });
}
let (_, new_cap) = Cc128::setCapBounds(&cs1_val, new_base, new_top);
conn.sreg.write_maybe_cap(rd, SafeTaggedCap::from_cap(new_cap))?;
}
(0x9, 0x0) => {
let cs1_reg = CapOrRegister::Reg(rs1);
let cs1_val = conn.sreg.read_maybe_cap(rs1)?.to_cap();
let rs2_val = conn.sreg.read_u64(rs2)?;
let new_base = cs1_val.address();
let new_top = new_base as u128 + rs2_val as u128;
if !cs1_val.tag() {
bail!(CapabilityException::TagViolation{ cap: cs1_reg });
} else if cs1_val.is_sealed() {
bail!(CapabilityException::SealViolation{ cap: cs1_reg });
} else if !Cc128::inCapBounds(&cs1_val, new_base, rs2_val as u128) {
bail!(CapabilityException::LengthViolation{ cap: cs1_reg, base: new_base, top: new_top });
}
let (exact, new_cap) = Cc128::setCapBounds(&cs1_val, new_base, new_top);
if !exact {
bail!(CapabilityException::InexactBounds{ cap: cs1_reg });
}
conn.sreg.write_maybe_cap(rd, SafeTaggedCap::from_cap(new_cap))?;
}
(_, 0x2) => {
let cs1_reg = CapOrRegister::Reg(rs1);
let cs1_val = conn.sreg.read_maybe_cap(rs1)?.to_cap();
let imm_val = imm.no_extend_u64();
let new_base = cs1_val.address();
let new_top = new_base as u128 + imm_val as u128;
if !cs1_val.tag() {
bail!(CapabilityException::TagViolation{ cap: cs1_reg });
} else if cs1_val.is_sealed() {
bail!(CapabilityException::SealViolation{ cap: cs1_reg });
} else if !Cc128::inCapBounds(&cs1_val, new_base, imm_val as u128) {
bail!(CapabilityException::LengthViolation{ cap: cs1_reg, base: new_base, top: new_top });
}
let (_, new_cap) = Cc128::setCapBounds(&cs1_val, new_base, new_top);
conn.sreg.write_maybe_cap(rd, SafeTaggedCap::from_cap(new_cap))?;
}
(0xb, 0x0) => {
bail!("Haven't implemented CSeal")
}
(0xc, 0x0) => {
bail!("Haven't implemented CUnseal")
}
(0xd, 0x0) => {
bail!("Haven't implemented CAndPerm")
}
(0xe, 0x0) => {
bail!("Haven't implemented CSetFlags")
}
(0xf, 0x0) => {
bail!("Haven't implemented CSetOffset")
}
(0x10, 0x0) => {
bail!("Haven't implemented CSetAddr")
}
(0x11, 0x0) => {
let cs1_val = conn.sreg.read_maybe_cap(rs1)?.to_cap();
let rs2_val = conn.sreg.read_u64(rs2)?;
if cs1_val.tag() && cs1_val.is_sealed() {
bail!(CapabilityException::SealViolation{ cap: CapOrRegister::Reg(rs1) })
} else {
let (success, mut new_cap) = Cc128::incCapOffset(&cs1_val, rs2_val);
if !success {
new_cap = Cc128::invalidateCap(&new_cap);
}
conn.sreg.write_maybe_cap(rd, SafeTaggedCap::from_cap(new_cap))?;
}
}
(_, 0x1) => {
let cs1_val = conn.sreg.read_maybe_cap(rs1)?.to_cap();
if cs1_val.tag() && cs1_val.is_sealed() {
bail!(CapabilityException::SealViolation{ cap: CapOrRegister::Reg(rs1) })
} else {
let (success, mut new_cap) = Cc128::incCapOffset(&cs1_val, imm.sign_extend_u64());
if !success {
new_cap = Cc128::invalidateCap(&new_cap);
}
conn.sreg.write_maybe_cap(rd, SafeTaggedCap::from_cap(new_cap))?;
}
}
(0x12, 0x0) => {
bail!("Haven't implemented CToPtr")
}
(0x13, 0x0) => {
let cs1_val = if rs1 == 0 {
conn.ddc
} else {
conn.sreg.read_maybe_cap(rs1)?.to_cap()
};
let rs2_val = conn.sreg.read_u64(rs2)?;
if rs2_val == 0 {
conn.sreg.write_u64(rd, 0)?;
} else if !cs1_val.tag() {
bail!(CapabilityException::TagViolation{ cap: CapOrRegister::Reg(rs1) })
} else if cs1_val.is_sealed() {
bail!(CapabilityException::SealViolation{ cap: CapOrRegister::Reg(rs1) })
} else {
let (success, mut new_cap) = Cc128::setCapOffset(&cs1_val, rs2_val);
if !success {
new_cap = Cc128::invalidateCap(&new_cap);
}
conn.sreg.write_maybe_cap(rd, SafeTaggedCap::from_cap(new_cap))?;
}
}
(0x14, 0x0) => {
bail!("Haven't implemented CSub")
}
(0x1d, 0x0) => {
bail!("Haven't implemented CBuildCap")
}
(0x1e, 0x0) => {
bail!("Haven't implemented CCopyType")
}
(0x1f, 0x0) => {
bail!("Haven't implemented CCSeal")
}
(0x20, 0x0) => {
bail!("Haven't implemented CestSubset")
}
(0x21, 0x0) => {
bail!("Haven't implemented CSetEqualExact")
}
(0x7e, 0x0) => match rd {
0x1 => {
bail!("Haven't implemented CInvoke")
}
0x1f => {
bail!("Haven't implemented CClearTags")
}
_ => bail!("Invalid funct3/funct7/rd combination {:x}/{:x}/{:x}", funct3, funct7, rd)
}
(0x7f, 0x0) => match rs2 {
0x0 => {
bail!("Haven't implemented CGetPerm")
}
0x1 => {
bail!("Haven't implemented CGetType")
}
0x2 => {
bail!("Haven't implemented CGetBase")
}
0x3 => {
bail!("Haven't implemented CGetLen")
}
0x4 => {
let cs1_val = conn.sreg.read_maybe_cap(rs1)?.to_cap();
conn.sreg.write_u64(
rd,
if cs1_val.tag() {
1
} else {
0
}
)?;
}
0x5 => {
bail!("Haven't implemented CGetSealed")
}
0x6 => {
bail!("Haven't implemented CGetOffset")
}
0x7 => {
bail!("Haven't implemented CGetFlags")
}
0x8 => {
bail!("Haven't implemented CRoundRepresentableLength")
}
0x9 => {
bail!("Haven't implemented CRepresentableAlignmentMask")
}
0xa => {
let val = conn.sreg.read_maybe_cap(rs1)?;
conn.sreg.write_maybe_cap(rd, val)?;
}
0xb => {
bail!("Haven't implemented CClearTag")
}
0xc => {
return Ok(Some(self.handle_cjalr(rd, rs1, 0, conn)?))
}
0xd => {
bail!("Haven't implemented Clear")
}
0xe => {
bail!("Haven't implemented CClear")
}
0xf => {
bail!("Haven't implemented CGetAddr")
}
0x10 => {
bail!("FPClear called when floating-point not supported")
}
0x11 => {
bail!("Haven't implemented CSealEntry")
}
0x12 => {
bail!("Haven't implemented CLoadTags")
}
0x14 => {
bail!("Haven't implemented CJALR relative to PCC")
}
_ => bail!("Invalid rs2 value {:x} for CHERI funct3=0x0,funct7=0x7f", rs2)
}
(0x7d, 0) => match rs2 {
0xb => {
let cs1_val = conn.sreg.read_maybe_cap(rs1)?.to_cap();
let loaded_cap = conn.memory.load_maybe_cap(cs1_val)?;
conn.sreg.write_maybe_cap(rd, loaded_cap)?;
}
_ => bail!("Unhandled rs2 value {:x} for CHERI load (funct7=0x7d, funct3=0)", rs2)
}
(0x7c, 0) => match rd {
0xb => {
let cs1_val = conn.sreg.read_maybe_cap(rs1)?.to_cap();
let stored_val = conn.sreg.read_u64(rs2)?;
conn.memory.store_u64(cs1_val, stored_val)?;
}
_ => bail!("Unhandled rd value {:x} for CHERI store (funct7=0x7c, funct3=0)", rd)
}
_ => bail!("Invalid funct3/funct7 combination {:x} {:x}", funct3, funct7)
}
}
_ => bail!("Invalid opcode/instruction pair passed to XCheri64")
}
Ok(None)
}
}
pub struct Rv64imCapabilityMode {}
impl IsaMod<XCheri64Conn<'_>> for Rv64imCapabilityMode {
type Pc = u64;
fn will_handle(&self, opcode: Opcode, _inst: InstructionBits) -> bool {
use crate::processor::decode::Opcode::*;
match opcode {
Load | Store | AddUpperImmPC => true,
_ => false
}
}
fn execute(&mut self, opcode: Opcode, inst: InstructionBits, _inst_bits: u32, conn: XCheri64Conn) -> ProcessorResult<Option<Self::Pc>> {
use crate::processor::decode::Opcode::*;
match (opcode, inst) {
(AddUpperImmPC, InstructionBits::UType{rd, imm}) => {
let addr = conn.pcc.address().wrapping_add(imm.sign_extend_u64());
let (representable, mut new_cap) = Cc128::setCapAddr(&conn.pcc, addr);
if !representable {
new_cap = Cc128::invalidateCap(&new_cap);
}
conn.sreg.write(rd, SafeTaggedCap::from_cap(new_cap))?;
}
(Load, InstructionBits::IType{rd, funct3, rs1, imm}) => {
let offset = imm.sign_extend_u64();
let mut cap = conn.sreg.read_maybe_cap(rs1)?.to_cap();
cap.set_address_unchecked(cap.address().wrapping_add(offset));
let new_val = match funct3 {
0b000 => (conn.memory.load_u8(cap)? as i8) as i64 as u64,
0b001 => (conn.memory.load_u16(cap)? as i16) as i64 as u64,
0b010 => (conn.memory.load_u32(cap)? as i32) as i64 as u64,
0b011 => conn.memory.load_u64(cap)? as u64,
0b100 => conn.memory.load_u8(cap)? as u64,
0b101 => conn.memory.load_u16(cap)? as u64,
0b110 => conn.memory.load_u32(cap)? as u64,
_ => bail!(UnsupportedParam(format!("Load funct3 {:03b}", funct3)))
};
conn.sreg.write(rd, new_val)?;
}
(Store, InstructionBits::SType{funct3, rs1, rs2, imm}) => {
let offset = imm.sign_extend_u64();
let mut cap = conn.sreg.read_maybe_cap(rs1)?.to_cap();
cap.set_address_unchecked(cap.address().wrapping_add(offset));
match funct3 {
0b000 => conn.memory.store_u8(cap, (conn.sreg.read_u64(rs2)? & 0xFF) as u8)?,
0b001 => conn.memory.store_u16(cap, (conn.sreg.read_u64(rs2)? & 0xFFFF) as u16)?,
0b010 => conn.memory.store_u32(cap, (conn.sreg.read_u64(rs2)? & 0xFFFF_FFFF) as u32)?,
0b011 => conn.memory.store_u64(cap, (conn.sreg.read_u64(rs2)? & 0xFFFF_FFFF_FFFF_FFFF) as u64)?,
_ => bail!(UnsupportedParam(format!("Store funct3 {:03b}", funct3)))
};
}
_ => bail!("Invalid opcode/instruction pair passed to RV32I")
}
Ok(None)
}
}