diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/VBoxPkg')
122 files changed, 25326 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/ComponentName.c new file mode 100644 index 00000000..8a716487 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/ComponentName.c @@ -0,0 +1,172 @@ +/** @file + + Component Name code for the virtio-net driver. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/UefiLib.h> + +#include "E1kNet.h" + +STATIC +EFI_UNICODE_STRING_TABLE mE1kNetDriverNameTable[] = { + { "eng;en", L"E1000 network interface card Driver" }, + { NULL, NULL } +}; + +STATIC +EFI_UNICODE_STRING_TABLE mE1kNetControllerNameTable[] = { + { "eng;en", L"E1000 network interface card Driver" }, + { NULL, NULL } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language + identifier. This is the language of the driver name that + 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. + @param 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. + +**/ + +STATIC +EFI_STATUS +EFIAPI +E1kNetGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return (Language == NULL || DriverName == NULL) ? + EFI_INVALID_PARAMETER : + LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mE1kNetDriverNameTable, + DriverName, + (BOOLEAN) (This == &gE1kNetComponentName) // Iso639Language + ); +} + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL + instance. + @param 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 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 Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller + 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. + @param 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. + +**/ + +STATIC +EFI_STATUS +EFIAPI +E1kNetGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + if (ControllerHandle == NULL || Language == NULL || ControllerName == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // confirm that the device is managed by this driver, using the PciIo + // Protocol + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gE1kNetDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // we don't give different names to the bus (= parent) handle and the + // child (= MAC) handle + // + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mE1kNetControllerNameTable, + ControllerName, + (BOOLEAN) (This == &gE1kNetComponentName) // Iso639Language + ); +} + +EFI_COMPONENT_NAME_PROTOCOL gE1kNetComponentName = { + &E1kNetGetDriverName, + &E1kNetGetControllerName, + "eng" // SupportedLanguages, ISO 639-2 language codes +}; + +EFI_COMPONENT_NAME2_PROTOCOL gE1kNetComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &E1kNetGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &E1kNetGetControllerName, + "en" // SupportedLanguages, RFC 4646 language codes +}; diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/DriverBinding.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/DriverBinding.c new file mode 100644 index 00000000..7ab92e6b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/DriverBinding.c @@ -0,0 +1,666 @@ +/** @file + + Driver Binding code and its private helpers for the virtio-net driver. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <IndustryStandard/Pci.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DevicePathLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +#define RECEIVE_FILTERS_NO_MCAST ((UINT32) ( \ + EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | \ + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | \ + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS \ + )) + +STATIC +EFI_STATUS +E1kNetEepromRead ( + IN E1K_NET_DEV *Dev, + IN UINT8 Offset, + OUT UINT16 *Data + ) +{ + EFI_STATUS Status; + UINT32 RegEerd = 0; + + Status = E1kNetRegWrite32(Dev, E1K_REG_EERD, ((UINT32)Offset << 8) | E1K_REG_EERD_START); + if (EFI_ERROR (Status)) + return Status; + + // Wait for the read to complete + while ( !EFI_ERROR (Status) + && !(RegEerd & E1K_REG_EERD_DONE)) { + gBS->Stall(1); + Status = E1kNetRegRead32(Dev, E1K_REG_EERD, &RegEerd); + } + + if (!EFI_ERROR(Status)) + *Data = E1K_REG_EERD_DATA_GET(RegEerd); + + return Status; +} + +STATIC +EFI_STATUS +E1kNetMacAddrRead ( + IN E1K_NET_DEV *Dev + ) +{ + EFI_STATUS Status; + UINT8 i; + + for (i = 0; i < 3; i++) + { + UINT16 MacAddr; + Status = E1kNetEepromRead (Dev, i, &MacAddr); + if (EFI_ERROR (Status)) + return Status; + + Dev->Snm.CurrentAddress.Addr[i * 2] = MacAddr & 0xff; + Dev->Snm.CurrentAddress.Addr[i * 2 + 1] = (MacAddr >> 8) & 0xff; + } + + return Status; +} + +/** + Set up the Simple Network Protocol fields, the Simple Network Mode fields, + and the Exit Boot Services Event of the virtio-net driver instance. + + This function may only be called by E1kNetDriverBindingStart(). + + @param[in,out] Dev The E1K_NET_DEV driver instance being created for the + e1000 device. + + @return Status codes from the CreateEvent(). + @retval EFI_SUCCESS Configuration successful. +*/ +STATIC +EFI_STATUS +EFIAPI +E1kNetSnpPopulate ( + IN OUT E1K_NET_DEV *Dev + ) +{ + UINT32 RegSts; + EFI_STATUS Status; + + // + // We set up a function here that is asynchronously callable by an + // external application to check if there are any packets available for + // reception. The least urgent task priority level we can specify for such a + // "software interrupt" is TPL_CALLBACK. + // + // TPL_CALLBACK is also the maximum TPL an SNP implementation is allowed to + // run at (see 6.1 Event, Timer, and Task Priority Services in the UEFI + // Specification 2.3.1+errC). + // + // Since we raise our TPL to TPL_CALLBACK in every single function that + // accesses the device, and the external application also queues its interest + // for received packets at the same TPL_CALLBACK, in effect the + // E1kNetIsPacketAvailable() function will never interrupt any + // device-accessing driver function, it will be scheduled in isolation. + // + // TPL_CALLBACK (which basically this entire driver runs at) is allowed + // for "[l]ong term operations (such as file system operations and disk + // I/O)". Because none of our functions block, we'd satisfy an even stronger + // requirement. + // + Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK, + &E1kNetIsPacketAvailable, Dev, &Dev->Snp.WaitForPacket); + if (EFI_ERROR (Status)) { + return Status; + } + + Dev->Snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; + Dev->Snp.Start = &E1kNetStart; + Dev->Snp.Stop = &E1kNetStop; + Dev->Snp.Initialize = &E1kNetInitialize; + Dev->Snp.Reset = &E1kNetReset; + Dev->Snp.Shutdown = &E1kNetShutdown; + Dev->Snp.ReceiveFilters = &E1kNetReceiveFilters; + Dev->Snp.StationAddress = &E1kNetStationAddress; + Dev->Snp.Statistics = &E1kNetStatistics; + Dev->Snp.MCastIpToMac = &E1kNetMcastIpToMac; + Dev->Snp.NvData = &E1kNetNvData; + Dev->Snp.GetStatus = &E1kNetGetStatus; + Dev->Snp.Transmit = &E1kNetTransmit; + Dev->Snp.Receive = &E1kNetReceive; + Dev->Snp.Mode = &Dev->Snm; + + Dev->Snm.State = EfiSimpleNetworkStopped; + Dev->Snm.HwAddressSize = sizeof (E1K_NET_MAC); + Dev->Snm.MediaHeaderSize = sizeof (E1K_NET_MAC) + // dst MAC + sizeof (E1K_NET_MAC) + // src MAC + 2; // Ethertype + Dev->Snm.MaxPacketSize = 1500; + Dev->Snm.NvRamSize = 0; + Dev->Snm.NvRamAccessSize = 0; + Dev->Snm.ReceiveFilterMask = RECEIVE_FILTERS_NO_MCAST; + Dev->Snm.ReceiveFilterSetting = RECEIVE_FILTERS_NO_MCAST; + Dev->Snm.MaxMCastFilterCount = 0; + Dev->Snm.MCastFilterCount = 0; + Dev->Snm.IfType = 1; // ethernet + Dev->Snm.MacAddressChangeable = FALSE; + Dev->Snm.MultipleTxSupported = TRUE; + + ASSERT (sizeof (E1K_NET_MAC) <= sizeof (EFI_MAC_ADDRESS)); + + Dev->Snm.MediaPresentSupported = TRUE; + Status = E1kNetRegRead32(Dev, E1K_REG_STATUS, &RegSts); + if (EFI_ERROR (Status)) { + goto CloseWaitForPacket; + } + + Dev->Snm.MediaPresent = (BOOLEAN)((RegSts & E1K_REG_STATUS_LU) != 0); + + Status = E1kNetMacAddrRead(Dev); + CopyMem (&Dev->Snm.PermanentAddress, &Dev->Snm.CurrentAddress, + sizeof (E1K_NET_MAC)); + SetMem (&Dev->Snm.BroadcastAddress, sizeof (E1K_NET_MAC), 0xFF); + + // + // E1kNetExitBoot() is queued by ExitBootServices(); its purpose is to + // cancel any pending requests. The TPL_CALLBACK reasoning is + // identical to the one above. There's one difference: this kind of + // event is "globally visible", which means it can be signalled as soon as + // we create it. We haven't raised our TPL here, hence E1kNetExitBoot() + // could be entered immediately. E1kNetExitBoot() checks Dev->Snm.State, + // so we're safe. + // + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, + &E1kNetExitBoot, Dev, &Dev->ExitBoot); + if (EFI_ERROR (Status)) { + goto CloseWaitForPacket; + } + + return EFI_SUCCESS; + +CloseWaitForPacket: + gBS->CloseEvent (Dev->Snp.WaitForPacket); + return Status; +} + + +/** + Release any resources allocated by E1kNetSnpPopulate(). + + This function may only be called by E1kNetDriverBindingStart(), when + rolling back a partial, failed driver instance creation, and by + E1kNetDriverBindingStop(), when disconnecting a virtio-net device from the + driver. + + @param[in,out] Dev The E1K_NET_DEV driver instance being destroyed. +*/ +STATIC +VOID +EFIAPI +E1kNetSnpEvacuate ( + IN OUT E1K_NET_DEV *Dev + ) +{ + // + // This function runs either at TPL_CALLBACK already (from + // E1kNetDriverBindingStop()), or it is part of a teardown following + // a partial, failed construction in E1kNetDriverBindingStart(), when + // WaitForPacket was never accessible to the world. + // + gBS->CloseEvent (Dev->ExitBoot); + gBS->CloseEvent (Dev->Snp.WaitForPacket); +} + + +/** + 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(). Because 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. Currently not + implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the + driver specified by This. +**/ + +STATIC +EFI_STATUS +EFIAPI +E1kNetDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + 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 == INTEL_PCI_VENDOR_ID && + (Pci.Hdr.DeviceId == INTEL_82540EM_PCI_DEVICE_ID || + Pci.Hdr.DeviceId == INTEL_82543GC_PCI_DEVICE_ID || + Pci.Hdr.DeviceId == INTEL_82545EM_PCI_DEVICE_ID)) { + Status = EFI_SUCCESS; + } else { + Status = EFI_UNSUPPORTED; + } + +Done: + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + 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.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval Others The driver failed to start the device. + +**/ +STATIC +EFI_STATUS +EFIAPI +E1kNetDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + E1K_NET_DEV *Dev; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + MAC_ADDR_DEVICE_PATH MacNode; + + DEBUG((DEBUG_INFO, "E1kNetControllerStart:\n")); + + Dev = AllocateZeroPool (sizeof (*Dev)); + if (Dev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Dev->Signature = E1K_NET_DEV_SIGNATURE; + + 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->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__ + )); + } + + DEBUG((DEBUG_INFO, "E1kNetControllerStart: Resetting NIC\n")); + Status = E1kNetDevReset (Dev); + if (EFI_ERROR (Status)) { + goto RestoreAttributes; + } + + // + // now we can run a basic one-shot e1000 initialization required to + // retrieve the MAC address + // + DEBUG((DEBUG_INFO, "E1kNetControllerStart: Populating SNP interface\n")); + Status = E1kNetSnpPopulate (Dev); + if (EFI_ERROR (Status)) { + goto UninitDev; + } + + // + // get the device path of the e1000 device -- one-shot open + // + Status = gBS->OpenProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid, + (VOID **)&DevicePath, This->DriverBindingHandle, + ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR (Status)) { + goto Evacuate; + } + + // + // create another device path that has the MAC address appended + // + MacNode.Header.Type = MESSAGING_DEVICE_PATH; + MacNode.Header.SubType = MSG_MAC_ADDR_DP; + SetDevicePathNodeLength (&MacNode, sizeof MacNode); + CopyMem (&MacNode.MacAddress, &Dev->Snm.CurrentAddress, + sizeof (EFI_MAC_ADDRESS)); + MacNode.IfType = Dev->Snm.IfType; + + Dev->MacDevicePath = AppendDevicePathNode (DevicePath, &MacNode.Header); + if (Dev->MacDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Evacuate; + } + + // + // create a child handle with the Simple Network Protocol and the new + // device path installed on it + // + Status = gBS->InstallMultipleProtocolInterfaces (&Dev->MacHandle, + &gEfiSimpleNetworkProtocolGuid, &Dev->Snp, + &gEfiDevicePathProtocolGuid, Dev->MacDevicePath, + NULL); + if (EFI_ERROR (Status)) { + goto FreeMacDevicePath; + } + + DEBUG((DEBUG_INFO, "E1kNetControllerStart: returns EFI_SUCCESS\n")); + return EFI_SUCCESS; + +FreeMacDevicePath: + FreePool (Dev->MacDevicePath); + +Evacuate: + E1kNetSnpEvacuate (Dev); + +UninitDev: + E1kNetDevReset (Dev); + +RestoreAttributes: + Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationSet, + Dev->OriginalPciAttributes, + NULL + ); + +CloseProtocol: + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + +FreePool: + FreePool (Dev); + + DEBUG((DEBUG_INFO, "E1kNetControllerStart: returns %u\n", Status)); + return Status; +} + +/** + 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. + +**/ +STATIC +EFI_STATUS +EFIAPI +E1kNetDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + if (NumberOfChildren > 0) { + // + // free all resources for whose access we need the child handle, because + // the child handle is going away + // + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + + ASSERT (NumberOfChildren == 1); + + Status = gBS->OpenProtocol (ChildHandleBuffer[0], + &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp, + This->DriverBindingHandle, ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + ASSERT_EFI_ERROR (Status); + Dev = E1K_NET_FROM_SNP (Snp); + + // + // prevent any interference with WaitForPacket + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + ASSERT (Dev->MacHandle == ChildHandleBuffer[0]); + if (Dev->Snm.State != EfiSimpleNetworkStopped) { + // + // device in use, cannot stop driver instance + // + Status = EFI_DEVICE_ERROR; + } + else { + gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle, + &gEfiDevicePathProtocolGuid, Dev->MacDevicePath, + &gEfiSimpleNetworkProtocolGuid, &Dev->Snp, + NULL); + FreePool (Dev->MacDevicePath); + E1kNetSnpEvacuate (Dev); + + Dev->PciIo->Attributes ( + Dev->PciIo, + EfiPciIoAttributeOperationSet, + Dev->OriginalPciAttributes, + NULL + ); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + FreePool (Dev); + } + + gBS->RestoreTPL (OldTpl); + return Status; + } + + return EFI_SUCCESS; +} + + +EFI_DRIVER_BINDING_PROTOCOL gE1kNetDriverBinding = { + &E1kNetDriverBindingSupported, + &E1kNetDriverBindingStart, + &E1kNetDriverBindingStop, + 0x10, + NULL, + NULL +}; diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kHwIo.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kHwIo.c new file mode 100644 index 00000000..f203048b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kHwIo.c @@ -0,0 +1,182 @@ +/** @file + + This file implements the hardware register access functions of the e1000 driver. + + Copyright (c) 2021, Oracle and/or its affiliates.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <IndustryStandard/Pci.h> +#include <Library/UefiLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +EFI_STATUS +EFIAPI +E1kNetRegWrite32 ( + IN E1K_NET_DEV *Dev, + IN UINT32 Addr, + IN UINT32 Data + ) +{ + EFI_STATUS Status; + + Status = Dev->PciIo->Io.Write ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX2, + 0, // IOADDR + 1, + &Addr + ); + if (!EFI_ERROR (Status)) + { + Status = Dev->PciIo->Io.Write ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX2, + 4, // IODATA + 1, + &Data + ); + } + + return Status; +} + +EFI_STATUS +EFIAPI +E1kNetRegRead32 ( + IN E1K_NET_DEV *Dev, + IN UINT32 Addr, + OUT UINT32 *Data + ) +{ + EFI_STATUS Status; + + Status = Dev->PciIo->Io.Write ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX2, + 0, // IOADDR + 1, + &Addr + ); + if (!EFI_ERROR (Status)) + { + return Dev->PciIo->Io.Read ( + Dev->PciIo, + EfiPciIoWidthUint32, + PCI_BAR_IDX2, + 4, // IODATA + 1, + Data + ); + } + + return Status; +} + +EFI_STATUS +EFIAPI +E1kNetRegSet32 ( + IN E1K_NET_DEV *Dev, + IN UINT32 Addr, + IN UINT32 Set) +{ + UINT32 Reg; + EFI_STATUS Status; + + Status = E1kNetRegRead32 (Dev, Addr, &Reg); + if (EFI_ERROR (Status)) { + return Status; + } + + Reg |= Set; + Status = E1kNetRegWrite32 (Dev, Addr, Reg); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +E1kNetRegClear32 ( + IN E1K_NET_DEV *Dev, + IN UINT32 Addr, + IN UINT32 Clear) +{ + UINT32 Reg; + EFI_STATUS Status; + + Status = E1kNetRegRead32 (Dev, Addr, &Reg); + if (EFI_ERROR (Status)) { + return Status; + } + + Reg &= ~Clear; + Status = E1kNetRegWrite32 (Dev, Addr, Reg); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +E1kNetDevReset ( + IN E1K_NET_DEV *Dev + ) +{ + EFI_STATUS Status; + + // + // Reset hardware + // + Status = E1kNetRegSet32 (Dev, E1K_REG_CTRL, E1K_REG_CTRL_RST); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Wait for the reset to complete + // + for (;;) + { + UINT32 Ctrl; + + Status = E1kNetRegRead32 (Dev, E1K_REG_CTRL, &Ctrl); + if (EFI_ERROR (Status)) { + return Status; + } + + /// @todo Timeout? + if (!(Ctrl & E1K_REG_CTRL_RST)) + break; + } + + // + // Reset the PHY. + // + Status = E1kNetRegSet32 (Dev, E1K_REG_CTRL, E1K_REG_CTRL_PHY_RST); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Wait for the specified amount of 3us and de-assert the PHY reset signal. + // + gBS->Stall(3); + Status = E1kNetRegClear32 (Dev, E1K_REG_CTRL, E1K_REG_CTRL_PHY_RST); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.h new file mode 100644 index 00000000..ea224eff --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.h @@ -0,0 +1,350 @@ +/** @file + + Internal definitions for the virtio-net driver, which produces Simple Network + Protocol instances for virtio-net devices. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (c) 2017, AMD Inc, All rights reserved. + Copyright (C) 2013, Red Hat, Inc.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _E1K_NET_DXE_H_ +#define _E1K_NET_DXE_H_ + +#include <Library/DebugLib.h> +#include <Protocol/PciIo.h> +#include <Protocol/PciRootBridgeIo.h> +#include <Protocol/ComponentName.h> +#include <Protocol/ComponentName2.h> +#include <Protocol/DevicePath.h> +#include <Protocol/DriverBinding.h> +#include <Protocol/SimpleNetwork.h> +#include <Library/OrderedCollectionLib.h> + +#include "E1kNetHw.h" + +#define E1K_NET_DEV_SIGNATURE SIGNATURE_32 ('E','1','K','N') + +// +// maximum number of pending packets, separately for each direction +// +#define E1K_NET_MAX_PENDING 64 + +// +// State diagram: +// +// | ^ +// | | +// BindingStart BindingStop +// +SnpPopulate | +// ++GetFeatures | +// | | +// v | +// +---------+ virtio-net device is reset, no resources are +// | stopped | allocated for traffic, but MAC address has +// +---------+ been retrieved +// | ^ +// | | +// SNP.Start SNP.Stop +// | | +// v | +// +---------+ +// | started | functionally identical to stopped +// +---------+ +// | ^ +// | | +// SNP.Initialize SNP.Shutdown +// | | +// v | +// +-------------+ Virtio-net setup complete, including DRIVER_OK +// | initialized | bit. The receive queue is populated with +// +-------------+ requests; McastIpToMac, GetStatus, Transmit, +// Receive are callable. +// + +typedef struct { + // + // Parts of this structure are initialized / torn down in various functions + // at various call depths. The table to the right should make it easier to + // track them. + // + // field init function + // ------------------ ------------------------------ + UINT32 Signature; // VirtioNetDriverBindingStart + EFI_PCI_IO_PROTOCOL *PciIo; // VirtioNetDriverBindingStart + UINT64 OriginalPciAttributes; // VirtioNetDriverBindingStart + EFI_SIMPLE_NETWORK_PROTOCOL Snp; // VirtioNetSnpPopulate + EFI_SIMPLE_NETWORK_MODE Snm; // VirtioNetSnpPopulate + EFI_EVENT ExitBoot; // VirtioNetSnpPopulate + EFI_DEVICE_PATH_PROTOCOL *MacDevicePath; // VirtioNetDriverBindingStart + EFI_HANDLE MacHandle; // VirtioNetDriverBindingStart + + E1K_RX_DESC *RxRing; // VirtioNetInitRing + UINT8 *RxBuf; // E1kNetInitRx + UINT32 RdhLastSeen; // E1kNetInitRx + UINTN RxBufNrPages; // E1kNetInitRx + EFI_PHYSICAL_ADDRESS RxBufDeviceBase; // E1kNetInitRx + EFI_PHYSICAL_ADDRESS RxDeviceBase; // E1kNetInitRx + VOID *RxMap; // E1kNetInitRx + + UINT16 TxMaxPending; // E1kNetInitTx + UINT16 TxCurPending; // E1kNetInitTx + E1K_TX_DESC *TxRing; // E1kNetInitTx + VOID *TxRingMap; // E1kNetInitTx + UINT16 TxLastUsed; // E1kNetInitTx + UINT32 TdhLastSeen; // E1kNetInitTx + ORDERED_COLLECTION *TxBufCollection; // E1kNetInitTx +} E1K_NET_DEV; + + +// +// In order to avoid duplication of interface documentation, please find all +// leading comments near the respective function / variable definitions (not +// the declarations here), which is where your code editor of choice takes you +// anyway when jumping to a function. +// + +// +// utility macros +// +#define E1K_NET_FROM_SNP(SnpPointer) \ + CR (SnpPointer, E1K_NET_DEV, Snp, E1K_NET_DEV_SIGNATURE) + +// +// component naming +// +extern EFI_COMPONENT_NAME_PROTOCOL gE1kNetComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gE1kNetComponentName2; + +// +// driver binding +// +extern EFI_DRIVER_BINDING_PROTOCOL gE1kNetDriverBinding; + +// +// member functions implementing the Simple Network Protocol +// +EFI_STATUS +EFIAPI +E1kNetStart ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +E1kNetStop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +E1kNetInitialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN ExtraRxBufferSize OPTIONAL, + IN UINTN ExtraTxBufferSize OPTIONAL + ); + +EFI_STATUS +EFIAPI +E1kNetReset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +EFI_STATUS +EFIAPI +E1kNetShutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +E1kNetReceiveFilters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINT32 Enable, + IN UINT32 Disable, + IN BOOLEAN ResetMCastFilter, + IN UINTN MCastFilterCnt OPTIONAL, + IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL + ); + +EFI_STATUS +EFIAPI +E1kNetStationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN Reset, + IN EFI_MAC_ADDRESS *New OPTIONAL + ); + +EFI_STATUS +EFIAPI +E1kNetStatistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN Reset, + IN OUT UINTN *StatisticsSize OPTIONAL, + OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL + ); + +EFI_STATUS +EFIAPI +E1kNetMcastIpToMac ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN IPv6, + IN EFI_IP_ADDRESS *Ip, + OUT EFI_MAC_ADDRESS *Mac + ); + +EFI_STATUS +EFIAPI +E1kNetNvData ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ReadWrite, + IN UINTN Offset, + IN UINTN BufferSize, + IN OUT VOID *Buffer + ); + +EFI_STATUS +EFIAPI +E1kNetGetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINT32 *InterruptStatus OPTIONAL, + OUT VOID **TxBuf OPTIONAL + ); + +EFI_STATUS +EFIAPI +E1kNetTransmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN /* +OUT! */ VOID *Buffer, + IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + IN EFI_MAC_ADDRESS *DestAddr OPTIONAL, + IN UINT16 *Protocol OPTIONAL + ); + +EFI_STATUS +EFIAPI +E1kNetReceive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINTN *HeaderSize OPTIONAL, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer, + OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL, + OUT UINT16 *Protocol OPTIONAL + ); + +// +// utility functions shared by various SNP member functions +// +VOID +EFIAPI +E1kNetShutdownRx ( + IN OUT E1K_NET_DEV *Dev + ); + +VOID +EFIAPI +E1kNetShutdownTx ( + IN OUT E1K_NET_DEV *Dev + ); + +// +// utility functions to map caller-supplied Tx buffer system physical address +// to a device address and vice versa +// +EFI_STATUS +EFIAPI +E1kNetMapTxBuf ( + IN E1K_NET_DEV *Dev, + IN VOID *Buffer, + IN UINTN NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress + ); + +EFI_STATUS +EFIAPI +E1kNetUnmapTxBuf ( + IN E1K_NET_DEV *Dev, + OUT VOID **Buffer, + IN EFI_PHYSICAL_ADDRESS DeviceAddress + ); + +INTN +EFIAPI +E1kNetTxBufMapInfoCompare ( + IN CONST VOID *UserStruct1, + IN CONST VOID *UserStruct2 + ); + +INTN +EFIAPI +E1kNetTxBufDeviceAddressCompare ( + IN CONST VOID *StandaloneKey, + IN CONST VOID *UserStruct + ); + + +// +// event callbacks +// +VOID +EFIAPI +E1kNetIsPacketAvailable ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +EFIAPI +E1kNetExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// +// Hardware I/O functions. +// +EFI_STATUS +EFIAPI +E1kNetRegWrite32 ( + IN E1K_NET_DEV *Dev, + IN UINT32 Addr, + IN UINT32 Data + ); + +EFI_STATUS +EFIAPI +E1kNetRegRead32 ( + IN E1K_NET_DEV *Dev, + IN UINT32 Addr, + OUT UINT32 *Data + ); + +EFI_STATUS +EFIAPI +E1kNetRegSet32 ( + IN E1K_NET_DEV *Dev, + IN UINT32 Addr, + IN UINT32 Set + ); + +EFI_STATUS +EFIAPI +E1kNetRegClear32 ( + IN E1K_NET_DEV *Dev, + IN UINT32 Addr, + IN UINT32 Clear + ); + +EFI_STATUS +EFIAPI +E1kNetDevReset ( + IN E1K_NET_DEV *Dev + ); + +#endif // _E1K_NET_DXE_H_ diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.inf new file mode 100644 index 00000000..7c9ba681 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.inf @@ -0,0 +1,60 @@ + +## @file +# +# This driver produces Simple Network Protocol instances for e1000 based +# devices. +# +# Copyright (C) 2021, Oracle and/or its affiliates. +# Copyright (C) 2013, Red Hat, Inc. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = E1kNetDxe + FILE_GUID = FFCC3DF0-C1EC-11EB-BA2F-0800200C9A66 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = E1kNetEntryPoint + +[Sources] + ComponentName.c + DriverBinding.c + EntryPoint.c + Events.c + SnpGetStatus.c + SnpInitialize.c + SnpMcastIpToMac.c + SnpReceive.c + SnpReceiveFilters.c + SnpSharedHelpers.c + SnpShutdown.c + SnpStart.c + SnpStop.c + SnpTransmit.c + SnpUnsupported.c + E1kHwIo.c + E1kNet.h + E1kNetHw.h + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + DevicePathLib + MemoryAllocationLib + OrderedCollectionLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiSimpleNetworkProtocolGuid ## BY_START + gEfiDevicePathProtocolGuid ## BY_START + gEfiPciIoProtocolGuid ## TO_START diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNetHw.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNetHw.h new file mode 100644 index 00000000..90afcf8a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNetHw.h @@ -0,0 +1,105 @@ +/** @file + + E1000 hardware interface definitions. + + Copyright (c) 2021, Oracle and/or its affiliates.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _E1K_NET_HW_H_ +#define _E1K_NET_HW_H_ + +#define INTEL_PCI_VENDOR_ID 0x8086 +#define INTEL_82540EM_PCI_DEVICE_ID 0x100e +#define INTEL_82543GC_PCI_DEVICE_ID 0x1004 +#define INTEL_82545EM_PCI_DEVICE_ID 0x100f + +// +// Receive descriptor. +// +typedef struct { + UINT32 AddrBufferLow; + UINT32 AddrBufferHigh; + UINT16 BufferLength; + UINT16 Checksum; + UINT8 Status; + UINT8 Errors; + UINT16 Special; +} E1K_RX_DESC; + +#define E1K_RX_STATUS_DONE BIT0 +#define E1K_RX_STATUS_EOP BIT1 + +#define E1K_RX_ERROR_CE BIT0 +#define E1K_RX_ERROR_SEQ BIT2 +#define E1K_RX_ERROR_CXE BIT4 +#define E1K_RX_ERROR_RXE BIT7 + +// +// Transmit descriptor. +// +typedef struct { + UINT32 AddrBufferLow; + UINT32 AddrBufferHigh; + UINT16 BufferLength; + UINT8 ChecksumOffset; + UINT8 Command; + UINT8 Status; + UINT8 ChecksumStart; + UINT16 Special; +} E1K_TX_DESC; + +#define E1K_TX_CMD_EOP BIT0 +#define E1K_TX_CMD_FCS BIT1 +#define E1K_TX_CMD_RS BIT3 + +#define E1K_REG_CTRL 0x00000000 +# define E1K_REG_CTRL_ASDE BIT5 +# define E1K_REG_CTRL_SLU BIT6 +# define E1K_REG_CTRL_RST BIT26 +# define E1K_REG_CTRL_PHY_RST BIT31 +#define E1K_REG_STATUS 0x00000008 +# define E1K_REG_STATUS_LU BIT1 +#define E1K_REG_EECD 0x00000010 +#define E1K_REG_EERD 0x00000014 +# define E1K_REG_EERD_START BIT0 +# define E1K_REG_EERD_DONE BIT4 +# define E1K_REG_EERD_DATA_GET(x) (((x) >> 16) & 0xffff) +#define E1K_REG_ICR 0x000000c0 +#define E1K_REG_ITR 0x000000c4 +#define E1K_REG_ICS 0x000000c8 +#define E1K_REG_IMS 0x000000d0 +#define E1K_REG_IMC 0x000000d8 +#define E1K_REG_RCTL 0x00000100 +# define E1K_REG_RCTL_EN BIT1 +# define E1K_REG_RCTL_MPE BIT4 +# define E1K_REG_RCTL_BSIZE_MASK 0x00030000 +#define E1K_REG_RDBAL 0x00002800 +#define E1K_REG_RDBAH 0x00002804 +#define E1K_REG_RDLEN 0x00002808 +#define E1K_REG_RDH 0x00002810 +#define E1K_REG_RDT 0x00002818 +#define E1K_REG_RDTR 0x00002820 +#define E1K_REG_TCTL 0x00000400 +# define E1K_REG_TCTL_EN BIT1 +# define E1K_REG_TCTL_PSP BIT3 +#define E1K_REG_TIPG 0x00000410 +#define E1K_REG_TDBAL 0x00003800 +#define E1K_REG_TDBAH 0x00003804 +#define E1K_REG_TDLEN 0x00003808 +#define E1K_REG_TDH 0x00003810 +#define E1K_REG_TDT 0x00003818 +#define E1K_REG_RAL 0x00005400 +#define E1K_REG_RAH 0x00005404 +# define E1K_REG_RAH_AV BIT31 + +// +// MAC address. +// +typedef struct +{ + UINT8 Mac[6]; +} E1K_NET_MAC; + +#endif // _E1K_NET_HW_H_ diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/EntryPoint.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/EntryPoint.c new file mode 100644 index 00000000..d4cf35fd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/EntryPoint.c @@ -0,0 +1,45 @@ +/** @file + + This file implements the entry point of the e1000 driver. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/UefiLib.h> + +#include "E1kNet.h" + +/** + This is the declaration of an EFI image entry point. This entry point is the + same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including both + device drivers and bus drivers. + + @param ImageHandle The firmware allocated handle for the UEFI + image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. +**/ + +EFI_STATUS +EFIAPI +E1kNetEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gE1kNetDriverBinding, + ImageHandle, + &gE1kNetComponentName, + &gE1kNetComponentName2 + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/Events.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/Events.c new file mode 100644 index 00000000..e51aee58 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/Events.c @@ -0,0 +1,82 @@ +/** @file + + Implements + - the SNM.WaitForPacket EVT_NOTIFY_WAIT event, + - the EVT_SIGNAL_EXIT_BOOT_SERVICES event + for the e1000 driver. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Invoke a notification event + + @param Event Event whose notification function is being + invoked. + @param Context The pointer to the notification function's + context, which is implementation-dependent. + +**/ + +VOID +EFIAPI +E1kNetIsPacketAvailable ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // This callback has been enqueued by an external application and is + // running at TPL_CALLBACK already. + // + // The WaitForPacket logic is similar to that of WaitForKey. The former has + // almost no documentation in either the UEFI-2.3.1+errC spec or the + // DWG-2.3.1, but WaitForKey does have some. + // + E1K_NET_DEV *Dev; + UINT32 RdhCur; + + Dev = Context; + if (Dev->Snm.State != EfiSimpleNetworkInitialized) { + return; + } + + E1kNetRegRead32(Dev, E1K_REG_RDH, &RdhCur); + + if (Dev->RdhLastSeen != RdhCur) { + gBS->SignalEvent (Dev->Snp.WaitForPacket); + } +} + +VOID +EFIAPI +E1kNetExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // This callback has been enqueued by ExitBootServices() and is running at + // TPL_CALLBACK already. + // + // Shut down pending transfers according to DWG-2.3.1, "25.5.1 Exit Boot + // Services Event". + // + E1K_NET_DEV *Dev; + + DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context)); + Dev = Context; + if (Dev->Snm.State == EfiSimpleNetworkInitialized) { + E1kNetDevReset (Dev); + } +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpGetStatus.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpGetStatus.c new file mode 100644 index 00000000..6927b46e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpGetStatus.c @@ -0,0 +1,160 @@ +/** @file + + Implementation of the SNP.GetStatus() function and its private helpers if + any. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Reads the current interrupt status and recycled transmit buffer status from + a network interface. + + @param This The protocol instance pointer. + @param InterruptStatus A pointer to the bit mask of the currently active + interrupts If this is NULL, the interrupt status will + not be read from the device. If this is not NULL, the + interrupt status will be read from the device. When + the interrupt status is read, it will also be + cleared. Clearing the transmit interrupt does not + empty the recycled transmit buffer array. + @param TxBuf Recycled transmit buffer address. The network + interface will not transmit if its internal recycled + transmit buffer array is full. Reading the transmit + buffer does not clear the transmit interrupt. If this + is NULL, then the transmit buffer status will not be + read. If there are no transmit buffers to recycle and + TxBuf is not NULL, * TxBuf will be set to NULL. + + @retval EFI_SUCCESS The status of the network interface was + retrieved. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetGetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINT32 *InterruptStatus OPTIONAL, + OUT VOID **TxBuf OPTIONAL + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT32 TdhCur; + UINT32 RdhCur; + EFI_PHYSICAL_ADDRESS DeviceAddress; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + switch (Dev->Snm.State) { + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto Exit; + case EfiSimpleNetworkStarted: + Status = EFI_DEVICE_ERROR; + goto Exit; + default: + break; + } + + // + // update link status + // + if (Dev->Snm.MediaPresentSupported) { + UINT32 RegSts; + + Status = E1kNetRegRead32(Dev, E1K_REG_STATUS, &RegSts); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Dev->Snm.MediaPresent = (BOOLEAN)((RegSts & E1K_REG_STATUS_LU) != 0); + } + + E1kNetRegRead32(Dev, E1K_REG_TDH, &TdhCur); + E1kNetRegRead32(Dev, E1K_REG_RDH, &RdhCur); + + + if (InterruptStatus != NULL) { + // + // report the receive interrupt if there is data available for reception, + // report the transmit interrupt if we have transmitted at least one buffer + // + *InterruptStatus = 0; + if (Dev->RdhLastSeen != RdhCur) { + *InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + } + if (Dev->TdhLastSeen != TdhCur) { + ASSERT (Dev->TxCurPending > 0); + *InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + } + } + + if (TxBuf != NULL) { + if (Dev->TdhLastSeen == TdhCur) { + *TxBuf = NULL; + } + else { + ASSERT (Dev->TxCurPending > 0); + ASSERT (Dev->TxCurPending <= Dev->TxMaxPending); + + // + // get the device address that has been enqueued for the caller's + // transmit buffer + // + DeviceAddress = Dev->TxRing[Dev->TdhLastSeen].AddrBufferLow; + DeviceAddress |= LShiftU64(Dev->TxRing[Dev->TdhLastSeen].AddrBufferHigh, 32); + + Dev->TdhLastSeen = (Dev->TdhLastSeen + 1) % E1K_NET_MAX_PENDING; + Dev->TxCurPending--; + + // + // Unmap the device address and perform the reverse mapping to find the + // caller buffer address. + // + Status = E1kNetUnmapTxBuf ( + Dev, + TxBuf, + DeviceAddress + ); + if (EFI_ERROR (Status)) { + // + // E1kNetUnmapTxBuf should never fail, if we have reached here + // that means our internal state has been corrupted + // + ASSERT (FALSE); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + } + } + + Status = EFI_SUCCESS; + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpInitialize.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpInitialize.c new file mode 100644 index 00000000..1fec91b4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpInitialize.c @@ -0,0 +1,321 @@ +/** @file + + Implementation of the SNP.Initialize() function and its private helpers if + any. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (c) 2017, AMD Inc, All rights reserved. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Set up static scaffolding for the E1kNetTransmit() and + E1kNetGetStatus() SNP methods. + + This function may only be called by E1kNetInitialize(). + + @param[in,out] Dev The E1K_NET_DEV driver instance about to enter the + EfiSimpleNetworkInitialized state. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate the stack to track the heads + of free descriptor chains or failed to init + TxBufCollection. + @return Status codes from VIRTIO_DEVICE_PROTOCOL. + AllocateSharedPages() or + VirtioMapAllBytesInSharedBuffer() + @retval EFI_SUCCESS TX setup successful. +*/ + +STATIC +EFI_STATUS +EFIAPI +E1kNetInitTx ( + IN OUT E1K_NET_DEV *Dev + ) +{ + UINTN TxRingSize; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS DeviceAddress; + VOID *TxRingBuffer; + + Dev->TxMaxPending = E1K_NET_MAX_PENDING; + Dev->TxCurPending = 0; + Dev->TxBufCollection = OrderedCollectionInit ( + E1kNetTxBufMapInfoCompare, + E1kNetTxBufDeviceAddressCompare + ); + if (Dev->TxBufCollection == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Allocate TxRing header and map with BusMasterCommonBuffer so that it + // can be accessed equally by both processor and device. + // + TxRingSize = Dev->TxMaxPending * sizeof (*Dev->TxRing); + Status = Dev->PciIo->AllocateBuffer ( + Dev->PciIo, + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (TxRingSize), + &TxRingBuffer, + EFI_PCI_ATTRIBUTE_MEMORY_CACHED + ); + if (EFI_ERROR (Status)) { + goto UninitTxBufCollection; + } + + ZeroMem (TxRingBuffer, TxRingSize); + + Status = Dev->PciIo->Map ( + Dev->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + TxRingBuffer, + &TxRingSize, + &DeviceAddress, + &Dev->TxRingMap + ); + if (EFI_ERROR (Status)) { + goto FreeTxRingBuffer; + } + + Dev->TxRing = TxRingBuffer; + Dev->TdhLastSeen = 0; + Dev->TxLastUsed = 0; + + // Program the transmit engine. + MemoryFence (); + E1kNetRegWrite32(Dev, E1K_REG_TDBAL, (UINT32)DeviceAddress); + E1kNetRegWrite32(Dev, E1K_REG_TDBAH, (UINT32)(RShiftU64 (DeviceAddress, 32))); + E1kNetRegWrite32(Dev, E1K_REG_TDLEN, (UINT32)TxRingSize); + E1kNetRegWrite32(Dev, E1K_REG_TDH, 0); + E1kNetRegWrite32(Dev, E1K_REG_TDT, 0); + E1kNetRegWrite32(Dev, E1K_REG_TCTL, E1K_REG_TCTL_EN | E1K_REG_TCTL_PSP); + + return EFI_SUCCESS; + +FreeTxRingBuffer: + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + EFI_SIZE_TO_PAGES (TxRingSize), + TxRingBuffer + ); + +UninitTxBufCollection: + OrderedCollectionUninit (Dev->TxBufCollection); + +Exit: + return Status; +} + + +/** + Set up static scaffolding for the E1kNetReceive() SNP method and enable + live device operation. + + This function may only be called as E1kNetInitialize()'s final step. + + @param[in,out] Dev The E1K_NET_DEV driver instance about to enter the + EfiSimpleNetworkInitialized state. + + @return Status codes from VIRTIO_CFG_WRITE() or + VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or + VirtioMapAllBytesInSharedBuffer(). + @retval EFI_SUCCESS RX setup successful. The device is live and may + already be writing to the receive area. +*/ + +STATIC +EFI_STATUS +EFIAPI +E1kNetInitRx ( + IN OUT E1K_NET_DEV *Dev + ) +{ + EFI_STATUS Status; + UINTN RxBufSize; + UINTN PktIdx; + UINTN NumBytes; + EFI_PHYSICAL_ADDRESS RxBufDeviceAddress; + VOID *RxBuffer; + + // + // For each incoming packet we must supply two buffers: + // - the recipient for the RX descriptor, plus + // - the recipient for the network data (which consists of Ethernet header + // and Ethernet payload) which is a 2KB buffer. + // + RxBufSize = sizeof(*Dev->RxRing) + 2048; + + // + // The RxBuf is shared between guest and hypervisor, use + // AllocateSharedPages() to allocate this memory region and map it with + // BusMasterCommonBuffer so that it can be accessed by both guest and + // hypervisor. + // + NumBytes = E1K_NET_MAX_PENDING * RxBufSize; + Dev->RxBufNrPages = EFI_SIZE_TO_PAGES (NumBytes); + Status = Dev->PciIo->AllocateBuffer ( + Dev->PciIo, + AllocateAnyPages, + EfiBootServicesData, + Dev->RxBufNrPages, + &RxBuffer, + EFI_PCI_ATTRIBUTE_MEMORY_CACHED + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (RxBuffer, NumBytes); + + Status = Dev->PciIo->Map ( + Dev->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + RxBuffer, + &NumBytes, + &Dev->RxDeviceBase, + &Dev->RxMap + ); + if (EFI_ERROR (Status)) { + goto FreeSharedBuffer; + } + + Dev->RxRing = RxBuffer; + Dev->RxBuf = (UINT8 *)RxBuffer + sizeof(*Dev->RxRing) * E1K_NET_MAX_PENDING; + Dev->RdhLastSeen = 0; + + // Set up the RX descriptors. + Dev->RxBufDeviceBase = Dev->RxDeviceBase + sizeof(*Dev->RxRing) * E1K_NET_MAX_PENDING; + RxBufDeviceAddress = Dev->RxBufDeviceBase; + for (PktIdx = 0; PktIdx < E1K_NET_MAX_PENDING; ++PktIdx) { + Dev->RxRing[PktIdx].AddrBufferLow = (UINT32)RxBufDeviceAddress; + Dev->RxRing[PktIdx].AddrBufferHigh = (UINT32)RShiftU64(RxBufDeviceAddress, 32); + Dev->RxRing[PktIdx].BufferLength = 2048; + + RxBufDeviceAddress += Dev->RxRing[PktIdx].BufferLength; + } + + // Program the receive engine. + MemoryFence (); + E1kNetRegWrite32(Dev, E1K_REG_RDBAL, (UINT32)Dev->RxDeviceBase); + E1kNetRegWrite32(Dev, E1K_REG_RDBAH, (UINT32)(RShiftU64 (Dev->RxDeviceBase, 32))); + E1kNetRegWrite32(Dev, E1K_REG_RDLEN, sizeof(*Dev->RxRing) * E1K_NET_MAX_PENDING); + E1kNetRegWrite32(Dev, E1K_REG_RDH, 0); + E1kNetRegWrite32(Dev, E1K_REG_RDT, E1K_NET_MAX_PENDING - 1); + E1kNetRegClear32(Dev, E1K_REG_RCTL, E1K_REG_RCTL_BSIZE_MASK); + E1kNetRegSet32(Dev, E1K_REG_RCTL, E1K_REG_RCTL_EN | E1K_REG_RCTL_MPE); + + return EFI_SUCCESS; + +FreeSharedBuffer: + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + Dev->RxBufNrPages, + RxBuffer + ); + return Status; +} + +/** + Resets a network adapter and allocates the transmit and receive buffers + required by the network interface; optionally, also requests allocation of + additional transmit and receive buffers. + + @param This The protocol instance pointer. + @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer + space that the driver should allocate for the + network interface. Some network interfaces will not + be able to use the extra buffer, and the caller + will not know if it is actually being used. + @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer + space that the driver should allocate for the + network interface. Some network interfaces will not + be able to use the extra buffer, and the caller + will not know if it is actually being used. + + @retval EFI_SUCCESS The network interface was initialized. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit + and receive buffers. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetInitialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN ExtraRxBufferSize OPTIONAL, + IN UINTN ExtraTxBufferSize OPTIONAL + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + + DEBUG((DEBUG_INFO, "E1kNetInitialize:\n")); + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + if (ExtraRxBufferSize > 0 || ExtraTxBufferSize > 0) { + return EFI_UNSUPPORTED; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (Dev->Snm.State != EfiSimpleNetworkStarted) { + Status = EFI_NOT_STARTED; + goto InitFailed; + } + + // Program the first Receive Address Low/High register. + E1kNetRegSet32(Dev, E1K_REG_CTRL, E1K_REG_CTRL_ASDE | E1K_REG_CTRL_SLU); + E1kNetRegWrite32(Dev, E1K_REG_RAL, *(UINT32 *)&Dev->Snm.CurrentAddress.Addr[0]); + E1kNetRegWrite32(Dev, E1K_REG_RAH, (*(UINT32 *)&Dev->Snm.CurrentAddress.Addr[4]) | E1K_REG_RAH_AV); + + Status = E1kNetInitTx (Dev); + if (EFI_ERROR (Status)) { + goto AbortDevice; + } + + // + // start receiving + // + Status = E1kNetInitRx (Dev); + if (EFI_ERROR (Status)) { + goto ReleaseTxAux; + } + + Dev->Snm.State = EfiSimpleNetworkInitialized; + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +ReleaseTxAux: + E1kNetShutdownTx (Dev); + +AbortDevice: + E1kNetDevReset(Dev); + +InitFailed: + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpMcastIpToMac.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpMcastIpToMac.c new file mode 100644 index 00000000..01508ee9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpMcastIpToMac.c @@ -0,0 +1,103 @@ +/** @file + + Implementation of the SNP.McastIpToMac() function and its private helpers if + any. + + Copyright (c) 2021, Oracle and/or its affiliates.<BR> + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Converts a multicast IP address to a multicast HW MAC address. + + @param This The protocol instance pointer. + @param IPv6 Set to TRUE if the multicast IP address is IPv6 [RFC 2460]. Set + to FALSE if the multicast IP address is IPv4 [RFC 791]. + @param IP The multicast IP address that is to be converted to a multicast + HW MAC address. + @param MAC The multicast HW MAC address that is to be generated from IP. + + @retval EFI_SUCCESS The multicast IP address was mapped to the + multicast HW MAC address. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The + current buffer size needed to hold the + statistics is returned in StatisticsSize. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetMcastIpToMac ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN IPv6, + IN EFI_IP_ADDRESS *Ip, + OUT EFI_MAC_ADDRESS *Mac + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + + // + // http://en.wikipedia.org/wiki/Multicast_address + // + if (This == NULL || Ip == NULL || Mac == NULL || + ( IPv6 && (Ip->v6.Addr[0] ) != 0xFF) || // invalid IPv6 mcast addr + (!IPv6 && (Ip->v4.Addr[0] & 0xF0) != 0xE0) // invalid IPv4 mcast addr + ) { + return EFI_INVALID_PARAMETER; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + switch (Dev->Snm.State) { + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto Exit; + case EfiSimpleNetworkStarted: + Status = EFI_DEVICE_ERROR; + goto Exit; + default: + break; + } + + // + // http://en.wikipedia.org/wiki/IP_multicast#Layer_2_delivery + // + if (IPv6) { + Mac->Addr[0] = 0x33; + Mac->Addr[1] = 0x33; + Mac->Addr[2] = Ip->v6.Addr[12]; + Mac->Addr[3] = Ip->v6.Addr[13]; + Mac->Addr[4] = Ip->v6.Addr[14]; + Mac->Addr[5] = Ip->v6.Addr[15]; + } + else { + Mac->Addr[0] = 0x01; + Mac->Addr[1] = 0x00; + Mac->Addr[2] = 0x5E; + Mac->Addr[3] = Ip->v4.Addr[1] & 0x7F; + Mac->Addr[4] = Ip->v4.Addr[2]; + Mac->Addr[5] = Ip->v4.Addr[3]; + } + Status = EFI_SUCCESS; + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceive.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceive.c new file mode 100644 index 00000000..0b8b0cbe --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceive.c @@ -0,0 +1,159 @@ +/** @file + + Implementation of the SNP.Receive() function and its private helpers if any. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Receives a packet from a network interface. + + @param This The protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header received on the + network interface. If this parameter is NULL, then the + media header size will not be returned. + @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the + size, in bytes, of the packet that was received on the + network interface. + @param Buffer A pointer to the data buffer to receive both the media + header and the data. + @param SrcAddr The source HW MAC address. If this parameter is NULL, the + HW MAC source address will not be extracted from the media + header. + @param DestAddr The destination HW MAC address. If this parameter is NULL, + the HW MAC destination address will not be extracted from + the media header. + @param Protocol The media header type. If this parameter is NULL, then the + protocol will not be extracted from the media header. See + RFC 1700 section "Ether Types" for examples. + + @retval EFI_SUCCESS The received data was stored in Buffer, and + BufferSize has been updated to the number of + bytes received. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept + this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetReceive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINTN *HeaderSize OPTIONAL, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer, + OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL, + OUT UINT16 *Protocol OPTIONAL + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT32 RdhCur; + UINT32 RxLen; + UINTN OrigBufferSize; + EFI_PHYSICAL_ADDRESS BufferAddress; + UINT8 *RxPtr; + UINTN RxBufOffset; + + DEBUG((DEBUG_INFO, "E1kNetReceive: HeaderSize=%p BufferSize=%u Buffer=%p\n", + HeaderSize, *BufferSize, Buffer)); + + if (This == NULL || BufferSize == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + switch (Dev->Snm.State) { + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto Exit; + case EfiSimpleNetworkStarted: + Status = EFI_DEVICE_ERROR; + goto Exit; + default: + break; + } + + E1kNetRegRead32(Dev, E1K_REG_RDH, &RdhCur); + + if (Dev->RdhLastSeen == RdhCur) { + Status = EFI_NOT_READY; + goto Exit; + } + + RxLen = Dev->RxRing[Dev->RdhLastSeen].BufferLength; + // + // the host must not have filled in more data than requested + // + ASSERT (RxLen <= 2048); + + OrigBufferSize = *BufferSize; + *BufferSize = RxLen; + + if (OrigBufferSize < RxLen) { + Status = EFI_BUFFER_TOO_SMALL; + goto Exit; // keep the packet + } + + if (RxLen < Dev->Snm.MediaHeaderSize) { + Status = EFI_DEVICE_ERROR; + goto RecycleDesc; // drop useless short packet + } + + if (HeaderSize != NULL) { + *HeaderSize = Dev->Snm.MediaHeaderSize; + } + + BufferAddress = Dev->RxRing[Dev->RdhLastSeen].AddrBufferLow; + BufferAddress |= LShiftU64(Dev->RxRing[Dev->RdhLastSeen].AddrBufferHigh, 32); + RxBufOffset = (UINTN)(BufferAddress - Dev->RxBufDeviceBase); + RxPtr = Dev->RxBuf + RxBufOffset; + CopyMem (Buffer, RxPtr, RxLen); + + if (DestAddr != NULL) { + CopyMem (DestAddr, RxPtr, sizeof (E1K_NET_MAC)); + } + RxPtr += sizeof (E1K_NET_MAC); + + if (SrcAddr != NULL) { + CopyMem (SrcAddr, RxPtr, sizeof (E1K_NET_MAC)); + } + RxPtr += sizeof (E1K_NET_MAC); + + if (Protocol != NULL) { + *Protocol = (UINT16) ((RxPtr[0] << 8) | RxPtr[1]); + } + RxPtr += sizeof (UINT16); + + Status = EFI_SUCCESS; + +RecycleDesc: + Dev->RdhLastSeen = (Dev->RdhLastSeen + 1) % E1K_NET_MAX_PENDING; + E1kNetRegWrite32(Dev, E1K_REG_RDT, Dev->RdhLastSeen); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceiveFilters.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceiveFilters.c new file mode 100644 index 00000000..ce559f40 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceiveFilters.c @@ -0,0 +1,100 @@ +/** @file + + Implementation of the SNP.ReceiveFilters() function and its private helpers + if any. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Manages the multicast receive filters of a network interface. + + @param This The protocol instance pointer. + @param Enable A bit mask of receive filters to enable on the + network interface. + @param Disable A bit mask of receive filters to disable on the + network interface. + @param ResetMCastFilter Set to TRUE to reset the contents of the multicast + receive filters on the network interface to their + default values. + @param McastFilterCnt Number of multicast HW MAC addresses in the new + MCastFilter list. This value must be less than or + equal to the MCastFilterCnt field of + EFI_SIMPLE_NETWORK_MODE. This field is optional if + ResetMCastFilter is TRUE. + @param MCastFilter A pointer to a list of new multicast receive filter + HW MAC addresses. This list will replace any + existing multicast HW MAC address list. This field + is optional if ResetMCastFilter is TRUE. + + @retval EFI_SUCCESS The multicast receive filter list was updated. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetReceiveFilters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINT32 Enable, + IN UINT32 Disable, + IN BOOLEAN ResetMCastFilter, + IN UINTN MCastFilterCnt OPTIONAL, + IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + switch (Dev->Snm.State) { + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto Exit; + case EfiSimpleNetworkStarted: + Status = EFI_DEVICE_ERROR; + goto Exit; + default: + break; + } + + // + // MNP apparently fails to initialize on top of us if we simply return + // EFI_UNSUPPORTED in this function. + // + // Hence we openly refuse multicast functionality, and fake the rest by + // selecting a no stricter filter setting than whatever is requested. The + // UEFI-2.3.1+errC spec allows this. In practice we don't change our current + // (default) filter. Additionally, receiving software is responsible for + // discarding any packets getting through the filter. + // + Status = ( + ((Enable | Disable) & ~Dev->Snm.ReceiveFilterMask) != 0 || + (!ResetMCastFilter && MCastFilterCnt > Dev->Snm.MaxMCastFilterCount) + ) ? EFI_INVALID_PARAMETER : EFI_SUCCESS; + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpSharedHelpers.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpSharedHelpers.c new file mode 100644 index 00000000..01a02d7a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpSharedHelpers.c @@ -0,0 +1,281 @@ +/** @file + + Helper functions used by at least two Simple Network Protocol methods. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/MemoryAllocationLib.h> + +#include "E1kNet.h" + +// +// The user structure for the ordered collection that will track the mapping +// info of the packets queued in TxRing +// +typedef struct { + VOID *Buffer; + EFI_PHYSICAL_ADDRESS DeviceAddress; // lookup key for reverse mapping + VOID *BufMap; +} TX_BUF_MAP_INFO; + +/** + Release RX and TX resources on the boundary of the + EfiSimpleNetworkInitialized state. + + These functions contribute to rolling back a partial, failed initialization + of the e1000 SNP driver instance, or to shutting down a fully + initialized, running instance. + + They are only callable by the E1kNetInitialize() and the + E1kNetShutdown() SNP methods. See the state diagram in "E1kNet.h". + + @param[in,out] Dev The E1K_NET_DEV driver instance being shut down, or whose + partial, failed initialization is being rolled back. +*/ + +VOID +EFIAPI +E1kNetShutdownRx ( + IN OUT E1K_NET_DEV *Dev + ) +{ + Dev->PciIo->Unmap (Dev->PciIo, Dev->RxMap); + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + Dev->RxBufNrPages, + Dev->RxRing + ); +} + + +VOID +EFIAPI +E1kNetShutdownTx ( + IN OUT E1K_NET_DEV *Dev + ) +{ + ORDERED_COLLECTION_ENTRY *Entry, *Entry2; + TX_BUF_MAP_INFO *TxBufMapInfo; + VOID *UserStruct; + + Dev->PciIo->Unmap (Dev->PciIo, Dev->TxRingMap); + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + EFI_SIZE_TO_PAGES (Dev->TxMaxPending * sizeof (*Dev->TxRing)), + Dev->TxRing + ); + + for (Entry = OrderedCollectionMin (Dev->TxBufCollection); + Entry != NULL; + Entry = Entry2) { + Entry2 = OrderedCollectionNext (Entry); + OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct); + TxBufMapInfo = UserStruct; + Dev->PciIo->Unmap (Dev->PciIo, TxBufMapInfo->BufMap); + FreePool (TxBufMapInfo); + } + OrderedCollectionUninit (Dev->TxBufCollection); +} + +/** + Map Caller-supplied TxBuf buffer to the device-mapped address + + @param[in] Dev The E1K_NET_DEV driver instance which wants to + map the Tx packet. + @param[in] Buffer The system physical address of TxBuf + @param[in] NumberOfBytes Number of bytes to map + @param[out] DeviceAddress The resulting device address for the bus + master access. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to + a lack of resources. + @return Status codes from + VirtioMapAllBytesInSharedBuffer() + @retval EFI_SUCCESS Caller-supplied buffer is successfully mapped. +*/ +EFI_STATUS +EFIAPI +E1kNetMapTxBuf ( + IN E1K_NET_DEV *Dev, + IN VOID *Buffer, + IN UINTN NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress + ) +{ + EFI_STATUS Status; + TX_BUF_MAP_INFO *TxBufMapInfo; + EFI_PHYSICAL_ADDRESS Address; + VOID *Mapping; + + TxBufMapInfo = AllocatePool (sizeof (*TxBufMapInfo)); + if (TxBufMapInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Dev->PciIo->Map ( + Dev->PciIo, + EfiPciIoOperationBusMasterRead, + Buffer, + &NumberOfBytes, + &Address, + &Mapping + ); + if (EFI_ERROR (Status)) { + goto FreeTxBufMapInfo; + } + + TxBufMapInfo->Buffer = Buffer; + TxBufMapInfo->DeviceAddress = Address; + TxBufMapInfo->BufMap = Mapping; + + Status = OrderedCollectionInsert ( + Dev->TxBufCollection, + NULL, + TxBufMapInfo + ); + switch (Status) { + case EFI_OUT_OF_RESOURCES: + goto UnmapTxBuf; + case EFI_ALREADY_STARTED: + // + // This should never happen: it implies + // + // - an identity-mapping VIRTIO_DEVICE_PROTOCOL.MapSharedBuffer() + // implementation -- which is fine, + // + // - and an SNP client that queues multiple instances of the exact same + // buffer address with SNP.Transmit() -- which is undefined behavior, + // based on the TxBuf language in UEFI-2.7, + // EFI_SIMPLE_NETWORK.GetStatus(). + // + ASSERT (FALSE); + Status = EFI_INVALID_PARAMETER; + goto UnmapTxBuf; + default: + ASSERT_EFI_ERROR (Status); + break; + } + + *DeviceAddress = Address; + return EFI_SUCCESS; + +UnmapTxBuf: + Dev->PciIo->Unmap (Dev->PciIo, Mapping); + +FreeTxBufMapInfo: + FreePool (TxBufMapInfo); + return Status; +} + +/** + Unmap (aka reverse mapping) device mapped TxBuf buffer to the system + physical address + + @param[in] Dev The E1K_NET_DEV driver instance which wants to + reverse- and unmap the Tx packet. + @param[out] Buffer The system physical address of TxBuf + @param[in] DeviceAddress The device address for the TxBuf + + @retval EFI_INVALID_PARAMETER The DeviceAddress is not mapped + @retval EFI_SUCCESS The TxBuf at DeviceAddress has been unmapped, + and Buffer has been set to TxBuf's system + physical address. + +*/ +EFI_STATUS +EFIAPI +E1kNetUnmapTxBuf ( + IN E1K_NET_DEV *Dev, + OUT VOID **Buffer, + IN EFI_PHYSICAL_ADDRESS DeviceAddress + ) +{ + ORDERED_COLLECTION_ENTRY *Entry; + TX_BUF_MAP_INFO *TxBufMapInfo; + VOID *UserStruct; + + Entry = OrderedCollectionFind (Dev->TxBufCollection, &DeviceAddress); + if (Entry == NULL) { + return EFI_INVALID_PARAMETER; + } + + OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct); + + TxBufMapInfo = UserStruct; + + *Buffer = TxBufMapInfo->Buffer; + Dev->PciIo->Unmap (Dev->PciIo, TxBufMapInfo->BufMap); + FreePool (TxBufMapInfo); + + return EFI_SUCCESS; +} + +/** + Comparator function for two TX_BUF_MAP_INFO objects. + + @param[in] UserStruct1 Pointer to the first TX_BUF_MAP_INFO object. + + @param[in] UserStruct2 Pointer to the second TX_BUF_MAP_INFO object. + + @retval <0 If UserStruct1 compares less than UserStruct2. + + @retval 0 If UserStruct1 compares equal to UserStruct2. + + @retval >0 If UserStruct1 compares greater than UserStruct2. +*/ +INTN +EFIAPI +E1kNetTxBufMapInfoCompare ( + IN CONST VOID *UserStruct1, + IN CONST VOID *UserStruct2 + ) +{ + CONST TX_BUF_MAP_INFO *MapInfo1; + CONST TX_BUF_MAP_INFO *MapInfo2; + + MapInfo1 = UserStruct1; + MapInfo2 = UserStruct2; + + return MapInfo1->DeviceAddress < MapInfo2->DeviceAddress ? -1 : + MapInfo1->DeviceAddress > MapInfo2->DeviceAddress ? 1 : + 0; +} + +/** + Compare a standalone DeviceAddress against a TX_BUF_MAP_INFO object + containing an embedded DeviceAddress. + + @param[in] StandaloneKey Pointer to DeviceAddress, which has type + EFI_PHYSICAL_ADDRESS. + + @param[in] UserStruct Pointer to the TX_BUF_MAP_INFO object with the + embedded DeviceAddress. + + @retval <0 If StandaloneKey compares less than UserStruct's key. + + @retval 0 If StandaloneKey compares equal to UserStruct's key. + + @retval >0 If StandaloneKey compares greater than UserStruct's key. +**/ +INTN +EFIAPI +E1kNetTxBufDeviceAddressCompare ( + IN CONST VOID *StandaloneKey, + IN CONST VOID *UserStruct + ) +{ + CONST EFI_PHYSICAL_ADDRESS *DeviceAddress; + CONST TX_BUF_MAP_INFO *MapInfo; + + DeviceAddress = StandaloneKey; + MapInfo = UserStruct; + + return *DeviceAddress < MapInfo->DeviceAddress ? -1 : + *DeviceAddress > MapInfo->DeviceAddress ? 1 : + 0; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpShutdown.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpShutdown.c new file mode 100644 index 00000000..d4dede01 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpShutdown.c @@ -0,0 +1,74 @@ +/** @file + + Implementation of the SNP.Shutdown() function and its private helpers if any. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (c) 2017, AMD Inc, All rights reserved. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Resets a network adapter and leaves it in a state that is safe for another + driver to initialize. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The network interface was shutdown. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetShutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + + DEBUG((DEBUG_INFO, "E1kNetShutdown:\n")); + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + switch (Dev->Snm.State) { + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto Exit; + case EfiSimpleNetworkStarted: + Status = EFI_DEVICE_ERROR; + goto Exit; + default: + break; + } + + E1kNetDevReset(Dev); + E1kNetShutdownRx (Dev); + E1kNetShutdownTx (Dev); + + Dev->Snm.State = EfiSimpleNetworkStarted; + Status = EFI_SUCCESS; + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStart.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStart.c new file mode 100644 index 00000000..7dd48081 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStart.c @@ -0,0 +1,59 @@ +/** @file + + Implementation of the SNP.Start() function and its private helpers if any. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Changes the state of a network interface from "stopped" to "started". + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The network interface was started. + @retval EFI_ALREADY_STARTED The network interface is already in the started + state. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. +**/ + +EFI_STATUS +EFIAPI +E1kNetStart ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (Dev->Snm.State != EfiSimpleNetworkStopped) { + Status = EFI_ALREADY_STARTED; + } + else { + Dev->Snm.State = EfiSimpleNetworkStarted; + Status = EFI_SUCCESS; + } + + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStop.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStop.c new file mode 100644 index 00000000..8f564ebb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStop.c @@ -0,0 +1,60 @@ +/** @file + + Implementation of the SNP.Stop() function and its private helpers if any. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Changes the state of a network interface from "started" to "stopped". + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The network interface was stopped. + @retval EFI_ALREADY_STARTED The network interface is already in the stopped + state. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetStop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (Dev->Snm.State != EfiSimpleNetworkStarted) { + Status = EFI_NOT_STARTED; + } + else { + Dev->Snm.State = EfiSimpleNetworkStopped; + Status = EFI_SUCCESS; + } + + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpTransmit.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpTransmit.c new file mode 100644 index 00000000..6dead2b0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpTransmit.c @@ -0,0 +1,170 @@ +/** @file + + Implementation of the SNP.Transmit() function and its private helpers if any. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#include "E1kNet.h" + +/** + Places a packet in the transmit queue of a network interface. + + @param This The protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then + it must be equal to This->Mode->MediaHeaderSize and the + DestAddr and Protocol parameters must not be NULL. + @param BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param Buffer A pointer to the packet (media header followed by data) to + be transmitted. This parameter cannot be NULL. If + HeaderSize is zero, then the media header in Buffer must + already be filled in by the caller. If HeaderSize is + non-zero, then the media header will be filled in by the + Transmit() function. + @param SrcAddr The source HW MAC address. If HeaderSize is zero, then + this parameter is ignored. If HeaderSize is non-zero and + SrcAddr is NULL, then This->Mode->CurrentAddress is used + for the source HW MAC address. + @param DestAddr The destination HW MAC address. If HeaderSize is zero, + then this parameter is ignored. + @param Protocol The type of header to build. If HeaderSize is zero, then + this parameter is ignored. See RFC 1700, section "Ether + Types", for examples. + + @retval EFI_SUCCESS The packet was placed on the transmit queue. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept + this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetTransmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN /* +OUT! */ VOID *Buffer, + IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + IN EFI_MAC_ADDRESS *DestAddr OPTIONAL, + IN UINT16 *Protocol OPTIONAL + ) +{ + E1K_NET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS DeviceAddress; + + DEBUG((DEBUG_INFO, "E1kNetTransmit: HeaderSize=%u BufferSize=%u Buffer=%p\n", + HeaderSize, BufferSize, Buffer)); + + if (This == NULL || BufferSize == 0 || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = E1K_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + switch (Dev->Snm.State) { + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto Exit; + case EfiSimpleNetworkStarted: + Status = EFI_DEVICE_ERROR; + goto Exit; + default: + break; + } + + if (BufferSize < Dev->Snm.MediaHeaderSize) { + Status = EFI_BUFFER_TOO_SMALL; + goto Exit; + } + if (BufferSize > Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // check if we have room for transmission + // + ASSERT (Dev->TxCurPending <= Dev->TxMaxPending); + if (Dev->TxCurPending == Dev->TxMaxPending) { + Status = EFI_NOT_READY; + goto Exit; + } + + // + // the caller may want us to fill in the media header: + // dst MAC, src MAC, Ethertype + // + if (HeaderSize != 0) { + UINT8 *Ptr; + + if (HeaderSize != Dev->Snm.MediaHeaderSize || + DestAddr == NULL || Protocol == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + Ptr = Buffer; + ASSERT (sizeof (E1K_NET_MAC) <= sizeof (EFI_MAC_ADDRESS)); + + CopyMem (Ptr, DestAddr, sizeof (E1K_NET_MAC)); + Ptr += sizeof (E1K_NET_MAC); + + CopyMem (Ptr, + (SrcAddr == NULL) ? &Dev->Snm.CurrentAddress : SrcAddr, + sizeof (E1K_NET_MAC)); + Ptr += sizeof (E1K_NET_MAC); + + *Ptr++ = (UINT8) (*Protocol >> 8); + *Ptr++ = (UINT8) *Protocol; + + ASSERT ((UINTN) (Ptr - (UINT8 *) Buffer) == Dev->Snm.MediaHeaderSize); + } + + // + // Map the transmit buffer system physical address to device address. + // + Status = E1kNetMapTxBuf ( + Dev, + Buffer, + BufferSize, + &DeviceAddress + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Dev->TxCurPending++; + Dev->TxRing[Dev->TxLastUsed].AddrBufferLow = (UINT32)DeviceAddress; + Dev->TxRing[Dev->TxLastUsed].AddrBufferHigh = (UINT32)RShiftU64(DeviceAddress, 32); + Dev->TxRing[Dev->TxLastUsed].BufferLength = (UINT16)BufferSize; + Dev->TxRing[Dev->TxLastUsed].Status = 0; + Dev->TxRing[Dev->TxLastUsed].Command = E1K_TX_CMD_EOP | E1K_TX_CMD_FCS | E1K_TX_CMD_RS; + + Dev->TxLastUsed = (Dev->TxLastUsed + 1) % E1K_NET_MAX_PENDING; + E1kNetRegWrite32(Dev, E1K_REG_TDT, Dev->TxLastUsed); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpUnsupported.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpUnsupported.c new file mode 100644 index 00000000..d66594e2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpUnsupported.c @@ -0,0 +1,155 @@ +/** @file + + Empty implementation of the SNP methods that dependent protocols don't + absolutely need and the UEFI-2.3.1+errC specification allows us not to + support. + + Copyright (c) 2021, Oracle and/or its affiliates. + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "E1kNet.h" + +/** + Resets a network adapter and re-initializes it with the parameters that were + provided in the previous call to Initialize(). + + @param This The protocol instance pointer. + @param ExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS The network interface was reset. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetReset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Modifies or resets the current station address, if supported. + + @param This The protocol instance pointer. + @param Reset Flag used to reset the station address to the network + interfaces permanent address. + @param New The new station address to be used for the network interface. + + @retval EFI_SUCCESS The network interfaces station address was + updated. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetStationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN Reset, + IN EFI_MAC_ADDRESS *New OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Resets or collects the statistics on a network interface. + + @param This Protocol instance pointer. + @param Reset Set to TRUE to reset the statistics for the network + interface. + @param StatisticsSize On input the size, in bytes, of StatisticsTable. On + output the size, in bytes, of the resulting table of + statistics. + @param StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure + that contains the statistics. + + @retval EFI_SUCCESS The statistics were collected from the network + interface. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The + current buffer size needed to hold the + statistics is returned in StatisticsSize. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetStatistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN Reset, + IN OUT UINTN *StatisticsSize OPTIONAL, + OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Performs read and write operations on the NVRAM device attached to a network + interface. + + @param This The protocol instance pointer. + @param ReadWrite TRUE for read operations, FALSE for write operations. + @param Offset Byte offset in the NVRAM device at which to start the read + or write operation. This must be a multiple of + NvRamAccessSize and less than NvRamSize. + @param BufferSize The number of bytes to read or write from the NVRAM + device. This must also be a multiple of NvramAccessSize. + @param Buffer A pointer to the data buffer. + + @retval EFI_SUCCESS The NVRAM access was performed. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +E1kNetNvData ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ReadWrite, + IN UINTN Offset, + IN UINTN BufferSize, + IN OUT VOID *Buffer + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/DevEFI.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/DevEFI.h new file mode 100644 index 00000000..203ea08c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/DevEFI.h @@ -0,0 +1,38 @@ +/* $Id: DevEFI.h $ */ +/** @file + * EFI for VirtualBox Common Definitions. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#include "../../../DevEFI.h" + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/Guid/VBoxFsBlessedFileInfo.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/Guid/VBoxFsBlessedFileInfo.h new file mode 100644 index 00000000..7eb7e617 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/Guid/VBoxFsBlessedFileInfo.h @@ -0,0 +1,58 @@ +/* $Id */ +/** @file + * Provides a GUID and a data structure that can be used with EFI_FILE_PROTOCOL.GetInfo() + * or EFI_FILE_PROTOCOL.SetInfo() to get or set the system's volume blessed file (for booting). + */ + +/* + * Copyright (C) 2019-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef __VBOX_FS_BLESSED_FILE_INFO_H__ +#define __VBOX_FS_BLESSED_FILE_INFO_H__ + +#define VBOX_FS_BLESSED_FILE_ID \ + { \ + 0xCC49FEFD, 0x41B7, 0x9823, { 0x98, 0x23, 0x0E, 0x8E, 0xBF, 0x35, 0x67, 0x7D } \ + } + +typedef struct { + /// + /// The Null-terminated string that is the volume's label. + /// + CHAR16 BlessedFile[1]; +} VBOX_FS_BLESSED_FILE; + +#define SIZE_OF_VBOX_FS_BLESSED_FILE \ + OFFSET_OF (VBOX_FS_BLESSED_FILE, BlessedFile) + +extern EFI_GUID gVBoxFsBlessedFileInfoGuid; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/IndustryStandard/VBoxFatImage.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/IndustryStandard/VBoxFatImage.h new file mode 100644 index 00000000..39656c6a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/IndustryStandard/VBoxFatImage.h @@ -0,0 +1,64 @@ +/** @file + * FAT binary definitions. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/** @todo Rename this file and/or reintegrate the FAT changes into MdePkg. */ +#ifndef ___VBoxFatImage_h +#define ___VBoxFatImage_h + +#define EFI_TE_IMAGE_HEADER_SIGNATURE SIGNATURE_16('V', 'Z') +typedef struct _EFI_FAT_IMAGE_HEADER +{ + UINT32 Signature; + UINT32 NFatArch; +} EFI_FAT_IMAGE_HEADER; + +#define EFI_FAT_IMAGE_HEADER_SIGNATURE 0xef1fab9 /* Note: it's deiffer from 0xcafebabe */ +typedef struct _EFI_FAT_IMAGE_HEADER_NLIST +{ + UINT32 CpuType; + UINT32 CpuSubType; + UINT32 Offset; + UINT32 Size; + UINT32 Align; +} EFI_FAT_IMAGE_HEADER_NLIST; + +#define EFI_FAT_CPU_TYPE_I386 0x7 +#define EFI_FAT_CPU_TYPE_X64 0x1000007 + +#define EFI_FAT_CPU_SUB_TYPE_PC 0x3 + + +#endif /* !___VBoxFatImage_h */ + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxDebugLib.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxDebugLib.h new file mode 100644 index 00000000..a734cc3d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxDebugLib.h @@ -0,0 +1,50 @@ +/* $Id: VBoxDebugLib.h $ */ +/** @file + * VBoxDebugLib.h - Debug and logging routines implemented by VBoxDebugLib. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef ___VBoxPkg_VBoxDebugLib_h +#define ___VBoxPkg_VBoxDebugLib_h + +#include <Uefi/UefiBaseType.h> +#include "VBoxPkg.h" + +size_t VBoxPrintChar(int ch); +size_t VBoxPrintGuid(CONST EFI_GUID *pGuid); +size_t VBoxPrintHex(UINT64 uValue, size_t cbType); +size_t VBoxPrintHexDump(const void *pv, size_t cb); +size_t VBoxPrintString(const char *pszString); + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxMemLayout.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxMemLayout.h new file mode 100644 index 00000000..1f8d41ed --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxMemLayout.h @@ -0,0 +1,44 @@ +/* $Id: VBoxMemLayout.h $ */ +/** @file + * VBoxMemLayout.h - Constants defining the memory layout. (Merge with DevEFI?) + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef ___VBoxMemLayout_h +#define ___VBoxMemLayout_h + +#define VBOXSEC_STACK_BASE (1*1024*1024) +#define VBOXSEC_STACK_SIZE (128*1024) + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxPkg.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxPkg.h new file mode 100644 index 00000000..ca15c417 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Include/VBoxPkg.h @@ -0,0 +1,69 @@ +/* $Id: VBoxPkg.h $ */ +/** @file + * VBoxPkg.h - Common header, must be include before IPRT and VBox headers. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef ___VBoxPkg_h +#define ___VBoxPkg_h + +/* + * IPRT configuration. + */ +#define IN_RING0 +/** @todo detect this */ +//#include <iprt/cdefs.h> +#if !defined(ARCH_BITS) || !defined(HC_ARCH_BITS) +//# error "please add right bitness" +#endif + +/* + * VBox and IPRT headers. + */ +#include <VBox/version.h> +#include <iprt/types.h> +#ifdef _MSC_VER +# pragma warning(disable: 4389) +# pragma warning(disable: 4245) +# pragma warning(disable: 4244) +#endif +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#ifdef _MSC_VER +# pragma warning(default: 4244) +# pragma warning(default: 4245) +# pragma warning(default: 4389) +#endif + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugAgentLib/VBoxDebugAgentLib.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugAgentLib/VBoxDebugAgentLib.c new file mode 100644 index 00000000..85ad614b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugAgentLib/VBoxDebugAgentLib.c @@ -0,0 +1,97 @@ +/* $Id: VBoxDebugAgentLib.c $ */ +/** @file + * VBox implementation of DebugAgentLib that reports EFI state transitions + * to DevEFI for debugging purposes. + */ + +/* + * Copyright (C) 2013-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Base.h> +#include <Library/DebugAgentLib.h> +#include <Library/DebugLib.h> +#include "VBoxPkg.h" +#include "../../../../DevEFI.h" + + +VOID +EFIAPI +InitializeDebugAgent( + IN UINT32 InitFlag, + IN VOID *Context OPTIONAL, + IN DEBUG_AGENT_CONTINUE Function OPTIONAL + ) +{ + /* + * Do the reporting. + */ + EFIDBGPOINT enmDbgPoint; + switch (InitFlag) + { + case DEBUG_AGENT_INIT_PREMEM_SEC: enmDbgPoint = EFIDBGPOINT_SEC_PREMEM; break; + case DEBUG_AGENT_INIT_POSTMEM_SEC: enmDbgPoint = EFIDBGPOINT_SEC_POSTMEM; break; + case DEBUG_AGENT_INIT_DXE_CORE: enmDbgPoint = EFIDBGPOINT_DXE_CORE; break; + case DEBUG_AGENT_INIT_SMM: enmDbgPoint = EFIDBGPOINT_SMM; break; + case DEBUG_AGENT_INIT_ENTER_SMI: enmDbgPoint = EFIDBGPOINT_SMI_ENTER; break; + case DEBUG_AGENT_INIT_EXIT_SMI: enmDbgPoint = EFIDBGPOINT_SMI_EXIT; break; + case DEBUG_AGENT_INIT_S3: enmDbgPoint = EFIDBGPOINT_GRAPHICS; break; + case DEBUG_AGENT_INIT_DXE_AP: enmDbgPoint = EFIDBGPOINT_DXE_AP; break; + default: + ASSERT(false); + enmDbgPoint = EFIDBGPOINT_INVALID; + break; + } + if (enmDbgPoint != EFIDBGPOINT_INVALID) + ASMOutU32(EFI_PORT_DEBUG_POINT, enmDbgPoint); + + + /* + * Call resume function if supplied. + */ + if (Function) + Function(Context); +} + + +BOOLEAN +EFIAPI +SaveAndSetDebugTimerInterrupt( + IN BOOLEAN EnableStatus + ) +{ + NOREF(EnableStatus); + return FALSE; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugAgentLib/VBoxDebugAgentLib.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugAgentLib/VBoxDebugAgentLib.inf new file mode 100644 index 00000000..c57f8496 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugAgentLib/VBoxDebugAgentLib.inf @@ -0,0 +1,54 @@ +# $Id: VBoxDebugAgentLib.inf $ +## @file +# VBox implementation of DebugAgentLib that reports EFI state transitions +# to DevEFI (for debugging purposes). +# + +# +# Copyright (C) 2013-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxDebugAgentLib + FILE_GUID = A13AC320-4717-99C4-B7FF-53A7D84326B0 + MODULE_TYPE = BASE + VERSION_STRING = 4.3 + LIBRARY_CLASS = DebugAgentLib + +[Sources.common] + VBoxDebugAgentLib.c + +[Packages] + VBoxPkg/VBoxPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxDebugLib.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxDebugLib.c new file mode 100644 index 00000000..6ad6f542 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxDebugLib.c @@ -0,0 +1,227 @@ +/* $Id: VBoxDebugLib.c $ */ +/** @file + * VBoxDebugLib.c - Debug logging and assertions support routines using DevEFI. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Base.h> +#include <Library/BaseLib.h> +#include <Library/PrintLib.h> +#include <Library/DebugLib.h> + +#include "VBoxDebugLib.h" +#include <Protocol/DevicePath.h> +#include <Protocol/DevicePathToText.h> +#include <Uefi/UefiSpec.h> +#include <Library/UefiBootServicesTableLib.h> +#include "DevEFI.h" +#include "iprt/asm.h" + + +VOID EFIAPI +DebugPrint(IN UINTN ErrorLevel, IN CONST CHAR8 *Format, ...) +{ + CHAR8 szBuf[256]; + VA_LIST va; + UINTN cch; + BOOLEAN InterruptState; + + /* No pool noise, please. */ + if (ErrorLevel == DEBUG_POOL) + return; + + VA_START(va, Format); + cch = AsciiVSPrint(szBuf, sizeof(szBuf), Format, va); + VA_END(va); + + /* make sure it's terminated and doesn't end with a newline */ + if (cch >= sizeof(szBuf)) + cch = sizeof(szBuf) - 1; + while (cch > 0 && (szBuf[cch - 1] == '\n' || szBuf[cch - 1] == '\r')) + cch--; + szBuf[cch] = '\0'; + + InterruptState = SaveAndDisableInterrupts(); + + /* Output the log string. */ + VBoxPrintString("dbg/"); + VBoxPrintHex(ErrorLevel, sizeof(ErrorLevel)); + VBoxPrintChar(' '); + VBoxPrintString(szBuf); + VBoxPrintChar('\n'); + + SetInterruptState(InterruptState); +} + +/** + * Our own log worker function, avoid the dbg/00000xxx prefix and makes it clear + * which log statements we added.. + * + * @param pszFormat Format string. EFI style! + * @param ... Argument referneced in the format string. + */ +VOID EFIAPI +VBoxLogWorker(const char *pszFormat, ...) +{ + CHAR8 szBuf[384]; + VA_LIST va; + BOOLEAN InterruptState; + + /* Format it. */ + VA_START(va, pszFormat); + AsciiVSPrint(szBuf, sizeof(szBuf), pszFormat, va); + VA_END(va); + szBuf[sizeof(szBuf) - 1] = '\0'; + + InterruptState = SaveAndDisableInterrupts(); + + /* Output the log string. */ + VBoxPrintString(szBuf); + VBoxPrintChar('\n'); + + SetInterruptState(InterruptState); +} + +/** + * Adds a character to the panic message. + * + * @param ch The ASCII char to add. + */ +static void +VBoxPanicMsgChar(int ch) +{ + ASMOutU16(EFI_PANIC_PORT, EFI_PANIC_CMD_MSG_FROM_CHAR(ch)); +} + +/** + * Adds a string to the panic message. + * + * @param pszString The string to add. + */ +static void +VBoxPanicMsgString(const char *pszString) +{ + char ch; + while ((ch = *pszString++) != '\0') + VBoxPanicMsgChar(ch); +} + +/** + * Adds a unsigned decimal number to the panic message. + * + * @param uValue The value. + */ +static void +VBoxPanicMsgDecimalU32(uint32_t uValue) +{ + char szTmp[32]; + unsigned off = sizeof(szTmp) - 1; + + szTmp[off] = '\0'; + do + { + char chDigit = uValue % 10; + uValue /= 10; + szTmp[--off] = chDigit + '0'; + } while (uValue != 0 && off > 0); + + VBoxPanicMsgString(&szTmp[off]); +} + +VOID EFIAPI +DebugAssert(IN CONST CHAR8 *FileName, IN UINTN LineNumber, IN CONST CHAR8 *Description) +{ + BOOLEAN InterruptState = SaveAndDisableInterrupts(); + + ASMOutU8(EFI_PANIC_PORT, EFI_PANIC_CMD_START_MSG); + VBoxPanicMsgString("EFI Assertion failed!" + "\nFile: "); + VBoxPanicMsgString(FileName ? FileName : "<NULL>"); + VBoxPanicMsgString("\nLine: "); + VBoxPanicMsgDecimalU32((uint32_t)LineNumber); + VBoxPanicMsgString("\nDescription: "); + VBoxPanicMsgString(Description ? Description : "<NULL>"); + ASMOutU8(EFI_PANIC_PORT, EFI_PANIC_CMD_END_MSG); + + SetInterruptState(InterruptState); +} + + +VOID * EFIAPI +DebugClearMemory(OUT VOID *Buffer, IN UINTN Length) +{ + return Buffer; +} + + +BOOLEAN EFIAPI +DebugAssertEnabled(VOID) +{ + return TRUE; +} + + +BOOLEAN EFIAPI +DebugPrintEnabled(VOID) +{ + /** @todo some PCD for this so we can disable it in release builds. */ + return TRUE; +} + + +BOOLEAN EFIAPI +DebugPrintLevelEnabled(IN CONST UINTN ErrorLevel) +{ + /** @todo some PCD for this so we can disable it in release builds. */ + return TRUE; +} + + +BOOLEAN EFIAPI +DebugCodeEnabled(VOID) +{ + /** @todo ditto */ + return TRUE; +} + + +BOOLEAN EFIAPI +DebugClearMemoryEnabled(VOID) +{ + return FALSE; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxDebugLib.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxDebugLib.inf new file mode 100644 index 00000000..2eb372b8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxDebugLib.inf @@ -0,0 +1,85 @@ +# $Id: VBoxDebugLib.inf $ +## @file +# VBoxDebugLib - Debug logging and assertions support routines using DevEFI. +# + + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + + + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxDebugLib + FILE_GUID = 549306AB-75C1-4585-8681-556EBB34C60C + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = DebugLib + +[Sources.common] + VBoxDebugLib.c + VBoxPrintChar.c + VBoxPrintGuid.c + VBoxPrintHex.c + VBoxPrintHexDump.c + VBoxPrintString.c + +[Packages] + MdePkg/MdePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + PrintLib + +[Protocols] + gEfiDevicePathToTextProtocolGuid + +[Guids] + # none + +[Ppis] + # none + +[Guids] + # none + +[Pcd.common] + # none + +[FixedPcd.common] + # none + +[FeaturePcd.common] + # none + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintChar.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintChar.c new file mode 100644 index 00000000..74cbc1cc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintChar.c @@ -0,0 +1,56 @@ +/* $Id: VBoxPrintChar.c $ */ +/** @file + * VBoxPrintChar.c - Implementation of the VBoxPrintChar() debug logging routine. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxDebugLib.h" +#include "DevEFI.h" +#include "iprt/asm.h" + + +/** + * Prints a char. + * @returns 1 + * @param ch The char to print. + */ +size_t VBoxPrintChar(int ch) +{ + ASMOutU8(EFI_DEBUG_PORT, (uint8_t)ch); + return 1; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintGuid.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintGuid.c new file mode 100644 index 00000000..a1794cd7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintGuid.c @@ -0,0 +1,73 @@ +/* $Id: VBoxPrintGuid.c $ */ +/** @file + * VBoxPrintGuid.c - Implementation of the VBoxPrintGuid() debug logging routine. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxDebugLib.h" +#include "DevEFI.h" + + +/** + * Prints a EFI GUID. + * + * @returns Number of bytes printed. + * + * @param pGuid The GUID to print + */ +size_t VBoxPrintGuid(CONST EFI_GUID *pGuid) +{ + VBoxPrintHex(pGuid->Data1, sizeof(pGuid->Data1)); + VBoxPrintChar('-'); + VBoxPrintHex(pGuid->Data2, sizeof(pGuid->Data2)); + VBoxPrintChar('-'); + VBoxPrintHex(pGuid->Data3, sizeof(pGuid->Data3)); + VBoxPrintChar('-'); + VBoxPrintHex(pGuid->Data4[0], sizeof(pGuid->Data4[0])); + VBoxPrintHex(pGuid->Data4[1], sizeof(pGuid->Data4[1])); + VBoxPrintChar('-'); + VBoxPrintHex(pGuid->Data4[2], sizeof(pGuid->Data4[2])); + VBoxPrintHex(pGuid->Data4[3], sizeof(pGuid->Data4[3])); + VBoxPrintHex(pGuid->Data4[4], sizeof(pGuid->Data4[4])); + VBoxPrintHex(pGuid->Data4[5], sizeof(pGuid->Data4[5])); + VBoxPrintHex(pGuid->Data4[6], sizeof(pGuid->Data4[6])); + VBoxPrintHex(pGuid->Data4[7], sizeof(pGuid->Data4[7])); + return 37; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintHex.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintHex.c new file mode 100644 index 00000000..92284385 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintHex.c @@ -0,0 +1,110 @@ +/* $Id: VBoxPrintHex.c $ */ +/** @file + * VBoxPrintHex.c - Implementation of the VBoxPrintHex() debug logging routine. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Library/BaseLib.h> + +#include "VBoxDebugLib.h" +#include "DevEFI.h" +#include "iprt/asm.h" + + +/** + * Prints a char. + * @param ch The char to print. + */ +DECLINLINE(void) vboxPrintHexChar(int ch) +{ + ASMOutU8(EFI_DEBUG_PORT, (uint8_t)ch); +} + + +/** + * Print a hex number, up to 64-bit long. + * + * @returns Number of chars printed. + * + * @param uValue The value. + * @param cbType The size of the value type. + */ +size_t VBoxPrintHex(UINT64 uValue, size_t cbType) +{ + static const char s_szHex[17] = "0123456789abcdef"; + switch (cbType) + { +/* + * We have to cast the result to UINTN before indexing into the array + * or cl.exe insists on generating a call to __allmul for unoptimized 32bit builds, + * see: https://patchew.org/EDK2/1486606121-226912-1-git-send-email-dandan.bi@intel.com/ + */ +#define VAL_NIBBLE_EXTRACT(a_uValue, a_iNibbleStart) (s_szHex[(UINTN)(RShiftU64((a_uValue), (a_iNibbleStart)) & 0xf)]) + case 8: + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 60)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 56)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 52)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 48)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 44)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 40)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 36)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 32)); + case 4: + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 28)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 24)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 20)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 16)); + case 2: + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 12)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 8)); + case 1: + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 4)); + vboxPrintHexChar(VAL_NIBBLE_EXTRACT(uValue, 0)); + break; +#undef VAL_NIBBLE_EXTRACT + } + +#if 0 /* There is no MultU32x32 for 32bit and cl insists on emitting __allmul otherwise so we just hardcode everything here... */ + return cbType * 2; +#else + static size_t s_acbPrinted[9] = { 0, 2, 4, 0, 8, 0, 0, 0, 16}; + if (cbType < RT_ELEMENTS(s_acbPrinted)) + return s_acbPrinted[cbType]; + return 0; +#endif +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintHexDump.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintHexDump.c new file mode 100644 index 00000000..516dc4db --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintHexDump.c @@ -0,0 +1,111 @@ +/* $Id: VBoxPrintHexDump.c $ */ +/** @file + * VBoxPrintHex.c - Implementation of the VBoxPrintHex() debug logging routine. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxDebugLib.h" +#include "DevEFI.h" +#include "iprt/asm.h" +#include "iprt/ctype.h" + + +/** + * Prints a char. + * @returns 1. + * @param ch The char to print. + */ +DECLINLINE(int) vboxPrintHexDumpChar(int ch) +{ + ASMOutU8(EFI_DEBUG_PORT, (uint8_t)ch); + return 1; +} + + +/** + * Prints a hex dump the specified memory block. + * + * @returns Number of bytes printed. + * + * @param pv The memory to dump. + * @param cb Number of bytes to dump. + */ +size_t VBoxPrintHexDump(const void *pv, size_t cb) +{ + size_t cchPrinted = 0; + uint8_t const *pb = (uint8_t const *)pv; + while (cb > 0) + { + unsigned i; + + /* the offset */ + cchPrinted += VBoxPrintHex((uintptr_t)pb, sizeof(pb)); + cchPrinted += VBoxPrintString(" "); + + /* the hex bytes value. */ + for (i = 0; i < 16; i++) + { + cchPrinted += vboxPrintHexDumpChar(i == 7 ? '-' : ' '); + if (i < cb) + cchPrinted += VBoxPrintHex(pb[i], 1); + else + cchPrinted += VBoxPrintString(" "); + } + + /* the printable chars */ + cchPrinted += VBoxPrintString(" "); + for (i = 0; i < 16 && i < cb; i++) + cchPrinted += vboxPrintHexDumpChar(RT_C_IS_PRINT(pb[i]) + ? pb[i] + : '.'); + + /* finally, the new line. */ + cchPrinted += vboxPrintHexDumpChar('\n'); + + /* + * Advance. + */ + if (cb <= 16) + break; + cb -= 16; + pb += 16; + } + + return cchPrinted; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintString.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintString.c new file mode 100644 index 00000000..7672b19f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxDebugLib/VBoxPrintString.c @@ -0,0 +1,60 @@ +/* $Id: VBoxPrintString.c $ */ +/** @file + * VBoxPrintString.c - Implementation of the VBoxPrintString() debug logging routine. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxDebugLib.h" +#include "DevEFI.h" +#include "iprt/asm.h" + + +/** + * Prints a string to the EFI debug port. + * + * @returns The string length. + * @param pszString The string to print. + */ +size_t VBoxPrintString(const char *pszString) +{ + const char *pszEnd = pszString; + while (*pszEnd) + pszEnd++; + ASMOutStrU8(EFI_DEBUG_PORT, (uint8_t const *)pszString, pszEnd - pszString); + return pszEnd - pszString; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxOemHookStatusCodeLib/VBoxOemHookStatusCodeLib.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxOemHookStatusCodeLib/VBoxOemHookStatusCodeLib.c new file mode 100644 index 00000000..db486e91 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxOemHookStatusCodeLib/VBoxOemHookStatusCodeLib.c @@ -0,0 +1,218 @@ +/* $Id: VBoxOemHookStatusCodeLib.c $ */ +/** @file + * DxeVBoxOemHookStatusCodeLib.c - Logging. + */ + + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Library/ReportStatusCodeLib.h> +#include <Library/OemHookStatusCodeLib.h> +#include <Library/PrintLib.h> +#include <Library/BaseMemoryLib.h> +#include <Guid/StatusCodeDataTypeId.h> +#include <Guid/StatusCodeDataTypeDebug.h> +#if 0 /* See VBoxSecExtractDebugInfo */ +# include <DebugInfo.h> +#endif + +#include "VBoxDebugLib.h" +#include "DevEFI.h" + + + +EFI_STATUS EFIAPI +OemHookStatusCodeInitialize(VOID) +{ + VBoxPrintString("OemHookStatusCodeInitialize\n"); + return EFI_SUCCESS; +} + + +#if 0 /* vvl: With thbe new version the API changed a bit and VA_LIST isn't used any more. Before applying + * any changes here I would like to understand in which cases we need this help function. + * bird: Some components sent information in this format. Search for the UUID or EFI_DEBUG_INFO usage. + */ +/** + * Helper VBoxSecPeiReportStatusCode uses for catching some odd reports. + */ +static BOOLEAN +VBoxSecExtractDebugInfo(IN CONST EFI_STATUS_CODE_DATA *pData, + OUT UINT32 *puErrorLevel, + OUT VA_LIST *pVa, + OUT CHAR8 **ppszFormat) +{ + EFI_DEBUG_INFO *pDebugInfo; + + if ( !CompareGuid(&pData->Type, &gEfiStatusCodeSpecificDataGuid) + || pData->HeaderSize != sizeof(*pData) + || pData->Size <= sizeof(UINT64) * 12 + sizeof(EFI_DEBUG_INFO) + 1) + return FALSE; + + pDebugInfo = (EFI_DEBUG_INFO *)(pData + 1); + *pVa = (VA_LIST)(pDebugInfo + 1); + *ppszFormat = (CHAR8 *)((UINT64 *)pVa + 12); + return TRUE; +} +#endif + +/** Worker that dumps the raw data. */ +static void +VBoxOemHookStatusCodeReportRawDump(EFI_STATUS_CODE_TYPE Type, + EFI_STATUS_CODE_VALUE Value, + UINT32 Instance, + CONST EFI_GUID *CallerId) +{ + VBoxPrintString("Report: Type="); + VBoxPrintHex(Type, sizeof(Type)); + VBoxPrintString(" Value="); + VBoxPrintHex(Value, sizeof(Value)); + + VBoxPrintString(" Instance="); + VBoxPrintHex(Instance, sizeof(Instance)); + if (CallerId) + { + VBoxPrintString(" CallerId="); + VBoxPrintGuid(CallerId); + } + +#define CASE_PRINT(Head,Print,Tail) \ + case Head ## Print ## Tail : VBoxPrintString(" " #Print); break + switch (Type & EFI_STATUS_CODE_SEVERITY_MASK) /* quick guess work... */ + { + CASE_PRINT(EFI_ERROR_,MINOR,); + CASE_PRINT(EFI_ERROR_,MAJOR,); + CASE_PRINT(EFI_ERROR_,UNRECOVERED,); + CASE_PRINT(EFI_ERROR_,UNCONTAINED,); + } + switch (Type & EFI_STATUS_CODE_TYPE_MASK) /* quick guess work... */ + { + CASE_PRINT(EFI_,PROGRESS,_CODE); + CASE_PRINT(EFI_,ERROR,_CODE); + CASE_PRINT(EFI_,DEBUG,_CODE); + } +#undef CASE_PRINT + VBoxPrintChar('\n'); +} + + +EFI_STATUS EFIAPI +OemHookStatusCodeReport(IN EFI_STATUS_CODE_TYPE Type, + IN EFI_STATUS_CODE_VALUE Value, + IN UINT32 Instance, + IN EFI_GUID *CallerId OPTIONAL, + IN EFI_STATUS_CODE_DATA *Data OPTIONAL) +{ + /* + * Try figure out the data payload + */ + if (Data != NULL) + { + CHAR8 *pszFilename; + CHAR8 *pszDescription; + UINT32 uLine; + UINT32 uErrorLevel; + BASE_LIST bs; + CHAR8 *pszFormat; + + if (ReportStatusCodeExtractAssertInfo(Type, Value, Data, &pszFilename, + &pszDescription, &uLine)) + { + VBoxPrintString("Assertion Failed! Line=0x"); + VBoxPrintHex(uLine, sizeof(uLine)); + if (pszFilename) + { + VBoxPrintString(" File="); + VBoxPrintString(pszFilename); + } + if (pszDescription) + { + VBoxPrintString(" Desc="); + VBoxPrintString(pszDescription); + } + VBoxPrintChar('\n'); + } + else if ( ReportStatusCodeExtractDebugInfo(Data, &uErrorLevel, &bs, &pszFormat) +#if 0 /* See question at VBoxSecExtractDebugInfo. */ + || VBoxSecExtractDebugInfo(Data, &uErrorLevel, &va, &pszFormat) +#endif + ) + { + CHAR8 szBuf[128]; + UINTN cch; + + cch = AsciiBSPrint(szBuf, sizeof(szBuf), pszFormat, bs); + if (cch >= sizeof(szBuf)) + cch = sizeof(szBuf) - 1; + while ( cch > 0 + && ( szBuf[cch - 1] == '\n' + || szBuf[cch - 1] == '\r')) + cch--; + szBuf[cch] = '\0'; + + VBoxPrintString("DBG/"); + VBoxPrintHex(uErrorLevel, sizeof(uErrorLevel)); + VBoxPrintString(": "); + VBoxPrintString(szBuf); + VBoxPrintChar('\n'); + } + else + { + /* + * Unknown data, resort to raw dump of everything. + */ + VBoxOemHookStatusCodeReportRawDump(Type, Value, Instance, CallerId); + + VBoxPrintString("OemReport: Unknown data type "); + VBoxPrintGuid(&Data->Type); + VBoxPrintString(" (Size="); + VBoxPrintHex(Data->Size, sizeof(Data->Size)); + VBoxPrintString(" HeaderSize="); + VBoxPrintHex(Data->HeaderSize, sizeof(Data->HeaderSize)); + VBoxPrintString(")\n"); + if (Data->Size > 0 && Data->Size <= 128) + VBoxPrintHexDump(Data + 1, Data->Size); + } + } + /* + * No data, do a raw dump. + */ + else + VBoxOemHookStatusCodeReportRawDump(Type, Value, Instance, CallerId); + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxOemHookStatusCodeLib/VBoxOemHookStatusCodeLib.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxOemHookStatusCodeLib/VBoxOemHookStatusCodeLib.inf new file mode 100644 index 00000000..831dc117 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxOemHookStatusCodeLib/VBoxOemHookStatusCodeLib.inf @@ -0,0 +1,80 @@ +# $Id: VBoxOemHookStatusCodeLib.inf $ +## @file +# VBoxOemHookStatusCodeLib - Logging. +# + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxOemHookStatusCodeLib + FILE_GUID = 2199AC76-067A-4DFC-B09F-9E8C02CE05B3 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = OemHookStatusCodeLib + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + +[Sources.common] + VBoxOemHookStatusCodeLib.c + +[Packages] + MdePkg/MdePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + DebugLib + BaseLib + BaseMemoryLib + ReportStatusCodeLib + PrintLib + +[Guids] + # none + +[Ppis] + # none + +[Guids] + # none + +[Pcd.common] + # none + +[FixedPcd.common] + # none + +[FeaturePcd.common] + # none + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/BasePeCoff.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/BasePeCoff.c new file mode 100644 index 00000000..d43340f0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/BasePeCoff.c @@ -0,0 +1,1647 @@ +/* $Id: BasePeCoff.c $ */ +/** @file + * BasePeCoff.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Base PE/COFF loader supports loading any PE32/PE32+ or TE image, but + only supports relocating IA32, x64, IPF, and EBC images. + + Copyright (c) 2006 - 2008, Intel Corporation<BR> + Portions copyright (c) 2008-2009 Apple Inc. All rights reserved.<BR> + All rights reserved. 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 "BasePeCoffLibInternals.h" +#if defined(MDE_CPU_IA32) +# define EFI_FAT_CPU_TYPE EFI_FAT_CPU_TYPE_I386 +#elif defined(MDE_CPU_X64) +# define EFI_FAT_CPU_TYPE EFI_FAT_CPU_TYPE_X64 +#else +# error "please define EFI_FAT_CPU_TYPE for your arch" +#endif + + +/** + Retrieves the magic value from the PE/COFF header. + + @param Hdr The buffer in which to return the PE32, PE32+, or TE header. + + @return EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC - Image is PE32 + @return EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC - Image is PE32+ + +**/ +UINT16 +PeCoffLoaderGetPeHeaderMagicValue ( + IN EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr + ) +{ + // + // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value + // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the + // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC + // then override the returned value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC + // + if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + return EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC; + } + // + // Return the magic value from the PC/COFF Optional Header + // + return Hdr.Pe32->OptionalHeader.Magic; +} + + +/** + Retrieves the PE or TE Header from a PE/COFF or TE image. + + @param ImageContext The context of the image being loaded. + @param Hdr The buffer in which to return the PE32, PE32+, or TE header. + + @retval RETURN_SUCCESS The PE or TE Header is read. + @retval Other The error status from reading the PE/COFF or TE image using the ImageRead function. + +**/ +RETURN_STATUS +PeCoffLoaderGetPeHeader ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext, + OUT EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr + ) +{ + RETURN_STATUS Status; + EFI_IMAGE_DOS_HEADER DosHdr; + EFI_FAT_IMAGE_HEADER Fat; + UINTN Size; +#ifndef VBOX /* VBox: 64-bit VS2010 say wrong type / loss of data. */ + UINTN Offset = 0; +#else + UINT32 Offset = 0; +#endif + UINT16 Magic; + EFI_FAT_IMAGE_HEADER_NLIST nlist[5]; + Size = sizeof (EFI_FAT_IMAGE_HEADER); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + 0, + &Size, + &Fat + ); + if (!RETURN_ERROR(Status) && Fat.Signature == EFI_FAT_IMAGE_HEADER_SIGNATURE) + { + UINT32 i; + DEBUG((DEBUG_LOAD, "%a:%d - %x narches:%d\n", __FILE__, __LINE__, Fat.Signature, Fat.NFatArch)); + /* Can't find the way to allocate here because library used in all phases */ + ASSERT((Fat.NFatArch < 5)); + Size = sizeof(EFI_FAT_IMAGE_HEADER_NLIST) * Fat.NFatArch; + Status = ImageContext->ImageRead ( + ImageContext->Handle, + sizeof (EFI_FAT_IMAGE_HEADER), + &Size, + nlist + ); + for (i = 0; i < Fat.NFatArch ; ++i) + { + + if (nlist[i].CpuType == EFI_FAT_CPU_TYPE) + { + ImageContext->IsFat = TRUE; + ImageContext->FatOffset = Offset; + Offset = nlist[i].Offset; + break; + } + } + } + // + // Read the DOS image header to check for its existence + // + ImageContext->FatOffset = Offset; + Size = sizeof (EFI_IMAGE_DOS_HEADER); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + Offset, + &Size, + &DosHdr + ); + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return Status; + } + + ImageContext->PeCoffHeaderOffset = 0; + if (DosHdr.e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, so read the PE header after the DOS image + // header + // + ImageContext->PeCoffHeaderOffset = DosHdr.e_lfanew; + } + + // + // Read the PE/COFF Header. For PE32 (32-bit) this will read in too much + // data, but that should not hurt anything. Hdr.Pe32->OptionalHeader.Magic + // determines if this is a PE32 or PE32+ image. The magic is in the same + // location in both images. + // + Size = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + ImageContext->PeCoffHeaderOffset + Offset, + &Size, + Hdr.Pe32 + ); + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return Status; + } + + // + // Use Signature to figure out if we understand the image format + // + if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { + ImageContext->IsTeImage = TRUE; + ImageContext->Machine = Hdr.Te->Machine; + ImageContext->ImageType = (UINT16)(Hdr.Te->Subsystem); + // + // For TeImage, SectionAlignment is undefined to be set to Zero + // ImageSize can be calculated. + // + ImageContext->ImageSize = 0; + ImageContext->SectionAlignment = 0; + ImageContext->SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize; + + } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { + ImageContext->IsTeImage = FALSE; + ImageContext->Machine = Hdr.Pe32->FileHeader.Machine; + + Magic = PeCoffLoaderGetPeHeaderMagicValue (Hdr); + + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + ImageContext->ImageType = Hdr.Pe32->OptionalHeader.Subsystem; + ImageContext->ImageSize = (UINT64)Hdr.Pe32->OptionalHeader.SizeOfImage; + ImageContext->SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment; + ImageContext->SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders; + + } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + // + // Use PE32+ offset + // + ImageContext->ImageType = Hdr.Pe32Plus->OptionalHeader.Subsystem; + ImageContext->ImageSize = (UINT64) Hdr.Pe32Plus->OptionalHeader.SizeOfImage; + ImageContext->SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment; + ImageContext->SizeOfHeaders = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders; + } else { + ImageContext->ImageError = IMAGE_ERROR_INVALID_MACHINE_TYPE; + DEBUG((DEBUG_LOAD, "%a:%d - %r\n", __FILE__, __LINE__, RETURN_UNSUPPORTED)); + return RETURN_UNSUPPORTED; + } + } else { + ImageContext->ImageError = IMAGE_ERROR_INVALID_MACHINE_TYPE; + return RETURN_UNSUPPORTED; + } + + if (!PeCoffLoaderImageFormatSupported (ImageContext->Machine)) { + // + // If the PE/COFF loader does not support the image type return + // unsupported. This library can support lots of types of images + // this does not mean the user of this library can call the entry + // point of the image. + // + DEBUG((DEBUG_LOAD, "%a:%d - %r\n", __FILE__, __LINE__, RETURN_UNSUPPORTED)); + return RETURN_UNSUPPORTED; + } + + return RETURN_SUCCESS; +} + + +/** + Retrieves information about a PE/COFF image. + + Computes the PeCoffHeaderOffset, IsTeImage, ImageType, ImageAddress, ImageSize, + DestinationAddress, RelocationsStripped, SectionAlignment, SizeOfHeaders, and + DebugDirectoryEntryRva fields of the ImageContext structure. + If ImageContext is NULL, then return RETURN_INVALID_PARAMETER. + If the PE/COFF image accessed through the ImageRead service in the ImageContext + structure is not a supported PE/COFF image type, then return RETURN_UNSUPPORTED. + If any errors occur while computing the fields of ImageContext, + then the error status is returned in the ImageError field of ImageContext. + If the image is a TE image, then SectionAlignment is set to 0. + The ImageRead and Handle fields of ImageContext structure must be valid prior + to invoking this service. + + @param ImageContext Pointer to the image context structure that describes the PE/COFF + image that needs to be examined by this function. + + @retval RETURN_SUCCESS The information on the PE/COFF image was collected. + @retval RETURN_INVALID_PARAMETER ImageContext is NULL. + @retval RETURN_UNSUPPORTED The PE/COFF image is not supported. + +**/ +RETURN_STATUS +EFIAPI +PeCoffLoaderGetImageInfo ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext + ) +{ + RETURN_STATUS Status; + EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + EFI_IMAGE_DATA_DIRECTORY *DebugDirectoryEntry; + UINTN Size; + UINTN Index; + UINTN DebugDirectoryEntryRva; + UINTN DebugDirectoryEntryFileOffset; + UINTN SectionHeaderOffset; + EFI_IMAGE_SECTION_HEADER SectionHeader; + EFI_IMAGE_DEBUG_DIRECTORY_ENTRY DebugEntry; + UINT32 NumberOfRvaAndSizes; + UINT16 Magic; + UINT32 FatOffset = 0; + + if (ImageContext == NULL) { + return RETURN_INVALID_PARAMETER; + } + // + // Assume success + // + ImageContext->ImageError = IMAGE_ERROR_SUCCESS; + + Hdr.Union = &HdrData; + Status = PeCoffLoaderGetPeHeader (ImageContext, Hdr); + if (RETURN_ERROR (Status)) { + return Status; + } + if (ImageContext->IsFat) + { + FatOffset = ImageContext->FatOffset; + } + + Magic = PeCoffLoaderGetPeHeaderMagicValue (Hdr); + + // + // Retrieve the base address of the image + // + if (!(ImageContext->IsTeImage)) { + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + ImageContext->ImageAddress = Hdr.Pe32->OptionalHeader.ImageBase; + } else { + // + // Use PE32+ offset + // + ImageContext->ImageAddress = Hdr.Pe32Plus->OptionalHeader.ImageBase; + } + } else { + ImageContext->ImageAddress = (PHYSICAL_ADDRESS)(Hdr.Te->ImageBase + Hdr.Te->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER)); + } + ImageContext->ImageAddress += FatOffset; + + // + // Initialize the alternate destination address to 0 indicating that it + // should not be used. + // + ImageContext->DestinationAddress = 0; + + // + // Initialize the debug codeview pointer. + // + ImageContext->DebugDirectoryEntryRva = 0; + ImageContext->CodeView = NULL; + ImageContext->PdbPointer = NULL; + + // + // Three cases with regards to relocations: + // - Image has base relocs, RELOCS_STRIPPED==0 => image is relocatable + // - Image has no base relocs, RELOCS_STRIPPED==1 => Image is not relocatable + // - Image has no base relocs, RELOCS_STRIPPED==0 => Image is relocatable but + // has no base relocs to apply + // Obviously having base relocations with RELOCS_STRIPPED==1 is invalid. + // + // Look at the file header to determine if relocations have been stripped, and + // save this info in the image context for later use. + // + if ((!(ImageContext->IsTeImage)) && ((Hdr.Pe32->FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0)) { + ImageContext->RelocationsStripped = TRUE; + } else if ((ImageContext->IsTeImage) && (Hdr.Te->DataDirectory[0].Size == 0) && (Hdr.Te->DataDirectory[0].VirtualAddress == 0)) { + ImageContext->RelocationsStripped = TRUE; + } else { + ImageContext->RelocationsStripped = FALSE; + } + + if (!(ImageContext->IsTeImage)) { + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; + DebugDirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); + } else { + // + // Use PE32+ offset + // + NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + DebugDirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); + } + + if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) { + + DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress; + + // + // Determine the file offset of the debug directory... This means we walk + // the sections to find which section contains the RVA of the debug + // directory + // + DebugDirectoryEntryFileOffset = 0; + + SectionHeaderOffset = (UINTN)( + ImageContext->PeCoffHeaderOffset + + sizeof (UINT32) + + sizeof (EFI_IMAGE_FILE_HEADER) + + Hdr.Pe32->FileHeader.SizeOfOptionalHeader + ); + + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + // + // Read section header from file + // + Size = sizeof (EFI_IMAGE_SECTION_HEADER); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + SectionHeaderOffset, + &Size, + &SectionHeader + ); + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + DEBUG((DEBUG_LOAD, "%a:%d - %r\n", __FILE__, __LINE__, Status)); + return Status; + } + + if (DebugDirectoryEntryRva >= SectionHeader.VirtualAddress && + DebugDirectoryEntryRva < SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize) { + + DebugDirectoryEntryFileOffset = DebugDirectoryEntryRva - SectionHeader.VirtualAddress + SectionHeader.PointerToRawData; + break; + } + + SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER); + } + + if (DebugDirectoryEntryFileOffset != 0) { + for (Index = 0; Index < DebugDirectoryEntry->Size; Index += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) { + // + // Read next debug directory entry + // + Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + DebugDirectoryEntryFileOffset, + &Size, + &DebugEntry + ); + DEBUG((DEBUG_LOAD, "%a:%d - %r\n", __FILE__, __LINE__, Status)); + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return Status; + } + if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) { + ImageContext->DebugDirectoryEntryRva = (UINT32) (DebugDirectoryEntryRva + Index); + if (DebugEntry.RVA == 0 && DebugEntry.FileOffset != 0) { + ImageContext->ImageSize += DebugEntry.SizeOfData; + } + + return RETURN_SUCCESS; + } + } + } + } + } else { + + DebugDirectoryEntry = &Hdr.Te->DataDirectory[1]; + DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress; + SectionHeaderOffset = (UINTN)(sizeof (EFI_TE_IMAGE_HEADER)) + FatOffset; + + DebugDirectoryEntryFileOffset = 0; + + for (Index = 0; Index < Hdr.Te->NumberOfSections;) { + // + // Read section header from file + // + Size = sizeof (EFI_IMAGE_SECTION_HEADER); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + SectionHeaderOffset, + &Size, + &SectionHeader + ); + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return Status; + } + + if (DebugDirectoryEntryRva >= SectionHeader.VirtualAddress && + DebugDirectoryEntryRva < SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize) { + DebugDirectoryEntryFileOffset = DebugDirectoryEntryRva - + SectionHeader.VirtualAddress + + SectionHeader.PointerToRawData + + sizeof (EFI_TE_IMAGE_HEADER) - + Hdr.Te->StrippedSize; + + // + // File offset of the debug directory was found, if this is not the last + // section, then skip to the last section for calculating the image size. + // + if (Index < (UINTN) Hdr.Te->NumberOfSections - 1) { + SectionHeaderOffset += (Hdr.Te->NumberOfSections - 1 - Index) * sizeof (EFI_IMAGE_SECTION_HEADER); + Index = Hdr.Te->NumberOfSections - 1; + continue; + } + } + + // + // In Te image header there is not a field to describe the ImageSize. + // Actually, the ImageSize equals the RVA plus the VirtualSize of + // the last section mapped into memory (Must be rounded up to + // a multiple of Section Alignment). Per the PE/COFF specification, the + // section headers in the Section Table must appear in order of the RVA + // values for the corresponding sections. So the ImageSize can be determined + // by the RVA and the VirtualSize of the last section header in the + // Section Table. + // + if ((++Index) == (UINTN)Hdr.Te->NumberOfSections) { + ImageContext->ImageSize = (SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize); + } + + SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER); + } + + if (DebugDirectoryEntryFileOffset != 0) { + for (Index = 0; Index < DebugDirectoryEntry->Size; Index += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) { + // + // Read next debug directory entry + // + Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + DebugDirectoryEntryFileOffset, + &Size, + &DebugEntry + ); + DEBUG((DEBUG_LOAD, "%a:%d - %r\n", __FILE__, __LINE__, Status)); + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return Status; + } + + if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) { + ImageContext->DebugDirectoryEntryRva = (UINT32) (DebugDirectoryEntryRva + Index); + return RETURN_SUCCESS; + } + } + } + } + + return RETURN_SUCCESS; +} + + +/** + Converts an image address to the loaded address. + + @param ImageContext The context of the image being loaded. + @param Address The relative virtual address to be converted to the loaded address. + + @return The converted address or NULL if the address can not be converted. + +**/ +VOID * +PeCoffLoaderImageAddress ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext, + IN UINTN Address + ) +{ + // + // Make sure that Address and ImageSize is correct for the loaded image. + // + if (Address >= ImageContext->ImageSize) { + ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS; + return NULL; + } + + return (CHAR8 *)((UINTN) ImageContext->ImageAddress + Address); +} + + +/** + Applies relocation fixups to a PE/COFF image that was loaded with PeCoffLoaderLoadImage(). + + If the DestinationAddress field of ImageContext is 0, then use the ImageAddress field of + ImageContext as the relocation base address. Otherwise, use the DestinationAddress field + of ImageContext as the relocation base address. The caller must allocate the relocation + fixup log buffer and fill in the FixupData field of ImageContext prior to calling this function. + + The ImageRead, Handle, PeCoffHeaderOffset, IsTeImage, Machine, ImageType, ImageAddress, + ImageSize, DestinationAddress, RelocationsStripped, SectionAlignment, SizeOfHeaders, + DebugDirectoryEntryRva, EntryPoint, FixupDataSize, CodeView, PdbPointer, and FixupData of + the ImageContext structure must be valid prior to invoking this service. + + If ImageContext is NULL, then ASSERT(). + + Note that if the platform does not maintain coherency between the instruction cache(s) and the data + cache(s) in hardware, then the caller is responsible for performing cache maintenance operations + prior to transferring control to a PE/COFF image that is loaded using this library. + + @param ImageContext Pointer to the image context structure that describes the PE/COFF + image that is being relocated. + + @retval RETURN_SUCCESS The PE/COFF image was relocated. + Extended status information is in the ImageError field of ImageContext. + @retval RETURN_LOAD_ERROR The image in not a valid PE/COFF image. + Extended status information is in the ImageError field of ImageContext. + @retval RETURN_UNSUPPORTED A relocation record type is not supported. + Extended status information is in the ImageError field of ImageContext. + +**/ +RETURN_STATUS +EFIAPI +PeCoffLoaderRelocateImage ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext + ) +{ + RETURN_STATUS Status; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + EFI_IMAGE_DATA_DIRECTORY *RelocDir; + UINT64 Adjust; + EFI_IMAGE_BASE_RELOCATION *RelocBase; + EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd; + UINT16 *Reloc; + UINT16 *RelocEnd; + CHAR8 *Fixup; + CHAR8 *FixupBase; + UINT16 *Fixup16; + UINT32 *Fixup32; + UINT64 *Fixup64; + CHAR8 *FixupData; + PHYSICAL_ADDRESS BaseAddress; + UINT32 NumberOfRvaAndSizes; + UINT16 Magic; + + ASSERT (ImageContext != NULL); + + // + // Assume success + // + ImageContext->ImageError = IMAGE_ERROR_SUCCESS; + + // + // If there are no relocation entries, then we are done + // + if (ImageContext->RelocationsStripped) { + // Applies additional environment specific actions to relocate fixups + // to a PE/COFF image if needed + PeCoffLoaderRelocateImageExtraAction (ImageContext); + return RETURN_SUCCESS; + } + + // + // If the destination address is not 0, use that rather than the + // image address as the relocation target. + // + if (ImageContext->DestinationAddress != 0) { + BaseAddress = ImageContext->DestinationAddress; + } else { + BaseAddress = ImageContext->ImageAddress; + } + + if (!(ImageContext->IsTeImage)) { + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)ImageContext->ImageAddress + ImageContext->PeCoffHeaderOffset); + + Magic = PeCoffLoaderGetPeHeaderMagicValue (Hdr); + + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + Adjust = (UINT64)BaseAddress - Hdr.Pe32->OptionalHeader.ImageBase; + Hdr.Pe32->OptionalHeader.ImageBase = (UINT32)BaseAddress; + + NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; + RelocDir = &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; + } else { + // + // Use PE32+ offset + // + Adjust = (UINT64) BaseAddress - Hdr.Pe32Plus->OptionalHeader.ImageBase; + Hdr.Pe32Plus->OptionalHeader.ImageBase = (UINT64)BaseAddress; + + NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + RelocDir = &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; + } + + // + // Find the relocation block + // Per the PE/COFF spec, you can't assume that a given data directory + // is present in the image. You have to check the NumberOfRvaAndSizes in + // the optional header to verify a desired directory entry is there. + // + + if ((NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) && (RelocDir->Size > 0)) { + RelocBase = PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress); + RelocBaseEnd = PeCoffLoaderImageAddress ( + ImageContext, + RelocDir->VirtualAddress + RelocDir->Size - 1 + ); + if (RelocBase == NULL || RelocBaseEnd == NULL) { + return RETURN_LOAD_ERROR; + } + } else { + // + // Set base and end to bypass processing below. + // + RelocBase = RelocBaseEnd = NULL; + } + } else { + Hdr.Te = (EFI_TE_IMAGE_HEADER *)(UINTN)(ImageContext->ImageAddress); + Adjust = (UINT64) (BaseAddress - Hdr.Te->StrippedSize + sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->ImageBase); + Hdr.Te->ImageBase = (UINT64) (BaseAddress - Hdr.Te->StrippedSize + sizeof (EFI_TE_IMAGE_HEADER)); + + // + // Find the relocation block + // + RelocDir = &Hdr.Te->DataDirectory[0]; + if (RelocDir->Size > 0) { + RelocBase = (EFI_IMAGE_BASE_RELOCATION *)(UINTN)( + ImageContext->ImageAddress + + RelocDir->VirtualAddress + + sizeof(EFI_TE_IMAGE_HEADER) - + Hdr.Te->StrippedSize + ); + RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *) ((UINTN) RelocBase + (UINTN) RelocDir->Size - 1); + } else { + // + // Set base and end to bypass processing below. + // + RelocBase = RelocBaseEnd = NULL; + } + } + + // + // If Adjust is not zero, then apply fix ups to the image + // + if (Adjust != 0) { + // + // Run the relocation information and apply the fixups + // + FixupData = ImageContext->FixupData; + while (RelocBase < RelocBaseEnd) { + + Reloc = (UINT16 *) ((CHAR8 *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); + RelocEnd = (UINT16 *) ((CHAR8 *) RelocBase + RelocBase->SizeOfBlock); + + // + // Make sure RelocEnd is in the Image range. + // + if ((CHAR8 *) RelocEnd < (CHAR8 *)((UINTN) ImageContext->ImageAddress) || + (CHAR8 *) RelocEnd > (CHAR8 *)((UINTN)ImageContext->ImageAddress + (UINTN)ImageContext->ImageSize)) { + ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION; + return RETURN_LOAD_ERROR; + } + + if (!(ImageContext->IsTeImage)) { + FixupBase = PeCoffLoaderImageAddress (ImageContext, RelocBase->VirtualAddress); + if (FixupBase == NULL) { + return RETURN_LOAD_ERROR; + } + } else { + FixupBase = (CHAR8 *)(UINTN)(ImageContext->ImageAddress + + RelocBase->VirtualAddress + + sizeof(EFI_TE_IMAGE_HEADER) - + Hdr.Te->StrippedSize + ); + } + + // + // Run this relocation record + // + while (Reloc < RelocEnd) { + + Fixup = FixupBase + (*Reloc & 0xFFF); + switch ((*Reloc) >> 12) { + case EFI_IMAGE_REL_BASED_ABSOLUTE: + break; + + case EFI_IMAGE_REL_BASED_HIGH: + Fixup16 = (UINT16 *) Fixup; + *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16))); + if (FixupData != NULL) { + *(UINT16 *) FixupData = *Fixup16; + FixupData = FixupData + sizeof (UINT16); + } + break; + + case EFI_IMAGE_REL_BASED_LOW: + Fixup16 = (UINT16 *) Fixup; + *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); + if (FixupData != NULL) { + *(UINT16 *) FixupData = *Fixup16; + FixupData = FixupData + sizeof (UINT16); + } + break; + + case EFI_IMAGE_REL_BASED_HIGHLOW: + Fixup32 = (UINT32 *) Fixup; + *Fixup32 = *Fixup32 + (UINT32) Adjust; + if (FixupData != NULL) { + FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32)); + *(UINT32 *)FixupData = *Fixup32; + FixupData = FixupData + sizeof (UINT32); + } + break; + + case EFI_IMAGE_REL_BASED_DIR64: + Fixup64 = (UINT64 *) Fixup; + *Fixup64 = *Fixup64 + (UINT64) Adjust; + if (FixupData != NULL) { + FixupData = ALIGN_POINTER (FixupData, sizeof(UINT64)); + *(UINT64 *)(FixupData) = *Fixup64; + FixupData = FixupData + sizeof(UINT64); + } + break; + + default: + // + // The common code does not handle some of the stranger IPF relocations + // PeCoffLoaderRelocateImageEx () adds support for these complex fixups + // on IPF and is a No-Op on other architectures. + // + Status = PeCoffLoaderRelocateImageEx (Reloc, Fixup, &FixupData, Adjust); + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION; + return Status; + } + } + + // + // Next relocation record + // + Reloc += 1; + } + + // + // Next reloc block + // + RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; + } + + // + // Adjust the EntryPoint to match the linked-to address + // + if (ImageContext->DestinationAddress != 0) { + ImageContext->EntryPoint -= (UINT64) ImageContext->ImageAddress; + ImageContext->EntryPoint += (UINT64) ImageContext->DestinationAddress; + } + } + + // Applies additional environment specific actions to relocate fixups + // to a PE/COFF image if needed + PeCoffLoaderRelocateImageExtraAction (ImageContext); + + return RETURN_SUCCESS; +} + +/** + Loads a PE/COFF image into memory. + + Loads the PE/COFF image accessed through the ImageRead service of ImageContext into the buffer + specified by the ImageAddress and ImageSize fields of ImageContext. The caller must allocate + the load buffer and fill in the ImageAddress and ImageSize fields prior to calling this function. + The EntryPoint, FixupDataSize, CodeView, PdbPointer and HiiResourceData fields of ImageContext are computed. + The ImageRead, Handle, PeCoffHeaderOffset, IsTeImage, Machine, ImageType, ImageAddress, ImageSize, + DestinationAddress, RelocationsStripped, SectionAlignment, SizeOfHeaders, and DebugDirectoryEntryRva + fields of the ImageContext structure must be valid prior to invoking this service. + + If ImageContext is NULL, then ASSERT(). + + Note that if the platform does not maintain coherency between the instruction cache(s) and the data + cache(s) in hardware, then the caller is responsible for performing cache maintenance operations + prior to transferring control to a PE/COFF image that is loaded using this library. + + @param ImageContext Pointer to the image context structure that describes the PE/COFF + image that is being loaded. + + @retval RETURN_SUCCESS The PE/COFF image was loaded into the buffer specified by + the ImageAddress and ImageSize fields of ImageContext. + Extended status information is in the ImageError field of ImageContext. + @retval RETURN_BUFFER_TOO_SMALL The caller did not provide a large enough buffer. + Extended status information is in the ImageError field of ImageContext. + @retval RETURN_LOAD_ERROR The PE/COFF image is an EFI Runtime image with no relocations. + Extended status information is in the ImageError field of ImageContext. + @retval RETURN_INVALID_PARAMETER The image address is invalid. + Extended status information is in the ImageError field of ImageContext. + +**/ +RETURN_STATUS +EFIAPI +PeCoffLoaderLoadImage ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext + ) +{ + RETURN_STATUS Status; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + PE_COFF_LOADER_IMAGE_CONTEXT CheckContext; + EFI_IMAGE_SECTION_HEADER *FirstSection; + EFI_IMAGE_SECTION_HEADER *Section; + UINTN NumberOfSections; + UINTN Index; + CHAR8 *Base; + CHAR8 *End; + CHAR8 *MaxEnd; + EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry; + EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry; + UINTN Size; + UINT32 TempDebugEntryRva; + UINT32 NumberOfRvaAndSizes; + UINT16 Magic; + EFI_IMAGE_RESOURCE_DIRECTORY *ResourceDirectory; + EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *ResourceDirectoryEntry; + EFI_IMAGE_RESOURCE_DIRECTORY_STRING *ResourceDirectoryString; + EFI_IMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry; + UINT32 Offset = 0; + + + ASSERT (ImageContext != NULL); + + // + // Assume success + // + ImageContext->ImageError = IMAGE_ERROR_SUCCESS; + + // + // Copy the provided context info into our local version, get what we + // can from the original image, and then use that to make sure everything + // is legit. + // + CopyMem (&CheckContext, ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT)); + + Status = PeCoffLoaderGetImageInfo (&CheckContext); + if (RETURN_ERROR (Status)) { + return Status; + } + + if (ImageContext->IsFat) + { + Offset = ImageContext->FatOffset; + } + + // + // Make sure there is enough allocated space for the image being loaded + // + if (ImageContext->ImageSize < CheckContext.ImageSize) { + ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_SIZE; + return RETURN_BUFFER_TOO_SMALL; + } + if (ImageContext->ImageAddress == 0) { + // + // Image cannot be loaded into 0 address. + // + ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS; + return RETURN_INVALID_PARAMETER; + } + // + // If there's no relocations, then make sure it's not a runtime driver, + // and that it's being loaded at the linked address. + // + if (CheckContext.RelocationsStripped) { + // + // If the image does not contain relocations and it is a runtime driver + // then return an error. + // + if (CheckContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) { + ImageContext->ImageError = IMAGE_ERROR_INVALID_SUBSYSTEM; + return RETURN_LOAD_ERROR; + } + // + // If the image does not contain relocations, and the requested load address + // is not the linked address, then return an error. + // + if (CheckContext.ImageAddress != ImageContext->ImageAddress) { + ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS; + return RETURN_INVALID_PARAMETER; + } + } + // + // Make sure the allocated space has the proper section alignment + // + if (!(ImageContext->IsTeImage)) { + if ((ImageContext->ImageAddress & (CheckContext.SectionAlignment - 1)) != 0) { + ImageContext->ImageError = IMAGE_ERROR_INVALID_SECTION_ALIGNMENT; + return RETURN_INVALID_PARAMETER; + } + } + // + // Read the entire PE/COFF or TE header into memory + // + if (!(ImageContext->IsTeImage)) { + Status = ImageContext->ImageRead ( + ImageContext->Handle, + Offset, + &ImageContext->SizeOfHeaders, + (VOID *) (UINTN) ImageContext->ImageAddress + ); + + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)ImageContext->ImageAddress + ImageContext->PeCoffHeaderOffset); + + FirstSection = (EFI_IMAGE_SECTION_HEADER *) ( + (UINTN)ImageContext->ImageAddress + + ImageContext->PeCoffHeaderOffset + + sizeof(UINT32) + + sizeof(EFI_IMAGE_FILE_HEADER) + + Hdr.Pe32->FileHeader.SizeOfOptionalHeader + ); + NumberOfSections = (UINTN) (Hdr.Pe32->FileHeader.NumberOfSections); + } else { + Status = ImageContext->ImageRead ( + ImageContext->Handle, + Offset, + &ImageContext->SizeOfHeaders, + (void *)(UINTN)ImageContext->ImageAddress + ); + + Hdr.Te = (EFI_TE_IMAGE_HEADER *)(UINTN)(ImageContext->ImageAddress); + + FirstSection = (EFI_IMAGE_SECTION_HEADER *) ( + (UINTN)ImageContext->ImageAddress + + sizeof(EFI_TE_IMAGE_HEADER) + ); + NumberOfSections = (UINTN) (Hdr.Te->NumberOfSections); + + } + + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return RETURN_LOAD_ERROR; + } + + // + // Load each section of the image + // + Section = FirstSection; + for (Index = 0, MaxEnd = NULL; Index < NumberOfSections; Index++) { + // + // Read the section + // + Size = (UINTN) Section->Misc.VirtualSize; + if ((Size == 0) || (Size > Section->SizeOfRawData)) { + Size = (UINTN) Section->SizeOfRawData; + } + + // + // Compute sections address + // + Base = PeCoffLoaderImageAddress (ImageContext, Section->VirtualAddress); + End = PeCoffLoaderImageAddress ( + ImageContext, + Section->VirtualAddress + Section->Misc.VirtualSize - 1 + ); + + // + // If the size of the section is non-zero and the base address or end address resolved to 0, then fail. + // + if ((Size > 0) && ((Base == NULL) || (End == NULL))) { + ImageContext->ImageError = IMAGE_ERROR_SECTION_NOT_LOADED; + return RETURN_LOAD_ERROR; + } + + if (ImageContext->IsTeImage) { + Base = (CHAR8 *)((UINTN) Base + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN)Hdr.Te->StrippedSize); + End = (CHAR8 *)((UINTN) End + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN)Hdr.Te->StrippedSize); + } + + if (End > MaxEnd) { + MaxEnd = End; + } + + if (Section->SizeOfRawData > 0) { + if (!(ImageContext->IsTeImage)) { + Status = ImageContext->ImageRead ( + ImageContext->Handle, + Section->PointerToRawData + Offset, + &Size, + Base + ); + } else { + Status = ImageContext->ImageRead ( + ImageContext->Handle, + Section->PointerToRawData + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN)Hdr.Te->StrippedSize + Offset, + &Size, + Base + ); + } + + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return Status; + } + } + + // + // If raw size is less then virtual size, zero fill the remaining + // + + if (Size < Section->Misc.VirtualSize) { + ZeroMem (Base + Size, Section->Misc.VirtualSize - Size); + } + + // + // Next Section + // + Section += 1; + } + + // + // Get image's entry point + // + Magic = PeCoffLoaderGetPeHeaderMagicValue (Hdr); + if (!(ImageContext->IsTeImage)) { + // + // Sizes of AddressOfEntryPoint are different so we need to do this safely + // + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + ImageContext->EntryPoint = (PHYSICAL_ADDRESS)(UINTN)PeCoffLoaderImageAddress ( + ImageContext, + (UINTN)Hdr.Pe32->OptionalHeader.AddressOfEntryPoint + ); + } else { + // + // Use PE32+ offset + // + ImageContext->EntryPoint = (PHYSICAL_ADDRESS)(UINTN)PeCoffLoaderImageAddress ( + ImageContext, + (UINTN)Hdr.Pe32Plus->OptionalHeader.AddressOfEntryPoint + ); + } + } else { + ImageContext->EntryPoint = (PHYSICAL_ADDRESS) ( + (UINTN)ImageContext->ImageAddress + + (UINTN)Hdr.Te->AddressOfEntryPoint + + (UINTN)sizeof(EFI_TE_IMAGE_HEADER) - + (UINTN)Hdr.Te->StrippedSize + ); + } + + // + // Determine the size of the fixup data + // + // Per the PE/COFF spec, you can't assume that a given data directory + // is present in the image. You have to check the NumberOfRvaAndSizes in + // the optional header to verify a desired directory entry is there. + // + if (!(ImageContext->IsTeImage)) { + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; + DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; + } else { + // + // Use PE32+ offset + // + NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; + } + + if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { + ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN); + } else { + ImageContext->FixupDataSize = 0; + } + } else { + DirectoryEntry = &Hdr.Te->DataDirectory[0]; + ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN); + } + // + // Consumer must allocate a buffer for the relocation fixup log. + // Only used for runtime drivers. + // + ImageContext->FixupData = NULL; + + // + // Load the Codeview info if present + // + if (ImageContext->DebugDirectoryEntryRva != 0) { + if (!(ImageContext->IsTeImage)) { + DebugEntry = PeCoffLoaderImageAddress ( + ImageContext, + ImageContext->DebugDirectoryEntryRva + ); + } else { + DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)(UINTN)( + ImageContext->ImageAddress + + ImageContext->DebugDirectoryEntryRva + + sizeof(EFI_TE_IMAGE_HEADER) - + Hdr.Te->StrippedSize + ); + } + + if (DebugEntry != NULL) { + TempDebugEntryRva = DebugEntry->RVA; + if (DebugEntry->RVA == 0 && DebugEntry->FileOffset != 0) { + Section--; + if ((UINTN)Section->SizeOfRawData < Section->Misc.VirtualSize) { + TempDebugEntryRva = Section->VirtualAddress + Section->Misc.VirtualSize; + } else { + TempDebugEntryRva = Section->VirtualAddress + Section->SizeOfRawData; + } + } + + if (TempDebugEntryRva != 0) { + if (!(ImageContext->IsTeImage)) { + ImageContext->CodeView = PeCoffLoaderImageAddress (ImageContext, TempDebugEntryRva); + } else { + ImageContext->CodeView = (VOID *)( + (UINTN)ImageContext->ImageAddress + + (UINTN)TempDebugEntryRva + + (UINTN)sizeof (EFI_TE_IMAGE_HEADER) - + (UINTN) Hdr.Te->StrippedSize + Offset + ); + } + + if (ImageContext->CodeView == NULL) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return RETURN_LOAD_ERROR; + } + + if (DebugEntry->RVA == 0) { + Size = DebugEntry->SizeOfData; + if (!(ImageContext->IsTeImage)) { + Status = ImageContext->ImageRead ( + ImageContext->Handle, + DebugEntry->FileOffset + Offset, + &Size, + ImageContext->CodeView + ); + } else { + Status = ImageContext->ImageRead ( + ImageContext->Handle, + DebugEntry->FileOffset + sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize + Offset, + &Size, + ImageContext->CodeView + ); + // + // Should we apply fix up to this field according to the size difference between PE and TE? + // Because now we maintain TE header fields unfixed, this field will also remain as they are + // in original PE image. + // + } + + if (RETURN_ERROR (Status)) { + ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ; + return RETURN_LOAD_ERROR; + } + + DebugEntry->RVA = TempDebugEntryRva; + } + + switch (*(UINT32 *) ImageContext->CodeView) { + case CODEVIEW_SIGNATURE_NB10: + ImageContext->PdbPointer = (CHAR8 *)ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY); + break; + + case CODEVIEW_SIGNATURE_RSDS: + ImageContext->PdbPointer = (CHAR8 *)ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY); + break; + + case CODEVIEW_SIGNATURE_MTOC: + ImageContext->PdbPointer = (CHAR8 *)ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY); + break; + + default: + break; + } + } + } + } + + // + // Get Image's HII resource section + // + ImageContext->HiiResourceData = 0; + if (!(ImageContext->IsTeImage)) { + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE]; + } else { + // + // Use PE32+ offset + // + DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE]; + } + + if (DirectoryEntry->Size != 0) { + Base = PeCoffLoaderImageAddress (ImageContext, DirectoryEntry->VirtualAddress); + if (Base != NULL) { + ResourceDirectory = (EFI_IMAGE_RESOURCE_DIRECTORY *) Base; + ResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (ResourceDirectory + 1); + + for (Index = 0; Index < ResourceDirectory->NumberOfNamedEntries; Index++) { + if (ResourceDirectoryEntry->u1.s.NameIsString) { + ResourceDirectoryString = (EFI_IMAGE_RESOURCE_DIRECTORY_STRING *) (Base + ResourceDirectoryEntry->u1.s.NameOffset); + + if (ResourceDirectoryString->Length == 3 && + ResourceDirectoryString->String[0] == L'H' && + ResourceDirectoryString->String[1] == L'I' && + ResourceDirectoryString->String[2] == L'I') { + // + // Resource Type "HII" found + // + if (ResourceDirectoryEntry->u2.s.DataIsDirectory) { + // + // Move to next level - resource Name + // + ResourceDirectory = (EFI_IMAGE_RESOURCE_DIRECTORY *) (Base + ResourceDirectoryEntry->u2.s.OffsetToDirectory); + ResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (ResourceDirectory + 1); + + if (ResourceDirectoryEntry->u2.s.DataIsDirectory) { + // + // Move to next level - resource Language + // + ResourceDirectory = (EFI_IMAGE_RESOURCE_DIRECTORY *) (Base + ResourceDirectoryEntry->u2.s.OffsetToDirectory); + ResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (ResourceDirectory + 1); + } + } + + // + // Now it ought to be resource Data + // + if (!ResourceDirectoryEntry->u2.s.DataIsDirectory) { + ResourceDataEntry = (EFI_IMAGE_RESOURCE_DATA_ENTRY *) (Base + ResourceDirectoryEntry->u2.OffsetToData); + ImageContext->HiiResourceData = (PHYSICAL_ADDRESS) (UINTN) PeCoffLoaderImageAddress (ImageContext, ResourceDataEntry->OffsetToData); + break; + } + } + } + ResourceDirectoryEntry++; + } + } + } + } + + return Status; +} + +extern VOID EFIAPI VBoxPeCoffLoaderMoveImageExtraAction(IN PHYSICAL_ADDRESS OldBase, IN PHYSICAL_ADDRESS NewBase); + +/** + Reapply fixups on a fixed up PE32/PE32+ image to allow virtual calling at EFI + runtime. + + This function reapplies relocation fixups to the PE/COFF image specified by ImageBase + and ImageSize so the image will execute correctly when the PE/COFF image is mapped + to the address specified by VirtualImageBase. RelocationData must be identical + to the FiuxupData buffer from the PE_COFF_LOADER_IMAGE_CONTEXT structure + after this PE/COFF image was relocated with PeCoffLoaderRelocateImage(). + + Note that if the platform does not maintain coherency between the instruction cache(s) and the data + cache(s) in hardware, then the caller is responsible for performing cache maintenance operations + prior to transferring control to a PE/COFF image that is loaded using this library. + + @param ImageBase Base address of a PE/COFF image that has been loaded + and relocated into system memory. + @param VirtImageBase The request virtual address that the PE/COFF image is to + be fixed up for. + @param ImageSize The size, in bytes, of the PE/COFF image. + @param RelocationData A pointer to the relocation data that was collected when the PE/COFF + image was relocated using PeCoffLoaderRelocateImage(). + +**/ +VOID +EFIAPI +PeCoffLoaderRelocateImageForRuntime ( + IN PHYSICAL_ADDRESS ImageBase, + IN PHYSICAL_ADDRESS VirtImageBase, + IN UINTN ImageSize, + IN VOID *RelocationData + ) +{ + CHAR8 *OldBase; + CHAR8 *NewBase; + EFI_IMAGE_DOS_HEADER *DosHdr; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + UINT32 NumberOfRvaAndSizes; + EFI_IMAGE_DATA_DIRECTORY *DataDirectory; + EFI_IMAGE_DATA_DIRECTORY *RelocDir; + EFI_IMAGE_BASE_RELOCATION *RelocBase; + EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd; + UINT16 *Reloc; + UINT16 *RelocEnd; + CHAR8 *Fixup; + CHAR8 *FixupBase; + UINT16 *Fixup16; + UINT32 *Fixup32; + UINT64 *Fixup64; + CHAR8 *FixupData; + UINTN Adjust; + RETURN_STATUS Status; + UINT16 Magic; + UINT32 FatOffset = 0; + EFI_FAT_IMAGE_HEADER *Fat; + + VBoxPeCoffLoaderMoveImageExtraAction(ImageBase, VirtImageBase); + + OldBase = (CHAR8 *)((UINTN)ImageBase); + NewBase = (CHAR8 *)((UINTN)VirtImageBase); + + Fat = (EFI_FAT_IMAGE_HEADER *)OldBase; + if(Fat->Signature == EFI_FAT_IMAGE_HEADER_SIGNATURE) + { + UINT32 i; + EFI_FAT_IMAGE_HEADER_NLIST *nlist = (EFI_FAT_IMAGE_HEADER_NLIST *)&Fat[1]; + for (i = 0; i < Fat->NFatArch; ++i) + { + if (nlist[i].CpuType == EFI_FAT_CPU_TYPE) + { + FatOffset = nlist[i].Offset; + break; + } + } + } + + OldBase += FatOffset; + Adjust = (UINTN) NewBase - (UINTN) OldBase; + + // + // Find the image's relocate dir info + // + DosHdr = (EFI_IMAGE_DOS_HEADER *)(OldBase); + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // Valid DOS header so get address of PE header + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(((CHAR8 *)DosHdr) + DosHdr->e_lfanew); + } else { + // + // No Dos header so assume image starts with PE header. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)OldBase; + } + + if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { + // + // Not a valid PE image so Exit + // + return ; + } + + Magic = PeCoffLoaderGetPeHeaderMagicValue (Hdr); + + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; + DataDirectory = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[0]); + } else { + // + // Use PE32+ offset + // + NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + DataDirectory = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[0]); + } + + // + // Find the relocation block + // + // Per the PE/COFF spec, you can't assume that a given data directory + // is present in the image. You have to check the NumberOfRvaAndSizes in + // the optional header to verify a desired directory entry is there. + // + if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { + RelocDir = DataDirectory + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC; + RelocBase = (EFI_IMAGE_BASE_RELOCATION *)(UINTN)(ImageBase + FatOffset + RelocDir->VirtualAddress); + RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *)(UINTN)(ImageBase + FatOffset + RelocDir->VirtualAddress + RelocDir->Size); + } else { + // + // Cannot find relocations, cannot continue to relocate the image, ASSERT for this invalid image. + // + ASSERT (FALSE); + return ; + } + + // + // ASSERT for the invalid image when RelocBase and RelocBaseEnd are both NULL. + // + ASSERT (RelocBase != NULL && RelocBaseEnd != NULL); + + // + // Run the whole relocation block. And re-fixup data that has not been + // modified. The FixupData is used to see if the image has been modified + // since it was relocated. This is so data sections that have been updated + // by code will not be fixed up, since that would set them back to + // defaults. + // + FixupData = RelocationData; + while (RelocBase < RelocBaseEnd) { + + Reloc = (UINT16 *) ((UINT8 *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); + RelocEnd = (UINT16 *) ((UINT8 *) RelocBase + RelocBase->SizeOfBlock); + FixupBase = (CHAR8 *) ((UINTN)ImageBase) + FatOffset + RelocBase->VirtualAddress; + + // + // Run this relocation record + // + while (Reloc < RelocEnd) { + + Fixup = FixupBase + (*Reloc & 0xFFF); + switch ((*Reloc) >> 12) { + + case EFI_IMAGE_REL_BASED_ABSOLUTE: + break; + + case EFI_IMAGE_REL_BASED_HIGH: + Fixup16 = (UINT16 *) Fixup; + if (*(UINT16 *) FixupData == *Fixup16) { + *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16))); + } + + FixupData = FixupData + sizeof (UINT16); + break; + + case EFI_IMAGE_REL_BASED_LOW: + Fixup16 = (UINT16 *) Fixup; + if (*(UINT16 *) FixupData == *Fixup16) { + *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) Adjust & 0xffff)); + } + + FixupData = FixupData + sizeof (UINT16); + break; + + case EFI_IMAGE_REL_BASED_HIGHLOW: + Fixup32 = (UINT32 *) Fixup; + FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32)); + if (*(UINT32 *) FixupData == *Fixup32) { + *Fixup32 = *Fixup32 + (UINT32) Adjust; + } + + FixupData = FixupData + sizeof (UINT32); + break; + + case EFI_IMAGE_REL_BASED_DIR64: + Fixup64 = (UINT64 *)Fixup; + FixupData = ALIGN_POINTER (FixupData, sizeof (UINT64)); + if (*(UINT64 *) FixupData == *Fixup64) { + *Fixup64 = *Fixup64 + (UINT64)Adjust; + } + + FixupData = FixupData + sizeof (UINT64); + break; + + case EFI_IMAGE_REL_BASED_HIGHADJ: + // + // Not valid Relocation type for UEFI image, ASSERT + // + ASSERT (FALSE); + break; + + default: + // + // Only Itanium requires ConvertPeImage_Ex + // + Status = PeHotRelocateImageEx (Reloc, Fixup, &FixupData, Adjust); + if (RETURN_ERROR (Status)) { + return ; + } + } + // + // Next relocation record + // + Reloc += 1; + } + // + // next reloc block + // + RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; + } +} + + +/** + Reads contents of a PE/COFF image from a buffer in system memory. + + This is the default implementation of a PE_COFF_LOADER_READ_FILE function + that assumes FileHandle pointer to the beginning of a PE/COFF image. + This function reads contents of the PE/COFF image that starts at the system memory + address specified by FileHandle. The read operation copies ReadSize bytes from the + PE/COFF image starting at byte offset FileOffset into the buffer specified by Buffer. + The size of the buffer actually read is returned in ReadSize. + + If FileHandle is NULL, then ASSERT(). + If ReadSize is NULL, then ASSERT(). + If Buffer is NULL, then ASSERT(). + + @param FileHandle Pointer to base of the input stream + @param FileOffset Offset into the PE/COFF image to begin the read operation. + @param ReadSize On input, the size in bytes of the requested read operation. + On output, the number of bytes actually read. + @param Buffer Output buffer that contains the data read from the PE/COFF image. + + @retval RETURN_SUCCESS Data is read from FileOffset from the Handle into + the buffer. +**/ +RETURN_STATUS +EFIAPI +PeCoffLoaderImageReadFromMemory ( + IN VOID *FileHandle, + IN UINTN FileOffset, + IN OUT UINTN *ReadSize, + OUT VOID *Buffer + ) +{ + ASSERT (ReadSize != NULL); + ASSERT (FileHandle != NULL); + ASSERT (Buffer != NULL); + + CopyMem (Buffer, ((UINT8 *)FileHandle) + FileOffset, *ReadSize); + return RETURN_SUCCESS; +} + +/** + Unloads a loaded PE/COFF image from memory and releases its taken resource. + Releases any environment specific resources that were allocated when the image + specified by ImageContext was loaded using PeCoffLoaderLoadImage(). + + For NT32 emulator, the PE/COFF image loaded by system needs to release. + For real platform, the PE/COFF image loaded by Core doesn't needs to be unloaded, + this function can simply return RETURN_SUCCESS. + + If ImageContext is NULL, then ASSERT(). + + @param ImageContext Pointer to the image context structure that describes the PE/COFF + image to be unloaded. + + @retval RETURN_SUCCESS The PE/COFF image was unloaded successfully. +**/ +RETURN_STATUS +EFIAPI +PeCoffLoaderUnloadImage ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext + ) +{ + // + // Applies additional environment specific actions to unload a + // PE/COFF image if needed + // + PeCoffLoaderUnloadImageExtraAction (ImageContext); + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/BasePeCoffLibInternals.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/BasePeCoffLibInternals.h new file mode 100644 index 00000000..cf513424 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/BasePeCoffLibInternals.h @@ -0,0 +1,171 @@ +/* $Id: BasePeCoffLibInternals.h $ */ +/** @file + * BasePeCoffLibInternals.h + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/* + This code is based on: + + Declaration of internal functions in PE/COFF Lib. + + Copyright (c) 2006, Intel Corporation<BR> + All rights reserved. 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 __BASE_PECOFF_LIB_INTERNALS__ +#define __BASE_PECOFF_LIB_INTERNALS__ + +#include <Base.h> +#include <Library/PeCoffLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/PeCoffExtraActionLib.h> +#include <IndustryStandard/PeImage.h> +#include <IndustryStandard/VBoxFatImage.h> + + + +/** + Performs an Itanium-based specific relocation fixup and is a no-op on other + instruction sets. + + @param Reloc Pointer to the relocation record. + @param Fixup Pointer to the address to fix up. + @param FixupData Pointer to a buffer to log the fixups. + @param Adjust The offset to adjust the fixup. + + @return Status code. + +**/ +RETURN_STATUS +PeCoffLoaderRelocateImageEx ( + IN UINT16 *Reloc, + IN OUT CHAR8 *Fixup, + IN OUT CHAR8 **FixupData, + IN UINT64 Adjust + ); + + +/** + Performs an Itanium-based specific re-relocation fixup and is a no-op on other + instruction sets. This is used to re-relocated the image into the EFI virtual + space for runtime calls. + + @param Reloc Pointer to the relocation record. + @param Fixup Pointer to the address to fix up. + @param FixupData Pointer to a buffer to log the fixups. + @param Adjust The offset to adjust the fixup. + + @return Status code. + +**/ +RETURN_STATUS +PeHotRelocateImageEx ( + IN UINT16 *Reloc, + IN OUT CHAR8 *Fixup, + IN OUT CHAR8 **FixupData, + IN UINT64 Adjust + ); + + +/** + Returns TRUE if the machine type of PE/COFF image is supported. Supported + does not mean the image can be executed it means the PE/COFF loader supports + loading and relocating of the image type. It's up to the caller to support + the entry point. + + @param Machine Machine type from the PE Header. + + @return TRUE if this PE/COFF loader can load the image + +**/ +BOOLEAN +PeCoffLoaderImageFormatSupported ( + IN UINT16 Machine + ); + +/** + Retrieves the magic value from the PE/COFF header. + + @param Hdr The buffer in which to return the PE32, PE32+, or TE header. + + @return EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC - Image is PE32 + @return EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC - Image is PE32+ + +**/ +UINT16 +PeCoffLoaderGetPeHeaderMagicValue ( + IN EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr + ); + +/** + Retrieves the PE or TE Header from a PE/COFF or TE image. + + @param ImageContext The context of the image being loaded. + @param Hdr The buffer in which to return the PE32, PE32+, or TE header. + + @retval RETURN_SUCCESS The PE or TE Header is read. + @retval Other The error status from reading the PE/COFF or TE image using the ImageRead function. + +**/ +RETURN_STATUS +PeCoffLoaderGetPeHeader ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext, + OUT EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr + ); + +/** + Converts an image address to the loaded address. + + @param ImageContext The context of the image being loaded. + @param Address The address to be converted to the loaded address. + + @return The converted address or NULL if the address can not be converted. + +**/ +VOID * +PeCoffLoaderImageAddress ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext, + IN UINTN Address + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/PeCoffLoaderEx.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/PeCoffLoaderEx.c new file mode 100644 index 00000000..1425da98 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/PeCoffLoaderEx.c @@ -0,0 +1,128 @@ +/* $Id: PeCoffLoaderEx.c $ */ +/** @file + * PeCoffLoaderEx.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Specific relocation fixups for none Itanium architecture. + + Copyright (c) 2006 - 2008, Intel Corporation<BR> + All rights reserved. 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 "BasePeCoffLibInternals.h" + + +/** + Performs an Itanium-based specific relocation fixup and is a no-op on other + instruction sets. + + @param Reloc Pointer to the relocation record. + @param Fixup Pointer to the address to fix up. + @param FixupData Pointer to a buffer to log the fixups. + @param Adjust The offset to adjust the fixup. + + @return Status code. + +**/ +RETURN_STATUS +PeCoffLoaderRelocateImageEx ( + IN UINT16 *Reloc, + IN OUT CHAR8 *Fixup, + IN OUT CHAR8 **FixupData, + IN UINT64 Adjust + ) +{ + return RETURN_UNSUPPORTED; +} + +/** + Returns TRUE if the machine type of PE/COFF image is supported. Supported + does not mean the image can be executed it means the PE/COFF loader supports + loading and relocating of the image type. It's up to the caller to support + the entry point. + + The IA32/X64 version PE/COFF loader/relocater both support IA32, X64 and EBC images. + + @param Machine Machine type from the PE Header. + + @return TRUE if this PE/COFF loader can load the image + +**/ +BOOLEAN +PeCoffLoaderImageFormatSupported ( + IN UINT16 Machine + ) +{ + if ((Machine == IMAGE_FILE_MACHINE_I386) || (Machine == IMAGE_FILE_MACHINE_X64) || + (Machine == IMAGE_FILE_MACHINE_EBC)) { + return TRUE; + } + + return FALSE; +} + +/** + Performs an Itanium-based specific re-relocation fixup and is a no-op on other + instruction sets. This is used to re-relocated the image into the EFI virtual + space for runtime calls. + + @param Reloc Pointer to the relocation record. + @param Fixup Pointer to the address to fix up. + @param FixupData Pointer to a buffer to log the fixups. + @param Adjust The offset to adjust the fixup. + + @return Status code. + +**/ +RETURN_STATUS +PeHotRelocateImageEx ( + IN UINT16 *Reloc, + IN OUT CHAR8 *Fixup, + IN OUT CHAR8 **FixupData, + IN UINT64 Adjust + ) +{ + return RETURN_UNSUPPORTED; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/VBoxPeCoffLib.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/VBoxPeCoffLib.inf new file mode 100644 index 00000000..b8b6766d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Library/VBoxPeCoffLib/VBoxPeCoffLib.inf @@ -0,0 +1,84 @@ +# $Id: VBoxPeCoffLib.inf $ +## @file +# VBoxPeCoffLib.inf +# + +# +# Copyright (C) 2010-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# This code is based on: +# +#/** @file +# PE/COFF Loader Library implementation. +# The IPF version library supports loading IPF and EBC PE/COFF image. +# The IA32 version library support loading IA32, X64 and EBC PE/COFF images. +# The X64 version library support loading IA32, X64 and EBC PE/COFF images. +# +# Copyright (c) 2006 - 2009, Intel Corporation.<BR> +# Portions copyright (c) 2008-2009 Apple Inc. All rights reserved.<BR> +# +# All rights reserved. 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 = VBoxPeCoffLib + FILE_GUID = 92e02078-d0ca-11de-a547-fb90b709a001 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = PeCoffLib + + +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC ARM +# + +[Sources.common] + BasePeCoffLibInternals.h + BasePeCoff.c + PeCoffLoaderEx.c + +[Packages] + VBoxPkg/VBoxPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + DebugLib + PeCoffExtraActionLib + BaseMemoryLib + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.bmp b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.bmp Binary files differnew file mode 100644 index 00000000..91c3ba89 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.bmp diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.c new file mode 100644 index 00000000..a3d8d9b4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.c @@ -0,0 +1,186 @@ +/* $Id: Logo.c $ */ +/** @file + * Logo DXE Driver, install Edkii Platform Logo protocol. + */ + +/* + * Copyright (C) 2019-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR> + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +#include <Uefi.h> +#include <Protocol/HiiDatabase.h> +#include <Protocol/GraphicsOutput.h> +#include <Protocol/HiiImageEx.h> +#include <Protocol/PlatformLogo.h> +#include <Protocol/HiiPackageList.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/DebugLib.h> + +typedef struct { + EFI_IMAGE_ID ImageId; + EDKII_PLATFORM_LOGO_DISPLAY_ATTRIBUTE Attribute; + INTN OffsetX; + INTN OffsetY; +} LOGO_ENTRY; + +EFI_HII_IMAGE_EX_PROTOCOL *mHiiImageEx; +EFI_HII_HANDLE mHiiHandle; +LOGO_ENTRY mLogos[] = { + { + IMAGE_TOKEN (IMG_LOGO), + EdkiiPlatformLogoDisplayAttributeCenter, + 0, + 0 + } +}; + +/** + Load a platform logo image and return its data and attributes. + + @param This The pointer to this protocol instance. + @param Instance The visible image instance is found. + @param Image Points to the image. + @param Attribute The display attributes of the image returned. + @param OffsetX The X offset of the image regarding the Attribute. + @param OffsetY The Y offset of the image regarding the Attribute. + + @retval EFI_SUCCESS The image was fetched successfully. + @retval EFI_NOT_FOUND The specified image could not be found. +**/ +EFI_STATUS +EFIAPI +GetImage ( + IN EDKII_PLATFORM_LOGO_PROTOCOL *This, + IN OUT UINT32 *Instance, + OUT EFI_IMAGE_INPUT *Image, + OUT EDKII_PLATFORM_LOGO_DISPLAY_ATTRIBUTE *Attribute, + OUT INTN *OffsetX, + OUT INTN *OffsetY + ) +{ + UINT32 Current; + if (Instance == NULL || Image == NULL || + Attribute == NULL || OffsetX == NULL || OffsetY == NULL) { + return EFI_INVALID_PARAMETER; + } + + Current = *Instance; + if (Current >= ARRAY_SIZE (mLogos)) { + return EFI_NOT_FOUND; + } + + (*Instance)++; + *Attribute = mLogos[Current].Attribute; + *OffsetX = mLogos[Current].OffsetX; + *OffsetY = mLogos[Current].OffsetY; + return mHiiImageEx->GetImageEx (mHiiImageEx, mHiiHandle, mLogos[Current].ImageId, Image); +} + +EDKII_PLATFORM_LOGO_PROTOCOL mPlatformLogo = { + GetImage +}; + +/** + Entrypoint of this module. + + This function is the entrypoint of this module. It installs the Edkii + Platform Logo protocol. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + +**/ +EFI_STATUS +EFIAPI +InitializeLogo ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HII_PACKAGE_LIST_HEADER *PackageList; + EFI_HII_DATABASE_PROTOCOL *HiiDatabase; + EFI_HANDLE Handle; + + Status = gBS->LocateProtocol ( + &gEfiHiiDatabaseProtocolGuid, + NULL, + (VOID **) &HiiDatabase + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->LocateProtocol ( + &gEfiHiiImageExProtocolGuid, + NULL, + (VOID **) &mHiiImageEx + ); + ASSERT_EFI_ERROR (Status); + + // + // Retrieve HII package list from ImageHandle + // + Status = gBS->OpenProtocol ( + ImageHandle, + &gEfiHiiPackageListProtocolGuid, + (VOID **) &PackageList, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "HII Image Package with logo not found in PE/COFF resource section\n")); + return Status; + } + + // + // Publish HII package list to HII Database. + // + Status = HiiDatabase->NewPackageList ( + HiiDatabase, + PackageList, + NULL, + &mHiiHandle + ); + if (!EFI_ERROR (Status)) { + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEdkiiPlatformLogoProtocolGuid, &mPlatformLogo, + NULL + ); + } + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.idf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.idf new file mode 100644 index 00000000..671ad6bc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.idf @@ -0,0 +1,10 @@ +// /** @file +// Platform Logo image definition file. +// +// Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#image IMG_LOGO Logo.bmp diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.inf new file mode 100644 index 00000000..72797550 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.inf @@ -0,0 +1,68 @@ +# $Id: Logo.inf $ +## @file +# Logo.inf +# + +# +# Copyright (C) 2019-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# +# This code is based on: +# +## @file +# The default logo bitmap picture shown on setup screen, which is corresponding to gEfiDefaultBmpLogoGuid. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Logo + MODULE_UNI_FILE = Logo.uni + FILE_GUID = 7BB28B99-61BB-11D5-9A5D-0090273FC14D + MODULE_TYPE = USER_DEFINED + VERSION_STRING = 1.0 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC ARM AARCH64 +# + +[Binaries] + BIN|Logo.bmp|* + +[UserExtensions.TianoCore."ExtraFiles"] + LogoExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.uni b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.uni new file mode 100644 index 00000000..9d1bbaff --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/Logo.uni @@ -0,0 +1,16 @@ +// /** @file
+// The default logo bitmap picture shown on setup screen, which is corresponding to gEfiDefaultBmpLogoGuid.
+//
+// This module provides the default logo bitmap picture shown on setup screen, which corresponds to gEfiDefaultBmpLogoGuid.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides the default logo bitmap picture shown on setup screen, which corresponds to gEfiDefaultBmpLogoGuid"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provides the default logo bitmap picture shown on setup screen, which corresponds to gEfiDefaultBmpLogoGuid."
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxe.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxe.inf new file mode 100644 index 00000000..e7c56f4d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxe.inf @@ -0,0 +1,95 @@ +# $Id: LogoDxe.inf $ +## @file +# LogoDxe.inf +# + +# +# Copyright (C) 2019-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# +# This code is based on: +# +## @file +# The default logo bitmap picture shown on setup screen. +# +# Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = LogoDxe + MODULE_UNI_FILE = LogoDxe.uni + FILE_GUID = F74D20EE-37E7-48FC-97F7-9B1047749C69 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeLogo +# +# This flag specifies whether HII resource section is generated into PE image. +# + UEFI_HII_RESOURCE_SECTION = TRUE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + Logo.bmp + Logo.c + Logo.idf + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + +[Protocols] + gEfiHiiDatabaseProtocolGuid ## CONSUMES + gEfiHiiImageExProtocolGuid ## CONSUMES + gEfiHiiPackageListProtocolGuid ## PRODUCES CONSUMES + gEdkiiPlatformLogoProtocolGuid ## PRODUCES + +[Depex] + gEfiHiiDatabaseProtocolGuid AND + gEfiHiiImageExProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + LogoDxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxe.uni b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxe.uni new file mode 100644 index 00000000..9635701b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxe.uni @@ -0,0 +1,16 @@ +// /** @file
+// The default logo bitmap picture shown on setup screen.
+//
+// This module provides the default logo bitmap picture shown on setup screen, through EDKII Platform Logo protocol.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides the default logo bitmap picture shown on setup screen."
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provides the default logo bitmap picture shown on setup screen, through EDKII Platform Logo protocol."
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxeExtra.uni b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxeExtra.uni new file mode 100644 index 00000000..c6ea34b8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// Logo Localized Strings and Content
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Logo Image File"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoExtra.uni b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoExtra.uni new file mode 100644 index 00000000..041179fb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Logo/LogoExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// Logo Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Logo Image File"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/Makefile.kup b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/Makefile.kup diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxApfsJmpStartDxe/VBoxApfsJmpStartDxe.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxApfsJmpStartDxe/VBoxApfsJmpStartDxe.c new file mode 100644 index 00000000..b93136d8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxApfsJmpStartDxe/VBoxApfsJmpStartDxe.c @@ -0,0 +1,521 @@ +/* $Id: VBoxApfsJmpStartDxe.c $ */ +/** @file + * VBoxApfsJmpStartDxe.c - VirtualBox APFS jumpstart driver. + */ + +/* + * Copyright (C) 2019-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Protocol/ComponentName.h> +#include <Protocol/ComponentName2.h> +#include <Protocol/DriverBinding.h> +#include <Protocol/BlockIo.h> +#include <Protocol/DiskIo.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> + +#define IN_RING0 +#include <iprt/cdefs.h> +#include <iprt/formats/apfs.h> + +/** + * Contains the full jump start context being worked on. + */ +typedef struct +{ + /** Block I/O protocol. */ + EFI_BLOCK_IO *pBlockIo; + /** Disk I/O protocol. */ + EFI_DISK_IO *pDiskIo; + /** Block size. */ + uint32_t cbBlock; + /** Controller handle. */ + EFI_HANDLE hController; + /** APFS UUID. */ + APFSUUID Uuid; +} APFSJMPSTARTCTX; +typedef APFSJMPSTARTCTX *PAPFSJMPSTARTCTX; +typedef const APFSJMPSTARTCTX *PCAPFSJMPSTARTCTX; + +static EFI_GUID g_ApfsDrvLoadedFromThisControllerGuid = { 0x01aaf8bc, 0x9c37, 0x4dc1, + { 0xb1, 0x68, 0xe9, 0x67, 0xd4, 0x2c, 0x79, 0x25 } }; + +typedef struct APFS_DRV_LOADED_INFO +{ + EFI_HANDLE hController; + EFI_GUID GuidContainer; +} APFS_DRV_LOADED_INFO; + +/** Driver name translation table. */ +static CONST EFI_UNICODE_STRING_TABLE g_aVBoxApfsJmpStartDriverLangAndNames[] = +{ + { "eng;en", L"VBox APFS Jumpstart Wrapper Driver" }, + { NULL, NULL } +}; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + +/** + * Reads data from the given offset into the buffer. + * + * @returns EFI status code. + * @param pCtx The jump start context. + * @param offRead Where to start reading from. + * @param pvBuf Where to read into. + * @param cbRead Number of bytes to read. + */ +static EFI_STATUS vboxApfsJmpStartRead(IN PAPFSJMPSTARTCTX pCtx, IN APFSPADDR offRead, IN void *pvBuf, IN size_t cbRead) +{ + return pCtx->pDiskIo->ReadDisk(pCtx->pDiskIo, pCtx->pBlockIo->Media->MediaId, offRead * pCtx->cbBlock, cbRead, pvBuf); +} + +/** + * Calculates the fletcher64 checksum of the given APFS block and returns TRUE if it matches the one given in the object header. + * + * @returns Flag indicating whether the checksum matched. + * @param pObjHdr The object header containing the checksum to check against. + * @param pvStruct Pointer to the struct to create the checksum of. + * @param cbStruct Size of the struct in bytes. + */ +static BOOLEAN vboxApfsObjPhysIsChksumValid(PCAPFSOBJPHYS pObjHdr, void *pvStruct, size_t cbStruct) +{ + if (cbStruct % sizeof(uint32_t) == 0) + { + uint32_t *pu32Data = (uint32_t *)pvStruct + 2; /* Start after the checksum field at the beginning. */ + size_t cWordsLeft = (cbStruct >> 2) - 2; + + uint64_t u64C0 = 0; + uint64_t u64C1 = 0; + uint64_t u64ChksumFletcher64 = 0; + uint64_t u64Check0 = 0; + uint64_t u64Check1 = 0; + + while (cWordsLeft) + { + u64C0 += (uint64_t)*pu32Data++; + u64C0 %= UINT32_C(0xffffffff); + + u64C1 += u64C0; + u64C1 %= UINT32_C(0xffffffff); + + cWordsLeft--; + } + + u64Check0 = UINT32_C(0xffffffff) - (u64C0 + u64C1) % UINT32_C(0xffffffff); + u64Check1 = UINT32_C(0xffffffff) - (u64C0 + u64Check0) % UINT32_C(0xffffffff); + + u64ChksumFletcher64 = (uint64_t)u64Check1 << 32 | u64Check0; + if (!CompareMem(&u64ChksumFletcher64, &pObjHdr->abChkSum[0], sizeof(pObjHdr->abChkSum))) + return TRUE; + else + DEBUG((DEBUG_INFO, "vboxApfsObjPhysIsChksumValid: Checksum mismatch, expected 0x%llx got 0x%llx", u64ChksumFletcher64, *(uint64_t *)&pObjHdr->abChkSum[0])); + } + else + DEBUG((DEBUG_INFO, "vboxApfsObjPhysIsChksumValid: Structure not a multiple of 32bit\n")); + + return FALSE; +} + +/** + * Loads and starts the EFI driver contained in the given jump start structure. + * + * @returns EFI status code. + * @param pCtx APFS jump start driver context structure. + * @param pJmpStart APFS jump start structure describing the EFI file to load and start. + */ +static EFI_STATUS vboxApfsJmpStartLoadAndExecEfiDriver(IN PAPFSJMPSTARTCTX pCtx, IN PCAPFSEFIJMPSTART pJmpStart) +{ + PCAPFSPRANGE paExtents = (PCAPFSPRANGE)(pJmpStart + 1); + UINTN cbReadLeft = RT_LE2H_U32(pJmpStart->cbEfiFile); + EFI_STATUS rc = EFI_SUCCESS; + + void *pvApfsDrv = AllocateZeroPool(cbReadLeft); + if (pvApfsDrv) + { + uint32_t i = 0; + uint8_t *pbBuf = (uint8_t *)pvApfsDrv; + + for (i = 0; i < RT_LE2H_U32(pJmpStart->cExtents) && !EFI_ERROR(rc) && cbReadLeft; i++) + { + UINTN cbRead = RT_MIN(cbReadLeft, (UINTN)RT_LE2H_U64(paExtents[i].cBlocks) * pCtx->cbBlock); + + rc = vboxApfsJmpStartRead(pCtx, RT_LE2H_U64(paExtents[i].PAddrStart), pbBuf, cbRead); + pbBuf += cbRead; + cbReadLeft -= cbRead; + } + + if (!EFI_ERROR(rc)) + { + /* Retrieve the parent device path. */ + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + rc = gBS->HandleProtocol(pCtx->hController, &gEfiDevicePathProtocolGuid, (VOID **)&ParentDevicePath); + if (!EFI_ERROR(rc)) + { + /* Load image and execute it. */ + EFI_HANDLE hImage; + + rc = gBS->LoadImage(FALSE, gImageHandle, ParentDevicePath, + pvApfsDrv, RT_LE2H_U32(pJmpStart->cbEfiFile), + &hImage); + if (!EFI_ERROR(rc)) + { + /* Try to start the image. */ + rc = gBS->StartImage(hImage, NULL, NULL); + if (!EFI_ERROR(rc)) + { + APFS_DRV_LOADED_INFO *pApfsDrvLoadedInfo = (APFS_DRV_LOADED_INFO *)AllocatePool (sizeof(APFS_DRV_LOADED_INFO)); + if (pApfsDrvLoadedInfo) + { + pApfsDrvLoadedInfo->hController = pCtx->hController; + CopyMem(&pApfsDrvLoadedInfo->GuidContainer, &pCtx->Uuid, sizeof(pApfsDrvLoadedInfo->GuidContainer)); + + rc = gBS->InstallMultipleProtocolInterfaces(&pCtx->hController, &g_ApfsDrvLoadedFromThisControllerGuid, pApfsDrvLoadedInfo, NULL); + if (!EFI_ERROR(rc)) + { + /* Connect the driver with the controller it came from. */ + EFI_HANDLE ahImage[2]; + + ahImage[0] = hImage; + ahImage[1] = NULL; + + gBS->ConnectController(pCtx->hController, &ahImage[0], NULL, TRUE); + return EFI_SUCCESS; + } + else + { + FreePool(pApfsDrvLoadedInfo); + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Failed to install APFS driver loaded info protocol with %r\n", rc)); + } + } + else + { + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Failed to allocate %u bytes for the driver loaded structure\n", sizeof(APFS_DRV_LOADED_INFO))); + rc = EFI_OUT_OF_RESOURCES; + } + } + else + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Starting APFS driver failed with %r\n", rc)); + + gBS->UnloadImage(hImage); + } + else + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Loading read image failed with %r\n", rc)); + } + else + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Querying device path protocol failed with %r\n", rc)); + } + else + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Reading the jump start extents failed with %r\n", rc)); + + FreePool(pvApfsDrv); + } + else + { + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Failed to allocate %u bytes for the APFS driver image\n", cbReadLeft)); + rc = EFI_OUT_OF_RESOURCES; + } + + return rc; +} + +/** + * @copydoc EFI_DRIVER_BINDING_SUPPORTED + */ +static EFI_STATUS EFIAPI +VBoxApfsJmpStart_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL) +{ + /* Check whether the controller supports the block I/O protocol. */ + EFI_STATUS rc = gBS->OpenProtocol(ControllerHandle, + &gEfiBlockIoProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (EFI_ERROR(rc)) + return rc; + + rc = gBS->OpenProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (EFI_ERROR(rc)) + return rc; + + return EFI_SUCCESS; +} + + +/** + * @copydoc EFI_DRIVER_BINDING_START + */ +static EFI_STATUS EFIAPI +VBoxApfsJmpStart_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL) +{ + APFSJMPSTARTCTX Ctx; + + /* Check whether the driver was already loaded from this controller. */ + EFI_STATUS rc = gBS->OpenProtocol(ControllerHandle, + &g_ApfsDrvLoadedFromThisControllerGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + if (!EFI_ERROR(rc)) + return EFI_UNSUPPORTED; + + Ctx.cbBlock = 0; /* Will get filled when the superblock was read (starting at 0 anyway). */ + Ctx.hController = ControllerHandle; + + rc = gBS->OpenProtocol(ControllerHandle, + &gEfiBlockIoProtocolGuid, + (void **)&Ctx.pBlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!EFI_ERROR(rc)) + { + rc = gBS->OpenProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + (void **)&Ctx.pDiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!EFI_ERROR(rc)) + { + /* Read the NX superblock structure from the first block and verify it. */ + APFSNXSUPERBLOCK Sb; + + rc = vboxApfsJmpStartRead(&Ctx, 0, &Sb, sizeof(Sb)); + if ( !EFI_ERROR(rc) + && RT_LE2H_U32(Sb.u32Magic) == APFS_NX_SUPERBLOCK_MAGIC) + { + uint8_t *pbBlock = (uint8_t *)AllocateZeroPool(RT_LE2H_U32(Sb.cbBlock)); + + if (pbBlock) + { + PCAPFSNXSUPERBLOCK pSb = (PCAPFSNXSUPERBLOCK)pbBlock; + + /* Read in the complete block (checksums always cover the whole block and not just the structure...). */ + Ctx.cbBlock = RT_LE2H_U32(Sb.cbBlock); + + rc = vboxApfsJmpStartRead(&Ctx, 0, pbBlock, Ctx.cbBlock); + if ( !EFI_ERROR(rc) + && RT_LE2H_U64(Sb.PAddrEfiJmpStart) > 0 + && vboxApfsObjPhysIsChksumValid(&pSb->ObjHdr, pbBlock, Ctx.cbBlock)) + { + PCAPFSEFIJMPSTART pJmpStart = (PCAPFSEFIJMPSTART)pbBlock; + + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Found APFS superblock, reading jumpstart structure from %llx\n", RT_LE2H_U64(Sb.PAddrEfiJmpStart))); + + CopyMem(&Ctx.Uuid, &pSb->Uuid, sizeof(Ctx.Uuid)); + + rc = vboxApfsJmpStartRead(&Ctx, RT_LE2H_U64(Sb.PAddrEfiJmpStart), pbBlock, Ctx.cbBlock); + if ( !EFI_ERROR(rc) + && RT_H2LE_U32(pJmpStart->u32Magic) == APFS_EFIJMPSTART_MAGIC + && RT_H2LE_U32(pJmpStart->u32Version) == APFS_EFIJMPSTART_VERSION + && vboxApfsObjPhysIsChksumValid(&pJmpStart->ObjHdr, pbBlock, Ctx.cbBlock) + && RT_H2LE_U32(pJmpStart->cExtents) <= (Ctx.cbBlock - sizeof(*pJmpStart)) / sizeof(APFSPRANGE)) + rc = vboxApfsJmpStartLoadAndExecEfiDriver(&Ctx, pJmpStart); + else + { + rc = EFI_UNSUPPORTED; + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: The APFS EFI jumpstart structure is invalid\n")); + } + } + else + { + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Invalid APFS superblock -> no APFS filesystem (%r %x %llx)\n", rc, Sb.u32Magic, Sb.PAddrEfiJmpStart)); + rc = EFI_UNSUPPORTED; + } + + FreePool(pbBlock); + } + else + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Failed to allocate memory for APFS block data (%u bytes)\n", RT_LE2H_U32(Sb.cbBlock))); + } + else + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Invalid APFS superblock -> no APFS filesystem (%r %x)\n", rc, Sb.u32Magic)); + + gBS->CloseProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle); + } + else + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Opening the Disk I/O protocol failed with %r\n", rc)); + + gBS->CloseProtocol(ControllerHandle, + &gEfiBlockIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle); + } + else + DEBUG((DEBUG_INFO, "VBoxApfsJmpStart: Opening the Block I/O protocol failed with %r\n", rc)); + + return rc; +} + + +/** + * @copydoc EFI_DRIVER_BINDING_STOP + */ +static EFI_STATUS EFIAPI +VBoxApfsJmpStart_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer OPTIONAL) +{ + /* EFI_STATUS rc; */ + + return EFI_UNSUPPORTED; +} + + +/** @copydoc EFI_COMPONENT_NAME_GET_DRIVER_NAME */ +static EFI_STATUS EFIAPI +VBoxApfsJmpStartCN_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, OUT CHAR16 **DriverName) +{ + return LookupUnicodeString2(Language, + This->SupportedLanguages, + &g_aVBoxApfsJmpStartDriverLangAndNames[0], + DriverName, + TRUE); +} + +/** @copydoc EFI_COMPONENT_NAME_GET_CONTROLLER_NAME */ +static EFI_STATUS EFIAPI +VBoxApfsJmpStartCN_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, OUT CHAR16 **ControllerName) +{ + /** @todo try query the protocol from the controller and forward the query. */ + return EFI_UNSUPPORTED; +} + +/** @copydoc EFI_COMPONENT_NAME2_GET_DRIVER_NAME */ +static EFI_STATUS EFIAPI +VBoxApfsJmpStartCN2_GetDriverName(IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, OUT CHAR16 **DriverName) +{ + return LookupUnicodeString2(Language, + This->SupportedLanguages, + &g_aVBoxApfsJmpStartDriverLangAndNames[0], + DriverName, + FALSE); +} + +/** @copydoc EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME */ +static EFI_STATUS EFIAPI +VBoxApfsJmpStartCN2_GetControllerName(IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, OUT CHAR16 **ControllerName) +{ + /** @todo try query the protocol from the controller and forward the query. */ + return EFI_UNSUPPORTED; +} + + + +/********************************************************************************************************************************* +* Entry point and driver registration * +*********************************************************************************************************************************/ + +/** EFI Driver Binding Protocol. */ +static EFI_DRIVER_BINDING_PROTOCOL g_VBoxApfsJmpStartDB = +{ + VBoxApfsJmpStart_Supported, + VBoxApfsJmpStart_Start, + VBoxApfsJmpStart_Stop, + /* .Version = */ 1, + /* .ImageHandle = */ NULL, + /* .DriverBindingHandle = */ NULL +}; + +/** EFI Component Name Protocol. */ +static const EFI_COMPONENT_NAME_PROTOCOL g_VBoxApfsJmpStartCN = +{ + VBoxApfsJmpStartCN_GetDriverName, + VBoxApfsJmpStartCN_GetControllerName, + "eng" +}; + +/** EFI Component Name 2 Protocol. */ +static const EFI_COMPONENT_NAME2_PROTOCOL g_VBoxApfsJmpStartCN2 = +{ + VBoxApfsJmpStartCN2_GetDriverName, + VBoxApfsJmpStartCN2_GetControllerName, + "en" +}; + + +/** + * VBoxApfsJmpStart entry point. + * + * @returns EFI status code. + * + * @param ImageHandle The image handle. + * @param SystemTable The system table pointer. + */ +EFI_STATUS EFIAPI +VBoxApfsjmpStartEntryDxe(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS rc; + DEBUG((DEBUG_INFO, "VBoxApfsjmpStartEntryDxe\n")); + + rc = EfiLibInstallDriverBindingComponentName2(ImageHandle, SystemTable, + &g_VBoxApfsJmpStartDB, ImageHandle, + &g_VBoxApfsJmpStartCN, &g_VBoxApfsJmpStartCN2); + ASSERT_EFI_ERROR(rc); + return rc; +} + +EFI_STATUS EFIAPI +VBoxApfsjmpStartUnloadDxe(IN EFI_HANDLE ImageHandle) +{ + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxApfsJmpStartDxe/VBoxApfsJmpStartDxe.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxApfsJmpStartDxe/VBoxApfsJmpStartDxe.inf new file mode 100644 index 00000000..bb5cd926 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxApfsJmpStartDxe/VBoxApfsJmpStartDxe.inf @@ -0,0 +1,89 @@ +# $Id: VBoxApfsJmpStartDxe.inf $ +## @file +# VBoxApfsJmpStartDxe - VBox wrapper for loading the APFS jump start EFI driver from an APFS volume. +# + +# +# Copyright (C) 2019-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxApfsJmpStartDxe + FILE_GUID = 16cb3fd4-576d-4ec8-8a81-2176c69a1229 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + ENTRY_POINT = VBoxApfsjmpStartEntryDxe + UNLOAD_IMAGE = VBoxApfsjmpStartUnloadDxe + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gUdfDriverBinding +# COMPONENT_NAME = gUdfComponentName +# COMPONENT_NAME2 = gUdfComponentName2 +# + +[Sources.common] + VBoxApfsJmpStartDxe.c + +[Packages] + MdePkg/MdePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + +[Guids] + gEfiFileInfoGuid + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + + +[Protocols] + gEfiDiskIoProtocolGuid + gEfiBlockIoProtocolGuid + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/Console.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/Console.c new file mode 100644 index 00000000..fc0e9d03 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/Console.c @@ -0,0 +1,106 @@ +/* $Id: Console.c $ */ +/** @file + * Console.c - VirtualBox Console control emulation + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ +#include <Uefi.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/DebugLib.h> + +#include "VBoxPkg.h" +#include "ConsoleControl.h" + +EFI_STATUS EFIAPI +GetModeImpl( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + OUT EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode, + OUT BOOLEAN *GopUgaExists, OPTIONAL + OUT BOOLEAN *StdInLocked OPTIONAL + ) +{ + *Mode = EfiConsoleControlScreenGraphics; + + if (GopUgaExists) + *GopUgaExists = TRUE; + if (StdInLocked) + *StdInLocked = FALSE; + return EFI_SUCCESS; +} + +EFI_STATUS EFIAPI +SetModeImpl( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode + ) +{ + return EFI_SUCCESS; +} + +EFI_STATUS EFIAPI +LockStdInImpl( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + IN CHAR16 *Password + ) +{ + return EFI_SUCCESS; +} + + +EFI_CONSOLE_CONTROL_PROTOCOL gConsoleController = +{ + GetModeImpl, + SetModeImpl, + LockStdInImpl +}; + +EFI_GUID gEfiConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; + +EFI_STATUS +EFIAPI +InitializeConsoleSim ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiConsoleControlProtocolGuid, + &gConsoleController, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/ConsoleControl.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/ConsoleControl.h new file mode 100644 index 00000000..8dabbb0e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/ConsoleControl.h @@ -0,0 +1,158 @@ +/* $Id: ConsoleControl.h $ */ +/** @file + * ConsoleControl.h + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + * This code is based on: + * + * Copyright (c) 2004 - 2006, Intel Corporation + * All rights reserved. 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. + * + * Module Name: + * + * ConsoleControl.h + * + * Abstract: + * + * Abstraction of a Text mode or GOP/UGA screen + */ + +#ifndef __CONSOLE_CONTROL_H__ +#define __CONSOLE_CONTROL_H__ + +#define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \ + { 0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21} } + +typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL EFI_CONSOLE_CONTROL_PROTOCOL; + + +typedef enum { + EfiConsoleControlScreenText, + EfiConsoleControlScreenGraphics, + EfiConsoleControlScreenMaxValue +} EFI_CONSOLE_CONTROL_SCREEN_MODE; + + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + OUT EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode, + OUT BOOLEAN *GopUgaExists, OPTIONAL + OUT BOOLEAN *StdInLocked OPTIONAL + ) +/*++ + + Routine Description: + Return the current video mode information. Also returns info about existence + of Graphics Output devices or UGA Draw devices in system, and if the Std In + device is locked. All the arguments are optional and only returned if a non + NULL pointer is passed in. + + Arguments: + This - Protocol instance pointer. + Mode - Are we in text of graphics mode. + GopUgaExists - TRUE if Console Splitter has found a GOP or UGA device + StdInLocked - TRUE if StdIn device is keyboard locked + + Returns: + EFI_SUCCESS - Mode information returned. + +--*/ +; + + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + OUT EFI_CONSOLE_CONTROL_SCREEN_MODE Mode + ) +/*++ + + Routine Description: + Set the current mode to either text or graphics. Graphics is + for Quiet Boot. + + Arguments: + This - Protocol instance pointer. + Mode - Mode to set the + + Returns: + EFI_SUCCESS - Mode information returned. + +--*/ +; + + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + IN CHAR16 *Password + ) +/*++ + + Routine Description: + Lock Std In devices until Password is typed. + + Arguments: + This - Protocol instance pointer. + Password - Password needed to unlock screen. NULL means unlock keyboard + + Returns: + EFI_SUCCESS - Mode information returned. + EFI_DEVICE_ERROR - Std In not locked + +--*/ +; + + + +struct _EFI_CONSOLE_CONTROL_PROTOCOL { + EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode; + EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode; + EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn; +}; + +extern EFI_GUID gEfiConsoleControlProtocolGuid; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/Cpu.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/Cpu.c new file mode 100644 index 00000000..791475d3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/Cpu.c @@ -0,0 +1,146 @@ +/* $Id: Cpu.c $ */ +/** @file + * Cpu.c - VirtualBox CPU descriptors + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiLib.h> +#include <Library/HiiLib.h> +#include <Library/BaseLib.h> + +#include <Protocol/Cpu.h> + +#include "DataHub.h" + +#define EFI_CPU_DATA_MAXIMUM_LENGTH 0x100 + +EFI_GUID gEfiAppleMagicHubGuid = { + 0x64517cc8, 0x6561, 0x4051, {0xb0, 0x3c, 0x59, 0x64, 0xb6, 0x0f, 0x4c, 0x7a } +}; + +EFI_GUID gEfiProcessorSubClassGuid = { + 0x26fdeb7e, 0xb8af, 0x4ccf, { 0xaa, 0x97, 0x02, 0x63, 0x3c, 0xe4, 0x8c, 0xa7 } +}; + +#pragma pack(1) +typedef struct { + UINT8 Pad0[0x10]; /* 0x48 */ + UINT32 NameLen; /* 0x58 , in bytes */ + UINT32 ValLen; /* 0x5c */ + UINT8 Data[1]; /* 0x60 Name Value */ +} MAGIC_HUB_DATA; +#pragma pack() + +UINT32 +CopyRecord(MAGIC_HUB_DATA* Rec, const CHAR16* Name, VOID* Val, UINT32 ValLen) +{ + Rec->NameLen = (UINT32)StrLen(Name) * sizeof(CHAR16); + Rec->ValLen = ValLen; + CopyMem(Rec->Data, Name, Rec->NameLen); + CopyMem(Rec->Data + Rec->NameLen, Val, ValLen); + + return 0x10 + 4 + 4 + Rec->NameLen + Rec->ValLen; +} + +EFI_STATUS EFIAPI +LogData(EFI_DATA_HUB_PROTOCOL *DataHub, + MAGIC_HUB_DATA *MagicData, + CHAR16 *Name, + VOID *Data, + UINT32 DataSize) +{ + UINT32 RecordSize; + EFI_STATUS Status; + + RecordSize = CopyRecord(MagicData, Name, Data, DataSize); + Status = DataHub->LogData ( + DataHub, + &gEfiProcessorSubClassGuid, /* DataRecordGuid */ + &gEfiAppleMagicHubGuid, /* ProducerName */ + EFI_DATA_CLASS_DATA, + MagicData, + RecordSize + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +EFI_STATUS EFIAPI +CpuUpdateDataHub(EFI_BOOT_SERVICES * bs, + UINT64 FSBFrequency, + UINT64 TSCFrequency, + UINT64 CPUFrequency) +{ + EFI_STATUS Status; + EFI_DATA_HUB_PROTOCOL *DataHub; + MAGIC_HUB_DATA *MagicData; + UINT32 Supported = 1; + // + // Locate DataHub protocol. + // + Status = bs->LocateProtocol (&gEfiDataHubProtocolGuid, NULL, (VOID**)&DataHub); + if (EFI_ERROR (Status)) { + return Status; + } + + MagicData = (MAGIC_HUB_DATA*)AllocatePool (0x200); + if (MagicData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // Log data in format some OSes like + LogData(DataHub, MagicData, L"FSBFrequency", &FSBFrequency, sizeof(FSBFrequency)); + // do that twice, as last variable read not really accounted for + LogData(DataHub, MagicData, L"FSBFrequency", &FSBFrequency, sizeof(FSBFrequency)); + LogData(DataHub, MagicData, L"TSCFrequency", &TSCFrequency, sizeof(TSCFrequency)); + LogData(DataHub, MagicData, L"CPUFrequency", &CPUFrequency, sizeof(CPUFrequency)); + + // The following is required for OS X to construct a SATA boot path. UEFI 2.0 (published + // in Jan 2006, same time as the first Intel Macs) did not standardize SATA device paths; + // if DevicePathsSupported is not set, OS X will create ATA boot paths which will fail + // to boot + LogData(DataHub, MagicData, L"DevicePathsSupported", &Supported, sizeof(Supported)); + + FreePool (MagicData); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/DataHub.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/DataHub.c new file mode 100644 index 00000000..8268289d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/DataHub.c @@ -0,0 +1,215 @@ +/* $Id: DataHub.c $ */ +/** @file + * Console.c - VirtualBox Console control emulation + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ +#include <Uefi.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiLib.h> + +#include "VBoxPkg.h" +#include "DataHub.h" + +/** + * Data hub logged entry. + */ +typedef struct +{ + /** List node for the linked list - must be at the top. */ + LIST_ENTRY NdEntries; + /** The record header. */ + EFI_DATA_RECORD_HEADER RecHdr; + /** The data logged, variable in size. */ + UINT8 abData[1]; +} EFI_DATA_HUB_ENTRY; + +/** + * DataHub instance data. + */ +typedef struct +{ + /** Monotonic increasing counter. */ + UINT64 cMonotonicCnt; + /** Linked list holding the logged entries. */ + LIST_ENTRY LstEntries; + /** The lock protecting the key members above. */ + EFI_LOCK Lck; +} EFI_DATA_HUB_INSTANCE; + + +EFI_DATA_HUB_INSTANCE mDataHubInstance; + +EFI_STATUS EFIAPI +DataHubLogData ( + IN EFI_DATA_HUB_PROTOCOL *This, + IN EFI_GUID *DataRecordGuid, + IN EFI_GUID *ProducerName, + IN UINT64 DataRecordClass, + IN VOID *RawData, + IN UINT32 RawDataSize + ) +{ + UINT32 cbEntry = sizeof(EFI_DATA_HUB_ENTRY) + RawDataSize; + EFI_DATA_HUB_ENTRY *pEntry = AllocatePool(cbEntry); + + if (pEntry == NULL) + return EFI_OUT_OF_RESOURCES; + + pEntry->RecHdr.Version = EFI_DATA_RECORD_HEADER_VERSION; + pEntry->RecHdr.HeaderSize = sizeof(EFI_DATA_RECORD_HEADER); + pEntry->RecHdr.RecordSize = RawDataSize + sizeof(EFI_DATA_RECORD_HEADER); + CopyMem(&pEntry->RecHdr.DataRecordGuid, DataRecordGuid, sizeof(pEntry->RecHdr.DataRecordGuid)); + CopyMem(&pEntry->RecHdr.ProducerName, ProducerName, sizeof(pEntry->RecHdr.ProducerName)); + pEntry->RecHdr.DataRecordClass = DataRecordClass; + SetMem(&pEntry->RecHdr.LogTime, sizeof(pEntry->RecHdr.LogTime), 0); + pEntry->RecHdr.LogMonotonicCount = ++mDataHubInstance.cMonotonicCnt; /* Ensure non zero value in record. */ + CopyMem(&pEntry->abData[0], RawData, RawDataSize); + + EfiAcquireLock(&mDataHubInstance.Lck); + InsertTailList(&mDataHubInstance.LstEntries, &pEntry->NdEntries); + EfiReleaseLock(&mDataHubInstance.Lck); + return EFI_SUCCESS; +} + + +EFI_STATUS EFIAPI +DataHubGetNextDataRecord ( + IN EFI_DATA_HUB_PROTOCOL *This, + IN OUT UINT64 *MonotonicCount, + IN EFI_EVENT *FilterDriver OPTIONAL, + OUT EFI_DATA_RECORD_HEADER **Record + ) +{ + EFI_DATA_HUB_ENTRY *pEntry = NULL; + + EfiAcquireLock(&mDataHubInstance.Lck); + if (*MonotonicCount == 0) + { + if (!IsListEmpty(&mDataHubInstance.LstEntries)) + pEntry = (EFI_DATA_HUB_ENTRY *)GetFirstNode(&mDataHubInstance.LstEntries); + } + else + { + /* Ignore filter driver handling for now. */ + LIST_ENTRY *pHead = &mDataHubInstance.LstEntries; + LIST_ENTRY *pIt = NULL; + + for (pIt = GetFirstNode(pHead); pIt != pHead; pIt = GetNextNode(pHead, pIt)) + { + EFI_DATA_HUB_ENTRY *pTmp = (EFI_DATA_HUB_ENTRY *)pIt; + if (pTmp->RecHdr.LogMonotonicCount == *MonotonicCount) + { + pEntry = pTmp; + break; + } + } + } + EfiReleaseLock(&mDataHubInstance.Lck); + + if (pEntry == NULL) + return EFI_NOT_FOUND; + + *Record = &pEntry->RecHdr; + + /* Look for the next entry and set MonotonicCount accordingly. */ + if (!IsNodeAtEnd(&mDataHubInstance.LstEntries, &pEntry->NdEntries)) + { + pEntry = (EFI_DATA_HUB_ENTRY *)GetNextNode(&mDataHubInstance.LstEntries, &pEntry->NdEntries); + *MonotonicCount = pEntry->RecHdr.LogMonotonicCount; + } + else + *MonotonicCount = 0; + + return EFI_SUCCESS; +} + + +EFI_STATUS EFIAPI +DataHubRegisterDataFilterDriver ( + IN EFI_DATA_HUB_PROTOCOL *This, + IN EFI_EVENT FilterEvent, + IN EFI_TPL FilterTpl, + IN UINT64 FilterClass, + IN EFI_GUID *FilterDataRecordGui OPTIONAL + ) +{ + return EFI_SUCCESS; +} + + +EFI_STATUS EFIAPI +DataHubUnregisterDataFilterDriver ( + IN EFI_DATA_HUB_PROTOCOL *This, + IN EFI_EVENT FilterEvent + ) +{ + return EFI_SUCCESS; +} + + +EFI_DATA_HUB_PROTOCOL gDataHub = +{ + DataHubLogData, + DataHubGetNextDataRecord, + DataHubRegisterDataFilterDriver, + DataHubUnregisterDataFilterDriver +}; + +EFI_GUID gEfiDataHubProtocolGuid = EFI_DATA_HUB_PROTOCOL_GUID; + +EFI_STATUS +EFIAPI +InitializeDataHub ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + + InitializeListHead(&mDataHubInstance.LstEntries); + EfiInitializeLock (&mDataHubInstance.Lck, TPL_NOTIFY); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiDataHubProtocolGuid, + &gDataHub, + NULL); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/DataHub.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/DataHub.h new file mode 100644 index 00000000..976de54d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/DataHub.h @@ -0,0 +1,178 @@ +/* $Id: DataHub.h $ */ +/** @file + * DataHub.h + */ + +/* + * Copyright (C) 2019-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef __DATA_HUB_H__ +#define __DATA_HUB_H__ + +#define EFI_DATA_HUB_PROTOCOL_GUID \ + { 0xae80d021, 0x618e, 0x11d4, {0xbc, 0xd7, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81} } + +typedef struct _EFI_DATA_HUB_PROTOCOL EFI_DATA_HUB_PROTOCOL; + + +#define EFI_DATA_RECORD_HEADER_VERSION 0x0100 + +#define EFI_DATA_CLASS_DEBUG 0x1ULL +#define EFI_DATA_CLASS_ERROR 0x2ULL +#define EFI_DATA_CLASS_DATA 0x4ULL +#define EFI_DATA_CLASS_PROGRESS_CODE 0x8ULL + +typedef struct { + UINT16 Version; + UINT16 HeaderSize; + UINT32 RecordSize; + EFI_GUID DataRecordGuid; + EFI_GUID ProducerName; + UINT64 DataRecordClass; + EFI_TIME LogTime; + UINT64 LogMonotonicCount; +} EFI_DATA_RECORD_HEADER; + +typedef +EFI_STATUS +(EFIAPI *EFI_DATA_HUB_LOG_DATA) ( + IN EFI_DATA_HUB_PROTOCOL *This, + IN EFI_GUID *DataRecordGuid, + IN EFI_GUID *ProducerName, + IN UINT64 DataRecordClass, + IN VOID *RawData, + IN UINT32 RawDataSize + ) +/*++ + + Routine Description: + Logs a data record to the system event log. + + Arguments: + This - Protocol instance pointer. + + + Returns: + EFI_SUCCESS - Data was logged. + +--*/ +; + + +typedef +EFI_STATUS +(EFIAPI *EFI_DATA_HUB_GET_NEXT_DATA_RECORD) ( + IN EFI_DATA_HUB_PROTOCOL *This, + IN OUT UINT64 *MonotonicCount, + IN EFI_EVENT *FilterDriver OPTIONAL, + OUT EFI_DATA_RECORD_HEADER **Record + ) +/*++ + + Routine Description: + Allows the system data log to be searched. + + Arguments: + This - Protocol instance pointer. + + + Returns: + EFI_SUCCESS - Data was logged. + +--*/ +; + + +typedef +EFI_STATUS +(EFIAPI *EFI_DATA_HUB_REGISTER_DATA_FILTER_DRIVER) ( + IN EFI_DATA_HUB_PROTOCOL *This, + IN EFI_EVENT FilterEvent, + IN EFI_TPL FilterTpl, + IN UINT64 FilterClass, + IN EFI_GUID *FilterDataRecordGui OPTIONAL + ) +/*++ + + Routine Description: + Registers an event to be signaled evey time a data record is logged in the system. + + Arguments: + This - Protocol instance pointer. + + + Returns: + EFI_SUCCESS - The filter driver event was registered. + +--*/ +; + + +typedef +EFI_STATUS +(EFIAPI *EFI_DATA_HUB_UNREGISTER_DATA_FILTER_DRIVER) ( + IN EFI_DATA_HUB_PROTOCOL *This, + IN EFI_EVENT FilterEvent + ) +/*++ + + Routine Description: + Stops a filter driver from being notified when data records are logged. + + Arguments: + This - Protocol instance pointer. + + + Returns: + EFI_SUCCESS - The filter driver represented by FilterEvent was shut off. + +--*/ +; + + +struct _EFI_DATA_HUB_PROTOCOL { + EFI_DATA_HUB_LOG_DATA LogData; + EFI_DATA_HUB_GET_NEXT_DATA_RECORD GetNextDataRecord; + EFI_DATA_HUB_REGISTER_DATA_FILTER_DRIVER RegisterFilterDriver; + EFI_DATA_HUB_UNREGISTER_DATA_FILTER_DRIVER UnregisterFilterDriver; +}; + +extern EFI_GUID gEfiDataHubProtocolGuid; + + +EFI_STATUS +EFIAPI +InitializeDataHub ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/VBoxAppleSim.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/VBoxAppleSim.c new file mode 100644 index 00000000..4d2b4ef4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/VBoxAppleSim.c @@ -0,0 +1,352 @@ +/* $Id: VBoxAppleSim.c $ */ +/** @file + * VBoxAppleSim.c - VirtualBox Apple Firmware simulation support + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> +#include <Library/PrintLib.h> + +#include <Protocol/DevicePathToText.h> + +#include <IndustryStandard/Acpi10.h> +#include <IndustryStandard/Acpi20.h> +#include <IndustryStandard/SmBios.h> + +#include <Guid/SmBios.h> +#include <Guid/Acpi.h> +#include <Guid/Mps.h> + +#include "DataHub.h" +#include "VBoxPkg.h" +#include "DevEFI.h" +#include "iprt/asm.h" + + +/* + * External functions + */ +EFI_STATUS EFIAPI +CpuUpdateDataHub(EFI_BOOT_SERVICES * bs, + UINT64 FSBFrequency, + UINT64 TSCFrequency, + UINT64 CPUFrequency); + +EFI_STATUS EFIAPI +InitializeConsoleSim (IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable); + + +/* + * Internal Functions + */ +static UINT32 +GetVmVariable(UINT32 Variable, CHAR8 *pbBuf, UINT32 cbBuf) +{ + UINT32 cbVar, offBuf; + + ASMOutU32(EFI_INFO_PORT, Variable); + cbVar = ASMInU32(EFI_INFO_PORT); + + for (offBuf = 0; offBuf < cbVar && offBuf < cbBuf; offBuf++) + pbBuf[offBuf] = ASMInU8(EFI_INFO_PORT); + + return cbVar; +} + +/* + * GUIDs + */ +/** The EFI variable GUID for the 'FirmwareFeatures' and friends. + * Also known as AppleFirmwareVariableGuid in other sources. */ +EFI_GUID gEfiAppleNvramGuid = { + 0x4D1EDE05, 0x38C7, 0x4A6A, {0x9C, 0xC6, 0x4B, 0xCC, 0xA8, 0xB3, 0x8C, 0x14 } +}; + +/** The EFI variable GUID for the 'boot-args' variable and others. + * Also known as AppleNVRAMVariableGuid in other sources. */ +EFI_GUID gEfiAppleBootGuid = { + 0x7C436110, 0xAB2A, 0x4BBB, {0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82} +}; + + +/* + * Device Properoty protocol implementation hack. + */ + +/** gEfiAppleVarGuid is aka AppleDevicePropertyProtocolGuid in other sources. */ +EFI_GUID gEfiAppleVarGuid = { + 0x91BD12FE, 0xF6C3, 0x44FB, {0xA5, 0xB7, 0x51, 0x22, 0xAB, 0x30, 0x3A, 0xE0} +}; + +/** APPLE_GETVAR_PROTOCOL is aka APPLE_DEVICE_PROPERTY_PROTOCOL in other sources. */ +typedef struct _APPLE_GETVAR_PROTOCOL APPLE_GETVAR_PROTOCOL; + +struct _APPLE_GETVAR_PROTOCOL +{ + /** Magic value or some version thingy. boot.efi doesn't check this, I think. */ + UINT64 u64Magic; + + EFI_STATUS (EFIAPI *pfnUnknown0)(IN APPLE_GETVAR_PROTOCOL *This, IN VOID *pvArg1, IN VOID *pvArg2, + IN VOID *pvArg3, IN VOID *pvArg4); + EFI_STATUS (EFIAPI *pfnUnknown1)(IN APPLE_GETVAR_PROTOCOL *This, IN VOID *pvArg1, IN VOID *pvArg2, + IN VOID *pvArg3, IN VOID *pvArg4); + EFI_STATUS (EFIAPI *pfnUnknown2)(IN APPLE_GETVAR_PROTOCOL *This, IN VOID *pvArg1, IN VOID *pvArg2); + + EFI_STATUS (EFIAPI *pfnGetDevProps)(IN APPLE_GETVAR_PROTOCOL *This, IN CHAR8 *pbBuf, IN OUT UINT32 *pcbBuf); +}; +/** The value of APPLE_GETVAR_PROTOCOL::u64Magic. */ +#define APPLE_GETVAR_PROTOCOL_MAGIC 0x10000 + +EFI_STATUS EFIAPI +AppleGetVar_Unknown0(IN APPLE_GETVAR_PROTOCOL *This, IN VOID *pvArg1, IN VOID *pvArg2, + IN VOID *pvArg3, IN VOID *pvArg4) +{ + CHAR8 szMsg[128]; + AsciiSPrint(szMsg, sizeof(szMsg), "AppleGetVar_Unknown0: pvArg1=%p pvArg2=%p pvArg3=%p pvArg4=%p", + pvArg1, pvArg2, pvArg3, pvArg4); + DebugAssert(__FILE__, __LINE__, szMsg); + return EFI_UNSUPPORTED; +} + +EFI_STATUS EFIAPI +AppleGetVar_Unknown1(IN APPLE_GETVAR_PROTOCOL *This, IN VOID *pvArg1, IN VOID *pvArg2, + IN VOID *pvArg3, IN VOID *pvArg4) +{ + CHAR8 szMsg[128]; + AsciiSPrint(szMsg, sizeof(szMsg), "AppleGetVar_Unknown1: pvArg1=%p pvArg2=%p pvArg3=%p pvArg4=%p", + pvArg1, pvArg2, pvArg3, pvArg4); + DebugAssert(__FILE__, __LINE__, szMsg); + return EFI_UNSUPPORTED; +} + +EFI_STATUS EFIAPI +AppleGetVar_Unknown2(IN APPLE_GETVAR_PROTOCOL *This, IN VOID *pvArg1, IN VOID *pvArg2) +{ + CHAR8 szMsg[80]; + AsciiSPrint(szMsg, sizeof(szMsg), "AppleGetVar_Unknown2: pvArg1=%p pvArg2=%p", pvArg1, pvArg2); + DebugAssert(__FILE__, __LINE__, szMsg); + return EFI_UNSUPPORTED; +} + + +/** + * This method obtains the 'device-properties' that get exposed by + * AppleEFIFirmware and parsed by AppleACPIPlatform. + * + * Check out the data in the IORegisteryExplorer, the device-properties property + * under IODeviceTree:/efi. + * + * @retval EFI_SUCCESS, check *pcbBuf or the number of bytes actually returned. + * @retval EFI_BUFFER_TOO_SMALL, check *pcbBuf for the necessary buffer size. + * @param pThis Not used. + * @param pbBuf The output buffer. + * @param pcbBuf On input, the varible pointed to contains the size of the + * buffer. The size is generally 4KB from what we've observed. + * On output, it contains the amount of data available, this + * is always set. + */ +EFI_STATUS EFIAPI +AppleGetVar_GetDeviceProps(IN APPLE_GETVAR_PROTOCOL *pThis, OUT CHAR8 *pbBuf, IN OUT UINT32 *pcbBuf) +{ + UINT32 cbBuf = *pcbBuf; + UINT32 cbActual; + + cbActual = GetVmVariable(EFI_INFO_INDEX_DEVICE_PROPS, pbBuf, cbBuf); + *pcbBuf = cbActual; + + if (cbActual > cbBuf) + return EFI_BUFFER_TOO_SMALL; + + return EFI_SUCCESS; +} + +APPLE_GETVAR_PROTOCOL gPrivateVarHandler = +{ + /* Magic = */ APPLE_GETVAR_PROTOCOL_MAGIC, + AppleGetVar_Unknown0, + AppleGetVar_Unknown1, + AppleGetVar_Unknown2, + AppleGetVar_GetDeviceProps +}; + + +/* + * Unknown Protocol #1. + */ + +/** This seems to be related to graphics/display... */ +EFI_GUID gEfiUnknown1ProtocolGuid = +{ + 0xDD8E06AC, 0x00E2, 0x49A9, {0x88, 0x8F, 0xFA, 0x46, 0xDE, 0xD4, 0x0A, 0x52} +}; + +EFI_STATUS EFIAPI +UnknownHandlerImpl() +{ +#ifdef DEBUG + ASSERT(0); +#endif + Print(L"Unknown called\n"); + return EFI_SUCCESS; +} + +/* array of pointers to function */ +EFI_STATUS (EFIAPI *gUnknownProtoHandler[])() = +{ + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl, + UnknownHandlerImpl +}; + +EFI_STATUS EFIAPI +SetProperVariables(IN EFI_HANDLE ImageHandle, EFI_RUNTIME_SERVICES * rs) +{ + EFI_STATUS rc; + UINT32 vBackgroundClear = 0x00000000; + UINT32 vFwFeatures = 0x80000015; + UINT32 vFwFeaturesMask = 0x800003ff; + + // -legacy acpi=0xffffffff acpi_debug=0xfffffff panic_io_port=0xef11 io=0xfffffffe trace=4096 io=0xffffffef -v serial=2 serialbaud=9600 + // 0x10 makes kdb default, thus 0x15e for kdb, 0x14e for gdb + // usb=0x800 is required to work around default behavior of the Apple xHCI driver which rejects high-speed + // USB devices and tries to force them to EHCI when running on the Intel Panther Point chipset. + + //static const CHAR8 vBootArgs[] = "debug=0x15e keepsyms=1 acpi=0xffffffff acpi_debug=0xff acpi_level=7 -v -x32 -s"; // or just "debug=0x8 -legacy" + // 0x14e for serial output + //static const CHAR8 vDefBootArgs[] = "debug=0x146 usb=0x800 keepsyms=1 -v -serial=0x1"; + static const CHAR8 vDefBootArgs[] = "usb=0x800 keepsyms=1 -v -serial=0x1"; + CHAR8 vBootArgs[256]; + UINT32 BootArgsLen; + + BootArgsLen = GetVmVariable(EFI_INFO_INDEX_BOOT_ARGS, vBootArgs, sizeof vBootArgs); + if (BootArgsLen <= 1) + { + BootArgsLen = sizeof vDefBootArgs; + CopyMem(vBootArgs, vDefBootArgs, BootArgsLen); + } + rc = rs->SetVariable(L"BackgroundClear", + &gEfiAppleNvramGuid, + /* EFI_VARIABLE_NON_VOLATILE | */ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(vBackgroundClear), &vBackgroundClear); + ASSERT_EFI_ERROR (rc); + + rc = rs->SetVariable(L"FirmwareFeatures", + &gEfiAppleNvramGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(vFwFeatures), &vFwFeatures); + ASSERT_EFI_ERROR (rc); + + rc = rs->SetVariable(L"FirmwareFeaturesMask", + &gEfiAppleNvramGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(vFwFeaturesMask), &vFwFeaturesMask); + ASSERT_EFI_ERROR (rc); + + rc = rs->SetVariable(L"boot-args", + &gEfiAppleBootGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + BootArgsLen, &vBootArgs); + ASSERT_EFI_ERROR (rc); + + return EFI_SUCCESS; +} + +/** + * VBoxInitAppleSim entry point. + * + * @returns EFI status code. + * + * @param ImageHandle The image handle. + * @param SystemTable The system table pointer. + */ +EFI_STATUS EFIAPI +VBoxInitAppleSim(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS rc; + UINT64 FSBFrequency; + UINT64 TSCFrequency; + UINT64 CPUFrequency; + + rc = SetProperVariables(ImageHandle, SystemTable->RuntimeServices); + ASSERT_EFI_ERROR(rc); + + rc = gBS->InstallMultipleProtocolInterfaces(&ImageHandle, &gEfiAppleVarGuid, &gPrivateVarHandler, NULL); + ASSERT_EFI_ERROR(rc); + + rc = InitializeDataHub(ImageHandle, SystemTable); + ASSERT_EFI_ERROR(rc); + + GetVmVariable(EFI_INFO_INDEX_FSB_FREQUENCY, (CHAR8 *)&FSBFrequency, sizeof(FSBFrequency)); + GetVmVariable(EFI_INFO_INDEX_TSC_FREQUENCY, (CHAR8 *)&TSCFrequency, sizeof(TSCFrequency)); + GetVmVariable(EFI_INFO_INDEX_CPU_FREQUENCY, (CHAR8 *)&CPUFrequency, sizeof(CPUFrequency)); + + rc = CpuUpdateDataHub(gBS, FSBFrequency, TSCFrequency, CPUFrequency); + ASSERT_EFI_ERROR(rc); + + rc = InitializeConsoleSim(ImageHandle, SystemTable); + ASSERT_EFI_ERROR(rc); + + rc = gBS->InstallMultipleProtocolInterfaces(&ImageHandle, &gEfiUnknown1ProtocolGuid, gUnknownProtoHandler, NULL); + ASSERT_EFI_ERROR(rc); + + return EFI_SUCCESS; +} + +EFI_STATUS EFIAPI +VBoxDeinitAppleSim(IN EFI_HANDLE ImageHandle) +{ + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/VBoxAppleSim.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/VBoxAppleSim.inf new file mode 100644 index 00000000..48d77335 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxAppleSim/VBoxAppleSim.inf @@ -0,0 +1,78 @@ +# $Id: VBoxAppleSim.inf $ +## @file +# VBoxAppleSim - VBox Apple interfaces simulation support. +# + +# +# Copyright (C) 2010-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxAppleSim + FILE_GUID = 17795471-FD9A-46BE-8806-AE787BA73CD4 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + ENTRY_POINT = VBoxInitAppleSim + UNLOAD_IMAGE = VBoxDeinitAppleSim + +[Sources.common] + VBoxAppleSim.c + Cpu.c + Console.c + DataHub.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + DevicePathLib + +[Protocols] + gEfiDevicePathToTextProtocolGuid + +[BuildOptions.common] + + GCC:*_*_*_CC_FLAGS = + INTEL:*_*_*_CC_FLAGS = + MSFT:*_*_*_CC_FLAGS = diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/ReadMe.txt b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/ReadMe.txt new file mode 100644 index 00000000..871ac64f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/ReadMe.txt @@ -0,0 +1,5 @@ +FSW code (all fsw_* files) are based on rEFIt's FSW (filesystem wrapper) +code, revison 448 taken from + + https://refit.svn.sourceforge.net/svnroot/refit/trunk/refit + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxFswParam.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxFswParam.h new file mode 100644 index 00000000..6904ad6d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxFswParam.h @@ -0,0 +1,89 @@ +/* $Id: VBoxFswParam.h $ */ +/** @file + * VBoxFswParam.h + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef VBOXFSPARAM_H +#define VBOXFSPARAM_H +/* + * Here is common declarations for EDK<->EDK2 compatibility + */ +# include <Uefi.h> +# include <Library/DebugLib.h> +# include <Library/BaseLib.h> +# include <Protocol/DriverBinding.h> +# include <Library/BaseMemoryLib.h> +# include <Library/UefiRuntimeServicesTableLib.h> +# include <Library/UefiDriverEntryPoint.h> +# include <Library/UefiBootServicesTableLib.h> +# include <Library/MemoryAllocationLib.h> +# include <Library/DevicePathLib.h> +# include <Protocol/DevicePathFromText.h> +# include <Protocol/DevicePathToText.h> +# include <Protocol/DebugPort.h> +# include <Protocol/DebugSupport.h> +# include <Library/PrintLib.h> +# include <Library/UefiLib.h> +# include <Protocol/SimpleFileSystem.h> +# include <Protocol/BlockIo.h> +# include <Protocol/DiskIo.h> +# include <Guid/FileSystemInfo.h> +# include <Guid/FileInfo.h> +# include <Guid/FileSystemVolumeLabelInfo.h> +# include <Protocol/ComponentName.h> + +# include <Guid/VBoxFsBlessedFileInfo.h> /* For HFS blessed file support. */ + +# define BS gBS +# define PROTO_NAME(x) gEfi ## x ## Guid +# define GUID_NAME(x) gEfi ## x ## Guid + +# define EFI_FILE_HANDLE_REVISION EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION +# define SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL +# define EFI_FILE_SYSTEM_VOLUME_LABEL_INFO EFI_FILE_SYSTEM_VOLUME_LABEL +# define EFI_SIGNATURE_32(a, b, c, d) SIGNATURE_32(a, b, c, d) +# define DivU64x32(x,y,z) DivU64x32((x),(y)) + + +INTN CompareGuidEdk1( + IN EFI_GUID *Guid1, + IN EFI_GUID *Guid2 + ); + +//#define CompareGuid(x, y) CompareGuidEdk1((x),(y)) +# define HOST_EFI 1 +//# define FSW_DEBUG_LEVEL 3 + +int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxHfs.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxHfs.inf new file mode 100644 index 00000000..96f11337 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxHfs.inf @@ -0,0 +1,95 @@ +# $Id: VBoxHfs.inf $ +## @file +# VBoxHfs - VBox HFS FS driver. +# + +# +# Copyright (C) 2010-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxHfs + FILE_GUID = 6E506CC2-65E0-4814-994A-08ECDA046987 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = fsw_efi_main + +[Sources.common] + fsw_hfs.c + fsw_core.c + fsw_efi_lib.c + fsw_efi.c + fsw_lib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + DevicePathLib + +[Guids] + gEfiFileInfoGuid + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + gVBoxFsBlessedFileInfoGuid + +[Protocols] + gEfiDiskIoProtocolGuid + gEfiBlockIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiUnicodeCollationProtocolGuid + gEfiUnicodeCollation2ProtocolGuid + gEfiDevicePathToTextProtocolGuid ## CONSUMED + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang + +[BuildOptions.common] + GCC:*_*_*_CC_FLAGS = -DFSTYPE=hfs -DEFI_LOG_ENABLED=1 +# -DFSW_DEBUG_LEVEL=3 + INTEL:*_*_*_CC_FLAGS = -DFSTYPE=hfs -DEFI_LOG_ENABLED=1 + MSFT:*_*_*_CC_FLAGS = -DFSTYPE=hfs -DEFI_LOG_ENABLED=1 + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxIso9660.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxIso9660.inf new file mode 100644 index 00000000..2f3875ad --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxIso9660.inf @@ -0,0 +1,95 @@ +# $Id: VBoxIso9660.inf $ +## @file +# VBox ISO9660 FS driver +# + +# +# Copyright (C) 2010-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxIso9600 + FILE_GUID = B34E57EE-2E02-4DAF-867F-7F40BE6FC33D + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + ENTRY_POINT = fsw_efi_main + +[Sources.common] + fsw_iso9660.c + fsw_core.c + fsw_efi_lib.c + fsw_efi.c + fsw_lib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + DevicePathLib + +[Guids] + gEfiFileInfoGuid + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + + +[Protocols] + gEfiDiskIoProtocolGuid + gEfiBlockIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiUnicodeCollationProtocolGuid + gEfiUnicodeCollation2ProtocolGuid + gEfiDevicePathToTextProtocolGuid ## CONSUMED + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang + +[BuildOptions.common] + GCC:*_*_*_CC_FLAGS = -DFSTYPE=iso9660 + + INTEL:*_*_*_CC_FLAGS = -DFSTYPE=iso9660 + + MSFT:*_*_*_CC_FLAGS = -DFSTYPE=iso9660 + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_base.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_base.h new file mode 100644 index 00000000..7770b009 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_base.h @@ -0,0 +1,192 @@ +/* $Id: fsw_base.h $ */ +/** @file + * fsw_base.h - Base definitions switch. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_BASE_H_ +#define _FSW_BASE_H_ + +#ifdef VBOX +#include "VBoxFswParam.h" +#endif + +#ifndef FSW_DEBUG_LEVEL +/** + * Global debugging level. Can be set locally for the scope of a single + * file by defining the macro before fsw_base.h is included. + */ +#define FSW_DEBUG_LEVEL 1 +#endif + + +#ifdef HOST_EFI +#include "fsw_efi_base.h" +#endif + +#ifdef HOST_POSIX +#include "fsw_posix_base.h" +#endif + +// message printing + +#if FSW_DEBUG_LEVEL >= 1 +#define FSW_MSG_ASSERT(params) FSW_MSGFUNC params +#else +#define FSW_MSG_ASSERT(params) +#endif + +#if FSW_DEBUG_LEVEL >= 2 +#define FSW_MSG_DEBUG(params) FSW_MSGFUNC params +#else +#define FSW_MSG_DEBUG(params) +#endif + +#if FSW_DEBUG_LEVEL >= 3 +#define FSW_MSG_DEBUGV(params) FSW_MSGFUNC params +#else +#define FSW_MSG_DEBUGV(params) +#endif + + +// Documentation for system-dependent defines + +/** + * \typedef fsw_s8 + * Signed 8-bit integer. + */ + +/** + * \typedef fsw_u8 + * Unsigned 8-bit integer. + */ + +/** + * \typedef fsw_s16 + * Signed 16-bit integer. + */ + +/** + * \typedef fsw_u16 + * Unsigned 16-bit integer. + */ + +/** + * \typedef fsw_s32 + * Signed 32-bit integer. + */ + +/** + * \typedef fsw_u32 + * Unsigned 32-bit integer. + */ + +/** + * \typedef fsw_s64 + * Signed 64-bit integer. + */ + +/** + * \typedef fsw_u64 + * Unsigned 64-bit integer. + */ + + +/** + * \def fsw_alloc(size,ptrptr) + * Allocate memory on the heap. This function or macro allocates \a size + * bytes of memory using host-specific methods. The address of the + * allocated memory block is stored into the pointer variable pointed + * to by \a ptrptr. A status code is returned; FSW_SUCCESS if the block + * was allocated or FSW_OUT_OF_MEMORY if there is not enough memory + * to allocated the requested block. + */ + +/** + * \def fsw_free(ptr) + * Release allocated memory. This function or macro returns an allocated + * memory block to the heap for reuse. Does not return a status. + */ + +/** + * \def fsw_memcpy(dest,src,size) + * Copies a block of memory from \a src to \a dest. The two memory blocks + * must not overlap, or the result of the operation will be undefined. + * Does not return a status. + */ + +/** + * \def fsw_memeq(dest,src,size) + * Compares two blocks of memory for equality. Returns boolean true if the + * memory blocks are equal, boolean false if they are different. + */ + +/** + * \def fsw_memzero(dest,size) + * Initializes a block of memory with zeros. Does not return a status. + */ + + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.c new file mode 100644 index 00000000..6c39a814 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.c @@ -0,0 +1,961 @@ +/* $Id: fsw_core.c $ */ +/** @file + * fsw_core.c - Core file system wrapper abstraction layer code. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_core.h" + + +// functions + +static void fsw_blockcache_free(struct fsw_volume *vol); + +#define MAX_CACHE_LEVEL (5) + + +/** + * Mount a volume with a given file system driver. This function is called by the + * host driver to make a volume accessible. The file system driver to use is specified + * by a pointer to its dispatch table. The file system driver will look at the + * data on the volume to determine if it can read the format. If the volume is found + * unsuitable, FSW_UNSUPPORTED is returned. + * + * If this function returns FSW_SUCCESS, *vol_out points at a valid volume data + * structure. The caller must release it later by calling fsw_unmount. + * + * If this function returns an error status, the caller only needs to clean up its + * own buffers that may have been allocated through the read_block interface. + */ + +fsw_status_t fsw_mount(void *host_data, + struct fsw_host_table *host_table, + struct fsw_fstype_table *fstype_table, + struct fsw_volume **vol_out) +{ + fsw_status_t status; + struct fsw_volume *vol; + + // allocate memory for the structure + status = fsw_alloc_zero(fstype_table->volume_struct_size, (void **)&vol); + if (status) + return status; + + // initialize fields + vol->phys_blocksize = 512; + vol->log_blocksize = 512; + vol->label.type = FSW_STRING_TYPE_EMPTY; + vol->host_data = host_data; + vol->host_table = host_table; + vol->fstype_table = fstype_table; + vol->host_string_type = host_table->native_string_type; + + // let the fs driver mount the file system + status = vol->fstype_table->volume_mount(vol); + if (status) + goto errorexit; + + /// @todo anything else? + + *vol_out = vol; + return FSW_SUCCESS; + +errorexit: + fsw_unmount(vol); + return status; +} + +/** + * Unmount a volume by releasing all memory associated with it. This function is + * called by the host driver when a volume is no longer needed. It is also called + * by the core after a failed mount to clean up any allocated memory. + * + * Note that all dnodes must have been released before calling this function. + */ + +void fsw_unmount(struct fsw_volume *vol) +{ + if (vol->root) + fsw_dnode_release(vol->root); + /// @todo check that no other dnodes are still around + + vol->fstype_table->volume_free(vol); + + fsw_blockcache_free(vol); + fsw_strfree(&vol->label); + fsw_free(vol); +} + +/** + * Get in-depth information on the volume. This function can be called by the host + * driver to get additional information on the volume. + */ + +fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb) +{ + return vol->fstype_table->volume_stat(vol, sb); +} + +/** + * Set the physical and logical block sizes of the volume. This functions is called by + * the file system driver to announce the block sizes it wants to use for accessing + * the disk (physical) and for addressing file contents (logical). + * Usually both sizes will be the same but there may be file systems that need to access + * metadata at a smaller block size than the allocation unit for files. + * + * Calling this function causes the block cache to be dropped. All pointers returned + * from fsw_block_get become invalid. This function should only be called while + * mounting the file system, not as a part of file access operations. + * + * Both sizes are measured in bytes, must be powers of 2, and must not be smaller + * than 512 bytes. The logical block size cannot be smaller than the physical block size. + */ + +void fsw_set_blocksize(struct fsw_volume *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize) +{ + /// @todo Check the sizes. Both must be powers of 2. log_blocksize must not be smaller than + // phys_blocksize. + + // drop core block cache if present + fsw_blockcache_free(vol); + + // signal host driver to drop caches etc. + vol->host_table->change_blocksize(vol, + vol->phys_blocksize, vol->log_blocksize, + phys_blocksize, log_blocksize); + + vol->phys_blocksize = phys_blocksize; + vol->log_blocksize = log_blocksize; +} + +/** + * Get a block of data from the disk. This function is called by the file system driver + * or by core functions. It calls through to the host driver's device access routine. + * Given a physical block number, it reads the block into memory (or fetches it from the + * block cache) and returns the address of the memory buffer. The caller should provide + * an indication of how important the block is in the cache_level parameter. Blocks with + * a low level are purged first. Some suggestions for cache levels: + * + * - 0: File data + * - 1: Directory data, symlink data + * - 2: File system metadata + * - 3..5: File system metadata with a high rate of access + * + * If this function returns successfully, the returned data pointer is valid until the + * caller calls fsw_block_release. + */ + +fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out) +{ + fsw_status_t status; + fsw_u32 i, discard_level, new_bcache_size; + struct fsw_blockcache *new_bcache; + + /// @todo allow the host driver to do its own caching; just call through if + // the appropriate function pointers are set + + if (cache_level > MAX_CACHE_LEVEL) + cache_level = MAX_CACHE_LEVEL; + + // check block cache + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == phys_bno) { + // cache hit! + if (vol->bcache[i].cache_level < cache_level) + vol->bcache[i].cache_level = cache_level; // promote the entry + vol->bcache[i].refcount++; + *buffer_out = vol->bcache[i].data; + return FSW_SUCCESS; + } + } + + // find a free entry in the cache table + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == FSW_INVALID_BNO) + break; + } + if (i >= vol->bcache_size) { + for (discard_level = 0; discard_level <= MAX_CACHE_LEVEL; discard_level++) { + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].refcount == 0 && vol->bcache[i].cache_level <= discard_level) + break; + } + if (i < vol->bcache_size) + break; + } + } + if (i >= vol->bcache_size) { + // enlarge / create the cache + if (vol->bcache_size < 16) + new_bcache_size = 16; + else + new_bcache_size = vol->bcache_size << 1; + status = fsw_alloc(new_bcache_size * sizeof(struct fsw_blockcache), &new_bcache); + if (status) + return status; + if (vol->bcache_size > 0) + fsw_memcpy(new_bcache, vol->bcache, vol->bcache_size * sizeof(struct fsw_blockcache)); + for (i = vol->bcache_size; i < new_bcache_size; i++) { + new_bcache[i].refcount = 0; + new_bcache[i].cache_level = 0; + new_bcache[i].phys_bno = FSW_INVALID_BNO; + new_bcache[i].data = NULL; + } + i = vol->bcache_size; + + // switch caches + if (vol->bcache != NULL) + fsw_free(vol->bcache); + vol->bcache = new_bcache; + vol->bcache_size = new_bcache_size; + } + vol->bcache[i].phys_bno = FSW_INVALID_BNO; + + // read the data + if (vol->bcache[i].data == NULL) { + status = fsw_alloc(vol->phys_blocksize, &vol->bcache[i].data); + if (status) + return status; + } + status = vol->host_table->read_block(vol, phys_bno, vol->bcache[i].data); + if (status) + return status; + + vol->bcache[i].phys_bno = phys_bno; + vol->bcache[i].cache_level = cache_level; + vol->bcache[i].refcount = 1; + *buffer_out = vol->bcache[i].data; + return FSW_SUCCESS; +} + +/** + * Releases a disk block. This function must be called to release disk blocks returned + * from fsw_block_get. + */ + +void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer) +{ + fsw_u32 i; + + /// @todo allow the host driver to do its own caching; just call through if + // the appropriate function pointers are set + + // update block cache + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == phys_bno && vol->bcache[i].refcount > 0) + vol->bcache[i].refcount--; + } +} + +/** + * Release the block cache. Called internally when changing block sizes and when + * unmounting the volume. It frees all data occupied by the generic block cache. + */ + +static void fsw_blockcache_free(struct fsw_volume *vol) +{ + fsw_u32 i; + + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].data != NULL) + fsw_free(vol->bcache[i].data); + } + if (vol->bcache != NULL) { + fsw_free(vol->bcache); + vol->bcache = NULL; + } + vol->bcache_size = 0; +} + +/** + * Add a new dnode to the list of known dnodes. This internal function is used when a + * dnode is created to add it to the dnode list that is used to search for existing + * dnodes by id. + */ + +static void fsw_dnode_register(struct fsw_volume *vol, struct fsw_dnode *dno) +{ + dno->next = vol->dnode_head; + if (vol->dnode_head != NULL) + vol->dnode_head->prev = dno; + dno->prev = NULL; + vol->dnode_head = dno; +} + +/** + * Create a dnode representing the root directory. This function is called by the file system + * driver while mounting the file system. The root directory is special because it has no parent + * dnode, its name is defined to be empty, and its type is also fixed. Otherwise, this functions + * behaves in the same way as fsw_dnode_create. + */ + +fsw_status_t fsw_dnode_create_root(struct fsw_volume *vol, fsw_u32 dnode_id, struct fsw_dnode **dno_out) +{ + fsw_status_t status; + struct fsw_dnode *dno; + + // allocate memory for the structure + status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno); + if (status) + return status; + + // fill the structure + dno->vol = vol; + dno->parent = NULL; + dno->dnode_id = dnode_id; + dno->type = FSW_DNODE_TYPE_DIR; + dno->refcount = 1; + dno->name.type = FSW_STRING_TYPE_EMPTY; + /// @todo instead, call a function to create an empty string in the native string type + + fsw_dnode_register(vol, dno); + + *dno_out = dno; + return FSW_SUCCESS; +} + +/** + * Create a new dnode representing a file system object. This function is called by + * the file system driver in response to directory lookup or read requests. Note that + * if there already is a dnode with the given dnode_id on record, then no new object + * is created. Instead, the existing dnode is returned and its reference count + * increased. All other parameters are ignored in this case. + * + * The type passed into this function may be FSW_DNODE_TYPE_UNKNOWN. It is sufficient + * to fill the type field during the dnode_fill call. + * + * The name parameter must describe a string with the object's name. A copy will be + * stored in the dnode structure for future reference. The name will not be used to + * shortcut directory lookups, but may be used to reconstruct paths. + * + * If the function returns successfully, *dno_out contains a pointer to the dnode + * that must be released by the caller with fsw_dnode_release. + */ + +fsw_status_t fsw_dnode_create(struct fsw_dnode *parent_dno, fsw_u32 dnode_id, int type, + struct fsw_string *name, struct fsw_dnode **dno_out) +{ + fsw_status_t status; + struct fsw_volume *vol = parent_dno->vol; + struct fsw_dnode *dno; + + // check if we already have a dnode with the same id + for (dno = vol->dnode_head; dno; dno = dno->next) { + if (dno->dnode_id == dnode_id) { + fsw_dnode_retain(dno); + *dno_out = dno; + return FSW_SUCCESS; + } + } + + // allocate memory for the structure + status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno); + if (status) + return status; + + // fill the structure + dno->vol = vol; + dno->parent = parent_dno; + fsw_dnode_retain(dno->parent); + dno->dnode_id = dnode_id; + dno->type = type; + dno->refcount = 1; + status = fsw_strdup_coerce(&dno->name, vol->host_table->native_string_type, name); + if (status) { + fsw_free(dno); + return status; + } + + fsw_dnode_register(vol, dno); + + *dno_out = dno; + return FSW_SUCCESS; +} + +/** + * Increases the reference count of a dnode. This must be balanced with + * fsw_dnode_release calls. Note that some dnode functions return a retained + * dnode pointer to their caller. + */ + +void fsw_dnode_retain(struct fsw_dnode *dno) +{ + dno->refcount++; +} + +/** + * Release a dnode pointer, deallocating it if this was the last reference. + * This function decrements the reference counter of the dnode. If the counter + * reaches zero, the dnode is freed. Since the parent dnode is released + * during that process, this function may cause it to be freed, too. + */ + +void fsw_dnode_release(struct fsw_dnode *dno) +{ + struct fsw_volume *vol = dno->vol; + struct fsw_dnode *parent_dno; + + dno->refcount--; + + if (dno->refcount == 0) { + parent_dno = dno->parent; + + // de-register from volume's list + if (dno->next) + dno->next->prev = dno->prev; + if (dno->prev) + dno->prev->next = dno->next; + if (vol->dnode_head == dno) + vol->dnode_head = dno->next; + + // run fstype-specific cleanup + vol->fstype_table->dnode_free(vol, dno); + + fsw_strfree(&dno->name); + fsw_free(dno); + + // release our pointer to the parent, possibly deallocating it, too + if (parent_dno) + fsw_dnode_release(parent_dno); + } +} + +/** + * Get full information about a dnode from disk. This function is called by the host + * driver as well as by the core functions. Some file systems defer reading full + * information on a dnode until it is actually needed (i.e. separation between + * directory and inode information). This function makes sure that all information + * is available in the dnode structure. The following fields may not have a correct + * value until fsw_dnode_fill has been called: + * + * type, size + */ + +fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno) +{ + /// @todo check a flag right here, call fstype's dnode_fill only once per dnode + + return dno->vol->fstype_table->dnode_fill(dno->vol, dno); +} + +/** + * Get extended information about a dnode. This function can be called by the host + * driver to get a full compliment of information about a dnode in addition to the + * fields of the fsw_dnode structure itself. + * + * Some data requires host-specific conversion to be useful (i.e. timestamps) and + * will be passed to callback functions instead of being written into the structure. + * These callbacks must be filled in by the caller. + */ + +fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb) +{ + fsw_status_t status; + + status = fsw_dnode_fill(dno); + if (status) + return status; + + sb->used_bytes = 0; + status = dno->vol->fstype_table->dnode_stat(dno->vol, dno, sb); + if (!status && !sb->used_bytes) + sb->used_bytes = FSW_U64_DIV(dno->size + dno->vol->log_blocksize - 1, dno->vol->log_blocksize); + return status; +} + +/** + * Lookup a directory entry by name. This function is called by the host driver. + * Given a directory dnode and a file name, it looks up the named entry in the + * directory. + * + * If the dnode is not a directory, the call will fail. The caller is responsible for + * resolving symbolic links before calling this function. + * + * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory + * entry. The caller must call fsw_dnode_release on it. + */ + +fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno, + struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out) +{ + fsw_status_t status; + + status = fsw_dnode_fill(dno); + if (status) + return status; + if (dno->type != FSW_DNODE_TYPE_DIR) + return FSW_UNSUPPORTED; + + return dno->vol->fstype_table->dir_lookup(dno->vol, dno, lookup_name, child_dno_out); +} + +/** + * Find a file system object by path. This function is called by the host driver. + * Given a directory dnode and a relative or absolute path, it walks the directory + * tree until it finds the target dnode. If an intermediate node turns out to be + * a symlink, it is resolved automatically. If the target node is a symlink, it + * is not resolved. + * + * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory + * entry. The caller must call fsw_dnode_release on it. + */ + +fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno, + struct fsw_string *lookup_path, char separator, + struct fsw_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_volume *vol = dno->vol; + struct fsw_dnode *child_dno = NULL; + struct fsw_string lookup_name; + struct fsw_string remaining_path; + int root_if_empty; + + remaining_path = *lookup_path; + fsw_dnode_retain(dno); + + // loop over the path + for (root_if_empty = 1; fsw_strlen(&remaining_path) > 0; root_if_empty = 0) { + // parse next path component + fsw_strsplit(&lookup_name, &remaining_path, separator); + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: split into %d '%s' and %d '%s'\n"), + lookup_name.len, lookup_name.data, + remaining_path.len, remaining_path.data)); + + if (fsw_strlen(&lookup_name) == 0) { // empty path component + if (root_if_empty) + child_dno = vol->root; + else + child_dno = dno; + fsw_dnode_retain(child_dno); + + } else { + // do an actual directory lookup + + // ensure we have full information + status = fsw_dnode_fill(dno); + if (status) + goto errorexit; + + // resolve symlink if necessary + if (dno->type == FSW_DNODE_TYPE_SYMLINK) { + status = fsw_dnode_resolve(dno, &child_dno); + if (status) + goto errorexit; + + // symlink target becomes the new dno + fsw_dnode_release(dno); + dno = child_dno; // is already retained + child_dno = NULL; + + // ensure we have full information + status = fsw_dnode_fill(dno); + if (status) + goto errorexit; + } + + // make sure we operate on a directory + if (dno->type != FSW_DNODE_TYPE_DIR) { + return FSW_UNSUPPORTED; + goto errorexit; + } + + // check special paths + if (fsw_streq_cstr(&lookup_name, ".")) { // self directory + child_dno = dno; + fsw_dnode_retain(child_dno); + + } else if (fsw_streq_cstr(&lookup_name, "..")) { // parent directory + if (dno->parent == NULL) { + // We cannot go up from the root directory. Caution: Certain apps like the EFI shell + // rely on this behaviour! + status = FSW_NOT_FOUND; + goto errorexit; + } + child_dno = dno->parent; + fsw_dnode_retain(child_dno); + + } else { + // do an actual lookup + status = vol->fstype_table->dir_lookup(vol, dno, &lookup_name, &child_dno); + if (status) + goto errorexit; + } + } + + // child_dno becomes the new dno + fsw_dnode_release(dno); + dno = child_dno; // is already retained + child_dno = NULL; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: now at inode %d\n"), dno->dnode_id)); + } + + *child_dno_out = dno; + return FSW_SUCCESS; + +errorexit: + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: leaving with error %d\n"), status)); + fsw_dnode_release(dno); + if (child_dno != NULL) + fsw_dnode_release(child_dno); + return status; +} + +/** + * Get the next directory item in sequential order. This function is called by the + * host driver to read the complete contents of a directory in sequential (file system + * defined) order. Calling this function returns the next entry. Iteration state is + * kept by a shandle on the directory's dnode. The caller must set up the shandle + * when starting the iteration. + * + * When the end of the directory is reached, this function returns FSW_NOT_FOUND. + * If the function returns FSW_SUCCESS, *child_dno_out points to the next directory + * entry. The caller must call fsw_dnode_release on it. + */ + +fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_dnode *dno = shand->dnode; + fsw_u64 saved_pos; + + if (dno->type != FSW_DNODE_TYPE_DIR) + return FSW_UNSUPPORTED; + + saved_pos = shand->pos; + status = dno->vol->fstype_table->dir_read(dno->vol, dno, shand, child_dno_out); + if (status) + shand->pos = saved_pos; + return status; +} + +/** + * Read the target path of a symbolic link. This function is called by the host driver + * to read the "content" of a symbolic link, that is the relative or absolute path + * it points to. + * + * If the function returns FSW_SUCCESS, the string handle provided by the caller is + * filled with a string in the host's preferred encoding. The caller is responsible + * for calling fsw_strfree on the string. + */ + +fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *target_name) +{ + fsw_status_t status; + + status = fsw_dnode_fill(dno); + if (status) + return status; + if (dno->type != FSW_DNODE_TYPE_SYMLINK) + return FSW_UNSUPPORTED; + + return dno->vol->fstype_table->readlink(dno->vol, dno, target_name); +} + +/** + * Read the target path of a symbolic link by accessing file data. This function can + * be called by the file system driver if the file system stores the target path + * as normal file data. This function will open an shandle, read the whole content + * of the file into a buffer, and build a string from that. Currently the encoding + * for the string is fixed as FSW_STRING_TYPE_ISO88591. + * + * If the function returns FSW_SUCCESS, the string handle provided by the caller is + * filled with a string in the host's preferred encoding. The caller is responsible + * for calling fsw_strfree on the string. + */ + +fsw_status_t fsw_dnode_readlink_data(struct fsw_dnode *dno, struct fsw_string *link_target) +{ + fsw_status_t status; + struct fsw_shandle shand; + fsw_u32 buffer_size; + char buffer[FSW_PATH_MAX]; + + struct fsw_string s; + + if (dno->size > FSW_PATH_MAX) + return FSW_VOLUME_CORRUPTED; + + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = (int)dno->size; + s.data = buffer; + + // open shandle and read the data + status = fsw_shandle_open(dno, &shand); + if (status) + return status; + buffer_size = (fsw_u32)s.size; + status = fsw_shandle_read(&shand, &buffer_size, buffer); + fsw_shandle_close(&shand); + if (status) + return status; + if ((int)buffer_size < s.size) + return FSW_VOLUME_CORRUPTED; + + status = fsw_strdup_coerce(link_target, dno->vol->host_string_type, &s); + return status; +} + +/** + * Resolve a symbolic link. This function can be called by the host driver to make + * sure the a dnode is fully resolved instead of pointing at a symlink. If the dnode + * passed in is not a symlink, it is returned unmodified. + * + * Note that absolute paths will be resolved relative to the root directory of the + * volume. If the host is an operating system with its own VFS layer, it should + * resolve symlinks on its own. + * + * If the function returns FSW_SUCCESS, *target_dno_out points at a dnode that is + * not a symlink. The caller is responsible for calling fsw_dnode_release on it. + */ + +fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out) +{ + fsw_status_t status; + struct fsw_string target_name; + struct fsw_dnode *target_dno; + + fsw_dnode_retain(dno); + + while (1) { + // get full information + status = fsw_dnode_fill(dno); + if (status) + goto errorexit; + if (dno->type != FSW_DNODE_TYPE_SYMLINK) { + // found a non-symlink target, return it + *target_dno_out = dno; + return FSW_SUCCESS; + } + if (dno->parent == NULL) { // safety measure, cannot happen in theory + status = FSW_NOT_FOUND; + goto errorexit; + } + + // read the link's target + status = fsw_dnode_readlink(dno, &target_name); + if (status) + goto errorexit; + + // resolve it + status = fsw_dnode_lookup_path(dno->parent, &target_name, '/', &target_dno); + fsw_strfree(&target_name); + if (status) + goto errorexit; + + // target_dno becomes the new dno + fsw_dnode_release(dno); + dno = target_dno; // is already retained + } + +errorexit: + fsw_dnode_release(dno); + return status; +} + +/** + * Set up a shandle (storage handle) to access a file's data. This function is called + * by the host driver and by the core when they need to access a file's data. It is also + * used in accessing the raw data of directories and symlinks if the file system uses + * the same mechanisms for storing the data of those items. + * + * The storage for the fsw_shandle structure is provided by the caller. The dnode and pos + * fields may be accessed, pos may also be written to to set the file pointer. The file's + * data size is available as shand->dnode->size. + * + * If this function returns FSW_SUCCESS, the caller must call fsw_shandle_close to release + * the dnode reference held by the shandle. + */ + +fsw_status_t fsw_shandle_open(struct fsw_dnode *dno, struct fsw_shandle *shand) +{ + fsw_status_t status; + struct fsw_volume *vol = dno->vol; + + // read full dnode information into memory + status = vol->fstype_table->dnode_fill(vol, dno); + if (status) + return status; + + // setup shandle + fsw_dnode_retain(dno); + + shand->dnode = dno; + shand->pos = 0; + shand->extent.type = FSW_EXTENT_TYPE_INVALID; + + return FSW_SUCCESS; +} + +/** + * Close a shandle after accessing the dnode's data. This function is called by the host + * driver or core functions when they are finished with accessing a file's data. It + * releases the dnode reference and frees any buffers associated with the shandle itself. + * The dnode is only released if this was the last reference using it. + */ + +void fsw_shandle_close(struct fsw_shandle *shand) +{ + if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) + fsw_free(shand->extent.buffer); + fsw_dnode_release(shand->dnode); +} + +/** + * Read data from a shandle (storage handle for a dnode). This function is called by the + * host driver or internally when data is read from a file. TODO: more + */ + +fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer_in) +{ + fsw_status_t status; + struct fsw_dnode *dno = shand->dnode; + struct fsw_volume *vol = dno->vol; + fsw_u8 *buffer, *block_buffer; + fsw_u32 buflen, copylen, pos; + fsw_u32 log_bno, pos_in_extent, phys_bno, pos_in_physblock; + fsw_u32 cache_level; + + if (shand->pos >= dno->size) { // already at EOF + *buffer_size_inout = 0; + return FSW_SUCCESS; + } + + // initialize vars + buffer = buffer_in; + buflen = *buffer_size_inout; + pos = (fsw_u32)shand->pos; + cache_level = (dno->type != FSW_DNODE_TYPE_FILE) ? 1 : 0; + // restrict read to file size + if (buflen > dno->size - pos) + buflen = (fsw_u32)(dno->size - pos); + + while (buflen > 0) { + // get extent for the current logical block + log_bno = pos / vol->log_blocksize; + if (shand->extent.type == FSW_EXTENT_TYPE_INVALID || + log_bno < shand->extent.log_start || + log_bno >= shand->extent.log_start + shand->extent.log_count) { + + if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) + fsw_free(shand->extent.buffer); + + // ask the file system for the proper extent + shand->extent.log_start = log_bno; + status = vol->fstype_table->get_extent(vol, dno, &shand->extent); + if (status) { + shand->extent.type = FSW_EXTENT_TYPE_INVALID; + return status; + } + } + + pos_in_extent = pos - shand->extent.log_start * vol->log_blocksize; + + // dispatch by extent type + if (shand->extent.type == FSW_EXTENT_TYPE_PHYSBLOCK) { + // convert to physical block number and offset + phys_bno = shand->extent.phys_start + pos_in_extent / vol->phys_blocksize; + pos_in_physblock = pos_in_extent & (vol->phys_blocksize - 1); + copylen = vol->phys_blocksize - pos_in_physblock; + if (copylen > buflen) + copylen = buflen; + + // get one physical block + status = fsw_block_get(vol, phys_bno, cache_level, (void **)&block_buffer); + if (status) + return status; + + // copy data from it + fsw_memcpy(buffer, block_buffer + pos_in_physblock, copylen); + fsw_block_release(vol, phys_bno, block_buffer); + + } else if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) { + copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent; + if (copylen > buflen) + copylen = buflen; + fsw_memcpy(buffer, (fsw_u8 *)shand->extent.buffer + pos_in_extent, copylen); + + } else { // _SPARSE or _INVALID + copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent; + if (copylen > buflen) + copylen = buflen; + fsw_memzero(buffer, copylen); + + } + + buffer += copylen; + buflen -= copylen; + pos += copylen; + } + + *buffer_size_inout = (fsw_u32)(pos - shand->pos); + shand->pos = pos; + + return FSW_SUCCESS; +} + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.h new file mode 100644 index 00000000..4bcb1416 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.h @@ -0,0 +1,554 @@ +/* $Id: fsw_core.h $ */ +/** @file + * fsw_core.h - Core file system wrapper abstraction layer header. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * Portions Copyright (c) The Regents of the University of California. + * Portions Copyright (c) UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_CORE_H_ +#define _FSW_CORE_H_ + +#include "fsw_base.h" + + +/** Maximum size for a path, specifically symlink target paths. */ +#ifndef VBOX +#define FSW_PATH_MAX (4096) +#else +/* Too big allocations are handled with alloca() */ +#define FSW_PATH_MAX (2048) +#endif + +/** Helper macro for token concatenation. */ +#define FSW_CONCAT3(a,b,c) a##b##c +/** Expands to the name of a fstype dispatch table (fsw_fstype_table) for a named file system type. */ +#define FSW_FSTYPE_TABLE_NAME(t) FSW_CONCAT3(fsw_,t,_table) + +/** Indicates that the block cache entry is empty. */ +#define FSW_INVALID_BNO (~0U) + + +// +// Byte-swapping macros +// + + +/** + * \name Byte Order Macros + * Implements big endian vs. little endian awareness and conversion. + */ +/*@{*/ + +typedef fsw_u16 fsw_u16_le; +typedef fsw_u16 fsw_u16_be; +typedef fsw_u32 fsw_u32_le; +typedef fsw_u32 fsw_u32_be; +typedef fsw_u64 fsw_u64_le; +typedef fsw_u64 fsw_u64_be; + +#define FSW_SWAPVALUE_U16(v) ((((fsw_u16)(v) & 0xff00) >> 8) | \ + (((fsw_u16)(v) & 0x00ff) << 8)) +#define FSW_SWAPVALUE_U32(v) ((((fsw_u32)(v) & 0xff000000UL) >> 24) | \ + (((fsw_u32)(v) & 0x00ff0000UL) >> 8) | \ + (((fsw_u32)(v) & 0x0000ff00UL) << 8) | \ + (((fsw_u32)(v) & 0x000000ffUL) << 24)) +#define FSW_SWAPVALUE_U64(v) ((((fsw_u64)(v) & 0xff00000000000000ULL) >> 56) | \ + (((fsw_u64)(v) & 0x00ff000000000000ULL) >> 40) | \ + (((fsw_u64)(v) & 0x0000ff0000000000ULL) >> 24) | \ + (((fsw_u64)(v) & 0x000000ff00000000ULL) >> 8) | \ + (((fsw_u64)(v) & 0x00000000ff000000ULL) << 8) | \ + (((fsw_u64)(v) & 0x0000000000ff0000ULL) << 24) | \ + (((fsw_u64)(v) & 0x000000000000ff00ULL) << 40) | \ + (((fsw_u64)(v) & 0x00000000000000ffULL) << 56)) + +#ifdef FSW_LITTLE_ENDIAN + +#define fsw_u16_le_swap(v) (v) +#define fsw_u16_be_swap(v) FSW_SWAPVALUE_U16(v) +#define fsw_u32_le_swap(v) (v) +#define fsw_u32_be_swap(v) FSW_SWAPVALUE_U32(v) +#define fsw_u64_le_swap(v) (v) +#define fsw_u64_be_swap(v) FSW_SWAPVALUE_U64(v) + +#define fsw_u16_le_sip(var) +#define fsw_u16_be_sip(var) (var = FSW_SWAPVALUE_U16(var)) +#define fsw_u32_le_sip(var) +#define fsw_u32_be_sip(var) (var = FSW_SWAPVALUE_U32(var)) +#define fsw_u64_le_sip(var) +#define fsw_u64_be_sip(var) (var = FSW_SWAPVALUE_U64(var)) + +#else +#ifdef FSW_BIG_ENDIAN + +#define fsw_u16_le_swap(v) FSW_SWAPVALUE_U16(v) +#define fsw_u16_be_swap(v) (v) +#define fsw_u32_le_swap(v) FSW_SWAPVALUE_U32(v) +#define fsw_u32_be_swap(v) (v) +#define fsw_u64_le_swap(v) FSW_SWAPVALUE_U64(v) +#define fsw_u64_be_swap(v) (v) + +#define fsw_u16_le_sip(var) (var = FSW_SWAPVALUE_U16(var)) +#define fsw_u16_be_sip(var) +#define fsw_u32_le_sip(var) (var = FSW_SWAPVALUE_U32(var)) +#define fsw_u32_be_sip(var) +#define fsw_u64_le_sip(var) (var = FSW_SWAPVALUE_U64(var)) +#define fsw_u64_be_sip(var) + +#else +#fail Neither FSW_BIG_ENDIAN nor FSW_LITTLE_ENDIAN are defined +#endif +#endif + +/*@}*/ + + +// +// The following evil hack avoids a lot of casts between generic and fstype-specific +// structures. +// + +#ifndef VOLSTRUCTNAME +#define VOLSTRUCTNAME fsw_volume +#else +struct VOLSTRUCTNAME; +#endif +#ifndef DNODESTRUCTNAME +#define DNODESTRUCTNAME fsw_dnode +#else +struct DNODESTRUCTNAME; +#endif + + +/** + * Status code type, returned from all functions that can fail. + */ +typedef int fsw_status_t; + +/** + * Possible status codes. + */ +enum { + FSW_SUCCESS, + FSW_OUT_OF_MEMORY, + FSW_IO_ERROR, + FSW_UNSUPPORTED, + FSW_NOT_FOUND, + FSW_VOLUME_CORRUPTED, + FSW_UNKNOWN_ERROR +}; + + +/** + * Core: A string with explicit length and encoding information. + */ + +struct fsw_string { + int type; //!< Encoding of the string - empty, ISO-8859-1, UTF8, UTF16 + int len; //!< Length in characters + int size; //!< Total data size in bytes + void *data; //!< Data pointer (may be NULL if type is EMPTY or len is zero) +}; + +/** + * Possible string types / encodings. In the case of FSW_STRING_TYPE_EMPTY, + * all other members of the fsw_string structure may be invalid. + */ +enum { + FSW_STRING_TYPE_EMPTY, + FSW_STRING_TYPE_ISO88591, + FSW_STRING_TYPE_UTF8, + FSW_STRING_TYPE_UTF16, + FSW_STRING_TYPE_UTF16_SWAPPED +}; + +#ifdef FSW_LITTLE_ENDIAN +#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16 +#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16_SWAPPED +#else +#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16_SWAPPED +#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16 +#endif + +/** Static initializer for an empty string. */ +#define FSW_STRING_INIT { FSW_STRING_TYPE_EMPTY, 0, 0, NULL } + + +/* forward declarations */ + +struct fsw_dnode; +struct fsw_host_table; +struct fsw_fstype_table; + +struct fsw_blockcache { + fsw_u32 refcount; //!< Reference count + fsw_u32 cache_level; //!< Level of importance of this block + fsw_u32 phys_bno; //!< Physical block number + void *data; //!< Block data buffer +}; + +/** + * Core: Represents a mounted volume. + */ + +struct fsw_volume { + fsw_u32 phys_blocksize; //!< Block size for disk access / file system structures + fsw_u32 log_blocksize; //!< Block size for logical file data + + struct DNODESTRUCTNAME *root; //!< Root directory dnode + struct fsw_string label; //!< Volume label + + struct fsw_dnode *dnode_head; //!< List of all dnodes allocated for this volume + + struct fsw_blockcache *bcache; //!< Array of block cache entries + fsw_u32 bcache_size; //!< Number of entries in the block cache array + + void *host_data; //!< Hook for a host-specific data structure + struct fsw_host_table *host_table; //!< Dispatch table for host-specific functions + struct fsw_fstype_table *fstype_table; //!< Dispatch table for file system specific functions + int host_string_type; //!< String type used by the host environment +}; + +/** + * Core: Represents a "directory node" - a file, directory, symlink, whatever. + */ + +struct fsw_dnode { + fsw_u32 refcount; //!< Reference count + + struct VOLSTRUCTNAME *vol; //!< The volume this dnode belongs to + struct DNODESTRUCTNAME *parent; //!< Parent directory dnode + struct fsw_string name; //!< Name of this item in the parent directory + + fsw_u32 dnode_id; //!< Unique id number (usually the inode number) + int type; //!< Type of the dnode - file, dir, symlink, special + fsw_u64 size; //!< Data size in bytes + + struct fsw_dnode *next; //!< Doubly-linked list of all dnodes: previous dnode + struct fsw_dnode *prev; //!< Doubly-linked list of all dnodes: next dnode +}; + +/** + * Possible dnode types. FSW_DNODE_TYPE_UNKNOWN may only be used before + * fsw_dnode_fill has been called on the dnode. + */ +enum { + FSW_DNODE_TYPE_UNKNOWN, + FSW_DNODE_TYPE_FILE, + FSW_DNODE_TYPE_DIR, + FSW_DNODE_TYPE_SYMLINK, + FSW_DNODE_TYPE_SPECIAL +}; + +/** + * Core: Stores the mapping of a region of a file to the data on disk. + */ + +struct fsw_extent { + int type; //!< Type of extent specification + fsw_u32 log_start; //!< Starting logical block number + fsw_u32 log_count; //!< Logical block count + fsw_u32 phys_start; //!< Starting physical block number (for FSW_EXTENT_TYPE_PHYSBLOCK only) + void *buffer; //!< Allocated buffer pointer (for FSW_EXTENT_TYPE_BUFFER only) +}; + +/** + * Possible extent representation types. FSW_EXTENT_TYPE_INVALID is for shandle's + * internal use only, it must not be returned from a get_extent function. + */ +enum { + FSW_EXTENT_TYPE_INVALID, + FSW_EXTENT_TYPE_SPARSE, + FSW_EXTENT_TYPE_PHYSBLOCK, + FSW_EXTENT_TYPE_BUFFER +}; + +/** + * Core: An access structure to a dnode's raw data. There can be multiple + * shandles per dnode, each of them has its own position pointer. + */ + +struct fsw_shandle { + struct fsw_dnode *dnode; //!< The dnode this handle reads data from + + fsw_u64 pos; //!< Current file pointer in bytes + struct fsw_extent extent; //!< Current extent +}; + +/** + * Core: Used in gathering detailed information on a volume. + */ + +struct fsw_volume_stat { + fsw_u64 total_bytes; //!< Total size of data area size in bytes + fsw_u64 free_bytes; //!< Bytes still available for storing file data +}; + +/** + * Core: Used in gathering detailed information on a dnode. + */ + +struct fsw_dnode_stat { + fsw_u64 used_bytes; //!< Bytes actually used by the file on disk + void (*store_time_posix)(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time); //!< Callback for storing a Posix-style timestamp + void (*store_attr_posix)(struct fsw_dnode_stat *sb, fsw_u16 posix_mode); //!< Callback for storing a Posix-style file mode + void *host_data; //!< Hook for a host-specific data structure +}; + +/** + * Type of the timestamp passed into store_time_posix. + */ +enum { + FSW_DNODE_STAT_CTIME, + FSW_DNODE_STAT_MTIME, + FSW_DNODE_STAT_ATIME +}; + +/** + * Core: Function table for a host environment. + */ + +struct fsw_host_table +{ + int native_string_type; //!< String type used by the host environment + + void (*change_blocksize)(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize); + fsw_status_t (*read_block)(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer); +}; + +/** + * Core: Function table for a file system driver. + */ + +struct fsw_fstype_table +{ + struct fsw_string name; //!< String giving the name of the file system + fsw_u32 volume_struct_size; //!< Size for allocating the fsw_volume structure + fsw_u32 dnode_struct_size; //!< Size for allocating the fsw_dnode structure + + fsw_status_t (*volume_mount)(struct VOLSTRUCTNAME *vol); + void (*volume_free)(struct VOLSTRUCTNAME *vol); + fsw_status_t (*volume_stat)(struct VOLSTRUCTNAME *vol, struct fsw_volume_stat *sb); + + fsw_status_t (*dnode_fill)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno); + void (*dnode_free)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno); + fsw_status_t (*dnode_stat)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_dnode_stat *sb); + fsw_status_t (*get_extent)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_extent *extent); + + fsw_status_t (*dir_lookup)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_string *lookup_name, struct DNODESTRUCTNAME **child_dno); + fsw_status_t (*dir_read)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_shandle *shand, struct DNODESTRUCTNAME **child_dno); + fsw_status_t (*readlink)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_string *link_target); +}; + + +/** + * \name Volume Functions + */ +/*@{*/ + +fsw_status_t fsw_mount(void *host_data, + struct fsw_host_table *host_table, + struct fsw_fstype_table *fstype_table, + struct fsw_volume **vol_out); +void fsw_unmount(struct fsw_volume *vol); +fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb); + +void fsw_set_blocksize(struct VOLSTRUCTNAME *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize); +fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out); +void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer); + +/*@}*/ + + +/** + * \name dnode Functions + */ +/*@{*/ + +fsw_status_t fsw_dnode_create_root(struct VOLSTRUCTNAME *vol, fsw_u32 dnode_id, struct DNODESTRUCTNAME **dno_out); +fsw_status_t fsw_dnode_create(struct DNODESTRUCTNAME *parent_dno, fsw_u32 dnode_id, int type, + struct fsw_string *name, struct DNODESTRUCTNAME **dno_out); +void fsw_dnode_retain(struct fsw_dnode *dno); +void fsw_dnode_release(struct fsw_dnode *dno); + +fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno); +fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb); + +fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno, + struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out); +fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno, + struct fsw_string *lookup_path, char separator, + struct fsw_dnode **child_dno_out); +fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out); +fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *link_target); +fsw_status_t fsw_dnode_readlink_data(struct DNODESTRUCTNAME *dno, struct fsw_string *link_target); +fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out); + +/*@}*/ + + +/** + * \name shandle Functions + */ +/*@{*/ + +fsw_status_t fsw_shandle_open(struct DNODESTRUCTNAME *dno, struct fsw_shandle *shand); +void fsw_shandle_close(struct fsw_shandle *shand); +fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer); + +/*@}*/ + + +/** + * \name Memory Functions + */ +/*@{*/ + +fsw_status_t fsw_alloc_zero(int len, void **ptr_out); +fsw_status_t fsw_memdup(void **dest_out, void *src, int len); + +/*@}*/ + + +/** + * \name String Functions + */ +/*@{*/ + +int fsw_strlen(struct fsw_string *s); +int fsw_streq(struct fsw_string *s1, struct fsw_string *s2); +int fsw_streq_cstr(struct fsw_string *s1, const char *s2); +fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src); +void fsw_strsplit(struct fsw_string *lookup_name, struct fsw_string *buffer, char separator); + +void fsw_strfree(struct fsw_string *s); +fsw_u16 fsw_to_lower(fsw_u16 ch); + +/*@}*/ + + +/** + * \name Posix Mode Macros + * These macros can be used globally to test fields and bits in + * Posix-style modes. + * + * Taken from FreeBSD sys/stat.h. + */ +/*@{*/ +#ifndef S_IRWXU + +#define S_ISUID 0004000 /* set user id on execution */ +#define S_ISGID 0002000 /* set group id on execution */ +#define S_ISTXT 0001000 /* sticky bit */ + +#define S_IRWXU 0000700 /* RWX mask for owner */ +#define S_IRUSR 0000400 /* R for owner */ +#define S_IWUSR 0000200 /* W for owner */ +#define S_IXUSR 0000100 /* X for owner */ + +#define S_IRWXG 0000070 /* RWX mask for group */ +#define S_IRGRP 0000040 /* R for group */ +#define S_IWGRP 0000020 /* W for group */ +#define S_IXGRP 0000010 /* X for group */ + +#define S_IRWXO 0000007 /* RWX mask for other */ +#define S_IROTH 0000004 /* R for other */ +#define S_IWOTH 0000002 /* W for other */ +#define S_IXOTH 0000001 /* X for other */ + +#define S_IFMT 0170000 /* type of file mask */ +#define S_IFIFO 0010000 /* named pipe (fifo) */ +#define S_IFCHR 0020000 /* character special */ +#define S_IFDIR 0040000 /* directory */ +#define S_IFBLK 0060000 /* block special */ +#define S_IFREG 0100000 /* regular */ +#define S_IFLNK 0120000 /* symbolic link */ +#define S_IFSOCK 0140000 /* socket */ +#define S_ISVTX 0001000 /* save swapped text even after use */ +#define S_IFWHT 0160000 /* whiteout */ + +#define S_ISDIR(m) (((m) & 0170000) == 0040000) /* directory */ +#define S_ISCHR(m) (((m) & 0170000) == 0020000) /* char special */ +#define S_ISBLK(m) (((m) & 0170000) == 0060000) /* block special */ +#define S_ISREG(m) (((m) & 0170000) == 0100000) /* regular file */ +#define S_ISFIFO(m) (((m) & 0170000) == 0010000) /* fifo or socket */ +#define S_ISLNK(m) (((m) & 0170000) == 0120000) /* symbolic link */ +#define S_ISSOCK(m) (((m) & 0170000) == 0140000) /* socket */ +#define S_ISWHT(m) (((m) & 0170000) == 0160000) /* whiteout */ + +#define S_BLKSIZE 512 /* block size used in the stat struct */ + +#endif +/*@}*/ + + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c new file mode 100644 index 00000000..6468aa0c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c @@ -0,0 +1,1142 @@ +/* $Id: fsw_efi.c $ */ +/** @file + * fsw_efi.c - EFI host environment code. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_efi.h" + +#define DEBUG_LEVEL 0 + +#ifndef FSTYPE +#ifdef VBOX +#error FSTYPE must be defined! +#else +#define FSTYPE ext2 +#endif +#endif + +/** Helper macro for stringification. */ +#define FSW_EFI_STRINGIFY(x) L#x +/** Expands to the EFI driver name given the file system type name. */ +#define FSW_EFI_DRIVER_NAME(t) L"Fsw " FSW_EFI_STRINGIFY(t) L" File System Driver" + + +// function prototypes + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath); +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath); +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer); + +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName); +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName); + +void fsw_efi_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize); +fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer); + +EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume); + +EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This, + OUT EFI_FILE **Root); +EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno, + OUT EFI_FILE **NewFileHandle); + +EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer); +EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File, + OUT UINT64 *Position); +EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File, + IN UINT64 Position); + +EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File, + OUT EFI_FILE **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes); +EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer); +EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File, + IN UINT64 Position); + +EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer); +EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, + IN struct fsw_dnode *dno, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer); + +#if defined(VBOX) && (FSTYPE == hfs) +extern fsw_status_t fsw_hfs_get_blessed_file(void *vol, struct fsw_string *path); +#endif + +/** + * Interface structure for the EFI Driver Binding protocol. + */ + +EFI_DRIVER_BINDING_PROTOCOL fsw_efi_DriverBinding_table = { + fsw_efi_DriverBinding_Supported, + fsw_efi_DriverBinding_Start, + fsw_efi_DriverBinding_Stop, + 0x10, + NULL, + NULL +}; + +/** + * Interface structure for the EFI Component Name protocol. + */ + +EFI_COMPONENT_NAME_PROTOCOL fsw_efi_ComponentName_table = { + fsw_efi_ComponentName_GetDriverName, + fsw_efi_ComponentName_GetControllerName, + "eng" +}; + +/** + * Dispatch table for our FSW host driver. + */ + +struct fsw_host_table fsw_efi_host_table = { + FSW_STRING_TYPE_UTF16, + + fsw_efi_change_blocksize, + fsw_efi_read_block +}; + +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE); + + + +/** + * Image entry point. Installs the Driver Binding and Component Name protocols + * on the image's handle. Actually mounting a file system is initiated through + * the Driver Binding protocol at the firmware's request. + */ +EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS Status; + +#ifndef VBOX + InitializeLib(ImageHandle, SystemTable); +#endif + + // complete Driver Binding protocol instance + fsw_efi_DriverBinding_table.ImageHandle = ImageHandle; + fsw_efi_DriverBinding_table.DriverBindingHandle = ImageHandle; + // install Driver Binding protocol + Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, + &PROTO_NAME(DriverBindingProtocol), + EFI_NATIVE_INTERFACE, + &fsw_efi_DriverBinding_table); + if (EFI_ERROR (Status)) { + return Status; + } + + // install Component Name protocol + Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, + &PROTO_NAME(ComponentNameProtocol), + EFI_NATIVE_INTERFACE, + &fsw_efi_ComponentName_table); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + * Driver Binding EFI protocol, Supported function. This function is called by EFI + * to test if this driver can handle a certain device. Our implementation only checks + * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols) + * and implicitly checks if the disk is already in use by another driver. + */ + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath) +{ + EFI_STATUS Status; + EFI_DISK_IO *DiskIo; + + // we check for both DiskIO and BlockIO protocols + + // first, open DiskIO + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(Status)) + { + return Status; + } + + // we were just checking, close it again + BS->CloseProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + This->DriverBindingHandle, + ControllerHandle); + + // next, check BlockIO without actually opening it + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(BlockIoProtocol), + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + return Status; +} + +static EFI_STATUS fsw_efi_ReMount(IN FSW_VOLUME_DATA *pVolume, + IN EFI_HANDLE ControllerHandle, + EFI_DISK_IO *pDiskIo, + EFI_BLOCK_IO *pBlockIo) +{ + EFI_STATUS Status; + pVolume->Signature = FSW_VOLUME_DATA_SIGNATURE; + pVolume->Handle = ControllerHandle; + pVolume->DiskIo = pDiskIo; + pVolume->MediaId = pBlockIo->Media->MediaId; + pVolume->LastIOStatus = EFI_SUCCESS; + + // mount the filesystem + Status = fsw_efi_map_status(fsw_mount(pVolume, &fsw_efi_host_table, + &FSW_FSTYPE_TABLE_NAME(FSTYPE), &pVolume->vol), + pVolume); + + if (!EFI_ERROR(Status)) { + // register the SimpleFileSystem protocol + pVolume->FileSystem.Revision = EFI_FILE_IO_INTERFACE_REVISION; + pVolume->FileSystem.OpenVolume = fsw_efi_FileSystem_OpenVolume; + Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle, + &PROTO_NAME(SimpleFileSystemProtocol), &pVolume->FileSystem, + NULL); +#if DEBUG_LEVEL /* This error is always printed and destroys the boot logo. */ + if (EFI_ERROR(Status)) + Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status); +#endif + } + return Status; +} + +/** + * Driver Binding EFI protocol, Start function. This function is called by EFI + * to start driving the given device. It is still possible at this point to + * return EFI_UNSUPPORTED, and in fact we will do so if the file system driver + * cannot find the superblock signature (or equivalent) that it expects. + * + * This function allocates memory for a per-volume structure, opens the + * required protocols (just Disk I/O in our case, Block I/O is only looked + * at to get the MediaId field), and lets the FSW core mount the file system. + * If successful, an EFI Simple File System protocol is exported on the + * device handle. + */ + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath) +{ + EFI_STATUS Status; + EFI_BLOCK_IO *BlockIo; + EFI_DISK_IO *DiskIo; + FSW_VOLUME_DATA *Volume; + + // open consumed protocols + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(BlockIoProtocol), + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); // NOTE: we only want to look at the MediaId + if (EFI_ERROR(Status)) { + return Status; + } + + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER); + if (EFI_ERROR(Status)) { + return Status; + } + + // allocate volume structure + Volume = AllocateZeroPool(sizeof(FSW_VOLUME_DATA)); + Status = fsw_efi_ReMount(Volume, ControllerHandle, DiskIo, BlockIo); + + // on errors, close the opened protocols + if (EFI_ERROR(Status)) { + if (Volume->vol != NULL) + fsw_unmount(Volume->vol); + FreePool(Volume); + +#if 0 + if (Status == EFI_MEDIA_CHANGED) + Status = fsw_efi_ReMount(Volume, ControllerHandle, DiskIo, BlockIo); + else +#endif + BS->CloseProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + This->DriverBindingHandle, + ControllerHandle); + } + + return Status; +} + +/** + * Driver Binding EFI protocol, Stop function. This function is called by EFI + * to stop the driver on the given device. This translates to an unmount + * call for the FSW core. + * + * We assume that all file handles on the volume have been closed before + * the driver is stopped. At least with the EFI shell, that is actually the + * case; it closes all file handles between commands. + */ + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer) +{ + EFI_STATUS Status; + EFI_FILE_IO_INTERFACE *FileSystem; + FSW_VOLUME_DATA *Volume; + +#if DEBUG_LEVEL + Print(L"fsw_efi_DriverBinding_Stop\n"); +#endif + + // get the installed SimpleFileSystem interface + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(SimpleFileSystemProtocol), + (VOID **) &FileSystem, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(Status)) + return EFI_UNSUPPORTED; + + // get private data structure + Volume = FSW_VOLUME_FROM_FILE_SYSTEM(FileSystem); + + // uninstall Simple File System protocol + Status = BS->UninstallMultipleProtocolInterfaces(ControllerHandle, + &PROTO_NAME(SimpleFileSystemProtocol), &Volume->FileSystem, + NULL); + if (EFI_ERROR(Status)) { + Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status); + return Status; + } +#if DEBUG_LEVEL + Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n"); +#endif + + // release private data structure + if (Volume->vol != NULL) + fsw_unmount(Volume->vol); + FreePool(Volume); + + // close the consumed protocols + Status = BS->CloseProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + This->DriverBindingHandle, + ControllerHandle); + + return Status; +} + +/** + * Component Name EFI protocol, GetDriverName function. Used by the EFI + * environment to inquire the name of this driver. The name returned is + * based on the file system type actually used in compilation. + */ + +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName) +{ + if (Language == NULL || DriverName == NULL) + return EFI_INVALID_PARAMETER; +#if 0 + + if (Language[0] == 'e' && Language[1] == 'n' && Language[2] == 'g' && Language[3] == 0) { + *DriverName = FSW_EFI_DRIVER_NAME(FSTYPE); + return EFI_SUCCESS; + } +#endif + return EFI_UNSUPPORTED; +} + +/** + * Component Name EFI protocol, GetControllerName function. Not implemented + * because this is not a "bus" driver in the sense of the EFI Driver Model. + */ + +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName) +{ + return EFI_UNSUPPORTED; +} + +/** + * FSW interface function for block size changes. This function is called by the FSW core + * when the file system driver changes the block sizes for the volume. + */ + +void fsw_efi_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize) +{ + // nothing to do +} + +/** + * FSW interface function to read data blocks. This function is called by the FSW core + * to read a block of data from the device. The buffer is allocated by the core code. + */ + +fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer) +{ + EFI_STATUS Status; + FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)vol->host_data; + + FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_efi_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize)); + + // read from disk + Status = Volume->DiskIo->ReadDisk(Volume->DiskIo, Volume->MediaId, + (UINT64)phys_bno * vol->phys_blocksize, + vol->phys_blocksize, + buffer); + Volume->LastIOStatus = Status; + if (EFI_ERROR(Status)) + return FSW_IO_ERROR; + return FSW_SUCCESS; +} + +/** + * Map FSW status codes to EFI status codes. The FSW_IO_ERROR code is only produced + * by fsw_efi_read_block, so we map it back to the EFI status code remembered from + * the last I/O operation. + */ + +EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume) +{ + switch (fsw_status) { + case FSW_SUCCESS: + return EFI_SUCCESS; + case FSW_OUT_OF_MEMORY: + return EFI_VOLUME_CORRUPTED; + case FSW_IO_ERROR: + return Volume->LastIOStatus; + case FSW_UNSUPPORTED: + return EFI_UNSUPPORTED; + case FSW_NOT_FOUND: + return EFI_NOT_FOUND; + case FSW_VOLUME_CORRUPTED: + return EFI_VOLUME_CORRUPTED; + default: + return EFI_DEVICE_ERROR; + } +} + +/** + * File System EFI protocol, OpenVolume function. Creates a file handle for + * the root directory and returns it. Note that this function may be called + * multiple times and returns a new file handle each time. Each returned + * handle is closed by the client using it. + */ + +EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This, + OUT EFI_FILE **Root) +{ + EFI_STATUS Status; + FSW_VOLUME_DATA *Volume = FSW_VOLUME_FROM_FILE_SYSTEM(This); + +#if DEBUG_LEVEL + Print(L"fsw_efi_FileSystem_OpenVolume\n"); +#endif + + Status = fsw_efi_dnode_to_FileHandle(Volume->vol->root, Root); + + return Status; +} + +/** + * File Handle EFI protocol, Open function. Dispatches the call + * based on the kind of file handle. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Open(IN EFI_FILE *This, + OUT EFI_FILE **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + if (File->Type == FSW_EFI_FILE_TYPE_DIR) + return fsw_efi_dir_open(File, NewHandle, FileName, OpenMode, Attributes); + // not supported for regular files + return EFI_UNSUPPORTED; +} + +/** + * File Handle EFI protocol, Close function. Closes the FSW shandle + * and frees the memory used for the structure. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Close(IN EFI_FILE *This) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + +#if DEBUG_LEVEL + Print(L"fsw_efi_FileHandle_Close\n"); +#endif + + fsw_shandle_close(&File->shand); + FreePool(File); + + return EFI_SUCCESS; +} + +/** + * File Handle EFI protocol, Delete function. Calls through to Close + * and returns a warning because this driver is read-only. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Delete(IN EFI_FILE *This) +{ + EFI_STATUS Status; + + Status = This->Close(This); + if (Status == EFI_SUCCESS) { + // this driver is read-only + Status = EFI_WARN_DELETE_FAILURE; + } + + return Status; +} + +/** + * File Handle EFI protocol, Read function. Dispatches the call + * based on the kind of file handle. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Read(IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + if (File->Type == FSW_EFI_FILE_TYPE_FILE) + return fsw_efi_file_read(File, BufferSize, Buffer); + else if (File->Type == FSW_EFI_FILE_TYPE_DIR) + return fsw_efi_dir_read(File, BufferSize, Buffer); + return EFI_UNSUPPORTED; +} + +/** + * File Handle EFI protocol, Write function. Returns unsupported status + * because this driver is read-only. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Write(IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer) +{ + // this driver is read-only + return EFI_WRITE_PROTECTED; +} + +/** + * File Handle EFI protocol, GetPosition function. Dispatches the call + * based on the kind of file handle. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_GetPosition(IN EFI_FILE *This, + OUT UINT64 *Position) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + if (File->Type == FSW_EFI_FILE_TYPE_FILE) + return fsw_efi_file_getpos(File, Position); + // not defined for directories + return EFI_UNSUPPORTED; +} + +/** + * File Handle EFI protocol, SetPosition function. Dispatches the call + * based on the kind of file handle. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_SetPosition(IN EFI_FILE *This, + IN UINT64 Position) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + if (File->Type == FSW_EFI_FILE_TYPE_FILE) + return fsw_efi_file_setpos(File, Position); + else if (File->Type == FSW_EFI_FILE_TYPE_DIR) + return fsw_efi_dir_setpos(File, Position); + return EFI_UNSUPPORTED; +} + +/** + * File Handle EFI protocol, GetInfo function. Dispatches to the common + * function implementing this. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_GetInfo(IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + return fsw_efi_dnode_getinfo(File, InformationType, BufferSize, Buffer); +} + +/** + * File Handle EFI protocol, SetInfo function. Returns unsupported status + * because this driver is read-only. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_SetInfo(IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer) +{ + // this driver is read-only + return EFI_WRITE_PROTECTED; +} + +/** + * File Handle EFI protocol, Flush function. Returns unsupported status + * because this driver is read-only. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Flush(IN EFI_FILE *This) +{ + // this driver is read-only + return EFI_WRITE_PROTECTED; +} + +/** + * Set up a file handle for a dnode. This function allocates a data structure + * for a file handle, opens a FSW shandle and populates the EFI_FILE structure + * with the interface functions. + */ + +EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno, + OUT EFI_FILE **NewFileHandle) +{ + EFI_STATUS Status; + FSW_FILE_DATA *File; + + // make sure the dnode has complete info + Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data); + if (EFI_ERROR(Status)) + return Status; + + // check type + if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR) + return EFI_UNSUPPORTED; + + // allocate file structure + File = AllocateZeroPool(sizeof(FSW_FILE_DATA)); + File->Signature = FSW_FILE_DATA_SIGNATURE; + if (dno->type == FSW_DNODE_TYPE_FILE) + File->Type = FSW_EFI_FILE_TYPE_FILE; + else if (dno->type == FSW_DNODE_TYPE_DIR) + File->Type = FSW_EFI_FILE_TYPE_DIR; + + // open shandle + Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand), + (FSW_VOLUME_DATA *)dno->vol->host_data); + if (EFI_ERROR(Status)) { + FreePool(File); + return Status; + } + + // populate the file handle + File->FileHandle.Revision = EFI_FILE_HANDLE_REVISION; + File->FileHandle.Open = fsw_efi_FileHandle_Open; + File->FileHandle.Close = fsw_efi_FileHandle_Close; + File->FileHandle.Delete = fsw_efi_FileHandle_Delete; + File->FileHandle.Read = fsw_efi_FileHandle_Read; + File->FileHandle.Write = fsw_efi_FileHandle_Write; + File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition; + File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition; + File->FileHandle.GetInfo = fsw_efi_FileHandle_GetInfo; + File->FileHandle.SetInfo = fsw_efi_FileHandle_SetInfo; + File->FileHandle.Flush = fsw_efi_FileHandle_Flush; + + *NewFileHandle = &File->FileHandle; + return EFI_SUCCESS; +} + +/** + * Data read function for regular files. Calls through to fsw_shandle_read. + */ + +EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + fsw_u32 buffer_size; + +#if DEBUG_LEVEL + Print(L"fsw_efi_file_read %d bytes\n", *BufferSize); +#endif + + buffer_size = (fsw_u32)*BufferSize; + if (buffer_size != *BufferSize) + buffer_size = ~(fsw_u32)0; + Status = fsw_efi_map_status(fsw_shandle_read(&File->shand, &buffer_size, Buffer), + (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data); + *BufferSize = buffer_size; + + return Status; +} + +/** + * Get file position for regular files. + */ + +EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File, + OUT UINT64 *Position) +{ + *Position = File->shand.pos; + return EFI_SUCCESS; +} + +/** + * Set file position for regular files. EFI specifies the all-ones value + * to be a special value for the end of the file. + */ + +EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File, + IN UINT64 Position) +{ + if (Position == 0xFFFFFFFFFFFFFFFFULL) + File->shand.pos = File->shand.dnode->size; + else + File->shand.pos = Position; + return EFI_SUCCESS; +} + +/** + * Open function used to open new file handles relative to a directory. + * In EFI, the "open file" function is implemented by directory file handles + * and is passed a relative or volume-absolute path to the file or directory + * to open. We use fsw_dnode_lookup_path to find the node plus an additional + * call to fsw_dnode_resolve because EFI has no concept of symbolic links. + */ + +EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File, + OUT EFI_FILE **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes) +{ + EFI_STATUS Status; + FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data; + struct fsw_dnode *dno; + struct fsw_dnode *target_dno; + struct fsw_string lookup_path; + +#if DEBUG_LEVEL + Print(L"fsw_efi_dir_open: '%s'\n", FileName); +#endif + + if (OpenMode != EFI_FILE_MODE_READ) + return EFI_WRITE_PROTECTED; + + lookup_path.type = FSW_STRING_TYPE_UTF16; + lookup_path.len = (int)StrLen(FileName); + lookup_path.size = lookup_path.len * sizeof(fsw_u16); + lookup_path.data = FileName; + + // resolve the path (symlinks along the way are automatically resolved) + Status = fsw_efi_map_status(fsw_dnode_lookup_path(File->shand.dnode, &lookup_path, '\\', &dno), + Volume); + if (EFI_ERROR(Status)) + return Status; + + // if the final node is a symlink, also resolve it + Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno), + Volume); + fsw_dnode_release(dno); + if (EFI_ERROR(Status)) + return Status; + dno = target_dno; + + // make a new EFI handle for the target dnode + Status = fsw_efi_dnode_to_FileHandle(dno, NewHandle); + fsw_dnode_release(dno); + return Status; +} + +/** + * Read function for directories. A file handle read on a directory retrieves + * the next directory entry. + */ + +EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data; + struct fsw_dnode *dno; + +#if DEBUG_LEVEL + Print(L"fsw_efi_dir_read...\n"); +#endif + + // read the next entry + Status = fsw_efi_map_status(fsw_dnode_dir_read(&File->shand, &dno), + Volume); + if (Status == EFI_NOT_FOUND) { + // end of directory + *BufferSize = 0; +#if DEBUG_LEVEL + Print(L"...no more entries\n"); +#endif + return EFI_SUCCESS; + } + if (EFI_ERROR(Status)) + return Status; + + // get info into buffer + Status = fsw_efi_dnode_fill_FileInfo(Volume, dno, BufferSize, Buffer); + fsw_dnode_release(dno); + return Status; +} + +/** + * Set file position for directories. The only allowed set position operation + * for directories is to rewind the directory completely by setting the + * position to zero. + */ + +EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File, + IN UINT64 Position) +{ + if (Position == 0) { + File->shand.pos = 0; + return EFI_SUCCESS; + } else { + // directories can only rewind to the start + return EFI_UNSUPPORTED; + } +} + +/** + * Get file or volume information. This function implements the GetInfo call + * for all file handles. Control is dispatched according to the type of information + * requested by the caller. + */ + +EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data; + EFI_FILE_SYSTEM_INFO *FSInfo; + UINTN RequiredSize; + struct fsw_volume_stat vsb; + + if (CompareGuid(InformationType, &GUID_NAME(FileInfo))) { +#if DEBUG_LEVEL + Print(L"fsw_efi_dnode_getinfo: FILE_INFO\n"); +#endif + + Status = fsw_efi_dnode_fill_FileInfo(Volume, File->shand.dnode, BufferSize, Buffer); + + } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemInfo))) { +#if DEBUG_LEVEL + Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n"); +#endif + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + fsw_efi_strsize(&Volume->vol->label); + if (*BufferSize < RequiredSize) { + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // fill structure + FSInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; + FSInfo->Size = RequiredSize; + FSInfo->ReadOnly = TRUE; + FSInfo->BlockSize = Volume->vol->log_blocksize; + fsw_efi_strcpy(FSInfo->VolumeLabel, &Volume->vol->label); + + // get the missing info from the fs driver + ZeroMem(&vsb, sizeof(struct fsw_volume_stat)); + Status = fsw_efi_map_status(fsw_volume_stat(Volume->vol, &vsb), Volume); + if (EFI_ERROR(Status)) + return Status; + FSInfo->VolumeSize = vsb.total_bytes; + FSInfo->FreeSpace = vsb.free_bytes; + + // prepare for return + *BufferSize = RequiredSize; + Status = EFI_SUCCESS; + + } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemVolumeLabelInfoId))) { +#if DEBUG_LEVEL + Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n"); +#endif + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + fsw_efi_strsize(&Volume->vol->label); + if (*BufferSize < RequiredSize) { + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // copy volume label + fsw_efi_strcpy(((EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Buffer)->VolumeLabel, &Volume->vol->label); + + // prepare for return + *BufferSize = RequiredSize; + Status = EFI_SUCCESS; + +#ifdef VBOX + } else if (CompareGuid(InformationType, &gVBoxFsBlessedFileInfoGuid)) { + +# if FSTYPE == hfs + struct fsw_string StrBlessedFile; + + fsw_status_t rc = fsw_hfs_get_blessed_file(Volume->vol, &StrBlessedFile); + if (!rc) + { + // check buffer size + RequiredSize = SIZE_OF_VBOX_FS_BLESSED_FILE + fsw_efi_strsize(&StrBlessedFile); + if (*BufferSize < RequiredSize) { + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // copy volume label + fsw_efi_strcpy(((VBOX_FS_BLESSED_FILE *)Buffer)->BlessedFile, &StrBlessedFile); + + // prepare for return + *BufferSize = RequiredSize; + Status = EFI_SUCCESS; + } + else +# endif + Status = EFI_UNSUPPORTED; +#endif + + } else { + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +/** + * Time mapping callback for the fsw_dnode_stat call. This function converts + * a Posix style timestamp into an EFI_TIME structure and writes it to the + * appropriate member of the EFI_FILE_INFO structure that we're filling. + */ + +static void fsw_efi_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if (which == FSW_DNODE_STAT_CTIME) + fsw_efi_decode_time(&FileInfo->CreateTime, posix_time); + else if (which == FSW_DNODE_STAT_MTIME) + fsw_efi_decode_time(&FileInfo->ModificationTime, posix_time); + else if (which == FSW_DNODE_STAT_ATIME) + fsw_efi_decode_time(&FileInfo->LastAccessTime, posix_time); +} + +/** + * Mode mapping callback for the fsw_dnode_stat call. This function looks at + * the Posix mode passed by the file system driver and makes appropriate + * adjustments to the EFI_FILE_INFO structure that we're filling. + */ + +static void fsw_efi_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if ((posix_mode & S_IWUSR) == 0) + FileInfo->Attribute |= EFI_FILE_READ_ONLY; +} + +/** + * Common function to fill an EFI_FILE_INFO with information about a dnode. + */ + +EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, + IN struct fsw_dnode *dno, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + EFI_FILE_INFO *FileInfo; + UINTN RequiredSize; + struct fsw_dnode *target_dno; + struct fsw_dnode_stat sb; + + // make sure the dnode has complete info + Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume); + if (EFI_ERROR(Status)) + return Status; + + /// @todo check/assert that the dno's name is in UTF16 + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name); + if (*BufferSize < RequiredSize) { + /// @todo wind back the directory in this case + +#if DEBUG_LEVEL + Print(L"...BUFFER TOO SMALL\n"); +#endif + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // fill structure + ZeroMem(Buffer, RequiredSize); + FileInfo = (EFI_FILE_INFO *)Buffer; + + // must preserve the original file name + fsw_efi_strcpy(FileInfo->FileName, &dno->name); + + // if the node is a symlink, also resolve it + Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno), Volume); + fsw_dnode_release(dno); + if (EFI_ERROR(Status)) + return Status; + dno = target_dno; + // make sure the dnode has complete info again + Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume); + if (EFI_ERROR(Status)) + return Status; + + FileInfo->Size = RequiredSize; + FileInfo->FileSize = dno->size; + FileInfo->Attribute = 0; + if (dno->type == FSW_DNODE_TYPE_DIR) + FileInfo->Attribute |= EFI_FILE_DIRECTORY; + + // get the missing info from the fs driver + ZeroMem(&sb, sizeof(struct fsw_dnode_stat)); + sb.store_time_posix = fsw_efi_store_time_posix; + sb.store_attr_posix = fsw_efi_store_attr_posix; + sb.host_data = FileInfo; + Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume); + if (EFI_ERROR(Status)) + return Status; + FileInfo->PhysicalSize = sb.used_bytes; + + // prepare for return + *BufferSize = RequiredSize; +#if DEBUG_LEVEL + Print(L"...returning '%s'\n", FileInfo->FileName); +#endif + return EFI_SUCCESS; +} + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.h new file mode 100644 index 00000000..d081552b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.h @@ -0,0 +1,133 @@ +/* $Id: fsw_efi.h $ */ +/** @file + * fsw_efi.h - EFI host environment header. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_EFI_H_ +#define _FSW_EFI_H_ + +#include "fsw_core.h" + + +/** + * EFI Host: Private per-volume structure. + */ + +typedef struct { + UINT64 Signature; //!< Used to identify this structure + + EFI_FILE_IO_INTERFACE FileSystem; //!< Published EFI protocol interface structure + + EFI_HANDLE Handle; //!< The device handle the protocol is attached to + EFI_DISK_IO *DiskIo; //!< The Disk I/O protocol we use for disk access + UINT32 MediaId; //!< The media ID from the Block I/O protocol + EFI_STATUS LastIOStatus; //!< Last status from Disk I/O + + struct fsw_volume *vol; //!< FSW volume structure + +} FSW_VOLUME_DATA; + +/** Signature for the volume structure. */ +#define FSW_VOLUME_DATA_SIGNATURE EFI_SIGNATURE_32 ('f', 's', 'w', 'V') +/** Access macro for the volume structure. */ +#define FSW_VOLUME_FROM_FILE_SYSTEM(a) CR (a, FSW_VOLUME_DATA, FileSystem, FSW_VOLUME_DATA_SIGNATURE) + +/** + * EFI Host: Private structure for a EFI_FILE interface. + */ + +typedef struct { + UINT64 Signature; //!< Used to identify this structure + + EFI_FILE FileHandle; //!< Published EFI protocol interface structure + + UINTN Type; //!< File type used for dispatching + struct fsw_shandle shand; //!< FSW handle for this file + +} FSW_FILE_DATA; + +/** File type: regular file. */ +#define FSW_EFI_FILE_TYPE_FILE (0) +/** File type: directory. */ +#define FSW_EFI_FILE_TYPE_DIR (1) + +/** Signature for the file handle structure. */ +#define FSW_FILE_DATA_SIGNATURE EFI_SIGNATURE_32 ('f', 's', 'w', 'F') +/** Access macro for the file handle structure. */ +#define FSW_FILE_FROM_FILE_HANDLE(a) CR (a, FSW_FILE_DATA, FileHandle, FSW_FILE_DATA_SIGNATURE) + + +// +// Library functions +// + +VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime); + +UINTN fsw_efi_strsize(struct fsw_string *s); +VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src); + + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_base.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_base.h new file mode 100644 index 00000000..758f11b4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_base.h @@ -0,0 +1,115 @@ +/* $Id: fsw_efi_base.h $ */ +/** @file + * fsw_efi_base.h - Base definitions for the EFI host environment. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_EFI_BASE_H_ +#define _FSW_EFI_BASE_H_ + +#ifndef VBOX +#include <efi.h> +#include <efilib.h> +#define PROTO_NAME(x) x +#endif + +#define FSW_LITTLE_ENDIAN (1) + + +// types, reuse EFI types + +typedef INT8 fsw_s8; +typedef UINT8 fsw_u8; +typedef INT16 fsw_s16; +typedef UINT16 fsw_u16; +typedef INT32 fsw_s32; +typedef UINT32 fsw_u32; +typedef INT64 fsw_s64; +typedef UINT64 fsw_u64; + + +// allocation functions + +#define fsw_alloc(size, ptrptr) (((*(ptrptr) = AllocatePool(size)) == NULL) ? FSW_OUT_OF_MEMORY : FSW_SUCCESS) +#define fsw_free(ptr) FreePool(ptr) + +// memory functions + +#define fsw_memzero(dest,size) ZeroMem(dest,size) +#define fsw_memcpy(dest,src,size) CopyMem(dest,src,size) +#define fsw_memeq(p1,p2,size) (CompareMem(p1,p2,size) == 0) + +// message printing + +#define FSW_MSGSTR(s) DEBUG_INFO, s +#define FSW_MSGFUNC DebugPrint + +// 64-bit hooks + +#define FSW_U64_SHR(val,shiftbits) RShiftU64((val), (shiftbits)) +#define FSW_U64_DIV(val,divisor) DivU64x32((val), (divisor), NULL) + + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_lib.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_lib.c new file mode 100644 index 00000000..25eeef3e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_lib.c @@ -0,0 +1,179 @@ +/* $Id: fsw_efi_lib.c $ */ +/** @file + * fsw_efi_lib.c - EFI host environment library functions. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_efi.h" + + +// +// time conversion +// +// Adopted from public domain code in FreeBSD libc. +// + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) +#define MONSPERYEAR 12 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) + +static const int mon_lengths[2][MONSPERYEAR] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; +static const int year_lengths[2] = { + DAYSPERNYEAR, DAYSPERLYEAR +}; + +VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime) +{ + long days, rem; + int y, newy, yleap; + const int *ip; + + ZeroMem(EfiTime, sizeof(EFI_TIME)); + + days = UnixTime / SECSPERDAY; + rem = UnixTime % SECSPERDAY; + + EfiTime->Hour = (UINT8) (rem / SECSPERHOUR); + rem = rem % SECSPERHOUR; + EfiTime->Minute = (UINT8) (rem / SECSPERMIN); + EfiTime->Second = (UINT8) (rem % SECSPERMIN); + + y = EPOCH_YEAR; + while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) { + newy = y + days / DAYSPERNYEAR; + if (days < 0) + --newy; + days -= (newy - y) * DAYSPERNYEAR + + LEAPS_THRU_END_OF(newy - 1) - + LEAPS_THRU_END_OF(y - 1); + y = newy; + } + EfiTime->Year = (UINT16)y; + ip = mon_lengths[yleap]; + for (EfiTime->Month = 0; days >= (long) ip[EfiTime->Month]; ++(EfiTime->Month)) + days = days - (long) ip[EfiTime->Month]; + EfiTime->Month++; // adjust range to EFI conventions + EfiTime->Day = (UINT8) (days + 1); +} + +// +// String functions, used for file and volume info +// + +UINTN fsw_efi_strsize(struct fsw_string *s) +{ + if (s->type == FSW_STRING_TYPE_EMPTY) + return sizeof(CHAR16); + return (s->len + 1) * sizeof(CHAR16); +} + +VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src) +{ + if (src->type == FSW_STRING_TYPE_EMPTY) { + Dest[0] = 0; + } else if (src->type == FSW_STRING_TYPE_UTF16) { + CopyMem(Dest, src->data, src->size); + Dest[src->len] = 0; + } else { + /// @todo coerce, recurse + Dest[0] = 0; + } +} + +#ifdef VBOX +int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + + for (i = 0; i<len; i++) + { + if (fsw_to_lower(p1[i]) != fsw_to_lower(p2[i])) + { + return 0; + } + } + + return 1; +} +#endif + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.c new file mode 100644 index 00000000..ba961214 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.c @@ -0,0 +1,1451 @@ +/* $Id: fsw_hfs.c $ */ +/** @file + * fsw_hfs.c - HFS file system driver code, see + * + * https://developer.apple.com/legacy/library/technotes/tn/tn1150.html + * (formerly http://developer.apple.com/technotes/tn/tn1150.html) + * + * Current limitations: + * - Doesn't support permissions + * - Complete Unicode case-insensitiveness disabled (large tables) + * - No links + * - Only supports pure HFS+ (i.e. no HFS, or HFS+ embedded to HFS) + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#include "fsw_hfs.h" + +#ifdef HOST_POSIX +#include <assert.h> +#define DPRINT(x) printf(x) +#define DPRINT2(x,y) printf(x,y) +#define BP(msg) do { printf("ERROR: %s", msg); assert(0); } while (0) +#elif defined DEBUG_LEVEL +#define CONCAT(x,y) x##y +#define DPRINT(x) Print(CONCAT(L,x)) +#define DPRINT2(x,y) Print(CONCAT(L,x), y) +#define BP(msg) DPRINT(msg) +#else +#include <Library/PrintLib.h> +#define DPRINT(x) do { } while (0) +#define DPRINT2(x,y) do { } while (0) +#define BP(msg) do { } while (0) +#endif + +// functions +#if 0 +void dump_str(fsw_u16* p, fsw_u32 len, int swap) +{ + int i; + + for (i=0; i<len; i++) + { + fprintf(stderr, "%c", swap ? be16_to_cpu(p[i]) : p[i]); + } + fprintf(stderr, "\n"); +} +#endif + +static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol); +static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol); +static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb); + +static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno); +static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno); +static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno, + struct fsw_dnode_stat *sb); +static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno, + struct fsw_extent *extent); + +static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno, + struct fsw_string *lookup_name, struct fsw_hfs_dnode **child_dno); +static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno, + struct fsw_shandle *shand, struct fsw_hfs_dnode **child_dno); +#if 0 +static fsw_status_t fsw_hfs_read_dirrec(struct fsw_shandle *shand, struct hfs_dirrec_buffer *dirrec_buffer); +#endif + +static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno, + struct fsw_string *link); + +// +// Dispatch Table +// + +struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfs) = { + { FSW_STRING_TYPE_ISO88591, 4, 4, "hfs" }, + sizeof(struct fsw_hfs_volume), + sizeof(struct fsw_hfs_dnode), + + fsw_hfs_volume_mount, + fsw_hfs_volume_free, + fsw_hfs_volume_stat, + fsw_hfs_dnode_fill, + fsw_hfs_dnode_free, + fsw_hfs_dnode_stat, + fsw_hfs_get_extent, + fsw_hfs_dir_lookup, + fsw_hfs_dir_read, + fsw_hfs_readlink, +}; + +static fsw_s32 +fsw_hfs_read_block (struct fsw_hfs_dnode * dno, + fsw_u32 log_bno, + fsw_u32 off, + fsw_s32 len, + fsw_u8 * buf) +{ + fsw_status_t status; + struct fsw_extent extent; + fsw_u32 phys_bno; + fsw_u8* buffer; + + extent.log_start = log_bno; + status = fsw_hfs_get_extent(dno->g.vol, dno, &extent); + if (status) + return status; + + phys_bno = extent.phys_start; + status = fsw_block_get(dno->g.vol, phys_bno, 0, (void **)&buffer); + if (status) + return status; + + fsw_memcpy(buf, buffer + off, len); + + fsw_block_release(dno->g.vol, phys_bno, buffer); + + return FSW_SUCCESS; + +} + +/* Read data from HFS file. */ +static fsw_s32 +fsw_hfs_read_file (struct fsw_hfs_dnode * dno, + fsw_u64 pos, + fsw_s32 len, + fsw_u8 * buf) +{ + + fsw_status_t status; + fsw_u32 log_bno; + fsw_u32 block_size_bits = dno->g.vol->block_size_shift; + fsw_u32 block_size = (1 << block_size_bits); + fsw_u32 block_size_mask = block_size - 1; + fsw_s32 read = 0; + + while (len > 0) + { + fsw_u32 off = (fsw_u32)(pos & block_size_mask); + fsw_s32 next_len = len; + + log_bno = (fsw_u32)RShiftU64(pos, block_size_bits); + + if ( next_len >= 0 + && (fsw_u32)next_len > block_size) + next_len = block_size; + status = fsw_hfs_read_block(dno, log_bno, off, next_len, buf); + if (status) + return -1; + buf += next_len; + pos += next_len; + len -= next_len; + read += next_len; + } + + return read; +} + + +static fsw_s32 +fsw_hfs_compute_shift(fsw_u32 size) +{ + fsw_s32 i; + + for (i=0; i<32; i++) + { + if ((size >> i) == 0) + return i - 1; + } + + BP("BUG\n"); + return 0; +} + +/** + * Mount an HFS+ volume. Reads the superblock and constructs the + * root directory dnode. + */ + +static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol) +{ + fsw_status_t status, rv; + void *buffer = NULL; + HFSPlusVolumeHeader *voldesc; + fsw_u32 blockno; + struct fsw_string s; + + rv = FSW_UNSUPPORTED; + + vol->primary_voldesc = NULL; + fsw_set_blocksize(vol, HFS_BLOCKSIZE, HFS_BLOCKSIZE); + blockno = HFS_SUPERBLOCK_BLOCKNO; + +#define CHECK(s) \ + if (status) { \ + rv = status; \ + break; \ + } + + vol->emb_block_off = 0; + vol->hfs_kind = 0; + do { + fsw_u16 signature; + BTHeaderRec tree_header; + fsw_s32 r; + fsw_u32 block_size; + + status = fsw_block_get(vol, blockno, 0, &buffer); + CHECK(status); + voldesc = (HFSPlusVolumeHeader *)buffer; + signature = be16_to_cpu(voldesc->signature); + + if ((signature == kHFSPlusSigWord) || (signature == kHFSXSigWord)) + { + if (vol->hfs_kind == 0) + { + DPRINT("found HFS+\n"); + vol->hfs_kind = FSW_HFS_PLUS; + } + } + else if (signature == kHFSSigWord) + { + HFSMasterDirectoryBlock* mdb = (HFSMasterDirectoryBlock*)buffer; + + if (be16_to_cpu(mdb->drEmbedSigWord) == kHFSPlusSigWord) + { + DPRINT("found HFS+ inside HFS, untested\n"); + vol->hfs_kind = FSW_HFS_PLUS_EMB; + vol->emb_block_off = be32_to_cpu(mdb->drEmbedExtent.startBlock); + blockno += vol->emb_block_off; + /* retry */ + continue; + } + else + { + DPRINT("found plain HFS, unsupported\n"); + vol->hfs_kind = FSW_HFS_PLAIN; + } + rv = FSW_UNSUPPORTED; + break; + } + else + { + rv = FSW_UNSUPPORTED; + break; + } + + status = fsw_memdup((void **)&vol->primary_voldesc, voldesc, + sizeof(*voldesc)); + CHECK(status); + + + block_size = be32_to_cpu(voldesc->blockSize); + vol->block_size_shift = fsw_hfs_compute_shift(block_size); + + fsw_block_release(vol, blockno, buffer); + buffer = NULL; + voldesc = NULL; + fsw_set_blocksize(vol, block_size, block_size); + + /* get volume name */ + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = kHFSMaxVolumeNameChars; + s.data = "HFS+ volume\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; /* Otherwise buffer overflow reading beyond the end of the buffer. */ + status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); + CHECK(status); + + /* Setup catalog dnode */ + status = fsw_dnode_create_root(vol, kHFSCatalogFileID, &vol->catalog_tree.file); + CHECK(status); + fsw_memcpy (vol->catalog_tree.file->extents, + vol->primary_voldesc->catalogFile.extents, + sizeof vol->catalog_tree.file->extents); + vol->catalog_tree.file->g.size = + be64_to_cpu(vol->primary_voldesc->catalogFile.logicalSize); + + /* Setup extents overflow file */ + status = fsw_dnode_create_root(vol, kHFSExtentsFileID, &vol->extents_tree.file); + fsw_memcpy (vol->extents_tree.file->extents, + vol->primary_voldesc->extentsFile.extents, + sizeof vol->extents_tree.file->extents); + vol->extents_tree.file->g.size = + be64_to_cpu(vol->primary_voldesc->extentsFile.logicalSize); + + /* Setup the root dnode */ + status = fsw_dnode_create_root(vol, kHFSRootFolderID, &vol->g.root); + CHECK(status); + + /* + * Read catalog file, we know that first record is in the first node, right after + * the node descriptor. + */ + r = fsw_hfs_read_file(vol->catalog_tree.file, + sizeof (BTNodeDescriptor), + sizeof (BTHeaderRec), (fsw_u8 *) &tree_header); + if (r <= 0) + { + status = FSW_VOLUME_CORRUPTED; + break; + } + vol->case_sensitive = + (signature == kHFSXSigWord) && + (tree_header.keyCompareType == kHFSBinaryCompare); + vol->catalog_tree.root_node = be32_to_cpu (tree_header.rootNode); + vol->catalog_tree.node_size = be16_to_cpu (tree_header.nodeSize); + + /* Read extents overflow file */ + r = fsw_hfs_read_file(vol->extents_tree.file, + sizeof (BTNodeDescriptor), + sizeof (BTHeaderRec), (fsw_u8 *) &tree_header); + if (r <= 0) + { + status = FSW_VOLUME_CORRUPTED; + break; + } + + vol->extents_tree.root_node = be32_to_cpu (tree_header.rootNode); + vol->extents_tree.node_size = be16_to_cpu (tree_header.nodeSize); + + rv = FSW_SUCCESS; + } while (0); + +#undef CHECK + + + if (buffer != NULL) + fsw_block_release(vol, blockno, buffer); + + return rv; +} + +/** + * Free the volume data structure. Called by the core after an unmount or after + * an unsuccessful mount to release the memory used by the file system type specific + * part of the volume structure. + */ + +static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol) +{ + if (vol->primary_voldesc) + { + fsw_free(vol->primary_voldesc); + vol->primary_voldesc = NULL; + } +} + +/** + * Get in-depth information on a volume. + */ + +static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb) +{ + sb->total_bytes = be32_to_cpu(vol->primary_voldesc->totalBlocks) << vol->block_size_shift; + sb->free_bytes = be32_to_cpu(vol->primary_voldesc->freeBlocks) << vol->block_size_shift; + return FSW_SUCCESS; +} + +/** + * Get full information on a dnode from disk. This function is called by the core + * whenever it needs to access fields in the dnode structure that may not + * be filled immediately upon creation of the dnode. + */ + +static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno) +{ + return FSW_SUCCESS; +} + +/** + * Free the dnode data structure. Called by the core when deallocating a dnode + * structure to release the memory used by the file system type specific part + * of the dnode structure. + */ + +static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno) +{ +} + +static fsw_u32 mac_to_posix(fsw_u32 mac_time) +{ + /* Mac time is 1904 year based */ + return mac_time ? mac_time - 2082844800 : 0; +} + +/** + * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill + * has been called on the dnode before this function is called. Note that some + * data is not directly stored into the structure, but passed to a host-specific + * callback that converts it to the host-specific format. + */ + +static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol, + struct fsw_hfs_dnode *dno, + struct fsw_dnode_stat *sb) +{ + sb->used_bytes = dno->used_bytes; + sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, mac_to_posix(dno->ctime)); + sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, mac_to_posix(dno->mtime)); + sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, 0); + sb->store_attr_posix(sb, 0700); + + return FSW_SUCCESS; +} + +static int +fsw_hfs_find_block(HFSPlusExtentRecord * exts, + fsw_u32 * lbno, + fsw_u32 * pbno) +{ + int i; + fsw_u32 cur_lbno = *lbno; + + for (i = 0; i < 8; i++) + { + fsw_u32 start = be32_to_cpu ((*exts)[i].startBlock); + fsw_u32 count = be32_to_cpu ((*exts)[i].blockCount); + + if (cur_lbno < count) + { + *pbno = start + cur_lbno; + return 1; + } + + cur_lbno -= count; + } + + *lbno = cur_lbno; + + return 0; +} + +/* Find record offset, numbering starts from the end */ +static fsw_u32 +fsw_hfs_btree_recoffset (struct fsw_hfs_btree * btree, + BTNodeDescriptor * node, + fsw_u32 index) +{ + fsw_u8 *cnode = (fsw_u8 *) node; + fsw_u16 *recptr; + recptr = (fsw_u16 *) (cnode+btree->node_size - index * 2 - 2); + return be16_to_cpu(*recptr); +} + +/* Pointer to the key inside node */ +static BTreeKey * +fsw_hfs_btree_rec (struct fsw_hfs_btree * btree, + BTNodeDescriptor * node, + fsw_u32 index) +{ + fsw_u8 *cnode = (fsw_u8 *) node; + fsw_u32 offset; + offset = fsw_hfs_btree_recoffset (btree, node, index); + return (BTreeKey *) (cnode + offset); +} + + +static fsw_status_t +fsw_hfs_btree_search (struct fsw_hfs_btree * btree, + BTreeKey * key, + int (*compare_keys) (BTreeKey* key1, BTreeKey* key2), + BTNodeDescriptor ** result, + fsw_u32 * key_offset) +{ + BTNodeDescriptor* node; + fsw_u32 currnode; + fsw_u32 rec; + fsw_status_t status; + fsw_u8* buffer = NULL; + + currnode = btree->root_node; + status = fsw_alloc(btree->node_size, &buffer); + if (status) + return status; + node = (BTNodeDescriptor*)buffer; + + while (1) + { + int cmp = 0; + int match; + fsw_u32 count; + + readnode: + match = 0; + /* Read a node. */ + if (fsw_hfs_read_file (btree->file, + (fsw_u64)currnode * btree->node_size, + btree->node_size, buffer) <= 0) + { + status = FSW_VOLUME_CORRUPTED; + break; + } + + if (be16_to_cpu(*(fsw_u16*)(buffer + btree->node_size - 2)) != sizeof(BTNodeDescriptor)) + BP("corrupted node\n"); + + count = be16_to_cpu (node->numRecords); + +#if 1 + for (rec = 0; rec < count; rec++) + { + BTreeKey *currkey; + + currkey = fsw_hfs_btree_rec (btree, node, rec); + cmp = compare_keys (currkey, key); + //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind); + + /* Leaf node. */ + if (node->kind == kBTLeafNode) + { + if (cmp == 0) + { + /* Found! */ + *result = node; + *key_offset = rec; + + status = FSW_SUCCESS; + goto done; + } + } + else if (node->kind == kBTIndexNode) + { + fsw_u32 *pointer; + + if (cmp > 0) + break; + + pointer = (fsw_u32 *) ((char *) currkey + + be16_to_cpu (currkey->length16) + + 2); + currnode = be32_to_cpu (*pointer); + match = 1; + } + } + + if (node->kind == kBTLeafNode && cmp < 0 && node->fLink) + { + currnode = be32_to_cpu(node->fLink); + goto readnode; + } + else if (!match) + { + status = FSW_NOT_FOUND; + break; + } +#else + /* Perform binary search */ + fsw_u32 lower = 0; + fsw_u32 upper = count - 1; + fsw_s32 cmp = -1; + BTreeKey *currkey = NULL; + + if (count == 0) + { + status = FSW_NOT_FOUND; + goto done; + } + + while (lower <= upper) + { + fsw_u32 index = (lower + upper) / 2; + + currkey = fsw_hfs_btree_rec (btree, node, index); + + cmp = compare_keys (currkey, key); + if (cmp < 0) upper = index - 1; + if (cmp > 0) lower = index + 1; + if (cmp == 0) + { + /* Found! */ + *result = node; + *key_offset = rec; + + status = FSW_SUCCESS; + goto done; + } + } + + if (cmp < 0) + currkey = fsw_hfs_btree_rec (btree, node, upper); + + if (node->kind == kBTIndexNode && currkey) + { + fsw_u32 *pointer; + + pointer = (fsw_u32 *) ((char *) currkey + + be16_to_cpu (currkey->length16) + + 2); + currnode = be32_to_cpu (*pointer); + } + else + { + status = FSW_NOT_FOUND; + break; + } +#endif + } + + + done: + if (buffer != NULL && status != FSW_SUCCESS) + fsw_free(buffer); + + return status; +} + +typedef struct +{ + fsw_u32 id; + fsw_u32 type; + struct fsw_string * name; + fsw_u64 size; + fsw_u64 used; + fsw_u32 ctime; + fsw_u32 mtime; + fsw_u32 node_num; + HFSPlusExtentRecord extents; +} file_info_t; + +typedef struct +{ + fsw_u32 cur_pos; /* current position */ + fsw_u32 parent; + struct fsw_hfs_volume * vol; + + struct fsw_shandle * shandle; /* this one track iterator's state */ + file_info_t file_info; +} visitor_parameter_t; + +static void hfs_fill_info(struct fsw_hfs_volume *vol, HFSPlusCatalogKey *file_key, file_info_t *file_info) +{ + fsw_u8 * base; + fsw_u16 rec_type; + + /* for plain HFS "-(keySize & 1)" would be needed */ + base = (fsw_u8*)file_key + be16_to_cpu(file_key->keyLength) + 2; + rec_type = be16_to_cpu(*(fsw_u16*)base); + + /** @todo read additional info */ + switch (rec_type) + { + case kHFSPlusFolderRecord: + { + HFSPlusCatalogFolder* info = (HFSPlusCatalogFolder*)base; + + file_info->id = be32_to_cpu(info->folderID); + file_info->type = FSW_DNODE_TYPE_DIR; + /** @todo return number of elements, maybe use smth else */ + file_info->size = be32_to_cpu(info->valence); + file_info->used = be32_to_cpu(info->valence); + file_info->ctime = be32_to_cpu(info->createDate); + file_info->mtime = be32_to_cpu(info->contentModDate); + break; + } + case kHFSPlusFileRecord: + { + HFSPlusCatalogFile* info = (HFSPlusCatalogFile*)base; + uint32_t creator = be32_to_cpu(info->userInfo.fdCreator); + uint32_t crtype = be32_to_cpu(info->userInfo.fdType); + + file_info->id = be32_to_cpu(info->fileID); + file_info->type = FSW_DNODE_TYPE_FILE; + file_info->size = be64_to_cpu(info->dataFork.logicalSize); + file_info->used = LShiftU64(be32_to_cpu(info->dataFork.totalBlocks), vol->block_size_shift); + file_info->ctime = be32_to_cpu(info->createDate); + file_info->mtime = be32_to_cpu(info->contentModDate); + fsw_memcpy(&file_info->extents, &info->dataFork.extents, + sizeof file_info->extents); + if (creator == kHFSPlusCreator && crtype == kHardLinkFileType) + { + /* Only hard links currently supported. */ + file_info->type = FSW_DNODE_TYPE_SYMLINK; + file_info->node_num = be32_to_cpu(info->bsdInfo.special.iNodeNum); + } + break; + } + case kHFSPlusFolderThreadRecord: + case kHFSPlusFileThreadRecord: + { + /* Do nothing. */ + break; + } + default: + BP("unknown file type\n"); + file_info->type = FSW_DNODE_TYPE_UNKNOWN; + + break; + } +} + +static int +fsw_hfs_btree_visit_node(BTreeKey *record, void* param) +{ + visitor_parameter_t* vp = (visitor_parameter_t*)param; + fsw_u8* base = (fsw_u8*)record->rawData + be16_to_cpu(record->length16) + 2; + fsw_u16 rec_type = be16_to_cpu(*(fsw_u16*)base); + struct HFSPlusCatalogKey* cat_key = (HFSPlusCatalogKey*)record; + fsw_u16 name_len; + fsw_u16 *name_ptr; + fsw_u32 i; + struct fsw_string * file_name; + + if (be32_to_cpu(cat_key->parentID) != vp->parent) + return -1; + + /* not smth we care about */ + if (vp->shandle->pos != vp->cur_pos++) + return 0; + + if (rec_type == kHFSPlusFolderThreadRecord || rec_type == kHFSPlusFileThreadRecord) + { + vp->shandle->pos++; + return 0; + } + + hfs_fill_info(vp->vol, cat_key, &vp->file_info); + + name_len = be16_to_cpu(cat_key->nodeName.length); + + file_name = vp->file_info.name; + file_name->len = name_len; + fsw_memdup(&file_name->data, &cat_key->nodeName.unicode[0], 2*name_len); + file_name->size = 2*name_len; + file_name->type = FSW_STRING_TYPE_UTF16; + name_ptr = (fsw_u16*)file_name->data; + for (i=0; i<name_len; i++) + { + name_ptr[i] = be16_to_cpu(name_ptr[i]); + } + vp->shandle->pos++; + + return 1; +} + +static fsw_status_t +fsw_hfs_btree_iterate_node (struct fsw_hfs_btree * btree, + BTNodeDescriptor * first_node, + fsw_u32 first_rec, + int (*callback) (BTreeKey *record, void* param), + void * param) +{ + fsw_status_t status; + /* We modify node, so make a copy */ + BTNodeDescriptor* node = first_node; + fsw_u8* buffer = NULL; + + status = fsw_alloc(btree->node_size, &buffer); + if (status) + return status; + + while (1) + { + fsw_u32 i; + fsw_u32 count = be16_to_cpu(node->numRecords); + fsw_u32 next_node; + + /* Iterate over all records in this node. */ + for (i = first_rec; i < count; i++) + { + int rv = callback(fsw_hfs_btree_rec (btree, node, i), param); + + switch (rv) + { + case 1: + status = FSW_SUCCESS; + goto done; + case -1: + status = FSW_NOT_FOUND; + goto done; + } + /* if callback returned 0 - continue */ + } + + next_node = be32_to_cpu(node->fLink); + + if (!next_node) + { + status = FSW_NOT_FOUND; + break; + } + + if (fsw_hfs_read_file (btree->file, + next_node * btree->node_size, + btree->node_size, buffer) <= 0) + { + status = FSW_VOLUME_CORRUPTED; + return 1; + } + + node = (BTNodeDescriptor*)buffer; + first_rec = 0; + } + done: + if (buffer) + fsw_free(buffer); + + return status; +} + +#if 0 +void deb(fsw_u16* p, int len, int swap) +{ + int i; + for (i=0; i<len; i++) + { + printf("%c", swap ? be16_to_cpu(p[i]) : p[i]); + } + printf("\n"); +} +#endif + +static int +fsw_hfs_cmp_extkey(BTreeKey* key1, BTreeKey* key2) +{ + HFSPlusExtentKey* ekey1 = (HFSPlusExtentKey*)key1; + HFSPlusExtentKey* ekey2 = (HFSPlusExtentKey*)key2; + int result; + + /* First key is read from the FS data, second is in-memory in CPU endianess */ + result = be32_to_cpu(ekey1->fileID) - ekey2->fileID; + + if (result) + return result; + + result = ekey1->forkType - ekey2->forkType; + + if (result) + return result; + + result = be32_to_cpu(ekey1->startBlock) - ekey2->startBlock; + return result; +} + +static int +fsw_hfs_cmp_catkey (BTreeKey *key1, BTreeKey *key2) +{ + HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1; + HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2; + + int apos, bpos, lc; + fsw_u16 ac, bc; + fsw_u32 parentId1; + int key1Len; + fsw_u16 *p1; + fsw_u16 *p2; + + parentId1 = be32_to_cpu(ckey1->parentID); + + if (parentId1 > ckey2->parentID) + return 1; + if (parentId1 < ckey2->parentID) + return -1; + + p1 = &ckey1->nodeName.unicode[0]; + p2 = &ckey2->nodeName.unicode[0]; + key1Len = be16_to_cpu (ckey1->nodeName.length); + apos = bpos = 0; + + while(1) + { + /* get next valid character from ckey1 */ + for (lc = 0; lc == 0 && apos < key1Len; apos++) { + ac = be16_to_cpu(p1[apos]); + lc = ac; + }; + ac = (fsw_u16)lc; + + /* get next valid character from ckey2 */ + for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) { + bc = p2[bpos]; + lc = bc; + }; + bc = (fsw_u16)lc; + + if (ac != bc || (ac == 0 && bc == 0)) + return ac - bc; + } +} + +static int +fsw_hfs_cmpi_catkey (BTreeKey *key1, BTreeKey *key2) +{ + HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1; + HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2; + + int apos, bpos, lc; + fsw_u16 ac, bc; + fsw_u32 parentId1; + int key1Len; + fsw_u16 *p1; + fsw_u16 *p2; + + parentId1 = be32_to_cpu(ckey1->parentID); + + if (parentId1 > ckey2->parentID) + return 1; + if (parentId1 < ckey2->parentID) + return -1; + + key1Len = be16_to_cpu (ckey1->nodeName.length); + + if (key1Len == 0 && ckey2->nodeName.length == 0) + return 0; + + p1 = &ckey1->nodeName.unicode[0]; + p2 = &ckey2->nodeName.unicode[0]; + + apos = bpos = 0; + + while(1) + { + /* get next valid (non-zero) character from ckey1 */ + for (lc = 0; lc == 0 && apos < key1Len; apos++) { + ac = be16_to_cpu(p1[apos]); + lc = fsw_to_lower(ac); /* NB: 0x0000 is translated to 0xffff */ + }; + ac = (fsw_u16)lc; + + /* get next valid (non-zero) character from ckey2 */ + for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) { + bc = p2[bpos]; + lc = fsw_to_lower(bc); /* NB: 0x0000 is translated to 0xffff */ + }; + bc = (fsw_u16)lc; + + if (ac != bc || (ac == 0 && bc == 0)) + return ac - bc; + } +} + +/** + * Retrieve file data mapping information. This function is called by the core when + * fsw_shandle_read needs to know where on the disk the required piece of the file's + * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called + * on the dnode before. Our task here is to get the physical disk block number for + * the requested logical block number. + */ + +static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume * vol, + struct fsw_hfs_dnode * dno, + struct fsw_extent * extent) +{ + fsw_status_t status; + fsw_u32 lbno; + HFSPlusExtentRecord *exts; + BTNodeDescriptor *node = NULL; + + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + extent->log_count = 1; + lbno = extent->log_start; + + /* we only care about data forks atm, do we? */ + exts = &dno->extents; + + while (1) + { + struct HFSPlusExtentKey* key; + struct HFSPlusExtentKey overflowkey; + fsw_u32 ptr; + fsw_u32 phys_bno; + + if (fsw_hfs_find_block(exts, &lbno, &phys_bno)) + { + extent->phys_start = phys_bno + vol->emb_block_off; + status = FSW_SUCCESS; + break; + } + + + /* Find appropriate overflow record */ + overflowkey.fileID = dno->g.dnode_id; + overflowkey.startBlock = extent->log_start - lbno; + + if (node != NULL) + { + fsw_free(node); + node = NULL; + } + + status = fsw_hfs_btree_search (&vol->extents_tree, + (BTreeKey*)&overflowkey, + fsw_hfs_cmp_extkey, + &node, &ptr); + if (status) + break; + + key = (struct HFSPlusExtentKey *) + fsw_hfs_btree_rec (&vol->extents_tree, node, ptr); + exts = (HFSPlusExtentRecord*) (key + 1); + } + + if (node != NULL) + fsw_free(node); + + return status; +} + +static const fsw_u16* g_blacklist[] = +{ + //L"AppleIntelCPUPowerManagement.kext", + NULL +}; + + +//#define HFS_FILE_INJECTION + +#ifdef HFS_FILE_INJECTION +static struct +{ + const fsw_u16* path; + const fsw_u16* name; +} g_injectList[] = +{ + { + L"/System/Library/Extensions", + L"ApplePS2Controller.kext" + }, + { + NULL, + NULL + } +}; +#endif + +static fsw_status_t +create_hfs_dnode(struct fsw_hfs_dnode * dno, + file_info_t * file_info, + struct fsw_hfs_dnode ** child_dno_out) +{ + fsw_status_t status; + struct fsw_hfs_dnode * baby; + + status = fsw_dnode_create(dno, file_info->id, file_info->type, + file_info->name, &baby); + if (status) + return status; + + baby->g.size = file_info->size; + baby->used_bytes = file_info->used; + baby->ctime = file_info->ctime; + baby->mtime = file_info->mtime; + baby->node_num = file_info->node_num; + + + /* Fill-in extents info */ + if (file_info->type == FSW_DNODE_TYPE_FILE) + { + fsw_memcpy(baby->extents, &file_info->extents, sizeof file_info->extents); + } + + *child_dno_out = baby; + + return FSW_SUCCESS; +} + + +/** + * Lookup a directory's child dnode by name. This function is called on a directory + * to retrieve the directory entry with the given name. A dnode is constructed for + * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called + * and the dnode is actually a directory. + */ + +static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume * vol, + struct fsw_hfs_dnode * dno, + struct fsw_string * lookup_name, + struct fsw_hfs_dnode ** child_dno_out) +{ + fsw_status_t status; + struct HFSPlusCatalogKey catkey; + fsw_u32 ptr; + BTNodeDescriptor * node = NULL; + struct fsw_string rec_name; + int free_data = 0, i; + HFSPlusCatalogKey* file_key; + file_info_t file_info; + + + fsw_memzero(&file_info, sizeof file_info); + file_info.name = &rec_name; + + catkey.parentID = dno->g.dnode_id; + catkey.nodeName.length = (fsw_u16)lookup_name->len; + + /* no need to allocate anything */ + if (lookup_name->type == FSW_STRING_TYPE_UTF16) + { + fsw_memcpy(catkey.nodeName.unicode, lookup_name->data, lookup_name->size); + rec_name = *lookup_name; + } else + { + status = fsw_strdup_coerce(&rec_name, FSW_STRING_TYPE_UTF16, lookup_name); + /* nothing allocated so far */ + if (status) + goto done; + free_data = 1; + fsw_memcpy(catkey.nodeName.unicode, rec_name.data, rec_name.size); + } + + /* Dirty hack: blacklisting of certain files on FS driver level */ + for (i = 0; g_blacklist[i]; i++) + { + if (fsw_memeq(g_blacklist[i], catkey.nodeName.unicode, catkey.nodeName.length*2)) + { + DPRINT2("Blacklisted %s\n", g_blacklist[i]); + status = FSW_NOT_FOUND; + goto done; + } + } + +#ifdef HFS_FILE_INJECTION + if (fsw_hfs_inject(vol, + dno, + catkey.nodeName.unicode, + catkey.nodeName.length, + &file_info)) + { + status = FSW_SUCCESS; + goto create; + } +#endif + + catkey.keyLength = (fsw_u16)(6 + rec_name.len); + + status = fsw_hfs_btree_search (&vol->catalog_tree, + (BTreeKey*)&catkey, + vol->case_sensitive ? + fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey, + &node, &ptr); + if (status) + goto done; + + file_key = (HFSPlusCatalogKey *)fsw_hfs_btree_rec (&vol->catalog_tree, node, ptr); + hfs_fill_info(vol, file_key, &file_info); + +#ifdef HFS_FILE_INJECTION +create: +#endif + status = create_hfs_dnode(dno, &file_info, child_dno_out); + if (status) + goto done; + +done: + + if (node != NULL) + fsw_free(node); + + if (free_data) + fsw_strfree(&rec_name); + + return status; +} + +/** + * Get the next directory entry when reading a directory. This function is called during + * directory iteration to retrieve the next directory entry. A dnode is constructed for + * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called + * and the dnode is actually a directory. The shandle provided by the caller is used to + * record the position in the directory between calls. + */ + +static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol, + struct fsw_hfs_dnode *dno, + struct fsw_shandle *shand, + struct fsw_hfs_dnode **child_dno_out) +{ + fsw_status_t status; + struct HFSPlusCatalogKey catkey; + fsw_u32 ptr; + BTNodeDescriptor * node = NULL; + + visitor_parameter_t param; + struct fsw_string rec_name; + + catkey.parentID = dno->g.dnode_id; + catkey.nodeName.length = 0; + + fsw_memzero(¶m, sizeof(param)); + + rec_name.type = FSW_STRING_TYPE_EMPTY; + param.file_info.name = &rec_name; + + status = fsw_hfs_btree_search (&vol->catalog_tree, + (BTreeKey*)&catkey, + vol->case_sensitive ? + fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey, + &node, &ptr); + if (status) + goto done; + + /* Iterator updates shand state */ + param.vol = vol; + param.shandle = shand; + param.parent = dno->g.dnode_id; + param.cur_pos = 0; + status = fsw_hfs_btree_iterate_node (&vol->catalog_tree, + node, + ptr, + fsw_hfs_btree_visit_node, + ¶m); + if (status) + goto done; + + status = create_hfs_dnode(dno, ¶m.file_info, child_dno_out); + + if (status) + goto done; + + done: + fsw_strfree(&rec_name); + + return status; +} + +static const char hfs_priv_prefix[] = "/\0\0\0\0HFS+ Private Data/" HFS_INODE_PREFIX; + +/** + * Get the target path of a symbolic link. This function is called when a symbolic + * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been + * called on the dnode and that it really is a symlink. + * + */ +static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno, + struct fsw_string *link_target) +{ + fsw_status_t status; + + if (dno->node_num) + { + struct fsw_string tgt; + + DPRINT2("hfs_readlink: %d\n", dno->node_num); + tgt.type = FSW_STRING_TYPE_ISO88591; + tgt.size = sizeof(hfs_priv_prefix) + 10; + tgt.len = tgt.size - 1; + status = fsw_alloc(tgt.size, &tgt.data); + if (!status) + { + char *str = tgt.data; + fsw_memcpy(tgt.data, hfs_priv_prefix, sizeof(hfs_priv_prefix)); // null chars here! +#ifdef HOST_POSIX + tgt.len = sprintf(&str[sizeof(hfs_priv_prefix) - 1], "%d", dno->node_num); +#else + tgt.len = (int)AsciiSPrint(&str[sizeof(hfs_priv_prefix) - 1], tgt.len, "%d", dno->node_num); +#endif + tgt.len += sizeof(hfs_priv_prefix) - 1; + status = fsw_strdup_coerce(link_target, vol->g.host_string_type, &tgt); + fsw_strfree(&tgt); + } + return status; + } + + return FSW_UNSUPPORTED; +} + +static int fsw_hfs_btree_find_id(BTreeKey *record, void* param) +{ + visitor_parameter_t *vp = (visitor_parameter_t*)param; + fsw_u8 *base = (fsw_u8*)record->rawData + be16_to_cpu(record->length16) + 2; + fsw_u16 rec_type = be16_to_cpu(*(fsw_u16*)base); + struct HFSPlusCatalogKey *cat_key = (HFSPlusCatalogKey*)record; + fsw_u16 name_len; + fsw_u16 *name_ptr; + fsw_u16 *old_ptr; + int i; + struct fsw_string *file_name; + struct fsw_string new_name; + + if (be32_to_cpu(cat_key->parentID) != vp->parent) + return -1; + + if (!vp->cur_pos) + vp->cur_pos = be32_to_cpu(cat_key->parentID); + + /* Not what we're looking for. */ + if (vp->file_info.id != vp->cur_pos++) + return 0; + + if (rec_type == kHFSPlusFolderThreadRecord || rec_type == kHFSPlusFileThreadRecord) + { + HFSPlusCatalogThread *thread; + + thread = (HFSPlusCatalogThread *)base; + vp->file_info.id = be32_to_cpu(thread->parentID); + + name_len = be16_to_cpu(thread->nodeName.length); + + file_name = vp->file_info.name; + + new_name.len = name_len + 1 + file_name->len; + new_name.size = sizeof(fsw_u16) * new_name.len; + fsw_alloc(new_name.size, &new_name.data); + name_ptr = (fsw_u16*)new_name.data; + /* Tack on path separator. */ +#ifdef HOST_POSIX + name_ptr[0] = L'/'; +#else + name_ptr[0] = L'\\'; +#endif + /* Copy over + swap the new path component. */ + for (i = 0; i < name_len; i++) + name_ptr[i + 1] = be16_to_cpu(thread->nodeName.unicode[i]); + if (file_name->len) { + /* Tack on the previous path. */ + old_ptr = (fsw_u16*)file_name->data; + for (++i; i < new_name.len; i++ ) + name_ptr[i] = *old_ptr++; + } + + fsw_free(file_name->data); + file_name->len = new_name.len; + file_name->size = new_name.size; + file_name->data = new_name.data; + file_name->type = FSW_STRING_TYPE_UTF16; + + /* This was it, stop iterating. */ + return 1; + } + + return 0; +} + +/** + * Obtain the full path of a file given its CNID (Catalog Node ID), i.e. + * file or folder ID. + * + */ +static fsw_status_t fsw_hfs_get_path_from_cnid(struct fsw_hfs_volume *vol, fsw_u32 cnid, struct fsw_string *path) +{ + fsw_status_t status = FSW_UNSUPPORTED; + fsw_u32 ptr; + BTNodeDescriptor *node = NULL; + struct HFSPlusCatalogKey catkey; + visitor_parameter_t param; + struct fsw_string rec_name; + + /* The CNID must be a valid user node ID. */ + if (cnid < kHFSFirstUserCatalogNodeID) + goto done; + + fsw_memzero(¶m, sizeof(param)); + fsw_memzero(&rec_name, sizeof(rec_name)); + + catkey.parentID = cnid; + catkey.nodeName.length = 0; + + param.vol = vol; + param.shandle = NULL; + param.file_info.id = cnid; + param.parent = cnid; + param.cur_pos = 0; + + do { + rec_name.type = FSW_STRING_TYPE_EMPTY; + param.file_info.name = &rec_name; + + status = fsw_hfs_btree_search(&vol->catalog_tree, (BTreeKey*)&catkey, + vol->case_sensitive ? fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey, + &node, &ptr); + if (status) + goto done; + + status = fsw_hfs_btree_iterate_node(&vol->catalog_tree, node, ptr, + fsw_hfs_btree_find_id, ¶m); + if (status) + goto done; + + param.parent = param.file_info.id; + param.cur_pos = 0; + + catkey.parentID = param.file_info.id; + catkey.nodeName.length = 0; + } while (catkey.parentID >= kHFSFirstUserCatalogNodeID); + + /* If everything worked out , the final parent ID will be the root folder ID. */ + if (catkey.parentID == kHFSRootFolderID) + { + *path = *param.file_info.name; + status = FSW_SUCCESS; + } + else + status = FSW_NOT_FOUND; + +done: + return status; +} + +/** + * Get the path of the HFS+ blessed file, if any. + * + */ +/*static*/ fsw_status_t fsw_hfs_get_blessed_file(struct fsw_hfs_volume *vol, struct fsw_string *path) +{ + fsw_status_t status = FSW_UNSUPPORTED; + fsw_u32 bfile_id; + fsw_u32 *finderinfo; + + finderinfo = (fsw_u32 *)&vol->primary_voldesc->finderInfo; + bfile_id = finderinfo[1]; + bfile_id = be32_to_cpu(bfile_id); + + DPRINT2("Blessed file ID: %u\n", bfile_id); + + status = fsw_hfs_get_path_from_cnid(vol, bfile_id, path); +#ifdef HOST_POSIX + if (!status) + { + fsw_u16 *name_ptr; + int i; + + printf("Blessed file: "); + name_ptr = (fsw_u16*)path->data; + for (i = 0; i < path->len; i++) + printf("%c", name_ptr[i]); + printf("\n"); + } +#endif + + return status; +} + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.h new file mode 100644 index 00000000..ecb1cfeb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.h @@ -0,0 +1,177 @@ +/* $Id: fsw_hfs.h $ */ +/** @file + * fsw_hfs.h - HFS file system driver header. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef _FSW_HFS_H_ +#define _FSW_HFS_H_ + +#define VOLSTRUCTNAME fsw_hfs_volume +#define DNODESTRUCTNAME fsw_hfs_dnode + +#include "fsw_core.h" + +#define IN_RING0 +#if !defined(ARCH_BITS) || !defined(HC_ARCH_BITS) +# error "please add right bitness" +#endif +#include "iprt/formats/hfs.h" +#include "iprt/asm.h" /* endian conversion */ + +#ifndef HOST_POSIX +#include <Library/BaseLib.h> +#endif + +//! Block size for HFS volumes. +#define HFS_BLOCKSIZE 512 + +//! Block number where the HFS superblock resides. +#define HFS_SUPERBLOCK_BLOCKNO 2 + +#ifdef _MSC_VER +/* vasily: disable warning for non-standard anonymous struct/union + * declarations + */ +# pragma warning (disable:4201) +#endif + +struct hfs_dirrec { + fsw_u8 _dummy; +}; + +#pragma pack(1) +struct fsw_hfs_key +{ + union + { + struct HFSPlusExtentKey ext_key; + struct HFSPlusCatalogKey cat_key; + fsw_u16 key_len; /* Length is at the beginning of all keys */ + }; +}; +#pragma pack() + +typedef enum { + /* Regular HFS */ + FSW_HFS_PLAIN = 0, + /* HFS+ */ + FSW_HFS_PLUS, + /* HFS+ embedded to HFS */ + FSW_HFS_PLUS_EMB +} fsw_hfs_kind; + +/** + * HFS: Dnode structure with HFS-specific data. + */ +struct fsw_hfs_dnode +{ + struct fsw_dnode g; //!< Generic dnode structure + HFSPlusExtentRecord extents; + fsw_u32 ctime; + fsw_u32 mtime; + fsw_u64 used_bytes; + fsw_u32 node_num; +}; + +/** + * HFS: In-memory B-tree structure. + */ +struct fsw_hfs_btree +{ + fsw_u32 root_node; + fsw_u32 node_size; + struct fsw_hfs_dnode* file; +}; + + +/** + * HFS: In-memory volume structure with HFS-specific data. + */ + +struct fsw_hfs_volume +{ + struct fsw_volume g; //!< Generic volume structure + + struct HFSPlusVolumeHeader *primary_voldesc; //!< Volume Descriptor + struct fsw_hfs_btree catalog_tree; // Catalog tree + struct fsw_hfs_btree extents_tree; // Extents overflow tree + struct fsw_hfs_dnode root_file; + int case_sensitive; + fsw_u32 block_size_shift; + fsw_hfs_kind hfs_kind; + fsw_u32 emb_block_off; +}; + +/* Endianess swappers. */ +DECLINLINE(fsw_u16) +be16_to_cpu(fsw_u16 x) +{ + return RT_BE2H_U16(x); +} + +DECLINLINE(fsw_u16) +cpu_to_be16(fsw_u16 x) +{ + return RT_H2BE_U16(x); +} + + +DECLINLINE(fsw_u32) +cpu_to_be32(fsw_u32 x) +{ + return RT_H2BE_U32(x); +} + +DECLINLINE(fsw_u32) +be32_to_cpu(fsw_u32 x) +{ + return RT_BE2H_U32(x); +} + +DECLINLINE(fsw_u64) +be64_to_cpu(fsw_u64 x) +{ +#ifdef RT_LITTLE_ENDIAN +#ifdef HOST_POSIX + return RT_BE2H_U64(x); +#else + return SwapBytes64(x); +#endif +#else + return x; +#endif +} + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.c new file mode 100644 index 00000000..3dc8c1db --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.c @@ -0,0 +1,679 @@ +/* $Id: fsw_iso9660.c $ */ +/** @file + * fsw_iso9660.c - ISO9660 file system driver code. + * + * Current limitations: + * - Files must be in one extent (i.e. Level 2) + * - No Joliet or Rock Ridge extensions + * - No interleaving + * - inode number generation strategy fails on volumes > 2 GB + * - No blocksizes != 2048 + * - No High Sierra or anything else != 'CD001' + * - No volume sets with directories pointing at other volumes + * - No extended attribute records + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_iso9660.h" + + +// functions + +static fsw_status_t fsw_iso9660_volume_mount(struct fsw_iso9660_volume *vol); +static void fsw_iso9660_volume_free(struct fsw_iso9660_volume *vol); +static fsw_status_t fsw_iso9660_volume_stat(struct fsw_iso9660_volume *vol, struct fsw_volume_stat *sb); + +static fsw_status_t fsw_iso9660_dnode_fill(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno); +static void fsw_iso9660_dnode_free(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno); +static fsw_status_t fsw_iso9660_dnode_stat(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_dnode_stat *sb); +static fsw_status_t fsw_iso9660_get_extent(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_extent *extent); + +static fsw_status_t fsw_iso9660_dir_lookup(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_string *lookup_name, struct fsw_iso9660_dnode **child_dno); +static fsw_status_t fsw_iso9660_dir_read(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_shandle *shand, struct fsw_iso9660_dnode **child_dno); +static fsw_status_t fsw_iso9660_read_dirrec(struct fsw_iso9660_volume *vol, struct fsw_shandle *shand, struct iso9660_dirrec_buffer *dirrec_buffer); + +static fsw_status_t fsw_iso9660_readlink(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_string *link); + +static fsw_status_t rr_find_sp(struct iso9660_dirrec *dirrec, struct fsw_rock_ridge_susp_sp **psp); +static fsw_status_t rr_find_nm(struct fsw_iso9660_volume *vol, struct iso9660_dirrec *dirrec, int off, struct fsw_string *str); +static fsw_status_t rr_read_ce(struct fsw_iso9660_volume *vol, union fsw_rock_ridge_susp_ce *ce, fsw_u8 *begin); +// +// Dispatch Table +// + +struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(iso9660) = { + { FSW_STRING_TYPE_ISO88591, 4, 4, "iso9660" }, + sizeof(struct fsw_iso9660_volume), + sizeof(struct fsw_iso9660_dnode), + + fsw_iso9660_volume_mount, + fsw_iso9660_volume_free, + fsw_iso9660_volume_stat, + fsw_iso9660_dnode_fill, + fsw_iso9660_dnode_free, + fsw_iso9660_dnode_stat, + fsw_iso9660_get_extent, + fsw_iso9660_dir_lookup, + fsw_iso9660_dir_read, + fsw_iso9660_readlink, +}; + +static fsw_status_t rr_find_sp(struct iso9660_dirrec *dirrec, struct fsw_rock_ridge_susp_sp **psp) +{ + fsw_u8 *r; + int off = 0; + struct fsw_rock_ridge_susp_sp *sp; + r = (fsw_u8 *)((fsw_u8 *)dirrec + sizeof(*dirrec) + dirrec->file_identifier_length); + off = (int)(r - (fsw_u8 *)dirrec); + while(off < dirrec->dirrec_length) + { + if (*r == 'S') + { + sp = (struct fsw_rock_ridge_susp_sp *)r; + if( sp->e.sig[0] == 'S' + && sp->e.sig[1] == 'P' + && sp->magic[0] == 0xbe + && sp->magic[1] == 0xef) + { + *psp = sp; + return FSW_SUCCESS; + } + } + r++; + off = (int)(r - (fsw_u8 *)dirrec); + } + *psp = NULL; + return FSW_NOT_FOUND; +} + +static fsw_status_t rr_find_nm(struct fsw_iso9660_volume *vol, struct iso9660_dirrec *dirrec, int off, struct fsw_string *str) +{ + fsw_u8 *r, *begin; + int fCe = 0; + struct fsw_rock_ridge_susp_nm *nm; + int limit = dirrec->dirrec_length; + begin = (fsw_u8 *)dirrec; + r = (fsw_u8 *)dirrec + off; + str->data = NULL; + str->len = 0; + str->size = 0; + str->type = 0; + while(off < limit) + { + if (r[0] == 'C' && r[1] == 'E' && r[2] == 28) + { + int rc; + int ce_off; + union fsw_rock_ridge_susp_ce *ce; + if (fCe == 0) + fsw_alloc_zero(ISO9660_BLOCKSIZE, (void *)&begin); + fCe = 1; + DEBUG((DEBUG_WARN, "%a:%d we found CE before NM or its continuation\n", __FILE__, __LINE__)); + ce = (union fsw_rock_ridge_susp_ce *)r; + limit = ISOINT(ce->X.len); + ce_off = ISOINT(ce->X.offset); + rc = rr_read_ce(vol, ce, begin); + if (rc != FSW_SUCCESS) + { + fsw_free(begin); + return rc; + } + begin += ce_off; + r = begin; + } + if (r[0] == 'N' && r[1] == 'M') + { + nm = (struct fsw_rock_ridge_susp_nm *)r; + if( nm->e.sig[0] == 'N' + && nm->e.sig[1] == 'M') + { + int len = 0; + fsw_u8 *tmp = NULL; + if (nm->flags & RR_NM_CURR) + { + fsw_memdup(str->data, ".", 1); + str->len = 1; + goto done; + } + if (nm->flags & RR_NM_PARE) + { + fsw_memdup(str->data, "..", 2); + str->len = 2; + goto done; + } + len = nm->e.len - sizeof(struct fsw_rock_ridge_susp_nm) + 1; + fsw_alloc_zero(str->len + len, (void **)&tmp); + if (str->data != NULL) + { + fsw_memcpy(tmp, str->data, str->len); + fsw_free(str->data); + } + DEBUG((DEBUG_INFO, "dst:%p src:%p len:%d\n", tmp + str->len, &nm->name[0], len)); + fsw_memcpy(tmp + str->len, &nm->name[0], len); + str->data = tmp; + str->len += len; + + if ((nm->flags & RR_NM_CONT) == 0) + goto done; + } + } + r++; + off = (int)(r - (fsw_u8 *)begin); + } + if(fCe == 1) + fsw_free(begin); + return FSW_NOT_FOUND; +done: + str->type = FSW_STRING_TYPE_ISO88591; + str->size = str->len; + if(fCe == 1) + fsw_free(begin); + return FSW_SUCCESS; +} + +static fsw_status_t rr_read_ce(struct fsw_iso9660_volume *vol, union fsw_rock_ridge_susp_ce *ce, fsw_u8 *begin) +{ + int rc; + rc = vol->g.host_table->read_block(&vol->g, ISOINT(ce->X.block_loc), begin); + if (rc != FSW_SUCCESS) + return rc; + return FSW_SUCCESS; +} +/** + * Mount an ISO9660 volume. Reads the superblock and constructs the + * root directory dnode. + */ + +static fsw_status_t fsw_iso9660_volume_mount(struct fsw_iso9660_volume *vol) +{ + fsw_status_t status; + void *buffer; + fsw_u32 blockno; + struct iso9660_volume_descriptor *voldesc; + struct iso9660_primary_volume_descriptor *pvoldesc; + fsw_u32 voldesc_type; + int i; + struct fsw_string s; + struct iso9660_dirrec rootdir; + int sua_pos; + char *sig; + int skip; + struct fsw_rock_ridge_susp_entry *entry; + + // read through the Volume Descriptor Set + fsw_set_blocksize(vol, ISO9660_BLOCKSIZE, ISO9660_BLOCKSIZE); + blockno = ISO9660_SUPERBLOCK_BLOCKNO; + + do { + status = fsw_block_get(vol, blockno, 0, &buffer); + if (status) + return status; + + voldesc = (struct iso9660_volume_descriptor *)buffer; + voldesc_type = voldesc->volume_descriptor_type; + if (fsw_memeq(voldesc->standard_identifier, "CD001", 5)) { + // descriptor follows ISO 9660 standard + if (voldesc_type == 1 && voldesc->volume_descriptor_version == 1) { + // suitable Primary Volume Descriptor found + if (vol->primary_voldesc) { + fsw_free(vol->primary_voldesc); + vol->primary_voldesc = NULL; + } + status = fsw_memdup((void **)&vol->primary_voldesc, voldesc, ISO9660_BLOCKSIZE); + } + } else if (!fsw_memeq(voldesc->standard_identifier, "CD", 2)) { + // completely alien standard identifier, stop reading + voldesc_type = 255; + } + + fsw_block_release(vol, blockno, buffer); + blockno++; + } while (!status && voldesc_type != 255); + if (status) + return status; + + // get information from Primary Volume Descriptor + if (vol->primary_voldesc == NULL) + return FSW_UNSUPPORTED; + pvoldesc = vol->primary_voldesc; + if (ISOINT(pvoldesc->logical_block_size) != 2048) + return FSW_UNSUPPORTED; + + // get volume name + for (i = 32; i > 0; i--) + if (pvoldesc->volume_identifier[i-1] != ' ') + break; + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = i; + s.data = pvoldesc->volume_identifier; + status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); + if (status) + return status; + + // setup the root dnode + status = fsw_dnode_create_root(vol, ISO9660_SUPERBLOCK_BLOCKNO << ISO9660_BLOCKSIZE_BITS, &vol->g.root); + if (status) + return status; + fsw_memcpy(&vol->g.root->dirrec, &pvoldesc->root_directory, sizeof(struct iso9660_dirrec)); + + if ( pvoldesc->escape[0] == 0x25 + && pvoldesc->escape[1] == 0x2f + && ( pvoldesc->escape[2] == 0x40 + || pvoldesc->escape[2] == 0x43 + || pvoldesc->escape[2] == 0x45)) + { + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success (joliet!!!)\n"))); + vol->fJoliet = 1; + } + + + fsw_memcpy(&rootdir, &pvoldesc->root_directory, sizeof(rootdir)); + sua_pos = (sizeof(struct iso9660_dirrec)) + rootdir.file_identifier_length + (rootdir.file_identifier_length % 2) - 2; + //int sua_size = rootdir.dirrec_length - rootdir.file_identifier_length; + //FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success (SUA(pos:%x, sz:%d)!!!)\n"), sua_pos, sua_size)); + +#if 1 + status = fsw_block_get(vol, ISOINT(rootdir.extent_location), 0, &buffer); + sig = (char *)buffer + sua_pos; + skip = 0; + entry = (struct fsw_rock_ridge_susp_entry *)sig; + if ( entry->sig[0] == 'S' + && entry->sig[1] == 'P') + { + struct fsw_rock_ridge_susp_sp *sp = (struct fsw_rock_ridge_susp_sp *)entry; + if (sp->magic[0] == 0xbe && sp->magic[1] == 0xef) + { + vol->fRockRidge = 1; + } else { + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: SP magic isn't valid\n"))); + } + skip = sp->skip; + } +#endif + // release volume descriptors + fsw_free(vol->primary_voldesc); + vol->primary_voldesc = NULL; + + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success\n"))); + + return FSW_SUCCESS; +} + +/** + * Free the volume data structure. Called by the core after an unmount or after + * an unsuccessful mount to release the memory used by the file system type specific + * part of the volume structure. + */ + +static void fsw_iso9660_volume_free(struct fsw_iso9660_volume *vol) +{ + if (vol->primary_voldesc) + fsw_free(vol->primary_voldesc); +} + +/** + * Get in-depth information on a volume. + */ + +static fsw_status_t fsw_iso9660_volume_stat(struct fsw_iso9660_volume *vol, struct fsw_volume_stat *sb) +{ + sb->total_bytes = 0; //(fsw_u64)vol->sb->s_blocks_count * vol->g.log_blocksize; + sb->free_bytes = 0; + return FSW_SUCCESS; +} + +/** + * Get full information on a dnode from disk. This function is called by the core + * whenever it needs to access fields in the dnode structure that may not + * be filled immediately upon creation of the dnode. In the case of iso9660, we + * delay fetching of the inode structure until dnode_fill is called. The size and + * type fields are invalid until this function has been called. + */ + +static fsw_status_t fsw_iso9660_dnode_fill(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno) +{ + // get info from the directory record + dno->g.size = ISOINT(dno->dirrec.data_length); + if (dno->dirrec.file_flags & 0x02) + dno->g.type = FSW_DNODE_TYPE_DIR; + else + dno->g.type = FSW_DNODE_TYPE_FILE; + + return FSW_SUCCESS; +} + +/** + * Free the dnode data structure. Called by the core when deallocating a dnode + * structure to release the memory used by the file system type specific part + * of the dnode structure. + */ + +static void fsw_iso9660_dnode_free(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno) +{ +} + +/** + * Get in-depth information on a dnode. The core makes sure that fsw_iso9660_dnode_fill + * has been called on the dnode before this function is called. Note that some + * data is not directly stored into the structure, but passed to a host-specific + * callback that converts it to the host-specific format. + */ + +static fsw_status_t fsw_iso9660_dnode_stat(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_dnode_stat *sb) +{ + sb->used_bytes = (dno->g.size + (ISO9660_BLOCKSIZE-1)) & ~(ISO9660_BLOCKSIZE-1); + /* + sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, dno->raw->i_ctime); + sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, dno->raw->i_atime); + sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, dno->raw->i_mtime); + sb->store_attr_posix(sb, dno->raw->i_mode); + */ + + return FSW_SUCCESS; +} + +/** + * Retrieve file data mapping information. This function is called by the core when + * fsw_shandle_read needs to know where on the disk the required piece of the file's + * data can be found. The core makes sure that fsw_iso9660_dnode_fill has been called + * on the dnode before. Our task here is to get the physical disk block number for + * the requested logical block number. + */ + +static fsw_status_t fsw_iso9660_get_extent(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_extent *extent) +{ + // Preconditions: The caller has checked that the requested logical block + // is within the file's size. The dnode has complete information, i.e. + // fsw_iso9660_dnode_read_info was called successfully on it. + + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + extent->phys_start = ISOINT(dno->dirrec.extent_location); + extent->log_start = 0; + extent->log_count = (ISOINT(dno->dirrec.data_length) + (ISO9660_BLOCKSIZE-1)) >> ISO9660_BLOCKSIZE_BITS; + return FSW_SUCCESS; +} + +/** + * Lookup a directory's child dnode by name. This function is called on a directory + * to retrieve the directory entry with the given name. A dnode is constructed for + * this entry and returned. The core makes sure that fsw_iso9660_dnode_fill has been called + * and the dnode is actually a directory. + */ + +static fsw_status_t fsw_iso9660_dir_lookup(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_string *lookup_name, struct fsw_iso9660_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_shandle shand; + struct iso9660_dirrec_buffer dirrec_buffer; + struct iso9660_dirrec *dirrec = &dirrec_buffer.dirrec; + + // Preconditions: The caller has checked that dno is a directory node. + + // setup handle to read the directory + status = fsw_shandle_open(dno, &shand); + if (status) + return status; + + // scan the directory for the file + while (1) { + // read next entry + status = fsw_iso9660_read_dirrec(vol, &shand, &dirrec_buffer); + if (status) + goto errorexit; + if (dirrec->dirrec_length == 0) { + // end of directory reached + status = FSW_NOT_FOUND; + goto errorexit; + } + + // skip . and .. + if (dirrec->file_identifier_length == 1 && + (dirrec->file_identifier[0] == 0 || dirrec->file_identifier[0] == 1)) + continue; + + // compare name + if (fsw_streq(lookup_name, &dirrec_buffer.name)) /// @todo compare case-insensitively + break; + } + + // setup a dnode for the child item + status = fsw_dnode_create(dno, dirrec_buffer.ino, FSW_DNODE_TYPE_UNKNOWN, &dirrec_buffer.name, child_dno_out); + if (status == FSW_SUCCESS) + fsw_memcpy(&(*child_dno_out)->dirrec, dirrec, sizeof(struct iso9660_dirrec)); + +errorexit: + fsw_shandle_close(&shand); + return status; +} + +/** + * Get the next directory entry when reading a directory. This function is called during + * directory iteration to retrieve the next directory entry. A dnode is constructed for + * the entry and returned. The core makes sure that fsw_iso9660_dnode_fill has been called + * and the dnode is actually a directory. The shandle provided by the caller is used to + * record the position in the directory between calls. + */ + +static fsw_status_t fsw_iso9660_dir_read(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_shandle *shand, struct fsw_iso9660_dnode **child_dno_out) +{ + fsw_status_t status; + struct iso9660_dirrec_buffer dirrec_buffer; + struct iso9660_dirrec *dirrec = &dirrec_buffer.dirrec; + + // Preconditions: The caller has checked that dno is a directory node. The caller + // has opened a storage handle to the directory's storage and keeps it around between + // calls. + /* (vasily) directory nodes are 4096 bytes that is two logical blocks so read dir operation + * should read both blocks. + */ + + while (1) { + // read next entry + if (shand->pos >= dno->g.size) + return FSW_NOT_FOUND; // end of directory + status = fsw_iso9660_read_dirrec(vol, shand, &dirrec_buffer); + if (status) + return status; + if (dirrec->dirrec_length == 0) + { + // try the next block + shand->pos =(shand->pos & ~(vol->g.log_blocksize - 1)) + vol->g.log_blocksize; + continue; + } + + // skip . and .. + if (dirrec->file_identifier_length == 1 && + (dirrec->file_identifier[0] == 0 || dirrec->file_identifier[0] == 1)) + continue; + break; + } + + // setup a dnode for the child item + status = fsw_dnode_create(dno, dirrec_buffer.ino, FSW_DNODE_TYPE_UNKNOWN, &dirrec_buffer.name, child_dno_out); + if (status == FSW_SUCCESS) + fsw_memcpy(&(*child_dno_out)->dirrec, dirrec, sizeof(struct iso9660_dirrec)); + + return status; +} + +/** + * Read a directory entry from the directory's raw data. This internal function is used + * to read a raw iso9660 directory entry into memory. The shandle's position pointer is adjusted + * to point to the next entry. + */ + +static fsw_status_t fsw_iso9660_read_dirrec(struct fsw_iso9660_volume *vol, struct fsw_shandle *shand, struct iso9660_dirrec_buffer *dirrec_buffer) +{ + fsw_status_t status; + fsw_u32 i, buffer_size, remaining_size, name_len; + struct fsw_rock_ridge_susp_sp *sp = NULL; + struct iso9660_dirrec *dirrec = &dirrec_buffer->dirrec; + int rc; + + dirrec_buffer->ino = (ISOINT(((struct fsw_iso9660_dnode *)shand->dnode)->dirrec.extent_location) + << ISO9660_BLOCKSIZE_BITS) + + (fsw_u32)shand->pos; + + // read fixed size part of directory record + buffer_size = 33; + status = fsw_shandle_read(shand, &buffer_size, dirrec); + if (status) + { + DEBUG((DEBUG_INFO, "%a:%d \n", __FILE__, __LINE__)); + return status; + } + + if (buffer_size < 33 || dirrec->dirrec_length == 0) { + // end of directory reached + fsw_u8 *r; + r = (fsw_u8 *)dirrec; + DEBUG((DEBUG_INFO, "%a:%d bs:%d dl:%d\n", __FILE__, __LINE__, buffer_size, dirrec->dirrec_length)); + for(i = 0; i < buffer_size; ++i) + { + DEBUG((DEBUG_INFO, "r[%d]:%c", i, r[i])); + } + dirrec->dirrec_length = 0; + return FSW_SUCCESS; + } + if (dirrec->dirrec_length < 33 || + dirrec->dirrec_length < 33 + dirrec->file_identifier_length) + return FSW_VOLUME_CORRUPTED; + + DEBUG((DEBUG_INFO, "%a:%d, dirrec_length: %d\n", __FILE__, __LINE__, dirrec->dirrec_length)); + + // read variable size part of directory record + buffer_size = remaining_size = dirrec->dirrec_length - 33; + status = fsw_shandle_read(shand, &buffer_size, dirrec->file_identifier); + if (status) + return status; + if (buffer_size < remaining_size) + return FSW_VOLUME_CORRUPTED; + + if (vol->fRockRidge) + { + UINTN sp_off = sizeof(*dirrec) + dirrec->file_identifier_length; + rc = rr_find_sp(dirrec, &sp); + if ( rc == FSW_SUCCESS + && sp != NULL) + { + sp_off = (fsw_u8 *)&sp[1] - (fsw_u8 *)dirrec + sp->skip; + } + rc = rr_find_nm(vol, dirrec, (int)sp_off, &dirrec_buffer->name); + if (rc == FSW_SUCCESS) + return FSW_SUCCESS; + } + + // setup name + name_len = dirrec->file_identifier_length; + for (i = name_len - 1; i > 0; i--) { + if (dirrec->file_identifier[i] == ';') { + name_len = i; // cut the ISO9660 version number off + break; + } + } + if (name_len > 0 && dirrec->file_identifier[name_len-1] == '.') + name_len--; // also cut the extension separator if the extension is empty + dirrec_buffer->name.type = FSW_STRING_TYPE_ISO88591; + dirrec_buffer->name.len = dirrec_buffer->name.size = name_len; + dirrec_buffer->name.data = dirrec->file_identifier; + DEBUG((DEBUG_INFO, "%a:%d: dirrec_buffer->name.data:%a\n", __FILE__, __LINE__, dirrec_buffer->name.data)); + return FSW_SUCCESS; +} + +/** + * Get the target path of a symbolic link. This function is called when a symbolic + * link needs to be resolved. The core makes sure that the fsw_iso9660_dnode_fill has been + * called on the dnode and that it really is a symlink. + * + * For iso9660, the target path can be stored inline in the inode structure (in the space + * otherwise occupied by the block pointers) or in the inode's data. There is no flag + * indicating this, only the number of blocks entry (i_blocks) can be used as an + * indication. The check used here comes from the Linux kernel. + */ + +static fsw_status_t fsw_iso9660_readlink(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_string *link_target) +{ + fsw_status_t status; + + if (dno->g.size > FSW_PATH_MAX) + return FSW_VOLUME_CORRUPTED; + + status = fsw_dnode_readlink_data(dno, link_target); + + return status; +} + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.h new file mode 100644 index 00000000..0779ab45 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.h @@ -0,0 +1,235 @@ +/* $Id: fsw_iso9660.h $ */ +/** @file + * fsw_iso9660.h - ISO9660 file system driver header. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_ISO9660_H_ +#define _FSW_ISO9660_H_ + +#define VOLSTRUCTNAME fsw_iso9660_volume +#define DNODESTRUCTNAME fsw_iso9660_dnode +#include "fsw_core.h" + + +//! Block size for ISO9660 volumes. +#define ISO9660_BLOCKSIZE 2048 +#define ISO9660_BLOCKSIZE_BITS 11 +//! Block number where the ISO9660 superblock resides. +#define ISO9660_SUPERBLOCK_BLOCKNO 16 + + +#pragma pack(1) + +typedef struct { + fsw_u16 lsb; + fsw_u16 msb; +} iso9660_u16; + +typedef struct { + fsw_u32 lsb; + fsw_u32 msb; +} iso9660_u32; + +#define ISOINT(lsbmsbvalue) ((lsbmsbvalue).lsb) + +struct iso9660_dirrec { + fsw_u8 dirrec_length; + fsw_u8 ear_length; + iso9660_u32 extent_location; + iso9660_u32 data_length; + fsw_u8 recording_datetime[7]; + fsw_u8 file_flags; + fsw_u8 file_unit_size; + fsw_u8 interleave_gap_size; + iso9660_u16 volume_sequence_number; + fsw_u8 file_identifier_length; + char file_identifier[1]; +}; +//#if sizeof(struct fsw_iso9660_dirrec) != 34 +//#fail Structure fsw_iso9660_dirrec has wrong size +//#endif + +struct iso9660_volume_descriptor { + fsw_u8 volume_descriptor_type; + char standard_identifier[5]; + fsw_u8 volume_descriptor_version; +}; + +struct iso9660_primary_volume_descriptor { + fsw_u8 volume_descriptor_type; + char standard_identifier[5]; + fsw_u8 volume_descriptor_version; + fsw_u8 unused1; + char system_identifier[32]; + char volume_identifier[32]; + fsw_u8 unused2[8]; + iso9660_u32 volume_space_size; + fsw_u8 unused3[4]; + fsw_u8 escape[3]; + fsw_u8 unused4[25]; + iso9660_u16 volume_set_size; + iso9660_u16 volume_sequence_number; + iso9660_u16 logical_block_size; + iso9660_u32 path_table_size; + fsw_u32 location_type_l_path_table; + fsw_u32 location_optional_type_l_path_table; + fsw_u32 location_type_m_path_table; + fsw_u32 location_optional_type_m_path_table; + struct iso9660_dirrec root_directory; + char volume_set_identifier[128]; + char publisher_identifier[128]; + char data_preparer_identifier[128]; + char application_identifier[128]; + char copyright_file_identifier[37]; + char abstract_file_identifier[37]; + char bibliographic_file_identifier[37]; + char volume_creation_datetime[17]; + char volume_modification_datetime[17]; + char volume_expiration_datetime[17]; + char volume_effective_datetime[17]; + fsw_u8 file_structure_version; + fsw_u8 reserved1; + fsw_u8 application_use[512]; + fsw_u8 reserved2[653]; +}; +//#if sizeof(struct fsw_iso9660_volume_descriptor) != 2048 +//#fail Structure fsw_iso9660_volume_descriptor has wrong size +//#endif + +#pragma pack() + +struct iso9660_dirrec_buffer { + fsw_u32 ino; + struct fsw_string name; + struct iso9660_dirrec dirrec; + char dirrec_buffer[222]; +}; + + +/** + * ISO9660: Volume structure with ISO9660-specific data. + */ + +struct fsw_iso9660_volume { + struct fsw_volume g; //!< Generic volume structure + /*Note: don't move g!*/ + int fJoliet; + /*Joliet specific fields*/ + int fRockRidge; + /*Rock Ridge specific fields*/ + int rr_susp_skip; + + struct iso9660_primary_volume_descriptor *primary_voldesc; //!< Full Primary Volume Descriptor +}; + +/** + * ISO9660: Dnode structure with ISO9660-specific data. + */ + +struct fsw_iso9660_dnode { + struct fsw_dnode g; //!< Generic dnode structure + + struct iso9660_dirrec dirrec; //!< Fixed part of the directory record (i.e. w/o name) +}; + + +struct fsw_rock_ridge_susp_entry +{ + fsw_u8 sig[2]; + fsw_u8 len; + fsw_u8 ver; +}; + +struct fsw_rock_ridge_susp_sp +{ + struct fsw_rock_ridge_susp_entry e; + fsw_u8 magic[2]; + fsw_u8 skip; +}; + +struct fsw_rock_ridge_susp_nm +{ + struct fsw_rock_ridge_susp_entry e; + fsw_u8 flags; + fsw_u8 name[1]; +}; + +#define RR_NM_CONT (1<<0) +#define RR_NM_CURR (1<<1) +#define RR_NM_PARE (1<<2) + +union fsw_rock_ridge_susp_ce +{ + struct X{ + struct fsw_rock_ridge_susp_entry e; + iso9660_u32 block_loc; + iso9660_u32 offset; + iso9660_u32 len; + } X; + fsw_u8 raw[28]; +}; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_lib.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_lib.c new file mode 100644 index 00000000..64a0a5cf --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_lib.c @@ -0,0 +1,564 @@ +/* $Id: fsw_lib.c $ */ +/** @file + * fsw_lib.c - Core file system wrapper library functions. + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * --------------------------------------------------------------------------- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_core.h" + +/* Include generated string encoding specific functions */ +#include "fsw_strfunc.h" + + +/** + * Allocate memory and clear it. + */ + +fsw_status_t fsw_alloc_zero(int len, void **ptr_out) +{ + fsw_status_t status; + + status = fsw_alloc(len, ptr_out); + if (status) + return status; + fsw_memzero(*ptr_out, len); + return FSW_SUCCESS; +} + +/** + * Duplicate a piece of data. + */ + +fsw_status_t fsw_memdup(void **dest_out, void *src, int len) +{ + fsw_status_t status; + + status = fsw_alloc(len, dest_out); + if (status) + return status; + fsw_memcpy(*dest_out, src, len); + return FSW_SUCCESS; +} + +/** + * Get the length of a string. Returns the number of characters in the string. + */ + +int fsw_strlen(struct fsw_string *s) +{ + if (s->type == FSW_STRING_TYPE_EMPTY) + return 0; + return s->len; +} + +#if 0 +static const fsw_u16 +fsw_lower_case_table[] = +{ + + /* High-byte indices ( == 0 iff no case mapping and no ignorables ) */ + + /* 0 */ 0x0000, 0x0100, 0x0000, 0x0200, 0x0300, 0x0400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 1 */ 0x0500, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 2 */ 0x0600, 0x0700, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x0900, + + /* Table 1 (for high byte 0x01) */ + + /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, + /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, + /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127, 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, + /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140, + /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, 0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, + /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F, + /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167, 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F, + /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, + /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259, + /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275, + /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8, 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF, + /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292, 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF, + /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9, 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF, + /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF, + /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7, 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF, + /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF, + + /* Table 2 (for high byte 0x03) */ + + /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, + /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, + /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, + /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, + /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, + /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, + /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, + /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F, + /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F, + /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF, + /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, + /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7, 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF, + /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7, 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, + + /* Table 3 (for high byte 0x04) */ + + /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407, 0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F, + /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F, + /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F, + /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477, 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F, + /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, + /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F, + /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7, 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF, + /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7, 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF, + /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8, 0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF, + /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7, 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF, + /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7, 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF, + /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7, 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, + + /* Table 4 (for high byte 0x05) */ + + /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F, + /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F, + /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F, + /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F, + /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F, + /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F, + /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, + /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7, 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF, + /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF, + /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7, 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF, + + /* Table 5 (for high byte 0x10) */ + + /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, + /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F, + /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F, + /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F, + /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, + /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F, + /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F, + /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F, + /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F, + /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F, + /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7, 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF, + /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7, 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF, + + /* Table 6 (for high byte 0x20) */ + + /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000, + /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F, + /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027, 0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F, + /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037, 0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F, + /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F, + /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F, + /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067, 0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F, + /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F, + /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F, + /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7, 0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF, + /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7, 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF, + /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7, 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF, + /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF, + /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7, 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF, + /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7, 0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF, + + /* Table 7 (for high byte 0x21) */ + + /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F, + /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F, + /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F, + /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F, + /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F, + /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F, + /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, + /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, + /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187, 0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F, + /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F, + /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7, 0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF, + /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7, 0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF, + /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7, 0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF, + /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7, 0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF, + /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7, 0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF, + /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7, 0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF, + + /* Table 8 (for high byte 0xFE) */ + + /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07, 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, + /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17, 0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F, + /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27, 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F, + /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37, 0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F, + /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47, 0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F, + /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57, 0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F, + /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67, 0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F, + /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77, 0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F, + /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87, 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F, + /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97, 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F, + /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF, + /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF, + /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF, + /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7, 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF, + /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF, + /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000, + + /* Table 9 (for high byte 0xFF) */ + + /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07, 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, + /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F, + /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F, + /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F, + /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, + /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F, + /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F, + /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F, + /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7, 0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF, + /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7, 0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF, + /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7, 0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF, + /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7, 0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF, + /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7, 0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF, + /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7, 0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, +}; +#endif + +static const fsw_u16 fsw_latin_case_fold[] = +{ + /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF, + /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, +}; + + +fsw_u16 fsw_to_lower(fsw_u16 ch) +{ +#if 0 + fsw_u16 temp = temp; +#endif + + if (ch < 0x0100) + return fsw_latin_case_fold[ch]; +#if 0 + /* + * Uncomment this along with above huge table (fsw_lower_case_table) + * for full UTF-16 case insensitivity + */ + temp = fsw_lower_case_table[ch>>8]; + if (temp != 0) + return fsw_lower_case_table[temp + (ch & 0x00FF)]; +#endif + + return ch; +} + +/** + * Compare two strings for equality. The two strings are compared, taking their + * encoding into account. If they are considered equal, boolean true is returned. + * Otherwise, boolean false is returned. + */ + +int fsw_streq(struct fsw_string *s1, struct fsw_string *s2) +{ + struct fsw_string temp_s; + + // handle empty strings + if (s1->type == FSW_STRING_TYPE_EMPTY) { + temp_s.type = FSW_STRING_TYPE_ISO88591; + temp_s.size = temp_s.len = 0; + temp_s.data = NULL; + return fsw_streq(&temp_s, s2); + } + if (s2->type == FSW_STRING_TYPE_EMPTY) { + temp_s.type = FSW_STRING_TYPE_ISO88591; + temp_s.size = temp_s.len = 0; + temp_s.data = NULL; + return fsw_streq(s1, &temp_s); + } + + // check length (count of chars) + if (s1->len != s2->len) + return 0; + if (s1->len == 0) // both strings are empty + return 1; + + if (s1->type == s2->type) { + // same type, do a dumb memory compare + if (s1->size != s2->size) + return 0; + return fsw_memeq(s1->data, s2->data, s1->size); + } + + // dispatch to type-specific functions + #define STREQ_DISPATCH(type1, type2) \ + if (s1->type == FSW_STRING_TYPE_##type1 && s2->type == FSW_STRING_TYPE_##type2) \ + return fsw_streq_##type1##_##type2(s1->data, s2->data, s1->len); \ + if (s2->type == FSW_STRING_TYPE_##type1 && s1->type == FSW_STRING_TYPE_##type2) \ + return fsw_streq_##type1##_##type2(s2->data, s1->data, s1->len); + STREQ_DISPATCH(ISO88591, UTF8); + STREQ_DISPATCH(ISO88591, UTF16); + STREQ_DISPATCH(ISO88591, UTF16_SWAPPED); + STREQ_DISPATCH(UTF8, UTF16); + STREQ_DISPATCH(UTF8, UTF16_SWAPPED); + STREQ_DISPATCH(UTF16, UTF16_SWAPPED); + + // final fallback + return 0; +} + +/** + * Compare a string with a C string constant. This sets up a string descriptor + * for the string constant (second argument) and runs fsw_streq on the two + * strings. Currently the C string is interpreted as ISO 8859-1. + * Returns boolean true if the strings are considered equal, boolean false otherwise. + */ + +int fsw_streq_cstr(struct fsw_string *s1, const char *s2) +{ + struct fsw_string temp_s; + int i; + + for (i = 0; s2[i]; i++) + ; + + temp_s.type = FSW_STRING_TYPE_ISO88591; + temp_s.size = temp_s.len = i; + temp_s.data = (char *)s2; + + return fsw_streq(s1, &temp_s); +} + +/** + * Creates a duplicate of a string, converting it to the given encoding during the copy. + * If the function returns FSW_SUCCESS, the caller must free the string later with + * fsw_strfree. + */ + +fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src) +{ + fsw_status_t status; + + if (src->type == FSW_STRING_TYPE_EMPTY || src->len == 0) { + dest->type = type; + dest->size = dest->len = 0; + dest->data = NULL; + return FSW_SUCCESS; + } + + if (src->type == type) { + dest->type = type; + dest->len = src->len; + dest->size = src->size; + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + fsw_memcpy(dest->data, src->data, dest->size); + return FSW_SUCCESS; + } + + // dispatch to type-specific functions + #define STRCOERCE_DISPATCH(type1, type2) \ + if (src->type == FSW_STRING_TYPE_##type1 && type == FSW_STRING_TYPE_##type2) \ + return fsw_strcoerce_##type1##_##type2(src->data, src->len, dest); + STRCOERCE_DISPATCH(UTF8, ISO88591); + STRCOERCE_DISPATCH(UTF16, ISO88591); + STRCOERCE_DISPATCH(UTF16_SWAPPED, ISO88591); + STRCOERCE_DISPATCH(ISO88591, UTF8); + STRCOERCE_DISPATCH(UTF16, UTF8); + STRCOERCE_DISPATCH(UTF16_SWAPPED, UTF8); + STRCOERCE_DISPATCH(ISO88591, UTF16); + STRCOERCE_DISPATCH(UTF8, UTF16); + STRCOERCE_DISPATCH(UTF16_SWAPPED, UTF16); + + return FSW_UNSUPPORTED; +} + +/** + * Splits a string at the first occurrence of the separator character. + * The buffer string is searched for the separator character. If it is found, the + * element string descriptor is filled to point at the part of the buffer string + * before the separator. The buffer string itself is adjusted to point at the + * remaining part of the string (without the separator). + * + * If the separator is not found in the buffer string, then element is changed to + * point at the whole buffer string, and the buffer string itself is changed into + * an empty string. + * + * This function only manipulates the pointers and lengths in the two string descriptors, + * it does not change the actual string. If the buffer string is dynamically allocated, + * you must make a copy of it so that you can release it later. + */ + +void fsw_strsplit(struct fsw_string *element, struct fsw_string *buffer, char separator) +{ + int i, maxlen; + + if (buffer->type == FSW_STRING_TYPE_EMPTY || buffer->len == 0) { + element->type = FSW_STRING_TYPE_EMPTY; + return; + } + + maxlen = buffer->len; + *element = *buffer; + + if (buffer->type == FSW_STRING_TYPE_ISO88591) { + fsw_u8 *p; + + p = (fsw_u8 *)element->data; + for (i = 0; i < maxlen; i++, p++) { + if (*p == separator) { + buffer->data = p + 1; + buffer->len -= i + 1; + break; + } + } + element->len = i; + if (i == maxlen) { + buffer->data = p; + buffer->len -= i; + } + + element->size = element->len; + buffer->size = buffer->len; + + } else if (buffer->type == FSW_STRING_TYPE_UTF16) { + fsw_u16 *p; + + p = (fsw_u16 *)element->data; + for (i = 0; i < maxlen; i++, p++) { + if (*p == separator) { + buffer->data = p + 1; + buffer->len -= i + 1; + break; + } + } + element->len = i; + if (i == maxlen) { + buffer->data = p; + buffer->len -= i; + } + + element->size = element->len * sizeof(fsw_u16); + buffer->size = buffer->len * sizeof(fsw_u16); + + } else { + // fallback + buffer->type = FSW_STRING_TYPE_EMPTY; + } + + /// @todo support UTF8 and UTF16_SWAPPED +} + +/** + * Frees the memory used by a string returned from fsw_strdup_coerce. + */ + +void fsw_strfree(struct fsw_string *s) +{ + if (s->type != FSW_STRING_TYPE_EMPTY && s->data) + fsw_free(s->data); + s->type = FSW_STRING_TYPE_EMPTY; +} + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_strfunc.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_strfunc.h new file mode 100644 index 00000000..22e7b120 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_strfunc.h @@ -0,0 +1,491 @@ +/* $Id: fsw_strfunc.h $ */ +/** @file + * fsw_strfunc.h + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* fsw_strfunc.h generated by mk_fsw_strfunc.py */ + +static int fsw_streq_ISO88591_UTF8(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u8 *p2 = (fsw_u8 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + c2 = *p2++; + if ((c2 & 0xe0) == 0xc0) { + c2 = ((c2 & 0x1f) << 6) | (*p2++ & 0x3f); + } else if ((c2 & 0xf0) == 0xe0) { + c2 = ((c2 & 0x0f) << 12) | ((*p2++ & 0x3f) << 6); + c2 |= (*p2++ & 0x3f); + } else if ((c2 & 0xf8) == 0xf0) { + c2 = ((c2 & 0x07) << 18) | ((*p2++ & 0x3f) << 12); + c2 |= ((*p2++ & 0x3f) << 6); + c2 |= (*p2++ & 0x3f); + } + if (c1 != c2) + return 0; + } + return 1; +} + +#ifndef HOST_EFI +static int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + c2 = *p2++; + if (c1 != c2) + return 0; + } + return 1; +} +#endif + +static int fsw_streq_ISO88591_UTF16_SWAPPED(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2); + if (c1 != c2) + return 0; + } + return 1; +} + +static int fsw_streq_UTF8_UTF16(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + if ((c1 & 0xe0) == 0xc0) { + c1 = ((c1 & 0x1f) << 6) | (*p1++ & 0x3f); + } else if ((c1 & 0xf0) == 0xe0) { + c1 = ((c1 & 0x0f) << 12) | ((*p1++ & 0x3f) << 6); + c1 |= (*p1++ & 0x3f); + } else if ((c1 & 0xf8) == 0xf0) { + c1 = ((c1 & 0x07) << 18) | ((*p1++ & 0x3f) << 12); + c1 |= ((*p1++ & 0x3f) << 6); + c1 |= (*p1++ & 0x3f); + } + c2 = *p2++; + if (c1 != c2) + return 0; + } + return 1; +} + +static int fsw_streq_UTF8_UTF16_SWAPPED(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + if ((c1 & 0xe0) == 0xc0) { + c1 = ((c1 & 0x1f) << 6) | (*p1++ & 0x3f); + } else if ((c1 & 0xf0) == 0xe0) { + c1 = ((c1 & 0x0f) << 12) | ((*p1++ & 0x3f) << 6); + c1 |= (*p1++ & 0x3f); + } else if ((c1 & 0xf8) == 0xf0) { + c1 = ((c1 & 0x07) << 18) | ((*p1++ & 0x3f) << 12); + c1 |= ((*p1++ & 0x3f) << 6); + c1 |= (*p1++ & 0x3f); + } + c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2); + if (c1 != c2) + return 0; + } + return 1; +} + +static int fsw_streq_UTF16_UTF16_SWAPPED(void *s1data, void *s2data, int len) +{ + int i; + fsw_u16 *p1 = (fsw_u16 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2); + if (c1 != c2) + return 0; + } + return 1; +} + +static fsw_status_t fsw_strcoerce_UTF8_ISO88591(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u8 *sp; + fsw_u8 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_ISO88591; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u8); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u8 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + if ((c & 0xe0) == 0xc0) { + c = ((c & 0x1f) << 6) | (*sp++ & 0x3f); + } else if ((c & 0xf0) == 0xe0) { + c = ((c & 0x0f) << 12) | ((*sp++ & 0x3f) << 6); + c |= (*sp++ & 0x3f); + } else if ((c & 0xf8) == 0xf0) { + c = ((c & 0x07) << 18) | ((*sp++ & 0x3f) << 12); + c |= ((*sp++ & 0x3f) << 6); + c |= (*sp++ & 0x3f); + } + *dp++ = (fsw_u8)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_ISO88591(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u16 *sp; + fsw_u8 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_ISO88591; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u8); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + *dp++ = (fsw_u8)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_ISO88591(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u16 *sp; + fsw_u8 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_ISO88591; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u8); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; c = FSW_SWAPVALUE_U16(c); + *dp++ = (fsw_u8)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_ISO88591_UTF16(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u8 *sp; + fsw_u16 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_UTF16; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u16); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u8 *)srcdata; + dp = (fsw_u16 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + *dp++ = (fsw_u16)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF8_UTF16(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u8 *sp; + fsw_u16 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_UTF16; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u16); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u8 *)srcdata; + dp = (fsw_u16 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + if ((c & 0xe0) == 0xc0) { + c = ((c & 0x1f) << 6) | (*sp++ & 0x3f); + } else if ((c & 0xf0) == 0xe0) { + c = ((c & 0x0f) << 12) | ((*sp++ & 0x3f) << 6); + c |= (*sp++ & 0x3f); + } else if ((c & 0xf8) == 0xf0) { + c = ((c & 0x07) << 18) | ((*sp++ & 0x3f) << 12); + c |= ((*sp++ & 0x3f) << 6); + c |= (*sp++ & 0x3f); + } + *dp++ = (fsw_u16)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_UTF16(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u16 *sp; + fsw_u16 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_UTF16; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u16); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u16 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; c = FSW_SWAPVALUE_U16(c); + *dp++ = (fsw_u16)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_ISO88591_UTF8(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i, destsize; + fsw_u8 *sp; + fsw_u8 *dp; + fsw_u32 c; + + sp = (fsw_u8 *)srcdata; + destsize = 0; + for (i = 0; i < srclen; i++) { + c = *sp++; + + if (c < 0x000080) + destsize++; + else if (c < 0x000800) + destsize += 2; + else if (c < 0x010000) + destsize += 3; + else + destsize += 4; + } + + dest->type = FSW_STRING_TYPE_UTF8; + dest->len = srclen; + dest->size = destsize; + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u8 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + + if (c < 0x000080) { + *dp++ = (fsw_u8)c; + } else if (c < 0x000800) { + *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else if (c < 0x010000) { + *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else { + *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07)); + *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_UTF8(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i, destsize; + fsw_u16 *sp; + fsw_u8 *dp; + fsw_u32 c; + + sp = (fsw_u16 *)srcdata; + destsize = 0; + for (i = 0; i < srclen; i++) { + c = *sp++; + + if (c < 0x000080) + destsize++; + else if (c < 0x000800) + destsize += 2; + else if (c < 0x010000) + destsize += 3; + else + destsize += 4; + } + + dest->type = FSW_STRING_TYPE_UTF8; + dest->len = srclen; + dest->size = destsize; + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + + if (c < 0x000080) { + *dp++ = (fsw_u8)c; + } else if (c < 0x000800) { + *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else if (c < 0x010000) { + *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else { + *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07)); + *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_UTF8(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i, destsize; + fsw_u16 *sp; + fsw_u8 *dp; + fsw_u32 c; + + sp = (fsw_u16 *)srcdata; + destsize = 0; + for (i = 0; i < srclen; i++) { + c = *sp++; c = FSW_SWAPVALUE_U16(c); + + if (c < 0x000080) + destsize++; + else if (c < 0x000800) + destsize += 2; + else if (c < 0x010000) + destsize += 3; + else + destsize += 4; + } + + dest->type = FSW_STRING_TYPE_UTF8; + dest->len = srclen; + dest->size = destsize; + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; c = FSW_SWAPVALUE_U16(c); + + if (c < 0x000080) { + *dp++ = (fsw_u8)c; + } else if (c < 0x000800) { + *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else if (c < 0x010000) { + *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else { + *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07)); + *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } + } + return FSW_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/Makefile b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/Makefile new file mode 100644 index 00000000..376bbba7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/Makefile @@ -0,0 +1,32 @@ + +FS=hfs + +SOURCES += ../fsw_core.c +SOURCES += ../fsw_$(FS).c +SOURCES += ../fsw_lib.c +SOURCES += fsw_posix.c + +OBJ = ${foreach src,$(SOURCES),$(addsuffix .o, $(basename $(src)))} + +#lslr.c +#lsroot.c +CPPFLAGS += -DHOST_POSIX +CPPFLAGS += -DFSTYPE=$(FS) +ASFLAGS += -include asm.h +CPPFLAGS += -I.. +CPPFLAGS += -I. +CFLAGS += -g -gdwarf-2 +CFLAGS += -m32 + +.PHONY: all clear +all:lslr lsroot +clean: + $(RM) $(OBJ) lslr lsroot lslr.o lsroot.o + +lslr: OBJ += lslr.o +lslr: $(OBJ) + +lsroot: OBJ += lsroot.o +lsroot: $(OBJ) + + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/README b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/README new file mode 100644 index 00000000..c7c34e4d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/README @@ -0,0 +1,2 @@ +This folder contains tests for VBoxFsDxe module, allowing up +and test filesystems without EFI environment and launching whole VBox. diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.c new file mode 100644 index 00000000..6e8255c6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.c @@ -0,0 +1,483 @@ +/** + * \file fsw_posix.c + * POSIX user space host environment code. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +#ifndef FSTYPE +/** The file system type name to use. */ +#define FSTYPE ext2 +#endif + + +// function prototypes + +fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type, + struct fsw_shandle *shand); + +void fsw_posix_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize); +fsw_status_t fsw_posix_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer); + +/** + * Dispatch table for our FSW host driver. + */ + +struct fsw_host_table fsw_posix_host_table = { + FSW_STRING_TYPE_ISO88591, + + fsw_posix_change_blocksize, + fsw_posix_read_block +}; + +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE); + + +/** + * Mount function. + */ + +struct fsw_posix_volume * fsw_posix_mount(const char *path, struct fsw_fstype_table *fstype_table) +{ + fsw_status_t status; + struct fsw_posix_volume *pvol; + + // allocate volume structure + status = fsw_alloc_zero(sizeof(struct fsw_posix_volume), (void **)&pvol); + if (status) + return NULL; + pvol->fd = -1; + + // open underlying file/device + pvol->fd = open(path, O_RDONLY | O_BINARY, 0); + if (pvol->fd < 0) { + fprintf(stderr, "fsw_posix_mount: %s: %s\n", path, strerror(errno)); + fsw_free(pvol); + return NULL; + } + + // mount the filesystem + if (fstype_table == NULL) + fstype_table = &FSW_FSTYPE_TABLE_NAME(FSTYPE); + status = fsw_mount(pvol, &fsw_posix_host_table, fstype_table, &pvol->vol); + if (status) { + fprintf(stderr, "fsw_posix_mount: fsw_mount returned %d\n", status); + fsw_free(pvol); + return NULL; + } + + return pvol; +} + +/** + * Unmount function. + */ + +int fsw_posix_unmount(struct fsw_posix_volume *pvol) +{ + if (pvol->vol != NULL) + fsw_unmount(pvol->vol); + fsw_free(pvol); + return 0; +} + +/** + * Open a named regular file. + */ + +struct fsw_posix_file * fsw_posix_open(struct fsw_posix_volume *pvol, const char *path, int flags, mode_t mode) +{ + fsw_status_t status; + struct fsw_posix_file *file; + + // TODO: check flags for unwanted values + + // allocate file structure + status = fsw_alloc(sizeof(struct fsw_posix_file), &file); + if (status) + return NULL; + file->pvol = pvol; + + // open the file + status = fsw_posix_open_dno(pvol, path, FSW_DNODE_TYPE_FILE, &file->shand); + if (status) { + fprintf(stderr, "fsw_posix_open: open_dno returned %d\n", status); + fsw_free(file); + return NULL; + } + + return file; +} + +/** + * Read data from a regular file. + */ + +ssize_t fsw_posix_read(struct fsw_posix_file *file, void *buf, size_t nbytes) +{ + fsw_status_t status; + fsw_u32 buffer_size; + + buffer_size = nbytes; + status = fsw_shandle_read(&file->shand, &buffer_size, buf); + if (status) + return -1; + return buffer_size; +} + +/** + * Change position within a regular file. + */ + +off_t fsw_posix_lseek(struct fsw_posix_file *file, off_t offset, int whence) +{ + fsw_u64 base_offset = 0; + + // get base offset + base_offset = 0; + if (whence == SEEK_CUR) + base_offset = file->shand.pos; + else if (whence == SEEK_END) + base_offset = file->shand.dnode->size; + + // calculate new offset, prevent seeks before the start of the file + if (offset < 0 && -offset > base_offset) + file->shand.pos = 0; + else + file->shand.pos = base_offset + offset; + + return file->shand.pos; +} + +/** + * Close a regular file. + */ + +int fsw_posix_close(struct fsw_posix_file *file) +{ + fsw_shandle_close(&file->shand); + fsw_free(file); + return 0; +} + +/** + * Open a directory for iteration. + */ + +struct fsw_posix_dir * fsw_posix_opendir(struct fsw_posix_volume *pvol, const char *path) +{ + fsw_status_t status; + struct fsw_posix_dir *dir; + + // allocate file structure + status = fsw_alloc(sizeof(struct fsw_posix_dir), &dir); + if (status) + return NULL; + dir->pvol = pvol; + + // open the directory + status = fsw_posix_open_dno(pvol, path, FSW_DNODE_TYPE_DIR, &dir->shand); + if (status) { + fprintf(stderr, "fsw_posix_opendir: open_dno returned %d\n", status); + fsw_free(dir); + return NULL; + } + + return dir; +} + +/** + * Read the next entry from a directory. + */ + +struct fsw_posix_dirent * fsw_posix_readdir(struct fsw_posix_dir *dir) +{ + fsw_status_t status; + struct fsw_dnode *dno; + static struct fsw_posix_dirent dent; + + // get next entry from file system + status = fsw_dnode_dir_read(&dir->shand, &dno); + if (status) { + if (status != 4) + fprintf(stderr, "fsw_posix_readdir: fsw_dnode_dir_read returned %d\n", status); + return NULL; + } + status = fsw_dnode_fill(dno); + if (status) { + fprintf(stderr, "fsw_posix_readdir: fsw_dnode_fill returned %d\n", status); + fsw_dnode_release(dno); + return NULL; + } + + // fill dirent structure + dent.d_fileno = dno->dnode_id; + //dent.d_reclen = 8 + dno->name.size + 1; + switch (dno->type) { + case FSW_DNODE_TYPE_FILE: + dent.d_type = DT_REG; + break; + case FSW_DNODE_TYPE_DIR: + dent.d_type = DT_DIR; + break; + case FSW_DNODE_TYPE_SYMLINK: + dent.d_type = DT_LNK; + break; + default: + dent.d_type = DT_UNKNOWN; + break; + } +#if 0 + dent.d_namlen = dno->name.size; +#endif + memcpy(dent.d_name, dno->name.data, dno->name.size); + dent.d_name[dno->name.size] = 0; + + return &dent; +} + +/** + * Rewind a directory to the start. + */ + +void fsw_posix_rewinddir(struct fsw_posix_dir *dir) +{ + dir->shand.pos = 0; +} + +/** + * Close a directory. + */ + +int fsw_posix_closedir(struct fsw_posix_dir *dir) +{ + fsw_shandle_close(&dir->shand); + fsw_free(dir); + return 0; +} + +/** + * Open a shand of a required type by path. + */ + +fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type, struct fsw_shandle *shand) +{ + fsw_status_t status; + struct fsw_dnode *dno; + struct fsw_dnode *target_dno; + struct fsw_string lookup_path; + + lookup_path.type = FSW_STRING_TYPE_ISO88591; + lookup_path.len = strlen(path); + lookup_path.size = lookup_path.len; + lookup_path.data = (void *)path; + + // resolve the path (symlinks along the way are automatically resolved) + status = fsw_dnode_lookup_path(pvol->vol->root, &lookup_path, '/', &dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_lookup_path returned %d\n", status); + return status; + } + + // if the final node is a symlink, also resolve it + status = fsw_dnode_resolve(dno, &target_dno); + fsw_dnode_release(dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_resolve returned %d\n", status); + return status; + } + dno = target_dno; + + // check that it is a regular file + status = fsw_dnode_fill(dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_fill returned %d\n", status); + fsw_dnode_release(dno); + return status; + } + if (dno->type != required_type) { + fprintf(stderr, "fsw_posix_open_dno: dnode is not of the requested type\n"); + fsw_dnode_release(dno); + return FSW_UNSUPPORTED; + } + + // open shandle + status = fsw_shandle_open(dno, shand); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_shandle_open returned %d\n", status); + } + fsw_dnode_release(dno); + return status; +} + +/** + * FSW interface function for block size changes. This function is called by the FSW core + * when the file system driver changes the block sizes for the volume. + */ + +void fsw_posix_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize) +{ + // nothing to do +} + +/** + * FSW interface function to read data blocks. This function is called by the FSW core + * to read a block of data from the device. The buffer is allocated by the core code. + */ + +fsw_status_t fsw_posix_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer) +{ + struct fsw_posix_volume *pvol = (struct fsw_posix_volume *)vol->host_data; + off_t block_offset, seek_result; + ssize_t read_result; + + FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_posix_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize)); + + // read from disk + block_offset = (off_t)phys_bno * vol->phys_blocksize; + seek_result = lseek(pvol->fd, block_offset, SEEK_SET); + if (seek_result != block_offset) { + fprintf(stderr, "fsw_posix_read_block: failed to seek to block %u (offset %u)\n", phys_bno, block_offset); + return FSW_IO_ERROR; + } + read_result = read(pvol->fd, buffer, vol->phys_blocksize); + if (read_result != vol->phys_blocksize) { + fprintf(stderr, "fsw_posix_read_block: failed to read %u bytes at %u\n", vol->phys_blocksize, block_offset); + return FSW_IO_ERROR; + } + + return FSW_SUCCESS; +} + + +/** + * Time mapping callback for the fsw_dnode_stat call. This function converts + * a Posix style timestamp into an EFI_TIME structure and writes it to the + * appropriate member of the EFI_FILE_INFO structure that we're filling. + */ + +/* +static void fsw_posix_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if (which == FSW_DNODE_STAT_CTIME) + fsw_posix_decode_time(&FileInfo->CreateTime, posix_time); + else if (which == FSW_DNODE_STAT_MTIME) + fsw_posix_decode_time(&FileInfo->ModificationTime, posix_time); + else if (which == FSW_DNODE_STAT_ATIME) + fsw_posix_decode_time(&FileInfo->LastAccessTime, posix_time); +} +*/ + +/** + * Mode mapping callback for the fsw_dnode_stat call. This function looks at + * the Posix mode passed by the file system driver and makes appropriate + * adjustments to the EFI_FILE_INFO structure that we're filling. + */ + +/* +static void fsw_posix_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if ((posix_mode & S_IWUSR) == 0) + FileInfo->Attribute |= EFI_FILE_READ_ONLY; +} +*/ + +/** + * Common function to fill an EFI_FILE_INFO with information about a dnode. + */ + +/* +EFI_STATUS fsw_posix_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, + IN struct fsw_dnode *dno, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + EFI_FILE_INFO *FileInfo; + UINTN RequiredSize; + struct fsw_dnode_stat sb; + + // make sure the dnode has complete info + Status = fsw_posix_map_status(fsw_dnode_fill(dno), Volume); + if (EFI_ERROR(Status)) + return Status; + + // TODO: check/assert that the dno's name is in UTF16 + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_posix_strsize(&dno->name); + if (*BufferSize < RequiredSize) { + // TODO: wind back the directory in this case + + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // fill structure + ZeroMem(Buffer, RequiredSize); + FileInfo = (EFI_FILE_INFO *)Buffer; + FileInfo->Size = RequiredSize; + FileInfo->FileSize = dno->size; + FileInfo->Attribute = 0; + if (dno->type == FSW_DNODE_TYPE_DIR) + FileInfo->Attribute |= EFI_FILE_DIRECTORY; + fsw_posix_strcpy(FileInfo->FileName, &dno->name); + + // get the missing info from the fs driver + ZeroMem(&sb, sizeof(struct fsw_dnode_stat)); + sb.store_time_posix = fsw_posix_store_time_posix; + sb.store_attr_posix = fsw_posix_store_attr_posix; + sb.host_data = FileInfo; + Status = fsw_posix_map_status(fsw_dnode_stat(dno, &sb), Volume); + if (EFI_ERROR(Status)) + return Status; + FileInfo->PhysicalSize = sb.used_bytes; + + // prepare for return + *BufferSize = RequiredSize; + return EFI_SUCCESS; +} +*/ + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.h new file mode 100644 index 00000000..4df2bbca --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.h @@ -0,0 +1,120 @@ +/** + * \file fsw_posix.h + * POSIX user space host environment header. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_POSIX_H_ +#define _FSW_POSIX_H_ + +#include "fsw_core.h" + +#include <fcntl.h> +#include <sys/types.h> + + +/** + * POSIX Host: Private per-volume structure. + */ + +struct fsw_posix_volume { + struct fsw_volume *vol; //!< FSW volume structure + + int fd; //!< System file descriptor for data access + +}; + +/** + * POSIX Host: Private structure for an open file. + */ + +struct fsw_posix_file { + struct fsw_posix_volume *pvol; //!< POSIX host volume structure + + struct fsw_shandle shand; //!< FSW handle for this file + +}; + +/** + * POSIX Host: Private structure for an open directory. + */ + +struct fsw_posix_dir { + struct fsw_posix_volume *pvol; //!< POSIX host volume structure + + struct fsw_shandle shand; //!< FSW handle for this file + +}; + + +#define NAME_MAX 4096 + +#define DT_UNKNOWN 'u' +#define DT_REG 'r' +#define DT_DIR 'd' +#define DT_LNK 'l' + + +/** + * POSIX Host: Private structure for an open directory. + */ + +struct fsw_posix_dirent { + char d_attr; /* file's attribute */ + unsigned d_type; + unsigned short int d_time; /* file's time */ + unsigned short int d_date; /* file's date */ + long d_size; /* file's size */ + char d_name[NAME_MAX+1]; /* file's name */ + unsigned d_fileno; /* file number/inode */ +}; +typedef struct fsw_posix_dirent DIR; + +/* functions */ + +struct fsw_posix_volume * fsw_posix_mount(const char *path, struct fsw_fstype_table *fstype_table); +int fsw_posix_unmount(struct fsw_posix_volume *pvol); + +struct fsw_posix_file * fsw_posix_open(struct fsw_posix_volume *pvol, const char *path, int flags, mode_t mode); +ssize_t fsw_posix_read(struct fsw_posix_file *file, void *buf, size_t nbytes); +off_t fsw_posix_lseek(struct fsw_posix_file *file, off_t offset, int whence); +int fsw_posix_close(struct fsw_posix_file *file); + +struct fsw_posix_dir * fsw_posix_opendir(struct fsw_posix_volume *pvol, const char *path); +struct fsw_posix_dirent * fsw_posix_readdir(struct fsw_posix_dir *dir); +void fsw_posix_rewinddir(struct fsw_posix_dir *dir); +int fsw_posix_closedir(struct fsw_posix_dir *dir); + + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix_base.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix_base.h new file mode 100644 index 00000000..114e21fd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix_base.h @@ -0,0 +1,90 @@ +/** + * \file fsw_posix_base.h + * Base definitions for the POSIX user space host environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_POSIX_BASE_H_ +#define _FSW_POSIX_BASE_H_ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> + +#define FSW_LITTLE_ENDIAN (1) +// TODO: use info from the headers to define FSW_LITTLE_ENDIAN or FSW_BIG_ENDIAN + + +// types + +typedef int8_t fsw_s8; +typedef uint8_t fsw_u8; +typedef int16_t fsw_s16; +typedef uint16_t fsw_u16; +typedef int32_t fsw_s32; +typedef uint32_t fsw_u32; +typedef int64_t fsw_s64; +typedef uint64_t fsw_u64; + + +// allocation functions + +#define fsw_alloc(size, ptrptr) (((*(ptrptr) = malloc(size)) == NULL) ? FSW_OUT_OF_MEMORY : FSW_SUCCESS) +#define fsw_free(ptr) free(ptr) + +// memory functions + +#define fsw_memzero(dest,size) memset(dest,0,size) +#define fsw_memcpy(dest,src,size) memcpy(dest,src,size) +#define fsw_memeq(p1,p2,size) (memcmp(p1,p2,size) == 0) + +// message printing + +#define FSW_MSGSTR(s) s +#define FSW_MSGFUNC printf + +// 64-bit hooks + +#define FSW_U64_SHR(val,shiftbits) ((val) >> (shiftbits)) +#define FSW_U64_DIV(val,divisor) ((val) / (divisor)) +#define DEBUG(x) + +#define RShiftU64(val, shift) ((val) >> (shift)) +#define LShiftU64(val, shift) ((val) << (shift)) + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lslr.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lslr.c new file mode 100644 index 00000000..19442916 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lslr.c @@ -0,0 +1,140 @@ +/** + * \file lslr.c + * Test program for the POSIX user space environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +//extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2); +//extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE); + +static struct fsw_fstype_table *fstypes[] = { + //&FSW_FSTYPE_TABLE_NAME(ext2), + //&FSW_FSTYPE_TABLE_NAME(reiserfs), + &FSW_FSTYPE_TABLE_NAME(FSTYPE ), + NULL +}; + +static int listdir(struct fsw_posix_volume *vol, char *path, int level) +{ + struct fsw_posix_dir *dir; + struct fsw_posix_dirent *dent; + int i; + char subpath[4096]; + + dir = fsw_posix_opendir(vol, path); + if (dir == NULL) { + printf("opendir(%s) call failed.\n", path); + return 1; + } + while ((dent = fsw_posix_readdir(dir)) != NULL) { + for (i = 0; i < level*2; i++) + fputc(' ', stdout); + printf("%c %s\n", dent->d_type, dent->d_name); + + if (dent->d_type == DT_DIR) { + snprintf(subpath, 4095, "%s%s/", path, dent->d_name); + listdir(vol, subpath, level + 1); + } + } + fsw_posix_closedir(dir); + + return 0; +} + +static int catfile(struct fsw_posix_volume *vol, char *path) +{ + struct fsw_posix_file *file; + int r; + char buf[256]; + + file = fsw_posix_open(vol, path, 0, 0); + if (file == NULL) { + printf("open(%s) call failed.\n", path); + return 1; + } + while ((r=fsw_posix_read(file, buf, sizeof(buf))) > 0) + { + int i; + for (i=0; i<r; i++) + { + printf("%c", buf[i]); + } + } + fsw_posix_close(file); + + return 0; +} + +extern fsw_status_t fsw_hfs_get_blessed_file(struct fsw_hfs_volume *vol, struct fsw_string *link_target); + +int main(int argc, char **argv) +{ + struct fsw_posix_volume *vol; + struct fsw_string blessed; + int i; + + if (argc != 2) { + printf("Usage: lslr <file/device>\n"); + return 1; + } + + for (i = 0; fstypes[i]; i++) { + vol = fsw_posix_mount(argv[1], fstypes[i]); + if (vol != NULL) { + printf("Mounted as '%s'.\n", fstypes[i]->name.data); + break; + } + } + if (vol == NULL) { + printf("Mounting failed.\n"); + return 1; + } + + listdir(vol, "/System/Library/CoreServices", 0); + //listdir(vol, "/System/Library/Extensions/AppleACPIPlatform.kext/", 0); + //listdir(vol, "/System/Library/Extensions/", 0); + catfile(vol, "/System/Library/CoreServices/SystemVersion.plist"); + //listdir(vol, "/", 0); + fsw_hfs_get_blessed_file((struct fsw_hfs_volume *)vol->vol, &blessed); + + fsw_posix_unmount(vol); + + return 0; +} + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lsroot.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lsroot.c new file mode 100644 index 00000000..741ae4c2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lsroot.c @@ -0,0 +1,79 @@ +/** + * \file lsroot.c + * Example program for the POSIX user space environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(iso9660); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfs); + +int main(int argc, char **argv) +{ + struct fsw_posix_volume *vol; + struct fsw_posix_dir *dir; + struct fsw_posix_dirent *dent; + + if (argc != 3) { + printf("Usage: lsroot <file/device> <directory>\n"); + return 1; + } + + //vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(ext2)); + //vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(reiserfs)); + vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(FSTYPE)); + if (vol == NULL) { + printf("Mounting failed.\n"); + return 1; + } + //dir = fsw_posix_opendir(vol, "/drivers/net/"); + dir = fsw_posix_opendir(vol, argv[2]); + if (dir == NULL) { + printf("opendir call failed.\n"); + return 1; + } + while ((dent = fsw_posix_readdir(dir)) != NULL) { + printf("%c %s %u\n", dent->d_type, dent->d_name, dent->d_fileno); + } + fsw_posix_closedir(dir); + fsw_posix_unmount(vol); + + return 0; +} + +// EOF diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/BootService.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/BootService.c new file mode 100644 index 00000000..42429094 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/BootService.c @@ -0,0 +1,41 @@ +/* $Id: BootService.c $ */ +/** @file + * BootService.c - boot service intercepter. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ +#define SERVICE bs +#define ORIG_SERVICE gBS + +#include "VBoxInterceptor.h" +#define SERVICE_H "boot_service_table.h" +#include "interceptor.h" diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/README b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/README new file mode 100644 index 00000000..4126bc8f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/README @@ -0,0 +1,3 @@ +This module isn't supposed to be used in normal UEFI imulation. +It dumps calls to EFI cores from EFI guests helping in investigation +of EFI guests behaviour and expectations. diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/RunTime.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/RunTime.c new file mode 100644 index 00000000..313dda4d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/RunTime.c @@ -0,0 +1,41 @@ +/* $Id: RunTime.c $ */ +/** @file + * Runtime.c - runtime service intercepter. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ +#define SERVICE rt +#define ORIG_SERVICE gRT + +#include "VBoxInterceptor.h" +#define SERVICE_H "runtime_service_table.h" +#include "interceptor.h" diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptor.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptor.c new file mode 100644 index 00000000..635eb92c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptor.c @@ -0,0 +1,129 @@ +/* $Id: VBoxInterceptor.c $ */ +/** @file + * VBoxIntercepter.c - Entry point. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ +#include "VBoxInterceptor.h" + +#define VBOX_INTERCEPTOR_VAR L"VBOX_INTERCEPTOR" +/*8e7505ec-d103-11de-8dbb-678848bdcb46*/ +static EFI_GUID gVBoxInterceptorVarGuid = { 0x817505ec, 0xd103, 0x11de, {0x8d, 0xbb, 0x67, 0x88, 0x48, 0xbd, 0xcb, 0x46}}; + +static int g_indent = 0; + +static char* getIndent(int count, int enter) +{ + static char buf[64]; + int i; + char ch = enter ? '>' : '<'; + + for (i=0; i<count+1; i++) + buf[i] = ch; + + buf[i++] = ' '; + buf[i] = 0; + + return buf; +} + +char* indentRight() +{ + return getIndent(g_indent++, 1); +} + +char* indentLeft() +{ + return getIndent(--g_indent, 0); +} + + +EFI_STATUS +EFIAPI +VBoxInterceptorInit(EFI_HANDLE hImage, EFI_SYSTEM_TABLE *pSysTable) +{ + /* Set'n'check intercept variable */ + EFI_STATUS r; + UINT32 val; + UINTN size = sizeof(UINT32); + r = gRT->GetVariable(VBOX_INTERCEPTOR_VAR, &gVBoxInterceptorVarGuid, NULL, &size, &val); + if ( EFI_ERROR(r) + && r == EFI_NOT_FOUND) + { + size = sizeof(UINT32); + val = 1; + r = gRT->SetVariable(VBOX_INTERCEPTOR_VAR, &gVBoxInterceptorVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, size, &val); + if (EFI_ERROR(r)) + { + DEBUG((DEBUG_INFO, "%a:%d - %r\n", __FILE__, __LINE__, r)); + return r; + } + /* intercept installation */ + gThis = AllocateZeroPool(sizeof(VBOXINTERCEPTOR)); + r = install_bs_interceptors(); + if(EFI_ERROR(r)) + { + DEBUG((DEBUG_INFO, "%a:%d - %r\n", __FILE__, __LINE__, r)); + return r; + } + + r = install_rt_interceptors(); + if(EFI_ERROR(r)) + { + DEBUG((DEBUG_INFO, "%a:%d - %r\n", __FILE__, __LINE__, r)); + return r; + } + return r; + } + if (!EFI_ERROR(r)) + { + return EFI_ALREADY_STARTED; + } + return r; +} + +EFI_STATUS +EFIAPI +VBoxInterceptorFini(EFI_HANDLE hImage) +{ + EFI_STATUS r; + uninstall_rt_interceptors(); + uninstall_bs_interceptors(); + FreePool(gThis); + r = gRT->SetVariable(VBOX_INTERCEPTOR_VAR, &gVBoxInterceptorVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0, NULL); + if (EFI_ERROR(r)) + { + DEBUG((DEBUG_INFO, "%a:%d - %r\n", __FILE__, __LINE__, r)); + return r; + } + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptor.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptor.h new file mode 100644 index 00000000..4f7ddb29 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptor.h @@ -0,0 +1,152 @@ +/* $Id: VBoxInterceptor.h $ */ +/** @file + * VBoxInterceptor.h - Helpful macrodefinitions used in the interceptor. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef __VBOXINTERCEPTOR_H__ +#define __VBOXINTERCEPTOR_H__ +#include <Uefi.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> +#include "print_types.h" + +#define VBOXINTERCEPTOR VBoxInterceptor +#define XCONCAT(a,b) a##b +#define CONCAT(a,b) XCONCAT(a,b) +#define CONCAT3(a,b,c) a ## b ## c + +#define SCL(type) PRNT_##type, type +#define PTR(type) PRNT_P_##type, type * +#define PTR2(type) PRNT_PP_##type, type ** +#define PTR3(type) PRNT_PP_##type, type *** +#define PTRC(type) PRNT_P_##type, CONST type * + +#define PARAMETER1(ign0, type0) type0 a0 +#define PARAMETER2(ign0, type0, ign1, type1) type0 a0, type1 a1 +#define PARAMETER3(ign0, type0, ign1, type1, ign2, type2) type0 a0, type1 a1, type2 a2 +#define PARAMETER4(ign0, type0, ign1, type1, ign2, type2, ign3, type3) type0 a0, type1 a1, type2 a2, type3 a3 +#define PARAMETER5(ign0, type0, ign1, type1, ign2, type2, ign3, type3, ign4, type4) type0 a0, type1 a1, type2 a2, type3 a3, type4 a4 +#define PARAMETER6(ign0, type0, ign1, type1, ign2, type2, ign3, type3, ign4, type4, ign5, type5) type0 a0, type1 a1, type2 a2, type3 a3, type4 a4, type5 a5 + +#define PARAMETER(x) PARAMETER##x + +#define PRNT_PARAMETER1(f0, ign0) "(" f0 ")" +#define PRNT_PARAMETER2(f0, ign0, f1, ign1) "(" f0 "," f1 ")" +#define PRNT_PARAMETER3(f0, ign0, f1, ign1, f2, ign2) "(" f0 "," f1 "," f2 ")" +#define PRNT_PARAMETER4(f0, ign0, f1, ign1, f2, ign2, f3, ign3) "(" f0 "," f1 "," f2 "," f3 ")" +#define PRNT_PARAMETER5(f0, ign0, f1, ign1, f2, ign2, f3, ign3, f4, ign4) "(" f0 "," f1 "," f2 "," f3 "," f4 ")" +#define PRNT_PARAMETER6(f0, ign0, f1, ign1, f2, ign2, f3, ign3, f4, ign4, f5, ign5) "(" f0 "," f1 "," f2 "," f3 "," f4 "," f5 ")" + +#define PRNT_PARAMETERS(x) PRNT_PARAMETER##x + +#define ARGS1 a0 +#define ARGS2 a0, a1 +#define ARGS3 a0, a1, a2 +#define ARGS4 a0, a1, a2, a3 +#define ARGS5 a0, a1, a2, a3, a4 +#define ARGS6 a0, a1, a2, a3, a4, a5 + +#define ARGS(x) ARGS##x + +char* indentRight(); +char* indentLeft(); + +#if ARCH_BITS == 64 +# define ARCH_FRAME_POINTER "rbp" +#elif ARCH_BITS == 32 +# define ARCH_FRAME_POINTER "ebp" +#else +# error "port me" +#endif + +#define DUMP_STACK(depth) \ +do { \ + int i; \ + UINTN *bp = (UINTN *)frame_pointer; \ + for (i = 0; i < depth; ++i) \ + { \ + DEBUG((DEBUG_INFO, "[%d frame pbp:%x ip: %x]\n", i, bp[0], bp[1])); \ + if (bp == NULL || bp < (UINTN *)0x1000) \ + break; \ + bp = *(UINTN **)bp; \ + } \ +} while(0) + + +register volatile UINTN *frame_pointer asm(ARCH_FRAME_POINTER); + +#define FUNCTION(RET_TYPE) RET_TYPE ## _FUNCTION +#define RVOID_FUNCTION(return_type, func_name, nparams, params) \ +static void EFIAPI CONCAT(VBOXINTERCEPTOR,func_name)(PARAMETER(nparams)params) \ +{ \ + UINT32 off = (UINT32)(uintptr_t)&(((typeof(gThis))0)->CONCAT(SERVICE,Orig).func_name); \ + DEBUG((DEBUG_INFO, "%a%a[%x] enter " PRNT_PARAMETERS(nparams)params "\n", \ + indentRight(), #func_name, off, ARGS(nparams))); \ + DUMP_STACK(2); \ + gThis->CONCAT(SERVICE,Orig).func_name(ARGS(nparams)); \ + DEBUG((DEBUG_INFO, "%a%a exit \n", indentLeft(), #func_name)); \ +} + +/*XXX: Assume atm that Bs and Rt if func returns smth, this smt is EFI_STATUS */ +#define NVOID_FUNCTION(return_type, func_name, nparams, params) \ +static return_type EFIAPI CONCAT(VBOXINTERCEPTOR,func_name)(PARAMETER(nparams)params) \ +{ \ + return_type r; \ + UINT32 off = (UINT32)(uintptr_t)&(((typeof(gThis))0)->CONCAT(SERVICE,Orig).func_name); \ + DEBUG((DEBUG_INFO, "%a%a[%x] enter " PRNT_PARAMETERS(nparams)params "\n", \ + indentRight(), #func_name, off, ARGS(nparams))); \ + DUMP_STACK(2); \ + r =gThis->CONCAT(SERVICE,Orig).func_name(ARGS(nparams)); \ + DEBUG((DEBUG_INFO, "%a%a exit:(%r) \n", \ + indentLeft(), #func_name, r)); \ + return r; \ +} + +#define INSTALLER(x) CONCAT3(install_ ,x, _interceptors) +#define UNINSTALLER(x) CONCAT3(uninstall_,x,_interceptors) + +typedef struct { + EFI_BOOT_SERVICES bsOrig; + EFI_RUNTIME_SERVICES rtOrig; +} VBOXINTERCEPTOR, *PVBOXINTERCEPTOR; + +PVBOXINTERCEPTOR gThis; + +EFI_STATUS install_bs_interceptors(); +EFI_STATUS uninstall_bs_interceptors(); +EFI_STATUS install_rt_interceptors(); +EFI_STATUS uninstall_rt_interceptors(); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptorDxe.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptorDxe.inf new file mode 100644 index 00000000..73eab036 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/VBoxInterceptorDxe.inf @@ -0,0 +1,86 @@ +# $Id: VBoxInterceptorDxe.inf $ +#* @file +# VBoxInterceptorDxe.inf - Interceptor module declaration. + + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxInterceptorDxe + FILE_GUID = FE3542FE-C1D3-4EF8-657C-8048606FF671 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = VBoxInterceptorInit + UNLOAD_IMAGE = VBoxInterceptorFini + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + VBoxInterceptor.c + RunTime.c + BootService.c + boot_service_table.h +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + HiiLib + PrintLib + +[Guids] + gEfiIfrTianoGuid ## CONSUMES ## Guid + +[Protocols] + gEfiHiiStringProtocolGuid ## CONSUMES + gEfiHiiConfigRoutingProtocolGuid ## CONSUMES + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + gEfiFormBrowser2ProtocolGuid ## CONSUMES + gEfiHiiDatabaseProtocolGuid ## CONSUMES + + +[Depex] + gEfiSimpleTextOutProtocolGuid AND gEfiHiiDatabaseProtocolGuid AND gEfiVariableArchProtocolGuid AND gEfiVariableWriteArchProtocolGuid + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/boot_service_table.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/boot_service_table.h new file mode 100644 index 00000000..769abd26 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/boot_service_table.h @@ -0,0 +1,103 @@ +/* $Id: boot_service_table.h $ */ +/** @file + * boot_service_table.h - boot service table declaration. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* NVOID - Non VOID, RVOID - Return VOID */ +/* + * PRINTS FLAGS + * SCL -type + * PTR, PTR2, PTR3 - type *(**)(***) + */ +#if 0 +/* Too much traffic */ +TBL_ENTRY(EFI_RAISE_TPL, RaiseTPL, NVOID, EFI_TPL, 1, (SCL(EFI_TPL))) +TBL_ENTRY(EFI_RESTORE_TPL, RestoreTPL, RVOID, VOID, 1, (SCL(EFI_TPL))) +#endif +TBL_ENTRY(EFI_ALLOCATE_PAGES, AllocatePages, NVOID, EFI_STATUS, 4, (SCL(EFI_ALLOCATE_TYPE ), SCL(EFI_MEMORY_TYPE), SCL(UINTN), PTR(EFI_PHYSICAL_ADDRESS))) +TBL_ENTRY(EFI_FREE_PAGES, FreePages, NVOID, EFI_STATUS, 2, (SCL(EFI_PHYSICAL_ADDRESS), SCL(UINTN))) +TBL_ENTRY(EFI_GET_MEMORY_MAP, GetMemoryMap, NVOID, EFI_STATUS, 5, (PTR(UINTN), PTR(EFI_MEMORY_DESCRIPTOR), PTR(UINTN), PTR(UINTN), PTR(UINT32))) +TBL_ENTRY(EFI_ALLOCATE_POOL, AllocatePool, NVOID, EFI_STATUS, 3, (SCL(EFI_MEMORY_TYPE), SCL(UINTN), PTR2(VOID))) +TBL_ENTRY(EFI_FREE_POOL, FreePool, NVOID, EFI_STATUS, 1, (PTR(VOID))) +TBL_ENTRY(EFI_CREATE_EVENT, CreateEvent, NVOID, EFI_STATUS, 5, (SCL(UINT32), SCL(EFI_TPL), SCL(EFI_EVENT_NOTIFY), PTR(VOID), PTR(EFI_EVENT))) +TBL_ENTRY(EFI_SET_TIMER, SetTimer, NVOID, EFI_STATUS, 3, (SCL(EFI_EVENT), SCL(EFI_TIMER_DELAY), SCL(UINT64))) +TBL_ENTRY(EFI_WAIT_FOR_EVENT, WaitForEvent, NVOID, EFI_STATUS, 3, (SCL(UINTN), PTR(EFI_EVENT), PTR(UINTN))) +TBL_ENTRY(EFI_SIGNAL_EVENT, SignalEvent, NVOID, EFI_STATUS, 1, (SCL(EFI_EVENT))) +TBL_ENTRY(EFI_CLOSE_EVENT, CloseEvent, NVOID, EFI_STATUS, 1, (SCL(EFI_EVENT))) +#if 0 +/* Too much traffic */ +TBL_ENTRY(EFI_CHECK_EVENT, CheckEvent, NVOID, EFI_STATUS, 1, (SCL(EFI_EVENT))) +#endif + +TBL_ENTRY(EFI_INSTALL_PROTOCOL_INTERFACE, InstallProtocolInterface, NVOID, EFI_STATUS, 4, (PTR(EFI_HANDLE), PTR(EFI_GUID), SCL(EFI_INTERFACE_TYPE), PTR(VOID))) +TBL_ENTRY(EFI_REINSTALL_PROTOCOL_INTERFACE, ReinstallProtocolInterface, NVOID, EFI_STATUS, 4, (SCL(EFI_HANDLE), PTR(EFI_GUID), PTR(VOID), PTR(VOID))) +TBL_ENTRY(EFI_UNINSTALL_PROTOCOL_INTERFACE, UninstallProtocolInterface, NVOID, EFI_STATUS, 3, (SCL(EFI_HANDLE), PTR(EFI_GUID), PTR(VOID))) +TBL_ENTRY(EFI_HANDLE_PROTOCOL, HandleProtocol, NVOID, EFI_STATUS, 3, (SCL(EFI_HANDLE), PTR(EFI_GUID), PTR2(VOID))) +TBL_ENTRY(EFI_REGISTER_PROTOCOL_NOTIFY, RegisterProtocolNotify, NVOID, EFI_STATUS, 3, (PTR(EFI_GUID), SCL(EFI_EVENT), PTR2(VOID))) + +TBL_ENTRY(EFI_LOCATE_HANDLE, LocateHandle, NVOID, EFI_STATUS, 5, (SCL(EFI_LOCATE_SEARCH_TYPE), PTR(EFI_GUID), PTR(VOID), PTR(UINTN), PTR(EFI_HANDLE))) + +TBL_ENTRY(EFI_LOCATE_DEVICE_PATH, LocateDevicePath, NVOID, EFI_STATUS, 3, (PTR(EFI_GUID), PTR2(EFI_DEVICE_PATH_PROTOCOL), PTR(EFI_HANDLE))) +TBL_ENTRY(EFI_INSTALL_CONFIGURATION_TABLE, InstallConfigurationTable, NVOID, EFI_STATUS, 2, (PTR(EFI_GUID), PTR(VOID))) + +TBL_ENTRY(EFI_IMAGE_LOAD, LoadImage, NVOID, EFI_STATUS, 6, (SCL(BOOLEAN), SCL(EFI_HANDLE), PTR(EFI_DEVICE_PATH_PROTOCOL), PTR(VOID), SCL(UINTN), PTR(EFI_HANDLE))) +TBL_ENTRY(EFI_IMAGE_START, StartImage, NVOID, EFI_STATUS, 3, (SCL(EFI_HANDLE), PTR(UINTN), PTR2(CHAR16))) + +TBL_ENTRY(EFI_EXIT, Exit, NVOID, EFI_STATUS, 4, (SCL(EFI_HANDLE), SCL(EFI_STATUS), SCL(UINTN), PTR(CHAR16))) +TBL_ENTRY(EFI_IMAGE_UNLOAD, UnloadImage, NVOID, EFI_STATUS, 1, (SCL(EFI_HANDLE))) +TBL_ENTRY(EFI_EXIT_BOOT_SERVICES, ExitBootServices, NVOID, EFI_STATUS, 2, (SCL(EFI_HANDLE), SCL(UINTN))) + +TBL_ENTRY(EFI_GET_NEXT_MONOTONIC_COUNT, GetNextMonotonicCount, NVOID, EFI_STATUS, 1, (PTR(UINT64))) +TBL_ENTRY(EFI_STALL, Stall, NVOID, EFI_STATUS, 1, (SCL(UINTN))) +TBL_ENTRY(EFI_SET_WATCHDOG_TIMER, SetWatchdogTimer, NVOID, EFI_STATUS, 4, (SCL(UINTN), SCL(UINT64), SCL(UINTN), PTR(CHAR16))) + +TBL_ENTRY(EFI_CONNECT_CONTROLLER, ConnectController, NVOID, EFI_STATUS, 4, (SCL(EFI_HANDLE), PTR(EFI_HANDLE), PTR(EFI_DEVICE_PATH_PROTOCOL), SCL(BOOLEAN))) +TBL_ENTRY(EFI_DISCONNECT_CONTROLLER, DisconnectController, NVOID, EFI_STATUS, 3, (SCL(EFI_HANDLE), SCL(EFI_HANDLE), SCL(EFI_HANDLE))) + +TBL_ENTRY(EFI_OPEN_PROTOCOL, OpenProtocol, NVOID, EFI_STATUS, 6, (SCL(EFI_HANDLE), PTR(EFI_GUID), PTR2(VOID), SCL(EFI_HANDLE), SCL(EFI_HANDLE), SCL(UINT32))) +TBL_ENTRY(EFI_CLOSE_PROTOCOL, CloseProtocol, NVOID, EFI_STATUS, 4, (SCL(EFI_HANDLE), PTR(EFI_GUID), SCL(EFI_HANDLE), SCL(EFI_HANDLE))) +TBL_ENTRY(EFI_OPEN_PROTOCOL_INFORMATION, OpenProtocolInformation, NVOID, EFI_STATUS, 4, (SCL(EFI_HANDLE), PTR(EFI_GUID), PTR2(EFI_OPEN_PROTOCOL_INFORMATION_ENTRY), PTR(UINTN))) + +TBL_ENTRY(EFI_PROTOCOLS_PER_HANDLE, ProtocolsPerHandle, NVOID, EFI_STATUS, 3, (SCL(EFI_HANDLE), PTR3(EFI_GUID), PTR(UINTN))) +TBL_ENTRY(EFI_LOCATE_HANDLE_BUFFER, LocateHandleBuffer, NVOID, EFI_STATUS, 5, (SCL(EFI_LOCATE_SEARCH_TYPE), PTR(EFI_GUID), PTR(VOID), PTR(UINTN), PTR2(EFI_HANDLE))) +TBL_ENTRY(EFI_LOCATE_PROTOCOL, LocateProtocol, NVOID, EFI_STATUS, 3, (PTR(EFI_GUID), PTR(VOID), PTR2(VOID))) +#if 0 +/* No var args */ +TBL_ENTRY(EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES, InstallMultipleProtocolInterfaces) +TBL_ENTRY(EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES, UninstallMultipleProtocolInterfaces) +#endif +TBL_ENTRY(EFI_CALCULATE_CRC32, CalculateCrc32, NVOID, EFI_STATUS, 3, (PTR(VOID), SCL(UINTN), PTR(UINT32))) +TBL_ENTRY(EFI_COPY_MEM, CopyMem, RVOID, VOID, 3, (PTR(VOID), PTR(VOID), SCL(UINTN))) +TBL_ENTRY(EFI_SET_MEM, SetMem, RVOID, VOID, 3, (PTR(VOID), SCL(UINTN), SCL(UINT8))) +TBL_ENTRY(EFI_CREATE_EVENT_EX, CreateEventEx, NVOID, EFI_STATUS, 6, (SCL(UINT32), SCL(EFI_TPL), SCL(EFI_EVENT_NOTIFY), PTRC(VOID), PTRC(EFI_GUID), PTR(EFI_EVENT))) diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/interceptor.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/interceptor.h new file mode 100644 index 00000000..b1e583de --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/interceptor.h @@ -0,0 +1,70 @@ +/* $Id: interceptor.h $ */ +/** @file + * interceptor.h - universal interceptor builder. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ +#define TBL_ENTRY(a,b, ign, return_type, params_num, params) static return_type EFIAPI CONCAT(VBOXINTERCEPTOR,b)(PARAMETER(params_num)params); +#include SERVICE_H +#undef TBL_ENTRY + + +#define TBL_ENTRY(a, b, voidness, return_type, nparams, params) \ + FUNCTION(voidness)(return_type, b, nparams, params) +#include SERVICE_H +#undef TBL_ENTRY + + + + +EFI_STATUS INSTALLER(SERVICE)() +{ +#define TBL_ENTRY(a,b, ign0, ign1, ign2, ign3)\ +do { \ + gThis->CONCAT(SERVICE, Orig).b = ORIG_SERVICE->b; \ + ORIG_SERVICE->b = CONCAT(VBOXINTERCEPTOR, b); \ +}while (0); +#include SERVICE_H +#undef TBL_ENTRY + return EFI_SUCCESS; +} + +EFI_STATUS UNINSTALLER(SERVICE)() +{ +#define TBL_ENTRY(a,b, ign0, ign1, ign2, ign3)\ +do { \ + ORIG_SERVICE->b = gThis->CONCAT(SERVICE,Orig).b; \ +}while (0); +#include SERVICE_H +#undef TBL_ENTRY + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/print_types.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/print_types.h new file mode 100644 index 00000000..452fe82b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/print_types.h @@ -0,0 +1,89 @@ +/* $Id: print_types.h $ */ +/** @file + * print_types.h - helper macrodifinition to convert types to right printf flags. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ +#ifndef PRINT_TYPES_H +#define PRINT_TYPES_H +#define PRNT_EFI_TPL PRNT_UINTN +#define PRNT_EFI_ALLOCATE_TYPE PRNT_UINTN +#define PRNT_EFI_MEMORY_TYPE PRNT_UINTN +#define PRNT_EFI_TIMER_DELAY PRNT_UINTN +#define PRNT_EFI_EVENT_NOTIFY PRNT_P_VOID +#define PRNT_EFI_EVENT PRNT_P_VOID +#define PRNT_EFI_INTERFACE_TYPE PRNT_UINTN +#define PRNT_EFI_LOCATE_SEARCH_TYPE PRNT_UINTN +#define PRNT_PP_EFI_CAPSULE_HEADER PRNT_P_VOID +#define PRNT_P_EFI_RESET_TYPE PRNT_P_UINTN +#define PRNT_EFI_RESET_TYPE PRNT_P_UINTN + +#define PRNT_EFI_HANDLE PRNT_P_VOID +#define PRNT_P_EFI_HANDLE PRNT_P_VOID +#define PRNT_PP_EFI_HANDLE PRNT_P_VOID + +#define PRNT_PP_EFI_DEVICE_PATH_PROTOCOL PRNT_P_EFI_DEVICE_PATH_PROTOCOL +#define PRNT_P_EFI_DEVICE_PATH_PROTOCOL PRNT_P_VOID +#define PRNT_PP_CHAR16 PRNT_P_VOID + +/* Pointers */ +#define PRNT_P_EFI_PHYSICAL_ADDRESS PRNT_P_UINT64 +#define PRNT_P_EFI_MEMORY_DESCRIPTOR PRNT_P_VOID +#define PRNT_PP_VOID PRNT_P_VOID +#define PRNT_P_EFI_EVENT PRNT_P_VOID +#define PRNT_P_UINTN PRNT_P_VOID +#define PRNT_PP_EFI_OPEN_PROTOCOL_INFORMATION_ENTRY PRNT_P_VOID +#define PRNT_EFI_PHYSICAL_ADDRESS PRNT_UINT64 +#define PRNT_P_EFI_TIME PRNT_P_VOID +#define PRNT_P_EFI_TIME_CAPABILITIES PRNT_P_VOID + +#define PRNT_P_VOID "%p" +#define PRNT_P_CHAR16 "%s" +#define PRNT_P_CHAR8 PRNT_P_VOID +#define PRNT_P_UINT64 PRNT_P_VOID +#define PRNT_P_UINT32 PRNT_P_VOID +#define PRNT_P_UINTN PRNT_P_VOID +#define PRNT_P_UINT8 PRNT_P_VOID +#define PRNT_PP_EFI_GUID PRNT_P_VOID +#define PRNT_PPP_EFI_GUID PRNT_P_VOID +#define PRNT_P_EFI_GUID "%g" +/* this is machine dependednt */ +#define PRNT_UINT64 "%llx" +#define PRNT_UINT32 "%x" +#define PRNT_UINTN "%x" +#define PRNT_UINT16 "%hx" +#define PRNT_UINT8 "%c" +#define PRNT_EFI_STATUS "%r" +#define PRNT_BOOLEAN PRNT_UINT8 +#define PRNT_CHAR8 PRNT_UINT8 +#define PRNT_P_BOOLEAN PRNT_P_UINT8 +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/runtime_service_table.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/runtime_service_table.h new file mode 100644 index 00000000..e74f8516 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxInterceptorDxe/runtime_service_table.h @@ -0,0 +1,69 @@ +/* $Id: runtime_service_table.h $ */ +/** @file + * runtime_service_table.h - runtime service table declaration. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ +TBL_ENTRY(EFI_GET_TIME, GetTime, NVOID, EFI_STATUS, 2, (PTR(EFI_TIME), PTR(EFI_TIME_CAPABILITIES))) +TBL_ENTRY(EFI_SET_TIME, SetTime, NVOID, EFI_STATUS, 1, (PTR(EFI_TIME))) +TBL_ENTRY(EFI_GET_WAKEUP_TIME, GetWakeupTime, NVOID, EFI_STATUS, 3, (PTR(BOOLEAN), PTR(BOOLEAN), PTR(EFI_TIME))) +TBL_ENTRY(EFI_SET_WAKEUP_TIME, SetWakeupTime, NVOID, EFI_STATUS, 2, (SCL(BOOLEAN), PTR(EFI_TIME))) + +// +// Virtual Memory Services +// +TBL_ENTRY(EFI_SET_VIRTUAL_ADDRESS_MAP, SetVirtualAddressMap, NVOID, EFI_STATUS, 4, (SCL(UINTN), SCL(UINTN), SCL(UINT32), PTR(EFI_MEMORY_DESCRIPTOR))) +TBL_ENTRY(EFI_CONVERT_POINTER, ConvertPointer, NVOID, EFI_STATUS, 2, (SCL(UINTN), PTR2(VOID))) + +// +// Variable Services +// +TBL_ENTRY(EFI_GET_VARIABLE, GetVariable, NVOID, EFI_STATUS, 5, (PTR(CHAR16), PTR(EFI_GUID), PTR(UINT32), PTR(UINTN), PTR(VOID))) +TBL_ENTRY(EFI_GET_NEXT_VARIABLE_NAME, GetNextVariableName, NVOID, EFI_STATUS, 3, (PTR(UINTN), PTR(CHAR16), PTR(EFI_GUID))) +TBL_ENTRY(EFI_SET_VARIABLE, SetVariable, NVOID, EFI_STATUS, 5, (PTR(CHAR16), PTR(EFI_GUID), SCL(UINT32), SCL(UINTN), PTR(VOID))) + +// +// Miscellaneous Services +// +TBL_ENTRY(EFI_GET_NEXT_HIGH_MONO_COUNT, GetNextHighMonotonicCount, NVOID, EFI_STATUS, 1, (PTR(UINT32))) +TBL_ENTRY(EFI_RESET_SYSTEM, ResetSystem, RVOID, VOID, 4, (SCL(EFI_RESET_TYPE), SCL(EFI_STATUS), SCL(UINTN), PTR(VOID))) + +// +// UEFI 2.0 Capsule Services +// +TBL_ENTRY(EFI_UPDATE_CAPSULE, UpdateCapsule, NVOID, EFI_STATUS, 3, (PTR2(EFI_CAPSULE_HEADER), SCL(UINTN), SCL(EFI_PHYSICAL_ADDRESS))) +TBL_ENTRY(EFI_QUERY_CAPSULE_CAPABILITIES, QueryCapsuleCapabilities, NVOID, EFI_STATUS, 4, (PTR2(EFI_CAPSULE_HEADER), SCL(UINTN), PTR(UINT64), PTR(EFI_RESET_TYPE))) + +// +// Miscellaneous UEFI 2.0 Service +// +TBL_ENTRY(EFI_QUERY_VARIABLE_INFO, QueryVariableInfo, NVOID, EFI_STATUS, 4, (SCL(UINT32), PTR(UINT64), PTR(UINT64), PTR(UINT64))) diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/README b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/README new file mode 100644 index 00000000..3b748de5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/README @@ -0,0 +1,4 @@ +This module isn't supposed to be used in normal UEFI functioning. +It used in case guest expects some undocumented UEFI interface and +this module helps in stubbing and simulating these interfaces and investigation of +guest behavior changes. diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicry.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicry.c new file mode 100644 index 00000000..a6edfc4d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicry.c @@ -0,0 +1,155 @@ +/* $Id: VBoxMimicry.c $ */ +/** @file + * VBoxMimicry.c - Mimic table entry. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#include "VBoxMimicry.h" +#define VBOX_MIMICRY_VAR L"VBOX_MIMICRY" + +/*610467a0-d8a7-11de-a911-87667af93b7d*/ +static EFI_GUID gVBoxMimicryVarGuid = { 0x610467a0, 0xd8a7, 0x11de, {0xa9, 0x11, 0x87, 0x66, 0x7a, 0xf9, 0x3b, 0x7d}}; + +#define MIM_TBL_ENTRY(name, guid) DO_9_FAKE_DECL(name) +#include "mimic_tbl.h" +#undef MIM_TBL_ENTRY + +#define MIM_TBL_ENTRY(name, guid) \ +static EFI_GUID gFake ## name = guid; \ +static void *gFuncArray_ ## name [] = \ +{ \ + name ## _fake_impl0, \ + name ## _fake_impl1, \ + name ## _fake_impl2, \ + name ## _fake_impl3, \ + name ## _fake_impl4, \ + name ## _fake_impl5, \ + name ## _fake_impl6, \ + name ## _fake_impl7, \ + name ## _fake_impl8, \ + name ## _fake_impl9 \ +}; +#include "mimic_tbl.h" +#undef MIM_TBL_ENTRY + +#define MIM_TBL_ENTRY(name, guid) \ +FAKE_IMPL(name ## _fake_impl0, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl1, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl2, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl3, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl4, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl5, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl6, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl7, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl8, gFake ## name) \ +FAKE_IMPL(name ## _fake_impl9, gFake ## name) +#include "mimic_tbl.h" +#undef MIM_TBL_ENTRY + + + +EFI_STATUS +EFIAPI +VBoxMimicryInit(EFI_HANDLE hImage, EFI_SYSTEM_TABLE *pSysTable) +{ + /* Set'n'check intercept variable */ + EFI_STATUS r; + UINT32 val; + UINTN size = sizeof(UINT32); + r = gRT->GetVariable(VBOX_MIMICRY_VAR, &gVBoxMimicryVarGuid, NULL, &size, &val); + if ( EFI_ERROR(r) + && r == EFI_NOT_FOUND) + { + size = sizeof(UINT32); + val = 1; + r = gRT->SetVariable(VBOX_MIMICRY_VAR, &gVBoxMimicryVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, size, &val); + if (EFI_ERROR(r)) + { + DEBUG((DEBUG_INFO, "%a:%d - %r\n", __FILE__, __LINE__, r)); + return r; + } + gThis = AllocateZeroPool(sizeof(VBOXMIMICRY)); + r = install_mimic_interfaces(); + if(EFI_ERROR(r)) + { + DEBUG((DEBUG_INFO, "%a:%d - %r\n", __FILE__, __LINE__, r)); + return r; + } + gThis->hImage = hImage; + return r; + } + if (!EFI_ERROR(r)) + { + return EFI_ALREADY_STARTED; + } + return r; +} + +EFI_STATUS +EFIAPI +VBoxMimicryFini(EFI_HANDLE hImage) +{ + EFI_STATUS r; + uninstall_mimic_interfaces(); + FreePool(gThis); + r = gRT->SetVariable(VBOX_MIMICRY_VAR, &gVBoxMimicryVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0, NULL); + if (EFI_ERROR(r)) + { + DEBUG((DEBUG_INFO, "%a:%d - %r\n", __FILE__, __LINE__, r)); + return r; + } + return EFI_SUCCESS; +} + +EFI_STATUS install_mimic_interfaces() +{ + EFI_STATUS Status; + Status = gBS->InstallMultipleProtocolInterfaces ( + &gThis->hImage, +#define MIM_TBL_ENTRY(name, guid) gFake##name, gFuncArray_##name, +#include "mimic_tbl.h" +#undef MIM_TBL_ENTRY + NULL); + return Status; +} +EFI_STATUS uninstall_mimic_interfaces() +{ + EFI_STATUS Status; + Status = gBS->InstallMultipleProtocolInterfaces ( + &gThis->hImage, +#define MIM_TBL_ENTRY(name, guid) gFake##name, +#include "mimic_tbl.h" +#undef MIM_TBL_ENTRY + NULL); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicry.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicry.h new file mode 100644 index 00000000..519f82f1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicry.h @@ -0,0 +1,75 @@ +/* $Id: VBoxMimicry.h $ */ +/** @file + * VBoxMimicry.h - Debug and logging routines implemented by VBoxDebugLib. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +#ifndef __VBOXMIMICRY_H__ +#define __VBOXMIMICRY_H__ +#include <Uefi.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> + +#define MIMICRY_INTERFACE_COUNT 1 +#define DO_9_FAKE_DECL(name) \ +static EFI_STATUS name ## _fake_impl0(); \ +static EFI_STATUS name ## _fake_impl1(); \ +static EFI_STATUS name ## _fake_impl2(); \ +static EFI_STATUS name ## _fake_impl3(); \ +static EFI_STATUS name ## _fake_impl4(); \ +static EFI_STATUS name ## _fake_impl5(); \ +static EFI_STATUS name ## _fake_impl6(); \ +static EFI_STATUS name ## _fake_impl7(); \ +static EFI_STATUS name ## _fake_impl8(); \ +static EFI_STATUS name ## _fake_impl9(); + +#define FAKE_IMPL(name, guid) \ +static EFI_STATUS name () \ +{ \ + DEBUG((DEBUG_INFO, #name ": of %g called\n", &guid)); \ + return EFI_SUCCESS; \ +} + +typedef struct +{ + EFI_HANDLE hImage; +} VBOXMIMICRY, *PVBOXMIMICRY; + +PVBOXMIMICRY gThis; + +EFI_STATUS install_mimic_interfaces(); +EFI_STATUS uninstall_mimic_interfaces(); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicryDxe.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicryDxe.inf new file mode 100644 index 00000000..182ebf1e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/VBoxMimicryDxe.inf @@ -0,0 +1,83 @@ +# $Id: VBoxMimicryDxe.inf $ */ +#* @file +# VBoxMimicryDxe.inf - Mimic module declaration. + + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxMimicryDxe + FILE_GUID = f346c474-d8a6-11de-9504-0fc338e40fda + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = VBoxMimicryInit + UNLOAD_IMAGE = VBoxMimicryFini + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + VBoxMimicry.c +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + HiiLib + PrintLib + +[Guids] + gEfiIfrTianoGuid ## CONSUMES ## Guid + +[Protocols] + gEfiHiiStringProtocolGuid ## CONSUMES + gEfiHiiConfigRoutingProtocolGuid ## CONSUMES + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + gEfiFormBrowser2ProtocolGuid ## CONSUMES + gEfiHiiDatabaseProtocolGuid ## CONSUMES + + +[Depex] + gEfiSimpleTextOutProtocolGuid AND gEfiHiiDatabaseProtocolGuid AND gEfiVariableArchProtocolGuid AND gEfiVariableWriteArchProtocolGuid + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/mimic_tbl.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/mimic_tbl.h new file mode 100644 index 00000000..21df2673 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMimicryDxe/mimic_tbl.h @@ -0,0 +1,39 @@ +/* $Id: mimic_tbl.h $ */ +/** @file + * mimic_tbl.h - Mimic table. + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/*4DF19259-DC71-4D46-BEF1-357BB578C418*/ +#define WIN7_0_GUID {0x4df19259, 0xdc71, 0x4d46, {0xbe, 0xf1, 0x35, 0x7b, 0xb5, 0x78, 0xc4, 0x18}} +MIM_TBL_ENTRY(win7_0, WIN7_0_GUID) diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/Makefile b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/Makefile new file mode 100644 index 00000000..f26167e2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/Makefile @@ -0,0 +1,66 @@ +# $Id: Makefile $ +## @file +# Makefile - assembling the iso image for experimenting with EFI. +# + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# +# Here we add modules to be burn on iso-image +# +SOURCES += VBoxInterceptorDxe.efi +SOURCES += VBoxMimicryDxe.efi + +ifeq ($(BUILD_ARCH), IA32) +ARCH_SUFFIX= +else +ARCH_SUFFIX=$(BUILD_ARCH) +endif + +BUILD_ARCH ?= IA32 +BUILD_DIR=$(WORKSPACE)/Build/VBoxPkg$(ARCH_SUFFIX)/DEBUG_UNIXGCC/$(BUILD_ARCH) + +ISO_DIR=efi-app +FILES_TO_ISO=$(foreach file, $(SOURCES), $(addprefix $(ISO_DIR)/, $(file))) + +DEST=efi-app.iso +all:${DEST} + +$(DEST):$(FILES_TO_ISO) + mkisofs -R -o $@ $(basename $@) + +$(ISO_DIR)/%.efi:$(BUILD_DIR)/%.efi + cp $< $@ + +clean: + $(RM) -rf ${DEST} $(FILES_TO_ISO) + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/Readme.txt b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/Readme.txt new file mode 100644 index 00000000..238eb7ff --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/Readme.txt @@ -0,0 +1,11 @@ +Some drivers and applications we don't wont to include into fw +but we might want to load/run from time to time for debug reasons +or even use on real hw. To do it it required add inf file to +VBoxPkg/VBoxPkg.dsc and don't forget add to SOURCES variable in +Makefile. + +# build +# make -C VBoxPkg/VBoxMisc + +Result iso is RockRidge image attachable to VM and readable +with our EFI. diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/efi-app/Readme.txt b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/efi-app/Readme.txt new file mode 100644 index 00000000..c756c925 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxMisc/efi-app/Readme.txt @@ -0,0 +1,39 @@ +# $Id: Readme.txt $ */ +#* @file +# Readme.txt - Some description about using this module. + + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# +Here could be some help how to manage files on efi-app ISO + +VBoxInterceptor - intercepts calls to gBS and gRT, outputs a lot of traffics to dev_efi.e.l2, (please be patient ;)) + Shell> load VBoxInterceptor.efi diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxPkg.dec b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxPkg.dec new file mode 100644 index 00000000..7b8858c8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxPkg.dec @@ -0,0 +1,91 @@ +# $Id: VBoxPkg.dec $ +## @file +# VBoxPkg.dec - VirtualBox Package description. +# + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = VBoxPkg + PACKAGE_GUID = D118A5AF-05C8-427E-8047-EF53092514B7 + PACKAGE_VERSION = 0.1 + +[Includes.common] + Include + +[Guids.common] +# gEfiPciExpressBaseAddressGuid = {0x3677d529, 0x326f, 0x4603, {0xa9, 0x26, 0xea, 0xac, 0xe0, 0x1d, 0xcb, 0xb0 }} + gEfiAcpiDescriptionGuid = {0x3c699197, 0x093c, 0x4c69, {0xb0, 0x6b, 0x12, 0x8a, 0xe3, 0x48, 0x1d, 0xc9 }} +# gEfiFlashMapHobGuid = { 0xb091e7d2, 0x5a0, 0x4198, {0x94, 0xf0, 0x74, 0xb7, 0xb8, 0xc5, 0x54, 0x59 }} + gVBoxVgaPkgTokenSpaceGuid = { 0xa3a8ce56, 0x4a07, 0x441f, {0xa3, 0xf5, 0x6f, 0x53, 0xdb, 0x9c, 0xb7, 0xd8}} + gVBoxFsBlessedFileInfoGuid = { 0xcc49fefd, 0x41b7, 0x473f, {0x98, 0x23, 0x0e, 0x8e, 0xbf, 0x35, 0x67, 0x7d } } + +# +# Various types of Platform Configuration Database (PCD) items. +# +[PcdsFixedAtBuild.common] +# gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase|0|UINT32|0x00001000 +# gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize|0|UINT32|0x00001001 +# gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase|0|UINT32|0x00001002 +# gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize|0|UINT32|0x00001003 +# gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase|0|UINT32|0x00001004 +# gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize|0|UINT32|0x00001005 + gVBoxVgaPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion|0x0002000a|UINT32|0x00010003 + +## XXX - boot mode - gEfiNt32PkgTokenSpaceGuid.PcdWinNtBootMode|1|UINT32|0x00001006 + +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFlashFvRecoveryBase|0x0|UINT32|0x00001010 +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFlashFvRecoverySize|0x0|UINT32|0x00001011 + +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFirmwareFdSize|0x0|UINT32|0x00001012 +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFirmwareFdSize|0x0|UINT32|0x00001012 +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFirmwareBlockSize|0|UINT32|0x00001013 + +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFlashNvStorageVariableBase|0x0|UINT32|0x00001014 +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFlashNvStorageFtwSpareBase|0x0|UINT32|0x00001015 +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFlashNvStorageFtwWorkingBase|0x0|UINT32|0x00001016 +# gEfiNt32PkgTokenSpaceGuid.PcdWinNtFdBaseAddress|0x0|UINT32|0x00001017 + +[PcdsDynamic.common] + # none yet + +[PcdsPatchableInModule.common] +## XXX - boot mode - gEfiNt32PkgTokenSpaceGuid.PcdWinNtBootMode|1|UINT32|0x00001006 +[PcdsFeatureFlag.common] + gVBoxVgaPkgTokenSpaceGuid.PcdSupportGop|TRUE|BOOLEAN|0x00010004 + gVBoxVgaPkgTokenSpaceGuid.PcdSupportUga|FALSE|BOOLEAN|0x00010005 + +[LibraryClasses] + ## @libraryclass Generic BDS library definition, include the data structure and function. + GenericBdsLib|Include/Library/GenericBdsLib.h + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/LegacyBiosMpTable.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/LegacyBiosMpTable.h new file mode 100644 index 00000000..31dd9692 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/LegacyBiosMpTable.h @@ -0,0 +1,319 @@ +/* $Id: LegacyBiosMpTable.h $ */ +/** @file + * LegacyBiosMpTable.h + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/*++ + +This code is based on: + +Copyright (c) 2007, Intel Corporation +All rights reserved. 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. + +Module Name: + + LegacyBiosMpTable.h + +Abstract: + Defines data structures per Multi Processor Specification Ver 1.4. + +--*/ + +#ifndef LEGACY_BIOS_MPTABLE_H_ +#define LEGACY_BIOS_MPTABLE_H_ + +#define EFI_LEGACY_MP_TABLE_REV_1_4 0x04 + +// +// Define MP table structures. All are packed. +// +#pragma pack(push, 1) + +#define EFI_LEGACY_MP_TABLE_FLOATING_POINTER_SIGNATURE SIGNATURE_32 ('_', 'M', 'P', '_') +typedef struct { + UINT32 Signature; + UINT32 PhysicalAddress; + UINT8 Length; + UINT8 SpecRev; + UINT8 Checksum; + UINT8 FeatureByte1; + struct { + UINT32 Reserved1 : 6; + UINT32 MutipleClk : 1; + UINT32 Imcr : 1; + UINT32 Reserved2 : 24; + } FeatureByte2_5; +} EFI_LEGACY_MP_TABLE_FLOATING_POINTER; + +#define EFI_LEGACY_MP_TABLE_HEADER_SIGNATURE SIGNATURE_32 ('P', 'C', 'M', 'P') +typedef struct { + UINT32 Signature; + UINT16 BaseTableLength; + UINT8 SpecRev; + UINT8 Checksum; + CHAR8 OemId[8]; + CHAR8 OemProductId[12]; + UINT32 OemTablePointer; + UINT16 OemTableSize; + UINT16 EntryCount; + UINT32 LocalApicAddress; + UINT16 ExtendedTableLength; + UINT8 ExtendedChecksum; + UINT8 Reserved; +} EFI_LEGACY_MP_TABLE_HEADER; + +typedef struct { + UINT8 EntryType; +} EFI_LEGACY_MP_TABLE_ENTRY_TYPE; + +// +// Entry Type 0: Processor. +// +#define EFI_LEGACY_MP_TABLE_ENTRY_TYPE_PROCESSOR 0x00 +typedef struct { + UINT8 EntryType; + UINT8 Id; + UINT8 Ver; + struct { + UINT8 Enabled : 1; + UINT8 Bsp : 1; + UINT8 Reserved : 6; + } Flags; + struct { + UINT32 Stepping : 4; + UINT32 Model : 4; + UINT32 Family : 4; + UINT32 Reserved : 20; + } Signature; + struct { + UINT32 Fpu : 1; + UINT32 Reserved1 : 6; + UINT32 Mce : 1; + UINT32 Cx8 : 1; + UINT32 Apic : 1; + UINT32 Reserved2 : 22; + } Features; + UINT32 Reserved1; + UINT32 Reserved2; +} EFI_LEGACY_MP_TABLE_ENTRY_PROCESSOR; + +// +// Entry Type 1: Bus. +// +#define EFI_LEGACY_MP_TABLE_ENTRY_TYPE_BUS 0x01 +typedef struct { + UINT8 EntryType; + UINT8 Id; + CHAR8 TypeString[6]; +} EFI_LEGACY_MP_TABLE_ENTRY_BUS; + +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_CBUS "CBUS " // Corollary CBus +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_CBUSII "CBUSII" // Corollary CBUS II +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_EISA "EISA " // Extended ISA +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_FUTURE "FUTURE" // IEEE FutureBus +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_INTERN "INTERN" // Internal bus +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_ISA "ISA " // Industry Standard Architecture +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_MBI "MBI " // Multibus I +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_MBII "MBII " // Multibus II +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_MCA "MCA " // Micro Channel Architecture +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_MPI "MPI " // MPI +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_MPSA "MPSA " // MPSA +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_NUBUS "NUBUS " // Apple Macintosh NuBus +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_PCI "PCI " // Peripheral Component Interconnect +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_PCMCIA "PCMCIA" // PC Memory Card International Assoc. +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_TC "TC " // DEC TurboChannel +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_VL "VL " // VESA Local Bus +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_VME "VME " // VMEbus +#define EFI_LEGACY_MP_TABLE_ENTRY_BUS_STRING_XPRESS "XPRESS" // Express System Bus +// +// Entry Type 2: I/O APIC. +// +#define EFI_LEGACY_MP_TABLE_ENTRY_TYPE_IOAPIC 0x02 +typedef struct { + UINT8 EntryType; + UINT8 Id; + UINT8 Ver; + struct { + UINT8 Enabled : 1; + UINT8 Reserved : 7; + } Flags; + UINT32 Address; +} EFI_LEGACY_MP_TABLE_ENTRY_IOAPIC; + +// +// Entry Type 3: I/O Interrupt Assignment. +// +#define EFI_LEGACY_MP_TABLE_ENTRY_TYPE_IO_INT 0x03 +typedef struct { + UINT8 EntryType; + UINT8 IntType; + struct { + UINT16 Polarity : 2; + UINT16 Trigger : 2; + UINT16 Reserved : 12; + } Flags; + UINT8 SourceBusId; + union { + struct { + UINT8 IntNo : 2; + UINT8 Dev : 5; + UINT8 Reserved : 1; + } fields; + UINT8 byte; + } SourceBusIrq; + UINT8 DestApicId; + UINT8 DestApicIntIn; +} EFI_LEGACY_MP_TABLE_ENTRY_IO_INT; + +typedef enum { + EfiLegacyMpTableEntryIoIntTypeInt = 0, + EfiLegacyMpTableEntryIoIntTypeNmi = 1, + EfiLegacyMpTableEntryIoIntTypeSmi = 2, + EfiLegacyMpTableEntryIoIntTypeExtInt= 3, +} EFI_LEGACY_MP_TABLE_ENTRY_IO_INT_TYPE; + +typedef enum { + EfiLegacyMpTableEntryIoIntFlagsPolaritySpec = 0x0, + EfiLegacyMpTableEntryIoIntFlagsPolarityActiveHigh = 0x1, + EfiLegacyMpTableEntryIoIntFlagsPolarityReserved = 0x2, + EfiLegacyMpTableEntryIoIntFlagsPolarityActiveLow = 0x3, +} EFI_LEGACY_MP_TABLE_ENTRY_IO_INT_FLAGS_POLARITY; + +typedef enum { + EfiLegacyMpTableEntryIoIntFlagsTriggerSpec = 0x0, + EfiLegacyMpTableEntryIoIntFlagsTriggerEdge = 0x1, + EfiLegacyMpTableEntryIoIntFlagsTriggerReserved = 0x2, + EfiLegacyMpTableEntryIoIntFlagsTriggerLevel = 0x3, +} EFI_LEGACY_MP_TABLE_ENTRY_IO_INT_FLAGS_TRIGGER; + +// +// Entry Type 4: Local Interrupt Assignment. +// +#define EFI_LEGACY_MP_TABLE_ENTRY_TYPE_LOCAL_INT 0x04 +typedef struct { + UINT8 EntryType; + UINT8 IntType; + struct { + UINT16 Polarity : 2; + UINT16 Trigger : 2; + UINT16 Reserved : 12; + } Flags; + UINT8 SourceBusId; + UINT8 SourceBusIrq; + UINT8 DestApicId; + UINT8 DestApicIntIn; +} EFI_LEGACY_MP_TABLE_ENTRY_LOCAL_INT; + +typedef enum { + EfiLegacyMpTableEntryLocalIntTypeInt = 0, + EfiLegacyMpTableEntryLocalIntTypeNmi = 1, + EfiLegacyMpTableEntryLocalIntTypeSmi = 2, + EfiLegacyMpTableEntryLocalIntTypeExtInt = 3, +} EFI_LEGACY_MP_TABLE_ENTRY_LOCAL_INT_TYPE; + +typedef enum { + EfiLegacyMpTableEntryLocalIntFlagsPolaritySpec = 0x0, + EfiLegacyMpTableEntryLocalIntFlagsPolarityActiveHigh= 0x1, + EfiLegacyMpTableEntryLocalIntFlagsPolarityReserved = 0x2, + EfiLegacyMpTableEntryLocalIntFlagsPolarityActiveLow = 0x3, +} EFI_LEGACY_MP_TABLE_ENTRY_LOCAL_INT_FLAGS_POLARITY; + +typedef enum { + EfiLegacyMpTableEntryLocalIntFlagsTriggerSpec = 0x0, + EfiLegacyMpTableEntryLocalIntFlagsTriggerEdge = 0x1, + EfiLegacyMpTableEntryLocalIntFlagsTriggerReserved = 0x2, + EfiLegacyMpTableEntryLocalIntFlagsTriggerLevel = 0x3, +} EFI_LEGACY_MP_TABLE_ENTRY_LOCAL_INT_FLAGS_TRIGGER; + +// +// Entry Type 128: System Address Space Mapping. +// +#define EFI_LEGACY_MP_TABLE_ENTRY_EXT_TYPE_SYS_ADDR_SPACE_MAPPING 0x80 +typedef struct { + UINT8 EntryType; + UINT8 Length; + UINT8 BusId; + UINT8 AddressType; + UINT64 AddressBase; + UINT64 AddressLength; +} EFI_LEGACY_MP_TABLE_ENTRY_EXT_SYS_ADDR_SPACE_MAPPING; + +typedef enum { + EfiLegacyMpTableEntryExtSysAddrSpaceMappingIo = 0, + EfiLegacyMpTableEntryExtSysAddrSpaceMappingMemory = 1, + EfiLegacyMpTableEntryExtSysAddrSpaceMappingPrefetch = 2, +} EFI_LEGACY_MP_TABLE_ENTRY_EXT_SYS_ADDR_SPACE_MAPPING_TYPE; + +// +// Entry Type 129: Bus Hierarchy. +// +#define EFI_LEGACY_MP_TABLE_ENTRY_EXT_TYPE_BUS_HIERARCHY 0x81 +typedef struct { + UINT8 EntryType; + UINT8 Length; + UINT8 BusId; + struct { + UINT8 SubtractiveDecode : 1; + UINT8 Reserved : 7; + } BusInfo; + UINT8 ParentBus; + UINT8 Reserved1; + UINT8 Reserved2; + UINT8 Reserved3; +} EFI_LEGACY_MP_TABLE_ENTRY_EXT_BUS_HIERARCHY; + +// +// Entry Type 130: Compatibility Bus Address Space Modifier. +// +#define EFI_LEGACY_MP_TABLE_ENTRY_EXT_TYPE_COMPAT_BUS_ADDR_SPACE_MODIFIER 0x82 +typedef struct { + UINT8 EntryType; + UINT8 Length; + UINT8 BusId; + struct { + UINT8 RangeMode : 1; + UINT8 Reserved : 7; + } AddrMode; + UINT32 PredefinedRangeList; +} EFI_LEGACY_MP_TABLE_ENTRY_EXT_COMPAT_BUS_ADDR_SPACE_MODIFIER; + +#pragma pack(pop) + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/TableConversion.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/TableConversion.c new file mode 100644 index 00000000..0cde7ef8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/TableConversion.c @@ -0,0 +1,380 @@ +/* $Id: TableConversion.c $ */ +/** @file + * TableConversion.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/*++ + + This code is baed on: + + Copyright (c) 2006 - 2007, Intel Corporation + All rights reserved. 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 <IndustryStandard/Pci.h> +#include <IndustryStandard/Acpi.h> +#include <IndustryStandard/SmBios.h> +#include "LegacyBiosMpTable.h" + +#include <Library/DebugLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/BaseLib.h> + +#include <Guid/Acpi.h> +#include <Guid/SmBios.h> +#include <Guid/Mps.h> +#include <Guid/HobList.h> +#include <Guid/GlobalVariable.h> + +#define SYS_TABLE_PAD(ptr) (((~ptr) +1) & 0x07 ) +#define EFI_SYSTEM_TABLE_MAX_ADDRESS 0xFFFFFFFF + +EFI_STATUS +ConvertAcpiTable ( + IN UINTN TableLen, + IN OUT VOID **Table + ) +/*++ + + Routine Description: + Convert RSDP of ACPI Table if its location is lower than Address:0x100000 + Assumption here: + As in legacy Bios, ACPI table is required to place in E/F Seg, + So here we just check if the range is E/F seg, + and if Not, assume the Memory type is EfiACPIReclaimMemory/EfiACPIMemoryNVS + + Arguments: + TableLen - Acpi RSDP length + Table - pointer to the table + + Returns: + EFI_SUCCESS - Convert Table successfully + Other - Failed + + --*/ +{ + VOID *AcpiTableOri; + VOID *AcpiTableNew; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS BufferPtr; + + + AcpiTableOri = (VOID *)(UINTN)((*Table)); + + BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES(TableLen), + &BufferPtr + ); + ASSERT_EFI_ERROR (Status); + AcpiTableNew = (VOID *)(UINTN)BufferPtr; + CopyMem (AcpiTableNew, AcpiTableOri, TableLen); + + // + // Change configuration table Pointer + // + *Table = AcpiTableNew; + + return EFI_SUCCESS; +} + +EFI_STATUS +ConvertSmbiosTable ( + IN OUT VOID **Table + ) +/*++ + + Routine Description: + + Convert Smbios Table if the Location of the SMBios Table is lower than Address 0x100000 + Assumption here: + As in legacy Bios, Smbios table is required to place in E/F Seg, + So here we just check if the range is F seg, + and if Not, assume the Memory type is EfiACPIMemoryNVS/EfiRuntimeServicesData + Arguments: + Table - pointer to the table + + Returns: + EFI_SUCCESS - Convert Table successfully + Other - Failed + + --*/ +{ + SMBIOS_TABLE_ENTRY_POINT *SmbiosTableNew; + SMBIOS_TABLE_ENTRY_POINT *SmbiosTableOri; + EFI_STATUS Status; + UINT32 SmbiosEntryLen; + UINT32 BufferLen; + EFI_PHYSICAL_ADDRESS BufferPtr; + + SmbiosTableNew = NULL; + SmbiosTableOri = NULL; + + // + // Get Smibos configuration Table + // + SmbiosTableOri = (SMBIOS_TABLE_ENTRY_POINT *)(UINTN)((*Table)); + + + ASSERT(CalculateSum8((UINT8*)SmbiosTableOri, sizeof(SMBIOS_TABLE_ENTRY_POINT)) == 0); + // + // Relocate the Smibos memory + // + BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS; + if (SmbiosTableOri->SmbiosBcdRevision != 0x21) { + SmbiosEntryLen = SmbiosTableOri->EntryPointLength; + } else { + // + // According to Smbios Spec 2.4, we should set entry point length as 0x1F if version is 2.1 + // + SmbiosEntryLen = 0x1F; + } + BufferLen = SmbiosEntryLen + SYS_TABLE_PAD(SmbiosEntryLen) + SmbiosTableOri->TableLength; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES(BufferLen), + &BufferPtr + ); + ASSERT_EFI_ERROR (Status); + SmbiosTableNew = (SMBIOS_TABLE_ENTRY_POINT *)(UINTN)BufferPtr; + CopyMem ( + SmbiosTableNew, + SmbiosTableOri, + SmbiosEntryLen + ); + // + // Get Smbios Structure table address, and make sure the start address is 32-bit align + // + BufferPtr += SmbiosEntryLen + SYS_TABLE_PAD(SmbiosEntryLen); + CopyMem ( + (VOID *)(UINTN)BufferPtr, + (VOID *)(UINTN)(SmbiosTableOri->TableAddress), + SmbiosTableOri->TableLength + ); + SmbiosTableNew->TableAddress = (UINT32)BufferPtr; + SmbiosTableNew->IntermediateChecksum = 0; + SmbiosTableNew->IntermediateChecksum = + CalculateCheckSum8 ((UINT8*)SmbiosTableNew + 0x10, SmbiosEntryLen -0x10); + // + // Change the SMBIOS pointer + // + *Table = SmbiosTableNew; + + return EFI_SUCCESS; +} + +EFI_STATUS +ConvertMpsTable ( + IN OUT VOID **Table + ) +/*++ + + Routine Description: + + Convert MP Table if the Location of the SMBios Table is lower than Address 0x100000 + Assumption here: + As in legacy Bios, MP table is required to place in E/F Seg, + So here we just check if the range is E/F seg, + and if Not, assume the Memory type is EfiACPIMemoryNVS/EfiRuntimeServicesData + Arguments: + Table - pointer to the table + + Returns: + EFI_SUCCESS - Convert Table successfully + Other - Failed + + --*/ +{ + UINT32 Data32; + UINT32 FPLength; + EFI_LEGACY_MP_TABLE_FLOATING_POINTER *MpsFloatingPointerOri; + EFI_LEGACY_MP_TABLE_FLOATING_POINTER *MpsFloatingPointerNew; + EFI_LEGACY_MP_TABLE_HEADER *MpsTableOri; + EFI_LEGACY_MP_TABLE_HEADER *MpsTableNew; + VOID *OemTableOri; + VOID *OemTableNew; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS BufferPtr; + + // + // Get MP configuration Table + // + MpsFloatingPointerOri = (EFI_LEGACY_MP_TABLE_FLOATING_POINTER *)(UINTN)(*Table); + // + // Get Floating pointer structure length + // + FPLength = MpsFloatingPointerOri->Length * 16; + ASSERT(CalculateSum8((UINT8*)MpsFloatingPointerOri, FPLength) == 0); + Data32 = FPLength + SYS_TABLE_PAD (FPLength); + MpsTableOri = (EFI_LEGACY_MP_TABLE_HEADER *)(UINTN)(MpsFloatingPointerOri->PhysicalAddress); + ASSERT(MpsTableOri != NULL); + ASSERT(CalculateSum8((UINT8*)MpsTableOri, MpsTableOri->BaseTableLength) == 0); + + Data32 += MpsTableOri->BaseTableLength; + Data32 += MpsTableOri->ExtendedTableLength; + if (MpsTableOri->OemTablePointer != 0x00) { + Data32 += SYS_TABLE_PAD (Data32); + Data32 += MpsTableOri->OemTableSize; + } + + // + // Relocate memory + // + BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES(Data32), + &BufferPtr + ); + ASSERT_EFI_ERROR (Status); + MpsFloatingPointerNew = (EFI_LEGACY_MP_TABLE_FLOATING_POINTER *)(UINTN)BufferPtr; + CopyMem (MpsFloatingPointerNew, MpsFloatingPointerOri, FPLength); + // + // If Mp Table exists + // + if (MpsTableOri != NULL) { + // + // Get Mps table length, including Ext table + // + BufferPtr = BufferPtr + FPLength + SYS_TABLE_PAD (FPLength); + MpsTableNew = (EFI_LEGACY_MP_TABLE_HEADER *)(UINTN)BufferPtr; + CopyMem (MpsTableNew, MpsTableOri, MpsTableOri->BaseTableLength + MpsTableOri->ExtendedTableLength); + + if ((MpsTableOri->OemTableSize != 0x0000) && (MpsTableOri->OemTablePointer != 0x0000)){ + BufferPtr += MpsTableOri->BaseTableLength + MpsTableOri->ExtendedTableLength; + BufferPtr += SYS_TABLE_PAD (BufferPtr); + OemTableNew = (VOID *)(UINTN)BufferPtr; + OemTableOri = (VOID *)(UINTN)MpsTableOri->OemTablePointer; + CopyMem (OemTableNew, OemTableOri, MpsTableOri->OemTableSize); + MpsTableNew->OemTablePointer = (UINT32)(UINTN)OemTableNew; + } + MpsTableNew->Checksum = 0; + MpsTableNew->Checksum = CalculateCheckSum8 ((UINT8*)MpsTableNew, MpsTableOri->BaseTableLength); + MpsFloatingPointerNew->PhysicalAddress = (UINT32)(UINTN)MpsTableNew; + MpsFloatingPointerNew->Checksum = 0; + MpsFloatingPointerNew->Checksum = CalculateCheckSum8 ((UINT8*)MpsFloatingPointerNew, FPLength); + } + // + // Change the pointer + // + *Table = MpsFloatingPointerNew; + + return EFI_SUCCESS; +} + +EFI_STATUS +ConvertSystemTable ( + IN EFI_GUID *TableGuid, + IN OUT VOID **Table + ) +/*++ + + Routine Description: + Convert ACPI Table /Smbios Table /MP Table if its location is lower than Address:0x100000 + Assumption here: + As in legacy Bios, ACPI/Smbios/MP table is required to place in E/F Seg, + So here we just check if the range is E/F seg, + and if Not, assume the Memory type is EfiACPIReclaimMemory/EfiACPIMemoryNVS + + Arguments: + TableGuid - Guid of the table + Table - pointer to the table + + Returns: + EFI_SUCCESS - Convert Table successfully + Other - Failed + + --*/ +{ + EFI_STATUS Status = EFI_SUCCESS; + VOID *AcpiHeader; + UINTN AcpiTableLen; + + // + // If match acpi guid (1.0, 2.0, or later), Convert ACPI table according to version. + // + + if (CompareGuid(TableGuid, &gEfiAcpiTableGuid) || CompareGuid(TableGuid, &gEfiAcpi20TableGuid)){ + AcpiHeader = (VOID*)(UINTN)(*Table); + + if (((EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)AcpiHeader)->Reserved == 0x00){ + // + // If Acpi 1.0 Table, then RSDP structure doesn't contain Length field, use structure size + // + AcpiTableLen = sizeof (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER); + } else if (((EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)AcpiHeader)->Reserved >= 0x02){ + // + // If Acpi 2.0 or later, use RSDP Length fied. + // + AcpiTableLen = ((EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)AcpiHeader)->Length; + } else { + // + // Invalid Acpi Version, return + // + return EFI_UNSUPPORTED; + } + Status = ConvertAcpiTable (AcpiTableLen, Table); + return Status; + } + + // + // If matches smbios guid, convert Smbios table. + // + if (CompareGuid(TableGuid, &gEfiSmbiosTableGuid)){ + Status = ConvertSmbiosTable (Table); + return Status; + } + + // + // If the table is MP table? + // + if (CompareGuid(TableGuid, &gEfiMpsTableGuid)){ + Status = ConvertMpsTable (Table); + return Status; + } + + return EFI_UNSUPPORTED; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.c new file mode 100644 index 00000000..06d0b6a9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.c @@ -0,0 +1,178 @@ +/* $Id: VBoxSysTables.c $ */ +/** @file + * VBoxSysTables.c - VirtualBox system tables + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> + +#include <Protocol/DevicePathToText.h> + +#include <IndustryStandard/Acpi10.h> +#include <IndustryStandard/Acpi20.h> +#include <IndustryStandard/SmBios.h> + +#include <Guid/SmBios.h> +#include <Guid/Acpi.h> +#include <Guid/Mps.h> + +#include "VBoxPkg.h" +#include "DevEFI.h" +#include "iprt/asm.h" + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + + +EFI_STATUS EFIAPI +ConvertSystemTable ( + IN EFI_GUID *TableGuid, + IN OUT VOID **Table + ); + +#define MPS_PTR SIGNATURE_32('_','M','P','_') +#define SMBIOS_PTR SIGNATURE_32('_','S','M','_') + +#define EBDA_BASE (0x9FC0 << 4) + +VOID * +FindSMBIOSPtr ( + VOID + ) +{ + UINTN Address; + + // + // First Search 0x0e0000 - 0x0fffff for SMBIOS Ptr + // + for (Address = 0xe0000; Address < 0xfffff; Address += 0x10) { + if (*(UINT32 *)(Address) == SMBIOS_PTR) { + return (VOID *)Address; + } + } + return NULL; +} + +VOID * +FindMPSPtr ( + VOID + ) +{ + UINTN Address; + UINTN Index; + + // + // First Search 0x0e0000 - 0x0fffff for MPS Ptr + // + for (Address = 0xe0000; Address < 0xfffff; Address += 0x10) { + if (*(UINT32 *)(Address) == MPS_PTR) { + return (VOID *)Address; + } + } + + // + // Search EBDA + // + + Address = EBDA_BASE; + for (Index = 0; Index < 0x400 ; Index += 16) { + if (*(UINT32 *)(Address + Index) == MPS_PTR) { + return (VOID *)(Address + Index); + } + } + return NULL; +} + +EFI_STATUS EFIAPI +ConvertAndInstallTable(EFI_GUID* Guid, VOID* Ptr) +{ + EFI_STATUS rc = EFI_SUCCESS; + + rc = ConvertSystemTable(Guid, &Ptr); + //ASSERT_EFI_ERROR (rc); + + rc = gBS->InstallConfigurationTable(Guid, Ptr); + ASSERT_EFI_ERROR (rc); + + return rc; +} + + +/** + * VBoxSysTablesDxe entry point. + * + * @returns EFI status code. + * + * @param ImageHandle The image handle. + * @param SystemTable The system table pointer. + */ +EFI_STATUS EFIAPI +DxeInitializeVBoxSysTables(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS rc; + VOID* Ptr; + + DEBUG((DEBUG_INFO, "DxeInitializeVBoxSysTables\n")); + + Ptr = FindSMBIOSPtr(); + DEBUG((DEBUG_INFO, "SMBIOS=%p\n", Ptr)); + ASSERT(Ptr != NULL); + if (Ptr) + { + rc = ConvertAndInstallTable(&gEfiSmbiosTableGuid, Ptr); + ASSERT_EFI_ERROR (rc); + } + + Ptr = FindMPSPtr(); + DEBUG((DEBUG_INFO, "MPS=%p\n", Ptr)); + // MPS can be null in non IO-APIC configs + if (Ptr) + rc = ConvertAndInstallTable(&gEfiMpsTableGuid, Ptr); + + return EFI_SUCCESS; +} + +EFI_STATUS EFIAPI +DxeUninitializeVBoxSysTables(IN EFI_HANDLE ImageHandle) +{ + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.inf new file mode 100644 index 00000000..51299160 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.inf @@ -0,0 +1,83 @@ +# $Id: VBoxSysTables.inf $ +## @file +# VBoxSysTables - VBox system tables. +# + +# +# Copyright (C) 2010-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxSysTables + FILE_GUID = 3749CF40-9086-4488-BB8E-44C9400D260F + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 +# SUPPORTED_ARCHITECTURES = IA32|X64|IPF|EBC + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + ENTRY_POINT = DxeInitializeVBoxSysTables + UNLOAD_IMAGE = DxeUninitializeVBoxSysTables + +[Sources.common] + VBoxSysTables.c + TableConversion.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + DevicePathLib + +[Guids] + gEfiSmbiosTableGuid + gEfiAcpiTableGuid + gEfiAcpi20TableGuid + gEfiMpsTableGuid + +[Protocols] + gEfiDevicePathToTextProtocolGuid + +[BuildOptions.common] + + GCC:*_*_*_CC_FLAGS = + INTEL:*_*_*_CC_FLAGS = + MSFT:*_*_*_CC_FLAGS = diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/ComponentName.c new file mode 100644 index 00000000..a3a64c94 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/ComponentName.c @@ -0,0 +1,247 @@ +/* $Id: ComponentName.c $ */ +/** @file + * ComponentName.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Copyright (c) 2006, Intel Corporation + All rights reserved. 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 "VBoxVga.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gVBoxVgaComponentName = { + VBoxVgaComponentNameGetDriverName, + VBoxVgaComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gVBoxVgaComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) VBoxVgaComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) VBoxVgaComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mVBoxVgaDriverNameTable[] = { + { "eng;en", L"VirtualBox SVGA Driver" }, + { NULL , NULL } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mVBoxVgaControllerNameTable[] = { + { "eng;en", L"VirtualBox SVGA PCI Adapter" }, + { NULL , NULL } +}; + +/** + 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] 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 DriverName[out] 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 +VBoxVgaComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mVBoxVgaDriverNameTable, + DriverName, + (BOOLEAN)(This == &gVBoxVgaComponentName) + ); +} + +/** + 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] 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 ChildHandle[in] 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 Language[in] 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 ControllerName[out] 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 not a valid EFI_HANDLE. + + @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 +VBoxVgaComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gVBoxVgaDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the Cirrus Logic 5430's Device structure + // + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mVBoxVgaControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gVBoxVgaComponentName) + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/DriverSupportedEfiVersion.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/DriverSupportedEfiVersion.c new file mode 100644 index 00000000..737e68ff --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/DriverSupportedEfiVersion.c @@ -0,0 +1,58 @@ +/* $Id: DriverSupportedEfiVersion.c $ */ +/** @file + * DriverSupportedEfiVersion.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Copyright (c) 2007, Intel Corporation + All rights reserved. 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. + + Module Name: DriverSupportEfiVersion.c + +*/ +#include "VBoxVga.h" + +EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gVBoxVgaDriverSupportedEfiVersion = { + sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure. + 0 // Version number to be filled at start up. +}; + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/Edid.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/Edid.c new file mode 100644 index 00000000..4c3c73c1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/Edid.c @@ -0,0 +1,676 @@ +/* $Id: Edid.c $ */ +/** @file + * Edid.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Read EDID information and parse EDID information. + + Copyright (c) 2008, Intel Corporation + All rights reserved. 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 "VBoxVga.h" +#include "VBoxVgaI2c.h" + +// +// EDID block +// +typedef struct { + UINT8 Header[8]; //EDID header "00 FF FF FF FF FF FF 00" + UINT16 ManufactureName; //EISA 3-character ID + UINT16 ProductCode; //Vendor assigned code + UINT32 SerialNumber; //32-bit serial number + UINT8 WeekOfManufacture; //Week number + UINT8 YearOfManufacture; //Year + UINT8 EdidVersion; //EDID Structure Version + UINT8 EdidRevision; //EDID Structure Revision + UINT8 VideoInputDefinition; + UINT8 MaxHorizontalImageSize; //cm + UINT8 MaxVerticalImageSize; //cm + UINT8 DisplayTransferCharacteristic; + UINT8 FeatureSupport; + UINT8 RedGreenLowBits; //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0 + UINT8 BlueWhiteLowBits; //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0 + UINT8 RedX; //Red-x Bits 9 - 2 + UINT8 RedY; //Red-y Bits 9 - 2 + UINT8 GreenX; //Green-x Bits 9 - 2 + UINT8 GreenY; //Green-y Bits 9 - 2 + UINT8 BlueX; //Blue-x Bits 9 - 2 + UINT8 BlueY; //Blue-y Bits 9 - 2 + UINT8 WhiteX; //White-x Bits 9 - 2 + UINT8 WhiteY; //White-x Bits 9 - 2 + UINT8 EstablishedTimings[3]; + UINT8 StandardTimingIdentification[16]; + UINT8 DetailedTimingDescriptions[72]; + UINT8 ExtensionFlag; //Number of (optional) 128-byte EDID extension blocks to follow + UINT8 Checksum; +} EDID_BLOCK; + +#define EDID_BLOCK_SIZE 128 +#define VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER 17 + +typedef struct { + UINT16 HorizontalResolution; + UINT16 VerticalResolution; + UINT16 RefreshRate; +} EDID_TIMING; + +typedef struct { + UINT32 ValidNumber; + UINT32 Key[VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER]; +} VALID_EDID_TIMING; + +// +// Standard timing defined by VESA EDID +// +EDID_TIMING mVbeEstablishedEdidTiming[] = { + // + // Established Timing I + // + {800, 600, 60}, + {800, 600, 56}, + {640, 480, 75}, + {640, 480, 72}, + {640, 480, 67}, + {640, 480, 60}, + {720, 400, 88}, + {720, 400, 70}, + // + // Established Timing II + // + {1280, 1024, 75}, + {1024, 768, 75}, + {1024, 768, 70}, + {1024, 768, 60}, + {1024, 768, 87}, + {832, 624, 75}, + {800, 600, 75}, + {800, 600, 72}, + // + // Established Timing III + // + {1152, 870, 75} +}; + +/** + Read EDID information from I2C Bus on CirrusLogic. + + @param Private Pointer to VBOX_VGA_PRIVATE_DATA. + @param EdidDataBlock Pointer to EDID data block. + @param EdidSize Returned EDID block size. + + @retval EFI_UNSUPPORTED + @retval EFI_SUCCESS + +**/ +EFI_STATUS +ReadEdidData ( + VBOX_VGA_PRIVATE_DATA *Private, + UINT8 **EdidDataBlock, + UINTN *EdidSize + ) +{ + UINTN Index; + UINT8 EdidData[EDID_BLOCK_SIZE * 2]; + UINT8 *ValidEdid; + UINT64 Signature; + + for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++) { + I2cReadByte (Private->PciIo, 0xa0, (UINT8)Index, &EdidData[Index]); + } + + // + // Search for the EDID signature + // + ValidEdid = &EdidData[0]; + Signature = 0x00ffffffffffff00ull; + for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++, ValidEdid ++) { + if (CompareMem (ValidEdid, &Signature, 8) == 0) { + break; + } + } + + if (Index == 256) { + // + // No EDID signature found + // + return EFI_UNSUPPORTED; + } + + *EdidDataBlock = AllocateCopyPool ( + sizeof (EDID_BLOCK_SIZE), + ValidEdid + ); + if (*EdidDataBlock == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Currently only support EDID 1.x + // + *EdidSize = EDID_BLOCK_SIZE; + + return EFI_SUCCESS; +} + +/** + Generate a search key for a specified timing data. + + @param EdidTiming Pointer to EDID timing + + @return The 32 bit unique key for search. + +**/ +UINT32 +CalculateEdidKey ( + EDID_TIMING *EdidTiming + ) +{ + UINT32 Key; + + // + // Be sure no conflicts for all standard timing defined by VESA. + // + Key = (EdidTiming->HorizontalResolution * 2) + EdidTiming->VerticalResolution; + return Key; +} + +/** + Search a specified Timing in all the valid EDID timings. + + @param ValidEdidTiming All valid EDID timing information. + @param EdidTiming The Timing to search for. + + @retval TRUE Found. + @retval FALSE Not found. + +**/ +BOOLEAN +SearchEdidTiming ( + VALID_EDID_TIMING *ValidEdidTiming, + EDID_TIMING *EdidTiming + ) +{ + UINT32 Index; + UINT32 Key; + + Key = CalculateEdidKey (EdidTiming); + + for (Index = 0; Index < ValidEdidTiming->ValidNumber; Index ++) { + if (Key == ValidEdidTiming->Key[Index]) { + return TRUE; + } + } + + return FALSE; +} + +/** + Parse the Established Timing and Standard Timing in EDID data block. + + @param EdidBuffer Pointer to EDID data block + @param ValidEdidTiming Valid EDID timing information + + @retval TRUE The EDID data is valid. + @retval FALSE The EDID data is invalid. + +**/ +BOOLEAN +ParseEdidData ( + UINT8 *EdidBuffer, + VALID_EDID_TIMING *ValidEdidTiming + ) +{ + UINT8 CheckSum; + UINT32 Index; + UINT32 ValidNumber; + UINT32 TimingBits; + UINT8 *BufferIndex; + UINT16 HorizontalResolution; + UINT16 VerticalResolution; + UINT8 AspectRatio; + UINT8 RefreshRate; + EDID_TIMING TempTiming; + EDID_BLOCK *EdidDataBlock; + + EdidDataBlock = (EDID_BLOCK *) EdidBuffer; + + // + // Check the checksum of EDID data + // + CheckSum = 0; + for (Index = 0; Index < EDID_BLOCK_SIZE; Index ++) { + CheckSum = (UINT8) (CheckSum + EdidBuffer[Index]); + } + if (CheckSum != 0) { + return FALSE; + } + + ValidNumber = 0; + SetMem (ValidEdidTiming, sizeof (VALID_EDID_TIMING), 0); + + if ((EdidDataBlock->EstablishedTimings[0] != 0) || + (EdidDataBlock->EstablishedTimings[1] != 0) || + (EdidDataBlock->EstablishedTimings[2] != 0) + ) { + // + // Established timing data + // + TimingBits = EdidDataBlock->EstablishedTimings[0] | + (EdidDataBlock->EstablishedTimings[1] << 8) | + ((EdidDataBlock->EstablishedTimings[2] & 0x80) << 9) ; + for (Index = 0; Index < VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER; Index ++) { + if (TimingBits & 0x1) { + ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&mVbeEstablishedEdidTiming[Index]); + ValidNumber ++; + } + TimingBits = TimingBits >> 1; + } + } else { + // + // If no Established timing data, read the standard timing data + // + BufferIndex = &EdidDataBlock->StandardTimingIdentification[0]; + for (Index = 0; Index < 8; Index ++) { + if ((BufferIndex[0] != 0x1) && (BufferIndex[1] != 0x1)){ + // + // A valid Standard Timing + // + HorizontalResolution = (UINT16) (BufferIndex[0] * 8 + 248); + AspectRatio = (UINT8) (BufferIndex[1] >> 6); + switch (AspectRatio) { + case 0: + VerticalResolution = (UINT16) (HorizontalResolution / 16 * 10); + break; + case 1: + VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3); + break; + case 2: + VerticalResolution = (UINT16) (HorizontalResolution / 5 * 4); + break; + case 3: + VerticalResolution = (UINT16) (HorizontalResolution / 16 * 9); + break; + default: + VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3); + break; + } + RefreshRate = (UINT8) ((BufferIndex[1] & 0x1f) + 60); + TempTiming.HorizontalResolution = HorizontalResolution; + TempTiming.VerticalResolution = VerticalResolution; + TempTiming.RefreshRate = RefreshRate; + ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming); + ValidNumber ++; + } + BufferIndex += 2; + } + } + + ValidEdidTiming->ValidNumber = ValidNumber; + return TRUE; +} + +static uint16_t in_word(uint16_t port, uint16_t addr) +{ + ASMOutU16(port, addr); + return ASMInU16(port); +} + +static EFI_STATUS VBoxVgaVideoModeInitExtra(void) +{ + UINT16 w, cur_info_ofs, vmode, xres, yres; + UINTN Index; + VBOX_VGA_VIDEO_MODES *VideoMode; + + // Read and check the VBE Extra Data magic + w = in_word(VBE_EXTRA_PORT, 0); + if (w != VBEHEADER_MAGIC) { + DEBUG((DEBUG_INFO, "%a:%d could not find VBE magic, got %x\n", __FILE__, __LINE__, w)); + return EFI_NOT_FOUND; + } + + cur_info_ofs = sizeof(VBEHeader); + + Index = VBoxVgaVideoModeCount - 16; + VideoMode = &VBoxVgaVideoModes[Index]; + vmode = in_word(VBE_EXTRA_PORT, cur_info_ofs + OFFSET_OF(ModeInfoListItem, mode)); + while (vmode != VBE_VESA_MODE_END_OF_LIST) + { + xres = in_word(VBE_EXTRA_PORT, cur_info_ofs + OFFSET_OF(ModeInfoListItem, info.XResolution)); + yres = in_word(VBE_EXTRA_PORT, cur_info_ofs + OFFSET_OF(ModeInfoListItem, info.YResolution)); + + if (vmode >= VBE_VBOX_MODE_CUSTOM1 && vmode <= VBE_VBOX_MODE_CUSTOM16 && xres && yres && Index < VBoxVgaVideoModeCount) { + VideoMode->Width = xres; + VideoMode->Height = yres; + VideoMode->ColorDepth = 32; + VideoMode->RefreshRate = 60; + VideoMode->MiscSetting = 0x01; + VideoMode++; + Index++; + } + + cur_info_ofs += sizeof(ModeInfoListItem); + vmode = in_word(VBE_EXTRA_PORT, cur_info_ofs + OFFSET_OF(ModeInfoListItem, mode)); + } + return EFI_SUCCESS; +} + +/** + Construct the valid video modes for VBoxVga. + +**/ +EFI_STATUS +VBoxVgaVideoModeSetup ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT32 Index; + BOOLEAN EdidFound; + EFI_EDID_OVERRIDE_PROTOCOL *EdidOverride; + UINT32 EdidAttributes; + BOOLEAN EdidOverrideFound; + UINTN EdidOverrideDataSize; + UINT8 *EdidOverrideDataBlock; + UINTN EdidDiscoveredDataSize; + UINT8 *EdidDiscoveredDataBlock; + UINTN EdidActiveDataSize; + UINT8 *EdidActiveDataBlock; + VALID_EDID_TIMING ValidEdidTiming; + UINT32 ValidModeCount; + VBOX_VGA_MODE_DATA *ModeData; + BOOLEAN TimingMatch; + const VBOX_VGA_VIDEO_MODES *VideoMode; + EDID_TIMING TempTiming; + + // + // setup EDID information + // + Private->EdidDiscovered.Edid = NULL; + Private->EdidDiscovered.SizeOfEdid = 0; + Private->EdidActive.Edid = NULL; + Private->EdidActive.SizeOfEdid = 0; + + EdidFound = FALSE; + EdidOverrideFound = FALSE; + EdidAttributes = 0xff; + EdidOverrideDataSize = 0; + EdidOverrideDataBlock = NULL; + EdidActiveDataSize = 0; + EdidActiveDataBlock = NULL; + EdidDiscoveredDataBlock = NULL; + + // + // Find EDID Override protocol firstly, this protocol is installed by platform if needed. + // + Status = gBS->LocateProtocol ( + &gEfiEdidOverrideProtocolGuid, + NULL, + (VOID **) &EdidOverride + ); + if (!EFI_ERROR (Status)) { + // + // Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow + // + EdidOverrideDataBlock = AllocatePool (sizeof (EDID_BLOCK_SIZE * 2)); + if (NULL == EdidOverrideDataBlock) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = EdidOverride->GetEdid ( + EdidOverride, + Private->Handle, + &EdidAttributes, + &EdidOverrideDataSize, + (UINT8 **) &EdidOverrideDataBlock + ); + if (!EFI_ERROR (Status) && + EdidAttributes == 0 && + EdidOverrideDataSize != 0) { + // + // Succeeded to get EDID Override Data + // + EdidOverrideFound = TRUE; + } + } + + if (EdidOverrideFound != TRUE || EdidAttributes == EFI_EDID_OVERRIDE_DONT_OVERRIDE) { + // + // If EDID Override data doesn't exist or EFI_EDID_OVERRIDE_DONT_OVERRIDE returned, + // read EDID information through I2C Bus + // + if (ReadEdidData (Private, &EdidDiscoveredDataBlock, &EdidDiscoveredDataSize) == EFI_SUCCESS) { + Private->EdidDiscovered.SizeOfEdid = (UINT32) EdidDiscoveredDataSize; + Private->EdidDiscovered.Edid = (UINT8 *) AllocateCopyPool ( + EdidDiscoveredDataSize, + EdidDiscoveredDataBlock + ); + + if (NULL == Private->EdidDiscovered.Edid) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + EdidActiveDataSize = Private->EdidDiscovered.SizeOfEdid; + EdidActiveDataBlock = Private->EdidDiscovered.Edid; + + EdidFound = TRUE; + } + } + + if (EdidFound != TRUE && EdidOverrideFound == TRUE) { + EdidActiveDataSize = EdidOverrideDataSize; + EdidActiveDataBlock = EdidOverrideDataBlock; + EdidFound = TRUE; + } + + if (EdidFound == TRUE) { + // + // Parse EDID data structure to retrieve modes supported by monitor + // + if (ParseEdidData ((UINT8 *) EdidActiveDataBlock, &ValidEdidTiming) == TRUE) { + // + // Copy EDID Override Data to EDID Active Data + // + Private->EdidActive.SizeOfEdid = (UINT32) EdidActiveDataSize; + Private->EdidActive.Edid = (UINT8 *) AllocateCopyPool ( + EdidActiveDataSize, + EdidActiveDataBlock + ); + if (NULL == Private->EdidActive.Edid) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + } + } else { + Private->EdidActive.SizeOfEdid = 0; + Private->EdidActive.Edid = NULL; + EdidFound = FALSE; + } + + if (EdidFound && 0) { + // + // Initialize the private mode data with the supported modes. + // + ValidModeCount = 0; + Private->ModeData = AllocatePool(sizeof(VBOX_VGA_MODE_DATA) * VBoxVgaVideoModeCount); + ModeData = &Private->ModeData[0]; + VideoMode = &VBoxVgaVideoModes[0]; + for (Index = 0; Index < VBoxVgaVideoModeCount; Index++) { + + TimingMatch = TRUE; + + // + // Check whether match with VBoxVga video mode + // + TempTiming.HorizontalResolution = (UINT16) VideoMode->Width; + TempTiming.VerticalResolution = (UINT16) VideoMode->Height; + TempTiming.RefreshRate = (UINT16) VideoMode->RefreshRate; + if (SearchEdidTiming (&ValidEdidTiming, &TempTiming) != TRUE) { + TimingMatch = FALSE; + } + + // + // Not export Mode 0x0 as GOP mode, this is not defined in spec. + // + if ((VideoMode->Width == 0) || (VideoMode->Height == 0)) { + TimingMatch = FALSE; + } + + // + // Check whether the mode would be exceeding the VRAM size. + // + if (VideoMode->Width * VideoMode->Height * (VideoMode->ColorDepth / 8) > Private->VRAMSize) { + TimingMatch = FALSE; + } + + if (TimingMatch) { + ModeData->ModeNumber = Index; + ModeData->HorizontalResolution = VideoMode->Width; + ModeData->VerticalResolution = VideoMode->Height; + ModeData->ColorDepth = VideoMode->ColorDepth; + ModeData->RefreshRate = VideoMode->RefreshRate; + + ModeData ++; + ValidModeCount ++; + } + + VideoMode ++; + } + } else { + // + // If EDID information wasn't found + // + VBoxVgaVideoModeInitExtra(); + ValidModeCount = 0; + Private->ModeData = AllocatePool(sizeof(VBOX_VGA_MODE_DATA) * VBoxVgaVideoModeCount); + ModeData = &Private->ModeData[0]; + VideoMode = &VBoxVgaVideoModes[0]; + for (Index = 0; Index < VBoxVgaVideoModeCount; Index ++) { + + TimingMatch = TRUE; + + // + // Not export Mode 0x0 as GOP mode, this is not defined in spec. + // + if ((VideoMode->Width == 0) || (VideoMode->Height == 0)) { + TimingMatch = FALSE; + } + + // + // Check whether the mode would be exceeding the VRAM size. + // + if (VideoMode->Width * VideoMode->Height * (VideoMode->ColorDepth / 8) > Private->VRAMSize) { + TimingMatch = FALSE; + } + + if (TimingMatch) { + ModeData->ModeNumber = Index; + ModeData->HorizontalResolution = VideoMode->Width; + ModeData->VerticalResolution = VideoMode->Height; + ModeData->ColorDepth = VideoMode->ColorDepth; + ModeData->RefreshRate = VideoMode->RefreshRate; + + ModeData ++; + ValidModeCount ++; + } + + VideoMode ++; + } + } + + // Sort list of video modes (keeping duplicates) by increasing X, then Y, + // then the mode number. This way the custom modes are not overriding the + // default modes if they are for the same resolution. + ModeData = &Private->ModeData[0]; + for (Index = 0; Index < ValidModeCount - 1; Index ++) { + UINT32 Index2; + VBOX_VGA_MODE_DATA *ModeData2 = ModeData + 1; + for (Index2 = Index + 1; Index2 < ValidModeCount; Index2 ++) { + if ( ModeData->HorizontalResolution > ModeData2->HorizontalResolution + || ( ModeData->HorizontalResolution == ModeData2->HorizontalResolution + && ModeData->VerticalResolution > ModeData2->VerticalResolution) + || ( ModeData->HorizontalResolution == ModeData2->HorizontalResolution + && ModeData->VerticalResolution == ModeData2->VerticalResolution + && ModeData->ModeNumber > ModeData2->ModeNumber)) { + VBOX_VGA_MODE_DATA Tmp; + CopyMem(&Tmp, ModeData, sizeof(Tmp)); + CopyMem(ModeData, ModeData2, sizeof(Tmp)); + CopyMem(ModeData2, &Tmp, sizeof(Tmp)); + DEBUG((DEBUG_INFO, "%a:%d swapped mode entries %d and %d\n", __FILE__, __LINE__, Index, Index2)); + } + ModeData2++; + } + ModeData++; + } + + // dump mode list for debugging purposes + ModeData = &Private->ModeData[0]; + for (Index = 0; Index < ValidModeCount; Index ++) { + DEBUG((DEBUG_INFO, "%a:%d mode %d: %dx%d mode number %d\n", __FILE__, __LINE__, Index, ModeData->HorizontalResolution, ModeData->VerticalResolution, ModeData->ModeNumber)); + ModeData++; + } + + Private->MaxMode = ValidModeCount; + + if (EdidOverrideDataBlock != NULL) { + FreePool (EdidOverrideDataBlock); + } + + return EFI_SUCCESS; + +Done: + if (EdidOverrideDataBlock != NULL) { + FreePool (EdidOverrideDataBlock); + } + if (Private->EdidDiscovered.Edid != NULL) { + FreePool (Private->EdidDiscovered.Edid); + } + if (Private->EdidDiscovered.Edid != NULL) { + FreePool (Private->EdidActive.Edid); + } + + return EFI_DEVICE_ERROR; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.c new file mode 100644 index 00000000..3d97fa94 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.c @@ -0,0 +1,1171 @@ +/* $Id: VBoxVga.c $ */ +/** @file + * VBoxVga.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Cirrus Logic 5430 Controller Driver. + This driver is a sample implementation of the UGA Draw and Graphics Output + Protocols for the Cirrus Logic 5430 family of PCI video controllers. + This driver is only usable in the EFI pre-boot environment. + This sample is intended to show how the UGA Draw and Graphics output Protocol + is able to function. + The UGA I/O Protocol is not implemented in this sample. + A fully compliant EFI UGA driver requires both + the UGA Draw and the UGA I/O Protocol. Please refer to Microsoft's + documentation on UGA for details on how to write a UGA driver that is able + to function both in the EFI pre-boot environment and from the OS runtime. + + Copyright (c) 2006 - 2009, Intel Corporation + All rights reserved. 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. + +*/ + +// +// VirtualBox VGA Controller Driver +// +#include "VBoxVga.h" +#include <IndustryStandard/Acpi.h> +#include "iprt/asm.h" + + +#define BOUTB(storage, count, aport, dport) \ + do { \ + for (i = 0 ; i < (count); ++i) \ + if ((dport) == (aport) + 1) \ + ASMOutU16((aport), ((UINT16)storage[i] << 8) | (UINT8)i); \ + else { \ + ASMOutU8((aport), (UINT8)i); \ + ASMOutU8((dport), storage[i]); \ + } \ + } while (0) + + + +EFI_DRIVER_BINDING_PROTOCOL gVBoxVgaDriverBinding = { + VBoxVgaControllerDriverSupported, + VBoxVgaControllerDriverStart, + VBoxVgaControllerDriverStop, + 0x10, + NULL, + NULL +}; + +/// +/// Generic Attribute Controller Register Settings +/// +UINT8 AttributeController[21] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x41, 0x00, 0x0F, 0x00, 0x00 +}; + +/// +/// Generic Graphics Controller Register Settings +/// +UINT8 GraphicsController[9] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xff +}; + +/// +/// Generic Graphics Controller Sequencer Register Settings +/// +UINT8 Seq_Default[5] = { + 0x01, 0x01, 0x0f, 0x00, 0x0a +}; + +#if 0 // CRTC tables not used (and not checked for correctness), as VBE is much simpler +// +// 640 x 480 x 256 color @ 60 Hertz +// +UINT8 Crtc_640_480_256_60[25] = { + /* r0 = */0x5f, /* r1 = */0x4f, /* r2 = */0x50, /* r3 = */0x82, + /* r4 = */0x54, /* r5 = */0x80, /* r6 = */0x0b, /* r7 = */0x3e, + /* r8 = */0x00, /* r9 = */0x40, /* r10 = */0x00, /* r11 = */0x00, + /* r12 = */0x00, /* r13 = */0x00, /* r14 = */0x00, /* r15 = */0x00, + /* r16 = */0xea, /* r17 = */0x0c, /* r18 = */0xdf, /* r19 = */0x28, + /* r20 = */0x4f, /* r21 = */0xe7, /* r22 = */0x04, /* r23 = */0xe3, + /* r24 = */0xff +}; + +// +// 800 x 600 x 256 color @ 60 Hertz +// +UINT8 Crtc_800_600_256_60[25] = { + /* r0 = */0x7f, /* r1 = */0x63, /* r2 = */0x64, /* r3 = */0x82, + /* r4 = */0x6b, /* r5 = */0x80, /* r6 = */0x0b, /* r7 = */0x3e, + /* r8 = */0x00, /* r9 = */0x60, /* r10 = */0x00, /* r11 = */0x00, + /* r12 = */0x00, /* r13 = */0x00, /* r14 = */0x00, /* r15 = */0x00, + /* r16 = */0xea, /* r17 = */0x0c, /* r18 = */0xdf, /* r19 = */0x28, + /* r20 = */0x4f, /* r21 = */0xe7, /* r22 = */0x04, /* r23 = */0xe3, + /* r24 = */0xff + +}; + +// +// 1024 x 768 x 256 color @ 60 Hertz +// +UINT8 Crtc_1024_768_256_60[25] = { + /* r0 = */0xa3, /* r1 = */0x7f, /* r2 = */0x81, /* r3 = */0x90, + /* r4 = */0x88, /* r5 = */0x05, /* r6 = */0x28, /* r7 = */0xfd, + /* r8 = */0x00, /* r9 = */0x60, /* r10 = */0x00, /* r11 = */0x00, + /* r12 = */0x00, /* r13 = */0x00, /* r14 = */0x00, /* r15 = */0x00, + /* r16 = */0x06, /* r17 = */0x0f, /* r18 = */0xff, /* r19 = */0x40, + /* r20 = */0x4f, /* r21 = */0x05, /* r22 = */0x1a, /* r23 = */0xe3, + /* r24 = */0xff +}; +#endif + +/// +/// Table of supported video modes (sorted by increasing horizontal, then by +/// increasing vertical resolution) +/// +VBOX_VGA_VIDEO_MODES VBoxVgaVideoModes[] = +{ + { 640, 480, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // VGA 4:3 + { 800, 600, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // SVGA 4:3 + { 1024, 768, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // XGA 4:3 + { 1152, 864, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // XGA+ 4:3 + { 1280, 720, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // HD 16:9 + { 1280, 800, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WXGA 16:10 + { 1280, 1024, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // SXGA 5:4 + { 1400, 1050, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // SXGA+ 4:3 + { 1440, 900, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WXGA+ 16:10 + { 1600, 900, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // HD+ 16:9 + { 1600, 1200, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // UXGA 4:3 + { 1680, 1050, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WSXGA+ 16:10 + { 1920, 1080, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // FHD 16:9 + { 1920, 1200, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WUXGA 16:10 + { 2048, 1080, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // DCI_2K 19:10 + { 2160, 1440, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // FHD+ 3:2 + { 2304, 1440, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // unnamed 16:10 + { 2560, 1440, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // QHD 16:9 + { 2560, 1600, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WQXGA 16:10 + { 2880, 1800, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // QWXGA+ 16:10 + { 3200, 1800, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // QHD+ 16:9 + { 3200, 2048, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WQSXGA 16:10 + { 3840, 2160, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // 4K_UHD 16:9 + { 3840, 2400, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WQUXGA 16:10 + { 4096, 2160, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // DCI_4K 19:10 + { 4096, 3072, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // HXGA 4:3 + { 5120, 2880, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // UHD+ 16:9 + { 5120, 3200, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WHXGA 16:10 + { 6400, 4096, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // WHSXGA 16:10 + { 6400, 4800, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // HUXGA 4:3 + { 7680, 4320, 32, 60, NULL /* crtc */, NULL /* sequencer */, 0x01 }, // 8K_UHD2 16:9 + { 0, }, // Custom video mode 0, do not delete, must be at the end! + { 0, }, // Custom video mode 1, do not delete, must be at the end! + { 0, }, // Custom video mode 2, do not delete, must be at the end! + { 0, }, // Custom video mode 3, do not delete, must be at the end! + { 0, }, // Custom video mode 4, do not delete, must be at the end! + { 0, }, // Custom video mode 5, do not delete, must be at the end! + { 0, }, // Custom video mode 6, do not delete, must be at the end! + { 0, }, // Custom video mode 7, do not delete, must be at the end! + { 0, }, // Custom video mode 8, do not delete, must be at the end! + { 0, }, // Custom video mode 9, do not delete, must be at the end! + { 0, }, // Custom video mode 10, do not delete, must be at the end! + { 0, }, // Custom video mode 11, do not delete, must be at the end! + { 0, }, // Custom video mode 12, do not delete, must be at the end! + { 0, }, // Custom video mode 13, do not delete, must be at the end! + { 0, }, // Custom video mode 14, do not delete, must be at the end! + { 0, } // Custom video mode 15, do not delete, must be at the end! +}; + +const UINT32 VBoxVgaVideoModeCount = sizeof(VBoxVgaVideoModes) / sizeof(VBoxVgaVideoModes[0]); + +typedef struct _APPLE_FRAMEBUFFERINFO_PROTOCOL APPLE_FRAMEBUFFERINFO_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *APPLE_FRAMEBUFFERINFO_PROTOCOL_GET_INFO) ( + IN APPLE_FRAMEBUFFERINFO_PROTOCOL *This, + OUT UINT32 *BaseAddr, + OUT UINT32 *Something, + OUT UINT32 *RowBytes, + OUT UINT32 *Width, + OUT UINT32 *Height, + OUT UINT32 *Depth); + +struct _APPLE_FRAMEBUFFERINFO_PROTOCOL { + APPLE_FRAMEBUFFERINFO_PROTOCOL_GET_INFO GetInfo; + VBOX_VGA_PRIVATE_DATA *Private; +}; + +EFI_STATUS EFIAPI +GetFrameBufferInfo(IN APPLE_FRAMEBUFFERINFO_PROTOCOL *This, + OUT UINT32 *BaseAddr, + OUT UINT32 *Something, + OUT UINT32 *RowBytes, + OUT UINT32 *Width, + OUT UINT32 *Height, + OUT UINT32 *Depth); + +static APPLE_FRAMEBUFFERINFO_PROTOCOL gAppleFrameBufferInfo = +{ + GetFrameBufferInfo, + NULL +}; + + +/* + * @todo move this function to the library. + */ +UINT32 VBoxVgaGetVmVariable(UINT32 Variable, CHAR8* Buffer, UINT32 Size) +{ + UINT32 VarLen, i; + + ASMOutU32(EFI_INFO_PORT, Variable); + VarLen = ASMInU32(EFI_INFO_PORT); + + for (i = 0; i < VarLen && i < Size; i++) + Buffer[i] = ASMInU8(EFI_INFO_PORT); + + return VarLen; +} + + +/** + VBoxVgaControllerDriverSupported + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: RemainingDevicePath - add argument and description to function comment +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverSupported ( + 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; + EFI_DEV_PATH *Node; + + // + // Open the PCI I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + return Status; + } + + // + // Read the PCI Configuration Header from the PCI Device + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + goto Done; + } + + Status = EFI_UNSUPPORTED; + // + // See if the I/O enable is on. Most systems only allow one VGA device to be turned on + // at a time, so see if this is one that is turned on. + // + // if (((Pci.Hdr.Command & 0x01) == 0x01)) { + // + // See if this is a VirtualBox VGA or VMSVGA II PCI controller + // + if ( ((Pci.Hdr.VendorId == VBOX_VENDOR_ID) && (Pci.Hdr.DeviceId == VBOX_VGA_DEVICE_ID)) + || ((Pci.Hdr.VendorId == VMSVGA_VENDOR_ID) && (Pci.Hdr.DeviceId == VMSVGA_II_DEVICE_ID))) { + + Status = EFI_SUCCESS; + if (RemainingDevicePath != NULL) { + Node = (EFI_DEV_PATH *) RemainingDevicePath; + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, return EFI_SUCCESS + // + if (!IsDevicePathEnd (Node)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + if (Node->DevPath.Type != ACPI_DEVICE_PATH || + Node->DevPath.SubType != ACPI_ADR_DP || + DevicePathNodeLength(&Node->DevPath) != sizeof(ACPI_ADR_DEVICE_PATH)) { + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + Status = EFI_UNSUPPORTED; + } + } + } + } + +Done: + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + return Status; +} + +/** + VBoxVgaControllerDriverStart + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: RemainingDevicePath - add argument and description to function comment +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + VBOX_VGA_PRIVATE_DATA *Private; + BOOLEAN PciAttributesSaved; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + ACPI_ADR_DEVICE_PATH AcpiDeviceNode; + PCI_TYPE00 Pci; + + PciAttributesSaved = FALSE; + // + // Allocate Private context data for UGA Draw interface. + // + Private = AllocateZeroPool (sizeof (VBOX_VGA_PRIVATE_DATA)); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + gAppleFrameBufferInfo.Private = Private; + // + // Set up context record + // + Private->Signature = VBOX_VGA_PRIVATE_DATA_SIGNATURE; + Private->Handle = NULL; + + // + // Open PCI I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &Private->PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Read the PCI Configuration Header from the PCI Device again to figure out the model. + // + Status = Private->PciIo->Pci.Read ( + Private->PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_INFO, "%a:%d status:%r\n", __FILE__, __LINE__, Status)); + goto Error; + } + + Private->DeviceType = Pci.Hdr.DeviceId; + + // + // Save original PCI attributes + // + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationGet, + 0, + &Private->OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + PciAttributesSaved = TRUE; + + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | EFI_PCI_IO_ATTRIBUTE_VGA_IO, + NULL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Get ParentDevicePath + // + Status = gBS->HandleProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (FeaturePcdGet (PcdSupportGop)) { + // + // Set Gop Device Path + // + if (RemainingDevicePath == NULL) { + ZeroMem (&AcpiDeviceNode, sizeof (ACPI_ADR_DEVICE_PATH)); + AcpiDeviceNode.Header.Type = ACPI_DEVICE_PATH; + AcpiDeviceNode.Header.SubType = ACPI_ADR_DP; + AcpiDeviceNode.ADR = ACPI_DISPLAY_ADR (1, 0, 0, 1, 0, ACPI_ADR_DISPLAY_TYPE_VGA, 0, 0); + SetDevicePathNodeLength (&AcpiDeviceNode.Header, sizeof (ACPI_ADR_DEVICE_PATH)); + + Private->GopDevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &AcpiDeviceNode + ); + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // only scan the specified device by RemainingDevicePath + // + Private->GopDevicePath = AppendDevicePathNode (ParentDevicePath, RemainingDevicePath); + } else { + // + // If RemainingDevicePath is the End of Device Path Node, + // don't create child device and return EFI_SUCCESS + // + Private->GopDevicePath = NULL; + } + + if (Private->GopDevicePath != NULL) { + // + // Create child handle and device path protocol first + // + Private->Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiDevicePathProtocolGuid, + Private->GopDevicePath, + NULL + ); + } + } + + // + // Now do some model-specific setup. + // + if (Private->DeviceType == VMSVGA_II_DEVICE_ID) { + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *IOPortDesc; + + // VMSVGA + Private->BarIndexFB = 1; + + Private->PciIo->GetBarAttributes ( + Private->PciIo, + 0, // BAR 0 is the I/O port space + NULL, + (VOID**) &IOPortDesc + ); + Private->IOBase = (UINT16)IOPortDesc->AddrRangeMin; + + // + // Query the VRAM size (for proper mode filtering) + // + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_VRAM_SIZE); + Private->VRAMSize = ASMInU32(Private->IOBase + SVGA_VALUE_PORT); + +#if 0 + // Not used because of buggy emulation(?) which is not fully compatible + // with the simple "legacy" VMSVGA II register interface. + + // Enable the device, set initial mode + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_WIDTH); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 1024); + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_HEIGHT); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 768); + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_BYTES_PER_LINE); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 768 * 4); + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_BITS_PER_PIXEL); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 32); + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_CONFIG_DONE); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, 1); + + ASMOutU32(Private->IOBase + SVGA_INDEX_PORT, SVGA_REG_ENABLE); + ASMOutU32(Private->IOBase + SVGA_VALUE_PORT, SVGA_REG_ENABLE_ENABLE); +#endif + } else { + // VBoxVGA / VBoxSVGA + Private->BarIndexFB = 0; + // + // Get VRAM size, needed for constructing a correct video mode list + // + Private->VRAMSize = ASMInU32(VBE_DISPI_IOPORT_DATA); + } + + + // + // Construct video mode list + // + Status = VBoxVgaVideoModeSetup (Private); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (FeaturePcdGet (PcdSupportUga)) { + // + // Start the UGA Draw software stack. + // + Status = VBoxVgaUgaDrawConstructor (Private); + ASSERT_EFI_ERROR (Status); + + Private->UgaDevicePath = ParentDevicePath; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + //&gEfiUgaDrawProtocolGuid, + //&Private->UgaDraw, + &gEfiDevicePathProtocolGuid, + Private->UgaDevicePath, + NULL + ); + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + NULL + ); + + } else if (FeaturePcdGet (PcdSupportGop)) { + if (Private->GopDevicePath == NULL) { + // + // If RemainingDevicePath is the End of Device Path Node, + // don't create child device and return EFI_SUCCESS + // + Status = EFI_SUCCESS; + } else { + + // + // Start the GOP software stack. + // + Status = VBoxVgaGraphicsOutputConstructor (Private); + ASSERT_EFI_ERROR (Status); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + &gEfiEdidDiscoveredProtocolGuid, + &Private->EdidDiscovered, + &gEfiEdidActiveProtocolGuid, + &Private->EdidActive, + NULL + ); + } + } else { + // + // This driver must support eithor GOP or UGA or both. + // + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + } + +Error: + if (EFI_ERROR (Status)) { + if (Private) { + if (Private->PciIo) { + if (PciAttributesSaved == TRUE) { + // + // Restore original PCI attributes + // + Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSet, + Private->OriginalPciAttributes, + NULL + ); + } + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Private->Handle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Private->Handle + ); + } + + gBS->FreePool (Private); + } + } + + return Status; +} + +/** + VBoxVgaControllerDriverStop + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: NumberOfChildren - add argument and description to function comment + TODO: ChildHandleBuffer - add argument and description to function comment + TODO: EFI_SUCCESS - add return value to function comment +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + + EFI_STATUS Status; + VBOX_VGA_PRIVATE_DATA *Private; + + if (FeaturePcdGet (PcdSupportUga)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiUgaDrawProtocolGuid, + (VOID **) &UgaDraw, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get our private context information + // + Private = VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS (UgaDraw); + VBoxVgaUgaDrawDestructor (Private); + + if (FeaturePcdGet (PcdSupportGop)) { + VBoxVgaGraphicsOutputDestructor (Private); + // + // Remove the UGA and GOP protocol interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + NULL + ); + } else { + // + // Remove the UGA Draw interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + NULL + ); + } + } else { + Status = gBS->OpenProtocol ( + Controller, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &GraphicsOutput, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get our private context information + // + Private = VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (GraphicsOutput); + + VBoxVgaGraphicsOutputDestructor (Private); + // + // Remove the GOP protocol interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + NULL + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Private->ModeData) { + FreePool(Private->ModeData); + Private->ModeData = NULL; + } + + // + // Restore original PCI attributes + // + Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSet, + Private->OriginalPciAttributes, + NULL + ); + + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Free our instance data + // + gBS->FreePool (Private); + + return EFI_SUCCESS; +} + +/** + VBoxVgaUgaDrawDestructor + + TODO: Private - add argument and description to function comment + TODO: EFI_SUCCESS - add return value to function comment +**/ +EFI_STATUS +VBoxVgaUgaDrawDestructor ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + return EFI_SUCCESS; +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param Index TODO: add argument description + @param Red TODO: add argument description + @param Green TODO: add argument description + @param Blue TODO: add argument description + + TODO: add return values + +**/ +VOID +SetPaletteColor ( + VBOX_VGA_PRIVATE_DATA *Private, + UINTN Index, + UINT8 Red, + UINT8 Green, + UINT8 Blue + ) +{ + ASMOutU8(PALETTE_INDEX_REGISTER, (UINT8) Index); + ASMOutU8(PALETTE_DATA_REGISTER, (UINT8) (Red >> 2)); + ASMOutU8(PALETTE_DATA_REGISTER, (UINT8) (Green >> 2)); + ASMOutU8(PALETTE_DATA_REGISTER, (UINT8) (Blue >> 2)); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +SetDefaultPalette ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ +#if 1 + UINTN Index; + UINTN RedIndex; + UINTN GreenIndex; + UINTN BlueIndex; + Index = 0; + for (RedIndex = 0; RedIndex < 8; RedIndex++) { + for (GreenIndex = 0; GreenIndex < 8; GreenIndex++) { + for (BlueIndex = 0; BlueIndex < 4; BlueIndex++) { + SetPaletteColor (Private, Index, (UINT8) (RedIndex << 5), (UINT8) (GreenIndex << 5), (UINT8) (BlueIndex << 6)); + Index++; + } + } + } +#else + { + int i; + static const UINT8 s_a3bVgaDac[64*3] = + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2A, + 0x00, 0x2A, 0x00, + 0x00, 0x2A, 0x2A, + 0x2A, 0x00, 0x00, + 0x2A, 0x00, 0x2A, + 0x2A, 0x2A, 0x00, + 0x2A, 0x2A, 0x2A, + 0x00, 0x00, 0x15, + 0x00, 0x00, 0x3F, + 0x00, 0x2A, 0x15, + 0x00, 0x2A, 0x3F, + 0x2A, 0x00, 0x15, + 0x2A, 0x00, 0x3F, + 0x2A, 0x2A, 0x15, + 0x2A, 0x2A, 0x3F, + 0x00, 0x15, 0x00, + 0x00, 0x15, 0x2A, + 0x00, 0x3F, 0x00, + 0x00, 0x3F, 0x2A, + 0x2A, 0x15, 0x00, + 0x2A, 0x15, 0x2A, + 0x2A, 0x3F, 0x00, + 0x2A, 0x3F, 0x2A, + 0x00, 0x15, 0x15, + 0x00, 0x15, 0x3F, + 0x00, 0x3F, 0x15, + 0x00, 0x3F, 0x3F, + 0x2A, 0x15, 0x15, + 0x2A, 0x15, 0x3F, + 0x2A, 0x3F, 0x15, + 0x2A, 0x3F, 0x3F, + 0x15, 0x00, 0x00, + 0x15, 0x00, 0x2A, + 0x15, 0x2A, 0x00, + 0x15, 0x2A, 0x2A, + 0x3F, 0x00, 0x00, + 0x3F, 0x00, 0x2A, + 0x3F, 0x2A, 0x00, + 0x3F, 0x2A, 0x2A, + 0x15, 0x00, 0x15, + 0x15, 0x00, 0x3F, + 0x15, 0x2A, 0x15, + 0x15, 0x2A, 0x3F, + 0x3F, 0x00, 0x15, + 0x3F, 0x00, 0x3F, + 0x3F, 0x2A, 0x15, + 0x3F, 0x2A, 0x3F, + 0x15, 0x15, 0x00, + 0x15, 0x15, 0x2A, + 0x15, 0x3F, 0x00, + 0x15, 0x3F, 0x2A, + 0x3F, 0x15, 0x00, + 0x3F, 0x15, 0x2A, + 0x3F, 0x3F, 0x00, + 0x3F, 0x3F, 0x2A, + 0x15, 0x15, 0x15, + 0x15, 0x15, 0x3F, + 0x15, 0x3F, 0x15, + 0x15, 0x3F, 0x3F, + 0x3F, 0x15, 0x15, + 0x3F, 0x15, 0x3F, + 0x3F, 0x3F, 0x15, + 0x3F, 0x3F, 0x3F + }; + + for (i = 0; i < 64; ++i) + { + ASMOutU8(PALETTE_INDEX_REGISTER, (UINT8)i); + ASMOutU8(PALETTE_DATA_REGISTER, s_a3bVgaDac[i*3 + 0]); + ASMOutU8(PALETTE_DATA_REGISTER, s_a3bVgaDac[i*3 + 1]); + ASMOutU8(PALETTE_DATA_REGISTER, s_a3bVgaDac[i*3 + 2]); + } + } + +#endif +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +ClearScreen ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL blt; + blt.Blue = 0; + blt.Green = 0; + blt.Red = 0; + blt.Reserved = 0; + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + 0, + Private->ModeData[Private->CurrentMode].HorizontalResolution + * Private->ModeData[Private->CurrentMode].VerticalResolution, + &blt + ); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +DrawLogo ( + VBOX_VGA_PRIVATE_DATA *Private, + UINTN ScreenWidth, + UINTN ScreenHeight + ) +{ + DEBUG((DEBUG_INFO, "UGA is %a GOP is %a\n", + FeaturePcdGet(PcdSupportUga) ? "on" : "off", + FeaturePcdGet(PcdSupportGop) ? "on" : "off" + )); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param ModeData TODO: add argument description + + TODO: add return values + +**/ +VOID +InitializeGraphicsMode ( + VBOX_VGA_PRIVATE_DATA *Private, + VBOX_VGA_VIDEO_MODES *ModeData + ) +{ + UINT16 DeviceId; + EFI_STATUS Status; + int i; + + DEBUG((DEBUG_INFO, "%a:%d InitializeGraphicsMode: %dx%d bpp:%d\n", __FILE__, __LINE__, ModeData->Width, ModeData->Height, ModeData->ColorDepth)); + + // + // Read the PCI ID from the PCI Device (dummy) + // + Status = Private->PciIo->Pci.Read ( + Private->PciIo, + EfiPciIoWidthUint16, + PCI_DEVICE_ID_OFFSET, + 1, + &DeviceId + ); + ASSERT_EFI_ERROR(Status); + + ASMOutU8(MISC_OUTPUT_REGISTER, 0xc3); + ASMOutU16(SEQ_ADDRESS_REGISTER, 0x0204); + + ASMInU8(INPUT_STATUS_1_REGISTER); // reset attribute address/data flip-flop + ASMOutU8(ATT_ADDRESS_REGISTER, 0); // blank screen using the attribute address register + + ASMOutU16(CRTC_ADDRESS_REGISTER, 0x0011); + + ASMOutU16(SEQ_ADDRESS_REGISTER, 0x0100); + if (ModeData->SeqSettings) + BOUTB(ModeData->SeqSettings, 5, SEQ_ADDRESS_REGISTER, SEQ_DATA_REGISTER); + else + BOUTB(Seq_Default, 5, SEQ_ADDRESS_REGISTER, SEQ_DATA_REGISTER); + ASMOutU16(SEQ_ADDRESS_REGISTER, 0x0300); + + BOUTB(GraphicsController, 9, GRAPH_ADDRESS_REGISTER, GRAPH_DATA_REGISTER); + + ASMInU8(INPUT_STATUS_1_REGISTER); // reset attribute address/data flip-flop + BOUTB(AttributeController, 21, ATT_ADDRESS_REGISTER, ATT_DATA_REGISTER); + + ASMOutU8(MISC_OUTPUT_REGISTER, ModeData->MiscSetting); + + if (ModeData->ColorDepth <= 8) + { + ASMOutU8(DAC_PIXEL_MASK_REGISTER, 0xff); + SetDefaultPalette(Private); + } + + if (!ModeData->CrtcSettings) + { + // No CRTC settings, use VBE + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x00); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0xb0c0); // ID + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x04); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0); // ENABLE + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x01); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->Width); // XRES + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x02); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->Height); // YRES + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x03); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->ColorDepth); // BPP + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x05); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0); // BANK + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x06); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->Width); // VIRT_WIDTH + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x07); ASMOutU16(VBE_DISPI_IOPORT_DATA, (UINT16)ModeData->Height); // VIRT_HEIGHT + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x08); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0); // X_OFFSET + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x09); ASMOutU16(VBE_DISPI_IOPORT_DATA, 0); // Y_OFFSET + ASMOutU16(VBE_DISPI_IOPORT_INDEX, 0x04); ASMOutU16(VBE_DISPI_IOPORT_DATA, 1); // ENABLE + /// @todo enabling VBE is automatically tweaking the CRTC, GC, SC, clears the + // screen and at the end unblanks graphics. So make sure that nothing is done + // after this which needs blanking. Way too much magic, but that's how it is... + } + else + { + BOUTB(ModeData->CrtcSettings, 25, CRTC_ADDRESS_REGISTER, CRTC_DATA_REGISTER); + } + + ASMInU8(INPUT_STATUS_1_REGISTER); // reset attribute address/data flip-flop + ASMOutU8(ATT_ADDRESS_REGISTER, 0x20); // unblank screen + + ClearScreen(Private); +} + +/** Aka know as AppleGraphInfoProtocolGuid in other sources. */ +#define EFI_UNKNOWN_2_PROTOCOL_GUID \ + { 0xE316E100, 0x0751, 0x4C49, {0x90, 0x56, 0x48, 0x6C, 0x7E, 0x47, 0x29, 0x03} } + +EFI_GUID gEfiAppleFrameBufferInfoGuid = EFI_UNKNOWN_2_PROTOCOL_GUID; + +EFI_STATUS EFIAPI +GetFrameBufferInfo(IN APPLE_FRAMEBUFFERINFO_PROTOCOL *This, + OUT UINT32 *BaseAddr, + OUT UINT32 *Something, + OUT UINT32 *RowBytes, + OUT UINT32 *Width, + OUT UINT32 *Height, + OUT UINT32 *Depth) +{ + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FrameBufDesc; + UINT32 W, H, BPP; + VBOX_VGA_PRIVATE_DATA *Private = This->Private; + UINTN CurrentModeNumber = Private->CurrentMode; + VBOX_VGA_MODE_DATA const *pCurrentMode = &Private->ModeData[CurrentModeNumber]; + + W = pCurrentMode->HorizontalResolution; + H = pCurrentMode->VerticalResolution; + BPP = pCurrentMode->ColorDepth; + DEBUG((DEBUG_INFO, "%a:%d GetFrameBufferInfo: %dx%d bpp:%d\n", __FILE__, __LINE__, W, H, BPP)); + + Private->PciIo->GetBarAttributes ( + Private->PciIo, + Private->BarIndexFB, + NULL, + (VOID**) &FrameBufDesc + ); + + + /* EFI firmware remaps it here */ + *BaseAddr = (UINT32)FrameBufDesc->AddrRangeMin; + *RowBytes = W * BPP / 8; + *Width = W; + *Height = H; + *Depth = BPP; + // what *Something shall be? + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +InitializeVBoxVga ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gVBoxVgaDriverBinding, + ImageHandle, + &gVBoxVgaComponentName, + &gVBoxVgaComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + // + // Install EFI Driver Supported EFI Version Protocol required for + // EFI drivers that are on PCI and other plug in cards. + // + gVBoxVgaDriverSupportedEfiVersion.FirmwareVersion = PcdGet32 (PcdDriverSupportedEfiVersion); + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiDriverSupportedEfiVersionProtocolGuid, + &gVBoxVgaDriverSupportedEfiVersion, + &gEfiAppleFrameBufferInfoGuid, + &gAppleFrameBufferInfo, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.h new file mode 100644 index 00000000..25ff1a4e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVga.h @@ -0,0 +1,473 @@ +/* $Id: VBoxVga.h $ */ +/** @file + * VBoxVga.h + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + Cirrus Logic 5430 Controller Driver + + Copyright (c) 2006 - 2007, Intel Corporation + All rights reserved. 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. + +*/ + +// +// VirtualBox VGA Controller Driver +// + +#ifndef _VBOX_VGA_H_ +#define _VBOX_VGA_H_ + + +#include <Uefi.h> +#include <Protocol/UgaDraw.h> +#include <Protocol/GraphicsOutput.h> +#include <Protocol/PciIo.h> +#include <Protocol/DriverSupportedEfiVersion.h> +#include <Protocol/EdidOverride.h> +#include <Protocol/EdidDiscovered.h> +#include <Protocol/EdidActive.h> +#include <Protocol/DevicePath.h> + +#include <Library/DebugLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiLib.h> +#include <Library/PcdLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DevicePathLib.h> +#include <Library/TimerLib.h> + +#include <IndustryStandard/Pci.h> + +#include "VBoxPkg.h" +#include "DevEFI.h" +#include "VBox/Graphics/VBoxVideoVBE.h" +#include "VBox/Graphics/VBoxVideoVBEPrivate.h" + +// +// VirtualBox VGA PCI Configuration Header values +// +#define VBOX_VENDOR_ID 0x80ee +#define VBOX_VGA_DEVICE_ID 0xbeef + + +// +// VMSVGA II PCI Configuration Header values +// +#define VMSVGA_VENDOR_ID 0x15ad +#define VMSVGA_II_DEVICE_ID 0x0405 + +// Port offsets relative to BAR 0 +#define SVGA_INDEX_PORT 0 +#define SVGA_VALUE_PORT 1 + +// SVGA_REG_ENABLE bits +#define SVGA_REG_ENABLE_DISABLE 0 +#define SVGA_REG_ENABLE_ENABLE 1 + +// Registers +#define SVGA_REG_ENABLE 1 +#define SVGA_REG_WIDTH 2 +#define SVGA_REG_HEIGHT 3 +#define SVGA_REG_MAX_WIDTH 4 +#define SVGA_REG_MAX_HEIGHT 5 +#define SVGA_REG_DEPTH 6 +#define SVGA_REG_BITS_PER_PIXEL 7 +#define SVGA_REG_BYTES_PER_LINE 12 +#define SVGA_REG_FB_START 13 +#define SVGA_REG_FB_OFFSET 14 +#define SVGA_REG_VRAM_SIZE 15 +#define SVGA_REG_CONFIG_DONE 20 ///@todo: Why do we need this? + +// +// VirtualBox VGA Graphical Mode Data +// +typedef struct { + UINT32 ModeNumber; + UINT32 HorizontalResolution; + UINT32 VerticalResolution; + UINT32 ColorDepth; + UINT32 RefreshRate; +} VBOX_VGA_MODE_DATA; + +#define GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER 0xffff + +// +// VirtualBox VGA Private Data Structure +// +#define VBOX_VGA_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('V', 'B', 'V', 'D') + +typedef struct { + UINT64 Signature; + EFI_HANDLE Handle; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 OriginalPciAttributes; + EFI_UGA_DRAW_PROTOCOL UgaDraw; + EFI_GRAPHICS_OUTPUT_PROTOCOL GraphicsOutput; + EFI_EDID_DISCOVERED_PROTOCOL EdidDiscovered; + EFI_EDID_ACTIVE_PROTOCOL EdidActive; + EFI_DEVICE_PATH_PROTOCOL *GopDevicePath; + EFI_DEVICE_PATH_PROTOCOL *UgaDevicePath; + UINTN CurrentMode; + UINTN MaxMode; + VBOX_VGA_MODE_DATA *ModeData; + BOOLEAN HardwareNeedsStarting; + UINT8 BarIndexFB; + UINT16 DeviceType; + UINT16 IOBase; + UINT32 VRAMSize; +} VBOX_VGA_PRIVATE_DATA; + +/// +/// Video Mode structure +/// +typedef struct { + UINT32 Width; + UINT32 Height; + UINT32 ColorDepth; + UINT32 RefreshRate; + /// CRTC settings are optional. If NULL then VBE is used + UINT8 *CrtcSettings; + /// Sequencer settings are optional. If NULL then defaults are used + UINT8 *SeqSettings; + UINT8 MiscSetting; +} VBOX_VGA_VIDEO_MODES; + +#define VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS(a) \ + CR(a, VBOX_VGA_PRIVATE_DATA, UgaDraw, VBOX_VGA_PRIVATE_DATA_SIGNATURE) + +#define VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS(a) \ + CR(a, VBOX_VGA_PRIVATE_DATA, GraphicsOutput, VBOX_VGA_PRIVATE_DATA_SIGNATURE) + + +// +// Global Variables +// +extern UINT8 AttributeController[]; +extern UINT8 GraphicsController[]; +extern UINT8 Crtc_640_480_256_60[]; +extern UINT8 Seq_640_480_256_60[]; +extern UINT8 Crtc_800_600_256_60[]; +extern UINT8 Seq_800_600_256_60[]; +extern UINT8 Crtc_1024_768_256_60[]; +extern UINT8 Seq_1024_768_256_60[]; +extern VBOX_VGA_VIDEO_MODES VBoxVgaVideoModes[]; +extern const UINT32 VBoxVgaVideoModeCount; +extern EFI_DRIVER_BINDING_PROTOCOL gVBoxVgaDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gVBoxVgaComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gVBoxVgaComponentName2; +extern EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gVBoxVgaDriverSupportedEfiVersion; + +// +// Io Registers defined by VGA +// +#define CRTC_ADDRESS_REGISTER 0x3d4 +#define CRTC_DATA_REGISTER 0x3d5 +#define SEQ_ADDRESS_REGISTER 0x3c4 +#define SEQ_DATA_REGISTER 0x3c5 +#define GRAPH_ADDRESS_REGISTER 0x3ce +#define GRAPH_DATA_REGISTER 0x3cf +#define ATT_ADDRESS_REGISTER 0x3c0 +#define ATT_DATA_REGISTER 0x3c1 +#define MISC_OUTPUT_REGISTER 0x3c2 +#define INPUT_STATUS_1_REGISTER 0x3da +#define DAC_PIXEL_MASK_REGISTER 0x3c6 +#define PALETTE_INDEX_REGISTER 0x3c8 +#define PALETTE_DATA_REGISTER 0x3c9 + + +// +// UGA Draw Hardware abstraction internal worker functions +// +EFI_STATUS +VBoxVgaUgaDrawConstructor ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +EFI_STATUS +VBoxVgaUgaDrawDestructor ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +// +// Graphics Output Hardware abstraction internal worker functions +// +EFI_STATUS +VBoxVgaGraphicsOutputConstructor ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +EFI_STATUS +VBoxVgaGraphicsOutputDestructor ( + VBOX_VGA_PRIVATE_DATA *Private + ); + + +// +// EFI_DRIVER_BINDING_PROTOCOL Protocol Interface +// +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param RemainingDevicePath TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param RemainingDevicePath TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param NumberOfChildren TODO: add argument description + @param ChildHandleBuffer TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +VBoxVgaControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] 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 DriverName[out] 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 +VBoxVgaComponentNameGetDriverName ( + 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 This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] 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 ChildHandle[in] 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 Language[in] 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 ControllerName[out] 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 not a valid EFI_HANDLE. + + @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 +VBoxVgaComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// Local Function Prototypes +// +VOID +InitializeGraphicsMode ( + VBOX_VGA_PRIVATE_DATA *Private, + VBOX_VGA_VIDEO_MODES *ModeData + ); + +VOID +SetPaletteColor ( + VBOX_VGA_PRIVATE_DATA *Private, + UINTN Index, + UINT8 Red, + UINT8 Green, + UINT8 Blue + ); + +VOID +SetDefaultPalette ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +VOID +DrawLogo ( + VBOX_VGA_PRIVATE_DATA *Private, + UINTN ScreenWidth, + UINTN ScreenHeight + ); + +EFI_STATUS +VBoxVgaVideoModeSetup ( + VBOX_VGA_PRIVATE_DATA *Private + ); + +UINT32 VBoxVgaGetVmVariable(UINT32 Variable, CHAR8* Buffer, UINT32 Size); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaDxe.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaDxe.inf new file mode 100644 index 00000000..79f6dc38 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaDxe.inf @@ -0,0 +1,133 @@ +# $Id: VBoxVgaDxe.inf $ +## @file +# VBoxVgaDxe.inf +# + +# +# Copyright (C) 2010-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +# +# This code is based on: +# +#/** @file +# Component description file for CirrusLogic5430 module +# +# Cirrus Logic 5430 Controller Driver.This driver is a sample implementation +# of the UGA Draw Protocol for the Cirrus Logic 5430 family of PCI video controllers. +# This driver is only usable in the EFI pre-boot environment. This sample is +# intended to show how the UGA Draw Protocol is able to function. The UGA I/O +# Protocol is not implemented in this sample. A fully compliant EFI UGA driver +# requires both the UGA Draw and the UGA I/O Protocol. Please refer to Microsoft's +# documentation on UGA for details on how to write a UGA driver that is able +# to function both in the EFI pre-boot environment and from the OS runtime. +# Copyright (c) 2006 - 2009, Intel Corporation +# +# All rights reserved. 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 = VBoxVgaDxe + FILE_GUID = b8a784bc-af4d-4d95-bdb1-ba28236a54f4 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = InitializeVBoxVga + + PCI_VENDOR_ID = 0x80EE + PCI_DEVICE_ID = 0xBEEF + PCI_CLASS_CODE = 0x030000 + PCI_REVISION = 0x00 + COMPRESS = TRUE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = VBoxVgaDriverBinding +# COMPONENT_NAME = VBoxVgaComponentName +# + +[Sources.common] + ComponentName.c + DriverSupportedEfiVersion.c + VBoxVgaUgaDraw.c + VBoxVgaGraphicsOutput.c + VBoxVga.c + VBoxVga.h + Edid.c + VBoxVgaI2c.h + VBoxVgaI2c.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + UefiLib + UefiDriverEntryPoint + DebugLib + BaseMemoryLib + DevicePathLib + TimerLib + +[Protocols] + gEfiDriverSupportedEfiVersionProtocolGuid # PROTOCOL ALWAYS_PRODUCED + gEfiUgaDrawProtocolGuid # PROTOCOL BY_START + gEfiGraphicsOutputProtocolGuid # PROTOCOL BY_START + gEfiEdidDiscoveredProtocolGuid # PROTOCOL BY_START + gEfiEdidActiveProtocolGuid # PROTOCOL BY_START + gEfiDevicePathProtocolGuid # PROTOCOL BY_START + gEfiPciIoProtocolGuid # PROTOCOL TO_START + gEfiEdidOverrideProtocolGuid # PROTOCOL TO_START + + +[FeaturePcd.common] + gVBoxVgaPkgTokenSpaceGuid.PcdSupportGop + gVBoxVgaPkgTokenSpaceGuid.PcdSupportUga + +[Pcd] + gVBoxVgaPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion + gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution ## PRODUCES + gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution ## PRODUCES diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaGraphicsOutput.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaGraphicsOutput.c new file mode 100644 index 00000000..f5706441 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaGraphicsOutput.c @@ -0,0 +1,544 @@ +/* $Id: VBoxVgaGraphicsOutput.c $ */ +/** @file + * VBoxVgaGraphicsOutput.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + +Copyright (c) 2007, Intel Corporation +All rights reserved. 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. + +Module Name: + + UefiVBoxVgaGraphicsOutput.c + +Abstract: + + This file produces the graphics abstraction of Graphics Output Protocol. It is called by + VBoxVga.c file which deals with the EFI 1.1 driver model. + This file just does graphics. + +*/ +#include "VBoxVga.h" +#include <IndustryStandard/Acpi.h> + + +STATIC +VOID +VBoxVgaCompleteModeInfo ( + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info + ) +{ + Info->Version = 0; + Info->PixelFormat = PixelBlueGreenRedReserved8BitPerColor; + Info->PixelsPerScanLine = Info->HorizontalResolution; +} + + +STATIC +EFI_STATUS +VBoxVgaCompleteModeData ( + IN VBOX_VGA_PRIVATE_DATA *Private, + OUT EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode + ) +{ + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FrameBufDesc; + + Info = Mode->Info; + VBoxVgaCompleteModeInfo (Info); + + Private->PciIo->GetBarAttributes ( + Private->PciIo, + Private->BarIndexFB, + NULL, + (VOID**) &FrameBufDesc + ); + + DEBUG((DEBUG_INFO, "%a:%d FrameBufferBase:%x\n", __FILE__, __LINE__, FrameBufDesc->AddrRangeMin)); + Mode->FrameBufferBase = FrameBufDesc->AddrRangeMin; + Mode->FrameBufferSize = Info->PixelsPerScanLine * Info->VerticalResolution + * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); /* 32bpp only! */ + + return EFI_SUCCESS; +} + + +// +// Graphics Output Protocol Member Functions +// +EFI_STATUS +EFIAPI +VBoxVgaGraphicsOutputQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ) +/*++ + +Routine Description: + + Graphics Output protocol interface to query video mode + + Arguments: + This - Protocol instance pointer. + ModeNumber - The mode number to return information on. + Info - Caller allocated buffer that returns information about ModeNumber. + SizeOfInfo - A pointer to the size, in bytes, of the Info buffer. + + Returns: + EFI_SUCCESS - Mode information returned. + EFI_BUFFER_TOO_SMALL - The Info buffer was too small. + EFI_DEVICE_ERROR - A hardware error occurred trying to retrieve the video mode. + EFI_NOT_STARTED - Video display is not initialized. Call SetMode () + EFI_INVALID_PARAMETER - One of the input args was NULL. + +--*/ +{ + VBOX_VGA_PRIVATE_DATA *Private; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + + if (Private->HardwareNeedsStarting) { + return EFI_NOT_STARTED; + } + + if (Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) { + return EFI_INVALID_PARAMETER; + } + + *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + if (*Info == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + (*Info)->HorizontalResolution = Private->ModeData[ModeNumber].HorizontalResolution; + (*Info)->VerticalResolution = Private->ModeData[ModeNumber].VerticalResolution; + VBoxVgaCompleteModeInfo (*Info); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VBoxVgaGraphicsOutputSetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber + ) +/*++ + +Routine Description: + + Graphics Output protocol interface to set video mode + + Arguments: + This - Protocol instance pointer. + ModeNumber - The mode number to be set. + + Returns: + EFI_SUCCESS - Graphics mode was changed. + EFI_DEVICE_ERROR - The device had an error and could not complete the request. + EFI_UNSUPPORTED - ModeNumber is not supported by this device. + +--*/ +{ + VBOX_VGA_PRIVATE_DATA *Private; + VBOX_VGA_MODE_DATA *ModeData; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + + DEBUG((DEBUG_INFO, "%a:%d mode:%d\n", __FILE__, __LINE__, ModeNumber)); + if (ModeNumber >= This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + ModeData = &Private->ModeData[ModeNumber]; + + InitializeGraphicsMode (Private, &VBoxVgaVideoModes[ModeData->ModeNumber]); + + This->Mode->Mode = ModeNumber; + This->Mode->Info->HorizontalResolution = ModeData->HorizontalResolution; + This->Mode->Info->VerticalResolution = ModeData->VerticalResolution; + This->Mode->SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + VBoxVgaCompleteModeData (Private, This->Mode); + + Private->HardwareNeedsStarting = FALSE; + /* update current mode */ + Private->CurrentMode = ModeNumber; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VBoxVgaGraphicsOutputBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +/*++ + +Routine Description: + + Graphics Output protocol instance to block transfer for CirrusLogic device + +Arguments: + + This - Pointer to Graphics Output protocol instance + BltBuffer - The data to transfer to screen + BltOperation - The operation to perform + SourceX - The X coordinate of the source for BltOperation + SourceY - The Y coordinate of the source for BltOperation + DestinationX - The X coordinate of the destination for BltOperation + DestinationY - The Y coordinate of the destination for BltOperation + Width - The width of a rectangle in the blt rectangle in pixels + Height - The height of a rectangle in the blt rectangle in pixels + Delta - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation. + If a Delta of 0 is used, the entire BltBuffer will be operated on. + If a subrectangle of the BltBuffer is used, then Delta represents + the number of bytes in a row of the BltBuffer. + +Returns: + + EFI_INVALID_PARAMETER - Invalid parameter passed in + EFI_SUCCESS - Blt operation success + +--*/ +{ + VBOX_VGA_PRIVATE_DATA *Private; + EFI_TPL OriginalTPL; + UINTN DstY; + UINTN SrcY; + UINT32 CurrentMode; + UINTN ScreenWidth; + UINTN ScreenHeight; + EFI_STATUS Status; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + CurrentMode = This->Mode->Mode; + ScreenWidth = Private->ModeData[CurrentMode].HorizontalResolution; + ScreenHeight = Private->ModeData[CurrentMode].VerticalResolution; + + if ((BltOperation < 0) || (BltOperation >= EfiGraphicsOutputBltOperationMax)) { + return EFI_INVALID_PARAMETER; + } + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + } + // code below assumes a Delta value in pixels, not bytes + Delta /= sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + + // + // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters + // are valid for the operation and the current screen geometry. + // + if (BltOperation == EfiBltVideoToBltBuffer || BltOperation == EfiBltVideoToVideo) { + if (SourceY + Height > ScreenHeight) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > ScreenWidth) { + return EFI_INVALID_PARAMETER; + } + } + if (BltOperation == EfiBltBufferToVideo || BltOperation == EfiBltVideoToVideo || BltOperation == EfiBltVideoFill) { + if (DestinationY + Height > ScreenHeight) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > ScreenWidth) { + return EFI_INVALID_PARAMETER; + } + } + + // + // We have to raise to TPL Notify, so we make an atomic write the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY); + + switch (BltOperation) { + case EfiBltVideoToBltBuffer: + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY) && BltBuffer; SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->Mem.Read ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width, + BltBuffer + (DstY * Delta) + DestinationX + ); + ASSERT_EFI_ERROR((Status)); + } + break; + + case EfiBltBufferToVideo: + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Width, + BltBuffer + (SrcY * Delta) + SourceX + ); + ASSERT_EFI_ERROR((Status)); + } + break; + + case EfiBltVideoToVideo: + // + // Video to Video: Source is Video, destination is Video + // + if (DestinationY <= SourceY) { + // forward copy + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->CopyMem ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width + ); + ASSERT_EFI_ERROR((Status)); + } + } else { + // reverse copy + for (SrcY = SourceY + Height - 1, DstY = DestinationY + Height - 1; SrcY >= SourceY && SrcY <= SourceY + Height - 1; SrcY--, DstY--) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->CopyMem ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width + ); + ASSERT_EFI_ERROR((Status)); + } + } + break; + + case EfiBltVideoFill: + // + // Video Fill: Source is BltBuffer, destination is Video + // + if (DestinationX == 0 && Width == ScreenWidth) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthFillUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + DestinationY * ScreenWidth * 4, + (Width * Height), + BltBuffer + ); + ASSERT_EFI_ERROR((Status)); + } else { + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthFillUint32) and format matches EFI_GRAPHICS_OUTPUT_BLT_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Width, + BltBuffer + ); + ASSERT_EFI_ERROR((Status)); + } + } + break; + + default: + ASSERT (FALSE); + } + + gBS->RestoreTPL (OriginalTPL); + + return EFI_SUCCESS; +} + +EFI_STATUS +VBoxVgaGraphicsOutputConstructor ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + UINT32 Index; + UINT32 HorizontalResolution = 1024; + UINT32 VerticalResolution = 768; + UINT32 ColorDepth = 32; + + DEBUG((DEBUG_INFO, "%a:%d construct\n", __FILE__, __LINE__)); + + GraphicsOutput = &Private->GraphicsOutput; + GraphicsOutput->QueryMode = VBoxVgaGraphicsOutputQueryMode; + GraphicsOutput->SetMode = VBoxVgaGraphicsOutputSetMode; + GraphicsOutput->Blt = VBoxVgaGraphicsOutputBlt; + + // + // Initialize the private data + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE), + (VOID **) &Private->GraphicsOutput.Mode + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION), + (VOID **) &Private->GraphicsOutput.Mode->Info + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private->GraphicsOutput.Mode->MaxMode = (UINT32) Private->MaxMode; + Private->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER; + Private->HardwareNeedsStarting = TRUE; + + // + // Initialize the hardware + // + VBoxVgaGetVmVariable(EFI_INFO_INDEX_HORIZONTAL_RESOLUTION, (CHAR8 *)&HorizontalResolution, + sizeof(HorizontalResolution)); + VBoxVgaGetVmVariable(EFI_INFO_INDEX_VERTICAL_RESOLUTION, (CHAR8 *)&VerticalResolution, + sizeof(VerticalResolution)); + for (Index = 0; Index < Private->MaxMode; Index++) + { + if ( HorizontalResolution == Private->ModeData[Index].HorizontalResolution + && VerticalResolution == Private->ModeData[Index].VerticalResolution + && ColorDepth == Private->ModeData[Index].ColorDepth) + break; + } + // not found? try mode number + if (Index >= Private->MaxMode) + { + VBoxVgaGetVmVariable(EFI_INFO_INDEX_GRAPHICS_MODE, (CHAR8 *)&Index, sizeof(Index)); + // try with mode 2 (usually 1024x768) as a fallback + if (Index >= Private->MaxMode) + Index = 2; + // try with mode 0 (usually 640x480) as a fallback + if (Index >= Private->MaxMode) + Index = 0; + } + + // skip mode setting completely if there is no valid mode + if (Index >= Private->MaxMode) + return EFI_UNSUPPORTED; + + GraphicsOutput->SetMode (GraphicsOutput, Index); + + DrawLogo ( + Private, + Private->ModeData[Private->GraphicsOutput.Mode->Mode].HorizontalResolution, + Private->ModeData[Private->GraphicsOutput.Mode->Mode].VerticalResolution + ); + + PcdSet32S(PcdVideoHorizontalResolution, Private->ModeData[Private->GraphicsOutput.Mode->Mode].HorizontalResolution); + PcdSet32S(PcdVideoVerticalResolution, Private->ModeData[Private->GraphicsOutput.Mode->Mode].VerticalResolution); + + return EFI_SUCCESS; +} + +EFI_STATUS +VBoxVgaGraphicsOutputDestructor ( + VBOX_VGA_PRIVATE_DATA *Private + ) +/*++ + +Routine Description: + +Arguments: + +Returns: + + None + +--*/ +{ + if (Private->GraphicsOutput.Mode != NULL) { + if (Private->GraphicsOutput.Mode->Info != NULL) { + gBS->FreePool (Private->GraphicsOutput.Mode->Info); + } + gBS->FreePool (Private->GraphicsOutput.Mode); + } + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.c new file mode 100644 index 00000000..ba1fceb9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.c @@ -0,0 +1,469 @@ +/* $Id: VBoxVgaI2c.c $ */ +/** @file + * VBoxVgaI2c.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + I2C Bus implementation upon CirrusLogic. + + Copyright (c) 2008 - 2009, Intel Corporation + All rights reserved. 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 "VBoxVga.h" +#include "VBoxVgaI2c.h" + +#define SEQ_ADDRESS_REGISTER 0x3c4 +#define SEQ_DATA_REGISTER 0x3c5 + +#define I2C_CONTROL 0x08 +#define I2CDAT_IN 7 +#define I2CCLK_IN 2 +#define I2CDAT_OUT 1 +#define I2CCLK_OUT 0 + +#define I2C_BUS_SPEED 100 //100kbps + +/** + PCI I/O byte write function. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Address The bit map of I2C Data or I2C Clock pins. + @param Data The date to write. + +**/ +VOID +I2cOutb ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINTN Address, + UINT8 Data + ) +{ + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); +} +/** + PCI I/O byte read function. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Address The bit map of I2C Data or I2C Clock pins. + + return byte value read from PCI I/O space. + +**/ +UINT8 +I2cInb ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINTN Address + ) +{ + UINT8 Data; + + PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); + return Data; +} + +/** + Read status of I2C Data and I2C Clock Pins. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Blt The bit map of I2C Data or I2C Clock pins. + + @retval 0 Low on I2C Data or I2C Clock Pin. + @retval 1 High on I2C Data or I2C Clock Pin. + +**/ +UINT8 +I2cPinRead ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Bit + ) +{ + I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL); + return (UINT8) ((I2cInb (PciIo, SEQ_DATA_REGISTER) >> Bit ) & 0xfe); +} + + +/** + Set/Clear I2C Data and I2C Clock Pins. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Blt The bit map to controller I2C Data or I2C Clock pins. + @param Value 1 or 0 stands for Set or Clear I2C Data and I2C Clock Pins. + +**/ +VOID +I2cPinWrite ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Bit, + UINT8 Value + ) +{ + UINT8 Byte; + I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL); + Byte = (UINT8) (I2cInb (PciIo, SEQ_DATA_REGISTER) & (UINT8) ~(1 << Bit)) ; + Byte = (UINT8) (Byte | ((Value & 0x01) << Bit)); + I2cOutb (PciIo, SEQ_DATA_REGISTER, (UINT8) (Byte | 0x40)); + return; +} + +/** + Read/write delay according to I2C Bus Speed. + +**/ +VOID +I2cDelay ( + VOID + ) +{ + MicroSecondDelay (1000 / I2C_BUS_SPEED); +} + +/** + Write a 8-bit data onto I2C Data Pin. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Data The byte data to write. + +**/ +VOID +I2cSendByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Data + ) +{ + UINTN Index; + // + // Send byte data onto I2C Bus + // + for (Index = 0; Index < 8; Index --) { + I2cPinWrite (PciIo, I2CDAT_OUT, (UINT8) (Data >> (7 - Index))); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); + } +} + +/** + Read a 8-bit data from I2C Data Pin. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + + Return the byte data read from I2C Data Pin. +**/ +UINT8 +I2cReceiveByte ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINT8 Data; + UINTN Index; + + Data = 0; + // + // Read byte data from I2C Bus + // + for (Index = 0; Index < 8; Index --) { + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + Data = (UINT8) (Data << 1); + Data = (UINT8) (Data | I2cPinRead (PciIo, I2CDAT_IN)); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); + } + + return Data; +} + +/** + Receive an ACK signal from I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +BOOLEAN +I2cWaitAck ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Wait for ACK signal + // + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + if (I2cPinRead (PciIo, I2CDAT_IN) == 0) { + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + return TRUE; + } else { + return FALSE; + } +} + +/** + Send an ACK signal onto I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cSendAck ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); +} + +/** + Start a I2C transfer on I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cStart ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Init CLK and DAT pins + // + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + // + // Start a I2C transfer, set SDA low from high, when SCL is high + // + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); +} + +/** + Stop a I2C transfer on I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cStop ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Stop a I2C transfer, set SDA high from low, when SCL is high + // + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); +} + +/** + Read one byte data on I2C Bus. + + Read one byte data from the slave device connected to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to returned data if EFI_SUCCESS returned. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cReadByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ) +{ + ASSERT (Data != NULL); + + // + // Start I2C transfer + // + I2cStart (PciIo); + + // + // Send slave address with enabling write flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send register address + // + I2cSendByte (PciIo, RegisterAddress); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send slave address with enabling read flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress | 0x01)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Read byte data from I2C Bus + // + *Data = I2cReceiveByte (PciIo); + + // + // Send ACK signal onto I2C Bus + // + I2cSendAck (PciIo); + + // + // Stop a I2C transfer + // + I2cStop (PciIo); + + return EFI_SUCCESS; +} + +/** + Write one byte data onto I2C Bus. + + Write one byte data to the slave device connected to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to write data. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cWriteByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ) +{ + ASSERT (Data != NULL); + + I2cStart (PciIo); + // + // Send slave address with enabling write flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send register address + // + I2cSendByte (PciIo, RegisterAddress); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send byte data onto I2C Bus + // + I2cSendByte (PciIo, *Data); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Stop a I2C transfer + // + I2cStop (PciIo); + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.h new file mode 100644 index 00000000..c88c0587 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaI2c.h @@ -0,0 +1,106 @@ +/* $Id: VBoxVgaI2c.h $ */ +/** @file + * VBoxVgaI2c.h + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + I2c Bus byte read/write functions. + + Copyright (c) 2008 - 2009, Intel Corporation + All rights reserved. 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 _CIRRUS_LOGIC_I2C_H_ +#define _CIRRUS_LOGIC_I2C_H_ + +#include <Protocol/PciIo.h> + +/** + Read one byte data on I2C Bus. + + Read one byte data from the slave device connected to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to returned data if EFI_SUCCESS returned. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cReadByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ); + +/** + Write one byte data onto I2C Bus. + + Write one byte data to the slave device connected to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to write data. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cWriteByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaUgaDraw.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaUgaDraw.c new file mode 100644 index 00000000..79b38073 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaDxe/VBoxVgaUgaDraw.c @@ -0,0 +1,417 @@ +/* $Id: VBoxVgaUgaDraw.c $ */ +/** @file + * VBoxVgaUgaDraw.c + */ + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/* + This code is based on: + + This file produces the graphics abstraction of UGA Draw. It is called by + VBoxVga.c file which deals with the EFI 1.1 driver model. + This file just does graphics. + + Copyright (c) 2006, Intel Corporation + All rights reserved. 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 "VBoxVga.h" + +// +// UGA Draw Protocol Member Functions +// +EFI_STATUS +EFIAPI +VBoxVgaUgaDrawGetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + OUT UINT32 *HorizontalResolution, + OUT UINT32 *VerticalResolution, + OUT UINT32 *ColorDepth, + OUT UINT32 *RefreshRate + ) +{ + VBOX_VGA_PRIVATE_DATA *Private; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + + if (Private->HardwareNeedsStarting) { + return EFI_NOT_STARTED; + } + + if ((HorizontalResolution == NULL) || + (VerticalResolution == NULL) || + (ColorDepth == NULL) || + (RefreshRate == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *HorizontalResolution = Private->ModeData[Private->CurrentMode].HorizontalResolution; + *VerticalResolution = Private->ModeData[Private->CurrentMode].VerticalResolution; + *ColorDepth = Private->ModeData[Private->CurrentMode].ColorDepth; + *RefreshRate = Private->ModeData[Private->CurrentMode].RefreshRate; + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +VBoxVgaUgaDrawSetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN UINT32 HorizontalResolution, + IN UINT32 VerticalResolution, + IN UINT32 ColorDepth, + IN UINT32 RefreshRate + ) +{ + VBOX_VGA_PRIVATE_DATA *Private; + UINTN Index; + + DEBUG((DEBUG_INFO, "%a:%d VIDEO: %dx%d %d bpp\n", __FILE__, __LINE__, HorizontalResolution, VerticalResolution, ColorDepth)); + Private = VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + + for (Index = 0; Index < Private->MaxMode; Index++) { + + if (HorizontalResolution != Private->ModeData[Index].HorizontalResolution) { + continue; + } + + if (VerticalResolution != Private->ModeData[Index].VerticalResolution) { + continue; + } + + if (ColorDepth != Private->ModeData[Index].ColorDepth) { + continue; + } + +#if 0 + if (RefreshRate != Private->ModeData[Index].RefreshRate) { + continue; + } +#endif + + InitializeGraphicsMode (Private, &VBoxVgaVideoModes[Private->ModeData[Index].ModeNumber]); + + Private->CurrentMode = Index; + + Private->HardwareNeedsStarting = FALSE; + + /* update current mode */ + Private->CurrentMode = Index; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS +EFIAPI +VBoxVgaUgaDrawBlt ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN EFI_UGA_PIXEL *BltBuffer, OPTIONAL + IN EFI_UGA_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + VBOX_VGA_PRIVATE_DATA *Private; + EFI_TPL OriginalTPL; + UINTN DstY; + UINTN SrcY; + UINTN ScreenWidth; + UINTN ScreenHeight; + EFI_STATUS Status; + + Private = VBOX_VGA_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + ScreenWidth = Private->ModeData[Private->CurrentMode].HorizontalResolution; + ScreenHeight = Private->ModeData[Private->CurrentMode].VerticalResolution; + + if ((BltOperation < 0) || (BltOperation >= EfiUgaBltMax)) { + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof (EFI_UGA_PIXEL); + } + // code below assumes a Delta value in pixels, not bytes + Delta /= sizeof (EFI_UGA_PIXEL); + + // + // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters + // are valid for the operation and the current screen geometry. + // + if (BltOperation == EfiUgaVideoToBltBuffer || BltOperation == EfiUgaVideoToVideo) { + if (SourceY + Height > ScreenHeight) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > ScreenWidth) { + return EFI_INVALID_PARAMETER; + } + } + if (BltOperation == EfiUgaBltBufferToVideo || BltOperation == EfiUgaVideoToVideo || BltOperation == EfiUgaVideoFill) { + if (DestinationY + Height > ScreenHeight) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > ScreenWidth) { + return EFI_INVALID_PARAMETER; + } + } + + // + // We have to raise to TPL Notify, so we make an atomic write the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY); + + switch (BltOperation) { + case EfiUgaVideoToBltBuffer: + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->Mem.Read ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width, + BltBuffer + (DstY * Delta) + DestinationX + ); + ASSERT_EFI_ERROR((Status)); + } + break; + + case EfiUgaBltBufferToVideo: + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Width, + BltBuffer + (SrcY * Delta) + SourceX + ); + ASSERT_EFI_ERROR((Status)); + } + break; + + case EfiUgaVideoToVideo: + // + // Video to Video: Source is Video, destination is Video + // + if (DestinationY <= SourceY) { + // forward copy + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->CopyMem ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width + ); + ASSERT_EFI_ERROR((Status)); + } + } else { + // reverse copy + for (SrcY = SourceY + Height - 1, DstY = DestinationY + Height - 1; SrcY >= SourceY && SrcY <= SourceY + Height - 1; SrcY--, DstY--) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->CopyMem ( + Private->PciIo, + EfiPciIoWidthUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Private->BarIndexFB, + ((SrcY * ScreenWidth) + SourceX) * 4, + Width + ); + ASSERT_EFI_ERROR((Status)); + } + } + break; + + case EfiUgaVideoFill: + // + // Video Fill: Source is BltBuffer, destination is Video + // + if (DestinationX == 0 && Width == ScreenWidth) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthFillUint32) and format matches EFI_UGA_PIXEL + Status = Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + DestinationY * ScreenWidth * 4, + (Width * Height), + BltBuffer + ); + ASSERT_EFI_ERROR((Status)); + } else { + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + /// @todo assumes that color depth is 32 (*4, EfiPciIoWidthFillUint32) and format matches EFI_UGA_PIXEL + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + Private->BarIndexFB, + ((DstY * ScreenWidth) + DestinationX) * 4, + Width, + BltBuffer + ); + } + } + break; + + default: + ASSERT (FALSE); + } + + gBS->RestoreTPL (OriginalTPL); + + return EFI_SUCCESS; +} + +// +// Construction and Destruction functions +// +EFI_STATUS +VBoxVgaUgaDrawConstructor ( + VBOX_VGA_PRIVATE_DATA *Private + ) +{ + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + UINT32 Index; + UINT32 HorizontalResolution = 1024; + UINT32 VerticalResolution = 768; + UINT32 ColorDepth = 32; + + // + // Fill in Private->UgaDraw protocol + // + UgaDraw = &Private->UgaDraw; + + UgaDraw->GetMode = VBoxVgaUgaDrawGetMode; + UgaDraw->SetMode = VBoxVgaUgaDrawSetMode; + UgaDraw->Blt = VBoxVgaUgaDrawBlt; + + // + // Initialize the private data + // + Private->CurrentMode = 0; + Private->HardwareNeedsStarting = TRUE; + + // + // Initialize the hardware + // + VBoxVgaGetVmVariable(EFI_INFO_INDEX_HORIZONTAL_RESOLUTION, (CHAR8 *)&HorizontalResolution, + sizeof(HorizontalResolution)); + VBoxVgaGetVmVariable(EFI_INFO_INDEX_VERTICAL_RESOLUTION, (CHAR8 *)&VerticalResolution, + sizeof(VerticalResolution)); + for (Index = 0; Index < Private->MaxMode; Index++) + { + if ( HorizontalResolution == Private->ModeData[Index].HorizontalResolution + && VerticalResolution == Private->ModeData[Index].VerticalResolution + && ColorDepth == Private->ModeData[Index].ColorDepth) + break; + } + // not found? try mode number + if (Index >= Private->MaxMode) + { + VBoxVgaGetVmVariable(EFI_INFO_INDEX_GRAPHICS_MODE, (CHAR8 *)&Index, sizeof(Index)); + // try with mode 2 (usually 1024x768) as a fallback + if (Index >= Private->MaxMode) + Index = 2; + // try with mode 0 (usually 640x480) as a fallback + if (Index >= Private->MaxMode) + Index = 0; + + // get the resolution from the mode if valid + if (Index < Private->MaxMode) + { + HorizontalResolution = Private->ModeData[Index].HorizontalResolution; + VerticalResolution = Private->ModeData[Index].VerticalResolution; + ColorDepth = Private->ModeData[Index].ColorDepth; + } + } + + // skip mode setting completely if there is no valid mode + if (Index >= Private->MaxMode) + return EFI_UNSUPPORTED; + + UgaDraw->SetMode ( + UgaDraw, + HorizontalResolution, + VerticalResolution, + ColorDepth, + 60 + ); + + DrawLogo ( + Private, + Private->ModeData[Private->CurrentMode].HorizontalResolution, + Private->ModeData[Private->CurrentMode].VerticalResolution + ); + + PcdSet32S(PcdVideoHorizontalResolution, HorizontalResolution); + PcdSet32S(PcdVideoVerticalResolution, VerticalResolution); + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x14.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x14.h new file mode 100644 index 00000000..2342f4a5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x14.h @@ -0,0 +1,243 @@ +/* $Id: VBoxVgaFont-8x14.h $ */ +/** @file + * VGA-ROM.F14 from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip . + * The package is (C) Joseph (Yossi) Gil. + * The individual fonts are in the public domain. + */ + +/* + * This file was automatically generated + * from VGA-ROM.F14 + * by \coding\vbox\svn\trunk\out\win.amd64\debug\obj\bin2c\bin2c.exe. + */ + +const unsigned char g_abVgaFont_8x14[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, + 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, + 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, + 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, + 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, + 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x70, 0xf0, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, + 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, + 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, + 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, + 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, + 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x66, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, + 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, + 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, + 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, + 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, + 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x66, 0x6c, 0x6c, + 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, + 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, + 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, + 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, + 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x3c, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, + 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, + 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, + 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, + 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, + 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, + 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, 0x00, + 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, + 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, + 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcc, 0xcc, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, + 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, + 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x76, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, + 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, 0x00, 0xc6, + 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x00, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, + 0x60, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, + 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xc6, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, + 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, + 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x66, + 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x55, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, + 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, + 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0x40, 0x00, 0x00, 0x00, 0xfe, 0xc6, + 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, + 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, + 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x30, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, + 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, + 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const unsigned g_cbVgaFont_8x14 = sizeof(g_abVgaFont_8x14); +/* end of file */ diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x16.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x16.h new file mode 100644 index 00000000..bb8b6b46 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x16.h @@ -0,0 +1,275 @@ +/* $Id: VBoxVgaFont-8x16.h $ */ +/** @file + * VGA-ROM.F16 from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip . + * The package is (C) Joseph (Yossi) Gil. + * The individual fonts are in the public domain. + */ + +/* + * This file was automatically generated + * from VGA-ROM.F16 + * by \coding\vbox\svn\trunk\out\win.amd64\debug\obj\bin2c\bin2c.exe. + */ + +const unsigned char g_abVgaFont_8x16[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const unsigned g_cbVgaFont_8x16 = sizeof(g_abVgaFont_8x16); +/* end of file */ diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x8.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x8.h new file mode 100644 index 00000000..369fbb11 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFont-8x8.h @@ -0,0 +1,275 @@ +/* $Id: VBoxVgaFont-8x8.h $ */ +/** @file + * VGA-ROM.F8 from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip . + * The package is (C) Joseph (Yossi) Gil. + * The individual fonts are in the public domain. + */ + +/* + * This file was automatically generated + * from VGA-ROM.F8 + * by \coding\vbox\svn\trunk\out\win.amd64\debug\obj\bin2c\bin2c.exe. + */ + +const unsigned char g_abVgaFont_8x8[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, + 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, + 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, + 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, + 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, + 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, + 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, + 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, + 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, + 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, + 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00, + 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38, + 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, + 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00, + 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00, + 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00, + 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, + 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, + 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, + 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, + 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, + 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, + 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00, + 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00, + 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, + 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, + 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f, + 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03, + 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00, + 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0, + 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, + 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00, + 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0, + 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc, + 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00, + 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, + 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0, + 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, + 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, + 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, + 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const unsigned g_cbVgaFont_8x8 = sizeof(g_abVgaFont_8x8); +/* end of file */ diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFonts.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFonts.h new file mode 100644 index 00000000..ef94497d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaFonts.h @@ -0,0 +1,13 @@ +/* $Id: VBoxVgaFonts.h $ */ +/** @file + * Some of the VGA-ROM fonts from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip . + * The package is (C) Joseph (Yossi) Gil. + * The individual fonts are in the public domain. + * + * Note. These fonts are also used by the VGA BIOS. + */ + +#include "VBoxVgaFont-8x8.h" +#include "VBoxVgaFont-8x14.h" +#include "VBoxVgaFont-8x16.h" + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaMiniPortDxe.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaMiniPortDxe.c new file mode 100644 index 00000000..4a2005a2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaMiniPortDxe.c @@ -0,0 +1,657 @@ +/* $Id: VBoxVgaMiniPortDxe.c $ */ +/** @file + * VBoxVgaMiniPortDxe.c - VgaMiniPort Protocol Implementation. + */ + + +/* + * Copyright (C) 2009-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <Protocol/ComponentName.h> +#include <Protocol/ComponentName2.h> +#include <Protocol/DriverBinding.h> +#include <Protocol/PciIo.h> +#include <Protocol/VgaMiniPort.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> +#include <IndustryStandard/Pci22.h> + +#include "VBoxPkg.h" +#include "VBoxVgaFonts.h" +#include "iprt/asm.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Instance data for a VGA device this driver handles. + */ +typedef struct VBOXVGAMINIPORT +{ + /** The VGA Mini Port Protocol. */ + EFI_VGA_MINI_PORT_PROTOCOL VgaMiniPort; + /** Magic value, VBOX_VGA_MINI_PORT_MAGIC. */ + UINT32 u32Magic; + /** The controller handle of the device. */ + EFI_HANDLE hController; + /** The PciIo protocol for the device. */ + EFI_PCI_IO_PROTOCOL *pPciIo; +} VBOXVGAMINIPORT; +/** Pointer to a VBOXVGAMINIPORT structure. */ +typedef VBOXVGAMINIPORT *PVBOXVGAMINIPORT; + +/** VBOXVGAMINIPORT::u32Magic value (Isaac Asimov). */ +#define VBOX_VGA_MINI_PORT_MAGIC 0x19200102 +/** VBOXVGAMINIPORT::u32Magic dead value. */ +#define VBOX_VGA_MINI_PORT_MAGIC_DEAD 0x19920406 + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortDB_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL); +static EFI_STATUS EFIAPI +VBoxVgaMiniPortDB_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL); +static EFI_STATUS EFIAPI +VBoxVgaMiniPortDB_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer OPTIONAL); + + +static EFI_STATUS EFIAPI +VBoxVgaMiniPortVMP_SetMode(IN EFI_VGA_MINI_PORT_PROTOCOL *This, IN UINTN ModeNumber); + + +static EFI_STATUS EFIAPI +VBoxVgaMiniPortCN_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, OUT CHAR16 **DriverName); +static EFI_STATUS EFIAPI +VBoxVgaMiniPortCN_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, OUT CHAR16 **ControllerName); + + +static EFI_STATUS EFIAPI +VBoxVgaMiniPortCN2_GetDriverName(IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, OUT CHAR16 **DriverName); +static EFI_STATUS EFIAPI +VBoxVgaMiniPortCN2_GetControllerName(IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, OUT CHAR16 **ControllerName); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** EFI Driver Binding Protocol. */ +static EFI_DRIVER_BINDING_PROTOCOL g_VBoxVgaMiniPortDB = +{ + VBoxVgaMiniPortDB_Supported, + VBoxVgaMiniPortDB_Start, + VBoxVgaMiniPortDB_Stop, + /* .Version = */ 1, /* One higher than Pci/VgaMiniPortDxe. */ + /* .ImageHandle = */ NULL, + /* .DriverBindingHandle = */ NULL +}; + +/** EFI Component Name Protocol. */ +static const EFI_COMPONENT_NAME_PROTOCOL g_VBoxVgaMiniPortCN = +{ + VBoxVgaMiniPortCN_GetDriverName, + VBoxVgaMiniPortCN_GetControllerName, + "eng" +}; + +/** EFI Component Name 2 Protocol. */ +static const EFI_COMPONENT_NAME2_PROTOCOL g_VBoxVgaMiniPortCN2 = +{ + VBoxVgaMiniPortCN2_GetDriverName, + VBoxVgaMiniPortCN2_GetControllerName, + "en" +}; + +/** Driver name translation table. */ +static CONST EFI_UNICODE_STRING_TABLE g_aVBoxMiniPortDriverLangAndNames[] = +{ + { "eng;en", L"PCI VGA Mini Port Driver" }, + { NULL, NULL } +}; + + + +/** + * VBoxVgaMiniPort entry point. + * + * @returns EFI status code. + * + * @param ImageHandle The image handle. + * @param SystemTable The system table pointer. + */ +EFI_STATUS EFIAPI +DxeInitializeVBoxVgaMiniPort(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS rc; + DEBUG((DEBUG_INFO, "DxeInitializeVBoxVgaMiniPort\n")); + + rc = EfiLibInstallDriverBindingComponentName2(ImageHandle, SystemTable, + &g_VBoxVgaMiniPortDB, ImageHandle, + &g_VBoxVgaMiniPortCN, &g_VBoxVgaMiniPortCN2); + ASSERT_EFI_ERROR(rc); + return rc; +} + +/** + * @copydoc EFI_DRIVER_BINDING_SUPPORTED + */ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortDB_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL) +{ + EFI_STATUS rcRet = EFI_UNSUPPORTED; + EFI_PCI_IO_PROTOCOL *pPciIo; + EFI_STATUS rc; + + DEBUG((DEBUG_INFO, "%a: Controller=%p\n", __FUNCTION__, ControllerHandle)); + + /* + * To perform the test we need to check some PCI configuration registers, + * just read all the standard ones to make life simpler. + */ + rc = gBS->OpenProtocol(ControllerHandle, &gEfiPciIoProtocolGuid, (VOID **)&pPciIo, + This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); + if (!EFI_ERROR(rc)) + { + PCI_TYPE00 CfgRegs; + + rc = pPciIo->Pci.Read(pPciIo, + EfiPciIoWidthUint32, + 0 /* Offset */, + sizeof(CfgRegs) / sizeof(UINT32) /* Count */ , + &CfgRegs); + if (!EFI_ERROR(rc)) + { + /* + * Perform the test. + */ + if (IS_PCI_VGA(&CfgRegs)) + { +#if 0 /** @todo this doesn't quite work with our DevVGA since it doesn't flag I/O access. */ + if ( CfgRegs.Hdr.Command & (PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS) + == (PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS)) +#else + if (1) +#endif + { + DEBUG((DEBUG_INFO, "%a: Found supported VGA device! (VendorId=%x DeviceId=%x)\n", + __FUNCTION__, CfgRegs.Hdr.VendorId, CfgRegs.Hdr.DeviceId)); + rcRet = EFI_SUCCESS; + } + else + DEBUG((DEBUG_INFO, "%a: VGA device not enabled! (VendorId=%x DeviceId=%x)\n", + __FUNCTION__, CfgRegs.Hdr.VendorId, CfgRegs.Hdr.DeviceId)); + } + else + DEBUG((DEBUG_INFO, "%a: Not VGA (Class=%x,%x,%x VendorId=%x DeviceId=%x)\n", + __FUNCTION__, CfgRegs.Hdr.ClassCode[0], CfgRegs.Hdr.ClassCode[1], + CfgRegs.Hdr.ClassCode[2], CfgRegs.Hdr.VendorId, CfgRegs.Hdr.DeviceId)); + } + else + DEBUG((DEBUG_INFO, "%a: pPciIo->Pci.Read -> %r\n", __FUNCTION__, rc)); + + gBS->CloseProtocol(ControllerHandle, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, ControllerHandle); + } + else + DEBUG((DEBUG_INFO, "%a: PciIoProtocol -> %r\n", __FUNCTION__, rc)); + return rcRet; +} + +/** + * @copydoc EFI_DRIVER_BINDING_START + */ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortDB_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL) +{ + EFI_STATUS rc; + EFI_PCI_IO_PROTOCOL *pPciIo; + + DEBUG((DEBUG_INFO, "%a\n", __FUNCTION__)); + + /* + * We need the PCI I/O abstraction protocol. + */ + rc = gBS->OpenProtocol(ControllerHandle, &gEfiPciIoProtocolGuid, (VOID **)&pPciIo, + This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); + if (!EFI_ERROR(rc)) + { + /* + * Allocate and initialize the instance data. + */ + PVBOXVGAMINIPORT pThisDev; + rc = gBS->AllocatePool(EfiBootServicesData, sizeof(*pThisDev), (VOID **)&pThisDev); + if (!EFI_ERROR(rc)) + { + pThisDev->VgaMiniPort.SetMode = VBoxVgaMiniPortVMP_SetMode; + pThisDev->VgaMiniPort.VgaMemoryOffset = 0x000b8000; + pThisDev->VgaMiniPort.CrtcAddressRegisterOffset = 0x03d4; + pThisDev->VgaMiniPort.CrtcDataRegisterOffset = 0x03d5; + pThisDev->VgaMiniPort.VgaMemoryBar = EFI_PCI_IO_PASS_THROUGH_BAR; + pThisDev->VgaMiniPort.CrtcAddressRegisterBar = EFI_PCI_IO_PASS_THROUGH_BAR; + pThisDev->VgaMiniPort.CrtcDataRegisterBar = EFI_PCI_IO_PASS_THROUGH_BAR; + pThisDev->VgaMiniPort.MaxMode = 2; + pThisDev->u32Magic = VBOX_VGA_MINI_PORT_MAGIC; + pThisDev->hController = ControllerHandle; + pThisDev->pPciIo = pPciIo; + + /* + * Register the VGA Mini Port Protocol. + */ + rc = gBS->InstallMultipleProtocolInterfaces(&ControllerHandle, + &gEfiVgaMiniPortProtocolGuid, &pThisDev->VgaMiniPort, + NULL, NULL); + if (!EFI_ERROR(rc)) + { + DEBUG((DEBUG_INFO, "%a: Successfully started, pThisDev=%p ControllerHandle=%p\n", + __FUNCTION__, pThisDev, ControllerHandle)); + return EFI_SUCCESS; + } + + DEBUG((DEBUG_INFO, "%a: InstallMultipleProtocolInterfaces -> %r\n", __FUNCTION__, rc)); + gBS->FreePool(pThisDev); + } + else + DEBUG((DEBUG_INFO, "%a: AllocatePool -> %r\n", __FUNCTION__, rc)); + + gBS->CloseProtocol(ControllerHandle, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, ControllerHandle); + } + else + DEBUG((DEBUG_INFO, "%a: PciIoProtocol -> %r\n", __FUNCTION__, rc)); + return rc; +} + +/** + * @copydoc EFI_DRIVER_BINDING_STOP + */ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortDB_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer OPTIONAL) +{ + EFI_STATUS rc; + PVBOXVGAMINIPORT pThisDev; + + DEBUG((DEBUG_INFO, "%a: ControllerHandle=%p NumberOfChildren=%u\n", __FUNCTION__, ControllerHandle, NumberOfChildren)); + + /* + * Get the miniport driver instance associated with the controller. + */ + rc = gBS->OpenProtocol(ControllerHandle, &gEfiVgaMiniPortProtocolGuid, + (VOID **)&pThisDev, This->DriverBindingHandle, + ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!EFI_ERROR(rc)) + { + ASSERT(pThisDev->u32Magic == VBOX_VGA_MINI_PORT_MAGIC); + ASSERT(pThisDev->hController == ControllerHandle); + if ( pThisDev->u32Magic == VBOX_VGA_MINI_PORT_MAGIC + && pThisDev->hController == ControllerHandle) + { + /* + * Uninstall the VgaMiniPort interface. + */ + rc = gBS->UninstallProtocolInterface(ControllerHandle, + &gEfiVgaMiniPortProtocolGuid, + &pThisDev->VgaMiniPort); + if (!EFI_ERROR(rc)) + { + /* + * Invalidate and release sources associated with the device instance. + */ + pThisDev->u32Magic = VBOX_VGA_MINI_PORT_MAGIC_DEAD; + gBS->FreePool(pThisDev); + + } + else + DEBUG((DEBUG_INFO, "%a: UninstallProtocolInterface -> %r\n", __FUNCTION__, rc)); + } + else + { + DEBUG((DEBUG_INFO, "%a: magic=%x/%x hController=%x/%x\n", __FUNCTION__, + pThisDev->u32Magic, VBOX_VGA_MINI_PORT_MAGIC, + pThisDev->hController, ControllerHandle)); + rc = EFI_DEVICE_ERROR; + } + gBS->CloseProtocol(ControllerHandle, &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, ControllerHandle); + } + else + DEBUG((DEBUG_INFO, "%a: VgaMiniPortProtocol -> %r\n", __FUNCTION__, rc)); + return rc; +} + + + + + +/** + * @copydoc EFI_VGA_MINI_PORT_SET_MODE + */ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortVMP_SetMode(IN EFI_VGA_MINI_PORT_PROTOCOL *This, IN UINTN ModeNumber) +{ + PVBOXVGAMINIPORT pThisDev = (PVBOXVGAMINIPORT)This; + UINT8 r[64]; + int i; + + /* + * Check input. + */ + if (pThisDev->u32Magic != VBOX_VGA_MINI_PORT_MAGIC) + { + DEBUG((DEBUG_INFO, "%a: u32Magic=%x/%x\n", __FUNCTION__, pThisDev->u32Magic, VBOX_VGA_MINI_PORT_MAGIC)); + return EFI_DEVICE_ERROR; + } + if (ModeNumber >= This->MaxMode) + { + DEBUG((DEBUG_INFO, "%a: ModeNumber=%d >= MaxMode=%d\n", __FUNCTION__, ModeNumber, This->MaxMode)); + return EFI_UNSUPPORTED; + } + DEBUG((DEBUG_INFO, "%a: ModeNumber=%d\n", __FUNCTION__, ModeNumber)); + + /* some initialization */ + ASMOutU8(0x3c2, 0xc3); + ASMOutU16(0x3c4, 0x0204); + + ASMInU8(0x3da); // reset attr F/F + ASMOutU8(0x3c0, 0); // disable palette + ASMOutU16(0x3d4, 0x0011); // unprotect crtc regs 0 - 7 + +#define BOUTB(count, aport, dport) \ + do { \ + for (i = 0 ; i < (count); ++i) \ + if ((dport) == (aport) + 1) \ + ASMOutU16((aport), ((UINT16)r[i] << 8) | (UINT8)i); \ + else { \ + ASMOutU8((aport), (UINT8)i); \ + ASMOutU8((dport), r[i]); \ + } \ + } while (0) + + /* Reset and set sequencer registers */ + r[0] = 0x01; + r[1] = 0x00; + r[2] = 0x03; + r[3] = 0x00; + r[4] = 0x02; + BOUTB(5, 0x3c4, 0x3c5); + + /* set misc out register */ + ASMOutU8(0x3c2, 0x67); + + /* enable sequencer */ + ASMOutU16(0x3c4, 0x0300); + + /* set all crtc registers */ + r[0] = 0x5f; r[1] = 0x4f; r[2] = 0x50; r[3] = 0x82; + r[4] = 0x55; r[5] = 0x81; r[6] = 0xbf; r[7] = 0x1f; + r[8] = 0x00; r[9] = 0x4f; r[10]= 0x0d; r[11]= 0x0e; + r[12]= 0x00; r[13]= 0x00; r[14]= 0x03; r[15]= 0xc0; + r[16]= 0x9c; r[17]= 0x0e; r[18]= 0x8f; r[19]= 0x28; + r[20]= 0x1f; r[21]= 0x96; r[22]= 0xb9; r[23]= 0xa3; + r[24]= 0xff; + BOUTB(25, 0x3d4, 0x3d5); + + /* set all graphics controller registers */ + r[0]= 0x00; r[1]= 0x00; r[2]= 0x00; r[3]= 0x00; + r[4]= 0x00; r[5]= 0x10; r[6]= 0x0e; r[7]= 0x00; + r[8]= 0xff; + BOUTB(9, 0x3ce, 0x3cf); + + /* set all attribute registers */ + ASMInU8(0x3da); // reset attr F/F + r[0] = 0x00; r[1] = 0x01; r[2] = 0x02; r[3] = 0x03; + r[4] = 0x04; r[5] = 0x05; r[6] = 0x14; r[7] = 0x07; + r[8] = 0x38; r[9] = 0x39; r[10]= 0x3a; r[11]= 0x3b; + r[12]= 0x3c; r[13]= 0x3d; r[14]= 0x3e; r[15]= 0x3f; + r[16]= 0x0c; r[17]= 0x00; r[18]= 0x0f; r[19]= 0x08; + r[20]= 0x00; + BOUTB(21, 0x3c0, 0x3c0); + ASMOutU8(0x3c0, 0x20); // re-enable palette + + /* set all VBox extended registers */ + + /* enable sequencer */ + ASMOutU16(0x3c4, 0x0100); + + ASMOutU16(0x1ce, 0x04); ASMOutU16(0x1cf, 0); // ENABLE + + /* enable sequencer */ + ASMOutU16(0x3c4, 0x0300); + + /* Load default values into the first 16 entries of the DAC */ + { + static const UINT8 s_a3bVgaDac[64*3] = + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2A, + 0x00, 0x2A, 0x00, + 0x00, 0x2A, 0x2A, + 0x2A, 0x00, 0x00, + 0x2A, 0x00, 0x2A, + 0x2A, 0x2A, 0x00, + 0x2A, 0x2A, 0x2A, + 0x00, 0x00, 0x15, + 0x00, 0x00, 0x3F, + 0x00, 0x2A, 0x15, + 0x00, 0x2A, 0x3F, + 0x2A, 0x00, 0x15, + 0x2A, 0x00, 0x3F, + 0x2A, 0x2A, 0x15, + 0x2A, 0x2A, 0x3F, + 0x00, 0x15, 0x00, + 0x00, 0x15, 0x2A, + 0x00, 0x3F, 0x00, + 0x00, 0x3F, 0x2A, + 0x2A, 0x15, 0x00, + 0x2A, 0x15, 0x2A, + 0x2A, 0x3F, 0x00, + 0x2A, 0x3F, 0x2A, + 0x00, 0x15, 0x15, + 0x00, 0x15, 0x3F, + 0x00, 0x3F, 0x15, + 0x00, 0x3F, 0x3F, + 0x2A, 0x15, 0x15, + 0x2A, 0x15, 0x3F, + 0x2A, 0x3F, 0x15, + 0x2A, 0x3F, 0x3F, + 0x15, 0x00, 0x00, + 0x15, 0x00, 0x2A, + 0x15, 0x2A, 0x00, + 0x15, 0x2A, 0x2A, + 0x3F, 0x00, 0x00, + 0x3F, 0x00, 0x2A, + 0x3F, 0x2A, 0x00, + 0x3F, 0x2A, 0x2A, + 0x15, 0x00, 0x15, + 0x15, 0x00, 0x3F, + 0x15, 0x2A, 0x15, + 0x15, 0x2A, 0x3F, + 0x3F, 0x00, 0x15, + 0x3F, 0x00, 0x3F, + 0x3F, 0x2A, 0x15, + 0x3F, 0x2A, 0x3F, + 0x15, 0x15, 0x00, + 0x15, 0x15, 0x2A, + 0x15, 0x3F, 0x00, + 0x15, 0x3F, 0x2A, + 0x3F, 0x15, 0x00, + 0x3F, 0x15, 0x2A, + 0x3F, 0x3F, 0x00, + 0x3F, 0x3F, 0x2A, + 0x15, 0x15, 0x15, + 0x15, 0x15, 0x3F, + 0x15, 0x3F, 0x15, + 0x15, 0x3F, 0x3F, + 0x3F, 0x15, 0x15, + 0x3F, 0x15, 0x3F, + 0x3F, 0x3F, 0x15, + 0x3F, 0x3F, 0x3F + }; + + for (i = 0; i < 64; ++i) + { + ASMOutU8(0x3c8, (UINT8)i); + ASMOutU8(0x3c9, s_a3bVgaDac[i*3 + 0]); + ASMOutU8(0x3c9, s_a3bVgaDac[i*3 + 1]); + ASMOutU8(0x3c9, s_a3bVgaDac[i*3 + 2]); + } + } + + /* Load the appropriate font into the first map */ + { + UINT8 const *pabFont; + unsigned offBase = 0; + UINT16 height; + + switch (ModeNumber) { + case 0: // 80x25 mode, uses 8x16 font + pabFont = g_abVgaFont_8x16; + height = 16; + break; + case 1: // 80x50 mode, uses 8x8 font + pabFont = g_abVgaFont_8x8; + height = 8; + break; + default: + ASSERT(0); // Valid mode numbers checked above + return EFI_UNSUPPORTED; + } + // Enable font map access + { + /* Write sequencer registers */ + ASMOutU16(0x3c4, 0x0100); + ASMOutU16(0x3c4, 0x0402); + ASMOutU16(0x3c4, 0x0704); + ASMOutU16(0x3c4, 0x0300); + /* Write graphics controller registers */ + ASMOutU16(0x3ce, 0x0204); + ASMOutU16(0x3ce, 0x0005); + ASMOutU16(0x3ce, 0x0406); + } + + for (i = 0; i < 256; ++i) + { + int offChr = i * height; + int offDst = offBase + i * 32; + CopyMem((UINT8 *)(UINTN)0xA0000 + offDst, pabFont + offChr, height); + } + + // Set the CRTC Maximum Scan Line register + ASMOutU16(0x3d4, 0x4009 | ((height - 1) << 8)); + + // Disable font map access again + { + /* Write sequencer registers */ + ASMOutU16(0x3c4, 0x0100); + ASMOutU16(0x3c4, 0x0302); + ASMOutU16(0x3c4, 0x0304); + ASMOutU16(0x3c4, 0x0300); + /* Write graphics controller registers */ + ASMOutU16(0x3ce, 0x0004); + ASMOutU16(0x3ce, 0x1005); + ASMOutU16(0x3ce, 0x0e06); + } + } + + return EFI_SUCCESS; +} + + + + +/** @copydoc EFI_COMPONENT_NAME_GET_DRIVER_NAME */ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortCN_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, OUT CHAR16 **DriverName) +{ + return LookupUnicodeString2(Language, + This->SupportedLanguages, + &g_aVBoxMiniPortDriverLangAndNames[0], + DriverName, + TRUE); +} + +/** @copydoc EFI_COMPONENT_NAME_GET_CONTROLLER_NAME */ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortCN_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, OUT CHAR16 **ControllerName) +{ + /** @todo try query the protocol from the controller and forward the query. */ + return EFI_UNSUPPORTED; +} + + + + +/** @copydoc EFI_COMPONENT_NAME2_GET_DRIVER_NAME */ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortCN2_GetDriverName(IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, OUT CHAR16 **DriverName) +{ + return LookupUnicodeString2(Language, + This->SupportedLanguages, + &g_aVBoxMiniPortDriverLangAndNames[0], + DriverName, + FALSE); +} + +/** @copydoc EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME */ +static EFI_STATUS EFIAPI +VBoxVgaMiniPortCN2_GetControllerName(IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, OUT CHAR16 **ControllerName) +{ + /** @todo try query the protocol from the controller and forward the query. */ + return EFI_UNSUPPORTED; +} + diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaMiniPortDxe.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaMiniPortDxe.inf new file mode 100644 index 00000000..6786517b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxVgaMiniPortDxe/VBoxVgaMiniPortDxe.inf @@ -0,0 +1,70 @@ +# $Id: VBoxVgaMiniPortDxe.inf $ +## @file +# VBoxVgaMiniPortDxe.inf - VgaMiniPort Protocol module declaration. +# + + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + + + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VBoxVgaMiniPortDxe + FILE_GUID = AE8558FD-9DA9-4FA1-AE16-94456C977244 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + ENTRY_POINT = DxeInitializeVBoxVgaMiniPort + +[Sources.common] + VBoxVgaMiniPortDxe.c + VBoxVgaFonts.h + VBoxVgaFont-8x14.h + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + VBoxPkg/VBoxPkg.dec + +[LibraryClasses] + UefiLib + DebugLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + MemoryAllocationLib + +[Protocols] + gEfiPciIoProtocolGuid ## TO_START + gEfiVgaMiniPortProtocolGuid ## BY_START |