use super::bpf_instr_info::{self, BpfInstrInfo, BpfOpcode};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BpfRegClass {
GPR,
FramePtr,
}
#[derive(Debug, Clone)]
pub struct BpfRegister {
pub index: u8,
pub name: &'static str,
pub class: BpfRegClass,
pub is_callee_saved: bool,
}
fn bpf_registers() -> Vec<BpfRegister> {
vec![
BpfRegister {
index: 0,
name: "r0",
class: BpfRegClass::GPR,
is_callee_saved: false,
},
BpfRegister {
index: 1,
name: "r1",
class: BpfRegClass::GPR,
is_callee_saved: false,
},
BpfRegister {
index: 2,
name: "r2",
class: BpfRegClass::GPR,
is_callee_saved: false,
},
BpfRegister {
index: 3,
name: "r3",
class: BpfRegClass::GPR,
is_callee_saved: false,
},
BpfRegister {
index: 4,
name: "r4",
class: BpfRegClass::GPR,
is_callee_saved: false,
},
BpfRegister {
index: 5,
name: "r5",
class: BpfRegClass::GPR,
is_callee_saved: false,
},
BpfRegister {
index: 6,
name: "r6",
class: BpfRegClass::GPR,
is_callee_saved: true,
},
BpfRegister {
index: 7,
name: "r7",
class: BpfRegClass::GPR,
is_callee_saved: true,
},
BpfRegister {
index: 8,
name: "r8",
class: BpfRegClass::GPR,
is_callee_saved: true,
},
BpfRegister {
index: 9,
name: "r9",
class: BpfRegClass::GPR,
is_callee_saved: true,
},
BpfRegister {
index: 10,
name: "r10",
class: BpfRegClass::FramePtr,
is_callee_saved: true,
},
]
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BpfTargetFeatures {
pub has_alu32: bool,
pub has_dwarfris: bool,
pub no_speculation_barrier: bool,
}
impl Default for BpfTargetFeatures {
fn default() -> Self {
Self {
has_alu32: true,
has_dwarfris: true,
no_speculation_barrier: false,
}
}
}
impl BpfTargetFeatures {
pub fn generic() -> Self {
Self::default()
}
pub fn v1() -> Self {
Self {
has_alu32: false,
has_dwarfris: false,
no_speculation_barrier: true,
}
}
pub fn v2() -> Self {
Self::default()
}
pub fn v3() -> Self {
Self {
has_alu32: true,
has_dwarfris: true,
no_speculation_barrier: false,
}
}
}
pub struct BpfTargetMachine {
pub initialized: bool,
pub features: BpfTargetFeatures,
pub big_endian: bool,
pub registers: Vec<BpfRegister>,
pub cpu: String,
pub triple: String,
}
impl Default for BpfTargetMachine {
fn default() -> Self {
Self {
initialized: false,
features: BpfTargetFeatures::default(),
big_endian: false,
registers: bpf_registers(),
cpu: String::from("generic"),
triple: String::from("bpf"),
}
}
}
impl BpfTargetMachine {
pub fn new() -> Self {
let mut tm = Self::default();
tm.initialize();
tm
}
pub fn for_cpu(cpu: &str) -> Self {
let mut tm = Self::default();
match cpu {
"v1" => tm.features = BpfTargetFeatures::v1(),
"v2" => tm.features = BpfTargetFeatures::v2(),
"v3" => tm.features = BpfTargetFeatures::v3(),
_ => tm.features = BpfTargetFeatures::generic(),
}
tm.cpu = cpu.to_string();
tm.initialize();
tm
}
pub fn big_endian() -> Self {
let mut tm = Self::new();
tm.big_endian = true;
tm.triple = String::from("bpfeb");
tm
}
fn initialize(&mut self) {
self.initialized = true;
}
pub fn get_register(&self, index: u8) -> Option<&BpfRegister> {
self.registers.get(index as usize)
}
pub fn get_reg_name(&self, index: u8) -> &'static str {
bpf_instr_info::reg_name(index)
}
pub fn num_gprs(&self) -> usize {
10 }
pub fn is_callee_saved(&self, index: u8) -> bool {
self.get_register(index)
.map(|r| r.is_callee_saved)
.unwrap_or(false)
}
pub fn frame_pointer_reg() -> u8 {
10
}
pub fn stack_pointer_reg() -> u8 {
10
}
pub fn return_reg() -> u8 {
0
}
pub fn arg_regs() -> Vec<u8> {
vec![1, 2, 3, 4, 5]
}
pub fn has_feature(&self, feature: &str) -> bool {
match feature {
"alu32" => self.features.has_alu32,
"dwarfris" => self.features.has_dwarfris,
_ => false,
}
}
pub fn get_triple(&self) -> &str {
&self.triple
}
pub fn get_cpu(&self) -> &str {
&self.cpu
}
pub fn instr_alignment() -> usize {
8
}
pub fn is_little_endian(&self) -> bool {
!self.big_endian
}
pub fn max_instructions() -> usize {
1_000_000
}
pub fn pointer_size() -> usize {
8
}
}
pub struct BpfCallingConvention;
impl BpfCallingConvention {
pub fn arg_reg(n: usize) -> Option<u8> {
match n {
0 => Some(1),
1 => Some(2),
2 => Some(3),
3 => Some(4),
4 => Some(5),
_ => None,
}
}
pub fn num_arg_regs() -> usize {
5
}
pub fn callee_saved_regs() -> Vec<u8> {
vec![6, 7, 8, 9]
}
pub fn emit_prologue(instrs: &mut Vec<super::bpf_mc_encoder::BpfInstr>) {
let _ = instrs;
}
pub fn emit_epilogue(instrs: &mut Vec<super::bpf_mc_encoder::BpfInstr>) {
let _ = instrs;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_target_machine() {
let tm = BpfTargetMachine::new();
assert!(tm.initialized);
assert_eq!(tm.triple, "bpf");
assert!(!tm.big_endian);
}
#[test]
fn test_for_cpu_v1() {
let tm = BpfTargetMachine::for_cpu("v1");
assert_eq!(tm.cpu, "v1");
assert!(!tm.features.has_alu32);
}
#[test]
fn test_for_cpu_v2() {
let tm = BpfTargetMachine::for_cpu("v2");
assert_eq!(tm.cpu, "v2");
assert!(tm.features.has_alu32);
}
#[test]
fn test_for_cpu_unknown() {
let tm = BpfTargetMachine::for_cpu("unknown_cpu");
assert!(tm.features.has_alu32); }
#[test]
fn test_big_endian() {
let tm = BpfTargetMachine::big_endian();
assert!(tm.big_endian);
assert_eq!(tm.triple, "bpfeb");
}
#[test]
fn test_get_register() {
let tm = BpfTargetMachine::new();
let reg = tm.get_register(0).expect("r0 should exist");
assert_eq!(reg.name, "r0");
assert!(!reg.is_callee_saved);
let r10 = tm.get_register(10).expect("r10 should exist");
assert_eq!(r10.name, "r10");
assert!(r10.is_callee_saved);
}
#[test]
fn test_get_register_out_of_bounds() {
let tm = BpfTargetMachine::new();
assert!(tm.get_register(255).is_none());
}
#[test]
fn test_is_callee_saved() {
let tm = BpfTargetMachine::new();
assert!(!tm.is_callee_saved(0));
assert!(!tm.is_callee_saved(5));
assert!(tm.is_callee_saved(6));
assert!(tm.is_callee_saved(9));
assert!(tm.is_callee_saved(10));
}
#[test]
fn test_return_reg() {
assert_eq!(BpfTargetMachine::return_reg(), 0);
}
#[test]
fn test_arg_regs() {
let regs = BpfTargetMachine::arg_regs();
assert_eq!(regs, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_frame_pointer_reg() {
assert_eq!(BpfTargetMachine::frame_pointer_reg(), 10);
}
#[test]
fn test_stack_pointer_reg() {
assert_eq!(BpfTargetMachine::stack_pointer_reg(), 10);
}
#[test]
fn test_has_feature() {
let tm_v1 = BpfTargetMachine::for_cpu("v1");
assert!(!tm_v1.has_feature("alu32"));
let tm = BpfTargetMachine::new();
assert!(tm.has_feature("alu32"));
assert!(tm.has_feature("dwarfris"));
}
#[test]
fn test_ptr_size() {
assert_eq!(BpfTargetMachine::pointer_size(), 8);
}
#[test]
fn test_instr_alignment() {
assert_eq!(BpfTargetMachine::instr_alignment(), 8);
}
#[test]
fn test_max_instructions() {
assert!(BpfTargetMachine::max_instructions() > 0);
}
#[test]
fn test_calling_conv_arg_reg() {
assert_eq!(BpfCallingConvention::arg_reg(0), Some(1));
assert_eq!(BpfCallingConvention::arg_reg(4), Some(5));
assert_eq!(BpfCallingConvention::arg_reg(5), None);
}
#[test]
fn test_calling_conv_callee_saved() {
assert_eq!(BpfCallingConvention::callee_saved_regs(), vec![6, 7, 8, 9]);
}
#[test]
fn test_emit_prologue_epilogue_no_panic() {
let mut instrs: Vec<super::super::bpf_mc_encoder::BpfInstr> = Vec::new();
BpfCallingConvention::emit_prologue(&mut instrs);
BpfCallingConvention::emit_epilogue(&mut instrs);
}
#[test]
fn test_target_features_default() {
let features = BpfTargetFeatures::default();
assert!(features.has_alu32);
assert!(features.has_dwarfris);
}
#[test]
fn test_num_gprs() {
let tm = BpfTargetMachine::new();
assert_eq!(tm.num_gprs(), 10);
}
#[test]
fn test_triple_is_little_endian() {
let tm = BpfTargetMachine::new();
assert!(tm.is_little_endian());
assert_eq!(tm.get_triple(), "bpf");
}
#[test]
fn test_get_cpu() {
let tm = BpfTargetMachine::for_cpu("v3");
assert_eq!(tm.get_cpu(), "v3");
}
}