Browse Source

OvmfPkg: Add an Super IO bus driver

REF:https://bugzilla.tianocore.org/show_bug.cgi?id=1495

There is a plan to remove the IntelFrameworkModulePkg:
https://bugzilla.tianocore.org/show_bug.cgi?id=1605

This patch will a new OVMF Super I/O bus driver which will create the
below child devices:

* COM 1 UART
* COM 2 UART
* PS/2 Keyboard

and installs the Super I/O Protocol on them.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Hao Wu <hao.a.wu@intel.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Anthony PERARD <anthony.perard@citrix.com>
Hao Wu 5 years ago
parent
commit
a5cc178aeb

+ 1 - 0
OvmfPkg/OvmfPkgIa32.dsc

@@ -752,6 +752,7 @@
   #
   # ISA Support
   #
+  OvmfPkg/SioBusDxe/SioBusDxe.inf
   PcAtChipsetPkg/IsaAcpiDxe/IsaAcpi.inf
   IntelFrameworkModulePkg/Bus/Isa/IsaBusDxe/IsaBusDxe.inf
   IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/IsaSerialDxe.inf

+ 1 - 0
OvmfPkg/OvmfPkgIa32X64.dsc

@@ -761,6 +761,7 @@
   #
   # ISA Support
   #
+  OvmfPkg/SioBusDxe/SioBusDxe.inf
   PcAtChipsetPkg/IsaAcpiDxe/IsaAcpi.inf
   IntelFrameworkModulePkg/Bus/Isa/IsaBusDxe/IsaBusDxe.inf
   IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/IsaSerialDxe.inf

+ 1 - 0
OvmfPkg/OvmfPkgX64.dsc

@@ -759,6 +759,7 @@
   #
   # ISA Support
   #
+  OvmfPkg/SioBusDxe/SioBusDxe.inf
   PcAtChipsetPkg/IsaAcpiDxe/IsaAcpi.inf
   IntelFrameworkModulePkg/Bus/Isa/IsaBusDxe/IsaBusDxe.inf
   IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/IsaSerialDxe.inf

+ 167 - 0
OvmfPkg/SioBusDxe/ComponentName.c

