blob: e2b5e5d06c709b49e05c94a07763fd98befaf8a6 [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use super::interrupter::Interrupter;
use super::transfer_ring_controller::{TransferRingController, TransferRingControllerError};
use super::usb_hub::{self, UsbHub};
use super::xhci_abi::{
AddressDeviceCommandTrb, ConfigureEndpointCommandTrb, DequeuePtr, DeviceContext,
DeviceSlotState, EndpointContext, EndpointState, EvaluateContextCommandTrb,
InputControlContext, SlotContext, TrbCompletionCode, DEVICE_CONTEXT_ENTRY_SIZE,
};
use super::xhci_regs::{valid_slot_id, MAX_PORTS, MAX_SLOTS};
use crate::register_space::Register;
use crate::usb::xhci::ring_buffer_stop_cb::{fallible_closure, RingBufferStopCallback};
use crate::utils::{EventLoop, FailHandle};
use bit_field::Error as BitFieldError;
use std::fmt::{self, Display};
use std::mem::size_of;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use sync::Mutex;
use sys_util::{error, GuestAddress, GuestMemory, GuestMemoryError};
#[derive(Debug)]
pub enum Error {
BadPortId(u8),
ReadGuestMemory(GuestMemoryError),
WriteGuestMemory(GuestMemoryError),
WeakReferenceUpgrade,
CallbackFailed,
GetSlotContextState(BitFieldError),
GetEndpointState(BitFieldError),
GetPort(u8),
GetTrc(u8),
BadInputContextAddr(GuestAddress),
BadDeviceContextAddr(GuestAddress),
CreateTransferController(TransferRingControllerError),
}
type Result<T> = std::result::Result<T, Error>;
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
BadPortId(id) => write!(f, "device slot get a bad port id: {}", id),
ReadGuestMemory(e) => write!(f, "failed to read guest memory: {}", e),
WriteGuestMemory(e) => write!(f, "failed to write guest memory: {}", e),
WeakReferenceUpgrade => write!(f, "failed to upgrade weak reference"),
CallbackFailed => write!(f, "callback failed"),
GetSlotContextState(e) => write!(f, "failed to get slot context state: {}", e),
GetEndpointState(e) => write!(f, "failed to get endpoint state: {}", e),
GetPort(v) => write!(f, "failed to get port: {}", v),
GetTrc(v) => write!(f, "failed to get trc: {}", v),
BadInputContextAddr(addr) => write!(f, "bad input context address: {}", addr),
BadDeviceContextAddr(addr) => write!(f, "bad device context: {}", addr),
CreateTransferController(e) => write!(f, "failed to create transfer controller: {}", e),
}
}
}
/// See spec 4.5.1 for dci.
/// index 0: Control endpoint. Device Context Index: 1.
/// index 1: Endpoint 1 out. Device Context Index: 2
/// index 2: Endpoint 1 in. Device Context Index: 3.
/// index 3: Endpoint 2 out. Device Context Index: 4
/// ...
/// index 30: Endpoint 15 in. Device Context Index: 31
pub const TRANSFER_RING_CONTROLLERS_INDEX_END: usize = 31;
/// End of device context index.
pub const DCI_INDEX_END: u8 = (TRANSFER_RING_CONTROLLERS_INDEX_END + 1) as u8;
/// Device context index of first transfer endpoint.
pub const FIRST_TRANSFER_ENDPOINT_DCI: u8 = 2;
fn valid_endpoint_id(endpoint_id: u8) -> bool {
endpoint_id < DCI_INDEX_END && endpoint_id > 0
}
#[derive(Clone)]
pub struct DeviceSlots {
fail_handle: Arc<dyn FailHandle>,
hub: Arc<UsbHub>,
slots: Vec<Arc<DeviceSlot>>,
}
impl DeviceSlots {
pub fn new(
fail_handle: Arc<dyn FailHandle>,
dcbaap: Register<u64>,
hub: Arc<UsbHub>,
interrupter: Arc<Mutex<Interrupter>>,
event_loop: Arc<EventLoop>,
mem: GuestMemory,
) -> DeviceSlots {
let mut slots = Vec::new();
for slot_id in 1..=MAX_SLOTS {
slots.push(Arc::new(DeviceSlot::new(
slot_id,
dcbaap.clone(),
hub.clone(),
interrupter.clone(),
event_loop.clone(),
mem.clone(),
)));
}
DeviceSlots {
fail_handle,
hub,
slots,
}
}
/// Note that slot id starts from 1. Slot index start from 0.
pub fn slot(&self, slot_id: u8) -> Option<Arc<DeviceSlot>> {
if valid_slot_id(slot_id) {
Some(self.slots[slot_id as usize - 1].clone())
} else {
error!(
"trying to index a wrong slot id {}, max slot = {}",
slot_id, MAX_SLOTS
);
None
}
}
/// Reset the device connected to a specific port.
pub fn reset_port(&self, port_id: u8) -> std::result::Result<(), ()> {
if let Some(port) = self.hub.get_port(port_id) {
if let Some(backend_device) = port.get_backend_device().as_mut() {
backend_device.reset()?;
}
}
// No device on port, so nothing to reset.
Ok(())
}
/// Stop all device slots and reset them.
pub fn stop_all_and_reset<C: FnMut() + 'static + Send>(&self, mut callback: C) {
usb_debug!("stopping all device slots and resetting host hub");
let slots = self.slots.clone();
let hub = self.hub.clone();
let auto_callback = RingBufferStopCallback::new(fallible_closure(
self.fail_handle.clone(),
move || -> std::result::Result<(), usb_hub::Error> {
for slot in &slots {
slot.reset();
}
hub.reset()?;
callback();
Ok(())
},
));
self.stop_all(auto_callback);
}
/// Stop all devices. The auto callback will be executed when all trc is stopped. It could
/// happen asynchronously, if there are any pending transfers.
pub fn stop_all(&self, auto_callback: RingBufferStopCallback) {
for slot in &self.slots {
slot.stop_all_trc(auto_callback.clone());
}
}
/// Disable a slot. This might happen asynchronously, if there is any pending transfers. The
/// callback will be invoked when slot is actually disabled.
pub fn disable_slot<
C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
>(
&self,
slot_id: u8,
cb: C,
) -> Result<()> {
usb_debug!("device slot {} is being disabled", slot_id);
DeviceSlot::disable(
self.fail_handle.clone(),
&self.slots[slot_id as usize - 1],
cb,
)
}
/// Reset a slot. This is a shortcut call for DeviceSlot::reset_slot.
pub fn reset_slot<
C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
>(
&self,
slot_id: u8,
cb: C,
) -> Result<()> {
usb_debug!("device slot {} is resetting", slot_id);
DeviceSlot::reset_slot(
self.fail_handle.clone(),
&self.slots[slot_id as usize - 1],
cb,
)
}
pub fn stop_endpoint<
C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
>(
&self,
slot_id: u8,
endpoint_id: u8,
cb: C,
) -> Result<()> {
self.slots[slot_id as usize - 1].stop_endpoint(self.fail_handle.clone(), endpoint_id, cb)
}
}
// Usb port id. Valid ids starts from 1, to MAX_PORTS.
struct PortId(Mutex<u8>);
impl PortId {
fn new() -> Self {
PortId(Mutex::new(0))
}
fn set(&self, value: u8) -> Result<()> {
if value < 1 || value > MAX_PORTS {
return Err(Error::BadPortId(value));
}
*self.0.lock() = value;
Ok(())
}
fn reset(&self) {
*self.0.lock() = 0;
}
fn get(&self) -> Result<(u8)> {
let val = *self.0.lock();
if val == 0 {
return Err(Error::BadPortId(val));
}
Ok(val)
}
}
pub struct DeviceSlot {
slot_id: u8,
port_id: PortId, // Valid port id starts from 1, to MAX_PORTS.
dcbaap: Register<u64>,
hub: Arc<UsbHub>,
interrupter: Arc<Mutex<Interrupter>>,
event_loop: Arc<EventLoop>,
mem: GuestMemory,
enabled: AtomicBool,
transfer_ring_controllers: Mutex<Vec<Option<Arc<TransferRingController>>>>,
}
impl DeviceSlot {
/// Create a new device slot.
pub fn new(
slot_id: u8,
dcbaap: Register<u64>,
hub: Arc<UsbHub>,
interrupter: Arc<Mutex<Interrupter>>,
event_loop: Arc<EventLoop>,
mem: GuestMemory,
) -> Self {
let transfer_ring_controllers = vec![None; TRANSFER_RING_CONTROLLERS_INDEX_END];
DeviceSlot {
slot_id,
port_id: PortId::new(),
dcbaap,
hub,
interrupter,
event_loop,
mem,
enabled: AtomicBool::new(false),
transfer_ring_controllers: Mutex::new(transfer_ring_controllers),
}
}
fn get_trc(&self, i: usize) -> Option<Arc<TransferRingController>> {
let trcs = self.transfer_ring_controllers.lock();
trcs[i].clone()
}
fn set_trc(&self, i: usize, trc: Option<Arc<TransferRingController>>) {
let mut trcs = self.transfer_ring_controllers.lock();
trcs[i] = trc;
}
fn trc_len(&self) -> usize {
self.transfer_ring_controllers.lock().len()
}
/// The arguments are identical to the fields in each doorbell register. The
/// target value:
/// 1: Reserved
/// 2: Control endpoint
/// 3: Endpoint 1 out
/// 4: Endpoint 1 in
/// 5: Endpoint 2 out
/// ...
/// 32: Endpoint 15 in
///
/// Steam ID will be useful when host controller support streams.
/// The stream ID must be zero for endpoints that do not have streams
/// configured.
/// This function will return false if it fails to trigger transfer ring start.
pub fn ring_doorbell(&self, target: u8, _stream_id: u16) -> Result<bool> {
if !valid_endpoint_id(target) {
error!(
"device slot {}: Invalid target written to doorbell register. target: {}",
self.slot_id, target
);
return Ok(false);
}
usb_debug!(
"device slot {}: ding-dong. who is that? target = {}",
self.slot_id,
target
);
// See DCI in spec.
let endpoint_index = (target - 1) as usize;
let transfer_ring_controller = match self.get_trc(endpoint_index) {
Some(tr) => tr,
None => {
error!("Device endpoint is not inited");
return Ok(false);
}
};
let context = self.get_device_context()?;
if context.endpoint_context[endpoint_index]
.get_endpoint_state()
.map_err(Error::GetEndpointState)?
== EndpointState::Running
{
usb_debug!("endpoint is started, start transfer ring");
transfer_ring_controller.start();
} else {
error!("doorbell rung when endpoint is not started");
}
Ok(true)
}
/// Enable the slot. This function returns false if it's already enabled.
pub fn enable(&self) -> bool {
let was_already_enabled = self.enabled.swap(true, Ordering::SeqCst);
if was_already_enabled {
error!("device slot is already enabled");
} else {
usb_debug!("device slot {} enabled", self.slot_id);
}
!was_already_enabled
}
/// Disable this device slot. If the slot is not enabled, callback will be invoked immediately
/// with error. Otherwise, callback will be invoked when all trc is stopped.
pub fn disable<C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send>(
fail_handle: Arc<dyn FailHandle>,
slot: &Arc<DeviceSlot>,
mut callback: C,
) -> Result<()> {
if slot.enabled.load(Ordering::SeqCst) {
let slot_weak = Arc::downgrade(slot);
let auto_callback =
RingBufferStopCallback::new(fallible_closure(fail_handle, move || {
// Slot should still be alive when the callback is invoked. If it's not, there
// must be a bug somewhere.
let slot = slot_weak.upgrade().ok_or(Error::WeakReferenceUpgrade)?;
let mut device_context = slot.get_device_context()?;
device_context
.slot_context
.set_slot_state(DeviceSlotState::DisabledOrEnabled);
slot.set_device_context(device_context)?;
slot.reset();
usb_debug!(
"device slot {}: all trc disabled, sending trb",
slot.slot_id
);
callback(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed)
}));
slot.stop_all_trc(auto_callback);
Ok(())
} else {
callback(TrbCompletionCode::SlotNotEnabledError).map_err(|_| Error::CallbackFailed)
}
}
// Assigns the device address and initializes slot and endpoint 0 context.
pub fn set_address(&self, trb: &AddressDeviceCommandTrb) -> Result<TrbCompletionCode> {
if !self.enabled.load(Ordering::SeqCst) {
error!(
"trying to set address to a disabled device slot {}",
self.slot_id
);
return Ok(TrbCompletionCode::SlotNotEnabledError);
}
let device_context = self.get_device_context()?;
let state = device_context
.slot_context
.get_slot_state()
.map_err(Error::GetSlotContextState)?;
match state {
DeviceSlotState::DisabledOrEnabled => {}
DeviceSlotState::Default if !trb.get_block_set_address_request() => {}
_ => {
error!("slot {} has unexpected slot state", self.slot_id);
return Ok(TrbCompletionCode::ContextStateError);
}
}
// Copy all fields of the slot context and endpoint 0 context from the input context
// to the output context.
let input_context_ptr = GuestAddress(trb.get_input_context_pointer());
// Copy slot context.
self.copy_context(input_context_ptr, 0)?;
// Copy control endpoint context.
self.copy_context(input_context_ptr, 1)?;
// Read back device context.
let mut device_context = self.get_device_context()?;
let port_id = device_context.slot_context.get_root_hub_port_number();
self.port_id.set(port_id)?;
usb_debug!(
"port id {} is assigned to slot id {}",
port_id,
self.slot_id
);
// Initialize the control endpoint. Endpoint id = 1.
self.set_trc(
0,
Some(
TransferRingController::new(
self.mem.clone(),
self.hub.get_port(port_id).ok_or(Error::GetPort(port_id))?,
self.event_loop.clone(),
self.interrupter.clone(),
self.slot_id,
1,
)
.map_err(Error::CreateTransferController)?,
),
);
// Assign slot ID as device address if block_set_address_request is not set.
if trb.get_block_set_address_request() {
device_context
.slot_context
.set_slot_state(DeviceSlotState::Default);
} else {
let port = self.hub.get_port(port_id).ok_or(Error::GetPort(port_id))?;
match port.get_backend_device().as_mut() {
Some(backend) => {
backend.set_address(self.slot_id as u32);
}
None => {
return Ok(TrbCompletionCode::TransactionError);
}
}
device_context
.slot_context
.set_usb_device_address(self.slot_id);
device_context
.slot_context
.set_slot_state(DeviceSlotState::Addressed);
}
// TODO(jkwang) trc should always exists. Fix this.
self.get_trc(0)
.ok_or(Error::GetTrc(0))?
.set_dequeue_pointer(
device_context.endpoint_context[0]
.get_tr_dequeue_pointer()
.get_gpa(),
);
self.get_trc(0)
.ok_or(Error::GetTrc(0))?
.set_consumer_cycle_state(device_context.endpoint_context[0].get_dequeue_cycle_state());
usb_debug!("Setting endpoint 0 to running");
device_context.endpoint_context[0].set_endpoint_state(EndpointState::Running);
self.set_device_context(device_context)?;
Ok(TrbCompletionCode::Success)
}
// Adds or drops multiple endpoints in the device slot.
pub fn configure_endpoint(
&self,
trb: &ConfigureEndpointCommandTrb,
) -> Result<TrbCompletionCode> {
usb_debug!("configuring endpoint");
let input_control_context = if trb.get_deconfigure() {
// From section 4.6.6 of the xHCI spec:
// Setting the deconfigure (DC) flag to '1' in the Configure Endpoint Command
// TRB is equivalent to setting Input Context Drop Context flags 2-31 to '1'
// and Add Context 2-31 flags to '0'.
let mut c = InputControlContext::new();
c.set_add_context_flags(0);
c.set_drop_context_flags(0xfffffffc);
c
} else {
self.mem
.read_obj_from_addr(GuestAddress(trb.get_input_context_pointer()))
.map_err(Error::ReadGuestMemory)?
};
for device_context_index in 1..DCI_INDEX_END {
if input_control_context.drop_context_flag(device_context_index) {
self.drop_one_endpoint(device_context_index)?;
}
if input_control_context.add_context_flag(device_context_index) {
self.copy_context(
GuestAddress(trb.get_input_context_pointer()),
device_context_index,
)?;
self.add_one_endpoint(device_context_index)?;
}
}
if trb.get_deconfigure() {
self.set_state(DeviceSlotState::Addressed)?;
} else {
self.set_state(DeviceSlotState::Configured)?;
}
Ok(TrbCompletionCode::Success)
}
// Evaluates the device context by reading new values for certain fields of
// the slot context and/or control endpoint context.
pub fn evaluate_context(&self, trb: &EvaluateContextCommandTrb) -> Result<TrbCompletionCode> {
if !self.enabled.load(Ordering::SeqCst) {
return Ok(TrbCompletionCode::SlotNotEnabledError);
}
// TODO(jkwang) verify this
// The spec has multiple contradictions about validating context parameters in sections
// 4.6.7, 6.2.3.3. To keep things as simple as possible we do no further validation here.
let input_control_context: InputControlContext = self
.mem
.read_obj_from_addr(GuestAddress(trb.get_input_context_pointer()))
.map_err(Error::ReadGuestMemory)?;
let mut device_context = self.get_device_context()?;
if input_control_context.add_context_flag(0) {
let input_slot_context: SlotContext = self
.mem
.read_obj_from_addr(GuestAddress(
trb.get_input_context_pointer() + DEVICE_CONTEXT_ENTRY_SIZE as u64,
))
.map_err(Error::ReadGuestMemory)?;
device_context
.slot_context
.set_interrupter_target(input_slot_context.get_interrupter_target());
device_context
.slot_context
.set_max_exit_latency(input_slot_context.get_max_exit_latency());
}
// From 6.2.3.3: "Endpoint Contexts 2 throught 31 shall not be evaluated by the Evaluate
// Context Command".
if input_control_context.add_context_flag(1) {
let ep0_context: EndpointContext = self
.mem
.read_obj_from_addr(GuestAddress(
trb.get_input_context_pointer() + 2 * DEVICE_CONTEXT_ENTRY_SIZE as u64,
))
.map_err(Error::ReadGuestMemory)?;
device_context.endpoint_context[0]
.set_max_packet_size(ep0_context.get_max_packet_size());
}
self.set_device_context(device_context)?;
Ok(TrbCompletionCode::Success)
}
/// Reset the device slot to default state and deconfigures all but the
/// control endpoint.
pub fn reset_slot<
C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
>(
fail_handle: Arc<dyn FailHandle>,
slot: &Arc<DeviceSlot>,
mut callback: C,
) -> Result<()> {
let weak_s = Arc::downgrade(&slot);
let auto_callback =
RingBufferStopCallback::new(fallible_closure(fail_handle, move || -> Result<()> {
let s = weak_s.upgrade().ok_or(Error::WeakReferenceUpgrade)?;
for i in FIRST_TRANSFER_ENDPOINT_DCI..DCI_INDEX_END {
s.drop_one_endpoint(i)?;
}
let mut ctx = s.get_device_context()?;
ctx.slot_context.set_slot_state(DeviceSlotState::Default);
ctx.slot_context.set_context_entries(1);
ctx.slot_context.set_root_hub_port_number(0);
s.set_device_context(ctx)?;
callback(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed)?;
Ok(())
}));
slot.stop_all_trc(auto_callback);
Ok(())
}
/// Stop all transfer ring controllers.
pub fn stop_all_trc(&self, auto_callback: RingBufferStopCallback) {
for i in 0..self.trc_len() {
if let Some(trc) = self.get_trc(i) {
trc.stop(auto_callback.clone());
}
}
}
/// Stop a endpoint.
pub fn stop_endpoint<
C: FnMut(TrbCompletionCode) -> std::result::Result<(), ()> + 'static + Send,
>(
&self,
fail_handle: Arc<dyn FailHandle>,
endpoint_id: u8,
mut cb: C,
) -> Result<()> {
if !valid_endpoint_id(endpoint_id) {
error!("trb indexing wrong endpoint id");
return cb(TrbCompletionCode::TrbError).map_err(|_| Error::CallbackFailed);
}
let index = endpoint_id - 1;
match self.get_trc(index as usize) {
Some(trc) => {
usb_debug!("stopping endpoint");
let auto_cb = RingBufferStopCallback::new(fallible_closure(
fail_handle,
move || -> Result<()> {
cb(TrbCompletionCode::Success).map_err(|_| Error::CallbackFailed)
},
));
trc.stop(auto_cb);
}
None => {
error!("endpoint at index {} is not started", index);
cb(TrbCompletionCode::ContextStateError).map_err(|_| Error::CallbackFailed)?;
}
}
Ok(())
}
/// Set transfer ring dequeue pointer.
pub fn set_tr_dequeue_ptr(&self, endpoint_id: u8, ptr: u64) -> Result<TrbCompletionCode> {
if !valid_endpoint_id(endpoint_id) {
error!("trb indexing wrong endpoint id");
return Ok(TrbCompletionCode::TrbError);
}
let index = (endpoint_id - 1) as usize;
match self.get_trc(index) {
Some(trc) => {
trc.set_dequeue_pointer(GuestAddress(ptr));
let mut ctx = self.get_device_context()?;
ctx.endpoint_context[index]
.set_tr_dequeue_pointer(DequeuePtr::new(GuestAddress(ptr)));
self.set_device_context(ctx)?;
Ok(TrbCompletionCode::Success)
}
None => {
error!("set tr dequeue ptr failed due to no trc started");
Ok(TrbCompletionCode::ContextStateError)
}
}
}
// Reset and reset_slot are different.
// Reset_slot handles command ring `reset slot` command. It will reset the slot state.
// Reset handles xhci reset. It will destroy everything.
fn reset(&self) {
for i in 0..self.trc_len() {
self.set_trc(i, None);
}
usb_debug!("reseting device slot {}!", self.slot_id);
self.enabled.store(false, Ordering::SeqCst);
self.port_id.reset();
}
fn add_one_endpoint(&self, device_context_index: u8) -> Result<()> {
usb_debug!(
"adding one endpoint, device context index {}",
device_context_index
);
let mut device_context = self.get_device_context()?;
let transfer_ring_index = (device_context_index - 1) as usize;
let trc = TransferRingController::new(
self.mem.clone(),
self.hub
.get_port(self.port_id.get()?)
.ok_or(Error::GetPort(self.port_id.get()?))?,
self.event_loop.clone(),
self.interrupter.clone(),
self.slot_id,
device_context_index,
)
.map_err(Error::CreateTransferController)?;
trc.set_dequeue_pointer(
device_context.endpoint_context[transfer_ring_index]
.get_tr_dequeue_pointer()
.get_gpa(),
);
trc.set_consumer_cycle_state(
device_context.endpoint_context[transfer_ring_index].get_dequeue_cycle_state(),
);
self.set_trc(transfer_ring_index, Some(trc));
device_context.endpoint_context[transfer_ring_index]
.set_endpoint_state(EndpointState::Running);
self.set_device_context(device_context)
}
fn drop_one_endpoint(&self, device_context_index: u8) -> Result<()> {
let endpoint_index = (device_context_index - 1) as usize;
self.set_trc(endpoint_index, None);
let mut ctx = self.get_device_context()?;
ctx.endpoint_context[endpoint_index].set_endpoint_state(EndpointState::Disabled);
self.set_device_context(ctx)
}
fn get_device_context(&self) -> Result<DeviceContext> {
let ctx = self
.mem
.read_obj_from_addr(self.get_device_context_addr()?)
.map_err(Error::ReadGuestMemory)?;
usb_debug!("read device ctx: {:?}", ctx);
Ok(ctx)
}
fn set_device_context(&self, device_context: DeviceContext) -> Result<()> {
self.mem
.write_obj_at_addr(device_context, self.get_device_context_addr()?)
.map_err(Error::WriteGuestMemory)
}
fn copy_context(
&self,
input_context_ptr: GuestAddress,
device_context_index: u8,
) -> Result<()> {
// Note that it could be slot context or device context. They have the same size. Won't
// make a difference here.
let ctx: EndpointContext = self
.mem
.read_obj_from_addr(
input_context_ptr
.checked_add(
(device_context_index as u64 + 1) * DEVICE_CONTEXT_ENTRY_SIZE as u64,
)
.ok_or(Error::BadInputContextAddr(input_context_ptr))?,
)
.map_err(Error::ReadGuestMemory)?;
usb_debug!("context being copied {:?}", ctx);
let device_context_ptr = self.get_device_context_addr()?;
self.mem
.write_obj_at_addr(
ctx,
device_context_ptr
.checked_add(device_context_index as u64 * DEVICE_CONTEXT_ENTRY_SIZE as u64)
.ok_or(Error::BadDeviceContextAddr(device_context_ptr))?,
)
.map_err(Error::WriteGuestMemory)
}
fn get_device_context_addr(&self) -> Result<GuestAddress> {
let addr: u64 = self
.mem
.read_obj_from_addr(GuestAddress(
self.dcbaap.get_value() + size_of::<u64>() as u64 * self.slot_id as u64,
))
.map_err(Error::ReadGuestMemory)?;
Ok(GuestAddress(addr))
}
fn set_state(&self, state: DeviceSlotState) -> Result<()> {
let mut ctx = self.get_device_context()?;
ctx.slot_context.set_slot_state(state);
self.set_device_context(ctx)
}
}