]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PvScsiDxe/PvScsi.c
OvmfPkg/PvScsiDxe: Open PciIo protocol for later use
[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 = gBS->OpenProtocol (
416 ControllerHandle,
417 &gEfiPciIoProtocolGuid,
418 (VOID **)&Dev->PciIo,
419 This->DriverBindingHandle,
420 ControllerHandle,
421 EFI_OPEN_PROTOCOL_BY_DRIVER
422 );
423 if (EFI_ERROR (Status)) {
424 goto FreePvScsi;
425 }
426
427 Status = PvScsiInit (Dev);
428 if (EFI_ERROR (Status)) {
429 goto ClosePciIo;
430 }
431
432 //
433 // Setup complete, attempt to export the driver instance's PassThru interface
434 //
435 Dev->Signature = PVSCSI_SIG;
436 Status = gBS->InstallProtocolInterface (
437 &ControllerHandle,
438 &gEfiExtScsiPassThruProtocolGuid,
439 EFI_NATIVE_INTERFACE,
440 &Dev->PassThru
441 );
442 if (EFI_ERROR (Status)) {
443 goto UninitDev;
444 }
445
446 return EFI_SUCCESS;
447
448 UninitDev:
449 PvScsiUninit (Dev);
450
451 ClosePciIo:
452 gBS->CloseProtocol (
453 ControllerHandle,
454 &gEfiPciIoProtocolGuid,
455 This->DriverBindingHandle,
456 ControllerHandle
457 );
458
459 FreePvScsi:
460 FreePool (Dev);
461
462 return Status;
463 }
464
465 STATIC
466 EFI_STATUS
467 EFIAPI
468 PvScsiDriverBindingStop (
469 IN EFI_DRIVER_BINDING_PROTOCOL *This,
470 IN EFI_HANDLE ControllerHandle,
471 IN UINTN NumberOfChildren,
472 IN EFI_HANDLE *ChildHandleBuffer
473 )
474 {
475 EFI_STATUS Status;
476 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
477 PVSCSI_DEV *Dev;
478
479 Status = gBS->OpenProtocol (
480 ControllerHandle,
481 &gEfiExtScsiPassThruProtocolGuid,
482 (VOID **)&PassThru,
483 This->DriverBindingHandle,
484 ControllerHandle,
485 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
486 );
487 if (EFI_ERROR (Status)) {
488 return Status;
489 }
490
491 Dev = PVSCSI_FROM_PASS_THRU (PassThru);
492
493 Status = gBS->UninstallProtocolInterface (
494 ControllerHandle,
495 &gEfiExtScsiPassThruProtocolGuid,
496 &Dev->PassThru
497 );
498 if (EFI_ERROR (Status)) {
499 return Status;
500 }
501
502 PvScsiUninit (Dev);
503
504 gBS->CloseProtocol (
505 ControllerHandle,
506 &gEfiPciIoProtocolGuid,
507 This->DriverBindingHandle,
508 ControllerHandle
509 );
510
511 FreePool (Dev);
512
513 return EFI_SUCCESS;
514 }
515
516 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {
517 &PvScsiDriverBindingSupported,
518 &PvScsiDriverBindingStart,
519 &PvScsiDriverBindingStop,
520 PVSCSI_BINDING_VERSION,
521 NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
522 NULL // DriverBindingHandle, filled as well
523 };
524
525 //
526 // Component Name
527 //
528
529 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
530 { "eng;en", L"PVSCSI Host Driver" },
531 { NULL, NULL }
532 };
533
534 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName;
535
536 STATIC
537 EFI_STATUS
538 EFIAPI
539 PvScsiGetDriverName (
540 IN EFI_COMPONENT_NAME_PROTOCOL *This,
541 IN CHAR8 *Language,
542 OUT CHAR16 **DriverName
543 )
544 {
545 return LookupUnicodeString2 (
546 Language,
547 This->SupportedLanguages,
548 mDriverNameTable,
549 DriverName,
550 (BOOLEAN)(This == &mComponentName) // Iso639Language
551 );
552 }
553
554 STATIC
555 EFI_STATUS
556 EFIAPI
557 PvScsiGetDeviceName (
558 IN EFI_COMPONENT_NAME_PROTOCOL *This,
559 IN EFI_HANDLE DeviceHandle,
560 IN EFI_HANDLE ChildHandle,
561 IN CHAR8 *Language,
562 OUT CHAR16 **ControllerName
563 )
564 {
565 return EFI_UNSUPPORTED;
566 }
567
568 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
569 &PvScsiGetDriverName,
570 &PvScsiGetDeviceName,
571 "eng" // SupportedLanguages, ISO 639-2 language codes
572 };
573
574 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
575 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &PvScsiGetDriverName,
576 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &PvScsiGetDeviceName,
577 "en" // SupportedLanguages, RFC 4646 language codes
578 };
579
580 //
581 // Entry Point
582 //
583
584 EFI_STATUS
585 EFIAPI
586 PvScsiEntryPoint (
587 IN EFI_HANDLE ImageHandle,
588 IN EFI_SYSTEM_TABLE *SystemTable
589 )
590 {
591 return EfiLibInstallDriverBindingComponentName2 (
592 ImageHandle,
593 SystemTable,
594 &mPvScsiDriverBinding,
595 ImageHandle,
596 &mComponentName,
597 &mComponentName2
598 );
599 }