@@ -0,0 +1,167 @@
+/** @file
+  UEFI Component Name(2) protocol implementation for SioBusDxe driver.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "SioBusDxe.h"
+
+//
+// Driver name table
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSioBusDriverNameTable[] = {
+  { "eng;en", L"OVMF Sio Bus Driver" },
+  { NULL , NULL }
+};
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gSioBusComponentName = {
+  SioBusComponentNameGetDriverName,
+  SioBusComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gSioBusComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) SioBusComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) SioBusComponentNameGetControllerName,
+  "en"
+};
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language, then
+  EFI_UNSUPPORTED is returned.
+
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL
+                                or EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param[in]  Language          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+  @param[out] DriverName        A pointer to the Unicode string to return. This
+                                Unicode string is the name of the driver
+                                specified by This in the language specified by
+                                Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL    *This,
+  IN  CHAR8                          *Language,
+  OUT CHAR16                         **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mSioBusDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gSioBusComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified
+  by Language, then a pointer to the controller name is returned in
+  ControllerName, and EFI_SUCCESS is returned.  If the driver specified by This
+  is not currently managing the controller specified by ControllerHandle and
+  ChildHandle, then EFI_UNSUPPORTED is returned.  If the driver specified by
+  This does not support the language specified by Language, then
+  EFI_UNSUPPORTED is returned.
+
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL
+                                or EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param[in]  ControllerHandle  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param[in]  ChildHandle       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter
+                                that may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus
+                                drivers that wish to retrieve the name of the
+                                bus controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param[in]  Language          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+  @param[out] ControllerName    A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name
+                                in the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL    *This,
+  IN  EFI_HANDLE                     ControllerHandle,
+  IN  EFI_HANDLE                     ChildHandle        OPTIONAL,
+  IN  CHAR8                          *Language,
+  OUT CHAR16                         **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}

+ 622 - 0
OvmfPkg/SioBusDxe/SioBusDxe.c

@@ -0,0 +1,622 @@
+/** @file
+  The SioBusDxe driver is used to create child devices on the ISA bus and
+  installs the Super I/O protocols on them.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "SioBusDxe.h"
+
+//
+// SioBus Driver Binding Protocol
+//
+EFI_DRIVER_BINDING_PROTOCOL gSioBusDriverBinding = {
+  SioBusDriverBindingSupported,
+  SioBusDriverBindingStart,
+  SioBusDriverBindingStop,
+  0x10,
+  NULL,
+  NULL
+};
+
+
+/**
+  Tests to see if this driver supports a given controller. If a child device is
+  provided, it further tests to see if this driver supports creating a handle
+  for the specified child device.
+
+  This function checks to see if the driver specified by This supports the
+  device specified by ControllerHandle. Drivers will typically use the device
+  path attached to ControllerHandle and/or the services from the bus I/O
+  abstraction attached to ControllerHandle to determine if the driver supports
+  ControllerHandle. This function may be called many times during platform
+  initialization. In order to reduce boot times, the tests performed by this
+  function must be very small, and take as little time as possible to execute.
+  This function must not change the state of any hardware devices, and this
+  function must be aware that the device specified by ControllerHandle may
+  already be managed by the same driver or a different driver. This function
+  must match its calls to AllocatePages() with FreePages(), AllocatePool() with
+  FreePool(), and OpenProtocol() with CloseProtocol(). Since ControllerHandle
+  may have been previously started by the same driver, if a protocol is already
+  in the opened state, then it must not be closed with CloseProtocol(). This is
+  required to guarantee the state of ControllerHandle is not modified by this
+  function.
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                   instance.
+  @param[in]  ControllerHandle     The handle of the controller to test. This
+                                   handle must support a protocol interface
+                                   that supplies an I/O abstraction to the
+                                   driver.
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a
+                                   device path.  This parameter is ignored by
+                                   device drivers, and is optional for bus
+                                   drivers. For bus drivers, if this parameter
+                                   is not NULL, then the bus driver must
+                                   determine if the bus controller specified by
+                                   ControllerHandle and the child controller
+                                   specified by RemainingDevicePath are both
+                                   supported by this bus driver.
+
+  @retval EFI_SUCCESS              The device specified by ControllerHandle and
+                                   RemainingDevicePath is supported by the
+                                   driver specified by This.
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and
+                                   RemainingDevicePath is already being managed
+                                   by the driver specified by This.
+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and
+                                   RemainingDevicePath is already being managed
+                                   by a different driver or an application that
+                                   requires exclusive access.
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and
+                                   RemainingDevicePath is not supported by the
+                                   driver specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS             Status;
+  EFI_PCI_IO_PROTOCOL    *PciIo;
+  PCI_TYPE00             Pci;
+  UINTN                  SegmentNumber;
+  UINTN                  BusNumber;
+  UINTN                  DeviceNumber;
+  UINTN                  FunctionNumber;
+
+  //
+  // Get PciIo protocol instance
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID**)&PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  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)) {
+    Status = EFI_UNSUPPORTED;
+    if ((Pci.Hdr.Command & 0x03) == 0x03) {
+      if (Pci.Hdr.ClassCode[2] == PCI_CLASS_BRIDGE) {
+        //
+        // See if this is a standard PCI to ISA Bridge from the Base Code and
+        // Class Code
+        //
+        if (Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA) {
+          Status = EFI_SUCCESS;
+        }
+
+        //
+        // See if this is an Intel PCI to ISA bridge in Positive Decode Mode
+        //
+        if (Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA_PDECODE &&
+            Pci.Hdr.VendorId     == 0x8086) {
+          //
+          // See if this is on Function #0 to avoid false positives on
+          // PCI_CLASS_BRIDGE_OTHER that has the same value as
+          // PCI_CLASS_BRIDGE_ISA_PDECODE
+          //
+          Status = PciIo->GetLocation (
+                            PciIo,
+                            &SegmentNumber,
+                            &BusNumber,
+                            &DeviceNumber,
+                            &FunctionNumber
+                            );
+          if (!EFI_ERROR (Status) && FunctionNumber == 0) {
+            Status = EFI_SUCCESS;
+          } else {
+            Status = EFI_UNSUPPORTED;
+          }
+        }
+      }
+    }
+  }
+
+  gBS->CloseProtocol (
+         Controller,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         Controller
+         );
+
+  return Status;
+}
+
+/**
+  Starts a device controller or a bus controller.
+
+  The Start() function is designed to be invoked from the EFI boot service
+  ConnectController(). As a result, much of the error checking on the
+  parameters to Start() has been moved into this common boot service. It is
+  legal to call Start() from other locations, but the following calling
+  restrictions must be followed or the system behavior will not be
+  deterministic.
+  1. ControllerHandle must be a valid EFI_HANDLE.
+  2. If RemainingDevicePath is not NULL, then it must be a pointer to a
+     naturally aligned EFI_DEVICE_PATH_PROTOCOL.
+  3. Prior to calling Start(), the Supported() function for the driver
+     specified by This must have been called with the same calling parameters,
+     and Supported() must have returned EFI_SUCCESS.
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                   instance.
+  @param[in]  ControllerHandle     The handle of the controller to start. This
+                                   handle must support a protocol interface
+                                   that supplies an I/O abstraction to the
+                                   driver.
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a
+                                   device path.  This parameter is ignored by
+                                   device drivers, and is optional for bus
+                                   drivers. For a bus driver, if this parameter
+                                   is NULL, then handles for all the children
+                                   of Controller are created by this driver. If
+                                   this parameter is not NULL and the first
+                                   Device Path Node is not the End of Device
+                                   Path Node, then only the handle for the
+                                   child device specified by the first Device
+                                   Path Node of RemainingDevicePath is created
+                                   by this driver. If the first Device Path
+                                   Node of RemainingDevicePath is the End of
+                                   Device Path Node, no child handle is created
+                                   by this driver.
+
+  @retval EFI_SUCCESS              The device was started.
+  @retval EFI_DEVICE_ERROR         The device could not be started due to a
+                                   device error.
+  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a
+                                   lack of resources.
+  @retval Others                   The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS                     Status;
+  EFI_PCI_IO_PROTOCOL            *PciIo;
+  EFI_DEVICE_PATH_PROTOCOL       *ParentDevicePath;
+  UINT64                         Supports;
+  UINT64                         OriginalAttributes;
+  UINT64                         Attributes;
+  BOOLEAN                        Enabled;
+  SIO_BUS_DRIVER_PRIVATE_DATA    *Private;
+  UINT32                         ChildDeviceNumber;
+
+  Enabled            = FALSE;
+  Supports           = 0;
+  OriginalAttributes = 0;
+  Private            = NULL;
+
+  //
+  // Open the PCI I/O Protocol Interface
+  //
+  PciIo = NULL;
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID**) &PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Open Device Path Protocol
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &ParentDevicePath,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiPciIoProtocolGuid,
+           This->DriverBindingHandle,
+           Controller
+           );
+    return Status;
+  }
+
+  //
+  // Get supported PCI attributes
+  //
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationSupported,
+                    0,
+                    &Supports
+                    );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  Supports &= (UINT64) (EFI_PCI_IO_ATTRIBUTE_ISA_IO |
+                        EFI_PCI_IO_ATTRIBUTE_ISA_IO_16);
+  if (Supports == 0 ||
+      Supports == (EFI_PCI_IO_ATTRIBUTE_ISA_IO |
+                   EFI_PCI_IO_ATTRIBUTE_ISA_IO_16)) {
+    Status = EFI_UNSUPPORTED;
+    goto Done;
+  }
+
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationGet,
+                    0,
+                    &OriginalAttributes
+                    );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  Attributes = EFI_PCI_DEVICE_ENABLE |
+               Supports |
+               EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO;
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationEnable,
+                    Attributes,
+                    NULL
+                    );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  Enabled = TRUE;
+
+  //
+  // Store the OriginalAttributes for the restore in BindingStop()
+  //
+  Private = AllocateZeroPool (sizeof (SIO_BUS_DRIVER_PRIVATE_DATA));
+  if (Private == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Done;
+  }
+  Private->PciIo              = PciIo;
+  Private->OriginalAttributes = OriginalAttributes;
+
+  Status = gBS->InstallProtocolInterface (
+                  &Controller,
+                  &gEfiCallerIdGuid,
+                  EFI_NATIVE_INTERFACE,
+                  Private
+                  );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  //
+  // Report status code for the start of general controller initialization
+  //
+  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+    EFI_PROGRESS_CODE,
+    (EFI_IO_BUS_LPC | EFI_IOB_PC_INIT),
+    ParentDevicePath
+    );
+
+  //
+  // Report status code for the start of enabling devices on the bus
+  //
+  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+    EFI_PROGRESS_CODE,
+    (EFI_IO_BUS_LPC | EFI_IOB_PC_ENABLE),
+    ParentDevicePath
+    );
+
+  //
+  // Create all the children upon the first entrance
+  //
+  ChildDeviceNumber = SioCreateAllChildDevices (
+                        This,
+                        Controller,
+                        PciIo,
+                        ParentDevicePath
+                        );
+  if (ChildDeviceNumber == 0) {
+    Status = EFI_DEVICE_ERROR;
+  }
+
+Done:
+  if (EFI_ERROR (Status)) {
+    if (PciIo != NULL && Enabled) {
+      PciIo->Attributes (
+               PciIo,
+               EfiPciIoAttributeOperationSet,
+               OriginalAttributes,
+               NULL
+               );
+    }
+
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiDevicePathProtocolGuid,
+           This->DriverBindingHandle,
+           Controller
+           );
+
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiPciIoProtocolGuid,
+           This->DriverBindingHandle,
+           Controller
+           );
+
+    if (Private != NULL) {
+      gBS->UninstallMultipleProtocolInterfaces (
+             Controller,
+             &gEfiCallerIdGuid,
+             Private,
+             NULL
+             );
+      FreePool (Private);
+    }
+
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Stops a device controller or a bus controller.
+
+  The Stop() function is designed to be invoked from the EFI boot service
+  DisconnectController(). As a result, much of the error checking on the
+  parameters to Stop() has been moved into this common boot service. It is
+  legal to call Stop() from other locations, but the following calling
+  restrictions must be followed or the system behavior will not be
+  deterministic.
+  1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
+     call to this same driver's Start() function.
+  2. The first NumberOfChildren handles of ChildHandleBuffer must all be a
+     valid EFI_HANDLE. In addition, all of these handles must have been created
+     in this driver's Start() function, and the Start() function must have
+     called OpenProtocol() on ControllerHandle with an Attribute of
+     EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+  @param[in]  This               A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                 instance.
+  @param[in]  ControllerHandle   A handle to the device being stopped. The
+                                 handle must support a bus specific I/O
+                                 protocol for the driver to use to stop the
+                                 device.
+  @param[in]  NumberOfChildren   The number of child device handles in
+                                 ChildHandleBuffer.
+  @param[in]  ChildHandleBuffer  An array of child handles to be freed. May be
+                                 NULL if NumberOfChildren is 0.
+
+  @retval EFI_SUCCESS            The device was stopped.
+  @retval EFI_DEVICE_ERROR       The device could not be stopped due to a
+                                 device error.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN  EFI_HANDLE                     Controller,
+  IN  UINTN                          NumberOfChildren,
+  IN  EFI_HANDLE                     *ChildHandleBuffer
+  )
+{
+  EFI_STATUS                     Status;
+  SIO_BUS_DRIVER_PRIVATE_DATA    *Private;
+  UINTN                          Index;
+  BOOLEAN                        AllChildrenStopped;
+  EFI_SIO_PROTOCOL               *Sio;
+  SIO_DEV                        *SioDevice;
+  EFI_PCI_IO_PROTOCOL            *PciIo;
+
+  if (NumberOfChildren == 0) {
+    //
+    // Restore PCI attributes
+    //
+    Status = gBS->OpenProtocol (
+                    Controller,
+                    &gEfiCallerIdGuid,
+                    (VOID **) &Private,
+                    This->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Status = Private->PciIo->Attributes (
+                               Private->PciIo,
+                               EfiPciIoAttributeOperationSet,
+                               Private->OriginalAttributes,
+                               NULL
+                               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    gBS->UninstallProtocolInterface (
+          Controller,
+          &gEfiCallerIdGuid,
+          Private
+          );
+    FreePool (Private);
+
+    //
+    // Close the bus driver
+    //
+    Status = gBS->CloseProtocol (
+                    Controller,
+                    &gEfiDevicePathProtocolGuid,
+                    This->DriverBindingHandle,
+                    Controller
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Status = gBS->CloseProtocol (
+                    Controller,
+                    &gEfiPciIoProtocolGuid,
+                    This->DriverBindingHandle,
+                    Controller
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Stop all the children
+  //
+  AllChildrenStopped = TRUE;
+
+  for (Index = 0; Index < NumberOfChildren; Index++) {
+    Status = gBS->OpenProtocol (
+                    ChildHandleBuffer[Index],
+                    &gEfiSioProtocolGuid,
+                    (VOID **) &Sio,
+                    This->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (!EFI_ERROR (Status)) {
+      SioDevice = SIO_DEV_FROM_SIO (Sio);
+
+      //
+      // Close the child handle
+      //
+      Status = gBS->CloseProtocol (
+                      Controller,
+                      &gEfiPciIoProtocolGuid,
+                      This->DriverBindingHandle,
+                      ChildHandleBuffer[Index]
+                      );
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+                      ChildHandleBuffer[Index],
+                      &gEfiDevicePathProtocolGuid,
+                      SioDevice->DevicePath,
+                      &gEfiSioProtocolGuid,
+                      &SioDevice->Sio,
+                      NULL
+                      );
+
+      if (!EFI_ERROR (Status)) {
+        FreePool (SioDevice->DevicePath);
+        FreePool (SioDevice);
+      } else {
+        //
+        // Re-open PCI IO Protocol on behalf of the child device
+        // because of failure of destroying the child device handle
+        //
+        gBS->OpenProtocol (
+               Controller,
+               &gEfiPciIoProtocolGuid,
+               (VOID **) &PciIo,
+               This->DriverBindingHandle,
+               ChildHandleBuffer[Index],
+               EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+               );
+      }
+    }
+
+    if (EFI_ERROR (Status)) {
+      AllChildrenStopped = FALSE;
+    }
+  }
+
+  if (!AllChildrenStopped) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  The entry point for the SioBusDxe driver.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS       The entry point is executed successfully.
+  @retval other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusDxeDriverEntryPoint (
+  IN EFI_HANDLE          ImageHandle,
+  IN EFI_SYSTEM_TABLE    *SystemTable
+  )
+{
+  //
+  // Install driver model protocol(s).
+  //
+  return EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &gSioBusDriverBinding,
+           ImageHandle,
+           &gSioBusComponentName,
+           &gSioBusComponentName2
+           );
+}

+ 332 - 0
OvmfPkg/SioBusDxe/SioBusDxe.h

@@ -0,0 +1,332 @@
+/** @file
+  The SioBusDxe driver is used to create child devices on the ISA bus and
+  installs the Super I/O protocols on them.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __SIO_BUS_DXE_H__
+#define __SIO_BUS_DXE_H__
+
+#include <Uefi.h>
+
+#include <IndustryStandard/Pci.h>
+
+#include <Protocol/PciIo.h>
+#include <Protocol/SuperIo.h>
+
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DevicePathLib.h>
+
+#include "SioService.h"
+
+//
+// SIO Bus driver private data structure
+//
+typedef struct {
+  EFI_PCI_IO_PROTOCOL    *PciIo;
+  UINT64                 OriginalAttributes;
+} SIO_BUS_DRIVER_PRIVATE_DATA;
+
+
+//
+// Global Variables
+//
+extern EFI_COMPONENT_NAME_PROTOCOL  gSioBusComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gSioBusComponentName2;
+
+
+//
+// EFI Component Name Functions
+//
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language, then
+  EFI_UNSUPPORTED is returned.
+
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL
+                                or EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param[in]  Language          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+  @param[out] DriverName        A pointer to the Unicode string to return. This
+                                Unicode string is the name of the driver
+                                specified by This in the language specified by
+                                Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL    *This,
+  IN  CHAR8                          *Language,
+  OUT CHAR16                         **DriverName
+  );
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified
+  by Language, then a pointer to the controller name is returned in
+  ControllerName, and EFI_SUCCESS is returned.  If the driver specified by This
+  is not currently managing the controller specified by ControllerHandle and
+  ChildHandle, then EFI_UNSUPPORTED is returned.  If the driver specified by
+  This does not support the language specified by Language, then
+  EFI_UNSUPPORTED is returned.
+
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL
+                                or EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param[in]  ControllerHandle  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param[in]  ChildHandle       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter
+                                that may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus
+                                drivers that wish to retrieve the name of the
+                                bus controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param[in]  Language          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+  @param[out] ControllerName    A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name
+                                in the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL    *This,
+  IN  EFI_HANDLE                     ControllerHandle,
+  IN  EFI_HANDLE                     ChildHandle        OPTIONAL,
+  IN  CHAR8                          *Language,
+  OUT CHAR16                         **ControllerName
+  );
+
+
+//
+// Driver Binding Protocol interfaces
+//
+
+/**
+  Tests to see if this driver supports a given controller. If a child device is
+  provided, it further tests to see if this driver supports creating a handle
+  for the specified child device.
+
+  This function checks to see if the driver specified by This supports the
+  device specified by ControllerHandle. Drivers will typically use the device
+  path attached to ControllerHandle and/or the services from the bus I/O
+  abstraction attached to ControllerHandle to determine if the driver supports
+  ControllerHandle. This function may be called many times during platform
+  initialization. In order to reduce boot times, the tests performed by this
+  function must be very small, and take as little time as possible to execute.
+  This function must not change the state of any hardware devices, and this
+  function must be aware that the device specified by ControllerHandle may
+  already be managed by the same driver or a different driver. This function
+  must match its calls to AllocatePages() with FreePages(), AllocatePool() with
+  FreePool(), and OpenProtocol() with CloseProtocol(). Since ControllerHandle
+  may have been previously started by the same driver, if a protocol is already
+  in the opened state, then it must not be closed with CloseProtocol(). This is
+  required to guarantee the state of ControllerHandle is not modified by this
+  function.
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                   instance.
+  @param[in]  ControllerHandle     The handle of the controller to test. This
+                                   handle must support a protocol interface
+                                   that supplies an I/O abstraction to the
+                                   driver.
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a
+                                   device path.  This parameter is ignored by
+                                   device drivers, and is optional for bus
+                                   drivers. For bus drivers, if this parameter
+                                   is not NULL, then the bus driver must
+                                   determine if the bus controller specified by
+                                   ControllerHandle and the child controller
+                                   specified by RemainingDevicePath are both
+                                   supported by this bus driver.
+
+  @retval EFI_SUCCESS              The device specified by ControllerHandle and
+                                   RemainingDevicePath is supported by the
+                                   driver specified by This.
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and
+                                   RemainingDevicePath is already being managed
+                                   by the driver specified by This.
+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and
+                                   RemainingDevicePath is already being managed
+                                   by a different driver or an application that
+                                   requires exclusive access.
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and
+                                   RemainingDevicePath is not supported by the
+                                   driver specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  );
+
+/**
+  Starts a device controller or a bus controller.
+
+  The Start() function is designed to be invoked from the EFI boot service
+  ConnectController(). As a result, much of the error checking on the
+  parameters to Start() has been moved into this common boot service. It is
+  legal to call Start() from other locations, but the following calling
+  restrictions must be followed or the system behavior will not be
+  deterministic.
+  1. ControllerHandle must be a valid EFI_HANDLE.
+  2. If RemainingDevicePath is not NULL, then it must be a pointer to a
+     naturally aligned EFI_DEVICE_PATH_PROTOCOL.
+  3. Prior to calling Start(), the Supported() function for the driver
+     specified by This must have been called with the same calling parameters,
+     and Supported() must have returned EFI_SUCCESS.
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                   instance.
+  @param[in]  ControllerHandle     The handle of the controller to start. This
+                                   handle must support a protocol interface
+                                   that supplies an I/O abstraction to the
+                                   driver.
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a
+                                   device path.  This parameter is ignored by
+                                   device drivers, and is optional for bus
+                                   drivers. For a bus driver, if this parameter
+                                   is NULL, then handles for all the children
+                                   of Controller are created by this driver. If
+                                   this parameter is not NULL and the first
+                                   Device Path Node is not the End of Device
+                                   Path Node, then only the handle for the
+                                   child device specified by the first Device
+                                   Path Node of RemainingDevicePath is created
+                                   by this driver. If the first Device Path
+                                   Node of RemainingDevicePath is the End of
+                                   Device Path Node, no child handle is created
+                                   by this driver.
+
+  @retval EFI_SUCCESS              The device was started.
+  @retval EFI_DEVICE_ERROR         The device could not be started due to a
+                                   device error.
+  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a
+                                   lack of resources.
+  @retval Others                   The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  );
+
+/**
+  Stops a device controller or a bus controller.
+
+  The Stop() function is designed to be invoked from the EFI boot service
+  DisconnectController(). As a result, much of the error checking on the
+  parameters to Stop() has been moved into this common boot service. It is
+  legal to call Stop() from other locations, but the following calling
+  restrictions must be followed or the system behavior will not be
+  deterministic.
+  1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
+     call to this same driver's Start() function.
+  2. The first NumberOfChildren handles of ChildHandleBuffer must all be a
+     valid EFI_HANDLE. In addition, all of these handles must have been created
+     in this driver's Start() function, and the Start() function must have
+     called OpenProtocol() on ControllerHandle with an Attribute of
+     EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+  @param[in]  This               A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+                                 instance.
+  @param[in]  ControllerHandle   A handle to the device being stopped. The
+                                 handle must support a bus specific I/O
+                                 protocol for the driver to use to stop the
+                                 device.
+  @param[in]  NumberOfChildren   The number of child device handles in
+                                 ChildHandleBuffer.
+  @param[in]  ChildHandleBuffer  An array of child handles to be freed. May be
+                                 NULL if NumberOfChildren is 0.
+
+  @retval EFI_SUCCESS            The device was stopped.
+  @retval EFI_DEVICE_ERROR       The device could not be stopped due to a
+                                 device error.
+
+**/
+EFI_STATUS
+EFIAPI
+SioBusDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN  EFI_HANDLE                     Controller,
+  IN  UINTN                          NumberOfChildren,
+  IN  EFI_HANDLE                     *ChildHandleBuffer
+  );
+
+#endif  // __SIO_BUS_DXE_H__

