1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openeuler-stratovirt

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
storage.rs 19 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
yezengruan Отправлено год назад 4f7e72f
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
use std::{
collections::HashMap,
sync::{Arc, Mutex, Weak},
};
use anyhow::{anyhow, bail, Context, Result};
use byteorder::{ByteOrder, LittleEndian};
use log::{error, info, warn};
use once_cell::sync::Lazy;
use super::descriptor::{
UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface,
UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor,
};
use super::xhci::xhci_controller::XhciDevice;
use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN};
use super::{UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus};
use crate::{
ScsiBus::{
ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, EMULATE_SCSI_OPS, GOOD,
SCSI_CMD_BUF_SIZE,
},
ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM},
};
use machine_manager::config::{DriveFile, UsbStorageConfig};
// Storage device descriptor
static DESC_DEVICE_STORAGE: Lazy<Arc<UsbDescDevice>> = Lazy::new(|| {
Arc::new(UsbDescDevice {
device_desc: UsbDeviceDescriptor {
bLength: USB_DT_DEVICE_SIZE,
bDescriptorType: USB_DT_DEVICE,
idVendor: USB_STORAGE_VENDOR_ID,
idProduct: 0x0001,
bcdDevice: 0,
iManufacturer: STR_MANUFACTURER_INDEX,
iProduct: STR_PRODUCT_STORAGE_INDEX,
iSerialNumber: STR_SERIAL_STORAGE_INDEX,
bcdUSB: 0x0200,
bDeviceClass: 0,
bDeviceSubClass: 0,
bDeviceProtocol: 0,
bMaxPacketSize0: 64,
bNumConfigurations: 1,
},
configs: vec![Arc::new(UsbDescConfig {
config_desc: UsbConfigDescriptor {
bLength: USB_DT_CONFIG_SIZE,
bDescriptorType: USB_DT_CONFIGURATION,
wTotalLength: 0,
bNumInterfaces: 1,
bConfigurationValue: 1,
iConfiguration: STR_CONFIG_STORAGE_HIGH_INDEX,
bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER,
bMaxPower: 50,
},
iad_desc: vec![],
interfaces: vec![DESC_IFACE_STORAGE.clone()],
})],
})
});
// Storage interface descriptor
static DESC_IFACE_STORAGE: Lazy<Arc<UsbDescIface>> = Lazy::new(|| {
Arc::new(UsbDescIface {
interface_desc: UsbInterfaceDescriptor {
bLength: USB_DT_INTERFACE_SIZE,
bDescriptorType: USB_DT_INTERFACE,
bInterfaceNumber: 0,
bAlternateSetting: 0,
bNumEndpoints: 2,
bInterfaceClass: USB_CLASS_MASS_STORAGE,
bInterfaceSubClass: 0x06, // SCSI
bInterfaceProtocol: 0x50, // Bulk-only
iInterface: 0,
},
other_desc: vec![],
endpoints: vec![
Arc::new(UsbDescEndpoint {
endpoint_desc: UsbEndpointDescriptor {
bLength: USB_DT_ENDPOINT_SIZE,
bDescriptorType: USB_DT_ENDPOINT,
bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | 0x01,
bmAttributes: USB_ENDPOINT_ATTR_BULK,
wMaxPacketSize: 512,
bInterval: 0,
},
extra: Vec::new(),
}),
Arc::new(UsbDescEndpoint {
endpoint_desc: UsbEndpointDescriptor {
bLength: USB_DT_ENDPOINT_SIZE,
bDescriptorType: USB_DT_ENDPOINT,
bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | 0x02,
bmAttributes: USB_ENDPOINT_ATTR_BULK,
wMaxPacketSize: 512,
bInterval: 0,
},
extra: Vec::new(),
}),
],
})
});
// CRC16 of "STRATOVIRT"
const USB_STORAGE_VENDOR_ID: u16 = 0xB74C;
// String descriptor index
const STR_MANUFACTURER_INDEX: u8 = 1;
const STR_PRODUCT_STORAGE_INDEX: u8 = 2;
const STR_SERIAL_STORAGE_INDEX: u8 = 3;
const STR_CONFIG_STORAGE_HIGH_INDEX: u8 = 5;
// String descriptor
const DESC_STRINGS: [&str; 7] = [
"",
"StratoVirt",
"StratoVirt USB Storage",
"3",
"Full speed config (usb 1.1)",
"High speed config (usb 2.0)",
"Super speed config (usb 3.0)",
];
pub const GET_MAX_LUN: u8 = 0xfe;
pub const MASS_STORAGE_RESET: u8 = 0xff;
pub const CBW_SIGNATURE: u32 = 0x43425355;
pub const CSW_SIGNATURE: u32 = 0x53425355;
pub const CBW_FLAG_IN: u8 = 1 << 7;
pub const CBW_FLAG_OUT: u8 = 0;
pub const CBW_SIZE: u8 = 31;
pub const CSW_SIZE: u8 = 13;
// USB-storage has only target 0 and lun 0.
const USB_STORAGE_SCSI_LUN_ID: u8 = 0;
struct UsbStorageState {
mode: UsbMsdMode,
cbw: UsbMsdCbw,
csw: UsbMsdCsw,
cdb: Option<[u8; SCSI_CMD_BUF_SIZE]>,
iovec_len: u32,
}
impl ScsiRequestOps for UsbMsdCsw {
fn scsi_request_complete_cb(&mut self, status: u8, _: Option<ScsiSense>) -> Result<()> {
if status != GOOD {
self.status = UsbMsdCswStatus::Failed as u8;
}
Ok(())
}
}
impl UsbStorageState {
fn new() -> Self {
UsbStorageState {
mode: UsbMsdMode::Cbw,
cbw: UsbMsdCbw::default(),
csw: UsbMsdCsw::new(),
cdb: None,
iovec_len: 0,
}
}
/// Check if there exists SCSI CDB.
///
/// # Arguments
///
/// `exist` - Expected existence status.
///
/// Return Error if expected existence status is not equal to the actual situation.
fn check_cdb_exist(&self, exist: bool) -> Result<()> {
if exist {
self.cdb.with_context(|| "No scsi CDB can be executed")?;
} else if self.cdb.is_some() {
bail!(
"Another request has not been done! cdb {:x?}",
self.cdb.unwrap()
);
}
Ok(())
}
/// Check if Iovec is empty.
///
/// # Arguments
///
/// `empty` - Expected status. If true, expect empty iovec.
///
/// Return Error if expected iovec status is not equal to the actual situation.
fn check_iovec_empty(&self, empty: bool) -> Result<()> {
if empty != (self.iovec_len == 0) {
match empty {
true => {
bail!(
"Another request has not been done! Data buffer length {}.",
self.iovec_len
);
}
false => {
bail!("Missing data buffer!");
}
};
}
Ok(())
}
}
/// USB storage device.
pub struct UsbStorage {
base: UsbDeviceBase,
state: UsbStorageState,
/// USB controller used to notify controller to transfer data.
cntlr: Option<Weak<Mutex<XhciDevice>>>,
/// Configuration of the USB storage device.
pub config: UsbStorageConfig,
/// Scsi bus attached to this usb-storage device.
scsi_bus: Arc<Mutex<ScsiBus>>,
/// Effective scsi backend.
// Note: scsi device should attach to scsi bus. Logically, scsi device should not be placed in
// UsbStorage. But scsi device is needed in processing scsi request. Because the three
// (usb-storage/scsi bus/scsi device) correspond one-to-one, add scsi device member here
// for the execution efficiency (No need to find a unique device from the hash table of the
// unique bus).
scsi_dev: Arc<Mutex<ScsiDevice>>,
}
#[derive(Debug)]
enum UsbMsdMode {
Cbw,
DataOut,
DataIn,
Csw,
}
pub enum UsbMsdCswStatus {
Passed,
Failed,
PhaseError,
}
#[derive(Debug, Default)]
struct UsbMsdCbw {
sig: u32,
tag: u32,
data_len: u32,
flags: u8,
lun: u8,
cmd_len: u8,
cmd: [u8; 16],
}
impl UsbMsdCbw {
fn convert(&mut self, data: &[u8]) {
self.sig = LittleEndian::read_u32(&data[0..4]);
self.tag = LittleEndian::read_u32(&data[4..8]);
self.data_len = LittleEndian::read_u32(&data[8..12]);
self.flags = data[12];
self.lun = data[13];
self.cmd_len = data[14];
self.cmd.copy_from_slice(&data[15..31]);
}
}
#[derive(Debug, Copy, Clone)]
struct UsbMsdCsw {
sig: u32,
tag: u32,
residue: u32,
status: u8,
}
impl UsbMsdCsw {
fn new() -> Self {
UsbMsdCsw {
sig: CSW_SIGNATURE,
tag: 0,
residue: 0,
status: 0,
}
}
fn convert(&mut self, data: &mut [u8]) {
LittleEndian::write_u32(&mut data[0..4], self.sig);
LittleEndian::write_u32(&mut data[4..8], self.tag);
LittleEndian::write_u32(&mut data[8..12], self.residue);
data[12] = self.status;
}
}
impl UsbStorage {
pub fn new(
config: UsbStorageConfig,
drive_files: Arc<Mutex<HashMap<String, DriveFile>>>,
) -> Self {
let scsi_type = match &config.media as &str {
"disk" => SCSI_TYPE_DISK,
_ => SCSI_TYPE_ROM,
};
Self {
base: UsbDeviceBase::new(config.id.clone().unwrap(), USB_DEVICE_BUFFER_DEFAULT_LEN),
state: UsbStorageState::new(),
cntlr: None,
config: config.clone(),
scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))),
scsi_dev: Arc::new(Mutex::new(ScsiDevice::new(
config.scsi_cfg,
scsi_type,
drive_files,
))),
}
}
fn handle_control_packet(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) {
match device_req.request_type {
USB_ENDPOINT_OUT_REQUEST => {
if device_req.request == USB_REQUEST_CLEAR_FEATURE {
return;
}
}
USB_INTERFACE_CLASS_OUT_REQUEST => {
if device_req.request == MASS_STORAGE_RESET {
self.state.mode = UsbMsdMode::Cbw;
return;
}
}
USB_INTERFACE_CLASS_IN_REQUEST => {
if device_req.request == GET_MAX_LUN {
// TODO: Now only supports 1 LUN.
let maxlun = USB_STORAGE_SCSI_LUN_ID;
self.base.data_buf[0] = maxlun;
packet.actual_length = 1;
return;
}
}
_ => {}
}
error!("Unhandled USB Storage request {}", device_req.request);
packet.status = UsbPacketStatus::Stall;
}
fn handle_token_out(&mut self, packet: &mut UsbPacket) -> Result<()> {
if packet.ep_number != 2 {
bail!("Error ep_number {}!", packet.ep_number);
}
match self.state.mode {
UsbMsdMode::Cbw => {
if packet.get_iovecs_size() < CBW_SIZE as u64 {
bail!("Bad CBW size {}", packet.get_iovecs_size());
}
self.state.check_cdb_exist(false)?;
let mut cbw_buf = [0_u8; CBW_SIZE as usize];
packet.transfer_packet(&mut cbw_buf, CBW_SIZE as usize);
self.state.cbw.convert(&cbw_buf);
trace::usb_storage_handle_token_out(&self.state.cbw);
if self.state.cbw.sig != CBW_SIGNATURE {
bail!("Bad signature {:x}", self.state.cbw.sig);
}
if self.state.cbw.lun != USB_STORAGE_SCSI_LUN_ID {
bail!(
"Bad lun id {:x}. Usb-storage only supports lun id 0!",
self.state.cbw.lun
);
}
self.state.cdb = Some(self.state.cbw.cmd);
if self.state.cbw.data_len == 0 {
self.handle_scsi_request(packet)?;
self.state.mode = UsbMsdMode::Csw;
} else if self.state.cbw.flags & CBW_FLAG_IN == CBW_FLAG_IN {
self.state.mode = UsbMsdMode::DataIn;
} else {
self.state.mode = UsbMsdMode::DataOut;
}
}
UsbMsdMode::DataOut => {
self.handle_data_inout_packet(packet, UsbMsdMode::DataOut)?;
}
_ => {
bail!(
"Unexpected token out. Expected mode {:?} packet.",
self.state.mode
);
}
}
Ok(())
}
fn handle_token_in(&mut self, packet: &mut UsbPacket) -> Result<()> {
if packet.ep_number != 1 {
bail!("Error ep_number {}!", packet.ep_number);
}
match self.state.mode {
UsbMsdMode::DataOut => {
bail!("Not supported usb packet(Token_in and data_out).");
}
UsbMsdMode::Csw => {
if packet.get_iovecs_size() < CSW_SIZE as u64 {
bail!("Bad CSW size {}", packet.get_iovecs_size());
}
self.state.check_cdb_exist(true)?;
self.state.check_iovec_empty(self.state.cbw.data_len == 0)?;
let mut csw_buf = [0_u8; CSW_SIZE as usize];
self.state.csw.tag = self.state.cbw.tag;
self.state.csw.convert(&mut csw_buf);
trace::usb_storage_handle_token_in(&self.state.csw);
packet.transfer_packet(&mut csw_buf, CSW_SIZE as usize);
// Reset UsbStorageState.
self.state = UsbStorageState::new();
}
UsbMsdMode::DataIn => {
self.handle_data_inout_packet(packet, UsbMsdMode::DataIn)?;
}
_ => {
bail!(
"Unexpected token in. Expected mode {:?} packet.",
self.state.mode
);
}
}
Ok(())
}
fn handle_data_inout_packet(&mut self, packet: &mut UsbPacket, mode: UsbMsdMode) -> Result<()> {
self.state.check_cdb_exist(true)?;
self.state.check_iovec_empty(true)?;
let iovec_len = packet.get_iovecs_size() as u32;
if iovec_len < self.state.cbw.data_len {
bail!(
"Insufficient transmission buffer, transfer size {}, buffer size {}, MSD mode {:?}!",
self.state.cbw.data_len,
iovec_len,
mode,
);
}
self.state.iovec_len = iovec_len;
self.handle_scsi_request(packet)?;
packet.actual_length = iovec_len;
self.state.mode = UsbMsdMode::Csw;
trace::usb_storage_handle_data_inout_packet(iovec_len);
Ok(())
}
// Handle scsi request and save result in self.csw for next CSW packet.
fn handle_scsi_request(&mut self, packet: &mut UsbPacket) -> Result<()> {
self.state
.cdb
.with_context(|| "No scsi CDB can be executed")?;
let csw = Box::new(UsbMsdCsw::new());
let sreq = ScsiRequest::new(
self.state.cdb.unwrap(),
0,
packet.iovecs.clone(),
self.state.iovec_len,
self.scsi_dev.clone(),
csw,
)
.with_context(|| "Error in creating scsirequest.")?;
if sreq.cmd.xfer > sreq.datalen && sreq.cmd.mode != ScsiXferMode::ScsiXferNone {
// Wrong USB packet which doesn't provide enough datain/dataout buffer.
bail!(
"command {:x} requested data's length({}), provided buffer length({})",
sreq.cmd.op,
sreq.cmd.xfer,
sreq.datalen
);
}
let sreq_h = match sreq.opstype {
EMULATE_SCSI_OPS => sreq.emulate_execute(),
_ => sreq.execute(),
}
.with_context(|| "Error in executing scsi request.")?;
let csw_h = &sreq_h.lock().unwrap().upper_req;
let csw = csw_h.as_ref().as_any().downcast_ref::<UsbMsdCsw>().unwrap();
self.state.csw = *csw;
trace::usb_storage_handle_scsi_request(csw);
Ok(())
}
}
impl UsbDevice for UsbStorage {
fn usb_device_base(&self) -> &UsbDeviceBase {
&self.base
}
fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase {
&mut self.base
}
fn realize(mut self) -> Result<Arc<Mutex<dyn UsbDevice>>> {
self.base.reset_usb_endpoint();
self.base.speed = USB_SPEED_HIGH;
let mut s: Vec<String> = DESC_STRINGS.iter().map(|&s| s.to_string()).collect();
let prefix = &s[STR_SERIAL_STORAGE_INDEX as usize];
s[STR_SERIAL_STORAGE_INDEX as usize] = self.base.generate_serial_number(prefix);
self.base.init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?;
// NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not
// supported.
let mut locked_scsi_dev = self.scsi_dev.lock().unwrap();
locked_scsi_dev.realize(None)?;
drop(locked_scsi_dev);
self.scsi_bus
.lock()
.unwrap()
.devices
.insert((0, 0), self.scsi_dev.clone());
let storage: Arc<Mutex<UsbStorage>> = Arc::new(Mutex::new(self));
Ok(storage)
}
fn reset(&mut self) {
info!("Storage device reset");
self.base.remote_wakeup = 0;
self.base.addr = 0;
self.state = UsbStorageState::new();
}
fn handle_control(&mut self, packet: &Arc<Mutex<UsbPacket>>, device_req: &UsbDeviceRequest) {
let mut locked_packet = packet.lock().unwrap();
match self
.base
.handle_control_for_descriptor(&mut locked_packet, device_req)
{
Ok(handled) => {
if handled {
trace::usb_storage_handle_control();
return;
}
self.handle_control_packet(&mut locked_packet, device_req)
}
Err(e) => {
warn!("Storage descriptor error {:?}", e);
locked_packet.status = UsbPacketStatus::Stall;
}
}
}
fn handle_data(&mut self, packet: &Arc<Mutex<UsbPacket>>) {
let mut locked_packet = packet.lock().unwrap();
trace::usb_storage_handle_data(
locked_packet.ep_number,
locked_packet.pid,
&self.state.mode,
);
let result = match locked_packet.pid as u8 {
USB_TOKEN_OUT => self.handle_token_out(&mut locked_packet),
USB_TOKEN_IN => self.handle_token_in(&mut locked_packet),
_ => Err(anyhow!("Bad token!")),
};
if let Err(e) = result {
warn!(
"USB-storage {}: handle data error: {:?}",
self.device_id(),
e
);
locked_packet.status = UsbPacketStatus::Stall;
}
}
fn set_controller(&mut self, cntlr: Weak<Mutex<XhciDevice>>) {
self.cntlr = Some(cntlr);
}
fn get_controller(&self) -> Option<Weak<Mutex<XhciDevice>>> {
self.cntlr.clone()
}
fn get_wakeup_endpoint(&self) -> &UsbEndpoint {
self.base.get_endpoint(true, 1)
}
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/openeuler-stratovirt.git
git@gitlife.ru:oschina-mirror/openeuler-stratovirt.git
oschina-mirror
openeuler-stratovirt
openeuler-stratovirt
master