- Compile a UEFI driver template created from UEFI Driver Wizard
- Test driver w/ Windows Emulation using UEFI Shell 2.0
- Port code into the template driver
Use this lab, if you’re not able to create a UEFI Driver Template using the UEFI Driver Wizard.
If UEFI Driver Wizard does not work:
Copy the directory UefiDriverTemplate
from
. . ./FW/LabSampleCode/ to C:/FW/edk2-ws/edk2
Rename Directory UefiDriverTemplate to MyWizardDriver
Review UEFI Driver Wizard Lab: https://gitpitch.com/tianocore-training/UEFI_Driver_Wizard_Win_lab/master#/ for protocols produced and which are being consumed
In this lab, you’ll build a UEFI Driver created by the UEFI Driver Wizard.
You will include the driver in the EmulatorPkg project.
Build the UEFI Driver from the Driver Wizard
Two Ways to Compile a Driver |
|
Standalone |
In a Project |
The build command directly compiles the .INF file |
Include the .INF file in the project’s .DSC file |
Results: The driver’s .EFI file is located in the Build directory |
Results: The driver’s .EFI file is a part of the project in the Build directory |
- Perform Lab Setup from previous EmulatorPkg Labs
- Open `C:/FW/edk2-ws/edk2/EmulatorPkg/EmulatorPkg.dsc`
- Add the following to the `[Components]` section:
*Hint:*add to the last module in the
[Components]
section - Save and close the file `C:/FW/edk2-ws/edk2/EmulatorPkg/EmulatorPkg.dsc`
# Add new modules here
MyWizardDriver/MyWizardDriver.inf
Open a VS Command Prompt and type: cd C:/FW/edk2-ws
then
C:/FW/edk2-ws/> setenv.bat
C:/FW/edk2-ws/> cd edk2
C:/FW/edk2-ws/edk2> edksetup
Build MyWizardDriver
C:/FW/edk2-ws/edk2> Build
C:/FW/edk2-ws/edk2> RunEmulator.bat
Load the UEFI Driver from the shell
At the Shell prompt, type Shell> fs0:
Type: FS0:\> load MyWizardDriver.efi
Build ERRORS: Copy the solution files from ~/FW/LabSampleCode/LabSolutions/LessonC.1
to C:/FW/edk2-ws/edk2/MyWizardDriver
At the shell prompt Type: `drivers`
Verify the UEFI Shell loaded the new driver.
The drivers
command will display the driver information and a driver handle number ("a9" in the example screenshot )
At the shell prompt using the handle from the `drivers` command, Type: `dh -d a9`
Note: The value a9
is the driver handle for MyWizardDriver. The handle value may change based on your system configuration.(see example screenshot - right)
At the shell prompt using the handle from the `drivers` command, Type: `unload a9`
See example screenshot - right
Type: drivers
again
Notice results of unload
command
Exit, type FS0:/ > Reset
Note:
END of Lab 2
### Lab 3: Component Name
In this lab, you’ll change the information reported to the drivers command using the ComponentName and ComponentName2 protocols.
- Open
C:/FW/edk2-ws/edk2/MyWizardDriver/ComponentName.c
- Change the string returned by the driver from
MyWizardDriver
to:UEFI Sample Driver
/// Table of driver names
///
GLOBAL_REMOVE_IF_UNREFERENCED
EFI_UNICODE_STRING_TABLE mMyWizardDriverDriverNameTable[] = {
{ "eng;en", (CHAR16 *)L"UEFI Sample Driver" },
{ NULL, NULL }
};
- Save and close the file:
C:/FW/edk2-ws/edk2/MyWizardDriver/ComponentName.c
Note:
The localization is using both the RFC 4646 and the ISO 639-2 Lanuage codes
as seen with the "eng;en
"
Build the MyWizardDriver
C:/FW/edk2-ws/edk2> Build
C:/FW/edk2-ws/edk2> RunEmulator.bat
-
Load the UEFI Driver from the shell
-
At the Shell prompt, type
Shell> fs0:
-
Type:
FS0:\>
load MyWizardDriver.efi
Type: drivers
Observe the change in the string that the driver returned
Exit, type FS0:/> Reset
Note: Lab 3 finished
The UEFI Driver Wizard produced a starting point for driver porting … so now what?
In this lab, you’ll port the “Supported” and “Start” functions for the UEFI driver
Review the Driver Binding Protocol
- Port Supported() to check for a specific protocol before returning ‘Success’
- Port Start() to allocate a memory buffer and fill it with a specific value
- Stop() Stops a driver from managing a controller
The UEFI Driver Wizard produced a Supported()
function but it only returns EFI_UNSUPPORTED
Supported Goals:
- Checks if the driver supports the device for the specified controller handle
- Associates the driver with the Serial I/O protocol
- Helps locate a protocol’s specific GUID through UEFI Boot Services’ function
EDK II has libraries to help with porting UEFI Drivers
AllocateZeroPool()
include -[MemoryAllocationLib.h]
SetMem16()
include -[BaseMemoryLib.h]
Check the MdePkg with libraries help file (.chm format)
Open C:/FW/edk2-ws/edk2/MyWizardDriver/MyWizardDriver.c
Locate MyWizardDriverDriverBindingSupported()
,
the supported function for this driver and comment out the "//
" in the line: "return EFI_UNSUPPORTED;
"
EFI_STATUS
EFIAPI
MyWizardDriverDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
// return EFI_UNSUPPORTED;
}
copy and past (next slide)
Note:
This code checks for a specific protocol before returning a status for the supported function (EFI_SUCCESS if the protocol GUID exists).
Copy & Paste the following code for the supported function MyWizardDriverDriverBindingSupported()
:
EFI_STATUS Status;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiSerialIoProtocolGuid,
(VOID **) &SerialIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE
);
if (EFI_ERROR (Status)) {
return Status; // Bail out if OpenProtocol returns an error
}
// We're here because OpenProtocol was a success, so clean up
gBS->CloseProtocol (
ControllerHandle,
&gEfiSerialIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_SUCCESS;
Note: end of copy & paste
- Open
C:/FW/edk2-ws/edk2/MyWizardDriver/MyWizardDriver.h
- Notice the following include statement is already added by the driver wizard:
// Produced Protocols
//
#include <Protocol/SerialIo.h>
- Review the Libraries section and see that UEFI Driver Wizard automatically includes library headers based on the form information. Also other common libary headers were included
// Libraries
//
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
#include <Library/DevicePathLib.h>
#include <Library/DebugLib.h>
Copy & Paste the following in MyWizardDriver.c
after the #include “MyWizardDriver.h”
line
#define DUMMY_SIZE 100*16 // Dummy buffer
CHAR16 *DummyBufferfromStart = NULL;
Locate MyWizardDriverDriverBindingStart()
, the start function for this driver and comment out the "//
" in the line "return EFI_UNSUPPORTED;
"
EFI_STATUS
EFIAPI
MyWizardDriverDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
// return EFI_UNSUPPORTED;
}
Copy & Paste the following code for the start function MyWizardDriverDriverBindingStart()
:
if (DummyBufferfromStart == NULL) { // was buffer already allocated?
DummyBufferfromStart = (CHAR16*)AllocateZeroPool (DUMMY_SIZE * sizeof(CHAR16));
}
if (DummyBufferfromStart == NULL) {
return EFI_OUT_OF_RESOURCES; // Exit if the buffer isn’t there
}
SetMem16 (DummyBufferfromStart, (DUMMY_SIZE * sizeof(CHAR16)), 0x0042); // Fill buffer
return EFI_SUCCESS;
- Notice the Library calls to
AllocateZeroPool()
andSetMem16()
- The
Start()
function is where there would be calls to "gBS->InstallMultipleProtocolInterfaces()
"
Note:
- This code checks for an allocated memory buffer. If the buffer doesn’t exist, memory will be allocated and filled with an initial value (0x0042).
- this lab's start does not do anything useful but if it did it would make calls to gBS->InstallMultipleProtocolInterfaces() to produce potocols and manage other handle devices
UEFI drivers can use the EDK II debug library
DEBUG( )
include -[DebugLib.h]
DEBUG()
Macro statements can show status progress interest points throughout the driver code
Copy & Paste the following DEBUG ()
macros for the supported function:
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiSerialIoProtocolGuid,
(VOID **)&SerialIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE
);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_INFO, "[MyWizardDriver] Not Supported \r\n"));
return Status; // Bail out if OpenProtocol returns an error
}
// We're here because OpenProtocol was a success, so clean up
gBS->CloseProtocol(
ControllerHandle,
&gEfiSerialIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
DEBUG((EFI_D_INFO, "[MyWizardDriver] Supported SUCCESS\r\n"));
return EFI_SUCCESS;
Copy & Paste the following `DEBUG` macro for the Start function just before the `return EFI_SUCCESS;` statement
DEBUG ((EFI_D_INFO, "\r\n***\r\n[MyWizardDriver] Buffer 0x%p\r\n", DummyBufferfromStart));
return EFI_SUCCESS;
Note: This debug macro displays the memory address of the allocated buffer on the debug console
Save C:/FW/edk2-ws/edk2/MyWizardDriver/MyWizardDriver.c
Build the MyWizardDriver
C:/FW/edk2-ws/edk2> Build
C:/FW/edk2-ws/edk2> RunEmulator.bat
Load the UEFI Driver from the shell
Shell> fs0:
FS0:\> load MyWizardDriver.efi
- Check the VS console output.
- Notice Debug messages indicate the driver did not return EFI_SUCCESS from the “
Supported()
” function most of the time. - See that the "
Start()
" function did get called and a Buffer was allocated.
Exit, type `FS0:/ > Reset`
Note:
use the right-side scroll bar with mouse to scroll back to see the “Supported SUCCESS”
Finished Lab 4
- In this lab you’ll create a non-volatile UEFI variable (NVRAM), and set and get the variable in the
Start()
function - Use Runtime services to "
SetVariable()
" and "GetVariable()
"
Note:
On systems without a serial port, the code from previous lab will not work since the Serial Protocol GUID does not exist.
With QEMU there is a serial device so the driver’s start function would then start to manage the serial port by creating child handles.
- Create .h file with new `typedef` definition and its own `GUID`
- Include the new .h file in the driver's top .h file
- `Supported()` make a call to a new function to set/get the new NVRam Variable
- Before `EntryPoint()` add the new function `CreateNVVariable()` to the driver.c file.
Create a new file in your editor called: "MyWizardDriverNVDataStruc.h
"
Copy, Paste and then Save this file
#ifndef _MYWIZARDDRIVERNVDATASTRUC_H_
#define _MYWIZARDDRIVERNVDATASTRUC_H_
#include <Guid/HiiPlatformSetupFormset.h>
#include <Guid/HiiFormMapMethodGuid.h>
#define MYWIZARDDRIVER_VAR_GUID \
{ \
0x363729f9, 0x35fc, 0x40a6, 0xaf, 0xc8, 0xe8, 0xf5, 0x49, 0x11, 0xf1, 0xd6 \
}
#pragma pack(1)
typedef struct {
UINT16 MyWizardDriverStringData[20];
UINT8 MyWizardDriverHexData;
UINT8 MyWizardDriverBaseAddress;
UINT8 MyWizardDriverChooseToEnable;
} MYWIZARDDRIVER_CONFIGURATION;
#pragma pack()
#endif
Note:
-
In order to set, retrieve, and use the UEFI variable, it requires the GUID reference that you just added.
-
another Note: For this lab, you were provided a GUID file. You can also generate a GUID through the UEFI Driver Wizard or Guidgenerator.com. But for the purposes of the lab, use the one above.
Open "`C:/FW/edk2-ws/edk2/MyWizardDriver/MyWizardDriver.c`"
Copy & Paste the following 4 lines after the #include "MyWizardDriver.h"
statement:
#include "MyWizardDriver.h"
EFI_GUID mMyWizardDriverVarGuid = MYWIZARDDRIVER_VAR_GUID;
CHAR16 mVariableName[] = L"MWD_NVData"; // Use Shell "Dmpstore" to see
MYWIZARDDRIVER_CONFIGURATION mMyWizDrv_Conf_buffer;
MYWIZARDDRIVER_CONFIGURATION *mMyWizDrv_Conf = &mMyWizDrv_Conf_buffer; //use the pointer
Locate "`MyWizardDriverDriverBindingStart ()`" function
Copy & Paste the following line at the beginning of the start function to declare a local variable
EFI_STATUS Status;
Copy & Paste the below 6 lines just before the line "return EFI_SUPPORTED
": 1) new call to "CreateNVVariable();
" 2-6) if
statement with DEBUG and 7) "return
" as below:
Status = CreateNVVariable();
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "[MyWizardDriver] NV Variable already created \r\n"));
}
else {
DEBUG((EFI_D_ERROR, "[MyWizardDriver] Created NV Variable in the Start \r\n"));
}
return EFI_SUCCESS;
Copy & Paste the new function before the call to MyWizardDriverDriverEntryPoint()
EFI_STATUS
EFIAPI
CreateNVVariable()
{
EFI_STATUS Status;
UINTN BufferSize;
BufferSize = sizeof (MYWIZARDDRIVER_CONFIGURATION);
Status = gRT->GetVariable(
mVariableName,
&mMyWizardDriverVarGuid,
NULL,
&BufferSize,
mMyWizDrv_Conf
);
if (EFI_ERROR(Status)) { // Not definded yet so add it to the NV Variables.
if (Status == EFI_NOT_FOUND) {
Status = gRT->SetVariable(
mVariableName,
&mMyWizardDriverVarGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (MYWIZARDDRIVER_CONFIGURATION),
mMyWizDrv_Conf // buffer is 000000 now for first time set
);
DEBUG((EFI_D_INFO, "[MyWizardDriver] Variable %s created in NVRam Var\r\n", mVariableName));
return EFI_SUCCESS;
}
}
// already defined once
return EFI_UNSUPPORTED;
}
Open "C:/FW/edk2-ws/edk2/MyWizardDriver/MyWizardDriver.h
"
Copy & Paste the following "#include" after the list of library include statements:
// Libraries
// . . .
#include <Library/UefiRuntimeServicesTableLib.h>
Copy & Paste the following "#include" after the list of protocol include statements:
// Produced Protocols
// . . .
#include "MyWizardDriverNVDataStruc.h"
Save "C:/FW/edk2-ws/edk2/MyWizardDriver/MyWizardDriver.h
"
Save "C:/FW/edk2-ws/edk2/MyWizardDriver/MyWizardDriver.c
"
Build the MyWizardDriver
C:/FW/edk2-ws/edk2> Build
C:/FW/edk2-ws/edk2> RunEmulator.bat
Load the UEFI Driver
Shell> fs0:
FS0:\>
load MyWizardDriver.efi
Observe the Buffer address returned by the debug statement in the VS Command window and the new NV Variable was created
Use the Buffer address pointer in the previous slide then use the “mem
” command
At the Shell prompt, use the mem
command with the address of the buffer observed from the VS Command window then type FS0:\>
mem 0x1be2d978018
Observe the Buffer is filled with the letter "B" or 0x0042
At the Shell prompt, type FS0:\> dmpstore -all -b
Observe new the NVRAM variable "MWD_NVData
" was created and filled with 0x00s
Exit, type FS0:/> Reset
Note:
End of Lab 5
In this lab, you’ll port the driver’s “Unload” and “Stop” functions to free any resources the driver allocated when it was loaded and started.
Open "C:/FW/edk2-ws/edk2/MyWizardDriver/MyWizardDriver.c
"
Locate "MyWizardDriverUnload ()
" function
Copy & Paste the following "if
" and "DEBUG
" statements before the "return EFI_SUCCESS;
" statement.
// Do any additional cleanup that is required for this driver
//
if (DummyBufferfromStart != NULL) {
FreePool(DummyBufferfromStart);
DEBUG((EFI_D_INFO, "[MyWizardDriver] Unload, clear buffer\r\n"));
}
DEBUG((EFI_D_INFO, "[MyWizardDriver] Unload success\r\n"));
return EFI_SUCCESS;
Note:
- The code will deallocate the buffer using the FreePool library function.
- Notice that the FreePool is called since there was an allocate call to get memory in the start function
- for the Unload similar "unwind" calls such as
- free memory
- Uninstall Protocol Interfaces
- Disconnect Controller calls are made
Locate "MyWizardDriverDriverBindingStop()
" function
Comment out with "//
" before the "return EFI_UNSUPPORTED;
" statement.
Copy & Paste the following "if
" and "DEBUG
" statements in place of the "return EFI_UNSUPPORTED;
" statement.
if (DummyBufferfromStart != NULL) {
FreePool(DummyBufferfromStart);
DEBUG((EFI_D_INFO, "[MyWizardDriver] Stop, clear buffer\r\n"));
}
DEBUG((EFI_D_INFO, "[MyWizardDriver] Stop, EFI_SUCCESS\r\n"));
return EFI_SUCCESS;
// return EFI_UNSUPPORTED;
}
Save & Close "MyWizardDriverDriver.c
"
Note:
- The code will deallocate the buffer using the FreePool library function.
- This a duplicate of the check performed in the unload function, in case the stop function was executed prior to unload.
- Notice that the FreePool is called since there was an allocate call to get memory in the start function
- for the stop similar "unwind" calls to mimic same functions in the start
- free memory for Alocate memory
- Uninstall Protocol Interfaces for Install interfaces
Build the MyWizardDriver
C:/FW/edk2-ws/edk2> Build
C:/FW/edk2-ws/edk2> RunEmulator.bat
Load the UEFI Driver
Shell> fs0:
FS0:\>
load MyWizardDriver.efi
Observe the Buffer address is at 0x25DE4F5C018
as this slide example
At the Shell prompt, type FS0:\>
drivers
Observe the handle is "A9
" as this slide example
Type: mem 0x25DE4F5C018
Observe the buffer was filled with the "0x0042"
At the Shell prompt, type FS0:\>
unload a9
Observe the DEBUG messages from the Unload
Type Drivers
again to verify
At the Shell prompt, type FS0:\>
mem 0x25DE4F5C018 -b
Observe the buffer is now NOT filled
Exit, type FS0:/> Reset
Note: End of Lab 6
- Adding strings and forms to setup (HII)
- Publish & consume protocols
- Hardware initialization
Refer to the UEFI Drivers Writer’s Guide for more tips – Pdf link
Note: Use the UEFI Driver Wizard to create a starting point for new drivers on EDK II
- Compile a UEFI driver template created fromUEFI Driver Wizard
- Test driver w/ Windows emulation using UEFI Shell 2.0
- Port code into the template driver
Return to Training schedule main page https://github.com/tianocore-training/Tianocore_Training_Contents/wiki
Redistribution and use in source (original document form) and 'compiled‘ forms (converted to PDF, epub, HTML and other formats) with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code (original document form) must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified.
Redistributions in compiled form (transformed to other DTDs, converted to PDF, epub, HTML and other formats) 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.
THIS DOCUMENTATION IS PROVIDED BY TIANOCORE PROJECT "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 TIANOCORE PROJECT 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 DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright (c) 2020, Intel Corporation. All rights reserved.