+ 54 - 0
OvmfPkg/SioBusDxe/SioBusDxe.inf

@@ -0,0 +1,54 @@
+## @file
+#  The SioBusDxe driver is used to create child devices on the ISA bus and
+#  installs the Super I/O protocols on them.
+#
+#  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+#
+#  This program and the accompanying materials
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which accompanies this distribution.  The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php
+#
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SioBusDxe
+  MODULE_UNI_FILE                = SioBusDxe.uni
+  FILE_GUID                      = 864E1CA8-85EB-4D63-9DCC-6E0FC90FFD55
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = SioBusDxeDriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources]
+  ComponentName.c
+  SioService.c
+  SioService.h
+  SioBusDxe.c
+  SioBusDxe.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiLib
+  UefiBootServicesTableLib
+  DebugLib
+  ReportStatusCodeLib
+  MemoryAllocationLib
+  BaseMemoryLib
+  DevicePathLib
+
+[Protocols]
+  gEfiPciIoProtocolGuid    ## TO_START
+  gEfiSioProtocolGuid      ## BY_START

+ 21 - 0
OvmfPkg/SioBusDxe/SioBusDxe.uni

@@ -0,0 +1,21 @@
+// /** @file
+// The SioBusDxe driver is used to create child devices on the ISA bus and
+// installs the Super I/O protocols on them.
+//
+// Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution.  The
+// full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT       #language en-US "Manage ISA devices on the ISA bus"
+
+#string STR_MODULE_DESCRIPTION    #language en-US "The SioBusDxe driver is used to create child devices on the ISA bus and installs the Super I/O protocols on them."

