Skip to content

Commit 245f51d

Browse files
ShadowCursealxiord
authored andcommitted
feat(aarch64): generic get/set_one_reg
Linux kernel defines arm registers up to 2048 bits wide. To support all different sized registers we change get/set_one_reg methods to accept a byte slice. For `set_one_reg` slice contains register data to be written to vcpu. For `get_one_reg` slice will have register data read from vcpu. Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
1 parent ca88c67 commit 245f51d

File tree

3 files changed

+59
-25
lines changed

3 files changed

+59
-25
lines changed

src/ioctls/vcpu.rs

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref};
1919
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2020
use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val};
2121

22+
/// Helper method to obtain the size of the register through its id
23+
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
24+
fn reg_size(reg_id: u64) -> usize {
25+
2_usize.pow(((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT) as u32)
26+
}
27+
2228
/// Reasons for vCPU exits.
2329
///
2430
/// The exit reasons are mapped to the `KVM_EXIT_*` defines in the
@@ -1162,45 +1168,61 @@ impl VcpuFd {
11621168
/// # Arguments
11631169
///
11641170
/// * `reg_id` - ID of the register for which we are setting the value.
1165-
/// * `data` - value for the specified register.
1171+
/// * `data` - byte slice where the register value will be written to.
1172+
///
1173+
/// # Note
1174+
///
1175+
/// `data` should be equal or bigger then the register size
1176+
/// oterwise function will return EINVAL error
11661177
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
1167-
pub fn set_one_reg(&self, reg_id: u64, data: u128) -> Result<()> {
1168-
let data_ptr = &data as *const _;
1178+
pub fn set_one_reg(&self, reg_id: u64, data: &[u8]) -> Result<usize> {
1179+
let reg_size = reg_size(reg_id);
1180+
if data.len() < reg_size {
1181+
return Err(errno::Error::new(libc::EINVAL));
1182+
}
11691183
let onereg = kvm_one_reg {
11701184
id: reg_id,
1171-
addr: data_ptr as u64,
1185+
addr: data.as_ptr() as u64,
11721186
};
11731187
// SAFETY: This is safe because we allocated the struct and we know the kernel will read
11741188
// exactly the size of the struct.
11751189
let ret = unsafe { ioctl_with_ref(self, KVM_SET_ONE_REG(), &onereg) };
11761190
if ret < 0 {
11771191
return Err(errno::Error::last());
11781192
}
1179-
Ok(())
1193+
Ok(reg_size)
11801194
}
11811195

1182-
/// Returns the value of the specified vCPU register.
1196+
/// Writes the value of the specified vCPU register into provided buffer.
11831197
///
11841198
/// The id of the register is encoded as specified in the kernel documentation
11851199
/// for `KVM_GET_ONE_REG`.
11861200
///
11871201
/// # Arguments
11881202
///
11891203
/// * `reg_id` - ID of the register.
1204+
/// * `data` - byte slice where the register value will be written to.
1205+
/// # Note
1206+
///
1207+
/// `data` should be equal or bigger then the register size
1208+
/// oterwise function will return EINVAL error
11901209
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
1191-
pub fn get_one_reg(&self, reg_id: u64) -> Result<u128> {
1192-
let mut reg_value = 0;
1210+
pub fn get_one_reg(&self, reg_id: u64, data: &mut [u8]) -> Result<usize> {
1211+
let reg_size = reg_size(reg_id);
1212+
if data.len() < reg_size {
1213+
return Err(errno::Error::new(libc::EINVAL));
1214+
}
11931215
let mut onereg = kvm_one_reg {
11941216
id: reg_id,
1195-
addr: &mut reg_value as *mut _ as u64,
1217+
addr: data.as_ptr() as u64,
11961218
};
11971219
// SAFETY: This is safe because we allocated the struct and we know the kernel will read
11981220
// exactly the size of the struct.
11991221
let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_ONE_REG(), &mut onereg) };
12001222
if ret < 0 {
12011223
return Err(errno::Error::last());
12021224
}
1203-
Ok(reg_value)
1225+
Ok(reg_size)
12041226
}
12051227

12061228
/// Notify the guest about the vCPU being paused.
@@ -2044,15 +2066,18 @@ mod tests {
20442066

20452067
// Set the PC to the guest address where we loaded the code.
20462068
vcpu_fd
2047-
.set_one_reg(core_reg_base + 2 * 32, guest_addr as u128)
2069+
.set_one_reg(core_reg_base + 2 * 32, &(guest_addr as u128).to_le_bytes())
20482070
.unwrap();
20492071

20502072
// Set x8 and x9 to the addresses the guest test code needs
20512073
vcpu_fd
2052-
.set_one_reg(core_reg_base + 2 * 8, guest_addr as u128 + 0x10000)
2074+
.set_one_reg(
2075+
core_reg_base + 2 * 8,
2076+
&(guest_addr as u128 + 0x10000).to_le_bytes(),
2077+
)
20532078
.unwrap();
20542079
vcpu_fd
2055-
.set_one_reg(core_reg_base + 2 * 9, mmio_addr as u128)
2080+
.set_one_reg(core_reg_base + 2 * 9, &(mmio_addr as u128).to_le_bytes())
20562081
.unwrap();
20572082

20582083
loop {
@@ -2389,12 +2414,16 @@ mod tests {
23892414
let data: u128 = 0;
23902415
let reg_id: u64 = 0;
23912416

2392-
assert!(vcpu.set_one_reg(reg_id, data).is_err());
2417+
assert!(vcpu.set_one_reg(reg_id, &data.to_le_bytes()).is_err());
23932418
// Exercising KVM_SET_ONE_REG by trying to alter the data inside the PSTATE register (which is a
23942419
// specific aarch64 register).
2420+
// This regiseter is 64 bit wide (8 bytes).
23952421
const PSTATE_REG_ID: u64 = 0x6030_0000_0010_0042;
2396-
vcpu.set_one_reg(PSTATE_REG_ID, data)
2422+
vcpu.set_one_reg(PSTATE_REG_ID, &data.to_le_bytes())
23972423
.expect("Failed to set pstate register");
2424+
2425+
// Trying to set 8 byte register with 7 bytes must fail.
2426+
assert!(vcpu.set_one_reg(PSTATE_REG_ID, &[0_u8; 7]).is_err());
23982427
}
23992428

24002429
#[test]
@@ -2420,14 +2449,17 @@ mod tests {
24202449
PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
24212450
let data: u128 = PSTATE_FAULT_BITS_64 as u128;
24222451
const PSTATE_REG_ID: u64 = 0x6030_0000_0010_0042;
2423-
vcpu.set_one_reg(PSTATE_REG_ID, data)
2452+
vcpu.set_one_reg(PSTATE_REG_ID, &data.to_le_bytes())
24242453
.expect("Failed to set pstate register");
24252454

2426-
assert_eq!(
2427-
vcpu.get_one_reg(PSTATE_REG_ID)
2428-
.expect("Failed to get pstate register"),
2429-
PSTATE_FAULT_BITS_64 as u128
2430-
);
2455+
let mut bytes = [0_u8; 16];
2456+
vcpu.get_one_reg(PSTATE_REG_ID, &mut bytes)
2457+
.expect("Failed to get pstate register");
2458+
let data = u128::from_le_bytes(bytes);
2459+
assert_eq!(data, PSTATE_FAULT_BITS_64 as u128);
2460+
2461+
// Trying to get 8 byte register with 7 bytes must fail.
2462+
assert!(vcpu.get_one_reg(PSTATE_REG_ID, &mut [0_u8; 7]).is_err());
24312463
}
24322464

24332465
#[test]

src/ioctls/vm.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,8 +784,8 @@ impl VmFd {
784784
///
785785
/// let core_reg_base: u64 = 0x6030_0000_0010_0000;
786786
/// let mmio_addr: u64 = guest_addr + mem_size as u64;
787-
/// vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr as u128); // set PC
788-
/// vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr as u128); // set X0
787+
/// vcpu_fd.set_one_reg(core_reg_base + 2 * 32, &guest_addr.to_le_bytes()); // set PC
788+
/// vcpu_fd.set_one_reg(core_reg_base + 2 * 0, &mmio_addr.to_le_bytes()); // set X0
789789
/// }
790790
///
791791
/// loop {

src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,10 @@
156156
//!
157157
//! let core_reg_base: u64 = 0x6030_0000_0010_0000;
158158
//! let mmio_addr: u64 = guest_addr + mem_size as u64;
159-
//! vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr as u128); // set PC
160-
//! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr as u128); // set X0
159+
//! // set PC
160+
//! vcpu_fd.set_one_reg(core_reg_base + 2 * 32, &guest_addr.to_le_bytes());
161+
//! // set X0
162+
//! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, &mmio_addr.to_le_bytes());
161163
//! }
162164
//!
163165
//! // 6. Run code on the vCPU.

0 commit comments

Comments
 (0)