1use super::ScsiError;
5use super::SimpleScsiDisk;
6use crate::UNMAP_RANGE_DESCRIPTOR_COUNT_MAX;
7use crate::VHDMP_MAX_WRITE_SAME_LENGTH_BYTES;
8use crate::scsi;
9use guestmem::MemoryWrite;
10use guid::Guid;
11use scsi::AdditionalSenseCode;
12use scsi_buffers::RequestBuffers;
13use scsi_core::Request;
14use zerocopy::FromBytes;
15use zerocopy::FromZeros;
16use zerocopy::Immutable;
17use zerocopy::IntoBytes;
18use zerocopy::KnownLayout;
19
20type U16BE = zerocopy::byteorder::U16<zerocopy::byteorder::BigEndian>;
21type U32BE = zerocopy::byteorder::U32<zerocopy::byteorder::BigEndian>;
22type U64BE = zerocopy::byteorder::U64<zerocopy::byteorder::BigEndian>;
23
24#[repr(C)]
25#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
26struct VhdmpVpdWindowsBlockDeviceRodLimitsEcopDescriptorHeader {
27 pub ecop_descriptor_type: U16BE,
28 pub ecop_descriptor_length: U16BE,
29}
30
31#[repr(C)]
32#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
33struct VhdmpVpdWindowsBlockDeviceRodLimitsEcopDescriptor {
34 pub header: VhdmpVpdWindowsBlockDeviceRodLimitsEcopDescriptorHeader,
35 pub microsoft_signature: u8, pub microsoft_command_id_and_versionsion: u8, pub populate_token_and_write_using_token_command_op_code: u8, pub receive_rod_token_information_command_op_code: u8, pub reserved1: [u8; 2],
40 pub maximum_range_descriptors: U16BE,
41 pub maximum_inactivity_timer: U32BE,
42 pub default_inactivity_timer: U32BE,
43 pub maximum_token_transfer_size: U64BE,
44 pub optimal_transfer_count: U64BE,
45}
46
47pub const INQUIRY_DATA_TEMPLATE: scsi::InquiryData = scsi::InquiryData {
48 header: scsi::InquiryDataHeader {
49 device_type: scsi::DIRECT_ACCESS_DEVICE,
53 flags2: scsi::InquiryDataFlag2::new()
59 .with_device_type_modifier(0x00)
60 .with_removable_media(false),
61 versions: scsi::T10_VERSION_SPC3,
62 flags3: scsi::InquiryDataFlag3::new()
63 .with_response_data_format(scsi::T10_RESPONSE_DATA_SPC3)
64 .with_aerc(false)
65 .with_hi_support(false)
66 .with_norm_aca(false)
67 .with_reserved_bit(false),
68 additional_length: (size_of::<scsi::InquiryData>() - size_of::<scsi::InquiryDataHeader>())
69 as u8,
70 },
71 reserved: [0; 2],
72 misc: 0,
73 vendor_id: *b"Msft ",
74 product_id: *b"Virtual Disk ",
75 product_revision_level: *b"1.0 ",
76 vendor_specific: [0; 20],
77 reserved3: [0; 2],
78 version_descriptors: [0; 8],
79 reserved4: [0; 30],
80};
81
82fn write_vpd_page<T: ?Sized + IntoBytes + Immutable + KnownLayout>(
87 external_data: &RequestBuffers<'_>,
88 allocation_length: usize,
89 page_code: u8,
90 page_data: &T,
91) -> Result<usize, ScsiError> {
92 let header = scsi::VpdPageHeader {
93 device_type: scsi::DIRECT_ACCESS_DEVICE,
94 page_code,
95 reserved: 0,
96 page_length: size_of_val(page_data).try_into().unwrap(),
97 };
98
99 let tx = std::cmp::min(
100 allocation_length,
101 size_of_val(&header) + size_of_val(page_data),
102 );
103
104 let mut writer = external_data.writer();
105 writer
106 .write(header.as_bytes())
107 .map_err(ScsiError::MemoryAccess)?;
108
109 writer
110 .write(&page_data.as_bytes()[..tx - size_of_val(&header)])
111 .map_err(ScsiError::MemoryAccess)?;
112
113 Ok(tx)
114}
115
116impl SimpleScsiDisk {
117 fn handle_vpd_supported_pages(
118 &self,
119 external_data: &RequestBuffers<'_>,
120 allocation_length: usize,
121 ) -> Result<usize, ScsiError> {
122 let mut supported_pages = vec![
123 scsi::VPD_SUPPORTED_PAGES, scsi::VPD_DEVICE_IDENTIFIERS, scsi::VPD_MSFT_PAGING_EXTENT_PROPERTIES, scsi::VPD_MSFT_VIRTUAL_DEVICE_PROPERTIES, ];
128
129 if self.scsi_parameters.support_odx {
130 supported_pages.push(scsi::VPD_THIRD_PARTY_COPY);
131 }
132
133 if self.scsi_parameters.support_unmap {
134 supported_pages.extend([
135 scsi::VPD_BLOCK_DEVICE_CHARACTERISTICS,
136 scsi::VPD_BLOCK_LIMITS,
137 scsi::VPD_LOGICAL_BLOCK_PROVISIONING,
138 ]);
139 }
140
141 if !self.scsi_parameters.serial_number.is_empty() {
142 supported_pages.push(scsi::VPD_SERIAL_NUMBER);
143 }
144
145 supported_pages.sort_unstable();
146
147 write_vpd_page(
148 external_data,
149 allocation_length,
150 scsi::VPD_SUPPORTED_PAGES,
151 supported_pages.as_slice(),
152 )
153 }
154
155 fn handle_vpd_serial_number(
156 &self,
157 external_data: &RequestBuffers<'_>,
158 allocation_length: usize,
159 ) -> Result<usize, ScsiError> {
160 write_vpd_page(
161 external_data,
162 allocation_length,
163 scsi::VPD_SERIAL_NUMBER,
164 self.scsi_parameters.serial_number.as_slice(),
165 )
166 }
167
168 fn handle_vpd_device_identifiers(
169 &self,
170 external_data: &RequestBuffers<'_>,
171 allocation_length: usize,
172 ) -> Result<usize, ScsiError> {
173 #[repr(C)]
174 #[derive(IntoBytes, Immutable, KnownLayout)]
175 struct Ids {
176 t10_id: scsi::VpdT10Id,
177 naa_id: scsi::VpdNaaId,
180 }
181
182 let mut page = Ids {
186 t10_id: scsi::VpdT10Id {
187 header: scsi::VpdIdentificationDescriptor {
188 code_set: scsi::VPD_CODE_SET_BINARY,
189 identifiertype: scsi::VPD_IDENTIFIER_TYPE_VENDOR_ID, reserved3: 0x00,
191 identifier_length: (size_of::<scsi::VpdT10Id>()
192 - size_of::<scsi::VpdIdentificationDescriptor>())
193 as u8,
194 },
195 vendor_id: self.scsi_parameters.identity.vendor_id.into(),
196 context_guid: self.scsi_parameters.disk_id,
197 },
198 naa_id: scsi::VpdNaaId {
199 header: scsi::VpdIdentificationDescriptor {
200 code_set: scsi::VPD_CODE_SET_BINARY,
201 identifiertype: scsi::VPD_IDENTIFIER_TYPE_FCPH_NAME, reserved3: 0x00,
203 identifier_length: (size_of::<scsi::VpdNaaId>()
204 - size_of::<scsi::VpdIdentificationDescriptor>())
205 as u8,
206 },
207 ouid_msb: 0x60, ouid_middle: [0x02, 0x24],
209 ouid_lsb: 0x80,
210 vendor_specific_id: [0; 12],
211 },
212 };
213
214 let id_split_size = page.naa_id.vendor_specific_id.len() / 2;
222 page.naa_id.vendor_specific_id[..id_split_size]
223 .copy_from_slice(&self.scsi_parameters.disk_id.as_bytes()[..id_split_size]);
224 page.naa_id.vendor_specific_id[id_split_size..]
225 .copy_from_slice(&page.t10_id.context_guid[10..]);
226
227 write_vpd_page(
228 external_data,
229 allocation_length,
230 scsi::VPD_DEVICE_IDENTIFIERS,
231 &page,
232 )
233 }
234
235 fn handle_vpd_block_limits(
236 &self,
237 external_data: &RequestBuffers<'_>,
238 allocation_length: usize,
239 ) -> Result<usize, ScsiError> {
240 let max_write_same_length = (self.scsi_parameters.maximum_transfer_length as u64)
241 .min(VHDMP_MAX_WRITE_SAME_LENGTH_BYTES)
242 >> self.sector_shift;
243
244 let optimal_unmap_granularity = self.scsi_parameters.optimal_unmap_sectors.max(1);
248
249 let page = scsi::VpdBlockLimitsDescriptor {
250 reserved0: 0x00,
251 max_compare_and_write_length: 0, max_unmap_lba_count: u32::MAX.into(),
253 max_unmap_block_descriptor_count: u32::from(UNMAP_RANGE_DESCRIPTOR_COUNT_MAX).into(),
254 optimal_unmap_granularity: optimal_unmap_granularity.into(),
255 unmap_granularity_alignment: [0x80, 0x00, 0x00, 0x00], max_write_same_length: max_write_same_length.into(),
257 ..FromZeros::new_zeroed()
258 };
259
260 tracing::debug!(
261 max_lba_count = page.max_unmap_lba_count.get(), max_block_descriptor_count = page.max_unmap_block_descriptor_count.get(),
263 optimal_unmap_granularity,
264 "handle_vpd_block_limits unmap properties",
265 );
266
267 write_vpd_page(
268 external_data,
269 allocation_length,
270 scsi::VPD_BLOCK_LIMITS,
271 &page,
272 )
273 }
274
275 fn handle_vpd_block_device_characteristics(
276 &self,
277 external_data: &RequestBuffers<'_>,
278 allocation_length: usize,
279 ) -> Result<usize, ScsiError> {
280 let page = scsi::VpdBlockDeviceCharacteristicsPage {
281 medium_rotation_rate: self.scsi_parameters.medium_rotation_rate.into(),
282 ..FromZeros::new_zeroed()
283 };
284
285 write_vpd_page(
286 external_data,
287 allocation_length,
288 scsi::VPD_BLOCK_DEVICE_CHARACTERISTICS,
289 &page,
290 )
291 }
292
293 fn handle_vpd_third_party_copy(
294 &self,
295 external_data: &RequestBuffers<'_>,
296 allocation_length: usize,
297 ) -> Result<usize, ScsiError> {
298 const VHDMP_MAX_BYTES_PER_OFFLOAD: u64 = 64 * 1024 * 1024;
325
326 let page = VhdmpVpdWindowsBlockDeviceRodLimitsEcopDescriptor {
328 header: VhdmpVpdWindowsBlockDeviceRodLimitsEcopDescriptorHeader {
329 ecop_descriptor_type: 0.into(),
330 ecop_descriptor_length: ((size_of::<
331 VhdmpVpdWindowsBlockDeviceRodLimitsEcopDescriptor,
332 >() - size_of::<
333 VhdmpVpdWindowsBlockDeviceRodLimitsEcopDescriptorHeader,
334 >()) as u16)
335 .into(),
336 },
337 microsoft_signature: 0x4D, microsoft_command_id_and_versionsion: 0x1F, populate_token_and_write_using_token_command_op_code: 0x83, receive_rod_token_information_command_op_code: 0x84, reserved1: [0; 2],
342 maximum_range_descriptors: 8.into(),
343 maximum_inactivity_timer: 0.into(), default_inactivity_timer: 0.into(),
345 maximum_token_transfer_size: (VHDMP_MAX_BYTES_PER_OFFLOAD >> self.sector_shift).into(),
346 optimal_transfer_count: (VHDMP_MAX_BYTES_PER_OFFLOAD >> self.sector_shift).into(),
347 };
348
349 write_vpd_page(
350 external_data,
351 allocation_length,
352 scsi::VPD_THIRD_PARTY_COPY,
353 &page,
354 )
355 }
356
357 fn handle_vpd_msft_virtual_device_properties(
358 &self,
359 external_data: &RequestBuffers<'_>,
360 allocation_length: usize,
361 ) -> Result<usize, ScsiError> {
362 const VPD_MSFT_VIRTUAL_DEVICE_PROPERTIES_PAGE_SIGNATURE: Guid =
363 guid::guid!("89a98f15-c928-4d8b-94cd-ef51faa99d33");
364
365 let page = scsi::VpdMsftVirtualDevicePropertiesPage {
366 version: 1, flags: 0, reserved: [0; 2],
369 signature: VPD_MSFT_VIRTUAL_DEVICE_PROPERTIES_PAGE_SIGNATURE.into(),
370 };
371
372 write_vpd_page(
373 external_data,
374 allocation_length,
375 scsi::VPD_MSFT_VIRTUAL_DEVICE_PROPERTIES,
376 &page,
377 )
378 }
379
380 fn handle_vpd_msft_paging_extent_properties(
381 &self,
382 external_data: &RequestBuffers<'_>,
383 allocation_length: usize,
384 ) -> Result<usize, ScsiError> {
385 let page = scsi::VpdMsftPagingExtentPropertiesPage {
386 version: 1,
387 mode_select_extension: 1,
388 reserved: [0; 2],
389 };
390
391 write_vpd_page(
392 external_data,
393 allocation_length,
394 scsi::VPD_MSFT_PAGING_EXTENT_PROPERTIES,
395 &page,
396 )
397 }
398
399 fn handle_vpd_logical_block_provisioning(
400 &self,
401 external_data: &RequestBuffers<'_>,
402 allocation_length: usize,
403 sector_count: u64,
404 ) -> Result<usize, ScsiError> {
405 let threshold_exponent = (sector_count >> 32)
413 .next_power_of_two()
414 .trailing_zeros()
415 .max(1) as u8;
416
417 let mut page = scsi::VpdLogicalBlockProvisioningPage {
418 threshold_exponent,
419 flags: 0,
420 provisioning_type: scsi::PROVISIONING_TYPE_THIN,
421 reserved2: 0,
422 };
423
424 if self.scsi_parameters.support_unmap {
425 page.flags = 0x80;
426 }
427
428 write_vpd_page(
429 external_data,
430 allocation_length,
431 scsi::VPD_LOGICAL_BLOCK_PROVISIONING,
432 &page,
433 )
434 }
435
436 fn handle_no_vpd_page(
437 &self,
438 external_data: &RequestBuffers<'_>,
439 allocation_length: usize,
440 ) -> Result<usize, ScsiError> {
441 if allocation_length < size_of::<scsi::InquiryDataHeader>() {
446 return Err(ScsiError::SrbError);
447 }
448
449 let page = scsi::InquiryData {
450 misc: 0x02, vendor_id: self.scsi_parameters.identity.vendor_id.into(),
452 product_id: self.scsi_parameters.identity.product_id.into(),
453 product_revision_level: self.scsi_parameters.identity.product_revision_level.into(),
454 ..INQUIRY_DATA_TEMPLATE
455 };
456
457 let tx = std::cmp::min(allocation_length, size_of_val(&page));
458 external_data
459 .writer()
460 .write(&page.as_bytes()[..tx])
461 .map_err(ScsiError::MemoryAccess)?;
462
463 Ok(tx)
464 }
465
466 pub(crate) fn handle_inquiry(
467 &self,
468 external_data: &RequestBuffers<'_>,
469 request: &Request,
470 sector_count: u64,
471 ) -> Result<usize, ScsiError> {
472 let cdb = scsi::CdbInquiry::read_from_prefix(&request.cdb[..])
473 .unwrap()
474 .0; let allocation_length = cdb.allocation_length.get() as usize;
477 if external_data.len() < allocation_length {
478 return Err(ScsiError::SrbError);
479 }
480
481 if cdb.flags.csd() {
483 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
484 }
485
486 let enable_vpd = cdb.flags.vpd();
488 if cdb.page_code != 0 && !enable_vpd {
489 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
490 }
491
492 if enable_vpd {
493 if allocation_length < size_of::<scsi::VpdPageHeader>() {
494 return Err(ScsiError::SrbError);
495 }
496
497 match cdb.page_code {
498 scsi::VPD_SUPPORTED_PAGES => {
499 self.handle_vpd_supported_pages(external_data, allocation_length)
500 }
501 scsi::VPD_SERIAL_NUMBER if !self.scsi_parameters.serial_number.is_empty() => {
502 self.handle_vpd_serial_number(external_data, allocation_length)
503 }
504 scsi::VPD_DEVICE_IDENTIFIERS => {
505 self.handle_vpd_device_identifiers(external_data, allocation_length)
506 }
507 scsi::VPD_BLOCK_LIMITS if self.scsi_parameters.support_unmap => {
508 self.handle_vpd_block_limits(external_data, allocation_length)
509 }
510 scsi::VPD_BLOCK_DEVICE_CHARACTERISTICS if self.scsi_parameters.support_unmap => {
511 self.handle_vpd_block_device_characteristics(external_data, allocation_length)
512 }
513 scsi::VPD_LOGICAL_BLOCK_PROVISIONING if self.scsi_parameters.support_unmap => self
514 .handle_vpd_logical_block_provisioning(
515 external_data,
516 allocation_length,
517 sector_count,
518 ),
519 scsi::VPD_THIRD_PARTY_COPY if self.scsi_parameters.support_odx => {
520 self.handle_vpd_third_party_copy(external_data, allocation_length)
521 }
522 scsi::VPD_MSFT_VIRTUAL_DEVICE_PROPERTIES => {
523 self.handle_vpd_msft_virtual_device_properties(external_data, allocation_length)
524 }
525 scsi::VPD_MSFT_PAGING_EXTENT_PROPERTIES => {
526 self.handle_vpd_msft_paging_extent_properties(external_data, allocation_length)
527 }
528 n => Err(ScsiError::UnsupportedVpdPageCode(n)),
529 }
530 } else {
531 self.handle_no_vpd_page(external_data, allocation_length)
532 }
533 }
534}