+ 405 - 0
OvmfPkg/SioBusDxe/SioService.c

@@ -0,0 +1,405 @@
+/** @file
+  The SioBusDxe driver is used to create child devices on the ISA bus and
+  installs the Super I/O protocols on them.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "SioBusDxe.h"
+
+//
+// Super I/O Protocol interfaces
+//
+EFI_SIO_PROTOCOL mSioInterface = {
+  SioRegisterAccess,
+  SioGetResources,
+  SioSetResources,
+  SioPossibleResources,
+  SioModify
+};
+
+//
+// COM 1 UART Controller
+//
+GLOBAL_REMOVE_IF_UNREFERENCED
+SIO_RESOURCES_IO mCom1Resources = {
+  { { ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR }, 0x3F8, 8 },
+  { ACPI_END_TAG_DESCRIPTOR,                    0        }
+};
+
+//
+// COM 2 UART Controller
+//
+GLOBAL_REMOVE_IF_UNREFERENCED
+SIO_RESOURCES_IO mCom2Resources = {
+  { { ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR }, 0x2F8, 8 },
+  { ACPI_END_TAG_DESCRIPTOR,                    0        }
+};
+
+//
+// PS/2 Keyboard Controller
+//
+GLOBAL_REMOVE_IF_UNREFERENCED
+SIO_RESOURCES_IO mPs2KeyboardDeviceResources = {
+  { { ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR }, 0x60, 5 },
+  { ACPI_END_TAG_DESCRIPTOR,                    0       }
+};
+
+//
+// Table of SIO Controllers
+//
+GLOBAL_REMOVE_IF_UNREFERENCED
+SIO_DEVICE_INFO mDevicesInfo[] = {
+  {
+    EISA_PNP_ID (0x501),
+    0,
+    { (ACPI_SMALL_RESOURCE_HEADER *) &mCom1Resources }
+  },  // COM 1 UART Controller
+  {
+    EISA_PNP_ID (0x501),
+    1,
+    { (ACPI_SMALL_RESOURCE_HEADER *) &mCom2Resources }
+  },  // COM 2 UART Controller
+  {
+    EISA_PNP_ID(0x303),
+    0,
+    { (ACPI_SMALL_RESOURCE_HEADER *) &mPs2KeyboardDeviceResources }
+  }   // PS/2 Keyboard Controller
+};
+
+//
+// ACPI Device Path Node template
+//
+GLOBAL_REMOVE_IF_UNREFERENCED
+ACPI_HID_DEVICE_PATH mAcpiDeviceNodeTemplate = {
+  {        // Header
+    ACPI_DEVICE_PATH,
+    ACPI_DP,
+    {
+      (UINT8) (sizeof (ACPI_HID_DEVICE_PATH)),
+      (UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8)
+    }
+  },
+  0x0,     // HID
+  0x0      // UID
+};
+
+
+/**
+  Provides a low level access to the registers for the Super I/O.
+
+  @param[in]     This           Indicates a pointer to the calling context.
+  @param[in]     Write          Specifies the type of the register operation.
+                                If this parameter is TRUE, Value is interpreted
+                                as an input parameter and the operation is a
+                                register write. If this parameter is FALSE,
+                                Value is interpreted as an output parameter and
+                                the operation is a register read.
+  @param[in]     ExitCfgMode    Exit Configuration Mode Indicator. If this
+                                parameter is set to TRUE, the Super I/O driver
+                                will turn off configuration mode of the Super
+                                I/O prior to returning from this function. If
+                                this parameter is set to FALSE, the Super I/O
+                                driver will leave Super I/O in the
+                                configuration mode. The Super I/O driver must
+                                track the current state of the Super I/O and
+                                enable the configuration mode of Super I/O if
+                                necessary prior to register access.
+  @param[in]     Register       Register number.
+  @param[in,out] Value          If Write is TRUE, Value is a pointer to the
+                                buffer containing the byte of data to be
+                                written to the Super I/O register. If Write is
+                                FALSE, Value is a pointer to the destination
+                                buffer for the byte of data to be read from the
+                                Super I/O register.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER The Value is NULL.
+  @retval EFI_INVALID_PARAMETER Invalid Register number.
+
+**/
+EFI_STATUS
+EFIAPI
+SioRegisterAccess (
+  IN CONST EFI_SIO_PROTOCOL    *This,
+  IN       BOOLEAN             Write,
+  IN       BOOLEAN             ExitCfgMode,
+  IN       UINT8               Register,
+  IN OUT   UINT8               *Value
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  Provides an interface to get a list of the current resources consumed by the
+  device in the ACPI Resource Descriptor format.
+
+  GetResources() returns a list of resources currently consumed by the device.
+  The ResourceList is a pointer to the buffer containing resource descriptors
+  for the device. The descriptors are in the format of Small or Large ACPI
+  resource descriptor as defined by ACPI specification (2.0 & 3.0). The buffer
+  of resource descriptors is terminated with the 'End tag' resource descriptor.
+
+  @param[in]  This              Indicates a pointer to the calling context.
+  @param[out] ResourceList      A pointer to an ACPI resource descriptor list
+                                that defines the current resources used by the
+                                device.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER ResourceList is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+SioGetResources (
+  IN CONST EFI_SIO_PROTOCOL            *This,
+  OUT      ACPI_RESOURCE_HEADER_PTR    *ResourceList
+  )
+{
+  SIO_DEV    *SioDevice;
+
+  if (ResourceList == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SioDevice = SIO_DEV_FROM_SIO (This);
+  if (SioDevice->DeviceIndex < ARRAY_SIZE (mDevicesInfo)) {
+    *ResourceList = mDevicesInfo[SioDevice->DeviceIndex].Resources;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Sets the resources for the device.
+
+  @param[in] This               Indicates a pointer to the calling context.
+  @param[in] ResourceList       Pointer to the ACPI resource descriptor list.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER ResourceList is invalid.
+  @retval EFI_ACCESS_DENIED     Some of the resources in ResourceList are in
+                                use.
+
+**/
+EFI_STATUS
+EFIAPI
+SioSetResources (
+  IN CONST EFI_SIO_PROTOCOL            *This,
+  IN       ACPI_RESOURCE_HEADER_PTR    ResourceList
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  Provides a collection of resource descriptor lists. Each resource descriptor
+  list in the collection defines a combination of resources that can
+  potentially be used by the device.
+
+  @param[in]  This                  Indicates a pointer to the calling context.
+  @param[out] ResourceCollection    Collection of the resource descriptor
+                                    lists.
+
+  @retval EFI_SUCCESS               The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER     ResourceCollection is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+SioPossibleResources (
+  IN CONST EFI_SIO_PROTOCOL            *This,
+  OUT      ACPI_RESOURCE_HEADER_PTR    *ResourceCollection
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  Provides an interface for a table based programming of the Super I/O
+  registers.
+
+  The Modify() function provides an interface for table based programming of
+  the Super I/O registers. This function can be used to perform programming of
+  multiple Super I/O registers with a single function call. For each table
+  entry, the Register is read, its content is bitwise ANDed with AndMask, and
+  then ORed with OrMask before being written back to the Register. The Super
+  I/O driver must track the current state of the Super I/O and enable the
+  configuration mode of Super I/O if necessary prior to table processing. Once
+  the table is processed, the Super I/O device has to be returned to the
+  original state.
+
+  @param[in] This               Indicates a pointer to the calling context.
+  @param[in] Command            A pointer to an array of NumberOfCommands
+                                EFI_SIO_REGISTER_MODIFY structures. Each
+                                structure specifies a single Super I/O register
+                                modify operation.
+  @param[in] NumberOfCommands   Number of elements in the Command array.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER Command is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+SioModify (
+  IN CONST EFI_SIO_PROTOCOL           *This,
+  IN CONST EFI_SIO_REGISTER_MODIFY    *Command,
+  IN       UINTN                      NumberOfCommands
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  Create the child device with a given device index.
+
+  @param[in] This              The EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in] Controller        The handle of ISA bus controller.
+  @param[in] PciIo             The pointer to the PCI protocol.
+  @param[in] ParentDevicePath  Device path of the ISA bus controller.
+  @param[in] DeviceIndex       Index of the device supported by this driver.
+
+  @retval EFI_SUCCESS          The child device has been created successfully.
+  @retval Others               Error occured during the child device creation.
+
+**/
+EFI_STATUS
+SioCreateChildDevice (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_PCI_IO_PROTOCOL          *PciIo,
+  IN EFI_DEVICE_PATH_PROTOCOL     *ParentDevicePath,
+  IN UINT32                       DeviceIndex
+  )
+{
+  EFI_STATUS    Status;
+  SIO_DEV       *SioDevice;
+
+  //
+  // Initialize the SIO_DEV structure
+  //
+  SioDevice = AllocateZeroPool (sizeof (SIO_DEV));
+  if (SioDevice == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  SioDevice->Signature  = SIO_DEV_SIGNATURE;
+  SioDevice->Handle     = NULL;
+  SioDevice->PciIo      = PciIo;
+
+  //
+  // Construct the child device path
+  //
+  mAcpiDeviceNodeTemplate.HID = mDevicesInfo[DeviceIndex].Hid;
+  mAcpiDeviceNodeTemplate.UID = mDevicesInfo[DeviceIndex].Uid;
+  SioDevice->DevicePath = AppendDevicePathNode (
+                            ParentDevicePath,
+                            (EFI_DEVICE_PATH_PROTOCOL *) &mAcpiDeviceNodeTemplate
+                            );
+  if (SioDevice->DevicePath == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Done;
+  }
+
+  CopyMem (&SioDevice->Sio, &mSioInterface, sizeof (EFI_SIO_PROTOCOL));
+  SioDevice->DeviceIndex = DeviceIndex;
+
+  //
+  // Create a child handle and install Device Path and Super I/O protocols
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &SioDevice->Handle,
+                  &gEfiDevicePathProtocolGuid,
+                  SioDevice->DevicePath,
+                  &gEfiSioProtocolGuid,
+                  &SioDevice->Sio,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo,
+                  This->DriverBindingHandle,
+                  SioDevice->Handle,
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                  );
+  if (EFI_ERROR (Status)) {
+    gBS->UninstallMultipleProtocolInterfaces (
+           SioDevice->Handle,
+           &gEfiDevicePathProtocolGuid,
+           SioDevice->DevicePath,
+           &gEfiSioProtocolGuid,
+           &SioDevice->Sio,
+           NULL
+           );
+  }
+
+Done:
+  if (EFI_ERROR (Status)) {
+    if (SioDevice->DevicePath != NULL) {
+      FreePool (SioDevice->DevicePath);
+    }
+
+    FreePool (SioDevice);
+  }
+
+  return Status;
+}
+
+/**
+  Create all the ISA child devices on the ISA bus controller (PCI to ISA
+  bridge).
+
+  @param[in] This              The EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in] Controller        The handle of ISA bus controller.
+  @param[in] PciIo             The pointer to the PCI protocol.
+  @param[in] ParentDevicePath  Device path of the ISA bus controller.
+
+  @retval The number of child device that is successfully created.
+
+**/
+UINT32
+SioCreateAllChildDevices (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_PCI_IO_PROTOCOL          *PciIo,
+  IN EFI_DEVICE_PATH_PROTOCOL     *ParentDevicePath
+  )
+{
+  UINT32        Index;
+  UINT32        ChildDeviceNumber;
+  EFI_STATUS    Status;
+
+  ChildDeviceNumber = 0;
+
+  for (Index = 0; Index < ARRAY_SIZE (mDevicesInfo); Index++) {
+    Status = SioCreateChildDevice (
+               This,
+               Controller,
+               PciIo,
+               ParentDevicePath,
+               Index
+               );
+    if (!EFI_ERROR (Status)) {
+      ChildDeviceNumber++;
+    }
+  }
+
+  return ChildDeviceNumber;
+}

+ 221 - 0
OvmfPkg/SioBusDxe/SioService.h

@@ -0,0 +1,221 @@
+/** @file
+  The SioBusDxe driver is used to create child devices on the ISA bus and
+  installs the Super I/O protocols on them.
+
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions
+  of the BSD License which accompanies this distribution.  The
+  full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __SIO_SERVICE_H__
+#define __SIO_SERVICE_H__
+
+#pragma pack(1)
+
+typedef struct {
+  EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR  Io;
+  EFI_ACPI_END_TAG_DESCRIPTOR                 End;
+} SIO_RESOURCES_IO;
+
+#pragma pack()
+
+typedef struct {
+  UINT32                      Hid;
+  UINT32                      Uid;
+  ACPI_RESOURCE_HEADER_PTR    Resources;
+} SIO_DEVICE_INFO;
+
+//
+// SIO device private data structure
+//
+typedef struct {
+  UINT32                      Signature;
+  EFI_HANDLE                  Handle;
+  EFI_PCI_IO_PROTOCOL         *PciIo;
+  EFI_DEVICE_PATH_PROTOCOL    *DevicePath;
+
+  EFI_SIO_PROTOCOL            Sio;
+  UINT32                      DeviceIndex;
+} SIO_DEV;
+#define SIO_DEV_SIGNATURE      SIGNATURE_32 ('S', 'I', 'O', 'D')
+#define SIO_DEV_FROM_SIO(a)    CR (a, SIO_DEV, Sio, SIO_DEV_SIGNATURE)
+
+
+//
+// Super I/O Protocol interfaces
+//
+
+/**
+  Provides a low level access to the registers for the Super I/O.
+
+  @param[in]     This           Indicates a pointer to the calling context.
+  @param[in]     Write          Specifies the type of the register operation.
+                                If this parameter is TRUE, Value is interpreted
+                                as an input parameter and the operation is a
+                                register write. If this parameter is FALSE,
+                                Value is interpreted as an output parameter and
+                                the operation is a register read.
+  @param[in]     ExitCfgMode    Exit Configuration Mode Indicator. If this
+                                parameter is set to TRUE, the Super I/O driver
+                                will turn off configuration mode of the Super
+                                I/O prior to returning from this function. If
+                                this parameter is set to FALSE, the Super I/O
+                                driver will leave Super I/O in the
+                                configuration mode. The Super I/O driver must
+                                track the current state of the Super I/O and
+                                enable the configuration mode of Super I/O if
+                                necessary prior to register access.
+  @param[in]     Register       Register number.
+  @param[in,out] Value          If Write is TRUE, Value is a pointer to the
+                                buffer containing the byte of data to be
+                                written to the Super I/O register. If Write is
+                                FALSE, Value is a pointer to the destination
+                                buffer for the byte of data to be read from the
+                                Super I/O register.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER The Value is NULL.
+  @retval EFI_INVALID_PARAMETER Invalid Register number.
+
+**/
+EFI_STATUS
+EFIAPI
+SioRegisterAccess (
+  IN CONST EFI_SIO_PROTOCOL    *This,
+  IN       BOOLEAN             Write,
+  IN       BOOLEAN             ExitCfgMode,
+  IN       UINT8               Register,
+  IN OUT   UINT8               *Value
+  );
+
+/**
+  Provides an interface to get a list of the current resources consumed by the
+  device in the ACPI Resource Descriptor format.
+
+  GetResources() returns a list of resources currently consumed by the device.
+  The ResourceList is a pointer to the buffer containing resource descriptors
+  for the device. The descriptors are in the format of Small or Large ACPI
+  resource descriptor as defined by ACPI specification (2.0 & 3.0). The buffer
+  of resource descriptors is terminated with the 'End tag' resource descriptor.
+
+  @param[in]  This              Indicates a pointer to the calling context.
+  @param[out] ResourceList      A pointer to an ACPI resource descriptor list
+                                that defines the current resources used by the
+                                device.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER ResourceList is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+SioGetResources (
+  IN CONST EFI_SIO_PROTOCOL            *This,
+  OUT      ACPI_RESOURCE_HEADER_PTR    *ResourceList
+  );
+
+/**
+  Sets the resources for the device.
+
+  @param[in] This               Indicates a pointer to the calling context.
+  @param[in] ResourceList       Pointer to the ACPI resource descriptor list.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER ResourceList is invalid.
+  @retval EFI_ACCESS_DENIED     Some of the resources in ResourceList are in
+                                use.
+
+**/
+EFI_STATUS
+EFIAPI
+SioSetResources (
+  IN CONST EFI_SIO_PROTOCOL            *This,
+  IN       ACPI_RESOURCE_HEADER_PTR    ResourceList
+  );
+
+/**
+  Provides a collection of resource descriptor lists. Each resource descriptor
+  list in the collection defines a combination of resources that can
+  potentially be used by the device.
+
+  @param[in]  This                  Indicates a pointer to the calling context.
+  @param[out] ResourceCollection    Collection of the resource descriptor
+                                    lists.
+
+  @retval EFI_SUCCESS               The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER     ResourceCollection is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+SioPossibleResources (
+  IN CONST EFI_SIO_PROTOCOL            *This,
+  OUT      ACPI_RESOURCE_HEADER_PTR    *ResourceCollection
+  );
+
+/**
+  Provides an interface for a table based programming of the Super I/O
+  registers.
+
+  The Modify() function provides an interface for table based programming of
+  the Super I/O registers. This function can be used to perform programming of
+  multiple Super I/O registers with a single function call. For each table
+  entry, the Register is read, its content is bitwise ANDed with AndMask, and
+  then ORed with OrMask before being written back to the Register. The Super
+  I/O driver must track the current state of the Super I/O and enable the
+  configuration mode of Super I/O if necessary prior to table processing. Once
+  the table is processed, the Super I/O device has to be returned to the
+  original state.
+
+  @param[in] This               Indicates a pointer to the calling context.
+  @param[in] Command            A pointer to an array of NumberOfCommands
+                                EFI_SIO_REGISTER_MODIFY structures. Each
+                                structure specifies a single Super I/O register
+                                modify operation.
+  @param[in] NumberOfCommands   Number of elements in the Command array.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER Command is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+SioModify (
+  IN CONST EFI_SIO_PROTOCOL           *This,
+  IN CONST EFI_SIO_REGISTER_MODIFY    *Command,
+  IN       UINTN                      NumberOfCommands
+  );
+
+//
+// Internal functions
+//
+
+/**
+  Create all the ISA child devices on the ISA bus controller (PCI to ISA
+  bridge).
+
+  @param[in] This              The EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in] Controller        The handle of ISA bus controller.
+  @param[in] PciIo             The pointer to the PCI protocol.
+  @param[in] ParentDevicePath  Device path of the ISA bus controller.
+
+  @retval The number of child device that is successfully created.
+
+**/
+UINT32
+SioCreateAllChildDevices (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_PCI_IO_PROTOCOL          *PciIo,
+  IN EFI_DEVICE_PATH_PROTOCOL     *ParentDevicePath
+  );
+
+#endif  // __SIO_SERVICE_H__