Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Step:15-SP4
ovmf.19611
ovmf-bsc1119454-additional-scsi-drivers.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File ovmf-bsc1119454-additional-scsi-drivers.patch of Package ovmf.19611
From 397408882da5346249ea5736bc07ed327226cb46 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:44 +0300 Subject: [PATCH 01/43] OvmfPkg/PvScsiDxe: Create empty driver In preparation for support booting from PvScsi devices, create a basic scaffolding for a driver. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-2-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 478c07d483d8f0b97df7e956457ec32bef92cf11) --- OvmfPkg/OvmfPkgIa32.dsc | 8 ++++++++ OvmfPkg/OvmfPkgIa32.fdf | 3 +++ OvmfPkg/OvmfPkgIa32X64.dsc | 8 ++++++++ OvmfPkg/OvmfPkgIa32X64.fdf | 3 +++ OvmfPkg/OvmfPkgX64.dsc | 8 ++++++++ OvmfPkg/OvmfPkgX64.fdf | 3 +++ OvmfPkg/PvScsiDxe/PvScsi.c | 26 ++++++++++++++++++++++++++ OvmfPkg/PvScsiDxe/PvScsiDxe.inf | 27 +++++++++++++++++++++++++++ 8 files changed, 86 insertions(+) create mode 100644 OvmfPkg/PvScsiDxe/PvScsi.c create mode 100644 OvmfPkg/PvScsiDxe/PvScsiDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 9a60eb8fe2b0..82a9444bb92b 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -44,6 +44,11 @@ [Defines] !include NetworkPkg/NetworkDefines.dsc.inc + # + # Device drivers + # + DEFINE PVSCSI_ENABLE = TRUE + # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to # one of the supported values, in place of any of the convenience macros, is @@ -711,6 +716,9 @@ [Components] OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf OvmfPkg/XenBusDxe/XenBusDxe.inf OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf +!if $(PVSCSI_ENABLE) == TRUE + OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf index 586bbff08585..d6b9c74e6cfe 100644 --- a/OvmfPkg/OvmfPkgIa32.fdf +++ b/OvmfPkg/OvmfPkgIa32.fdf @@ -227,6 +227,9 @@ [FV.DXEFV] INF OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf INF OvmfPkg/XenBusDxe/XenBusDxe.inf INF OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf +!if $(PVSCSI_ENABLE) == TRUE +INF OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 1d1480b50b02..d34a51e86ba1 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -44,6 +44,11 @@ [Defines] !include NetworkPkg/NetworkDefines.dsc.inc + # + # Device drivers + # + DEFINE PVSCSI_ENABLE = TRUE + # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to # one of the supported values, in place of any of the convenience macros, is @@ -724,6 +729,9 @@ [Components.X64] OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf OvmfPkg/XenBusDxe/XenBusDxe.inf OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf +!if $(PVSCSI_ENABLE) == TRUE + OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf index e49adc425fce..bce1bdb42d71 100644 --- a/OvmfPkg/OvmfPkgIa32X64.fdf +++ b/OvmfPkg/OvmfPkgIa32X64.fdf @@ -228,6 +228,9 @@ [FV.DXEFV] INF OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf INF OvmfPkg/XenBusDxe/XenBusDxe.inf INF OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf +!if $(PVSCSI_ENABLE) == TRUE +INF OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index c287a436f8ec..55d975c4d3f9 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -44,6 +44,11 @@ [Defines] !include NetworkPkg/NetworkDefines.dsc.inc + # + # Device drivers + # + DEFINE PVSCSI_ENABLE = TRUE + # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to # one of the supported values, in place of any of the convenience macros, is @@ -722,6 +727,9 @@ [Components] OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf OvmfPkg/XenBusDxe/XenBusDxe.inf OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf +!if $(PVSCSI_ENABLE) == TRUE + OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index e49adc425fce..bce1bdb42d71 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -228,6 +228,9 @@ [FV.DXEFV] INF OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf INF OvmfPkg/XenBusDxe/XenBusDxe.inf INF OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf +!if $(PVSCSI_ENABLE) == TRUE +INF OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c new file mode 100644 index 000000000000..1ae4de9869c1 --- /dev/null +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -0,0 +1,26 @@ +/** @file + + This driver produces Extended SCSI Pass Thru Protocol instances for + pvscsi devices. + + Copyright (C) 2020, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi/UefiSpec.h> + +// +// Entry Point +// + +EFI_STATUS +EFIAPI +PvScsiEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf new file mode 100644 index 000000000000..093cc0171338 --- /dev/null +++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf @@ -0,0 +1,27 @@ +## @file +# +# This driver produces Extended SCSI Pass Thru Protocol instances for +# pvscsi devices. +# +# Copyright (C) 2020, Oracle and/or its affiliates. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.29 + BASE_NAME = PvScsiDxe + FILE_GUID = 30346B14-1580-4781-879D-BA0C55AE9BB2 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = PvScsiEntryPoint + +[Sources] + PvScsi.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint -- 2.27.0 From aeb9ec41a3c52bc29dc937b9b8112ab14949bccc Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:45 +0300 Subject: [PATCH 02/43] OvmfPkg/PvScsiDxe: Install DriverBinding protocol In order to probe and connect to the PvScsi device we need this protocol. Currently it does nothing. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-3-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit ed08c5711339c870e8793319401e07526d7b816c) --- OvmfPkg/PvScsiDxe/PvScsi.c | 66 ++++++++++++++++++++++++++++++++- OvmfPkg/PvScsiDxe/PvScsiDxe.inf | 1 + 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 1ae4de9869c1..77b28b326784 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -9,8 +9,65 @@ **/ +#include <Library/UefiLib.h> #include <Uefi/UefiSpec.h> +// +// Higher versions will be used before lower, 0x10-0xffffffef is the version +// range for IHV (Indie Hardware Vendors) +// +#define PVSCSI_BINDING_VERSION 0x10 + +// +// Driver Binding +// + +STATIC +EFI_STATUS +EFIAPI +PvScsiDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = { + &PvScsiDriverBindingSupported, + &PvScsiDriverBindingStart, + &PvScsiDriverBindingStop, + PVSCSI_BINDING_VERSION, + NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2() + NULL // DriverBindingHandle, filled as well +}; + // // Entry Point // @@ -22,5 +79,12 @@ PvScsiEntryPoint ( IN EFI_SYSTEM_TABLE *SystemTable ) { - return EFI_UNSUPPORTED; + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &mPvScsiDriverBinding, + ImageHandle, + NULL, // TODO Component name + NULL // TODO Component name + ); } diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf index 093cc0171338..d1d0e963f96d 100644 --- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf +++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf @@ -25,3 +25,4 @@ [Packages] [LibraryClasses] UefiDriverEntryPoint + UefiLib -- 2.27.0 From c9b3b6d95caeb540ee42be256105b8735c54c7b1 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:46 +0300 Subject: [PATCH 03/43] OvmfPkg/PvScsiDxe: Report name of driver Install Component Name protocols to have a nice display name for the driver in places such as UEFI shell. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-4-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 419b30d642264bef34f9054a4adf64bf4f9e31aa) --- OvmfPkg/PvScsiDxe/PvScsi.c | 59 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 77b28b326784..51b03f709040 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -68,6 +68,61 @@ STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = { NULL // DriverBindingHandle, filled as well }; +// +// Component Name +// + +STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { + { "eng;en", L"PVSCSI Host Driver" }, + { NULL, NULL } +}; + +STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName; + +STATIC +EFI_STATUS +EFIAPI +PvScsiGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDriverNameTable, + DriverName, + (BOOLEAN)(This == &mComponentName) // Iso639Language + ); +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiGetDeviceName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN EFI_HANDLE ChildHandle, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = { + &PvScsiGetDriverName, + &PvScsiGetDeviceName, + "eng" // SupportedLanguages, ISO 639-2 language codes +}; + +STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &PvScsiGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &PvScsiGetDeviceName, + "en" // SupportedLanguages, RFC 4646 language codes +}; + // // Entry Point // @@ -84,7 +139,7 @@ PvScsiEntryPoint ( SystemTable, &mPvScsiDriverBinding, ImageHandle, - NULL, // TODO Component name - NULL // TODO Component name + &mComponentName, + &mComponentName2 ); } -- 2.27.0 From bb19235ec75cb2a65903f3ca3d0e7124197b5fd4 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:47 +0300 Subject: [PATCH 04/43] OvmfPkg/PvScsiDxe: Probe PCI devices and look for PvScsi PvScsiControllerSupported() is called on handles passed in by the ConnectController() boot service and if the handle is the PVSCSI controller, the function would return success. A success return value will attach our driver to the device. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-5-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit a9f9d5cf5636d9757be542a2544729f343bb607e) --- OvmfPkg/Include/IndustryStandard/PvScsi.h | 21 ++++++++++ OvmfPkg/PvScsiDxe/PvScsi.c | 49 ++++++++++++++++++++++- OvmfPkg/PvScsiDxe/PvScsiDxe.inf | 5 +++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 OvmfPkg/Include/IndustryStandard/PvScsi.h diff --git a/OvmfPkg/Include/IndustryStandard/PvScsi.h b/OvmfPkg/Include/IndustryStandard/PvScsi.h new file mode 100644 index 000000000000..004c0af84989 --- /dev/null +++ b/OvmfPkg/Include/IndustryStandard/PvScsi.h @@ -0,0 +1,21 @@ +/** @file + + VMware PVSCSI Device specific type and macro definitions. + + Copyright (C) 2020, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __PVSCSI_H_ +#define __PVSCSI_H_ + +// +// Device offsets and constants +// + +#define PCI_VENDOR_ID_VMWARE (0x15ad) +#define PCI_DEVICE_ID_VMWARE_PVSCSI (0x07c0) + +#endif // __PVSCSI_H_ diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 51b03f709040..9923a31d25d7 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -9,7 +9,11 @@ **/ +#include <IndustryStandard/Pci.h> +#include <IndustryStandard/PvScsi.h> +#include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> +#include <Protocol/PciIo.h> #include <Uefi/UefiSpec.h> // @@ -31,7 +35,50 @@ PvScsiDriverBindingSupported ( IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + (VOID **)&PciIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + if ((Pci.Hdr.VendorId != PCI_VENDOR_ID_VMWARE) || + (Pci.Hdr.DeviceId != PCI_DEVICE_ID_VMWARE_PVSCSI)) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + Status = EFI_SUCCESS; + +Done: + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return Status; } STATIC diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf index d1d0e963f96d..c1f0663832ed 100644 --- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf +++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf @@ -22,7 +22,12 @@ [Sources] [Packages] MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec [LibraryClasses] + UefiBootServicesTableLib UefiDriverEntryPoint UefiLib + +[Protocols] + gEfiPciIoProtocolGuid ## TO_START -- 2.27.0 From 5722da4cac12d298221db65e2892fd516027f23d Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:48 +0300 Subject: [PATCH 05/43] OvmfPkg/PvScsiDxe: Install stubbed EXT_SCSI_PASS_THRU Support dynamic insertion and removal of the protocol. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-6-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit e497432c2ceadc3924f96e49593c8a77044efea7) --- OvmfPkg/PvScsiDxe/PvScsi.c | 209 +++++++++++++++++++++++++++++++- OvmfPkg/PvScsiDxe/PvScsi.h | 29 +++++ OvmfPkg/PvScsiDxe/PvScsiDxe.inf | 6 +- 3 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 OvmfPkg/PvScsiDxe/PvScsi.h diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 9923a31d25d7..04c08036b799 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -11,17 +11,156 @@ #include <IndustryStandard/Pci.h> #include <IndustryStandard/PvScsi.h> +#include <Library/MemoryAllocationLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> #include <Protocol/PciIo.h> #include <Uefi/UefiSpec.h> +#include "PvScsi.h" + // // Higher versions will be used before lower, 0x10-0xffffffef is the version // range for IHV (Indie Hardware Vendors) // #define PVSCSI_BINDING_VERSION 0x10 +// +// Ext SCSI Pass Thru +// + +STATIC +EFI_STATUS +EFIAPI +PvScsiPassThru ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiResetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +PvScsiGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +PvScsiInit ( + IN OUT PVSCSI_DEV *Dev + ) +{ + // + // Populate the exported interface's attributes + // + Dev->PassThru.Mode = &Dev->PassThruMode; + Dev->PassThru.PassThru = &PvScsiPassThru; + Dev->PassThru.GetNextTargetLun = &PvScsiGetNextTargetLun; + Dev->PassThru.BuildDevicePath = &PvScsiBuildDevicePath; + Dev->PassThru.GetTargetLun = &PvScsiGetTargetLun; + Dev->PassThru.ResetChannel = &PvScsiResetChannel; + Dev->PassThru.ResetTargetLun = &PvScsiResetTargetLun; + Dev->PassThru.GetNextTarget = &PvScsiGetNextTarget; + + // + // AdapterId is a target for which no handle will be created during bus scan. + // Prevent any conflict with real devices. + // + Dev->PassThruMode.AdapterId = MAX_UINT32; + + // + // Set both physical and logical attributes for non-RAID SCSI channel + // + Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | + EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL; + + // + // No restriction on transfer buffer alignment + // + Dev->PassThruMode.IoAlign = 0; + + return EFI_SUCCESS; +} + +STATIC +VOID +PvScsiUninit ( + IN OUT PVSCSI_DEV *Dev + ) +{ + // Currently nothing to do here +} + // // Driver Binding // @@ -90,7 +229,42 @@ PvScsiDriverBindingStart ( IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { - return EFI_UNSUPPORTED; + PVSCSI_DEV *Dev; + EFI_STATUS Status; + + Dev = (PVSCSI_DEV *) AllocateZeroPool (sizeof (*Dev)); + if (Dev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = PvScsiInit (Dev); + if (EFI_ERROR (Status)) { + goto FreePvScsi; + } + + // + // Setup complete, attempt to export the driver instance's PassThru interface + // + Dev->Signature = PVSCSI_SIG; + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + EFI_NATIVE_INTERFACE, + &Dev->PassThru + ); + if (EFI_ERROR (Status)) { + goto UninitDev; + } + + return EFI_SUCCESS; + +UninitDev: + PvScsiUninit (Dev); + +FreePvScsi: + FreePool (Dev); + + return Status; } STATIC @@ -103,7 +277,38 @@ PvScsiDriverBindingStop ( IN EFI_HANDLE *ChildHandleBuffer ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru; + PVSCSI_DEV *Dev; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **)&PassThru, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Dev = PVSCSI_FROM_PASS_THRU (PassThru); + + Status = gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + &Dev->PassThru + ); + if (EFI_ERROR (Status)) { + return Status; + } + + PvScsiUninit (Dev); + + FreePool (Dev); + + return EFI_SUCCESS; } STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = { diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h new file mode 100644 index 000000000000..3940b4c20019 --- /dev/null +++ b/OvmfPkg/PvScsiDxe/PvScsi.h @@ -0,0 +1,29 @@ +/** @file + + Internal definitions for the PVSCSI driver, which produces Extended SCSI + Pass Thru Protocol instances for pvscsi devices. + + Copyright (C) 2020, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __PVSCSI_DXE_H_ +#define __PVSCSI_DXE_H_ + +#include <Library/DebugLib.h> +#include <Protocol/ScsiPassThruExt.h> + +#define PVSCSI_SIG SIGNATURE_32 ('P', 'S', 'C', 'S') + +typedef struct { + UINT32 Signature; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; + EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; +} PVSCSI_DEV; + +#define PVSCSI_FROM_PASS_THRU(PassThruPointer) \ + CR (PassThruPointer, PVSCSI_DEV, PassThru, PVSCSI_SIG) + +#endif // __PVSCSI_DXE_H_ diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf index c1f0663832ed..f4d452c6c3d2 100644 --- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf +++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf @@ -19,15 +19,19 @@ [Defines] [Sources] PvScsi.c + PvScsi.h [Packages] MdePkg/MdePkg.dec OvmfPkg/OvmfPkg.dec [LibraryClasses] + DebugLib + MemoryAllocationLib UefiBootServicesTableLib UefiDriverEntryPoint UefiLib [Protocols] - gEfiPciIoProtocolGuid ## TO_START + gEfiExtScsiPassThruProtocolGuid ## BY_START + gEfiPciIoProtocolGuid ## TO_START -- 2.27.0 From 585f196933b0dd9e2f4bccad8a386415d9c1bf76 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:49 +0300 Subject: [PATCH 06/43] OvmfPkg/PvScsiDxe: Report the number of targets and LUNs Implement EXT_SCSI_PASS_THRU.GetNextTarget() and EXT_SCSI_PASS_THRU.GetNextTargetLun(). ScsiBusDxe scans all MaxTarget * MaxLun possible devices. This can take unnecessarily long for large number of targets. To deal with this, VirtioScsiDxe has defined PCDs to limit the MaxTarget & MaxLun to desired values which gives sufficient performance. It is very important in virtio-scsi as it can have very big MaxTarget & MaxLun. Even though a common PVSCSI device has a default MaxTarget=64 and MaxLun=0, we implement similar mechanism as virtio-scsi for completeness. This may be useful in the future when PVSCSI will have bigger values for MaxTarget and MaxLun. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-7-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 7efce2e59c2016f0b2572d4b79845f14a8758475) --- OvmfPkg/OvmfPkg.dec | 9 +++ OvmfPkg/PvScsiDxe/PvScsi.c | 122 +++++++++++++++++++++++++++++++- OvmfPkg/PvScsiDxe/PvScsi.h | 2 + OvmfPkg/PvScsiDxe/PvScsiDxe.inf | 5 ++ 4 files changed, 136 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index d5fee805ef4a..b0893c3591ee 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -121,6 +121,15 @@ [PcdsFixedAtBuild] gUefiOvmfPkgTokenSpaceGuid.PcdVirtioScsiMaxTargetLimit|31|UINT16|6 gUefiOvmfPkgTokenSpaceGuid.PcdVirtioScsiMaxLunLimit|7|UINT32|7 + ## Sets the *inclusive* number of targets and LUNs that PvScsi exposes for + # scan by ScsiBusDxe. + # As specified above for VirtioScsi, ScsiBusDxe scans all MaxTarget * MaxLun + # possible devices, which can take extremely long. Thus, the below constants + # are used so that scanning the number of devices given by their product + # is still acceptably fast. + gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit|64|UINT8|0x36 + gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit|0|UINT8|0x37 + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 04c08036b799..7f51ada19a1a 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -11,6 +11,7 @@ #include <IndustryStandard/Pci.h> #include <IndustryStandard/PvScsi.h> +#include <Library/BaseMemoryLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> @@ -25,6 +26,30 @@ // #define PVSCSI_BINDING_VERSION 0x10 +// +// Ext SCSI Pass Thru utilities +// + +/** + Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and + EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized +**/ +STATIC +BOOLEAN +IsTargetInitialized ( + IN UINT8 *Target + ) +{ + UINTN Idx; + + for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) { + if (Target[Idx] != 0xFF) { + return TRUE; + } + } + return FALSE; +} + // // Ext SCSI Pass Thru // @@ -52,7 +77,54 @@ PvScsiGetNextTargetLun ( IN OUT UINT64 *Lun ) { - return EFI_UNSUPPORTED; + UINT8 *TargetPtr; + UINT8 LastTarget; + PVSCSI_DEV *Dev; + + if (Target == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The Target input parameter is unnecessarily a pointer-to-pointer + // + TargetPtr = *Target; + + // + // If target not initialized, return first target & LUN + // + if (!IsTargetInitialized (TargetPtr)) { + ZeroMem (TargetPtr, TARGET_MAX_BYTES); + *Lun = 0; + return EFI_SUCCESS; + } + + // + // We only use first byte of target identifer + // + LastTarget = *TargetPtr; + + // + // Increment (target, LUN) pair if valid on input + // + Dev = PVSCSI_FROM_PASS_THRU (This); + if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) { + return EFI_INVALID_PARAMETER; + } + + if (*Lun < Dev->MaxLun) { + ++*Lun; + return EFI_SUCCESS; + } + + if (LastTarget < Dev->MaxTarget) { + *Lun = 0; + ++LastTarget; + *TargetPtr = LastTarget; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; } STATIC @@ -111,7 +183,47 @@ PvScsiGetNextTarget ( IN OUT UINT8 **Target ) { - return EFI_UNSUPPORTED; + UINT8 *TargetPtr; + UINT8 LastTarget; + PVSCSI_DEV *Dev; + + if (Target == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The Target input parameter is unnecessarily a pointer-to-pointer + // + TargetPtr = *Target; + + // + // If target not initialized, return first target + // + if (!IsTargetInitialized (TargetPtr)) { + ZeroMem (TargetPtr, TARGET_MAX_BYTES); + return EFI_SUCCESS; + } + + // + // We only use first byte of target identifer + // + LastTarget = *TargetPtr; + + // + // Increment target if valid on input + // + Dev = PVSCSI_FROM_PASS_THRU (This); + if (LastTarget > Dev->MaxTarget) { + return EFI_INVALID_PARAMETER; + } + + if (LastTarget < Dev->MaxTarget) { + ++LastTarget; + *TargetPtr = LastTarget; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; } STATIC @@ -120,6 +232,12 @@ PvScsiInit ( IN OUT PVSCSI_DEV *Dev ) { + // + // Init configuration + // + Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit); + Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit); + // // Populate the exported interface's attributes // diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h index 3940b4c20019..dd3e0c68e6da 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.h +++ b/OvmfPkg/PvScsiDxe/PvScsi.h @@ -19,6 +19,8 @@ typedef struct { UINT32 Signature; + UINT8 MaxTarget; + UINT8 MaxLun; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; } PVSCSI_DEV; diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf index f4d452c6c3d2..fcffc90d46c8 100644 --- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf +++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf @@ -26,6 +26,7 @@ [Packages] OvmfPkg/OvmfPkg.dec [LibraryClasses] + BaseMemoryLib DebugLib MemoryAllocationLib UefiBootServicesTableLib @@ -35,3 +36,7 @@ [LibraryClasses] [Protocols] gEfiExtScsiPassThruProtocolGuid ## BY_START gEfiPciIoProtocolGuid ## TO_START + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit ## CONSUMES -- 2.27.0 From aa567c940fbf25f8437c31d48a32ef8bffd32f1d Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:50 +0300 Subject: [PATCH 07/43] OvmfPkg/PvScsiDxe: Translate Target & LUN to/from DevicePath Implement EXT_SCSI_PASS_THRU.BuildDevicePath() and EXT_SCSI_PASS_THRU.GetTargetLun(). Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-8-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 9c2d8281af9bc4a3c598994ba1fee96f49dfc8e9) --- OvmfPkg/PvScsiDxe/PvScsi.c | 61 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 7f51ada19a1a..76fc1eb910f2 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -137,7 +137,38 @@ PvScsiBuildDevicePath ( IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath ) { - return EFI_UNSUPPORTED; + UINT8 TargetValue; + PVSCSI_DEV *Dev; + SCSI_DEVICE_PATH *ScsiDevicePath; + + if (DevicePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // We only use first byte of target identifer + // + TargetValue = *Target; + + Dev = PVSCSI_FROM_PASS_THRU (This); + if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun) { + return EFI_NOT_FOUND; + } + + ScsiDevicePath = AllocatePool (sizeof (*ScsiDevicePath)); + if (ScsiDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH; + ScsiDevicePath->Header.SubType = MSG_SCSI_DP; + ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath); + ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8); + ScsiDevicePath->Pun = TargetValue; + ScsiDevicePath->Lun = (UINT16)Lun; + + *DevicePath = &ScsiDevicePath->Header; + return EFI_SUCCESS; } STATIC @@ -150,7 +181,33 @@ PvScsiGetTargetLun ( OUT UINT64 *Lun ) { - return EFI_UNSUPPORTED; + SCSI_DEVICE_PATH *ScsiDevicePath; + PVSCSI_DEV *Dev; + + if (DevicePath == NULL || Target == NULL || *Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DevicePath->Type != MESSAGING_DEVICE_PATH || + DevicePath->SubType != MSG_SCSI_DP) { + return EFI_UNSUPPORTED; + } + + ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath; + Dev = PVSCSI_FROM_PASS_THRU (This); + if (ScsiDevicePath->Pun > Dev->MaxTarget || + ScsiDevicePath->Lun > Dev->MaxLun) { + return EFI_NOT_FOUND; + } + + // + // We only use first byte of target identifer + // + **Target = (UINT8)ScsiDevicePath->Pun; + ZeroMem (*Target + 1, TARGET_MAX_BYTES - 1); + *Lun = ScsiDevicePath->Lun; + + return EFI_SUCCESS; } STATIC -- 2.27.0 From 13f8888e012eaa17151808e09b061e76d3f81d4a Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:51 +0300 Subject: [PATCH 08/43] OvmfPkg/PvScsiDxe: Open PciIo protocol for later use This will give us an exclusive access to the PciIo of this device after it was started and until it will be stopped. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-9-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit c08eaaaf373bafe71f8a506d55230f1a4874e9e2) --- OvmfPkg/PvScsiDxe/PvScsi.c | 29 ++++++++++++++++++++++++++++- OvmfPkg/PvScsiDxe/PvScsi.h | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 76fc1eb910f2..e0380d729b3c 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -412,11 +412,23 @@ PvScsiDriverBindingStart ( return EFI_OUT_OF_RESOURCES; } - Status = PvScsiInit (Dev); + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + (VOID **)&Dev->PciIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); if (EFI_ERROR (Status)) { goto FreePvScsi; } + Status = PvScsiInit (Dev); + if (EFI_ERROR (Status)) { + goto ClosePciIo; + } + // // Setup complete, attempt to export the driver instance's PassThru interface // @@ -436,6 +448,14 @@ PvScsiDriverBindingStart ( UninitDev: PvScsiUninit (Dev); +ClosePciIo: + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + FreePvScsi: FreePool (Dev); @@ -481,6 +501,13 @@ PvScsiDriverBindingStop ( PvScsiUninit (Dev); + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + FreePool (Dev); return EFI_SUCCESS; diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h index dd3e0c68e6da..e1e5ae18ebf2 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.h +++ b/OvmfPkg/PvScsiDxe/PvScsi.h @@ -19,6 +19,7 @@ typedef struct { UINT32 Signature; + EFI_PCI_IO_PROTOCOL *PciIo; UINT8 MaxTarget; UINT8 MaxLun; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; -- 2.27.0 From 4eb8af37fb838ecd95637bb64cdf79c752b11b41 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:52 +0300 Subject: [PATCH 09/43] OvmfPkg/PvScsiDxe: Backup/Restore PCI attributes on Init/UnInit This commit doesn't change semantics. It is done as a preparation for future commits which will modify PCI attributes. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-10-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 45098e8a9aeefaf8405e4b772ffa2694efe507a0) --- OvmfPkg/PvScsiDxe/PvScsi.c | 54 +++++++++++++++++++++++++++++++++++++- OvmfPkg/PvScsiDxe/PvScsi.h | 1 + 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index e0380d729b3c..5566b4cce467 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -283,18 +283,70 @@ PvScsiGetNextTarget ( return EFI_NOT_FOUND; } +STATIC +EFI_STATUS +PvScsiSetPciAttributes ( + IN OUT PVSCSI_DEV *Dev + ) +{ + EFI_STATUS Status; + + // + // Backup original PCI Attributes + // + Status = Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationGet, + 0, + &Dev->OriginalPciAttributes + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // TODO: Change PCI Attributes + // + + return EFI_SUCCESS; +} + +STATIC +VOID +PvScsiRestorePciAttributes ( + IN PVSCSI_DEV *Dev + ) +{ + Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationSet, + Dev->OriginalPciAttributes, + NULL + ); +} + STATIC EFI_STATUS PvScsiInit ( IN OUT PVSCSI_DEV *Dev ) { + EFI_STATUS Status; + // // Init configuration // Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit); Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit); + // + // Set PCI Attributes + // + Status = PvScsiSetPciAttributes (Dev); + if (EFI_ERROR (Status)) { + return Status; + } + // // Populate the exported interface's attributes // @@ -333,7 +385,7 @@ PvScsiUninit ( IN OUT PVSCSI_DEV *Dev ) { - // Currently nothing to do here + PvScsiRestorePciAttributes (Dev); } // diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h index e1e5ae18ebf2..5f611dbbc98c 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.h +++ b/OvmfPkg/PvScsiDxe/PvScsi.h @@ -20,6 +20,7 @@ typedef struct { UINT32 Signature; EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 OriginalPciAttributes; UINT8 MaxTarget; UINT8 MaxLun; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; -- 2.27.0 From f16e4c898de848bdc154c2d15143848a473f960c Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:53 +0300 Subject: [PATCH 10/43] OvmfPkg/PvScsiDxe: Enable MMIO-Space & Bus-Mastering in PCI attributes Enable MMIO-Space & Bus-Mastering PCI attributes when device is started. Note that original PCI attributes are restored when device is stopped. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-11-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 6672b3cff2e164093ebc120ef8304472b9a7d23f) --- OvmfPkg/PvScsiDxe/PvScsi.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 5566b4cce467..531bed4e5ab7 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -305,8 +305,18 @@ PvScsiSetPciAttributes ( } // - // TODO: Change PCI Attributes + // Enable MMIO-Space & Bus-Mastering // + Status = Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationEnable, + (EFI_PCI_IO_ATTRIBUTE_MEMORY | + EFI_PCI_IO_ATTRIBUTE_BUS_MASTER), + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } return EFI_SUCCESS; } -- 2.27.0 From 9e79a360afc579b87e1d88b75de116934c020711 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:54 +0300 Subject: [PATCH 11/43] OvmfPkg/PvScsiDxe: Define device interface structures and constants These definitions will be used by the following commits to complete the implementation of PVSCSI device driver. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Acked-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-12-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 447e5d39025a80ad2810559ccda43cdc73c12b16) --- OvmfPkg/Include/IndustryStandard/PvScsi.h | 165 ++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/OvmfPkg/Include/IndustryStandard/PvScsi.h b/OvmfPkg/Include/IndustryStandard/PvScsi.h index 004c0af84989..a4d6634f3ba0 100644 --- a/OvmfPkg/Include/IndustryStandard/PvScsi.h +++ b/OvmfPkg/Include/IndustryStandard/PvScsi.h @@ -18,4 +18,169 @@ #define PCI_VENDOR_ID_VMWARE (0x15ad) #define PCI_DEVICE_ID_VMWARE_PVSCSI (0x07c0) +// +// CDB (Command Descriptor Block) with size above this constant +// should be considered out-of-band +// +#define PVSCSI_CDB_MAX_SIZE (16) + +typedef enum { + PvScsiRegOffsetCommand = 0x0, + PvScsiRegOffsetCommandData = 0x4, + PvScsiRegOffsetCommandStatus = 0x8, + PvScsiRegOffsetLastSts0 = 0x100, + PvScsiRegOffsetLastSts1 = 0x104, + PvScsiRegOffsetLastSts2 = 0x108, + PvScsiRegOffsetLastSts3 = 0x10c, + PvScsiRegOffsetIntrStatus = 0x100c, + PvScsiRegOffsetIntrMask = 0x2010, + PvScsiRegOffsetKickNonRwIo = 0x3014, + PvScsiRegOffsetDebug = 0x3018, + PvScsiRegOffsetKickRwIo = 0x4018, +} PVSCSI_BAR0_OFFSETS; + +// +// Define Interrupt-Status register flags +// +#define PVSCSI_INTR_CMPL_0 BIT0 +#define PVSCSI_INTR_CMPL_1 BIT1 +#define PVSCSI_INTR_CMPL_MASK (PVSCSI_INTR_CMPL_0 | PVSCSI_INTR_CMPL_1) + +typedef enum { + PvScsiCmdFirst = 0, + PvScsiCmdAdapterReset = 1, + PvScsiCmdIssueScsi = 2, + PvScsiCmdSetupRings = 3, + PvScsiCmdResetBus = 4, + PvScsiCmdResetDevice = 5, + PvScsiCmdAbortCmd = 6, + PvScsiCmdConfig = 7, + PvScsiCmdSetupMsgRing = 8, + PvScsiCmdDeviceUnplug = 9, + PvScsiCmdLast = 10 +} PVSCSI_COMMANDS; + +#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES (32) + +#pragma pack (1) +typedef struct { + UINT32 ReqRingNumPages; + UINT32 CmpRingNumPages; + UINT64 RingsStatePPN; + UINT64 ReqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; + UINT64 CmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; +} PVSCSI_CMD_DESC_SETUP_RINGS; +#pragma pack () + +#define PVSCSI_MAX_CMD_DATA_WORDS \ + (sizeof (PVSCSI_CMD_DESC_SETUP_RINGS) / sizeof (UINT32)) + +#pragma pack (1) +typedef struct { + UINT32 ReqProdIdx; + UINT32 ReqConsIdx; + UINT32 ReqNumEntriesLog2; + + UINT32 CmpProdIdx; + UINT32 CmpConsIdx; + UINT32 CmpNumEntriesLog2; + + UINT8 Pad[104]; + + UINT32 MsgProdIdx; + UINT32 MsgConsIdx; + UINT32 MsgNumEntriesLog2; +} PVSCSI_RINGS_STATE; +#pragma pack () + +// +// Define PVSCSI request descriptor tags +// +#define PVSCSI_SIMPLE_QUEUE_TAG (0x20) + +// +// Define PVSCSI request descriptor flags +// +#define PVSCSI_FLAG_CMD_WITH_SG_LIST BIT0 +#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB BIT1 +#define PVSCSI_FLAG_CMD_DIR_NONE BIT2 +#define PVSCSI_FLAG_CMD_DIR_TOHOST BIT3 +#define PVSCSI_FLAG_CMD_DIR_TODEVICE BIT4 + +#pragma pack (1) +typedef struct { + UINT64 Context; + UINT64 DataAddr; + UINT64 DataLen; + UINT64 SenseAddr; + UINT32 SenseLen; + UINT32 Flags; + UINT8 Cdb[16]; + UINT8 CdbLen; + UINT8 Lun[8]; + UINT8 Tag; + UINT8 Bus; + UINT8 Target; + UINT8 VcpuHint; + UINT8 Unused[59]; +} PVSCSI_RING_REQ_DESC; +#pragma pack () + +// +// Host adapter status/error codes +// +typedef enum { + PvScsiBtStatSuccess = 0x00, // CCB complete normally with no errors + PvScsiBtStatLinkedCommandCompleted = 0x0a, + PvScsiBtStatLinkedCommandCompletedWithFlag = 0x0b, + PvScsiBtStatDataUnderrun = 0x0c, + PvScsiBtStatSelTimeout = 0x11, // SCSI selection timeout + PvScsiBtStatDatarun = 0x12, // Data overrun/underrun + PvScsiBtStatBusFree = 0x13, // Unexpected bus free + PvScsiBtStatInvPhase = 0x14, // + // Invalid bus phase or sequence requested + // by target + // + PvScsiBtStatLunMismatch = 0x17, // + // Linked CCB has different LUN from first + // CCB + // + PvScsiBtStatSensFailed = 0x1b, // Auto request sense failed + PvScsiBtStatTagReject = 0x1c, // + // SCSI II tagged queueing message rejected + // by target + // + PvScsiBtStatBadMsg = 0x1d, // + // Unsupported message received by the host + // adapter + // + PvScsiBtStatHaHardware = 0x20, // Host adapter hardware failed + PvScsiBtStatNoResponse = 0x21, // + // Target did not respond to SCSI ATN sent + // a SCSI RST + // + PvScsiBtStatSentRst = 0x22, // Host adapter asserted a SCSI RST + PvScsiBtStatRecvRst = 0x23, // Other SCSI devices asserted a SCSI RST + PvScsiBtStatDisconnect = 0x24, // + // Target device reconnected improperly + // (w/o tag) + // + PvScsiBtStatBusReset = 0x25, // Host adapter issued BUS device reset + PvScsiBtStatAbortQueue = 0x26, // Abort queue generated + PvScsiBtStatHaSoftware = 0x27, // Host adapter software error + PvScsiBtStatHaTimeout = 0x30, // Host adapter hardware timeout error + PvScsiBtStatScsiParity = 0x34, // SCSI parity error detected +} PVSCSI_HOST_BUS_ADAPTER_STATUS; + +#pragma pack (1) +typedef struct { + UINT64 Context; + UINT64 DataLen; + UINT32 SenseLen; + UINT16 HostStatus; + UINT16 ScsiStatus; + UINT32 Pad[2]; +} PVSCSI_RING_CMP_DESC; +#pragma pack () + #endif // __PVSCSI_H_ -- 2.27.0 From 5fb1691240d789ce9383a5902761c2d60ab42abf Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:55 +0300 Subject: [PATCH 12/43] OvmfPkg/PvScsiDxe: Reset adapter on init The following commits will complete the implementation of device initialization. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-13-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 5269c26e07381dcc7e9de4fe1f2a5b7ed1e5c78a) --- OvmfPkg/PvScsiDxe/PvScsi.c | 114 +++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 531bed4e5ab7..cf75884350ee 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -30,6 +30,107 @@ // Ext SCSI Pass Thru utilities // +/** + Writes a 32-bit value into BAR0 using MMIO +**/ +STATIC +EFI_STATUS +PvScsiMmioWrite32 ( + IN CONST PVSCSI_DEV *Dev, + IN UINT64 Offset, + IN UINT32 Value + ) +{ + return Dev->PciIo->Mem.Write ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX0, + Offset, + 1, // Count + &Value + ); +} + +/** + Writes multiple words of data into BAR0 using MMIO +**/ +STATIC +EFI_STATUS +PvScsiMmioWrite32Multiple ( + IN CONST PVSCSI_DEV *Dev, + IN UINT64 Offset, + IN UINTN Count, + IN UINT32 *Words + ) +{ + return Dev->PciIo->Mem.Write ( + Dev->PciIo, + EfiPciIoWidthFifoUint32, + PCI_BAR_IDX0, + Offset, + Count, + Words + ); +} + +/** + Send a PVSCSI command to device. + + @param[in] Dev The pvscsi host device. + @param[in] Cmd The command to send to device. + @param[in] OPTIONAL DescWords An optional command descriptor (If command + have a descriptor). The descriptor is + provided as an array of UINT32 words and + is must be 32-bit aligned. + @param[in] DescWordsCount The number of words in command descriptor. + Caller must specify here 0 if DescWords + is not supplied (It is optional). In that + case, DescWords is ignored. + + @return Status codes returned by Dev->PciIo->Mem.Write(). + +**/ +STATIC +EFI_STATUS +PvScsiWriteCmdDesc ( + IN CONST PVSCSI_DEV *Dev, + IN UINT32 Cmd, + IN UINT32 *DescWords OPTIONAL, + IN UINTN DescWordsCount + ) +{ + EFI_STATUS Status; + + if (DescWordsCount > PVSCSI_MAX_CMD_DATA_WORDS) { + return EFI_INVALID_PARAMETER; + } + + Status = PvScsiMmioWrite32 (Dev, PvScsiRegOffsetCommand, Cmd); + if (EFI_ERROR (Status)) { + return Status; + } + + if (DescWordsCount > 0) { + return PvScsiMmioWrite32Multiple ( + Dev, + PvScsiRegOffsetCommandData, + DescWordsCount, + DescWords + ); + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +PvScsiResetAdapter ( + IN CONST PVSCSI_DEV *Dev + ) +{ + return PvScsiWriteCmdDesc (Dev, PvScsiCmdAdapterReset, NULL, 0); +} + /** Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized @@ -357,6 +458,14 @@ PvScsiInit ( return Status; } + // + // Reset adapter + // + Status = PvScsiResetAdapter (Dev); + if (EFI_ERROR (Status)) { + goto RestorePciAttributes; + } + // // Populate the exported interface's attributes // @@ -387,6 +496,11 @@ PvScsiInit ( Dev->PassThruMode.IoAlign = 0; return EFI_SUCCESS; + +RestorePciAttributes: + PvScsiRestorePciAttributes (Dev); + + return Status; } STATIC -- 2.27.0 From 2f8da920bbdb5f6d75ac8e65015890973f55495f Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:56 +0300 Subject: [PATCH 13/43] OvmfPkg/PvScsiDxe: Setup requests and completions rings These rings are shared memory buffers between host and device in which a cyclic buffer is managed to send request descriptors from host to device and receive completion descriptors from device to host. Note that because device may be constrained by IOMMU or guest may be run under AMD SEV, we make sure to map these rings to device by using PciIo->Map(). Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-14-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit b654edec034a5e5dcc440f2f30fd2aa6c31aef3c) --- OvmfPkg/PvScsiDxe/PvScsi.c | 219 ++++++++++++++++++++++++++++++++ OvmfPkg/PvScsiDxe/PvScsi.h | 17 +++ OvmfPkg/PvScsiDxe/PvScsiDxe.inf | 1 + 3 files changed, 237 insertions(+) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index cf75884350ee..c7d367e83a2d 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -11,11 +11,13 @@ #include <IndustryStandard/Pci.h> #include <IndustryStandard/PvScsi.h> +#include <Library/BaseLib.h> #include <Library/BaseMemoryLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> #include <Protocol/PciIo.h> +#include <Protocol/PciRootBridgeIo.h> #include <Uefi/UefiSpec.h> #include "PvScsi.h" @@ -436,6 +438,207 @@ PvScsiRestorePciAttributes ( ); } +STATIC +EFI_STATUS +PvScsiAllocateSharedPages ( + IN PVSCSI_DEV *Dev, + IN UINTN Pages, + OUT VOID **HostAddress, + OUT PVSCSI_DMA_DESC *DmaDesc + ) +{ + EFI_STATUS Status; + UINTN NumberOfBytes; + + Status = Dev->PciIo->AllocateBuffer ( + Dev->PciIo, + AllocateAnyPages, + EfiBootServicesData, + Pages, + HostAddress, + EFI_PCI_ATTRIBUTE_MEMORY_CACHED + ); + if (EFI_ERROR (Status)) { + return Status; + } + + NumberOfBytes = EFI_PAGES_TO_SIZE (Pages); + Status = Dev->PciIo->Map ( + Dev->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + *HostAddress, + &NumberOfBytes, + &DmaDesc->DeviceAddress, + &DmaDesc->Mapping + ); + if (EFI_ERROR (Status)) { + goto FreeBuffer; + } + + if (NumberOfBytes != EFI_PAGES_TO_SIZE (Pages)) { + Status = EFI_OUT_OF_RESOURCES; + goto Unmap; + } + + return EFI_SUCCESS; + +Unmap: + Dev->PciIo->Unmap (Dev->PciIo, DmaDesc->Mapping); + +FreeBuffer: + Dev->PciIo->FreeBuffer (Dev->PciIo, Pages, *HostAddress); + + return Status; +} + +STATIC +VOID +PvScsiFreeSharedPages ( + IN PVSCSI_DEV *Dev, + IN UINTN Pages, + IN VOID *HostAddress, + IN PVSCSI_DMA_DESC *DmaDesc + ) +{ + Dev->PciIo->Unmap (Dev->PciIo, DmaDesc->Mapping); + Dev->PciIo->FreeBuffer (Dev->PciIo, Pages, HostAddress); +} + +STATIC +EFI_STATUS +PvScsiInitRings ( + IN OUT PVSCSI_DEV *Dev + ) +{ + EFI_STATUS Status; + union { + PVSCSI_CMD_DESC_SETUP_RINGS Cmd; + UINT32 Uint32; + } AlignedCmd; + PVSCSI_CMD_DESC_SETUP_RINGS *Cmd; + + Cmd = &AlignedCmd.Cmd; + + Status = PvScsiAllocateSharedPages ( + Dev, + 1, + (VOID **)&Dev->RingDesc.RingState, + &Dev->RingDesc.RingStateDmaDesc + ); + if (EFI_ERROR (Status)) { + return Status; + } + ZeroMem (Dev->RingDesc.RingState, EFI_PAGE_SIZE); + + Status = PvScsiAllocateSharedPages ( + Dev, + 1, + (VOID **)&Dev->RingDesc.RingReqs, + &Dev->RingDesc.RingReqsDmaDesc + ); + if (EFI_ERROR (Status)) { + goto FreeRingState; + } + ZeroMem (Dev->RingDesc.RingReqs, EFI_PAGE_SIZE); + + Status = PvScsiAllocateSharedPages ( + Dev, + 1, + (VOID **)&Dev->RingDesc.RingCmps, + &Dev->RingDesc.RingCmpsDmaDesc + ); + if (EFI_ERROR (Status)) { + goto FreeRingReqs; + } + ZeroMem (Dev->RingDesc.RingCmps, EFI_PAGE_SIZE); + + ZeroMem (Cmd, sizeof (*Cmd)); + Cmd->ReqRingNumPages = 1; + Cmd->CmpRingNumPages = 1; + Cmd->RingsStatePPN = RShiftU64 ( + Dev->RingDesc.RingStateDmaDesc.DeviceAddress, + EFI_PAGE_SHIFT + ); + Cmd->ReqRingPPNs[0] = RShiftU64 ( + Dev->RingDesc.RingReqsDmaDesc.DeviceAddress, + EFI_PAGE_SHIFT + ); + Cmd->CmpRingPPNs[0] = RShiftU64 ( + Dev->RingDesc.RingCmpsDmaDesc.DeviceAddress, + EFI_PAGE_SHIFT + ); + + STATIC_ASSERT ( + sizeof (*Cmd) % sizeof (UINT32) == 0, + "Cmd must be multiple of 32-bit words" + ); + Status = PvScsiWriteCmdDesc ( + Dev, + PvScsiCmdSetupRings, + (UINT32 *)Cmd, + sizeof (*Cmd) / sizeof (UINT32) + ); + if (EFI_ERROR (Status)) { + goto FreeRingCmps; + } + + return EFI_SUCCESS; + +FreeRingCmps: + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingCmps, + &Dev->RingDesc.RingCmpsDmaDesc + ); + +FreeRingReqs: + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingReqs, + &Dev->RingDesc.RingReqsDmaDesc + ); + +FreeRingState: + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingState, + &Dev->RingDesc.RingStateDmaDesc + ); + + return Status; +} + +STATIC +VOID +PvScsiFreeRings ( + IN OUT PVSCSI_DEV *Dev + ) +{ + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingCmps, + &Dev->RingDesc.RingCmpsDmaDesc + ); + + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingReqs, + &Dev->RingDesc.RingReqsDmaDesc + ); + + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingState, + &Dev->RingDesc.RingStateDmaDesc + ); +} + STATIC EFI_STATUS PvScsiInit ( @@ -466,6 +669,14 @@ PvScsiInit ( goto RestorePciAttributes; } + // + // Init PVSCSI rings + // + Status = PvScsiInitRings (Dev); + if (EFI_ERROR (Status)) { + goto RestorePciAttributes; + } + // // Populate the exported interface's attributes // @@ -509,6 +720,14 @@ PvScsiUninit ( IN OUT PVSCSI_DEV *Dev ) { + // + // Reset device to stop device usage of the rings. + // This is required to safely free the rings. + // + PvScsiResetAdapter (Dev); + + PvScsiFreeRings (Dev); + PvScsiRestorePciAttributes (Dev); } diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h index 5f611dbbc98c..6d23b6e1eccf 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.h +++ b/OvmfPkg/PvScsiDxe/PvScsi.h @@ -15,12 +15,29 @@ #include <Library/DebugLib.h> #include <Protocol/ScsiPassThruExt.h> +typedef struct { + EFI_PHYSICAL_ADDRESS DeviceAddress; + VOID *Mapping; +} PVSCSI_DMA_DESC; + +typedef struct { + PVSCSI_RINGS_STATE *RingState; + PVSCSI_DMA_DESC RingStateDmaDesc; + + PVSCSI_RING_REQ_DESC *RingReqs; + PVSCSI_DMA_DESC RingReqsDmaDesc; + + PVSCSI_RING_CMP_DESC *RingCmps; + PVSCSI_DMA_DESC RingCmpsDmaDesc; +} PVSCSI_RING_DESC; + #define PVSCSI_SIG SIGNATURE_32 ('P', 'S', 'C', 'S') typedef struct { UINT32 Signature; EFI_PCI_IO_PROTOCOL *PciIo; UINT64 OriginalPciAttributes; + PVSCSI_RING_DESC RingDesc; UINT8 MaxTarget; UINT8 MaxLun; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf index fcffc90d46c8..6200533698fc 100644 --- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf +++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf @@ -26,6 +26,7 @@ [Packages] OvmfPkg/OvmfPkg.dec [LibraryClasses] + BaseLib BaseMemoryLib DebugLib MemoryAllocationLib -- 2.27.0 From efad6e392e6efeaf4e2693036592da798b7232ce Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:57 +0300 Subject: [PATCH 14/43] OvmfPkg/PvScsiDxe: Introduce DMA communication buffer In case device is constrained by IOMMU or guest is running under AMD SEV, input/output buffers provided to device (DataBuffer and SenseData) needs to be explicitly mapped to device by PciIo->Map(). To avoid the overhead of mapping/unmapping the DataBuffer and SenseData to the device for every SCSI requst (and to simplify code), introduce a single DMA communication buffer that will be mapped to device on initialization. When a SCSI request needs to be sent to device, the DataBuffer and SenseData will be copied from/to the DMA communication buffer as required. This will be done by the following commits. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-15-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit 6510e1979491d28c82944e0cfdab5713d482efa1) --- OvmfPkg/PvScsiDxe/PvScsi.c | 60 ++++++++++++++++++++++++++++++-------- OvmfPkg/PvScsiDxe/PvScsi.h | 20 +++++++++++++ 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index c7d367e83a2d..6e350bb2d6e0 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -677,6 +677,19 @@ PvScsiInit ( goto RestorePciAttributes; } + // + // Allocate DMA communication buffer + // + Status = PvScsiAllocateSharedPages ( + Dev, + EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)), + (VOID **)&Dev->DmaBuf, + &Dev->DmaBufDmaDesc + ); + if (EFI_ERROR (Status)) { + goto FreeRings; + } + // // Populate the exported interface's attributes // @@ -708,18 +721,7 @@ PvScsiInit ( return EFI_SUCCESS; -RestorePciAttributes: - PvScsiRestorePciAttributes (Dev); - - return Status; -} - -STATIC -VOID -PvScsiUninit ( - IN OUT PVSCSI_DEV *Dev - ) -{ +FreeRings: // // Reset device to stop device usage of the rings. // This is required to safely free the rings. @@ -728,6 +730,40 @@ PvScsiUninit ( PvScsiFreeRings (Dev); +RestorePciAttributes: + PvScsiRestorePciAttributes (Dev); + + return Status; +} + +STATIC +VOID +PvScsiUninit ( + IN OUT PVSCSI_DEV *Dev + ) +{ + // + // Reset device to: + // - Make device stop processing all requests. + // - Stop device usage of the rings. + // + // This is required to safely free the DMA communication buffer + // and the rings. + // + PvScsiResetAdapter (Dev); + + // + // Free DMA communication buffer + // + PvScsiFreeSharedPages ( + Dev, + EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)), + Dev->DmaBuf, + &Dev->DmaBufDmaDesc + ); + + PvScsiFreeRings (Dev); + PvScsiRestorePciAttributes (Dev); } diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h index 6d23b6e1eccf..fff12146dc75 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.h +++ b/OvmfPkg/PvScsiDxe/PvScsi.h @@ -31,6 +31,21 @@ typedef struct { PVSCSI_DMA_DESC RingCmpsDmaDesc; } PVSCSI_RING_DESC; +typedef struct { + // + // As EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.SenseDataLength is defined + // as UINT8, defining here SenseData size to MAX_UINT8 will guarantee it + // cannot overflow when passed to device. + // + UINT8 SenseData[MAX_UINT8]; + // + // This size of the data is arbitrarily chosen. + // It seems to be sufficient for all I/O requests sent through + // EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() for common boot scenarios. + // + UINT8 Data[0x2000]; +} PVSCSI_DMA_BUFFER; + #define PVSCSI_SIG SIGNATURE_32 ('P', 'S', 'C', 'S') typedef struct { @@ -38,6 +53,8 @@ typedef struct { EFI_PCI_IO_PROTOCOL *PciIo; UINT64 OriginalPciAttributes; PVSCSI_RING_DESC RingDesc; + PVSCSI_DMA_BUFFER *DmaBuf; + PVSCSI_DMA_DESC DmaBufDmaDesc; UINT8 MaxTarget; UINT8 MaxLun; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; @@ -47,4 +64,7 @@ typedef struct { #define PVSCSI_FROM_PASS_THRU(PassThruPointer) \ CR (PassThruPointer, PVSCSI_DEV, PassThru, PVSCSI_SIG) +#define PVSCSI_DMA_BUF_DEV_ADDR(Dev, MemberName) \ + (Dev->DmaBufDmaDesc.DeviceAddress + OFFSET_OF(PVSCSI_DMA_BUFFER, MemberName)) + #endif // __PVSCSI_DXE_H_ -- 2.27.0 From 6791773eff3532209d31d57acb911da06780b362 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:58 +0300 Subject: [PATCH 15/43] OvmfPkg/PvScsiDxe: Support sending SCSI request and receive response Implement EXT_SCSI_PASS_THRU.PassThru(). Machines should be able to boot after this commit. Tested with Ubuntu 16.04 guest. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-16-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit c4c15b870239c2c23442dfc29c4421ac98d00ee0) --- OvmfPkg/OvmfPkg.dec | 6 + OvmfPkg/PvScsiDxe/PvScsi.c | 456 +++++++++++++++++++++++++++++++- OvmfPkg/PvScsiDxe/PvScsi.h | 1 + OvmfPkg/PvScsiDxe/PvScsiDxe.inf | 5 +- 4 files changed, 465 insertions(+), 3 deletions(-) diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index b0893c3591ee..06aed2b70818 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -130,6 +130,12 @@ [PcdsFixedAtBuild] gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit|64|UINT8|0x36 gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit|0|UINT8|0x37 + ## After PvScsiDxe sends a SCSI request to the device, it waits for + # the request completion in a polling loop. + # This constant defines how many micro-seconds to wait between each + # polling loop iteration. + gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiWaitForCmpStallInUsecs|5|UINT32|0x38 + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 6e350bb2d6e0..da3535c75220 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -32,6 +32,27 @@ // Ext SCSI Pass Thru utilities // +/** + Reads a 32-bit value into BAR0 using MMIO +**/ +STATIC +EFI_STATUS +PvScsiMmioRead32 ( + IN CONST PVSCSI_DEV *Dev, + IN UINT64 Offset, + OUT UINT32 *Value + ) +{ + return Dev->PciIo->Mem.Read ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX0, + Offset, + 1, // Count + Value + ); +} + /** Writes a 32-bit value into BAR0 using MMIO **/ @@ -133,6 +154,383 @@ PvScsiResetAdapter ( return PvScsiWriteCmdDesc (Dev, PvScsiCmdAdapterReset, NULL, 0); } +/** + Returns if PVSCSI request ring is full +**/ +STATIC +BOOLEAN +PvScsiIsReqRingFull ( + IN CONST PVSCSI_DEV *Dev + ) +{ + PVSCSI_RINGS_STATE *RingsState; + UINT32 ReqNumEntries; + + RingsState = Dev->RingDesc.RingState; + ReqNumEntries = 1U << RingsState->ReqNumEntriesLog2; + return (RingsState->ReqProdIdx - RingsState->CmpConsIdx) >= ReqNumEntries; +} + +/** + Returns pointer to current request descriptor to produce +**/ +STATIC +PVSCSI_RING_REQ_DESC * +PvScsiGetCurrentRequest ( + IN CONST PVSCSI_DEV *Dev + ) +{ + PVSCSI_RINGS_STATE *RingState; + UINT32 ReqNumEntries; + + RingState = Dev->RingDesc.RingState; + ReqNumEntries = 1U << RingState->ReqNumEntriesLog2; + return Dev->RingDesc.RingReqs + + (RingState->ReqProdIdx & (ReqNumEntries - 1)); +} + +/** + Returns pointer to current completion descriptor to consume +**/ +STATIC +PVSCSI_RING_CMP_DESC * +PvScsiGetCurrentResponse ( + IN CONST PVSCSI_DEV *Dev + ) +{ + PVSCSI_RINGS_STATE *RingState; + UINT32 CmpNumEntries; + + RingState = Dev->RingDesc.RingState; + CmpNumEntries = 1U << RingState->CmpNumEntriesLog2; + return Dev->RingDesc.RingCmps + + (RingState->CmpConsIdx & (CmpNumEntries - 1)); +} + +/** + Wait for device to signal completion of submitted requests +**/ +STATIC +EFI_STATUS +PvScsiWaitForRequestCompletion ( + IN CONST PVSCSI_DEV *Dev + ) +{ + EFI_STATUS Status; + UINT32 IntrStatus; + + // + // Note: We don't yet support Timeout according to + // EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.Timeout. + // + // This is consistent with some other Scsi PassThru drivers + // such as VirtioScsi. + // + for (;;) { + Status = PvScsiMmioRead32 (Dev, PvScsiRegOffsetIntrStatus, &IntrStatus); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // PVSCSI_INTR_CMPL_MASK is set if device completed submitted requests + // + if ((IntrStatus & PVSCSI_INTR_CMPL_MASK) != 0) { + break; + } + + gBS->Stall (Dev->WaitForCmpStallInUsecs); + } + + // + // Acknowledge PVSCSI_INTR_CMPL_MASK in device interrupt-status register + // + return PvScsiMmioWrite32 ( + Dev, + PvScsiRegOffsetIntrStatus, + PVSCSI_INTR_CMPL_MASK + ); +} + +/** + Create a fake host adapter error +**/ +STATIC +EFI_STATUS +ReportHostAdapterError ( + OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + Packet->InTransferLength = 0; + Packet->OutTransferLength = 0; + Packet->SenseDataLength = 0; + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + return EFI_DEVICE_ERROR; +} + +/** + Create a fake host adapter overrun error +**/ +STATIC +EFI_STATUS +ReportHostAdapterOverrunError ( + OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + Packet->SenseDataLength = 0; + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + return EFI_BAD_BUFFER_SIZE; +} + +/** + Populate a PVSCSI request descriptor from the Extended SCSI Pass Thru + Protocol packet. +**/ +STATIC +EFI_STATUS +PopulateRequest ( + IN CONST PVSCSI_DEV *Dev, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + OUT PVSCSI_RING_REQ_DESC *Request + ) +{ + UINT8 TargetValue; + + // + // We only use first byte of target identifer + // + TargetValue = *Target; + + // + // Check for unsupported requests + // + if ( + // + // Bidirectional transfer was requested + // + (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) || + (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) || + // + // Command Descriptor Block bigger than this constant should be considered + // out-of-band. We currently don't support these CDBs. + // + (Packet->CdbLength > PVSCSI_CDB_MAX_SIZE) + ) { + + // + // This error code doesn't require updates to the Packet output fields + // + return EFI_UNSUPPORTED; + } + + // + // Check for invalid parameters + // + if ( + // + // Addressed invalid device + // + (TargetValue > Dev->MaxTarget) || (Lun > Dev->MaxLun) || + // + // Invalid direction (there doesn't seem to be a macro for the "no data + // transferred" "direction", eg. for TEST UNIT READY) + // + (Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) || + // + // Trying to receive, but destination pointer is NULL, or contradicting + // transfer direction + // + ((Packet->InTransferLength > 0) && + ((Packet->InDataBuffer == NULL) || + (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE) + ) + ) || + // + // Trying to send, but source pointer is NULL, or contradicting + // transfer direction + // + ((Packet->OutTransferLength > 0) && + ((Packet->OutDataBuffer == NULL) || + (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) + ) + ) + ) { + + // + // This error code doesn't require updates to the Packet output fields + // + return EFI_INVALID_PARAMETER; + } + + // + // Check for input/output buffer too large for DMA communication buffer + // + if (Packet->InTransferLength > sizeof (Dev->DmaBuf->Data)) { + Packet->InTransferLength = sizeof (Dev->DmaBuf->Data); + return ReportHostAdapterOverrunError (Packet); + } + if (Packet->OutTransferLength > sizeof (Dev->DmaBuf->Data)) { + Packet->OutTransferLength = sizeof (Dev->DmaBuf->Data); + return ReportHostAdapterOverrunError (Packet); + } + + // + // Encode PVSCSI request + // + ZeroMem (Request, sizeof (*Request)); + + Request->Bus = 0; + Request->Target = TargetValue; + // + // This cast is safe as PVSCSI_DEV.MaxLun is defined as UINT8 + // + Request->Lun[1] = (UINT8)Lun; + Request->SenseLen = Packet->SenseDataLength; + // + // DMA communication buffer SenseData overflow is not possible + // due to Packet->SenseDataLength defined as UINT8 + // + Request->SenseAddr = PVSCSI_DMA_BUF_DEV_ADDR (Dev, SenseData); + Request->CdbLen = Packet->CdbLength; + CopyMem (Request->Cdb, Packet->Cdb, Packet->CdbLength); + Request->VcpuHint = 0; + Request->Tag = PVSCSI_SIMPLE_QUEUE_TAG; + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + Request->Flags = PVSCSI_FLAG_CMD_DIR_TOHOST; + Request->DataLen = Packet->InTransferLength; + } else { + Request->Flags = PVSCSI_FLAG_CMD_DIR_TODEVICE; + Request->DataLen = Packet->OutTransferLength; + CopyMem ( + Dev->DmaBuf->Data, + Packet->OutDataBuffer, + Packet->OutTransferLength + ); + } + Request->DataAddr = PVSCSI_DMA_BUF_DEV_ADDR (Dev, Data); + + return EFI_SUCCESS; +} + +/** + Handle the PVSCSI device response: + - Copy returned data from DMA communication buffer. + - Update fields in Extended SCSI Pass Thru Protocol packet as required. + - Translate response code to EFI status code and host adapter status. +**/ +STATIC +EFI_STATUS +HandleResponse ( + IN PVSCSI_DEV *Dev, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN CONST PVSCSI_RING_CMP_DESC *Response + ) +{ + // + // Fix SenseDataLength to amount of data returned + // + if (Packet->SenseDataLength > Response->SenseLen) { + Packet->SenseDataLength = (UINT8)Response->SenseLen; + } + // + // Copy sense data from DMA communication buffer + // + CopyMem ( + Packet->SenseData, + Dev->DmaBuf->SenseData, + Packet->SenseDataLength + ); + + // + // Copy device output from DMA communication buffer + // + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + CopyMem (Packet->InDataBuffer, Dev->DmaBuf->Data, Packet->InTransferLength); + } + + // + // Report target status + // + Packet->TargetStatus = Response->ScsiStatus; + + // + // Host adapter status and function return value depend on + // device response's host status + // + switch (Response->HostStatus) { + case PvScsiBtStatSuccess: + case PvScsiBtStatLinkedCommandCompleted: + case PvScsiBtStatLinkedCommandCompletedWithFlag: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK; + return EFI_SUCCESS; + + case PvScsiBtStatDataUnderrun: + // + // Report transferred amount in underrun + // + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + Packet->InTransferLength = (UINT32)Response->DataLen; + } else { + Packet->OutTransferLength = (UINT32)Response->DataLen; + } + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN; + return EFI_SUCCESS; + + case PvScsiBtStatDatarun: + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN; + return EFI_SUCCESS; + + case PvScsiBtStatSelTimeout: + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT; + return EFI_TIMEOUT; + + case PvScsiBtStatBusFree: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE; + break; + + case PvScsiBtStatInvPhase: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR; + break; + + case PvScsiBtStatSensFailed: + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED; + break; + + case PvScsiBtStatTagReject: + case PvScsiBtStatBadMsg: + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT; + break; + + case PvScsiBtStatBusReset: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET; + break; + + case PvScsiBtStatHaTimeout: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT; + return EFI_TIMEOUT; + + case PvScsiBtStatScsiParity: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR; + break; + + default: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + break; + } + + return EFI_DEVICE_ERROR; +} + /** Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized @@ -168,7 +566,62 @@ PvScsiPassThru ( IN EFI_EVENT Event OPTIONAL ) { - return EFI_UNSUPPORTED; + PVSCSI_DEV *Dev; + EFI_STATUS Status; + PVSCSI_RING_REQ_DESC *Request; + PVSCSI_RING_CMP_DESC *Response; + + Dev = PVSCSI_FROM_PASS_THRU (This); + + if (PvScsiIsReqRingFull (Dev)) { + return EFI_NOT_READY; + } + + Request = PvScsiGetCurrentRequest (Dev); + + Status = PopulateRequest (Dev, Target, Lun, Packet, Request); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Writes to Request must be globally visible before making request + // available to device + // + MemoryFence (); + Dev->RingDesc.RingState->ReqProdIdx++; + + Status = PvScsiMmioWrite32 (Dev, PvScsiRegOffsetKickRwIo, 0); + if (EFI_ERROR (Status)) { + // + // If kicking the host fails, we must fake a host adapter error. + // EFI_NOT_READY would save us the effort, but it would also suggest that + // the caller retry. + // + return ReportHostAdapterError (Packet); + } + + Status = PvScsiWaitForRequestCompletion (Dev); + if (EFI_ERROR (Status)) { + // + // If waiting for request completion fails, we must fake a host adapter + // error. EFI_NOT_READY would save us the effort, but it would also suggest + // that the caller retry. + // + return ReportHostAdapterError (Packet); + } + + Response = PvScsiGetCurrentResponse (Dev); + Status = HandleResponse (Dev, Packet, Response); + + // + // Reads from response must complete before releasing completion entry + // to device + // + MemoryFence (); + Dev->RingDesc.RingState->CmpConsIdx++; + + return Status; } STATIC @@ -652,6 +1105,7 @@ PvScsiInit ( // Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit); Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit); + Dev->WaitForCmpStallInUsecs = PcdGet32 (PcdPvScsiWaitForCmpStallInUsecs); // // Set PCI Attributes diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h index fff12146dc75..02feac734743 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.h +++ b/OvmfPkg/PvScsiDxe/PvScsi.h @@ -57,6 +57,7 @@ typedef struct { PVSCSI_DMA_DESC DmaBufDmaDesc; UINT8 MaxTarget; UINT8 MaxLun; + UINTN WaitForCmpStallInUsecs; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; } PVSCSI_DEV; diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf index 6200533698fc..284035fb10d4 100644 --- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf +++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf @@ -39,5 +39,6 @@ [Protocols] gEfiPciIoProtocolGuid ## TO_START [Pcd] - gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit ## CONSUMES - gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiWaitForCmpStallInUsecs ## CONSUMES -- 2.27.0 From 61837682b71ac2da6dc87b542df983a44eb3b89a Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:00:59 +0300 Subject: [PATCH 16/43] OvmfPkg/PvScsiDxe: Reset device on ExitBootServices() This causes the device to forget about the request/completion rings. We allocated said rings in EfiBootServicesData type memory, and code executing after ExitBootServices() is permitted to overwrite it. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-17-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit 7d8a04e9d2fd8a5298636d5a0c702a978b5f22f1) --- OvmfPkg/PvScsiDxe/PvScsi.c | 43 +++++++++++++++++++++++++++++++++++++- OvmfPkg/PvScsiDxe/PvScsi.h | 1 + 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index da3535c75220..d7f0d3c8790c 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -1221,6 +1221,31 @@ PvScsiUninit ( PvScsiRestorePciAttributes (Dev); } +/** + Event notification called by ExitBootServices() +**/ +STATIC +VOID +EFIAPI +PvScsiExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PVSCSI_DEV *Dev; + + Dev = Context; + DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context)); + + // + // Reset the device to stop device usage of the rings. + // + // We allocated said rings in EfiBootServicesData type memory, and code + // executing after ExitBootServices() is permitted to overwrite it. + // + PvScsiResetAdapter (Dev); +} + // // Driver Binding // @@ -1314,6 +1339,17 @@ PvScsiDriverBindingStart ( goto ClosePciIo; } + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_CALLBACK, + &PvScsiExitBoot, + Dev, + &Dev->ExitBoot + ); + if (EFI_ERROR (Status)) { + goto UninitDev; + } + // // Setup complete, attempt to export the driver instance's PassThru interface // @@ -1325,11 +1361,14 @@ PvScsiDriverBindingStart ( &Dev->PassThru ); if (EFI_ERROR (Status)) { - goto UninitDev; + goto CloseExitBoot; } return EFI_SUCCESS; +CloseExitBoot: + gBS->CloseEvent (Dev->ExitBoot); + UninitDev: PvScsiUninit (Dev); @@ -1384,6 +1423,8 @@ PvScsiDriverBindingStop ( return Status; } + gBS->CloseEvent (Dev->ExitBoot); + PvScsiUninit (Dev); gBS->CloseProtocol ( diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h index 02feac734743..544359ebc05c 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.h +++ b/OvmfPkg/PvScsiDxe/PvScsi.h @@ -51,6 +51,7 @@ typedef struct { typedef struct { UINT32 Signature; EFI_PCI_IO_PROTOCOL *PciIo; + EFI_EVENT ExitBoot; UINT64 OriginalPciAttributes; PVSCSI_RING_DESC RingDesc; PVSCSI_DMA_BUFFER *DmaBuf; -- 2.27.0 From 2bfb72a44735e621e765b7b44a74a4bbf7baab82 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Sat, 28 Mar 2020 23:01:00 +0300 Subject: [PATCH 17/43] OvmfPkg/PvScsiDxe: Enable device 64-bit DMA addresses Enable PCI dual-address cycle attribute to signal device supports 64-bit DMA addresses. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567 Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200328200100.60786-18-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> (cherry picked from commit f34c7645bd87c61f72c2bc4a8f88afabc69de512) --- OvmfPkg/PvScsiDxe/PvScsi.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index d7f0d3c8790c..0a66c98421a9 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -874,6 +874,29 @@ PvScsiSetPciAttributes ( return Status; } + // + // Signal device supports 64-bit DMA addresses + // + Status = Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE, + NULL + ); + if (EFI_ERROR (Status)) { + // + // Warn user that device will only be using 32-bit DMA addresses. + // + // Note that this does not prevent the device/driver from working + // and therefore we only warn and continue as usual. + // + DEBUG (( + DEBUG_WARN, + "%a: failed to enable 64-bit DMA addresses\n", + __FUNCTION__ + )); + } + return EFI_SUCCESS; } -- 2.27.0 From b7591ff579fba871940e823a18ce96db7836563d Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Tue, 31 Mar 2020 14:02:44 +0300 Subject: [PATCH 18/43] Maintainers.txt: Add Liran and Nikita as OvmfPkg/PvScsiDxe reviewers Laszlo suggested that as I have contributed the OvmfPkg PVSCSI driver, I will also register myself as a reviewer in Maintainers.txt. In addition, as Nikita have assisted the development of the PVSCSI driver and have developed another similar OvmfPkg SCSI driver, add him as a reviewer to PVSCSI driver as-well. Cc: Nikita Leshenko <nikita.leshchenko@oracle.com> Suggested-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200331110244.51409-1-liran.alon@oracle.com> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com> [lersek@redhat.com: fixup "F:" pattern by appending slash character] Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit 335644f90f15fac8bfd5575f937fa65af4978a08) --- Maintainers.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Maintainers.txt b/Maintainers.txt index 180ad35d0976..a75d5e1f916f 100644 --- a/Maintainers.txt +++ b/Maintainers.txt @@ -435,6 +435,11 @@ OvmfPkg: CSM modules F: OvmfPkg/Csm/ R: David Woodhouse <dwmw2@infradead.org> +OvmfPkg: PVSCSI driver +F: OvmfPkg/PvScsiDxe/ +R: Liran Alon <liran.alon@oracle.com> +R: Nikita Leshenko <nikita.leshchenko@oracle.com> + PcAtChipsetPkg F: PcAtChipsetPkg/ W: https://github.com/tianocore/tianocore.github.io/wiki/PcAtChipsetPkg -- 2.27.0 From 61e3d52d532985e1fc13125a79c61fdc5ef16cec Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Tue, 31 Mar 2020 14:04:52 +0300 Subject: [PATCH 19/43] OvmfPkg/PvScsiDxe: Fix VS2019 build error because of implicit cast Sean reported that VS2019 build produce the following build error: INFO - PvScsi.c INFO - Generating code INFO - d:\a\1\s\OvmfPkg\PvScsiDxe\PvScsi.c(459): error C2220: the following warning is treated as an error INFO - d:\a\1\s\OvmfPkg\PvScsiDxe\PvScsi.c(459): warning C4244: '=': conversion from 'const UINT16' to 'UINT8', possible loss of data This result from an implicit cast from PVSCSI Response->ScsiStatus (Which is UINT16) to Packet->TargetResponse (Which is UINT8). Fix this issue by adding an appropriate explicit cast and verify with assert that this truncation do not result in loss of data. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2651 Reported-by: Sean Brogan <sean.brogan@microsoft.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200331110452.51992-1-liran.alon@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> [lersek@redhat.com: rewrap VS2019 diags in commit msg for PatchCheck.py] (cherry picked from commit 98936dc4f44b4ef47e7221d435de06a0813aa00a) --- OvmfPkg/PvScsiDxe/PvScsi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 0a66c98421a9..1ca50390c0e5 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -455,8 +455,12 @@ HandleResponse ( // // Report target status + // (Strangely, PVSCSI interface defines Response->ScsiStatus as UINT16. + // But it should de-facto always have a value that fits UINT8. To avoid + // unexpected behavior, verify value is in UINT8 bounds before casting) // - Packet->TargetStatus = Response->ScsiStatus; + ASSERT (Response->ScsiStatus <= MAX_UINT8); + Packet->TargetStatus = (UINT8)Response->ScsiStatus; // // Host adapter status and function return value depend on -- 2.27.0 From edc25314cbe771c4f151c2dd76596c50000fa0e7 Mon Sep 17 00:00:00 2001 From: Liran Alon <liran.alon@oracle.com> Date: Wed, 1 Apr 2020 01:56:37 +0300 Subject: [PATCH 20/43] OvmfPkg/PvScsiDxe: Refactor setup of rings to separate function Previous to this change, PvScsiFreeRings() was not undoing all operations that was done by PvScsiInitRings(). This is because PvScsiInitRings() was both preparing rings (Allocate memory and map it for device DMA) and setup the rings against device by issueing a device command. While PvScsiFreeRings() only unmaps the rings and free their memory. Driver do not have a functional error as it makes sure to reset device before every call site to PvScsiFreeRings(). However, this is not intuitive. Therefore, prefer to refactor the setup of the ring against device to a separate function than PvScsiInitRings(). Signed-off-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200331225637.123318-1-liran.alon@oracle.com> [lersek@redhat.com: rename FreeDMACommBuffer label to FreeDmaCommBuffer] Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit e210fc130e5c9738909dca432bbf8bf277ba6e37) --- OvmfPkg/PvScsiDxe/PvScsi.c | 163 +++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 78 deletions(-) diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c index 1ca50390c0e5..843534ebf711 100644 --- a/OvmfPkg/PvScsiDxe/PvScsi.c +++ b/OvmfPkg/PvScsiDxe/PvScsi.c @@ -991,13 +991,6 @@ PvScsiInitRings ( ) { EFI_STATUS Status; - union { - PVSCSI_CMD_DESC_SETUP_RINGS Cmd; - UINT32 Uint32; - } AlignedCmd; - PVSCSI_CMD_DESC_SETUP_RINGS *Cmd; - - Cmd = &AlignedCmd.Cmd; Status = PvScsiAllocateSharedPages ( Dev, @@ -1032,6 +1025,69 @@ PvScsiInitRings ( } ZeroMem (Dev->RingDesc.RingCmps, EFI_PAGE_SIZE); + return EFI_SUCCESS; + +FreeRingReqs: + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingReqs, + &Dev->RingDesc.RingReqsDmaDesc + ); + +FreeRingState: + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingState, + &Dev->RingDesc.RingStateDmaDesc + ); + + return Status; +} + +STATIC +VOID +PvScsiFreeRings ( + IN OUT PVSCSI_DEV *Dev + ) +{ + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingCmps, + &Dev->RingDesc.RingCmpsDmaDesc + ); + + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingReqs, + &Dev->RingDesc.RingReqsDmaDesc + ); + + PvScsiFreeSharedPages ( + Dev, + 1, + Dev->RingDesc.RingState, + &Dev->RingDesc.RingStateDmaDesc + ); +} + +STATIC +EFI_STATUS +PvScsiSetupRings ( + IN OUT PVSCSI_DEV *Dev + ) +{ + union { + PVSCSI_CMD_DESC_SETUP_RINGS Cmd; + UINT32 Uint32; + } AlignedCmd; + PVSCSI_CMD_DESC_SETUP_RINGS *Cmd; + + Cmd = &AlignedCmd.Cmd; + ZeroMem (Cmd, sizeof (*Cmd)); Cmd->ReqRingNumPages = 1; Cmd->CmpRingNumPages = 1; @@ -1052,71 +1108,12 @@ PvScsiInitRings ( sizeof (*Cmd) % sizeof (UINT32) == 0, "Cmd must be multiple of 32-bit words" ); - Status = PvScsiWriteCmdDesc ( - Dev, - PvScsiCmdSetupRings, - (UINT32 *)Cmd, - sizeof (*Cmd) / sizeof (UINT32) - ); - if (EFI_ERROR (Status)) { - goto FreeRingCmps; - } - - return EFI_SUCCESS; - -FreeRingCmps: - PvScsiFreeSharedPages ( - Dev, - 1, - Dev->RingDesc.RingCmps, - &Dev->RingDesc.RingCmpsDmaDesc - ); - -FreeRingReqs: - PvScsiFreeSharedPages ( - Dev, - 1, - Dev->RingDesc.RingReqs, - &Dev->RingDesc.RingReqsDmaDesc - ); - -FreeRingState: - PvScsiFreeSharedPages ( - Dev, - 1, - Dev->RingDesc.RingState, - &Dev->RingDesc.RingStateDmaDesc - ); - - return Status; -} - -STATIC -VOID -PvScsiFreeRings ( - IN OUT PVSCSI_DEV *Dev - ) -{ - PvScsiFreeSharedPages ( - Dev, - 1, - Dev->RingDesc.RingCmps, - &Dev->RingDesc.RingCmpsDmaDesc - ); - - PvScsiFreeSharedPages ( - Dev, - 1, - Dev->RingDesc.RingReqs, - &Dev->RingDesc.RingReqsDmaDesc - ); - - PvScsiFreeSharedPages ( - Dev, - 1, - Dev->RingDesc.RingState, - &Dev->RingDesc.RingStateDmaDesc - ); + return PvScsiWriteCmdDesc ( + Dev, + PvScsiCmdSetupRings, + (UINT32 *)Cmd, + sizeof (*Cmd) / sizeof (UINT32) + ); } STATIC @@ -1171,6 +1168,14 @@ PvScsiInit ( goto FreeRings; } + // + // Setup rings against device + // + Status = PvScsiSetupRings (Dev); + if (EFI_ERROR (Status)) { + goto FreeDmaCommBuffer; + } + // // Populate the exported interface's attributes // @@ -1202,13 +1207,15 @@ PvScsiInit ( return EFI_SUCCESS; +FreeDmaCommBuffer: + PvScsiFreeSharedPages ( + Dev, + EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)), + Dev->DmaBuf, + &Dev->DmaBufDmaDesc + ); + FreeRings: - // - // Reset device to stop device usage of the rings. - // This is required to safely free the rings. - // - PvScsiResetAdapter (Dev); - PvScsiFreeRings (Dev); RestorePciAttributes: -- 2.27.0 From db0155248bcda9b6b77767eedba8912c7242d769 Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:05:56 +0300 Subject: [PATCH 21/43] OvmfPkg/MptScsiDxe: Create empty driver In preparation for implementing LSI Fusion MPT SCSI devices, create a basic scaffolding for a driver. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200504210607.144434-2-nikita.leshchenko@oracle.com> (cherry picked from commit feec20b28dd0291b1a5d951e856d642659d23f7e) --- Maintainers.txt | 3 ++- OvmfPkg/MptScsiDxe/MptScsi.c | 26 ++++++++++++++++++++++++++ OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 26 ++++++++++++++++++++++++++ OvmfPkg/OvmfPkgIa32.dsc | 4 ++++ OvmfPkg/OvmfPkgIa32.fdf | 3 +++ OvmfPkg/OvmfPkgIa32X64.dsc | 4 ++++ OvmfPkg/OvmfPkgIa32X64.fdf | 3 +++ OvmfPkg/OvmfPkgX64.dsc | 4 ++++ OvmfPkg/OvmfPkgX64.fdf | 3 +++ 9 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 OvmfPkg/MptScsiDxe/MptScsi.c create mode 100644 OvmfPkg/MptScsiDxe/MptScsiDxe.inf diff --git a/Maintainers.txt b/Maintainers.txt index a75d5e1f916f..3b0ec2b8bea6 100644 --- a/Maintainers.txt +++ b/Maintainers.txt @@ -435,7 +435,8 @@ OvmfPkg: CSM modules F: OvmfPkg/Csm/ R: David Woodhouse <dwmw2@infradead.org> -OvmfPkg: PVSCSI driver +OvmfPkg: MptScsi and PVSCSI driver +F: OvmfPkg/MptScsiDxe/ F: OvmfPkg/PvScsiDxe/ R: Liran Alon <liran.alon@oracle.com> R: Nikita Leshenko <nikita.leshchenko@oracle.com> diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c new file mode 100644 index 000000000000..c6c8142dfde6 --- /dev/null +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -0,0 +1,26 @@ +/** @file + + This driver produces Extended SCSI Pass Thru Protocol instances for + LSI Fusion MPT SCSI devices. + + Copyright (C) 2020, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi/UefiSpec.h> + +// +// Entry Point +// + +EFI_STATUS +EFIAPI +MptScsiEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf new file mode 100644 index 000000000000..b4006a7c2d97 --- /dev/null +++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf @@ -0,0 +1,26 @@ +## @file +# This driver produces Extended SCSI Pass Thru Protocol instances for +# LSI Fusion MPT SCSI devices. +# +# Copyright (C) 2020, Oracle and/or its affiliates. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.29 + BASE_NAME = MptScsiDxe + FILE_GUID = 2B3DB5DD-B315-4961-8454-0AFF3C811B19 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = MptScsiEntryPoint + +[Sources] + MptScsi.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 82a9444bb92b..d391ee0a28df 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -48,6 +48,7 @@ [Defines] # Device drivers # DEFINE PVSCSI_ENABLE = TRUE + DEFINE MPT_SCSI_ENABLE = TRUE # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to @@ -718,6 +719,9 @@ [Components] OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf !if $(PVSCSI_ENABLE) == TRUE OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif +!if $(MPT_SCSI_ENABLE) == TRUE + OvmfPkg/MptScsiDxe/MptScsiDxe.inf !endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf index d6b9c74e6cfe..b47d50276d13 100644 --- a/OvmfPkg/OvmfPkgIa32.fdf +++ b/OvmfPkg/OvmfPkgIa32.fdf @@ -230,6 +230,9 @@ [FV.DXEFV] !if $(PVSCSI_ENABLE) == TRUE INF OvmfPkg/PvScsiDxe/PvScsiDxe.inf !endif +!if $(MPT_SCSI_ENABLE) == TRUE +INF OvmfPkg/MptScsiDxe/MptScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index d34a51e86ba1..2341d7f53a1c 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -48,6 +48,7 @@ [Defines] # Device drivers # DEFINE PVSCSI_ENABLE = TRUE + DEFINE MPT_SCSI_ENABLE = TRUE # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to @@ -731,6 +732,9 @@ [Components.X64] OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf !if $(PVSCSI_ENABLE) == TRUE OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif +!if $(MPT_SCSI_ENABLE) == TRUE + OvmfPkg/MptScsiDxe/MptScsiDxe.inf !endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf index bce1bdb42d71..af0f4c958841 100644 --- a/OvmfPkg/OvmfPkgIa32X64.fdf +++ b/OvmfPkg/OvmfPkgIa32X64.fdf @@ -231,6 +231,9 @@ [FV.DXEFV] !if $(PVSCSI_ENABLE) == TRUE INF OvmfPkg/PvScsiDxe/PvScsiDxe.inf !endif +!if $(MPT_SCSI_ENABLE) == TRUE +INF OvmfPkg/MptScsiDxe/MptScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 55d975c4d3f9..0036455d9c62 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -48,6 +48,7 @@ [Defines] # Device drivers # DEFINE PVSCSI_ENABLE = TRUE + DEFINE MPT_SCSI_ENABLE = TRUE # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to @@ -729,6 +730,9 @@ [Components] OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf !if $(PVSCSI_ENABLE) == TRUE OvmfPkg/PvScsiDxe/PvScsiDxe.inf +!endif +!if $(MPT_SCSI_ENABLE) == TRUE + OvmfPkg/MptScsiDxe/MptScsiDxe.inf !endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index bce1bdb42d71..af0f4c958841 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -231,6 +231,9 @@ [FV.DXEFV] !if $(PVSCSI_ENABLE) == TRUE INF OvmfPkg/PvScsiDxe/PvScsiDxe.inf !endif +!if $(MPT_SCSI_ENABLE) == TRUE +INF OvmfPkg/MptScsiDxe/MptScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf -- 2.27.0 From 206fa42f38976013017b428632d761da5ee39297 Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:05:57 +0300 Subject: [PATCH 22/43] OvmfPkg/MptScsiDxe: Install DriverBinding Protocol In order to probe and connect to the MptScsi device we need this protocol. Currently it does nothing. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200504210607.144434-3-nikita.leshchenko@oracle.com> (cherry picked from commit ad8f2d6b073e5c3a9612c6ca78174f8e4890c98d) --- OvmfPkg/MptScsiDxe/MptScsi.c | 67 ++++++++++++++++++++++++++++++- OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 1 + 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index c6c8142dfde6..581d3909b84d 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -9,8 +9,66 @@ **/ +#include <Library/UefiLib.h> #include <Uefi/UefiSpec.h> +// +// Higher versions will be used before lower, 0x10-0xffffffef is the version +// range for IVH (Indie Hardware Vendors) +// +#define MPT_SCSI_BINDING_VERSION 0x10 + +// +// Driver Binding +// + +STATIC +EFI_STATUS +EFIAPI +MptScsiControllerSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +MptScsiControllerStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +MptScsiControllerStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_DRIVER_BINDING_PROTOCOL mMptScsiDriverBinding = { + &MptScsiControllerSupported, + &MptScsiControllerStart, + &MptScsiControllerStop, + MPT_SCSI_BINDING_VERSION, + NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2 + NULL, // DriverBindingHandle, filled as well +}; + // // Entry Point // @@ -22,5 +80,12 @@ MptScsiEntryPoint ( IN EFI_SYSTEM_TABLE *SystemTable ) { - return EFI_UNSUPPORTED; + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &mMptScsiDriverBinding, + ImageHandle, // The handle to install onto + NULL, // TODO Component name + NULL // TODO Component name + ); } diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf index b4006a7c2d97..53585068684f 100644 --- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf +++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf @@ -24,3 +24,4 @@ [Packages] [LibraryClasses] UefiDriverEntryPoint + UefiLib -- 2.27.0 From 629cce5c452a436642699c67b83e8e64a993ffec Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:05:58 +0300 Subject: [PATCH 23/43] OvmfPkg/MptScsiDxe: Report name of driver Install Component Name protocols to have a nice display name for the driver in places such as UEFI shell. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jaben Carsey <jaben.carsey@intel.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200504210607.144434-4-nikita.leshchenko@oracle.com> (cherry picked from commit be7fcaa1c9e952aaacdcbe806933ce70a391aa78) --- OvmfPkg/MptScsiDxe/MptScsi.c | 61 ++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 581d3909b84d..64949a809022 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -69,6 +69,63 @@ EFI_DRIVER_BINDING_PROTOCOL mMptScsiDriverBinding = { NULL, // DriverBindingHandle, filled as well }; +// +// Component Name +// + +STATIC +EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { + { "eng;en", L"LSI Fusion MPT SCSI Driver" }, + { NULL, NULL } +}; + +STATIC +EFI_COMPONENT_NAME_PROTOCOL mComponentName; + +EFI_STATUS +EFIAPI +MptScsiGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDriverNameTable, + DriverName, + (BOOLEAN)(This == &mComponentName) // Iso639Language + ); +} + +EFI_STATUS +EFIAPI +MptScsiGetDeviceName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN EFI_HANDLE ChildHandle, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_COMPONENT_NAME_PROTOCOL mComponentName = { + &MptScsiGetDriverName, + &MptScsiGetDeviceName, + "eng" // SupportedLanguages, ISO 639-2 language codes +}; + +STATIC +EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &MptScsiGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &MptScsiGetDeviceName, + "en" // SupportedLanguages, RFC 4646 language codes +}; + // // Entry Point // @@ -85,7 +142,7 @@ MptScsiEntryPoint ( SystemTable, &mMptScsiDriverBinding, ImageHandle, // The handle to install onto - NULL, // TODO Component name - NULL // TODO Component name + &mComponentName, + &mComponentName2 ); } -- 2.27.0 From ead01d91cb8aeaab6a8eb6a535d2c291b4343435 Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:05:59 +0300 Subject: [PATCH 24/43] OvmfPkg/MptScsiDxe: Probe PCI devices and look for MptScsi The MptScsiControllerSupported function is called on handles passed in by the ConnectController() boot service and if the handle is the lsi53c1030 controller the function would return success. A successful return value will attach our driver to the device. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> Message-Id: <20200504210607.144434-5-nikita.leshchenko@oracle.com> (cherry picked from commit f47074425d2ecdd03cb651cc218b88226ad4a0fb) --- .../Include/IndustryStandard/FusionMptScsi.h | 23 +++++++++ OvmfPkg/MptScsiDxe/MptScsi.c | 49 ++++++++++++++++++- OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 5 ++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 OvmfPkg/Include/IndustryStandard/FusionMptScsi.h diff --git a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h new file mode 100644 index 000000000000..df9bdc2f0348 --- /dev/null +++ b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h @@ -0,0 +1,23 @@ +/** @file + + Macros and type definitions for LSI Fusion MPT SCSI devices. + + Copyright (C) 2020, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __FUSION_MPT_SCSI_H__ +#define __FUSION_MPT_SCSI_H__ + +// +// Device offsets and constants +// + +#define LSI_LOGIC_PCI_VENDOR_ID 0x1000 +#define LSI_53C1030_PCI_DEVICE_ID 0x0030 +#define LSI_SAS1068_PCI_DEVICE_ID 0x0054 +#define LSI_SAS1068E_PCI_DEVICE_ID 0x0058 + +#endif // __FUSION_MPT_SCSI_H__ diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 64949a809022..4e2f8f2296fb 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -9,7 +9,11 @@ **/ +#include <IndustryStandard/FusionMptScsi.h> +#include <IndustryStandard/Pci.h> +#include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> +#include <Protocol/PciIo.h> #include <Uefi/UefiSpec.h> // @@ -31,7 +35,50 @@ MptScsiControllerSupported ( IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + (VOID **)&PciIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID && + (Pci.Hdr.DeviceId == LSI_53C1030_PCI_DEVICE_ID || + Pci.Hdr.DeviceId == LSI_SAS1068_PCI_DEVICE_ID || + Pci.Hdr.DeviceId == LSI_SAS1068E_PCI_DEVICE_ID)) { + Status = EFI_SUCCESS; + } else { + Status = EFI_UNSUPPORTED; + } + +Done: + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return Status; } STATIC diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf index 53585068684f..414b96e5a248 100644 --- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf +++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf @@ -21,7 +21,12 @@ [Sources] [Packages] MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec [LibraryClasses] + UefiBootServicesTableLib UefiDriverEntryPoint UefiLib + +[Protocols] + gEfiPciIoProtocolGuid ## TO_START -- 2.27.0 From 81a88638f64aa1f328da5642edeff785ea59849e Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:06:00 +0300 Subject: [PATCH 25/43] OvmfPkg/MptScsiDxe: Install stubbed EXT_SCSI_PASS_THRU Support dynamic insertion and removal of the protocol Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200504210607.144434-6-nikita.leshchenko@oracle.com> (cherry picked from commit a53e5b417413bae3f3ea89976380d169e2f6861d) --- OvmfPkg/MptScsiDxe/MptScsi.c | 181 +++++++++++++++++++++++++++++- OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 5 +- 2 files changed, 183 insertions(+), 3 deletions(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 4e2f8f2296fb..40d392c2346f 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -11,9 +11,12 @@ #include <IndustryStandard/FusionMptScsi.h> #include <IndustryStandard/Pci.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> #include <Protocol/PciIo.h> +#include <Protocol/ScsiPassThruExt.h> #include <Uefi/UefiSpec.h> // @@ -22,6 +25,109 @@ // #define MPT_SCSI_BINDING_VERSION 0x10 +// +// Runtime Structures +// + +#define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S') +typedef struct { + UINT32 Signature; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; + EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; +} MPT_SCSI_DEV; + +#define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \ + CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE) + +// +// Ext SCSI Pass Thru +// + +STATIC +EFI_STATUS +EFIAPI +MptScsiPassThru ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +MptScsiGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +MptScsiGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +MptScsiBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +MptScsiGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +MptScsiResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +EFIAPI +MptScsiResetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ) +{ + return EFI_UNSUPPORTED; +} + // // Driver Binding // @@ -90,7 +196,49 @@ MptScsiControllerStart ( IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + MPT_SCSI_DEV *Dev; + + Dev = AllocateZeroPool (sizeof (*Dev)); + if (Dev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Dev->Signature = MPT_SCSI_DEV_SIGNATURE; + + // + // Host adapter channel, doesn't exist + // + Dev->PassThruMode.AdapterId = MAX_UINT32; + Dev->PassThruMode.Attributes = + EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | + EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL; + + Dev->PassThru.Mode = &Dev->PassThruMode; + Dev->PassThru.PassThru = &MptScsiPassThru; + Dev->PassThru.GetNextTargetLun = &MptScsiGetNextTargetLun; + Dev->PassThru.BuildDevicePath = &MptScsiBuildDevicePath; + Dev->PassThru.GetTargetLun = &MptScsiGetTargetLun; + Dev->PassThru.ResetChannel = &MptScsiResetChannel; + Dev->PassThru.ResetTargetLun = &MptScsiResetTargetLun; + Dev->PassThru.GetNextTarget = &MptScsiGetNextTarget; + + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + EFI_NATIVE_INTERFACE, + &Dev->PassThru + ); + if (EFI_ERROR (Status)) { + goto FreePool; + } + + return EFI_SUCCESS; + +FreePool: + FreePool (Dev); + + return Status; } STATIC @@ -103,7 +251,36 @@ MptScsiControllerStop ( IN EFI_HANDLE *ChildHandleBuffer ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru; + MPT_SCSI_DEV *Dev; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **)&PassThru, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Dev = MPT_SCSI_FROM_PASS_THRU (PassThru); + + Status = gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + &Dev->PassThru + ); + if (EFI_ERROR (Status)) { + return Status; + } + + FreePool (Dev); + + return Status; } STATIC diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf index 414b96e5a248..9f7c98829ee1 100644 --- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf +++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf @@ -24,9 +24,12 @@ [Packages] OvmfPkg/OvmfPkg.dec [LibraryClasses] + DebugLib + MemoryAllocationLib UefiBootServicesTableLib UefiDriverEntryPoint UefiLib [Protocols] - gEfiPciIoProtocolGuid ## TO_START + gEfiExtScsiPassThruProtocolGuid ## BY_START + gEfiPciIoProtocolGuid ## TO_START -- 2.27.0 From d0c17fe46f6da9da22c4bb5ef4cbd7d7d38fb99f Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:06:01 +0300 Subject: [PATCH 26/43] OvmfPkg/MptScsiDxe: Report targets and one LUN The controller supports up to 8 targets in practice (Not reported by the controller, but based on the implementation of the virtual device), report them in GetNextTarget and GetNextTargetLun. The firmware will then try to communicate with them and create a block device for each one that responds. Support for multiple LUNs will be implemented in another series. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200504210607.144434-7-nikita.leshchenko@oracle.com> (cherry picked from commit 093cceaf79b5d16880050a08e4bd465954ea6a97) --- OvmfPkg/MptScsiDxe/MptScsi.c | 63 ++++++++++++++++++++++++++++++- OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 5 +++ OvmfPkg/OvmfPkg.dec | 4 ++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 40d392c2346f..d396bff85cb6 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -11,8 +11,10 @@ #include <IndustryStandard/FusionMptScsi.h> #include <IndustryStandard/Pci.h> +#include <Library/BaseMemoryLib.h> #include <Library/DebugLib.h> #include <Library/MemoryAllocationLib.h> +#include <Library/PcdLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> #include <Protocol/PciIo.h> @@ -34,6 +36,7 @@ typedef struct { UINT32 Signature; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; + UINT8 MaxTarget; } MPT_SCSI_DEV; #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \ @@ -57,6 +60,22 @@ MptScsiPassThru ( return EFI_UNSUPPORTED; } +STATIC +BOOLEAN +IsTargetInitialized ( + IN UINT8 *Target + ) +{ + UINTN Idx; + + for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) { + if (Target[Idx] != 0xFF) { + return TRUE; + } + } + return FALSE; +} + STATIC EFI_STATUS EFIAPI @@ -66,7 +85,28 @@ MptScsiGetNextTargetLun ( IN OUT UINT64 *Lun ) { - return EFI_UNSUPPORTED; + MPT_SCSI_DEV *Dev; + + Dev = MPT_SCSI_FROM_PASS_THRU (This); + // + // Currently support only LUN 0, so hardcode it + // + if (!IsTargetInitialized (*Target)) { + ZeroMem (*Target, TARGET_MAX_BYTES); + *Lun = 0; + } else if (**Target > Dev->MaxTarget || *Lun > 0) { + return EFI_INVALID_PARAMETER; + } else if (**Target < Dev->MaxTarget) { + // + // This device interface support 256 targets only, so it's enough to + // increment the LSB of Target, as it will never overflow. + // + **Target += 1; + } else { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; } STATIC @@ -77,7 +117,24 @@ MptScsiGetNextTarget ( IN OUT UINT8 **Target ) { - return EFI_UNSUPPORTED; + MPT_SCSI_DEV *Dev; + + Dev = MPT_SCSI_FROM_PASS_THRU (This); + if (!IsTargetInitialized (*Target)) { + ZeroMem (*Target, TARGET_MAX_BYTES); + } else if (**Target > Dev->MaxTarget) { + return EFI_INVALID_PARAMETER; + } else if (**Target < Dev->MaxTarget) { + // + // This device interface support 256 targets only, so it's enough to + // increment the LSB of Target, as it will never overflow. + // + **Target += 1; + } else { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; } STATIC @@ -206,6 +263,8 @@ MptScsiControllerStart ( Dev->Signature = MPT_SCSI_DEV_SIGNATURE; + Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit); + // // Host adapter channel, doesn't exist // diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf index 9f7c98829ee1..d5fd2516e475 100644 --- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf +++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf @@ -24,8 +24,10 @@ [Packages] OvmfPkg/OvmfPkg.dec [LibraryClasses] + BaseMemoryLib DebugLib MemoryAllocationLib + PcdLib UefiBootServicesTableLib UefiDriverEntryPoint UefiLib @@ -33,3 +35,6 @@ [LibraryClasses] [Protocols] gEfiExtScsiPassThruProtocolGuid ## BY_START gEfiPciIoProtocolGuid ## TO_START + +[FixedPcd] + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit ## CONSUMES diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 06aed2b70818..f4017664aaac 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -136,6 +136,10 @@ [PcdsFixedAtBuild] # polling loop iteration. gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiWaitForCmpStallInUsecs|5|UINT32|0x38 + ## Set the *inclusive* number of targets that MptScsi exposes for scan + # by ScsiBusDxe. + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit|7|UINT8|0x39 + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa -- 2.27.0 From 9fe792f1519aae4c470a2cf172bdc51de78d9c5e Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:06:02 +0300 Subject: [PATCH 27/43] OvmfPkg/MptScsiDxe: Build and decode DevicePath Used to identify the individual disks in the hardware tree. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200504210607.144434-8-nikita.leshchenko@oracle.com> (cherry picked from commit f9941d31dd6f95ca7557d5a5f2c1874bd3bd0e14) --- OvmfPkg/MptScsiDxe/MptScsi.c | 61 ++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index d396bff85cb6..66d57f1c85d8 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -147,7 +147,36 @@ MptScsiBuildDevicePath ( IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath ) { - return EFI_UNSUPPORTED; + MPT_SCSI_DEV *Dev; + SCSI_DEVICE_PATH *ScsiDevicePath; + + if (DevicePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // This device support 256 targets only, so it's enough to dereference + // the LSB of Target. + // + Dev = MPT_SCSI_FROM_PASS_THRU (This); + if (*Target > Dev->MaxTarget || Lun > 0) { + return EFI_NOT_FOUND; + } + + ScsiDevicePath = AllocateZeroPool (sizeof (*ScsiDevicePath)); + if (ScsiDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH; + ScsiDevicePath->Header.SubType = MSG_SCSI_DP; + ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath); + ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8); + ScsiDevicePath->Pun = *Target; + ScsiDevicePath->Lun = (UINT16)Lun; + + *DevicePath = &ScsiDevicePath->Header; + return EFI_SUCCESS; } STATIC @@ -160,7 +189,35 @@ MptScsiGetTargetLun ( OUT UINT64 *Lun ) { - return EFI_UNSUPPORTED; + MPT_SCSI_DEV *Dev; + SCSI_DEVICE_PATH *ScsiDevicePath; + + if (DevicePath == NULL || + Target == NULL || *Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DevicePath->Type != MESSAGING_DEVICE_PATH || + DevicePath->SubType != MSG_SCSI_DP) { + return EFI_UNSUPPORTED; + } + + Dev = MPT_SCSI_FROM_PASS_THRU (This); + ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath; + if (ScsiDevicePath->Pun > Dev->MaxTarget || + ScsiDevicePath->Lun > 0) { + return EFI_NOT_FOUND; + } + + ZeroMem (*Target, TARGET_MAX_BYTES); + // + // This device support 256 targets only, so it's enough to set the LSB + // of Target. + // + **Target = (UINT8)ScsiDevicePath->Pun; + *Lun = ScsiDevicePath->Lun; + + return EFI_SUCCESS; } STATIC -- 2.27.0 From 9460f6d25d1a41a6e3b07aede84179281c23265b Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:06:03 +0300 Subject: [PATCH 28/43] OvmfPkg/MptScsiDxe: Open PciIo protocol for later use This will give us an exclusive access to the PciIo of this device after it was started and until is will be stopped. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200504210607.144434-9-nikita.leshchenko@oracle.com> (cherry picked from commit da8c0b8f4d2df23de249580a595afea76b5185fd) --- OvmfPkg/MptScsiDxe/MptScsi.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 66d57f1c85d8..3dfc78cf2e1f 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -37,6 +37,7 @@ typedef struct { EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; UINT8 MaxTarget; + EFI_PCI_IO_PROTOCOL *PciIo; } MPT_SCSI_DEV; #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \ @@ -322,6 +323,18 @@ MptScsiControllerStart ( Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit); + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + (VOID **)&Dev->PciIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto FreePool; + } + // // Host adapter channel, doesn't exist // @@ -346,11 +359,19 @@ MptScsiControllerStart ( &Dev->PassThru ); if (EFI_ERROR (Status)) { - goto FreePool; + goto CloseProtocol; } return EFI_SUCCESS; +CloseProtocol: + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + FreePool: FreePool (Dev); @@ -394,6 +415,13 @@ MptScsiControllerStop ( return Status; } + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + FreePool (Dev); return Status; -- 2.27.0 From 8b5974a1b340cd14484b67c6d8f67da52375cd82 Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:06:04 +0300 Subject: [PATCH 29/43] OvmfPkg/MptScsiDxe: Set and restore PCI attributes Enable the IO Space and Bus Mastering and restore the original values when the device is stopped. This is a standard procedure in PCI drivers. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200504210607.144434-10-nikita.leshchenko@oracle.com> (cherry picked from commit ecdbdba636b6bac706641ca47aa973193079a3cc) --- OvmfPkg/MptScsiDxe/MptScsi.c | 65 +++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 3dfc78cf2e1f..289bd9fc372b 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -38,6 +38,7 @@ typedef struct { EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; UINT8 MaxTarget; EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 OriginalPciAttributes; } MPT_SCSI_DEV; #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \ @@ -335,6 +336,53 @@ MptScsiControllerStart ( goto FreePool; } + Status = Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationGet, + 0, + &Dev->OriginalPciAttributes + ); + if (EFI_ERROR (Status)) { + goto CloseProtocol; + } + + // + // Enable I/O Space & Bus-Mastering + // + Status = Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationEnable, + (EFI_PCI_IO_ATTRIBUTE_IO | + EFI_PCI_IO_ATTRIBUTE_BUS_MASTER), + NULL + ); + if (EFI_ERROR (Status)) { + goto CloseProtocol; + } + + // + // Signal device supports 64-bit DMA addresses + // + Status = Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE, + NULL + ); + if (EFI_ERROR (Status)) { + // + // Warn user that device will only be using 32-bit DMA addresses. + // + // Note that this does not prevent the device/driver from working + // and therefore we only warn and continue as usual. + // + DEBUG (( + DEBUG_WARN, + "%a: failed to enable 64-bit DMA addresses\n", + __FUNCTION__ + )); + } + // // Host adapter channel, doesn't exist // @@ -359,11 +407,19 @@ MptScsiControllerStart ( &Dev->PassThru ); if (EFI_ERROR (Status)) { - goto CloseProtocol; + goto RestoreAttributes; } return EFI_SUCCESS; +RestoreAttributes: + Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationSet, + Dev->OriginalPciAttributes, + NULL + ); + CloseProtocol: gBS->CloseProtocol ( ControllerHandle, @@ -415,6 +471,13 @@ MptScsiControllerStop ( return Status; } + Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationSet, + Dev->OriginalPciAttributes, + NULL + ); + gBS->CloseProtocol ( ControllerHandle, &gEfiPciIoProtocolGuid, -- 2.27.0 From 3616bb13045b070f45999800788adb8e07679648 Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:06:05 +0300 Subject: [PATCH 30/43] OvmfPkg/MptScsiDxe: Initialize hardware Reset and send the IO controller initialization request. The reply is read back to complete the doorbell function but it isn't useful to us because it doesn't contain relevant data or status codes. See "LSI53C1030 PCI-X to Dual Channel Ultra320 SCSI Multifunction Controller" technical manual for more information. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Message-Id: <20200504210607.144434-11-nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit 81cada9892cc4bf3725009c5fb836115da10b1c8) --- .../Include/IndustryStandard/FusionMptScsi.h | 128 +++++++++++ OvmfPkg/MptScsiDxe/MptScsi.c | 198 +++++++++++++++++- 2 files changed, 325 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h index df9bdc2f0348..655d629d902e 100644 --- a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h +++ b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h @@ -20,4 +20,132 @@ #define LSI_SAS1068_PCI_DEVICE_ID 0x0054 #define LSI_SAS1068E_PCI_DEVICE_ID 0x0058 +#define MPT_REG_DOORBELL 0x00 +#define MPT_REG_WRITE_SEQ 0x04 +#define MPT_REG_HOST_DIAG 0x08 +#define MPT_REG_TEST 0x0c +#define MPT_REG_DIAG_DATA 0x10 +#define MPT_REG_DIAG_ADDR 0x14 +#define MPT_REG_ISTATUS 0x30 +#define MPT_REG_IMASK 0x34 +#define MPT_REG_REQ_Q 0x40 +#define MPT_REG_REP_Q 0x44 + +#define MPT_DOORBELL_RESET 0x40 +#define MPT_DOORBELL_HANDSHAKE 0x42 + +#define MPT_IMASK_DOORBELL 0x01 +#define MPT_IMASK_REPLY 0x08 + +#define MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST 0x00 +#define MPT_MESSAGE_HDR_FUNCTION_IOC_INIT 0x02 + +#define MPT_SG_ENTRY_TYPE_SIMPLE 0x01 + +#define MPT_IOC_WHOINIT_ROM_BIOS 0x02 + +// +// Device structures +// + +#pragma pack (1) +typedef struct { + UINT8 WhoInit; + UINT8 Reserved1; + UINT8 ChainOffset; + UINT8 Function; + UINT8 Flags; + UINT8 MaxDevices; + UINT8 MaxBuses; + UINT8 MessageFlags; + UINT32 MessageContext; + UINT16 ReplyFrameSize; + UINT16 Reserved2; + UINT32 HostMfaHighAddr; + UINT32 SenseBufferHighAddr; +} MPT_IO_CONTROLLER_INIT_REQUEST; + +typedef struct { + UINT8 WhoInit; + UINT8 Reserved1; + UINT8 MessageLength; + UINT8 Function; + UINT8 Flags; + UINT8 MaxDevices; + UINT8 MaxBuses; + UINT8 MessageFlags; + UINT32 MessageContext; + UINT16 Reserved2; + UINT16 IocStatus; + UINT32 IocLogInfo; +} MPT_IO_CONTROLLER_INIT_REPLY; + +typedef struct { + UINT8 TargetId; + UINT8 Bus; + UINT8 ChainOffset; + UINT8 Function; + UINT8 CdbLength; + UINT8 SenseBufferLength; + UINT8 Reserved; + UINT8 MessageFlags; + UINT32 MessageContext; + UINT8 Lun[8]; + UINT32 Control; + UINT8 Cdb[16]; + UINT32 DataLength; + UINT32 SenseBufferLowAddress; +} MPT_SCSI_IO_REQUEST; + +typedef struct { + UINT32 Length: 24; + UINT32 EndOfList: 1; + UINT32 Is64BitAddress: 1; + // + // True when the buffer contains data to be transfered. Otherwise it's the + // destination buffer + // + UINT32 BufferContainsData: 1; + UINT32 LocalAddress: 1; + UINT32 ElementType: 2; + UINT32 EndOfBuffer: 1; + UINT32 LastElement: 1; + UINT64 DataBufferAddress; +} MPT_SG_ENTRY_SIMPLE; + +typedef struct { + UINT8 TargetId; + UINT8 Bus; + UINT8 MessageLength; + UINT8 Function; + UINT8 CdbLength; + UINT8 SenseBufferLength; + UINT8 Reserved; + UINT8 MessageFlags; + UINT32 MessageContext; + UINT8 ScsiStatus; + UINT8 ScsiState; + UINT16 IocStatus; + UINT32 IocLogInfo; + UINT32 TransferCount; + UINT32 SenseCount; + UINT32 ResponseInfo; +} MPT_SCSI_IO_REPLY; + +typedef struct { + MPT_SCSI_IO_REQUEST Header; + MPT_SG_ENTRY_SIMPLE Sg; +} MPT_SCSI_REQUEST_WITH_SG; +#pragma pack () + +typedef union { + MPT_SCSI_IO_REPLY Data; + UINT64 Uint64; // 8 byte alignment required by HW +} MPT_SCSI_IO_REPLY_ALIGNED; + +typedef union { + MPT_SCSI_REQUEST_WITH_SG Data; + UINT64 Uint64; // 8 byte alignment required by HW +} MPT_SCSI_REQUEST_ALIGNED; + #endif // __FUSION_MPT_SCSI_H__ diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 289bd9fc372b..2cc69b88dab3 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -44,6 +44,192 @@ typedef struct { #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \ CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE) +// +// Hardware functions +// + +STATIC +EFI_STATUS +Out32 ( + IN MPT_SCSI_DEV *Dev, + IN UINT32 Addr, + IN UINT32 Data + ) +{ + return Dev->PciIo->Io.Write ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX0, + Addr, + 1, + &Data + ); +} + +STATIC +EFI_STATUS +In32 ( + IN MPT_SCSI_DEV *Dev, + IN UINT32 Addr, + OUT UINT32 *Data + ) +{ + return Dev->PciIo->Io.Read ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX0, + Addr, + 1, + Data + ); +} + +STATIC +EFI_STATUS +MptDoorbell ( + IN MPT_SCSI_DEV *Dev, + IN UINT8 DoorbellFunc, + IN UINT8 DoorbellArg + ) +{ + return Out32 ( + Dev, + MPT_REG_DOORBELL, + (((UINT32)DoorbellFunc) << 24) | (DoorbellArg << 16) + ); +} + +STATIC +EFI_STATUS +MptScsiReset ( + IN MPT_SCSI_DEV *Dev + ) +{ + EFI_STATUS Status; + + // + // Reset hardware + // + Status = MptDoorbell (Dev, MPT_DOORBELL_RESET, 0); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Mask interrupts + // + Status = Out32 (Dev, MPT_REG_IMASK, MPT_IMASK_DOORBELL | MPT_IMASK_REPLY); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Clear interrupt status + // + Status = Out32 (Dev, MPT_REG_ISTATUS, 0); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +MptScsiInit ( + IN MPT_SCSI_DEV *Dev + ) +{ + EFI_STATUS Status; + union { + MPT_IO_CONTROLLER_INIT_REQUEST Data; + UINT32 Uint32; + } AlignedReq; + MPT_IO_CONTROLLER_INIT_REQUEST *Req; + MPT_IO_CONTROLLER_INIT_REPLY Reply; + UINT8 *ReplyBytes; + UINT32 ReplyWord; + + Req = &AlignedReq.Data; + + Status = MptScsiReset (Dev); + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (Req, sizeof (*Req)); + ZeroMem (&Reply, sizeof (Reply)); + Req->WhoInit = MPT_IOC_WHOINIT_ROM_BIOS; + Req->Function = MPT_MESSAGE_HDR_FUNCTION_IOC_INIT; + STATIC_ASSERT ( + FixedPcdGet8 (PcdMptScsiMaxTargetLimit) < 255, + "Req supports 255 targets only (max target is 254)" + ); + Req->MaxDevices = Dev->MaxTarget + 1; + Req->MaxBuses = 1; + + // + // Send controller init through doorbell + // + STATIC_ASSERT ( + sizeof (*Req) % sizeof (UINT32) == 0, + "Req must be multiple of UINT32" + ); + STATIC_ASSERT ( + sizeof (*Req) / sizeof (UINT32) <= MAX_UINT8, + "Req must fit in MAX_UINT8 Dwords" + ); + Status = MptDoorbell ( + Dev, + MPT_DOORBELL_HANDSHAKE, + (UINT8)(sizeof (*Req) / sizeof (UINT32)) + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = Dev->PciIo->Io.Write ( + Dev->PciIo, + EfiPciIoWidthFifoUint32, + PCI_BAR_IDX0, + MPT_REG_DOORBELL, + sizeof (*Req) / sizeof (UINT32), + Req + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Read reply through doorbell + // Each 32bit (Dword) read produces 16bit (Word) of data + // + // The reply is read back to complete the doorbell function but it + // isn't useful because it doesn't contain relevant data or status + // codes. + // + STATIC_ASSERT ( + sizeof (Reply) % sizeof (UINT16) == 0, + "Reply must be multiple of UINT16" + ); + ReplyBytes = (UINT8 *)&Reply; + while (ReplyBytes != (UINT8 *)(&Reply + 1)) { + Status = In32 (Dev, MPT_REG_DOORBELL, &ReplyWord); + if (EFI_ERROR (Status)) { + return Status; + } + CopyMem (ReplyBytes, &ReplyWord, sizeof (UINT16)); + ReplyBytes += sizeof (UINT16); + } + + // + // Clear interrupts generated by doorbell reply + // + Status = Out32 (Dev, MPT_REG_ISTATUS, 0); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + // // Ext SCSI Pass Thru // @@ -383,6 +569,11 @@ MptScsiControllerStart ( )); } + Status = MptScsiInit (Dev); + if (EFI_ERROR (Status)) { + goto RestoreAttributes; + } + // // Host adapter channel, doesn't exist // @@ -407,11 +598,14 @@ MptScsiControllerStart ( &Dev->PassThru ); if (EFI_ERROR (Status)) { - goto RestoreAttributes; + goto UninitDev; } return EFI_SUCCESS; +UninitDev: + MptScsiReset (Dev); + RestoreAttributes: Dev->PciIo->Attributes ( Dev->PciIo, @@ -471,6 +665,8 @@ MptScsiControllerStop ( return Status; } + MptScsiReset (Dev); + Dev->PciIo->Attributes ( Dev->PciIo, EfiPciIoAttributeOperationSet, -- 2.27.0 From 25a9eaf6e5be947bf365fceab30a6b356251f80a Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:06:06 +0300 Subject: [PATCH 31/43] OvmfPkg/MptScsiDxe: Implement the PassThru method Machines should be able to boot after this commit. Tested with different Linux distributions (Ubuntu, CentOS) and different Windows versions (Windows 7, Windows 10, Server 2016). Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200504210607.144434-12-nikita.leshchenko@oracle.com> [lersek@redhat.com: MPT_SCSI_DMA_ADDR_HIGH: drop redundant space char] (cherry picked from commit 505812ae1d2d1d3efa5d9c622d87a990918442ee) --- .../Include/IndustryStandard/FusionMptScsi.h | 9 + OvmfPkg/MptScsiDxe/MptScsi.c | 409 +++++++++++++++++- OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 4 + OvmfPkg/OvmfPkg.dec | 3 + 4 files changed, 423 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h index 655d629d902e..99778d1537da 100644 --- a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h +++ b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h @@ -44,6 +44,15 @@ #define MPT_IOC_WHOINIT_ROM_BIOS 0x02 +#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE (0x00 << 24) +#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE (0x01 << 24) +#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ (0x02 << 24) + +#define MPT_SCSI_IOCSTATUS_SUCCESS 0x0000 +#define MPT_SCSI_IOCSTATUS_DEVICE_NOT_THERE 0x0043 +#define MPT_SCSI_IOCSTATUS_DATA_OVERRUN 0x0044 +#define MPT_SCSI_IOCSTATUS_DATA_UNDERRUN 0x0045 + // // Device structures // diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 2cc69b88dab3..8be38f6cdb88 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -11,6 +11,7 @@ #include <IndustryStandard/FusionMptScsi.h> #include <IndustryStandard/Pci.h> +#include <Library/BaseLib.h> #include <Library/BaseMemoryLib.h> #include <Library/DebugLib.h> #include <Library/MemoryAllocationLib.h> @@ -18,6 +19,7 @@ #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> #include <Protocol/PciIo.h> +#include <Protocol/PciRootBridgeIo.h> #include <Protocol/ScsiPassThruExt.h> #include <Uefi/UefiSpec.h> @@ -31,19 +33,50 @@ // Runtime Structures // +typedef struct { + MPT_SCSI_REQUEST_ALIGNED IoRequest; + MPT_SCSI_IO_REPLY_ALIGNED IoReply; + // + // As EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.SenseDataLength is defined + // as UINT8, defining here SenseData size to MAX_UINT8 will guarantee it + // cannot overflow when passed to device. + // + UINT8 Sense[MAX_UINT8]; + // + // This size of the data is arbitrarily chosen. + // It seems to be sufficient for all I/O requests sent through + // EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() for common boot scenarios. + // + UINT8 Data[0x2000]; +} MPT_SCSI_DMA_BUFFER; + #define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S') typedef struct { UINT32 Signature; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; UINT8 MaxTarget; + UINT32 StallPerPollUsec; EFI_PCI_IO_PROTOCOL *PciIo; UINT64 OriginalPciAttributes; + MPT_SCSI_DMA_BUFFER *Dma; + EFI_PHYSICAL_ADDRESS DmaPhysical; + VOID *DmaMapping; + BOOLEAN IoReplyEnqueued; } MPT_SCSI_DEV; #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \ CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE) +#define MPT_SCSI_DMA_ADDR(Dev, MemberName) \ + (Dev->DmaPhysical + OFFSET_OF (MPT_SCSI_DMA_BUFFER, MemberName)) + +#define MPT_SCSI_DMA_ADDR_HIGH(Dev, MemberName) \ + ((UINT32)RShiftU64 (MPT_SCSI_DMA_ADDR (Dev, MemberName), 32)) + +#define MPT_SCSI_DMA_ADDR_LOW(Dev, MemberName) \ + ((UINT32)MPT_SCSI_DMA_ADDR (Dev, MemberName)) + // // Hardware functions // @@ -165,6 +198,9 @@ MptScsiInit ( ); Req->MaxDevices = Dev->MaxTarget + 1; Req->MaxBuses = 1; + Req->ReplyFrameSize = sizeof Dev->Dma->IoReply.Data; + Req->HostMfaHighAddr = MPT_SCSI_DMA_ADDR_HIGH (Dev, IoRequest); + Req->SenseBufferHighAddr = MPT_SCSI_DMA_ADDR_HIGH (Dev, Sense); // // Send controller init through doorbell @@ -230,6 +266,288 @@ MptScsiInit ( return EFI_SUCCESS; } +STATIC +EFI_STATUS +ReportHostAdapterError ( + OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + DEBUG ((DEBUG_ERROR, "%a: fatal error in scsi request\n", __FUNCTION__)); + Packet->InTransferLength = 0; + Packet->OutTransferLength = 0; + Packet->SenseDataLength = 0; + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED; + return EFI_DEVICE_ERROR; +} + +STATIC +EFI_STATUS +ReportHostAdapterOverrunError ( + OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + Packet->SenseDataLength = 0; + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + return EFI_BAD_BUFFER_SIZE; +} + +STATIC +EFI_STATUS +MptScsiPopulateRequest ( + IN MPT_SCSI_DEV *Dev, + IN UINT8 Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + MPT_SCSI_REQUEST_WITH_SG *Request; + + Request = &Dev->Dma->IoRequest.Data; + + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL || + (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) || + Packet->CdbLength > sizeof (Request->Header.Cdb)) { + return EFI_UNSUPPORTED; + } + + if (Target > Dev->MaxTarget || Lun > 0 || + Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL || + // + // Trying to receive, but destination pointer is NULL, or contradicting + // transfer direction + // + (Packet->InTransferLength > 0 && + (Packet->InDataBuffer == NULL || + Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE + ) + ) || + + // + // Trying to send, but source pointer is NULL, or contradicting transfer + // direction + // + (Packet->OutTransferLength > 0 && + (Packet->OutDataBuffer == NULL || + Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ + ) + ) + ) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->InTransferLength > sizeof (Dev->Dma->Data)) { + Packet->InTransferLength = sizeof (Dev->Dma->Data); + return ReportHostAdapterOverrunError (Packet); + } + if (Packet->OutTransferLength > sizeof (Dev->Dma->Data)) { + Packet->OutTransferLength = sizeof (Dev->Dma->Data); + return ReportHostAdapterOverrunError (Packet); + } + + ZeroMem (Request, sizeof (*Request)); + Request->Header.TargetId = Target; + // + // Only LUN 0 is currently supported, hence the cast is safe + // + Request->Header.Lun[1] = (UINT8)Lun; + Request->Header.Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST; + Request->Header.MessageContext = 1; // We handle one request at a time + + Request->Header.CdbLength = Packet->CdbLength; + CopyMem (Request->Header.Cdb, Packet->Cdb, Packet->CdbLength); + + // + // SenseDataLength is UINT8, Sense[] is MAX_UINT8, so we can't overflow + // + ZeroMem (Dev->Dma->Sense, Packet->SenseDataLength); + Request->Header.SenseBufferLength = Packet->SenseDataLength; + Request->Header.SenseBufferLowAddress = MPT_SCSI_DMA_ADDR_LOW (Dev, Sense); + + Request->Sg.EndOfList = 1; + Request->Sg.EndOfBuffer = 1; + Request->Sg.LastElement = 1; + Request->Sg.ElementType = MPT_SG_ENTRY_TYPE_SIMPLE; + Request->Sg.Is64BitAddress = 1; + Request->Sg.DataBufferAddress = MPT_SCSI_DMA_ADDR (Dev, Data); + + // + // "MPT_SG_ENTRY_SIMPLE.Length" is a 24-bit quantity. + // + STATIC_ASSERT ( + sizeof (Dev->Dma->Data) < SIZE_16MB, + "MPT_SCSI_DMA_BUFFER.Data must be smaller than 16MB" + ); + + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + Request->Header.DataLength = Packet->InTransferLength; + Request->Sg.Length = Packet->InTransferLength; + Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ; + } else { + Request->Header.DataLength = Packet->OutTransferLength; + Request->Sg.Length = Packet->OutTransferLength; + Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE; + + CopyMem (Dev->Dma->Data, Packet->OutDataBuffer, Packet->OutTransferLength); + Request->Sg.BufferContainsData = 1; + } + + if (Request->Header.DataLength == 0) { + Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +MptScsiSendRequest ( + IN MPT_SCSI_DEV *Dev, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_STATUS Status; + + if (!Dev->IoReplyEnqueued) { + // + // Put one free reply frame on the reply queue, the hardware may use it to + // report an error to us. + // + Status = Out32 (Dev, MPT_REG_REP_Q, MPT_SCSI_DMA_ADDR_LOW (Dev, IoReply)); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + Dev->IoReplyEnqueued = TRUE; + } + + Status = Out32 (Dev, MPT_REG_REQ_Q, MPT_SCSI_DMA_ADDR_LOW (Dev, IoRequest)); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +MptScsiGetReply ( + IN MPT_SCSI_DEV *Dev, + OUT UINT32 *Reply + ) +{ + EFI_STATUS Status; + UINT32 Istatus; + UINT32 EmptyReply; + + // + // Timeouts are not supported for + // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() in this implementation. + // + for (;;) { + Status = In32 (Dev, MPT_REG_ISTATUS, &Istatus); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Interrupt raised + // + if (Istatus & MPT_IMASK_REPLY) { + break; + } + + gBS->Stall (Dev->StallPerPollUsec); + } + + Status = In32 (Dev, MPT_REG_REP_Q, Reply); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // The driver is supposed to fetch replies until 0xffffffff is returned, which + // will reset the interrupt status. We put only one request, so we expect the + // next read reply to be the last. + // + Status = In32 (Dev, MPT_REG_REP_Q, &EmptyReply); + if (EFI_ERROR (Status) || EmptyReply != MAX_UINT32) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +MptScsiHandleReply ( + IN MPT_SCSI_DEV *Dev, + IN UINT32 Reply, + OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + CopyMem (Packet->InDataBuffer, Dev->Dma->Data, Packet->InTransferLength); + } + + if (Reply == Dev->Dma->IoRequest.Data.Header.MessageContext) { + // + // This is a turbo reply, everything is good + // + Packet->SenseDataLength = 0; + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + + } else if ((Reply & BIT31) != 0) { + DEBUG ((DEBUG_INFO, "%a: Full reply returned\n", __FUNCTION__)); + // + // When reply MSB is set, we got a full reply. Since we submitted only one + // reply frame, we know it's IoReply. + // + Dev->IoReplyEnqueued = FALSE; + + Packet->TargetStatus = Dev->Dma->IoReply.Data.ScsiStatus; + // + // Make sure device only lowers SenseDataLength before copying sense + // + ASSERT (Dev->Dma->IoReply.Data.SenseCount <= Packet->SenseDataLength); + Packet->SenseDataLength = + (UINT8)MIN (Dev->Dma->IoReply.Data.SenseCount, Packet->SenseDataLength); + CopyMem (Packet->SenseData, Dev->Dma->Sense, Packet->SenseDataLength); + + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + Packet->InTransferLength = Dev->Dma->IoReply.Data.TransferCount; + } else { + Packet->OutTransferLength = Dev->Dma->IoReply.Data.TransferCount; + } + + switch (Dev->Dma->IoReply.Data.IocStatus) { + case MPT_SCSI_IOCSTATUS_SUCCESS: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK; + break; + case MPT_SCSI_IOCSTATUS_DEVICE_NOT_THERE: + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT; + return EFI_TIMEOUT; + case MPT_SCSI_IOCSTATUS_DATA_UNDERRUN: + case MPT_SCSI_IOCSTATUS_DATA_OVERRUN: + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN; + break; + default: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + return EFI_DEVICE_ERROR; + } + + } else { + DEBUG ((DEBUG_ERROR, "%a: unexpected reply (%x)\n", __FUNCTION__, Reply)); + return ReportHostAdapterError (Packet); + } + + return EFI_SUCCESS; +} + // // Ext SCSI Pass Thru // @@ -245,7 +563,33 @@ MptScsiPassThru ( IN EFI_EVENT Event OPTIONAL ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + MPT_SCSI_DEV *Dev; + UINT32 Reply; + + Dev = MPT_SCSI_FROM_PASS_THRU (This); + // + // We only use first byte of target identifer + // + Status = MptScsiPopulateRequest (Dev, *Target, Lun, Packet); + if (EFI_ERROR (Status)) { + // + // MptScsiPopulateRequest modified packet according to the error + // + return Status; + } + + Status = MptScsiSendRequest (Dev, Packet); + if (EFI_ERROR (Status)) { + return ReportHostAdapterError (Packet); + } + + Status = MptScsiGetReply (Dev, &Reply); + if (EFI_ERROR (Status)) { + return ReportHostAdapterError (Packet); + } + + return MptScsiHandleReply (Dev, Reply, Packet); } STATIC @@ -500,6 +844,8 @@ MptScsiControllerStart ( { EFI_STATUS Status; MPT_SCSI_DEV *Dev; + UINTN Pages; + UINTN BytesMapped; Dev = AllocateZeroPool (sizeof (*Dev)); if (Dev == NULL) { @@ -509,6 +855,7 @@ MptScsiControllerStart ( Dev->Signature = MPT_SCSI_DEV_SIGNATURE; Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit); + Dev->StallPerPollUsec = PcdGet32 (PcdMptScsiStallPerPollUsec); Status = gBS->OpenProtocol ( ControllerHandle, @@ -569,11 +916,45 @@ MptScsiControllerStart ( )); } - Status = MptScsiInit (Dev); + // + // Create buffers for data transfer + // + Pages = EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)); + Status = Dev->PciIo->AllocateBuffer ( + Dev->PciIo, + AllocateAnyPages, + EfiBootServicesData, + Pages, + (VOID **)&Dev->Dma, + EFI_PCI_ATTRIBUTE_MEMORY_CACHED + ); if (EFI_ERROR (Status)) { goto RestoreAttributes; } + BytesMapped = EFI_PAGES_TO_SIZE (Pages); + Status = Dev->PciIo->Map ( + Dev->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + Dev->Dma, + &BytesMapped, + &Dev->DmaPhysical, + &Dev->DmaMapping + ); + if (EFI_ERROR (Status)) { + goto FreeBuffer; + } + + if (BytesMapped != EFI_PAGES_TO_SIZE (Pages)) { + Status = EFI_OUT_OF_RESOURCES; + goto Unmap; + } + + Status = MptScsiInit (Dev); + if (EFI_ERROR (Status)) { + goto Unmap; + } + // // Host adapter channel, doesn't exist // @@ -606,6 +987,19 @@ MptScsiControllerStart ( UninitDev: MptScsiReset (Dev); +Unmap: + Dev->PciIo->Unmap ( + Dev->PciIo, + Dev->DmaMapping + ); + +FreeBuffer: + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + Pages, + Dev->Dma + ); + RestoreAttributes: Dev->PciIo->Attributes ( Dev->PciIo, @@ -667,6 +1061,17 @@ MptScsiControllerStop ( MptScsiReset (Dev); + Dev->PciIo->Unmap ( + Dev->PciIo, + Dev->DmaMapping + ); + + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)), + Dev->Dma + ); + Dev->PciIo->Attributes ( Dev->PciIo, EfiPciIoAttributeOperationSet, diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf index d5fd2516e475..09108939a5a2 100644 --- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf +++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf @@ -24,6 +24,7 @@ [Packages] OvmfPkg/OvmfPkg.dec [LibraryClasses] + BaseLib BaseMemoryLib DebugLib MemoryAllocationLib @@ -38,3 +39,6 @@ [Protocols] [FixedPcd] gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit ## CONSUMES + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec ## CONSUMES diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index f4017664aaac..464a7aaec2ec 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -140,6 +140,9 @@ [PcdsFixedAtBuild] # by ScsiBusDxe. gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit|7|UINT8|0x39 + ## Microseconds to stall between polling for MptScsi request result + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x40 + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa -- 2.27.0 From 4b54230503fbc263f0f311692a42137f7d8587be Mon Sep 17 00:00:00 2001 From: Nikita Leshenko <nikita.leshchenko@oracle.com> Date: Tue, 5 May 2020 00:06:07 +0300 Subject: [PATCH 32/43] OvmfPkg/MptScsiDxe: Reset device on ExitBootServices() This causes the device to forget about the reply frame. We allocated the reply frame in EfiBootServicesData type memory, and code executing after ExitBootServices() is permitted to overwrite it. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200504210607.144434-13-nikita.leshchenko@oracle.com> (cherry picked from commit c635a56384bfc4fdf3e00d102ea0eb7aa8d840fd) --- OvmfPkg/MptScsiDxe/MptScsi.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 8be38f6cdb88..9ed1831bac6d 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -59,6 +59,7 @@ typedef struct { UINT32 StallPerPollUsec; EFI_PCI_IO_PROTOCOL *PciIo; UINT64 OriginalPciAttributes; + EFI_EVENT ExitBoot; MPT_SCSI_DMA_BUFFER *Dma; EFI_PHYSICAL_ADDRESS DmaPhysical; VOID *DmaMapping; @@ -762,6 +763,20 @@ MptScsiResetChannel ( return EFI_UNSUPPORTED; } +STATIC +VOID +EFIAPI +MptScsiExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + MPT_SCSI_DEV *Dev; + + Dev = Context; + DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context)); + MptScsiReset (Dev); +} STATIC EFI_STATUS EFIAPI @@ -955,6 +970,17 @@ MptScsiControllerStart ( goto Unmap; } + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_CALLBACK, + &MptScsiExitBoot, + Dev, + &Dev->ExitBoot + ); + if (EFI_ERROR (Status)) { + goto UninitDev; + } + // // Host adapter channel, doesn't exist // @@ -979,11 +1005,14 @@ MptScsiControllerStart ( &Dev->PassThru ); if (EFI_ERROR (Status)) { - goto UninitDev; + goto CloseExitBoot; } return EFI_SUCCESS; +CloseExitBoot: + gBS->CloseEvent (Dev->ExitBoot); + UninitDev: MptScsiReset (Dev); @@ -1059,6 +1088,8 @@ MptScsiControllerStop ( return Status; } + gBS->CloseEvent (Dev->ExitBoot); + MptScsiReset (Dev); Dev->PciIo->Unmap ( -- 2.27.0 From 9e9876bc2e2bb794d785858eda7c6d0572a7914c Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Wed, 15 Jul 2020 16:20:31 +0800 Subject: [PATCH 33/43] OvmfPkg/OvmfPkg.dec: Adjust PcdMptScsiStallPerPollUsec token value The token value of PcdMptScsiStallPerPollUsec should be 0x3a since the previous token value is 0x39. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Cc: Liran Alon <liran.alon@oracle.com> Cc: Nikita Leshenko <nikita.leshchenko@oracle.com> Signed-off-by: Gary Lin <glin@suse.com> Message-Id: <20200715082031.30978-1-glin@suse.com> Reviewed-by: Liran Alon <liran.alon@oracle.com> [lersek@redhat.com: clarify subject, fix typos in commit message] Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit d9269d69138860edb1ec9796ed48549dc6ba5735) --- OvmfPkg/OvmfPkg.dec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 464a7aaec2ec..e86a3f86c106 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -141,7 +141,7 @@ [PcdsFixedAtBuild] gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit|7|UINT8|0x39 ## Microseconds to stall between polling for MptScsi request result - gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x40 + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x3a gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9 -- 2.27.0 From 01a45461ec6c28cb62824bf995cf210b89768e6f Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:20 +0800 Subject: [PATCH 34/43] OvmfPkg/LsiScsiDxe: Create the empty driver Create the driver with only a dummy LsiScsiEntryPoint() for the further implementation of the driver for LSI 53C895A SCSI controller. v2: Fix the mixed-case GUID string Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200717061130.8881-2-glin@suse.com> (cherry picked from commit e94d04a01b5a631390f6abd0d1e8f62a347ca72c) --- OvmfPkg/LsiScsiDxe/LsiScsi.c | 25 +++++++++++++++++++++++++ OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf | 26 ++++++++++++++++++++++++++ OvmfPkg/OvmfPkgIa32.dsc | 4 ++++ OvmfPkg/OvmfPkgIa32.fdf | 3 +++ OvmfPkg/OvmfPkgIa32X64.dsc | 4 ++++ OvmfPkg/OvmfPkgIa32X64.fdf | 3 +++ OvmfPkg/OvmfPkgX64.dsc | 4 ++++ OvmfPkg/OvmfPkgX64.fdf | 3 +++ 8 files changed, 72 insertions(+) create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsi.c create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c new file mode 100644 index 000000000000..9c90941688ed --- /dev/null +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -0,0 +1,25 @@ +/** @file + + This driver produces Extended SCSI Pass Thru Protocol instances for + LSI 53C895A SCSI devices. + + Copyright (C) 2020, SUSE LLC. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi/UefiSpec.h> + +// +// Entry point of this driver +// +EFI_STATUS +EFIAPI +LsiScsiEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf new file mode 100644 index 000000000000..8b6dccaff3eb --- /dev/null +++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf @@ -0,0 +1,26 @@ +## @file +# This driver produces Extended SCSI Pass Thru Protocol instances for +# LSI 53C895A SCSI devices. +# +# Copyright (C) 2020, SUSE LLC. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.29 + BASE_NAME = LsiScsiDxe + FILE_GUID = EB4EB21F-5A3D-40BE-8BD2-F1B0E38E5744 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = LsiScsiEntryPoint + +[Sources] + LsiScsi.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index d391ee0a28df..c5a7ed59b072 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -49,6 +49,7 @@ [Defines] # DEFINE PVSCSI_ENABLE = TRUE DEFINE MPT_SCSI_ENABLE = TRUE + DEFINE LSI_SCSI_ENABLE = FALSE # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to @@ -722,6 +723,9 @@ [Components] !endif !if $(MPT_SCSI_ENABLE) == TRUE OvmfPkg/MptScsiDxe/MptScsiDxe.inf +!endif +!if $(LSI_SCSI_ENABLE) == TRUE + OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf !endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf index b47d50276d13..0db81d3774d3 100644 --- a/OvmfPkg/OvmfPkgIa32.fdf +++ b/OvmfPkg/OvmfPkgIa32.fdf @@ -233,6 +233,9 @@ [FV.DXEFV] !if $(MPT_SCSI_ENABLE) == TRUE INF OvmfPkg/MptScsiDxe/MptScsiDxe.inf !endif +!if $(LSI_SCSI_ENABLE) == TRUE +INF OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 2341d7f53a1c..1ae4a238e810 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -49,6 +49,7 @@ [Defines] # DEFINE PVSCSI_ENABLE = TRUE DEFINE MPT_SCSI_ENABLE = TRUE + DEFINE LSI_SCSI_ENABLE = FALSE # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to @@ -735,6 +736,9 @@ [Components.X64] !endif !if $(MPT_SCSI_ENABLE) == TRUE OvmfPkg/MptScsiDxe/MptScsiDxe.inf +!endif +!if $(LSI_SCSI_ENABLE) == TRUE + OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf !endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf index af0f4c958841..47824f0d8127 100644 --- a/OvmfPkg/OvmfPkgIa32X64.fdf +++ b/OvmfPkg/OvmfPkgIa32X64.fdf @@ -234,6 +234,9 @@ [FV.DXEFV] !if $(MPT_SCSI_ENABLE) == TRUE INF OvmfPkg/MptScsiDxe/MptScsiDxe.inf !endif +!if $(LSI_SCSI_ENABLE) == TRUE +INF OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 0036455d9c62..7e22d2599878 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -49,6 +49,7 @@ [Defines] # DEFINE PVSCSI_ENABLE = TRUE DEFINE MPT_SCSI_ENABLE = TRUE + DEFINE LSI_SCSI_ENABLE = FALSE # # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to @@ -733,6 +734,9 @@ [Components] !endif !if $(MPT_SCSI_ENABLE) == TRUE OvmfPkg/MptScsiDxe/MptScsiDxe.inf +!endif +!if $(LSI_SCSI_ENABLE) == TRUE + OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf !endif MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index af0f4c958841..47824f0d8127 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -234,6 +234,9 @@ [FV.DXEFV] !if $(MPT_SCSI_ENABLE) == TRUE INF OvmfPkg/MptScsiDxe/MptScsiDxe.inf !endif +!if $(LSI_SCSI_ENABLE) == TRUE +INF OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf +!endif !if $(SECURE_BOOT_ENABLE) == TRUE INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf -- 2.27.0 From 49bdbdf1fb5a5f59d4c85e7f151cc524b19f11c7 Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:21 +0800 Subject: [PATCH 35/43] OvmfPkg/LsiScsiDxe: Install the skeleton of driver binding Implement the dummy functions for EFI Driver Binding protocol. v2: Remove "STATIC" from LsiScsiControllerSupported() Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200717061130.8881-3-glin@suse.com> (cherry picked from commit 5e6b870a53f7c6eb38bdab27bcdae3e42b2a4ff2) --- OvmfPkg/LsiScsiDxe/LsiScsi.c | 72 ++++++++++++++++++++++++++++++- OvmfPkg/LsiScsiDxe/LsiScsi.h | 49 +++++++++++++++++++++ OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf | 2 + 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsi.h diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index 9c90941688ed..79a2af4fee73 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -9,8 +9,71 @@ **/ +#include <Library/UefiLib.h> #include <Uefi/UefiSpec.h> +#include "LsiScsi.h" + +// +// Probe, start and stop functions of this driver, called by the DXE core for +// specific devices. +// +// The following specifications document these interfaces: +// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol +// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol +// + +EFI_STATUS +EFIAPI +LsiScsiControllerSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +LsiScsiControllerStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +LsiScsiControllerStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return EFI_SUCCESS; +} + +// +// The static object that groups the Supported() (ie. probe), Start() and +// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata +// C, 10.1 EFI Driver Binding Protocol. +// +STATIC +EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { + &LsiScsiControllerSupported, + &LsiScsiControllerStart, + &LsiScsiControllerStop, + 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers + NULL, // ImageHandle, to be overwritten by + // EfiLibInstallDriverBindingComponentName2() in LsiScsiEntryPoint() + NULL // DriverBindingHandle, ditto +}; + + // // Entry point of this driver // @@ -21,5 +84,12 @@ LsiScsiEntryPoint ( IN EFI_SYSTEM_TABLE *SystemTable ) { - return EFI_UNSUPPORTED; + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDriverBinding, + ImageHandle, // The handle to install onto + NULL, // TODO Component name + NULL // TODO Component name + ); } diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h new file mode 100644 index 000000000000..328bd289b8e8 --- /dev/null +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h @@ -0,0 +1,49 @@ +/** @file + + Internal definitions for the LSI 53C895A SCSI driver, which produces + Extended SCSI Pass Thru Protocol instances for LSI 53C895A SCSI devices. + + Copyright (C) 2020, SUSE LLC. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LSI_SCSI_DXE_H_ +#define _LSI_SCSI_DXE_H_ + +// +// Probe, start and stop functions of this driver, called by the DXE core for +// specific devices. +// +// The following specifications document these interfaces: +// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol +// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol +// + +EFI_STATUS +EFIAPI +LsiScsiControllerSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +LsiScsiControllerStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +LsiScsiControllerStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +#endif // _LSI_SCSI_DXE_H_ diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf index 8b6dccaff3eb..5cb15c456549 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf +++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf @@ -18,9 +18,11 @@ [Defines] [Sources] LsiScsi.c + LsiScsi.h [Packages] MdePkg/MdePkg.dec [LibraryClasses] UefiDriverEntryPoint + UefiLib -- 2.27.0 From 6e528eaa889cc6f90e7c62c32c6960f0d0a9106e Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:22 +0800 Subject: [PATCH 36/43] OvmfPkg/LsiScsiDxe: Report the name of the driver Implement LsiScsiGetDriverName() and LsiScsiGetDeviceName() to report the name of the driver. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200717061130.8881-4-glin@suse.com> (cherry picked from commit 386ca8abf74cd98ca9c0ef64b4427acd4e2e2431) --- OvmfPkg/LsiScsiDxe/LsiScsi.c | 69 ++++++++++++++++++++++++++++++++++-- OvmfPkg/LsiScsiDxe/LsiScsi.h | 31 ++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index 79a2af4fee73..62daa3ab99bf 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -74,6 +74,71 @@ EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { }; +// +// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and +// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name +// in English, for display on standard console devices. This is recommended for +// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's +// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names. +// +// Device type names ("LSI 53C895A SCSI Controller") are not formatted because +// the driver supports only that device type. Therefore the driver name +// suffices for unambiguous identification. +// + +STATIC +EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { + { "eng;en", L"LSI 53C895A SCSI Controller Driver" }, + { NULL, NULL } +}; + +STATIC +EFI_COMPONENT_NAME_PROTOCOL gComponentName; + +EFI_STATUS +EFIAPI +LsiScsiGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDriverNameTable, + DriverName, + (BOOLEAN)(This == &gComponentName) // Iso639Language + ); +} + +EFI_STATUS +EFIAPI +LsiScsiGetDeviceName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN EFI_HANDLE ChildHandle, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + +STATIC +EFI_COMPONENT_NAME_PROTOCOL gComponentName = { + &LsiScsiGetDriverName, + &LsiScsiGetDeviceName, + "eng" // SupportedLanguages, ISO 639-2 language codes +}; + +STATIC +EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &LsiScsiGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &LsiScsiGetDeviceName, + "en" // SupportedLanguages, RFC 4646 language codes +}; + // // Entry point of this driver // @@ -89,7 +154,7 @@ LsiScsiEntryPoint ( SystemTable, &gDriverBinding, ImageHandle, // The handle to install onto - NULL, // TODO Component name - NULL // TODO Component name + &gComponentName, + &gComponentName2 ); } diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h index 328bd289b8e8..6c8dcbd70a0a 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.h +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h @@ -46,4 +46,35 @@ LsiScsiControllerStop ( IN EFI_HANDLE *ChildHandleBuffer ); + +// +// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and +// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name +// in English, for display on standard console devices. This is recommended for +// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's +// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names. +// +// Device type names ("LSI 53C895A SCSI Controller") are not formatted because +// the driver supports only that device type. Therefore the driver name +// suffices for unambiguous identification. +// + +EFI_STATUS +EFIAPI +LsiScsiGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +LsiScsiGetDeviceName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN EFI_HANDLE ChildHandle, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + #endif // _LSI_SCSI_DXE_H_ -- 2.27.0 From efcf241a0def31de5ca9a6384401ac39da27113a Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:23 +0800 Subject: [PATCH 37/43] OvmfPkg/LsiScsiDxe: Probe PCI devices and look for LsiScsi Implement LsiScsiControllerSupported() to probe the PCI ID and look for LSI 53C895A. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200717061130.8881-5-glin@suse.com> (cherry picked from commit 79f802a50ee942e297c86138e6c19cab77a3a2eb) --- OvmfPkg/Include/IndustryStandard/LsiScsi.h | 20 +++++++++ OvmfPkg/LsiScsiDxe/LsiScsi.c | 48 +++++++++++++++++++++- OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf | 6 +++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 OvmfPkg/Include/IndustryStandard/LsiScsi.h diff --git a/OvmfPkg/Include/IndustryStandard/LsiScsi.h b/OvmfPkg/Include/IndustryStandard/LsiScsi.h new file mode 100644 index 000000000000..c09e864a1f39 --- /dev/null +++ b/OvmfPkg/Include/IndustryStandard/LsiScsi.h @@ -0,0 +1,20 @@ +/** @file + + Macros and type definitions for LSI 53C895A SCSI devices. + + Copyright (C) 2020, SUSE LLC. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LSI_SCSI_H_ +#define _LSI_SCSI_H_ + +// +// Device ID +// +#define LSI_LOGIC_PCI_VENDOR_ID 0x1000 +#define LSI_53C895A_PCI_DEVICE_ID 0x0012 + +#endif // _LSI_SCSI_H_ diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index 62daa3ab99bf..5bca85bd75eb 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -9,7 +9,12 @@ **/ +#include <IndustryStandard/LsiScsi.h> +#include <IndustryStandard/Pci.h> +#include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> +#include <Protocol/PciIo.h> +#include <Protocol/PciRootBridgeIo.h> #include <Uefi/UefiSpec.h> #include "LsiScsi.h" @@ -31,7 +36,48 @@ LsiScsiControllerSupported ( IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + (VOID **)&PciIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID && + Pci.Hdr.DeviceId == LSI_53C895A_PCI_DEVICE_ID) { + Status = EFI_SUCCESS; + } else { + Status = EFI_UNSUPPORTED; + } + +Done: + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return Status; } EFI_STATUS diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf index 5cb15c456549..7ce11fcc6a03 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf +++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf @@ -22,7 +22,13 @@ [Sources] [Packages] MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec [LibraryClasses] + BaseLib + UefiBootServicesTableLib UefiDriverEntryPoint UefiLib + +[Protocols] + gEfiPciIoProtocolGuid ## TO_START -- 2.27.0 From 8e0389bf8a719a0ca410a8fef5e2f84666454351 Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:24 +0800 Subject: [PATCH 38/43] OvmfPkg/LsiScsiDxe: Install stubbed EXT_SCSI_PASS_THRU Partially implement LsiScsiControllerStart() and LsiScsiControllerStop() to insert the scaffolding of EXT_SCSI_PASS_THRU functions. v3: Squash the newline below the declaration of LSI_SCSI_FROM_PASS_THRU v2: Remove the closing of PciIo protocol from LsiScsiControllerStop(). Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200717061130.8881-6-glin@suse.com> (cherry picked from commit 23d982e20528261b0bb3a704131d78d7e05bc59d) --- OvmfPkg/LsiScsiDxe/LsiScsi.c | 164 +++++++++++++++++++++++++++++- OvmfPkg/LsiScsiDxe/LsiScsi.h | 78 ++++++++++++++ OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf | 3 + 3 files changed, 244 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index 5bca85bd75eb..67fadb411e85 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -11,14 +11,105 @@ #include <IndustryStandard/LsiScsi.h> #include <IndustryStandard/Pci.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> #include <Protocol/PciIo.h> #include <Protocol/PciRootBridgeIo.h> +#include <Protocol/ScsiPassThruExt.h> #include <Uefi/UefiSpec.h> #include "LsiScsi.h" +// +// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL +// for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C, +// sections +// - 14.1 SCSI Driver Model Overview, +// - 14.7 Extended SCSI Pass Thru Protocol. +// + +EFI_STATUS +EFIAPI +LsiScsiPassThru ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +LsiScsiGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **TargetPointer, + IN OUT UINT64 *Lun + ) +{ + return EFI_NOT_FOUND; +} + +EFI_STATUS +EFIAPI +LsiScsiBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + return EFI_NOT_FOUND; +} + +EFI_STATUS +EFIAPI +LsiScsiGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **TargetPointer, + OUT UINT64 *Lun + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +LsiScsiResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +LsiScsiResetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +LsiScsiGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **TargetPointer + ) +{ + return EFI_NOT_FOUND; +} + // // Probe, start and stop functions of this driver, called by the DXE core for // specific devices. @@ -88,7 +179,49 @@ LsiScsiControllerStart ( IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { + EFI_STATUS Status; + LSI_SCSI_DEV *Dev; + + Dev = AllocateZeroPool (sizeof (*Dev)); + if (Dev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Dev->Signature = LSI_SCSI_DEV_SIGNATURE; + + // + // Host adapter channel, doesn't exist + // + Dev->PassThruMode.AdapterId = MAX_UINT32; + Dev->PassThruMode.Attributes = + EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | + EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL; + + Dev->PassThru.Mode = &Dev->PassThruMode; + Dev->PassThru.PassThru = &LsiScsiPassThru; + Dev->PassThru.GetNextTargetLun = &LsiScsiGetNextTargetLun; + Dev->PassThru.BuildDevicePath = &LsiScsiBuildDevicePath; + Dev->PassThru.GetTargetLun = &LsiScsiGetTargetLun; + Dev->PassThru.ResetChannel = &LsiScsiResetChannel; + Dev->PassThru.ResetTargetLun = &LsiScsiResetTargetLun; + Dev->PassThru.GetNextTarget = &LsiScsiGetNextTarget; + + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + EFI_NATIVE_INTERFACE, + &Dev->PassThru + ); + if (EFI_ERROR (Status)) { + goto FreePool; + } + return EFI_SUCCESS; + +FreePool: + FreePool (Dev); + + return Status; } EFI_STATUS @@ -100,7 +233,36 @@ LsiScsiControllerStop ( IN EFI_HANDLE *ChildHandleBuffer ) { - return EFI_SUCCESS; + EFI_STATUS Status; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru; + LSI_SCSI_DEV *Dev; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **)&PassThru, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Dev = LSI_SCSI_FROM_PASS_THRU (PassThru); + + Status = gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + &Dev->PassThru + ); + if (EFI_ERROR (Status)) { + return Status; + } + + FreePool (Dev); + + return Status; } // diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h index 6c8dcbd70a0a..751d5b193b51 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.h +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h @@ -12,6 +12,18 @@ #ifndef _LSI_SCSI_DXE_H_ #define _LSI_SCSI_DXE_H_ +typedef struct { + UINT32 Signature; + EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; +} LSI_SCSI_DEV; + +#define LSI_SCSI_DEV_SIGNATURE SIGNATURE_32 ('L','S','I','S') + +#define LSI_SCSI_FROM_PASS_THRU(PassThruPtr) \ + CR (PassThruPtr, LSI_SCSI_DEV, PassThru, LSI_SCSI_DEV_SIGNATURE) + + // // Probe, start and stop functions of this driver, called by the DXE core for // specific devices. @@ -47,6 +59,72 @@ LsiScsiControllerStop ( ); +// +// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL +// for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C, +// sections +// - 14.1 SCSI Driver Model Overview, +// - 14.7 Extended SCSI Pass Thru Protocol. +// + +EFI_STATUS +EFIAPI +LsiScsiPassThru ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +EFI_STATUS +EFIAPI +LsiScsiGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **TargetPointer, + IN OUT UINT64 *Lun + ); + +EFI_STATUS +EFIAPI +LsiScsiBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +EFI_STATUS +EFIAPI +LsiScsiGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **TargetPointer, + OUT UINT64 *Lun + ); + +EFI_STATUS +EFIAPI +LsiScsiResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +LsiScsiResetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ); + +EFI_STATUS +EFIAPI +LsiScsiGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **TargetPointer + ); + + // // The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and // EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf index 7ce11fcc6a03..14f9c68308cc 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf +++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf @@ -26,9 +26,12 @@ [Packages] [LibraryClasses] BaseLib + BaseMemoryLib + MemoryAllocationLib UefiBootServicesTableLib UefiDriverEntryPoint UefiLib [Protocols] + gEfiExtScsiPassThruProtocolGuid ## BY_START gEfiPciIoProtocolGuid ## TO_START -- 2.27.0 From 98072462fce37b9a1079f1427fa512ad93e91f88 Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:25 +0800 Subject: [PATCH 39/43] OvmfPkg/LsiScsiDxe: Report Targets and LUNs Implement LsiScsiGetNextTargetLun(), LsiScsiBuildDevicePath(), LsiScsiGetTargetLun(), and LsiScsiGetNextTarget() to report Targets and LUNs and build the device path. This commit also introduces two PCD value: PcdLsiScsiMaxTargetLimit and PcdLsiScsiMaxLunLimit as the limits for Targets and LUNs. v3: - Update the range of LUN in the assertioin - Squash the spurious newline into the previous commit v2: - Zero out (*Target) in LsiScsiGetTargetLun() - Use CopyMem() instead of the one-byte shortcut to copy target from ScsiDevicePath->Pun - Add asserts for PcdLsiScsiMaxTargetLimit and PcdLsiScsiMaxLunLimit Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Message-Id: <20200717061130.8881-7-glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit 12d99b8f2367c2fd1ca9b3165acd52a0c9dcc99e) --- OvmfPkg/LsiScsiDxe/LsiScsi.c | 148 +++++++++++++++++++++++++++++- OvmfPkg/LsiScsiDxe/LsiScsi.h | 2 + OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf | 6 ++ OvmfPkg/OvmfPkg.dec | 5 + 4 files changed, 159 insertions(+), 2 deletions(-) diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index 67fadb411e85..172779240636 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -15,6 +15,7 @@ #include <Library/BaseMemoryLib.h> #include <Library/DebugLib.h> #include <Library/MemoryAllocationLib.h> +#include <Library/PcdLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiLib.h> #include <Protocol/PciIo.h> @@ -53,6 +54,49 @@ LsiScsiGetNextTargetLun ( IN OUT UINT64 *Lun ) { + LSI_SCSI_DEV *Dev; + UINTN Idx; + UINT8 *Target; + UINT16 LastTarget; + + // + // the TargetPointer input parameter is unnecessarily a pointer-to-pointer + // + Target = *TargetPointer; + + // + // Search for first non-0xFF byte. If not found, return first target & LUN. + // + for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx) + ; + if (Idx == TARGET_MAX_BYTES) { + SetMem (Target, TARGET_MAX_BYTES, 0x00); + *Lun = 0; + return EFI_SUCCESS; + } + + CopyMem (&LastTarget, Target, sizeof LastTarget); + + // + // increment (target, LUN) pair if valid on input + // + Dev = LSI_SCSI_FROM_PASS_THRU (This); + if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) { + return EFI_INVALID_PARAMETER; + } + + if (*Lun < Dev->MaxLun) { + ++*Lun; + return EFI_SUCCESS; + } + + if (LastTarget < Dev->MaxTarget) { + *Lun = 0; + ++LastTarget; + CopyMem (Target, &LastTarget, sizeof LastTarget); + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; } @@ -65,7 +109,34 @@ LsiScsiBuildDevicePath ( IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath ) { - return EFI_NOT_FOUND; + UINT16 TargetValue; + LSI_SCSI_DEV *Dev; + SCSI_DEVICE_PATH *ScsiDevicePath; + + if (DevicePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + CopyMem (&TargetValue, Target, sizeof TargetValue); + Dev = LSI_SCSI_FROM_PASS_THRU (This); + if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun || Lun > 0xFFFF) { + return EFI_NOT_FOUND; + } + + ScsiDevicePath = AllocatePool (sizeof *ScsiDevicePath); + if (ScsiDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH; + ScsiDevicePath->Header.SubType = MSG_SCSI_DP; + ScsiDevicePath->Header.Length[0] = (UINT8) sizeof *ScsiDevicePath; + ScsiDevicePath->Header.Length[1] = (UINT8) (sizeof *ScsiDevicePath >> 8); + ScsiDevicePath->Pun = TargetValue; + ScsiDevicePath->Lun = (UINT16) Lun; + + *DevicePath = &ScsiDevicePath->Header; + return EFI_SUCCESS; } EFI_STATUS @@ -77,7 +148,33 @@ LsiScsiGetTargetLun ( OUT UINT64 *Lun ) { - return EFI_UNSUPPORTED; + SCSI_DEVICE_PATH *ScsiDevicePath; + LSI_SCSI_DEV *Dev; + UINT8 *Target; + + if (DevicePath == NULL || TargetPointer == NULL || *TargetPointer == NULL || + Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DevicePath->Type != MESSAGING_DEVICE_PATH || + DevicePath->SubType != MSG_SCSI_DP) { + return EFI_UNSUPPORTED; + } + + ScsiDevicePath = (SCSI_DEVICE_PATH *) DevicePath; + Dev = LSI_SCSI_FROM_PASS_THRU (This); + if (ScsiDevicePath->Pun > Dev->MaxTarget || + ScsiDevicePath->Lun > Dev->MaxLun) { + return EFI_NOT_FOUND; + } + + Target = *TargetPointer; + ZeroMem (Target, TARGET_MAX_BYTES); + CopyMem (Target, &ScsiDevicePath->Pun, sizeof ScsiDevicePath->Pun); + *Lun = ScsiDevicePath->Lun; + + return EFI_SUCCESS; } EFI_STATUS @@ -107,6 +204,42 @@ LsiScsiGetNextTarget ( IN OUT UINT8 **TargetPointer ) { + LSI_SCSI_DEV *Dev; + UINTN Idx; + UINT8 *Target; + UINT16 LastTarget; + + // + // the TargetPointer input parameter is unnecessarily a pointer-to-pointer + // + Target = *TargetPointer; + + // + // Search for first non-0xFF byte. If not found, return first target. + // + for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx) + ; + if (Idx == TARGET_MAX_BYTES) { + SetMem (Target, TARGET_MAX_BYTES, 0x00); + return EFI_SUCCESS; + } + + CopyMem (&LastTarget, Target, sizeof LastTarget); + + // + // increment target if valid on input + // + Dev = LSI_SCSI_FROM_PASS_THRU (This); + if (LastTarget > Dev->MaxTarget) { + return EFI_INVALID_PARAMETER; + } + + if (LastTarget < Dev->MaxTarget) { + ++LastTarget; + CopyMem (Target, &LastTarget, sizeof LastTarget); + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; } @@ -189,6 +322,17 @@ LsiScsiControllerStart ( Dev->Signature = LSI_SCSI_DEV_SIGNATURE; + STATIC_ASSERT ( + FixedPcdGet8 (PcdLsiScsiMaxTargetLimit) < 8, + "LSI 53C895A supports targets [0..7]" + ); + STATIC_ASSERT ( + FixedPcdGet8 (PcdLsiScsiMaxLunLimit) < 128, + "LSI 53C895A supports LUNs [0..127]" + ); + Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit); + Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit); + // // Host adapter channel, doesn't exist // diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h index 751d5b193b51..6c6ed25f1c33 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.h +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h @@ -14,6 +14,8 @@ typedef struct { UINT32 Signature; + UINT8 MaxTarget; + UINT8 MaxLun; EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; } LSI_SCSI_DEV; diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf index 14f9c68308cc..6111449577a8 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf +++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf @@ -27,7 +27,9 @@ [Packages] [LibraryClasses] BaseLib BaseMemoryLib + DebugLib MemoryAllocationLib + PcdLib UefiBootServicesTableLib UefiDriverEntryPoint UefiLib @@ -35,3 +37,7 @@ [LibraryClasses] [Protocols] gEfiExtScsiPassThruProtocolGuid ## BY_START gEfiPciIoProtocolGuid ## TO_START + +[FixedPcd] + gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit ## CONSUMES diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index e86a3f86c106..30f9f046a296 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -143,6 +143,11 @@ [PcdsFixedAtBuild] ## Microseconds to stall between polling for MptScsi request result gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x3a + ## Set the *inclusive* number of targets and LUNs that LsiScsi exposes for + # scan by ScsiBusDxe. + gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit|7|UINT8|0x3b + gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit|0|UINT8|0x3c + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa -- 2.27.0 From 2d207ac82ba5022971715b786ad9ccb32fc2d205 Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:26 +0800 Subject: [PATCH 40/43] OvmfPkg/LsiScsiDxe: Open PciIo protocol and initialize the device Open PciIo protocol and use it to initialize the device. The initialization of LSI 53C895A is simple: just set the SRST bit in Interrupt Status Zero register to reset the device. v2: - Use the BITx macros for the bit constants - Add the closing of PciIo protocol in LsiScsiControllerStop() Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Message-Id: <20200717061130.8881-8-glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit 8d6193902f2da15c8bad9941bfe493cca903a3ca) --- OvmfPkg/Include/IndustryStandard/LsiScsi.h | 21 ++++ OvmfPkg/LsiScsiDxe/LsiScsi.c | 136 ++++++++++++++++++++- OvmfPkg/LsiScsiDxe/LsiScsi.h | 3 + 3 files changed, 159 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/Include/IndustryStandard/LsiScsi.h b/OvmfPkg/Include/IndustryStandard/LsiScsi.h index c09e864a1f39..185e553c8eb4 100644 --- a/OvmfPkg/Include/IndustryStandard/LsiScsi.h +++ b/OvmfPkg/Include/IndustryStandard/LsiScsi.h @@ -17,4 +17,25 @@ #define LSI_LOGIC_PCI_VENDOR_ID 0x1000 #define LSI_53C895A_PCI_DEVICE_ID 0x0012 +// +// LSI 53C895A Registers +// +#define LSI_REG_DSTAT 0x0C +#define LSI_REG_ISTAT0 0x14 +#define LSI_REG_DSP 0x2C +#define LSI_REG_SIST0 0x42 +#define LSI_REG_SIST1 0x43 + +// +// The status bits for Interrupt Status Zero (ISTAT0) +// +#define LSI_ISTAT0_DIP BIT0 +#define LSI_ISTAT0_SIP BIT1 +#define LSI_ISTAT0_INTF BIT2 +#define LSI_ISTAT0_CON BIT3 +#define LSI_ISTAT0_SEM BIT4 +#define LSI_ISTAT0_SIGP BIT5 +#define LSI_ISTAT0_SRST BIT6 +#define LSI_ISTAT0_ABRT BIT7 + #endif // _LSI_SCSI_H_ diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index 172779240636..a9c107c04ef1 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -25,6 +25,33 @@ #include "LsiScsi.h" +STATIC +EFI_STATUS +Out8 ( + IN LSI_SCSI_DEV *Dev, + IN UINT32 Addr, + IN UINT8 Data + ) +{ + return Dev->PciIo->Io.Write ( + Dev->PciIo, + EfiPciIoWidthUint8, + PCI_BAR_IDX0, + Addr, + 1, + &Data + ); +} + +STATIC +EFI_STATUS +LsiScsiReset ( + IN LSI_SCSI_DEV *Dev + ) +{ + return Out8 (Dev, LSI_REG_ISTAT0, LSI_ISTAT0_SRST); +} + // // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL // for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C, @@ -243,6 +270,21 @@ LsiScsiGetNextTarget ( return EFI_NOT_FOUND; } +STATIC +VOID +EFIAPI +LsiScsiExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LSI_SCSI_DEV *Dev; + + Dev = Context; + DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context)); + LsiScsiReset (Dev); +} + // // Probe, start and stop functions of this driver, called by the DXE core for // specific devices. @@ -333,6 +375,58 @@ LsiScsiControllerStart ( Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit); Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit); + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + (VOID **)&Dev->PciIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto FreePool; + } + + Status = Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationGet, + 0, + &Dev->OrigPciAttrs + ); + if (EFI_ERROR (Status)) { + goto CloseProtocol; + } + + // + // Enable I/O Space & Bus-Mastering + // + Status = Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationEnable, + (EFI_PCI_IO_ATTRIBUTE_IO | + EFI_PCI_IO_ATTRIBUTE_BUS_MASTER), + NULL + ); + if (EFI_ERROR (Status)) { + goto CloseProtocol; + } + + Status = LsiScsiReset (Dev); + if (EFI_ERROR (Status)) { + goto RestoreAttributes; + } + + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_CALLBACK, + &LsiScsiExitBoot, + Dev, + &Dev->ExitBoot + ); + if (EFI_ERROR (Status)) { + goto UninitDev; + } + // // Host adapter channel, doesn't exist // @@ -357,11 +451,33 @@ LsiScsiControllerStart ( &Dev->PassThru ); if (EFI_ERROR (Status)) { - goto FreePool; + goto CloseExitBoot; } return EFI_SUCCESS; +CloseExitBoot: + gBS->CloseEvent (Dev->ExitBoot); + +UninitDev: + LsiScsiReset (Dev); + +RestoreAttributes: + Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationSet, + Dev->OrigPciAttrs, + NULL + ); + +CloseProtocol: + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + FreePool: FreePool (Dev); @@ -404,6 +520,24 @@ LsiScsiControllerStop ( return Status; } + gBS->CloseEvent (Dev->ExitBoot); + + LsiScsiReset (Dev); + + Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationSet, + Dev->OrigPciAttrs, + NULL + ); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + FreePool (Dev); return Status; diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h index 6c6ed25f1c33..8c2acff6e86f 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.h +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h @@ -14,6 +14,9 @@ typedef struct { UINT32 Signature; + UINT64 OrigPciAttrs; + EFI_EVENT ExitBoot; + EFI_PCI_IO_PROTOCOL *PciIo; UINT8 MaxTarget; UINT8 MaxLun; EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; -- 2.27.0 From 88aabab1fbb8375fc288d600462292de5f4b5f60 Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:27 +0800 Subject: [PATCH 41/43] OvmfPkg/LsiScsiDxe: Map DMA buffer Map DMA buffer and perpare for the implementation of LsiScsiPassThru(). v2: - Replace 0x10000 with SIZE_64KB macro for the DMA buffer data array - Remove DUAL_ADDRESS_CYCLE from PciIo since we don't really need 64-bit DMA address Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200717061130.8881-9-glin@suse.com> (cherry picked from commit f1d6c1eba1b76e57568c8d7c947f60041939f1f9) --- OvmfPkg/LsiScsiDxe/LsiScsi.c | 62 +++++++++++++++++++++++++++++++++++- OvmfPkg/LsiScsiDxe/LsiScsi.h | 14 ++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index a9c107c04ef1..52c224aad9a5 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -356,6 +356,8 @@ LsiScsiControllerStart ( { EFI_STATUS Status; LSI_SCSI_DEV *Dev; + UINTN Pages; + UINTN BytesMapped; Dev = AllocateZeroPool (sizeof (*Dev)); if (Dev == NULL) { @@ -411,11 +413,45 @@ LsiScsiControllerStart ( goto CloseProtocol; } - Status = LsiScsiReset (Dev); + // + // Create buffers for data transfer + // + Pages = EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)); + Status = Dev->PciIo->AllocateBuffer ( + Dev->PciIo, + AllocateAnyPages, + EfiBootServicesData, + Pages, + (VOID **)&Dev->Dma, + EFI_PCI_ATTRIBUTE_MEMORY_CACHED + ); if (EFI_ERROR (Status)) { goto RestoreAttributes; } + BytesMapped = EFI_PAGES_TO_SIZE (Pages); + Status = Dev->PciIo->Map ( + Dev->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + Dev->Dma, + &BytesMapped, + &Dev->DmaPhysical, + &Dev->DmaMapping + ); + if (EFI_ERROR (Status)) { + goto FreeBuffer; + } + + if (BytesMapped != EFI_PAGES_TO_SIZE (Pages)) { + Status = EFI_OUT_OF_RESOURCES; + goto Unmap; + } + + Status = LsiScsiReset (Dev); + if (EFI_ERROR (Status)) { + goto Unmap; + } + Status = gBS->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, @@ -462,6 +498,19 @@ CloseExitBoot: UninitDev: LsiScsiReset (Dev); +Unmap: + Dev->PciIo->Unmap ( + Dev->PciIo, + Dev->DmaMapping + ); + +FreeBuffer: + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + Pages, + Dev->Dma + ); + RestoreAttributes: Dev->PciIo->Attributes ( Dev->PciIo, @@ -524,6 +573,17 @@ LsiScsiControllerStop ( LsiScsiReset (Dev); + Dev->PciIo->Unmap ( + Dev->PciIo, + Dev->DmaMapping + ); + + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)), + Dev->Dma + ); + Dev->PciIo->Attributes ( Dev->PciIo, EfiPciIoAttributeOperationSet, diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h index 8c2acff6e86f..9f9e5c7fed00 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.h +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h @@ -12,6 +12,17 @@ #ifndef _LSI_SCSI_DXE_H_ #define _LSI_SCSI_DXE_H_ +typedef struct { + // + // Allocate 64KB for read/write buffer. It seems sufficient for the common + // boot scenarios. + // + // NOTE: The number of bytes for data transmission is bounded by DMA Byte + // Count (DBC), a 24-bit register, so the maximum is 0xFFFFFF (16MB-1). + // + UINT8 Data[SIZE_64KB]; +} LSI_SCSI_DMA_BUFFER; + typedef struct { UINT32 Signature; UINT64 OrigPciAttrs; @@ -19,6 +30,9 @@ typedef struct { EFI_PCI_IO_PROTOCOL *PciIo; UINT8 MaxTarget; UINT8 MaxLun; + LSI_SCSI_DMA_BUFFER *Dma; + EFI_PHYSICAL_ADDRESS DmaPhysical; + VOID *DmaMapping; EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; } LSI_SCSI_DEV; -- 2.27.0 From 91b167b9b7ad67cc6129ba610e2c2d39518f1d14 Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:28 +0800 Subject: [PATCH 42/43] OvmfPkg/LsiScsiDxe: Examine the incoming SCSI Request Packet This is the first part of LsiScsiPassThru(). Before processing the SCSI Request packet, we have to make sure whether the packet is valid or not. v2: Make LsiScsiPassThru() return EFI_UNSUPPORTED since this function is half-implemented Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200717061130.8881-10-glin@suse.com> (cherry picked from commit 97e60818b64495db562dc456a1fb31980898f4c3) --- OvmfPkg/LsiScsiDxe/LsiScsi.c | 98 ++++++++++++++++++++++++++++++++++++ OvmfPkg/LsiScsiDxe/LsiScsi.h | 4 ++ 2 files changed, 102 insertions(+) diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index 52c224aad9a5..9859632e02d6 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -52,6 +52,95 @@ LsiScsiReset ( return Out8 (Dev, LSI_REG_ISTAT0, LSI_ISTAT0_SRST); } +STATIC +EFI_STATUS +ReportHostAdapterOverrunError ( + OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + Packet->SenseDataLength = 0; + Packet->HostAdapterStatus = + EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + return EFI_BAD_BUFFER_SIZE; +} + +/** + + Check the request packet from the Extended SCSI Pass Thru Protocol. The + request packet is modified, to be forwarded outwards by LsiScsiPassThru(), + if invalid or unsupported parameters are detected. + + @param[in] Dev The LSI 53C895A SCSI device the packet targets. + + @param[in] Target The SCSI target controlled by the LSI 53C895A SCSI + device. + + @param[in] Lun The Logical Unit Number under the SCSI target. + + @param[in out] Packet The Extended SCSI Pass Thru Protocol packet. + + + @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid. + + @return Otherwise, invalid or unsupported parameters were + detected. Status codes are meant for direct forwarding + by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() + implementation. + + **/ +STATIC +EFI_STATUS +LsiScsiCheckRequest ( + IN LSI_SCSI_DEV *Dev, + IN UINT8 Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + if (Target > Dev->MaxTarget || Lun > Dev->MaxLun || + Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL || + // + // Trying to receive, but destination pointer is NULL, or contradicting + // transfer direction + // + (Packet->InTransferLength > 0 && + (Packet->InDataBuffer == NULL || + Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE + ) + ) || + + // + // Trying to send, but source pointer is NULL, or contradicting transfer + // direction + // + (Packet->OutTransferLength > 0 && + (Packet->OutDataBuffer == NULL || + Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ + ) + ) + ) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL || + (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) || + Packet->CdbLength > sizeof Dev->Dma->Cdb) { + return EFI_UNSUPPORTED; + } + + if (Packet->InTransferLength > sizeof Dev->Dma->Data) { + Packet->InTransferLength = sizeof Dev->Dma->Data; + return ReportHostAdapterOverrunError (Packet); + } + if (Packet->OutTransferLength > sizeof Dev->Dma->Data) { + Packet->OutTransferLength = sizeof Dev->Dma->Data; + return ReportHostAdapterOverrunError (Packet); + } + + return EFI_SUCCESS; +} + // // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL // for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C, @@ -70,6 +159,15 @@ LsiScsiPassThru ( IN EFI_EVENT Event OPTIONAL ) { + EFI_STATUS Status; + LSI_SCSI_DEV *Dev; + + Dev = LSI_SCSI_FROM_PASS_THRU (This); + Status = LsiScsiCheckRequest (Dev, *Target, Lun, Packet); + if (EFI_ERROR (Status)) { + return Status; + } + return EFI_UNSUPPORTED; } diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h index 9f9e5c7fed00..05deeed379fe 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.h +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h @@ -13,6 +13,10 @@ #define _LSI_SCSI_DXE_H_ typedef struct { + // + // The max size of CDB is 32. + // + UINT8 Cdb[32]; // // Allocate 64KB for read/write buffer. It seems sufficient for the common // boot scenarios. -- 2.27.0 From 8db13bac6cde898fa87b84ee1ca96d2295b3501c Mon Sep 17 00:00:00 2001 From: Gary Lin <glin@suse.com> Date: Fri, 17 Jul 2020 14:11:29 +0800 Subject: [PATCH 43/43] OvmfPkg/LsiScsiDxe: Process the SCSI Request Packet This is the second part of LsiScsiPassThru(). LsiScsiProcessRequest() is added to translate the SCSI Request Packet into the LSI 53C895A commands. This function utilizes the so-called Script buffer to transmit a series of commands to the chip and then polls the DMA Status (DSTAT) register until the Scripts Interrupt Instruction Received (SIR) bit sets. Once the script is done, the SCSI Request Packet will be modified to reflect the result of the script. The Cumulative SCSI Byte Count (CSBC) register is fetched before and after the script to calculate the transferred bytes and update InTransferLength/OutTransferLength if necessary. v3: - Set DStat, SIst0, and SIst1 to 0 before using them - Amend the if statements for the DMA data instruction and add the assertions for the data direction - Also set SenseDataLength to 0 on the error path - Fix typos and amend comments - Amend the error handling of the calculation of transferred bytes v2: - Use the BITx macros for the most of LSI_* constants - Fix a typo: contorller => controller - Add SeaBIOS lsi-scsi driver as one of the references of the script - Cast the result of sizeof to UINT32 for the instructions of the script - Drop the backslashes - Replace LSI_SCSI_DMA_ADDR_LOW with LSI_SCSI_DMA_ADDR since we already removed DUAL_ADDRESS_CYCLE - Add more comments for the script - Fix the check of the script size at the end of the script - Always set SenseDataLength to 0 to avoid the caller to access SenseData - Improve the error handling in LsiScsiProcessRequest() Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Signed-off-by: Gary Lin <glin@suse.com> Message-Id: <20200717061130.8881-11-glin@suse.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> (cherry picked from commit 31830b07020c9796579d5b08dd642d19bce7d4f5) --- OvmfPkg/Include/IndustryStandard/LsiScsi.h | 64 ++++ OvmfPkg/LsiScsiDxe/LsiScsi.c | 408 ++++++++++++++++++++- OvmfPkg/LsiScsiDxe/LsiScsi.h | 21 ++ OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf | 3 + OvmfPkg/OvmfPkg.dec | 3 + 5 files changed, 498 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/Include/IndustryStandard/LsiScsi.h b/OvmfPkg/Include/IndustryStandard/LsiScsi.h index 185e553c8eb4..01d75323cdbc 100644 --- a/OvmfPkg/Include/IndustryStandard/LsiScsi.h +++ b/OvmfPkg/Include/IndustryStandard/LsiScsi.h @@ -25,6 +25,19 @@ #define LSI_REG_DSP 0x2C #define LSI_REG_SIST0 0x42 #define LSI_REG_SIST1 0x43 +#define LSI_REG_CSBC 0xDC + +// +// The status bits for DMA Status (DSTAT) +// +#define LSI_DSTAT_IID BIT0 +#define LSI_DSTAT_R BIT1 +#define LSI_DSTAT_SIR BIT2 +#define LSI_DSTAT_SSI BIT3 +#define LSI_DSTAT_ABRT BIT4 +#define LSI_DSTAT_BF BIT5 +#define LSI_DSTAT_MDPE BIT6 +#define LSI_DSTAT_DFE BIT7 // // The status bits for Interrupt Status Zero (ISTAT0) @@ -38,4 +51,55 @@ #define LSI_ISTAT0_SRST BIT6 #define LSI_ISTAT0_ABRT BIT7 +// +// The status bits for SCSI Interrupt Status Zero (SIST0) +// +#define LSI_SIST0_PAR BIT0 +#define LSI_SIST0_RST BIT1 +#define LSI_SIST0_UDC BIT2 +#define LSI_SIST0_SGE BIT3 +#define LSI_SIST0_RSL BIT4 +#define LSI_SIST0_SEL BIT5 +#define LSI_SIST0_CMP BIT6 +#define LSI_SIST0_MA BIT7 + +// +// The status bits for SCSI Interrupt Status One (SIST1) +// +#define LSI_SIST1_HTH BIT0 +#define LSI_SIST1_GEN BIT1 +#define LSI_SIST1_STO BIT2 +#define LSI_SIST1_R3 BIT3 +#define LSI_SIST1_SBMC BIT4 +#define LSI_SIST1_R5 BIT5 +#define LSI_SIST1_R6 BIT6 +#define LSI_SIST1_R7 BIT7 + +// +// LSI 53C895A Script Instructions +// +#define LSI_INS_TYPE_BLK 0x00000000 +#define LSI_INS_TYPE_IO BIT30 +#define LSI_INS_TYPE_TC BIT31 + +#define LSI_INS_BLK_SCSIP_DAT_OUT 0x00000000 +#define LSI_INS_BLK_SCSIP_DAT_IN BIT24 +#define LSI_INS_BLK_SCSIP_CMD BIT25 +#define LSI_INS_BLK_SCSIP_STAT (BIT24 | BIT25) +#define LSI_INS_BLK_SCSIP_MSG_OUT (BIT25 | BIT26) +#define LSI_INS_BLK_SCSIP_MSG_IN (BIT24 | BIT25 | BIT26) + +#define LSI_INS_IO_OPC_SEL 0x00000000 +#define LSI_INS_IO_OPC_WAIT_RESEL BIT28 + +#define LSI_INS_TC_CP BIT17 +#define LSI_INS_TC_JMP BIT19 +#define LSI_INS_TC_RA BIT23 + +#define LSI_INS_TC_OPC_JMP 0x00000000 +#define LSI_INS_TC_OPC_INT (BIT27 | BIT28) + +#define LSI_INS_TC_SCSIP_DAT_OUT 0x00000000 +#define LSI_INS_TC_SCSIP_MSG_IN (BIT24 | BIT25 | BIT26) + #endif // _LSI_SCSI_H_ diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c index 9859632e02d6..ed5fc3bfdd9d 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.c +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c @@ -43,6 +43,60 @@ Out8 ( ); } +STATIC +EFI_STATUS +Out32 ( + IN LSI_SCSI_DEV *Dev, + IN UINT32 Addr, + IN UINT32 Data + ) +{ + return Dev->PciIo->Io.Write ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX0, + Addr, + 1, + &Data + ); +} + +STATIC +EFI_STATUS +In8 ( + IN LSI_SCSI_DEV *Dev, + IN UINT32 Addr, + OUT UINT8 *Data + ) +{ + return Dev->PciIo->Io.Read ( + Dev->PciIo, + EfiPciIoWidthUint8, + PCI_BAR_IDX0, + Addr, + 1, + Data + ); +} + +STATIC +EFI_STATUS +In32 ( + IN LSI_SCSI_DEV *Dev, + IN UINT32 Addr, + OUT UINT32 *Data + ) +{ + return Dev->PciIo->Io.Read ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX0, + Addr, + 1, + Data + ); +} + STATIC EFI_STATUS LsiScsiReset ( @@ -141,6 +195,357 @@ LsiScsiCheckRequest ( return EFI_SUCCESS; } +/** + + Interpret the request packet from the Extended SCSI Pass Thru Protocol and + compose the script to submit the command and data to the controller. + + @param[in] Dev The LSI 53C895A SCSI device the packet targets. + + @param[in] Target The SCSI target controlled by the LSI 53C895A SCSI + device. + + @param[in] Lun The Logical Unit Number under the SCSI target. + + @param[in out] Packet The Extended SCSI Pass Thru Protocol packet. + + + @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid. + + @return Otherwise, invalid or unsupported parameters were + detected. Status codes are meant for direct forwarding + by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() + implementation. + + **/ +STATIC +EFI_STATUS +LsiScsiProcessRequest ( + IN LSI_SCSI_DEV *Dev, + IN UINT8 Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT32 *Script; + UINT8 *Cdb; + UINT8 *MsgOut; + UINT8 *MsgIn; + UINT8 *ScsiStatus; + UINT8 *Data; + UINT8 DStat; + UINT8 SIst0; + UINT8 SIst1; + UINT32 Csbc; + UINT32 CsbcBase; + UINT32 Transferred; + + Script = Dev->Dma->Script; + Cdb = Dev->Dma->Cdb; + Data = Dev->Dma->Data; + MsgIn = Dev->Dma->MsgIn; + MsgOut = &Dev->Dma->MsgOut; + ScsiStatus = &Dev->Dma->Status; + + *ScsiStatus = 0xFF; + + DStat = 0; + SIst0 = 0; + SIst1 = 0; + + SetMem (Cdb, sizeof Dev->Dma->Cdb, 0x00); + CopyMem (Cdb, Packet->Cdb, Packet->CdbLength); + + // + // Fetch the first Cumulative SCSI Byte Count (CSBC). + // + // CSBC is a cumulative counter of the actual number of bytes that have been + // transferred across the SCSI bus during data phases, i.e. it will not + // count bytes sent in command, status, message in and out phases. + // + Status = In32 (Dev, LSI_REG_CSBC, &CsbcBase); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Clean up the DMA buffer for the script. + // + SetMem (Script, sizeof Dev->Dma->Script, 0x00); + + // + // Compose the script to transfer data between the host and the device. + // + // References: + // * LSI53C895A PCI to Ultra2 SCSI Controller Version 2.2 + // - Chapter 5 SCSI SCRIPT Instruction Set + // * SEABIOS lsi-scsi driver + // + // All instructions used here consist of 2 32bit words. The first word + // contains the command to execute. The second word is loaded into the + // DMA SCRIPTS Pointer Save (DSPS) register as either the DMA address + // for data transmission or the address/offset for the jump command. + // Some commands, such as the selection of the target, don't need to + // transfer data through DMA or jump to another instruction, then DSPS + // has to be zero. + // + // There are 3 major parts in this script. The first part (1~3) contains + // the instructions to select target and LUN and send the SCSI command + // from the request packet. The second part (4~7) is to handle the + // potential disconnection and prepare for the data transmission. The + // instructions in the third part (8~10) transmit the given data and + // collect the result. Instruction 11 raises the interrupt and marks the + // end of the script. + // + + // + // 1. Select target. + // + *Script++ = LSI_INS_TYPE_IO | LSI_INS_IO_OPC_SEL | (UINT32)Target << 16; + *Script++ = 0x00000000; + + // + // 2. Select LUN. + // + *MsgOut = 0x80 | (UINT8) Lun; // 0x80: Identify bit + *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_OUT | + (UINT32)sizeof Dev->Dma->MsgOut; + *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgOut); + + // + // 3. Send the SCSI Command. + // + *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_CMD | + (UINT32)sizeof Dev->Dma->Cdb; + *Script++ = LSI_SCSI_DMA_ADDR (Dev, Cdb); + + // + // 4. Check whether the current SCSI phase is "Message In" or not + // and jump to 7 if it is. + // Note: LSI_INS_TC_RA stands for "Relative Address Mode", so the + // offset 0x18 in the second word means jumping forward + // 3 (0x18/8) instructions. + // + *Script++ = LSI_INS_TYPE_TC | LSI_INS_TC_OPC_JMP | + LSI_INS_TC_SCSIP_MSG_IN | LSI_INS_TC_RA | + LSI_INS_TC_CP; + *Script++ = 0x00000018; + + // + // 5. Read "Message" from the initiator to trigger reselect. + // + *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN | + (UINT32)sizeof Dev->Dma->MsgIn; + *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn); + + // + // 6. Wait reselect. + // + *Script++ = LSI_INS_TYPE_IO | LSI_INS_IO_OPC_WAIT_RESEL; + *Script++ = 0x00000000; + + // + // 7. Read "Message" from the initiator again + // + *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN | + (UINT32)sizeof Dev->Dma->MsgIn; + *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn); + + // + // 8. Set the DMA command for the read/write operations. + // Note: Some requests, e.g. "TEST UNIT READY", do not come with + // allocated InDataBuffer or OutDataBuffer. We skip the DMA + // data command for those requests or this script would fail + // with LSI_SIST0_SGE due to the zero data length. + // + // LsiScsiCheckRequest() prevents both integer overflows in the command + // opcodes, and buffer overflows. + // + if (Packet->InTransferLength > 0) { + ASSERT (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ); + ASSERT (Packet->InTransferLength <= sizeof Dev->Dma->Data); + *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_DAT_IN | + Packet->InTransferLength; + *Script++ = LSI_SCSI_DMA_ADDR (Dev, Data); + } else if (Packet->OutTransferLength > 0) { + ASSERT (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE); + ASSERT (Packet->OutTransferLength <= sizeof Dev->Dma->Data); + CopyMem (Data, Packet->OutDataBuffer, Packet->OutTransferLength); + *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_DAT_OUT | + Packet->OutTransferLength; + *Script++ = LSI_SCSI_DMA_ADDR (Dev, Data); + } + + // + // 9. Get the SCSI status. + // + *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_STAT | + (UINT32)sizeof Dev->Dma->Status; + *Script++ = LSI_SCSI_DMA_ADDR (Dev, Status); + + // + // 10. Get the SCSI message. + // + *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN | + (UINT32)sizeof Dev->Dma->MsgIn; + *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn); + + // + // 11. Raise the interrupt to end the script. + // + *Script++ = LSI_INS_TYPE_TC | LSI_INS_TC_OPC_INT | + LSI_INS_TC_SCSIP_DAT_OUT | LSI_INS_TC_JMP; + *Script++ = 0x00000000; + + // + // Make sure the size of the script doesn't exceed the buffer. + // + ASSERT (Script <= Dev->Dma->Script + ARRAY_SIZE (Dev->Dma->Script)); + + // + // The controller starts to execute the script once the DMA Script + // Pointer (DSP) register is set. + // + Status = Out32 (Dev, LSI_REG_DSP, LSI_SCSI_DMA_ADDR (Dev, Script)); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Poll the device registers (DSTAT, SIST0, and SIST1) until the SIR + // bit sets. + // + for(;;) { + Status = In8 (Dev, LSI_REG_DSTAT, &DStat); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = In8 (Dev, LSI_REG_SIST0, &SIst0); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = In8 (Dev, LSI_REG_SIST1, &SIst1); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (SIst0 != 0 || SIst1 != 0) { + goto Error; + } + + // + // Check the SIR (SCRIPTS Interrupt Instruction Received) bit. + // + if (DStat & LSI_DSTAT_SIR) { + break; + } + + gBS->Stall (Dev->StallPerPollUsec); + } + + // + // Check if everything is good. + // SCSI Message Code 0x00: COMMAND COMPLETE + // SCSI Status Code 0x00: Good + // + if (MsgIn[0] != 0 || *ScsiStatus != 0) { + goto Error; + } + + // + // Fetch CSBC again to calculate the transferred bytes and update + // InTransferLength/OutTransferLength. + // + // Note: The number of transferred bytes is bounded by + // "sizeof Dev->Dma->Data", so it's safe to subtract CsbcBase + // from Csbc. If the CSBC register wraps around, the correct + // difference is ensured by the standard C modular arithmetic. + // + Status = In32 (Dev, LSI_REG_CSBC, &Csbc); + if (EFI_ERROR (Status)) { + goto Error; + } + + Transferred = Csbc - CsbcBase; + if (Packet->InTransferLength > 0) { + if (Transferred <= Packet->InTransferLength) { + Packet->InTransferLength = Transferred; + } else { + goto Error; + } + } else if (Packet->OutTransferLength > 0) { + if (Transferred <= Packet->OutTransferLength) { + Packet->OutTransferLength = Transferred; + } else { + goto Error; + } + } + + // + // Copy Data to InDataBuffer if necessary. + // + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + CopyMem (Packet->InDataBuffer, Data, Packet->InTransferLength); + } + + // + // Always set SenseDataLength to 0. + // The instructions of LSI53C895A don't reply sense data. Instead, it + // relies on the SCSI command, "REQUEST SENSE", to get sense data. We set + // SenseDataLength to 0 to notify ScsiDiskDxe that there is no sense data + // written even if this request is processed successfully, so that It will + // issue "REQUEST SENSE" later to retrieve sense data. + // + Packet->SenseDataLength = 0; + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + + return EFI_SUCCESS; + +Error: + DEBUG ((DEBUG_VERBOSE, "%a: dstat: %02X, sist0: %02X, sist1: %02X\n", + __FUNCTION__, DStat, SIst0, SIst1)); + // + // Update the request packet to reflect the status. + // + if (*ScsiStatus != 0xFF) { + Packet->TargetStatus = *ScsiStatus; + } else { + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED; + } + + if (SIst0 & LSI_SIST0_PAR) { + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR; + } else if (SIst0 & LSI_SIST0_RST) { + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET; + } else if (SIst0 & LSI_SIST0_UDC) { + // + // The target device is disconnected unexpectedly. According to UEFI spec, + // this is TIMEOUT_COMMAND. + // + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND; + } else if (SIst0 & LSI_SIST0_SGE) { + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN; + } else if (SIst1 & LSI_SIST1_HTH) { + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT; + } else if (SIst1 & LSI_SIST1_GEN) { + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT; + } else if (SIst1 & LSI_SIST1_STO) { + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT; + } else { + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + } + + // + // SenseData may be used to inspect the error. Since we don't set sense data, + // SenseDataLength has to be 0. + // + Packet->SenseDataLength = 0; + + return EFI_DEVICE_ERROR; +} + // // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL // for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C, @@ -168,7 +573,7 @@ LsiScsiPassThru ( return Status; } - return EFI_UNSUPPORTED; + return LsiScsiProcessRequest (Dev, *Target, Lun, Packet); } EFI_STATUS @@ -474,6 +879,7 @@ LsiScsiControllerStart ( ); Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit); Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit); + Dev->StallPerPollUsec = PcdGet32 (PcdLsiScsiStallPerPollUsec); Status = gBS->OpenProtocol ( ControllerHandle, diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h index 05deeed379fe..6ecf523f5a9e 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsi.h +++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h @@ -13,6 +13,11 @@ #define _LSI_SCSI_DXE_H_ typedef struct { + // + // Allocate 32 UINT32 entries for the script and it's sufficient for + // 16 instructions. + // + UINT32 Script[32]; // // The max size of CDB is 32. // @@ -25,6 +30,18 @@ typedef struct { // Count (DBC), a 24-bit register, so the maximum is 0xFFFFFF (16MB-1). // UINT8 Data[SIZE_64KB]; + // + // For SCSI Message In phase + // + UINT8 MsgIn[2]; + // + // For SCSI Message Out phase + // + UINT8 MsgOut; + // + // For SCSI Status phase + // + UINT8 Status; } LSI_SCSI_DMA_BUFFER; typedef struct { @@ -34,6 +51,7 @@ typedef struct { EFI_PCI_IO_PROTOCOL *PciIo; UINT8 MaxTarget; UINT8 MaxLun; + UINT32 StallPerPollUsec; LSI_SCSI_DMA_BUFFER *Dma; EFI_PHYSICAL_ADDRESS DmaPhysical; VOID *DmaMapping; @@ -46,6 +64,9 @@ typedef struct { #define LSI_SCSI_FROM_PASS_THRU(PassThruPtr) \ CR (PassThruPtr, LSI_SCSI_DEV, PassThru, LSI_SCSI_DEV_SIGNATURE) +#define LSI_SCSI_DMA_ADDR(Dev, MemberName) \ + ((UINT32)(Dev->DmaPhysical + OFFSET_OF (LSI_SCSI_DMA_BUFFER, MemberName))) + // // Probe, start and stop functions of this driver, called by the DXE core for diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf index 6111449577a8..4c7abcec618f 100644 --- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf +++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf @@ -41,3 +41,6 @@ [Protocols] [FixedPcd] gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit ## CONSUMES gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit ## CONSUMES + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiStallPerPollUsec ## CONSUMES diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 30f9f046a296..0b38dc27d7e5 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -148,6 +148,9 @@ [PcdsFixedAtBuild] gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit|7|UINT8|0x3b gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit|0|UINT8|0x3c + ## Microseconds to stall between polling for LsiScsi request result + gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiStallPerPollUsec|5|UINT32|0x3d + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa -- 2.27.0
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor