]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PvScsiDxe/PvScsi.c
OvmfPkg/PvScsiDxe: Translate Target & LUN to/from DevicePath
[mirror_edk2.git] / OvmfPkg / PvScsiDxe / PvScsi.c
1 /** @file
2
3 This driver produces Extended SCSI Pass Thru Protocol instances for
4 pvscsi devices.
5
6 Copyright (C) 2020, Oracle and/or its affiliates.
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include <IndustryStandard/Pci.h>
13 #include <IndustryStandard/PvScsi.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/MemoryAllocationLib.h>
16 #include <Library/UefiBootServicesTableLib.h>
17 #include <Library/UefiLib.h>
18 #include <Protocol/PciIo.h>
19 #include <Uefi/UefiSpec.h>
20
21 #include "PvScsi.h"
22
23 //
24 // Higher versions will be used before lower, 0x10-0xffffffef is the version
25 // range for IHV (Indie Hardware Vendors)
26 //
27 #define PVSCSI_BINDING_VERSION 0x10
28
29 //
30 // Ext SCSI Pass Thru utilities
31 //
32
33 /**
34 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
35 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
36 **/
37 STATIC
38 BOOLEAN
39 IsTargetInitialized (
40 IN UINT8 *Target
41 )
42 {
43 UINTN Idx;
44
45 for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {
46 if (Target[Idx] != 0xFF) {
47 return TRUE;
48 }
49 }
50 return FALSE;
51 }
52
53 //
54 // Ext SCSI Pass Thru
55 //
56
57 STATIC
58 EFI_STATUS
59 EFIAPI
60 PvScsiPassThru (
61 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
62 IN UINT8 *Target,
63 IN UINT64 Lun,
64 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
65 IN EFI_EVENT Event OPTIONAL
66 )
67 {
68 return EFI_UNSUPPORTED;
69 }
70
71 STATIC
72 EFI_STATUS
73 EFIAPI
74 PvScsiGetNextTargetLun (
75 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
76 IN OUT UINT8 **Target,
77 IN OUT UINT64 *Lun
78 )
79 {
80 UINT8 *TargetPtr;
81 UINT8 LastTarget;
82 PVSCSI_DEV *Dev;
83
84 if (Target == NULL) {
85 return EFI_INVALID_PARAMETER;
86 }
87
88 //
89 // The Target input parameter is unnecessarily a pointer-to-pointer
90 //
91 TargetPtr = *Target;
92
93 //
94 // If target not initialized, return first target & LUN
95 //
96 if (!IsTargetInitialized (TargetPtr)) {
97 ZeroMem (TargetPtr, TARGET_MAX_BYTES);
98 *Lun = 0;
99 return EFI_SUCCESS;
100 }
101
102 //
103 // We only use first byte of target identifer
104 //
105 LastTarget = *TargetPtr;
106
107 //
108 // Increment (target, LUN) pair if valid on input
109 //
110 Dev = PVSCSI_FROM_PASS_THRU (This);
111 if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {
112 return EFI_INVALID_PARAMETER;
113 }
114
115 if (*Lun < Dev->MaxLun) {
116 ++*Lun;
117 return EFI_SUCCESS;
118 }
119
120 if (LastTarget < Dev->MaxTarget) {
121 *Lun = 0;
122 ++LastTarget;
123 *TargetPtr = LastTarget;
124 return EFI_SUCCESS;
125 }
126
127 return EFI_NOT_FOUND;
128 }
129
130 STATIC
131 EFI_STATUS
132 EFIAPI
133 PvScsiBuildDevicePath (
134 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
135 IN UINT8 *Target,
136 IN UINT64 Lun,
137 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
138 )
139 {
140 UINT8 TargetValue;
141 PVSCSI_DEV *Dev;
142 SCSI_DEVICE_PATH *ScsiDevicePath;
143
144 if (DevicePath == NULL) {
145 return EFI_INVALID_PARAMETER;
146 }
147
148 //
149 // We only use first byte of target identifer
150 //
151 TargetValue = *Target;
152
153 Dev = PVSCSI_FROM_PASS_THRU (This);
154 if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun) {
155 return EFI_NOT_FOUND;
156 }
157
158 ScsiDevicePath = AllocatePool (sizeof (*ScsiDevicePath));
159 if (ScsiDevicePath == NULL) {
160 return EFI_OUT_OF_RESOURCES;
161 }
162
163 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
164 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;
165 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);
166 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);
167 ScsiDevicePath->Pun = TargetValue;
168 ScsiDevicePath->Lun = (UINT16)Lun;
169
170 *DevicePath = &ScsiDevicePath->Header;
171 return EFI_SUCCESS;
172 }
173
174 STATIC
175 EFI_STATUS
176 EFIAPI
177 PvScsiGetTargetLun (
178 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
179 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
180 OUT UINT8 **Target,
181 OUT UINT64 *Lun
182 )
183 {
184 SCSI_DEVICE_PATH *ScsiDevicePath;
185 PVSCSI_DEV *Dev;
186
187 if (DevicePath == NULL || Target == NULL || *Target == NULL || Lun == NULL) {
188 return EFI_INVALID_PARAMETER;
189 }
190
191 if (DevicePath->Type != MESSAGING_DEVICE_PATH ||
192 DevicePath->SubType != MSG_SCSI_DP) {
193 return EFI_UNSUPPORTED;
194 }
195
196 ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;
197 Dev = PVSCSI_FROM_PASS_THRU (This);
198 if (ScsiDevicePath->Pun > Dev->MaxTarget ||
199 ScsiDevicePath->Lun > Dev->MaxLun) {
200 return EFI_NOT_FOUND;
201 }
202
203 //
204 // We only use first byte of target identifer
205 //
206 **Target = (UINT8)ScsiDevicePath->Pun;
207 ZeroMem (*Target + 1, TARGET_MAX_BYTES - 1);
208 *Lun = ScsiDevicePath->Lun;
209
210 return EFI_SUCCESS;
211 }
212
213 STATIC
214 EFI_STATUS
215 EFIAPI
216 PvScsiResetChannel (
217 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
218 )
219 {
220 return EFI_UNSUPPORTED;
221 }
222
223 STATIC
224 EFI_STATUS
225 EFIAPI
226 PvScsiResetTargetLun (
227 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
228 IN UINT8 *Target,
229 IN UINT64 Lun
230 )
231 {
232 return EFI_UNSUPPORTED;
233 }
234
235 STATIC
236 EFI_STATUS
237 EFIAPI
238 PvScsiGetNextTarget (
239 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
240 IN OUT UINT8 **Target
241 )
242 {
243 UINT8 *TargetPtr;
244 UINT8 LastTarget;
245 PVSCSI_DEV *Dev;
246
247 if (Target == NULL) {
248 return EFI_INVALID_PARAMETER;
249 }
250
251 //
252 // The Target input parameter is unnecessarily a pointer-to-pointer
253 //
254 TargetPtr = *Target;
255
256 //
257 // If target not initialized, return first target
258 //
259 if (!IsTargetInitialized (TargetPtr)) {
260 ZeroMem (TargetPtr, TARGET_MAX_BYTES);
261 return EFI_SUCCESS;
262 }
263
264 //
265 // We only use first byte of target identifer
266 //
267 LastTarget = *TargetPtr;
268
269 //
270 // Increment target if valid on input
271 //
272 Dev = PVSCSI_FROM_PASS_THRU (This);
273 if (LastTarget > Dev->MaxTarget) {
274 return EFI_INVALID_PARAMETER;
275 }
276
277 if (LastTarget < Dev->MaxTarget) {
278 ++LastTarget;
279 *TargetPtr = LastTarget;
280 return EFI_SUCCESS;
281 }
282
283 return EFI_NOT_FOUND;
284 }
285
286 STATIC
287 EFI_STATUS
288 PvScsiInit (
289 IN OUT PVSCSI_DEV *Dev
290 )
291 {
292 //
293 // Init configuration
294 //
295 Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);
296 Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);
297
298 //
299 // Populate the exported interface's attributes
300 //
301 Dev->PassThru.Mode = &Dev->PassThruMode;
302 Dev->PassThru.PassThru = &PvScsiPassThru;
303 Dev->PassThru.GetNextTargetLun = &PvScsiGetNextTargetLun;
304 Dev->PassThru.BuildDevicePath = &PvScsiBuildDevicePath;
305 Dev->PassThru.GetTargetLun = &PvScsiGetTargetLun;
306 Dev->PassThru.ResetChannel = &PvScsiResetChannel;
307 Dev->PassThru.ResetTargetLun = &PvScsiResetTargetLun;
308 Dev->PassThru.GetNextTarget = &PvScsiGetNextTarget;
309
310 //
311 // AdapterId is a target for which no handle will be created during bus scan.
312 // Prevent any conflict with real devices.
313 //
314 Dev->PassThruMode.AdapterId = MAX_UINT32;
315
316 //
317 // Set both physical and logical attributes for non-RAID SCSI channel
318 //
319 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
320 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
321
322 //
323 // No restriction on transfer buffer alignment
324 //
325 Dev->PassThruMode.IoAlign = 0;
326
327 return EFI_SUCCESS;
328 }
329
330 STATIC
331 VOID
332 PvScsiUninit (
333 IN OUT PVSCSI_DEV *Dev
334 )
335 {
336 // Currently nothing to do here
337 }
338
339 //
340 // Driver Binding
341 //
342
343 STATIC
344 EFI_STATUS
345 EFIAPI
346 PvScsiDriverBindingSupported (
347 IN EFI_DRIVER_BINDING_PROTOCOL *This,
348 IN EFI_HANDLE ControllerHandle,
349 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
350 )
351 {
352 EFI_STATUS Status;
353 EFI_PCI_IO_PROTOCOL *PciIo;
354 PCI_TYPE00 Pci;
355
356 Status = gBS->OpenProtocol (
357 ControllerHandle,
358 &gEfiPciIoProtocolGuid,
359 (VOID **)&PciIo,
360 This->DriverBindingHandle,
361 ControllerHandle,
362 EFI_OPEN_PROTOCOL_BY_DRIVER
363 );
364 if (EFI_ERROR (Status)) {
365 return Status;
366 }
367
368 Status = PciIo->Pci.Read (
369 PciIo,
370 EfiPciIoWidthUint32,
371 0,
372 sizeof (Pci) / sizeof (UINT32),
373 &Pci
374 );
375 if (EFI_ERROR (Status)) {
376 goto Done;
377 }
378
379 if ((Pci.Hdr.VendorId != PCI_VENDOR_ID_VMWARE) ||
380 (Pci.Hdr.DeviceId != PCI_DEVICE_ID_VMWARE_PVSCSI)) {
381 Status = EFI_UNSUPPORTED;
382 goto Done;
383 }
384
385 Status = EFI_SUCCESS;
386
387 Done:
388 gBS->CloseProtocol (
389 ControllerHandle,
390 &gEfiPciIoProtocolGuid,
391 This->DriverBindingHandle,
392 ControllerHandle
393 );
394
395 return Status;
396 }
397
398 STATIC
399 EFI_STATUS
400 EFIAPI
401 PvScsiDriverBindingStart (
402 IN EFI_DRIVER_BINDING_PROTOCOL *This,
403 IN EFI_HANDLE ControllerHandle,
404 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
405 )
406 {
407 PVSCSI_DEV *Dev;
408 EFI_STATUS Status;
409
410 Dev = (PVSCSI_DEV *) AllocateZeroPool (sizeof (*Dev));
411 if (Dev == NULL) {
412 return EFI_OUT_OF_RESOURCES;
413 }
414
415 Status = PvScsiInit (Dev);
416 if (EFI_ERROR (Status)) {
417 goto FreePvScsi;
418 }
419
420 //
421 // Setup complete, attempt to export the driver instance's PassThru interface
422 //
423 Dev->Signature = PVSCSI_SIG;
424 Status = gBS->InstallProtocolInterface (
425 &ControllerHandle,
426 &gEfiExtScsiPassThruProtocolGuid,
427 EFI_NATIVE_INTERFACE,
428 &Dev->PassThru
429 );
430 if (EFI_ERROR (Status)) {
431 goto UninitDev;
432 }
433
434 return EFI_SUCCESS;
435
436 UninitDev:
437 PvScsiUninit (Dev);
438
439 FreePvScsi:
440 FreePool (Dev);
441
442 return Status;
443 }
444
445 STATIC
446 EFI_STATUS
447 EFIAPI
448 PvScsiDriverBindingStop (
449 IN EFI_DRIVER_BINDING_PROTOCOL *This,
450 IN EFI_HANDLE ControllerHandle,
451 IN UINTN NumberOfChildren,
452 IN EFI_HANDLE *ChildHandleBuffer
453 )
454 {
455 EFI_STATUS Status;
456 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
457 PVSCSI_DEV *Dev;
458
459 Status = gBS->OpenProtocol (
460 ControllerHandle,
461 &gEfiExtScsiPassThruProtocolGuid,
462 (VOID **)&PassThru,
463 This->DriverBindingHandle,
464 ControllerHandle,
465 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
466 );
467 if (EFI_ERROR (Status)) {
468 return Status;
469 }
470
471 Dev = PVSCSI_FROM_PASS_THRU (PassThru);
472
473 Status = gBS->UninstallProtocolInterface (
474 ControllerHandle,
475 &gEfiExtScsiPassThruProtocolGuid,
476 &Dev->PassThru
477 );
478 if (EFI_ERROR (Status)) {
479 return Status;
480 }
481
482 PvScsiUninit (Dev);
483
484 FreePool (Dev);
485
486 return EFI_SUCCESS;
487 }
488
489 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {
490 &PvScsiDriverBindingSupported,
491 &PvScsiDriverBindingStart,
492 &PvScsiDriverBindingStop,
493 PVSCSI_BINDING_VERSION,
494 NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
495 NULL // DriverBindingHandle, filled as well
496 };
497
498 //
499 // Component Name
500 //
501
502 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
503 { "eng;en", L"PVSCSI Host Driver" },
504 { NULL, NULL }
505 };
506
507 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName;
508
509 STATIC
510 EFI_STATUS
511 EFIAPI
512 PvScsiGetDriverName (
513 IN EFI_COMPONENT_NAME_PROTOCOL *This,
514 IN CHAR8 *Language,
515 OUT CHAR16 **DriverName
516 )
517 {
518 return LookupUnicodeString2 (
519 Language,
520 This->SupportedLanguages,
521 mDriverNameTable,
522 DriverName,
523 (BOOLEAN)(This == &mComponentName) // Iso639Language
524 );
525 }
526
527 STATIC
528 EFI_STATUS
529 EFIAPI
530 PvScsiGetDeviceName (
531 IN EFI_COMPONENT_NAME_PROTOCOL *This,
532 IN EFI_HANDLE DeviceHandle,
533 IN EFI_HANDLE ChildHandle,
534 IN CHAR8 *Language,
535 OUT CHAR16 **ControllerName
536 )
537 {
538 return EFI_UNSUPPORTED;
539 }
540
541 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
542 &PvScsiGetDriverName,
543 &PvScsiGetDeviceName,
544 "eng" // SupportedLanguages, ISO 639-2 language codes
545 };
546
547 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
548 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &PvScsiGetDriverName,
549 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &PvScsiGetDeviceName,
550 "en" // SupportedLanguages, RFC 4646 language codes
551 };
552
553 //
554 // Entry Point
555 //
556
557 EFI_STATUS
558 EFIAPI
559 PvScsiEntryPoint (
560 IN EFI_HANDLE ImageHandle,
561 IN EFI_SYSTEM_TABLE *SystemTable
562 )
563 {
564 return EfiLibInstallDriverBindingComponentName2 (
565 ImageHandle,
566 SystemTable,
567 &mPvScsiDriverBinding,
568 ImageHandle,
569 &mComponentName,
570 &mComponentName2
571 );
572 }