Highlights this time around: - Lots of minor spec compliance fixes - Support full range of GOP BLT commands - More fine grained error checking - Network fixes (init, DP) - Lots of other bug fixes... -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABAgAGBQJay2U6AAoJECszeR4D/txgE8UP/A5xHq707fv4CcQez/cZj/KE Twv4HoSY28by71VWS+SjXXTKkx+XGM/2tLzl6dQCB9V1aFUwtB12zu4mXpplfT8R K+dJU9PgkPKJzWRvn08JgeuXs1RQnfSgXz5J2pvSnJ4mySMlZKuVRGwHxw7DLFPf EZOqkd2+NI90fogGFoZ2l/aAzbDssBI45jDmH0KcPO49/+7XRVYJqpg0s7TQYmhY BeoJ0mAp12Ybpb4E7XDIGKKgCTjOGnftoRmzZyPZPfHPrmVWo2srmtycDbz0f/N0 tEiXIOAo72DzmhL6aPPNJtt1mjXTGU2iavkV74JVqXODCd3bRJxz7TF6R30Nmvfz WMaKM+69Ahior9YJnGv2GUe8ebThxivWUUCB6HmfRh07sGCes6eV3HfklY5Fnglu Gw5pRl9+YBOywuUuyMg/yBPpPyA7b1HaTjwhsYWAB+q7htB6JkPhQruMxdiMaQnC gvOEoguW1XZo4MSudfC/SUOdayGp+KsQiEivi8gUh11uBPA2nj1Bh028Pj7tTET3 J4C527uF/ivKwhx2hUHq2YkkgxHWz0Z98LFG3Xkj3q3fZFRwSEzugik1TswT8z3l qPxWgwX7Q9dxNYLW4ogvnlXU/09APRg9fVeaXxOfqJTHCCn9PHehbKKpkJ9g3cOt lPr1Q/4C5daK8dnxDzYo =fzbe -----END PGP SIGNATURE----- Merge tag 'signed-efi-next' of git://github.com/agraf/u-boot Patch queue for efi - 2018-04-09 Highlights this time around: - Lots of minor spec compliance fixes - Support full range of GOP BLT commands - More fine grained error checking - Network fixes (init, DP) - Lots of other bug fixes...master
commit
844fb498cc
@ -1,86 +0,0 @@ |
|||||||
# |
|
||||||
# Copyright (C) 2015 Google, Inc |
|
||||||
# |
|
||||||
# SPDX-License-Identifier: GPL-2.0+ |
|
||||||
# |
|
||||||
|
|
||||||
EFI on U-Boot |
|
||||||
============= |
|
||||||
This document provides information about the implementation of the UEFI API [1] |
|
||||||
in U-Boot. |
|
||||||
|
|
||||||
|
|
||||||
=========== Table of Contents =========== |
|
||||||
|
|
||||||
Motivation |
|
||||||
How do I get it? |
|
||||||
Status |
|
||||||
Future work |
|
||||||
|
|
||||||
|
|
||||||
Motivation |
|
||||||
---------- |
|
||||||
|
|
||||||
With this API support in place, you can run any UEFI payload (such as the Linux |
|
||||||
kernel, grub2 or gummiboot) on U-Boot. This dramatically simplifies boot loader |
|
||||||
configuration, as U-Boot based systems now look and feel (almost) the same way |
|
||||||
as TianoCore based systems. |
|
||||||
|
|
||||||
How do I get it? |
|
||||||
---------------- |
|
||||||
|
|
||||||
EFI support for 32bit ARM and AArch64 is already included in U-Boot. All you |
|
||||||
need to do is enable |
|
||||||
|
|
||||||
CONFIG_CMD_BOOTEFI=y |
|
||||||
CONFIG_EFI_LOADER=y |
|
||||||
|
|
||||||
in your .config file and you will automatically get a bootefi command to run |
|
||||||
an efi application as well as snippet in the default distro boot script that |
|
||||||
scans for removable media efi binaries as fallback. |
|
||||||
|
|
||||||
Status |
|
||||||
------ |
|
||||||
|
|
||||||
I am successfully able to run grub2 and Linux EFI binaries with this code on |
|
||||||
ARMv7 as well as AArch64 systems. |
|
||||||
|
|
||||||
When enabled, the resulting U-Boot binary only grows by ~10KB, so it's very |
|
||||||
light weight. |
|
||||||
|
|
||||||
All storage devices are directly accessible from the uEFI payload |
|
||||||
|
|
||||||
Removable media booting (search for /efi/boot/boota{a64,arm}.efi) is supported. |
|
||||||
|
|
||||||
Simple use cases like "Plug this SD card into my ARM device and it just |
|
||||||
boots into grub which boots into Linux", work very well. |
|
||||||
|
|
||||||
|
|
||||||
Running HelloWord.efi |
|
||||||
--------------------- |
|
||||||
|
|
||||||
You can run a simple 'hello world' EFI program in U-Boot. |
|
||||||
Enable the option CONFIG_CMD_BOOTEFI_HELLO. |
|
||||||
|
|
||||||
Then you can boot into U-Boot and type: |
|
||||||
|
|
||||||
> bootefi hello |
|
||||||
|
|
||||||
The 'hello world EFI' program will then run, print a message and exit. |
|
||||||
|
|
||||||
|
|
||||||
Future work |
|
||||||
----------- |
|
||||||
|
|
||||||
Of course, there are still a few things one could do on top: |
|
||||||
|
|
||||||
- Improve disk media detection (don't scan, use what information we |
|
||||||
have) |
|
||||||
- Add EFI variable support using NVRAM |
|
||||||
- Add GFX support |
|
||||||
- Make EFI Shell work |
|
||||||
- Network device support |
|
||||||
- Support for payload exit |
|
||||||
- Payload Watchdog support |
|
||||||
|
|
||||||
[1] http://uefi.org/ |
|
@ -0,0 +1,332 @@ |
|||||||
|
<!-- |
||||||
|
Copyright (c) 2018 Heinrich Schuchardt |
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-2.0+ |
||||||
|
--> |
||||||
|
|
||||||
|
# UEFI on U-Boot |
||||||
|
|
||||||
|
The Unified Extensible Firmware Interface Specification (UEFI) [1] has become |
||||||
|
the default for booting on AArch64 and x86 systems. It provides a stable API for |
||||||
|
the interaction of drivers and applications with the firmware. The API comprises |
||||||
|
access to block storage, network, and console to name a few. The Linux kernel |
||||||
|
and boot loaders like GRUB or the FreeBSD loader can be executed. |
||||||
|
|
||||||
|
## Building for UEFI |
||||||
|
|
||||||
|
The UEFI standard supports only little endian systems. The UEFI support can be |
||||||
|
activated for ARM and x86 by specifying |
||||||
|
|
||||||
|
CONFIG_CMD_BOOTEFI=y |
||||||
|
CONFIG_EFI_LOADER=y |
||||||
|
|
||||||
|
in the .config file. |
||||||
|
|
||||||
|
Support for attaching virtual block devices, e.g. iSCSI drives connected by the |
||||||
|
loaded UEFI application [3], requires |
||||||
|
|
||||||
|
CONFIG_BLK=y |
||||||
|
CONFIG_PARTITIONS=y |
||||||
|
|
||||||
|
### Executing a UEFI binary |
||||||
|
|
||||||
|
The bootefi command is used to start UEFI applications or to install UEFI |
||||||
|
drivers. It takes two parameters |
||||||
|
|
||||||
|
bootefi <image address> [fdt address] |
||||||
|
|
||||||
|
* image address - the memory address of the UEFI binary |
||||||
|
* fdt address - the memory address of the flattened device tree |
||||||
|
|
||||||
|
Below you find the output of an example session starting GRUB. |
||||||
|
|
||||||
|
=> load mmc 0:2 ${fdt_addr_r} boot/dtb |
||||||
|
29830 bytes read in 14 ms (2 MiB/s) |
||||||
|
=> load mmc 0:1 ${kernel_addr_r} efi/debian/grubaa64.efi |
||||||
|
reading efi/debian/grubaa64.efi |
||||||
|
120832 bytes read in 7 ms (16.5 MiB/s) |
||||||
|
=> bootefi ${kernel_addr_r} ${fdt_addr_r} |
||||||
|
|
||||||
|
The environment variable 'bootargs' is passed as load options in the UEFI system |
||||||
|
table. The Linux kernel EFI stub uses the load options as command line |
||||||
|
arguments. |
||||||
|
|
||||||
|
### Executing the boot manager |
||||||
|
|
||||||
|
The UEFI specfication foresees to define boot entries and boot sequence via UEFI |
||||||
|
variables. Booting according to these variables is possible via |
||||||
|
|
||||||
|
bootefi bootmgr [fdt address] |
||||||
|
|
||||||
|
As of U-Boot v2018.03 UEFI variables are not persisted and cannot be set at |
||||||
|
runtime. |
||||||
|
|
||||||
|
### Executing the built in hello world application |
||||||
|
|
||||||
|
A hello world UEFI application can be built with |
||||||
|
|
||||||
|
CONFIG_CMD_BOOTEFI_HELLO_COMPILE=y |
||||||
|
|
||||||
|
It can be embedded into the U-Boot binary with |
||||||
|
|
||||||
|
CONFIG_CMD_BOOTEFI_HELLO=y |
||||||
|
|
||||||
|
The bootefi command is used to start the embedded hello world application. |
||||||
|
|
||||||
|
bootefi hello [fdt address] |
||||||
|
|
||||||
|
Below you find the output of an example session. |
||||||
|
|
||||||
|
=> bootefi hello ${fdtcontroladdr} |
||||||
|
## Starting EFI application at 01000000 ... |
||||||
|
WARNING: using memory device/image path, this may confuse some payloads! |
||||||
|
Hello, world! |
||||||
|
Running on UEFI 2.7 |
||||||
|
Have SMBIOS table |
||||||
|
Have device tree |
||||||
|
Load options: root=/dev/sdb3 init=/sbin/init rootwait ro |
||||||
|
## Application terminated, r = 0 |
||||||
|
|
||||||
|
The environment variable fdtcontroladdr points to U-Boot's internal device tree |
||||||
|
(if available). |
||||||
|
|
||||||
|
### Executing the built-in selftest |
||||||
|
|
||||||
|
An UEFI selftest suite can be embedded in U-Boot by building with |
||||||
|
|
||||||
|
CONFIG_CMD_BOOTEFI_SELFTEST=y |
||||||
|
|
||||||
|
For testing the UEFI implementation the bootefi command can be used to start the |
||||||
|
selftest. |
||||||
|
|
||||||
|
bootefi selftest [fdt address] |
||||||
|
|
||||||
|
The environment variable 'efi_selftest' can be used to select a single test. If |
||||||
|
it is not provided all tests are executed except those marked as 'on request'. |
||||||
|
If the environment variable is set to 'list' a list of all tests is shown. |
||||||
|
|
||||||
|
Below you can find the output of an example session. |
||||||
|
|
||||||
|
=> setenv efi_selftest simple network protocol |
||||||
|
=> bootefi selftest |
||||||
|
Testing EFI API implementation |
||||||
|
Selected test: 'simple network protocol' |
||||||
|
Setting up 'simple network protocol' |
||||||
|
Setting up 'simple network protocol' succeeded |
||||||
|
Executing 'simple network protocol' |
||||||
|
DHCP Discover |
||||||
|
DHCP reply received from 192.168.76.2 (52:55:c0:a8:4c:02) |
||||||
|
as broadcast message. |
||||||
|
Executing 'simple network protocol' succeeded |
||||||
|
Tearing down 'simple network protocol' |
||||||
|
Tearing down 'simple network protocol' succeeded |
||||||
|
Boot services terminated |
||||||
|
Summary: 0 failures |
||||||
|
Preparing for reset. Press any key. |
||||||
|
|
||||||
|
## The UEFI life cycle |
||||||
|
|
||||||
|
After the U-Boot platform has been initialized the UEFI API provides two kinds |
||||||
|
of services |
||||||
|
|
||||||
|
* boot services and |
||||||
|
* runtime services. |
||||||
|
|
||||||
|
The API can be extended by loading UEFI drivers which come in two variants |
||||||
|
|
||||||
|
* boot drivers and |
||||||
|
* runtime drivers. |
||||||
|
|
||||||
|
UEFI drivers are installed with U-Boot's bootefi command. With the same command |
||||||
|
UEFI applications can be executed. |
||||||
|
|
||||||
|
Loaded images of UEFI drivers stay in memory after returning to U-Boot while |
||||||
|
loaded images of applications are removed from memory. |
||||||
|
|
||||||
|
An UEFI application (e.g. an operating system) that wants to take full control |
||||||
|
of the system calls ExitBootServices. After a UEFI application calls |
||||||
|
ExitBootServices |
||||||
|
|
||||||
|
* boot services are not available anymore |
||||||
|
* timer events are stopped |
||||||
|
* the memory used by U-Boot except for runtime services is released |
||||||
|
* the memory used by boot time drivers is released |
||||||
|
|
||||||
|
So this is a point of no return. Afterwards the UEFI application can only return |
||||||
|
to U-Boot by rebooting. |
||||||
|
|
||||||
|
## The UEFI object model |
||||||
|
|
||||||
|
UEFI offers a flexible and expandable object model. The objects in the UEFI API |
||||||
|
are devices, drivers, and loaded images. These objects are referenced by |
||||||
|
handles. |
||||||
|
|
||||||
|
The interfaces implemented by the objects are referred to as protocols. These |
||||||
|
are identified by GUIDs. They can be installed and uninstalled by calling the |
||||||
|
appropriate boot services. |
||||||
|
|
||||||
|
Handles are created by the InstallProtocolInterface or the |
||||||
|
InstallMultipleProtocolinterfaces service if NULL is passed as handle. |
||||||
|
|
||||||
|
Handles are deleted when the last protocol has been removed with the |
||||||
|
UninstallProtocolInterface or the UninstallMultipleProtocolInterfaces service. |
||||||
|
|
||||||
|
Devices offer the EFI_DEVICE_PATH_PROTOCOL. A device path is the concatenation |
||||||
|
of device nodes. By their device paths all devices of a system are arranged in a |
||||||
|
tree. |
||||||
|
|
||||||
|
Drivers offer the EFI_DRIVER_BINDING_PROTOCOL. This protocol is used to connect |
||||||
|
a driver to devices (which are referenced as controllers in this context). |
||||||
|
|
||||||
|
Loaded images offer the EFI_LOADED_IMAGE_PROTOCOL. This protocol provides meta |
||||||
|
information about the image and a pointer to the unload callback function. |
||||||
|
|
||||||
|
## The UEFI events |
||||||
|
|
||||||
|
In the UEFI terminology an event is a data object referencing a notification |
||||||
|
function which is queued for calling when the event is signaled. The following |
||||||
|
types of events exist: |
||||||
|
|
||||||
|
* periodic and single shot timer events |
||||||
|
* exit boot services events, triggered by calling the ExitBootServices() service |
||||||
|
* virtual address change events |
||||||
|
* memory map change events |
||||||
|
* read to boot events |
||||||
|
* reset system events |
||||||
|
* system table events |
||||||
|
* events that are only triggered programmatically |
||||||
|
|
||||||
|
Events can be created with the CreateEvent service and deleted with CloseEvent |
||||||
|
service. |
||||||
|
|
||||||
|
Events can be assigned to an event group. If any of the events in a group is |
||||||
|
signaled, all other events in the group are also set to the signaled state. |
||||||
|
|
||||||
|
## The UEFI driver model |
||||||
|
|
||||||
|
A driver is specific for a single protocol installed on a device. To install a |
||||||
|
driver on a device the ConnectController service is called. In this context |
||||||
|
controller refers to the device for which the driver is installed. |
||||||
|
|
||||||
|
The relevant drivers are identified using the EFI_DRIVER_BINDING_PROTOCOL. This |
||||||
|
protocol has has three functions: |
||||||
|
|
||||||
|
* supported - determines if the driver is compatible with the device |
||||||
|
* start - installs the driver by opening the relevant protocol with |
||||||
|
attribute EFI_OPEN_PROTOCOL_BY_DRIVER |
||||||
|
* stop - uninstalls the driver |
||||||
|
|
||||||
|
The driver may create child controllers (child devices). E.g. a driver for block |
||||||
|
IO devices will create the device handles for the partitions. The child |
||||||
|
controllers will open the supported protocol with the attribute |
||||||
|
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. |
||||||
|
|
||||||
|
A driver can be detached from a device using the DisconnectController service. |
||||||
|
|
||||||
|
## U-Boot devices mapped as UEFI devices |
||||||
|
|
||||||
|
Some of the U-Boot devices are mapped as UEFI devices |
||||||
|
|
||||||
|
* block IO devices |
||||||
|
* console |
||||||
|
* graphical output |
||||||
|
* network adapter |
||||||
|
|
||||||
|
As of U-Boot 2018.03 the logic for doing this is hard coded. |
||||||
|
|
||||||
|
The development target is to integrate the setup of these UEFI devices with the |
||||||
|
U-Boot driver model. So when a U-Boot device is discovered a handle should be |
||||||
|
created and the device path protocol and the relevant IO protocol should be |
||||||
|
installed. The UEFI driver then would be attached by calling ConnectController. |
||||||
|
When a U-Boot device is removed DisconnectController should be called. |
||||||
|
|
||||||
|
## UEFI devices mapped as U-Boot devices |
||||||
|
|
||||||
|
UEFI drivers binaries and applications may create new (virtual) devices, install |
||||||
|
a protocol and call the ConnectController service. Now the matching UEFI driver |
||||||
|
is determined by iterating over the implementations of the |
||||||
|
EFI_DRIVER_BINDING_PROTOCOL. |
||||||
|
|
||||||
|
It is the task of the UEFI driver to create a corresponding U-Boot device and to |
||||||
|
proxy calls for this U-Boot device to the controller. |
||||||
|
|
||||||
|
In U-Boot 2018.03 this has only been implemented for block IO devices. |
||||||
|
|
||||||
|
### UEFI uclass |
||||||
|
|
||||||
|
An UEFI uclass driver (lib/efi_driver/efi_uclass.c) has been created that |
||||||
|
takes care of initializing the UEFI drivers and providing the |
||||||
|
EFI_DRIVER_BINDING_PROTOCOL implementation for the UEFI drivers. |
||||||
|
|
||||||
|
A linker created list is used to keep track of the UEFI drivers. To create an |
||||||
|
entry in the list the UEFI driver uses the U_BOOT_DRIVER macro specifying |
||||||
|
UCLASS_EFI as the ID of its uclass, e.g. |
||||||
|
|
||||||
|
/* Identify as UEFI driver */ |
||||||
|
U_BOOT_DRIVER(efi_block) = { |
||||||
|
.name = "EFI block driver", |
||||||
|
.id = UCLASS_EFI, |
||||||
|
.ops = &driver_ops, |
||||||
|
}; |
||||||
|
|
||||||
|
The available operations are defined via the structure struct efi_driver_ops. |
||||||
|
|
||||||
|
struct efi_driver_ops { |
||||||
|
const efi_guid_t *protocol; |
||||||
|
const efi_guid_t *child_protocol; |
||||||
|
int (*bind)(efi_handle_t handle, void *interface); |
||||||
|
}; |
||||||
|
|
||||||
|
When the supported() function of the EFI_DRIVER_BINDING_PROTOCOL is called the |
||||||
|
uclass checks if the protocol GUID matches the protocol GUID of the UEFI driver. |
||||||
|
In the start() function the bind() function of the UEFI driver is called after |
||||||
|
checking the GUID. |
||||||
|
The stop() function of the EFI_DRIVER_BINDING_PROTOCOL disconnects the child |
||||||
|
controllers created by the UEFI driver and the UEFI driver. (In U-Boot v2013.03 |
||||||
|
this is not yet completely implemented.) |
||||||
|
|
||||||
|
### UEFI block IO driver |
||||||
|
|
||||||
|
The UEFI block IO driver supports devices exposing the EFI_BLOCK_IO_PROTOCOL. |
||||||
|
|
||||||
|
When connected it creates a new U-Boot block IO device with interface type |
||||||
|
IF_TYPE_EFI, adds child controllers mapping the partitions, and installs the |
||||||
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on these. This can be used together with the |
||||||
|
software iPXE to boot from iSCSI network drives [3]. |
||||||
|
|
||||||
|
This driver is only available if U-Boot is configured with |
||||||
|
|
||||||
|
CONFIG_BLK=y |
||||||
|
CONFIG_PARTITIONS=y |
||||||
|
|
||||||
|
## TODOs as of U-Boot 2018.03 |
||||||
|
|
||||||
|
* unimplemented or incompletely implemented boot services |
||||||
|
* Exit - call unload function, unload applications only |
||||||
|
* ReinstallProtocolInterface |
||||||
|
* UnloadImage |
||||||
|
|
||||||
|
* unimplemented events |
||||||
|
* EVT_RUNTIME |
||||||
|
* EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE |
||||||
|
* event groups |
||||||
|
|
||||||
|
* data model |
||||||
|
* manage events in a linked list |
||||||
|
* manage configuration tables in a linked list |
||||||
|
|
||||||
|
* UEFI drivers |
||||||
|
* support DisconnectController for UEFI block devices. |
||||||
|
|
||||||
|
* support for CONFIG_EFI_LOADER in the sandbox (CONFIG_SANDBOX=y) |
||||||
|
|
||||||
|
* UEFI variables |
||||||
|
* persistence |
||||||
|
* runtime support |
||||||
|
|
||||||
|
## Links |
||||||
|
|
||||||
|
* [1](http://uefi.org/specifications) |
||||||
|
http://uefi.org/specifications - UEFI specifications |
||||||
|
* [2](./driver-model/README.txt) doc/driver-model/README.txt - Driver model |
||||||
|
* [3](./README.iscsi) doc/README.iscsi - iSCSI booting with U-Boot and iPXE |
@ -0,0 +1,89 @@ |
|||||||
|
/*
|
||||||
|
* EFI device path interface |
||||||
|
* |
||||||
|
* Copyright (c) 2017 Leif Lindholm |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
#include <efi_loader.h> |
||||||
|
|
||||||
|
const efi_guid_t efi_guid_device_path_utilities_protocol = |
||||||
|
EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID; |
||||||
|
|
||||||
|
static efi_uintn_t EFIAPI get_device_path_size( |
||||||
|
const struct efi_device_path *device_path) |
||||||
|
{ |
||||||
|
efi_uintn_t sz = 0; |
||||||
|
|
||||||
|
EFI_ENTRY("%p", device_path); |
||||||
|
/* size includes the END node: */ |
||||||
|
if (device_path) |
||||||
|
sz = efi_dp_size(device_path) + sizeof(struct efi_device_path); |
||||||
|
return EFI_EXIT(sz); |
||||||
|
} |
||||||
|
|
||||||
|
static struct efi_device_path * EFIAPI duplicate_device_path( |
||||||
|
const struct efi_device_path *device_path) |
||||||
|
{ |
||||||
|
EFI_ENTRY("%p", device_path); |
||||||
|
return EFI_EXIT(efi_dp_dup(device_path)); |
||||||
|
} |
||||||
|
|
||||||
|
static struct efi_device_path * EFIAPI append_device_path( |
||||||
|
const struct efi_device_path *src1, |
||||||
|
const struct efi_device_path *src2) |
||||||
|
{ |
||||||
|
EFI_ENTRY("%p, %p", src1, src2); |
||||||
|
return EFI_EXIT(efi_dp_append(src1, src2)); |
||||||
|
} |
||||||
|
|
||||||
|
static struct efi_device_path * EFIAPI append_device_node( |
||||||
|
const struct efi_device_path *device_path, |
||||||
|
const struct efi_device_path *device_node) |
||||||
|
{ |
||||||
|
EFI_ENTRY("%p, %p", device_path, device_node); |
||||||
|
return EFI_EXIT(efi_dp_append_node(device_path, device_node)); |
||||||
|
} |
||||||
|
|
||||||
|
static struct efi_device_path * EFIAPI append_device_path_instance( |
||||||
|
const struct efi_device_path *device_path, |
||||||
|
const struct efi_device_path *device_path_instance) |
||||||
|
{ |
||||||
|
EFI_ENTRY("%p, %p", device_path, device_path_instance); |
||||||
|
return EFI_EXIT(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
static struct efi_device_path * EFIAPI get_next_device_path_instance( |
||||||
|
struct efi_device_path **device_path_instance, |
||||||
|
efi_uintn_t *device_path_instance_size) |
||||||
|
{ |
||||||
|
EFI_ENTRY("%p, %p", device_path_instance, device_path_instance_size); |
||||||
|
return EFI_EXIT(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
static bool EFIAPI is_device_path_multi_instance( |
||||||
|
const struct efi_device_path *device_path) |
||||||
|
{ |
||||||
|
EFI_ENTRY("%p", device_path); |
||||||
|
return EFI_EXIT(false); |
||||||
|
} |
||||||
|
|
||||||
|
static struct efi_device_path * EFIAPI create_device_node( |
||||||
|
uint8_t node_type, uint8_t node_sub_type, uint16_t node_length) |
||||||
|
{ |
||||||
|
EFI_ENTRY("%u, %u, %u", node_type, node_sub_type, node_length); |
||||||
|
return EFI_EXIT(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
const struct efi_device_path_utilities_protocol efi_device_path_utilities = { |
||||||
|
.get_device_path_size = get_device_path_size, |
||||||
|
.duplicate_device_path = duplicate_device_path, |
||||||
|
.append_device_path = append_device_path, |
||||||
|
.append_device_node = append_device_node, |
||||||
|
.append_device_path_instance = append_device_path_instance, |
||||||
|
.get_next_device_path_instance = get_next_device_path_instance, |
||||||
|
.is_device_path_multi_instance = is_device_path_multi_instance, |
||||||
|
.create_device_node = create_device_node, |
||||||
|
}; |
@ -0,0 +1,311 @@ |
|||||||
|
/*
|
||||||
|
* efi_selftest_bitblt |
||||||
|
* |
||||||
|
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
* |
||||||
|
* Test the block image transfer in the graphical output protocol. |
||||||
|
* An animated submarine is shown. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <efi_selftest.h> |
||||||
|
|
||||||
|
#define WIDTH 200 |
||||||
|
#define HEIGHT 120 |
||||||
|
#define DEPTH 60 |
||||||
|
|
||||||
|
static const struct efi_gop_pixel BLACK = { 0, 0, 0, 0}; |
||||||
|
static const struct efi_gop_pixel RED = { 0, 0, 255, 0}; |
||||||
|
static const struct efi_gop_pixel ORANGE = { 0, 128, 255, 0}; |
||||||
|
static const struct efi_gop_pixel YELLOW = { 0, 255, 255, 0}; |
||||||
|
static const struct efi_gop_pixel GREEN = { 0, 255, 0, 0}; |
||||||
|
static const struct efi_gop_pixel DARK_BLUE = {128, 0, 0, 0}; |
||||||
|
static const struct efi_gop_pixel LIGHT_BLUE = {255, 192, 192, 0}; |
||||||
|
|
||||||
|
static struct efi_boot_services *boottime; |
||||||
|
static efi_guid_t efi_gop_guid = EFI_GOP_GUID; |
||||||
|
static struct efi_gop *gop; |
||||||
|
static struct efi_gop_pixel *bitmap; |
||||||
|
static struct efi_event *event; |
||||||
|
static efi_uintn_t xpos; |
||||||
|
|
||||||
|
static void ellipse(efi_uintn_t x, efi_uintn_t y, |
||||||
|
efi_uintn_t x0, efi_uintn_t y0, |
||||||
|
efi_uintn_t x1, efi_uintn_t y1, |
||||||
|
const struct efi_gop_pixel col, struct efi_gop_pixel *pix) |
||||||
|
{ |
||||||
|
efi_uintn_t xm = x0 + x1; |
||||||
|
efi_uintn_t ym = y0 + y1; |
||||||
|
efi_uintn_t dx = x1 - x0 + 1; |
||||||
|
efi_uintn_t dy = y1 - y0 + 1; |
||||||
|
|
||||||
|
if (dy * dy * (2 * x - xm) * (2 * x - xm) + |
||||||
|
dx * dx * (2 * y - ym) * (2 * y - ym) <= dx * dx * dy * dy) |
||||||
|
*pix = col; |
||||||
|
} |
||||||
|
|
||||||
|
static void rectangle(efi_uintn_t x, efi_uintn_t y, |
||||||
|
efi_uintn_t x0, efi_uintn_t y0, |
||||||
|
efi_uintn_t x1, efi_uintn_t y1, |
||||||
|
const struct efi_gop_pixel col, struct efi_gop_pixel *pix) |
||||||
|
{ |
||||||
|
if (x >= x0 && y >= y0 && x <= x1 && y <= y1) |
||||||
|
*pix = col; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Notification function, copies image to video. |
||||||
|
* The position is incremented in each call. |
||||||
|
* |
||||||
|
* @event notified event |
||||||
|
* @context pointer to the notification count |
||||||
|
*/ |
||||||
|
static void EFIAPI notify(struct efi_event *event, void *context) |
||||||
|
{ |
||||||
|
efi_uintn_t *pos = context; |
||||||
|
efi_uintn_t dx, sx, width; |
||||||
|
|
||||||
|
if (!pos) |
||||||
|
return; |
||||||
|
|
||||||
|
/* Increment position */ |
||||||
|
*pos += 5; |
||||||
|
if (*pos >= WIDTH + gop->mode->info->width) |
||||||
|
*pos = 0; |
||||||
|
|
||||||
|
width = WIDTH; |
||||||
|
dx = *pos - WIDTH; |
||||||
|
sx = 0; |
||||||
|
if (*pos >= gop->mode->info->width) { |
||||||
|
width = WIDTH + gop->mode->info->width - *pos; |
||||||
|
} else if (*pos < WIDTH) { |
||||||
|
dx = 0; |
||||||
|
sx = WIDTH - *pos; |
||||||
|
width = *pos; |
||||||
|
} |
||||||
|
|
||||||
|
/* Copy image to video */ |
||||||
|
gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, sx, 0, dx, DEPTH, |
||||||
|
width, HEIGHT, WIDTH * sizeof(struct efi_gop_pixel)); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup unit test. |
||||||
|
* |
||||||
|
* @handle: handle of the loaded image |
||||||
|
* @systable: system table |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int setup(const efi_handle_t handle, |
||||||
|
const struct efi_system_table *systable) |
||||||
|
{ |
||||||
|
efi_status_t ret; |
||||||
|
struct efi_gop_pixel pix; |
||||||
|
efi_uintn_t x, y; |
||||||
|
|
||||||
|
boottime = systable->boottime; |
||||||
|
|
||||||
|
/* Create event */ |
||||||
|
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, |
||||||
|
TPL_CALLBACK, notify, (void *)&xpos, |
||||||
|
&event); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("could not create event\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
/* Get graphical output protocol */ |
||||||
|
ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
gop = NULL; |
||||||
|
efi_st_printf("Graphical output protocol is not available.\n"); |
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/* Prepare image of submarine */ |
||||||
|
ret = boottime->allocate_pool(EFI_LOADER_DATA, |
||||||
|
sizeof(struct efi_gop_pixel) * |
||||||
|
WIDTH * HEIGHT, (void **)&bitmap); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("Out of memory\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
for (y = 0; y < HEIGHT; ++y) { |
||||||
|
for (x = 0; x < WIDTH; ++x) { |
||||||
|
pix = DARK_BLUE; |
||||||
|
|
||||||
|
/* Propeller */ |
||||||
|
ellipse(x, y, 35, 55, 43, 75, BLACK, &pix); |
||||||
|
ellipse(x, y, 36, 56, 42, 74, LIGHT_BLUE, &pix); |
||||||
|
|
||||||
|
ellipse(x, y, 35, 75, 43, 95, BLACK, &pix); |
||||||
|
ellipse(x, y, 36, 76, 42, 94, LIGHT_BLUE, &pix); |
||||||
|
|
||||||
|
/* Shaft */ |
||||||
|
rectangle(x, y, 35, 73, 100, 77, BLACK, &pix); |
||||||
|
|
||||||
|
/* Periscope */ |
||||||
|
ellipse(x, y, 120, 10, 160, 50, BLACK, &pix); |
||||||
|
ellipse(x, y, 121, 11, 159, 59, YELLOW, &pix); |
||||||
|
ellipse(x, y, 130, 20, 150, 40, BLACK, &pix); |
||||||
|
ellipse(x, y, 131, 21, 149, 49, DARK_BLUE, &pix); |
||||||
|
rectangle(x, y, 135, 10, 160, 50, DARK_BLUE, &pix); |
||||||
|
ellipse(x, y, 132, 10, 138, 20, BLACK, &pix); |
||||||
|
ellipse(x, y, 133, 11, 139, 19, RED, &pix); |
||||||
|
|
||||||
|
/* Rudder */ |
||||||
|
ellipse(x, y, 45, 40, 75, 70, BLACK, &pix); |
||||||
|
ellipse(x, y, 46, 41, 74, 69, ORANGE, &pix); |
||||||
|
ellipse(x, y, 45, 80, 75, 109, BLACK, &pix); |
||||||
|
ellipse(x, y, 46, 81, 74, 108, RED, &pix); |
||||||
|
|
||||||
|
/* Bridge */ |
||||||
|
ellipse(x, y, 100, 30, 120, 50, BLACK, &pix); |
||||||
|
ellipse(x, y, 101, 31, 119, 49, GREEN, &pix); |
||||||
|
ellipse(x, y, 140, 30, 160, 50, BLACK, &pix); |
||||||
|
ellipse(x, y, 141, 31, 159, 49, GREEN, &pix); |
||||||
|
rectangle(x, y, 110, 30, 150, 50, BLACK, &pix); |
||||||
|
rectangle(x, y, 110, 31, 150, 50, GREEN, &pix); |
||||||
|
|
||||||
|
/* Hull */ |
||||||
|
ellipse(x, y, 50, 40, 199, 109, BLACK, &pix); |
||||||
|
ellipse(x, y, 51, 41, 198, 108, LIGHT_BLUE, &pix); |
||||||
|
|
||||||
|
/* Port holes */ |
||||||
|
ellipse(x, y, 79, 57, 109, 82, BLACK, &pix); |
||||||
|
ellipse(x, y, 80, 58, 108, 81, LIGHT_BLUE, &pix); |
||||||
|
ellipse(x, y, 83, 61, 105, 78, BLACK, &pix); |
||||||
|
ellipse(x, y, 84, 62, 104, 77, YELLOW, &pix); |
||||||
|
/*
|
||||||
|
* This port hole is created by copying |
||||||
|
* ellipse(x, y, 119, 57, 149, 82, BLACK, &pix); |
||||||
|
* ellipse(x, y, 120, 58, 148, 81, LIGHT_BLUE, &pix); |
||||||
|
* ellipse(x, y, 123, 61, 145, 78, BLACK, &pix); |
||||||
|
* ellipse(x, y, 124, 62, 144, 77, YELLOW, &pix); |
||||||
|
*/ |
||||||
|
ellipse(x, y, 159, 57, 189, 82, BLACK, &pix); |
||||||
|
ellipse(x, y, 160, 58, 188, 81, LIGHT_BLUE, &pix); |
||||||
|
ellipse(x, y, 163, 61, 185, 78, BLACK, &pix); |
||||||
|
ellipse(x, y, 164, 62, 184, 77, YELLOW, &pix); |
||||||
|
|
||||||
|
bitmap[WIDTH * y + x] = pix; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Tear down unit test. |
||||||
|
* |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int teardown(void) |
||||||
|
{ |
||||||
|
efi_status_t ret; |
||||||
|
|
||||||
|
if (bitmap) { |
||||||
|
ret = boottime->free_pool(bitmap); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("FreePool failed\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
} |
||||||
|
if (event) { |
||||||
|
ret = boottime->close_event(event); |
||||||
|
event = NULL; |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("could not close event\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
} |
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute unit test. |
||||||
|
* |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int execute(void) |
||||||
|
{ |
||||||
|
u32 max_mode; |
||||||
|
efi_status_t ret; |
||||||
|
struct efi_gop_mode_info *info; |
||||||
|
|
||||||
|
if (!gop) |
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
|
||||||
|
if (!gop->mode) { |
||||||
|
efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
info = gop->mode->info; |
||||||
|
max_mode = gop->mode->max_mode; |
||||||
|
if (!max_mode) { |
||||||
|
efi_st_error("No graphical mode available\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
/* Fill background */ |
||||||
|
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0, |
||||||
|
info->width, info->height, 0); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("EFI_BLT_VIDEO_FILL failed\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
/* Copy image to video */ |
||||||
|
ret = gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, 0, 0, 0, DEPTH, |
||||||
|
WIDTH, HEIGHT, 0); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("EFI_BLT_BUFFER_TO_VIDEO failed\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
/* Copy left port hole */ |
||||||
|
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_VIDEO, |
||||||
|
79, 57 + DEPTH, 119, 57 + DEPTH, |
||||||
|
31, 26, 0); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("EFI_BLT_VIDEO_TO_VIDEO failed\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
/* Copy port holes back to buffer */ |
||||||
|
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_BLT_BUFFER, |
||||||
|
94, 57 + DEPTH, 94, 57, |
||||||
|
90, 26, WIDTH * sizeof(struct efi_gop_pixel)); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("EFI_BLT_VIDEO_TO_BLT_BUFFER failed\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
/* Set 250ms timer */ |
||||||
|
xpos = WIDTH; |
||||||
|
ret = boottime->set_timer(event, EFI_TIMER_PERIODIC, 250000); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("Could not set timer\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
con_out->set_cursor_position(con_out, 0, 0); |
||||||
|
con_out->set_attribute(con_out, EFI_WHITE | EFI_BACKGROUND_BLUE); |
||||||
|
efi_st_printf("The submarine should have three yellow port holes.\n"); |
||||||
|
efi_st_printf("Press any key to continue"); |
||||||
|
efi_st_get_key(); |
||||||
|
con_out->set_attribute(con_out, EFI_LIGHTGRAY); |
||||||
|
efi_st_printf("\n"); |
||||||
|
|
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
EFI_UNIT_TEST(bitblt) = { |
||||||
|
.name = "block image transfer", |
||||||
|
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
||||||
|
.setup = setup, |
||||||
|
.execute = execute, |
||||||
|
.teardown = teardown, |
||||||
|
.on_request = true, |
||||||
|
}; |
@ -0,0 +1,140 @@ |
|||||||
|
/*
|
||||||
|
* efi_selftest_event_groups |
||||||
|
* |
||||||
|
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
* |
||||||
|
* This test checks the notification of group events and the |
||||||
|
* following services: |
||||||
|
* CreateEventEx, CloseEvent, SignalEvent, CheckEvent. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <efi_selftest.h> |
||||||
|
|
||||||
|
#define GROUP_SIZE 16 |
||||||
|
|
||||||
|
static struct efi_boot_services *boottime; |
||||||
|
static efi_guid_t event_group = |
||||||
|
EFI_GUID(0x2335905b, 0xc3b9, 0x4221, 0xa3, 0x71, |
||||||
|
0x0e, 0x5b, 0x45, 0xc0, 0x56, 0x91); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Notification function, increments the notfication count if parameter |
||||||
|
* context is provided. |
||||||
|
* |
||||||
|
* @event notified event |
||||||
|
* @context pointer to the notification count |
||||||
|
*/ |
||||||
|
static void EFIAPI notify(struct efi_event *event, void *context) |
||||||
|
{ |
||||||
|
unsigned int *count = context; |
||||||
|
|
||||||
|
if (count) |
||||||
|
++*count; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup unit test. |
||||||
|
* |
||||||
|
* @handle: handle of the loaded image |
||||||
|
* @systable: system table |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int setup(const efi_handle_t handle, |
||||||
|
const struct efi_system_table *systable) |
||||||
|
{ |
||||||
|
boottime = systable->boottime; |
||||||
|
|
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute unit test. |
||||||
|
* |
||||||
|
* Create multiple events in an event group. Signal each event once and check |
||||||
|
* that all events are notified once in each round. |
||||||
|
* |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int execute(void) |
||||||
|
{ |
||||||
|
unsigned int counter[GROUP_SIZE] = {0}; |
||||||
|
struct efi_event *events[GROUP_SIZE]; |
||||||
|
size_t i, j; |
||||||
|
efi_status_t ret; |
||||||
|
|
||||||
|
for (i = 0; i < GROUP_SIZE; ++i) { |
||||||
|
ret = boottime->create_event_ex(0, TPL_NOTIFY, |
||||||
|
notify, (void *)&counter[i], |
||||||
|
&event_group, &events[i]); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("Failed to create event\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (i = 0; i < GROUP_SIZE; ++i) { |
||||||
|
ret = boottime->signal_event(events[i]); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("Failed to signal event\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
for (j = 0; j < GROUP_SIZE; ++j) { |
||||||
|
if (counter[j] != i) { |
||||||
|
efi_st_printf("i %u, j %u, count %u\n", |
||||||
|
(unsigned int)i, (unsigned int)j, |
||||||
|
(unsigned int)counter[j]); |
||||||
|
efi_st_error( |
||||||
|
"Notification function was called\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
/* Clear signaled state */ |
||||||
|
ret = boottime->check_event(events[j]); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("Event was not signaled\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
if (counter[j] != i) { |
||||||
|
efi_st_printf("i %u, j %u, count %u\n", |
||||||
|
(unsigned int)i, (unsigned int)j, |
||||||
|
(unsigned int)counter[j]); |
||||||
|
efi_st_error( |
||||||
|
"Notification function was called\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
/* Call notification function */ |
||||||
|
ret = boottime->check_event(events[j]); |
||||||
|
if (ret != EFI_NOT_READY) { |
||||||
|
efi_st_error( |
||||||
|
"Signaled state not cleared\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
if (counter[j] != i + 1) { |
||||||
|
efi_st_printf("i %u, j %u, count %u\n", |
||||||
|
(unsigned int)i, (unsigned int)j, |
||||||
|
(unsigned int)counter[j]); |
||||||
|
efi_st_error( |
||||||
|
"Nofification function not called\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (i = 0; i < GROUP_SIZE; ++i) { |
||||||
|
ret = boottime->close_event(events[i]); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("Failed to close event\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
EFI_UNIT_TEST(eventgoups) = { |
||||||
|
.name = "event groups", |
||||||
|
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
||||||
|
.setup = setup, |
||||||
|
.execute = execute, |
||||||
|
}; |
@ -0,0 +1,188 @@ |
|||||||
|
/*
|
||||||
|
* efi_selftest_pos |
||||||
|
* |
||||||
|
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
* |
||||||
|
* Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. |
||||||
|
* |
||||||
|
* The following services are tested: |
||||||
|
* OutputString, TestString, SetAttribute. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <efi_selftest.h> |
||||||
|
#include <linux/libfdt.h> |
||||||
|
|
||||||
|
static struct efi_boot_services *boottime; |
||||||
|
static const char *fdt; |
||||||
|
|
||||||
|
/* This should be sufficent for */ |
||||||
|
#define BUFFERSIZE 0x100000 |
||||||
|
|
||||||
|
static efi_guid_t fdt_guid = EFI_FDT_GUID; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert FDT value to host endianness. |
||||||
|
* |
||||||
|
* @val FDT value |
||||||
|
* @return converted value |
||||||
|
*/ |
||||||
|
static uint32_t f2h(fdt32_t val) |
||||||
|
{ |
||||||
|
char *buf = (char *)&val; |
||||||
|
char i; |
||||||
|
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
||||||
|
/* Swap the bytes */ |
||||||
|
i = buf[0]; buf[0] = buf[3]; buf[3] = i; |
||||||
|
i = buf[1]; buf[1] = buf[2]; buf[2] = i; |
||||||
|
#endif |
||||||
|
return *(uint32_t *)buf; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the value of a property of the FDT root node. |
||||||
|
* |
||||||
|
* @name name of the property |
||||||
|
* @return value of the property |
||||||
|
*/ |
||||||
|
static char *get_property(const u16 *property) |
||||||
|
{ |
||||||
|
struct fdt_header *header = (struct fdt_header *)fdt; |
||||||
|
const fdt32_t *pos; |
||||||
|
const char *strings; |
||||||
|
|
||||||
|
if (!header) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
if (f2h(header->magic) != FDT_MAGIC) { |
||||||
|
printf("Wrong magic\n"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct)); |
||||||
|
strings = fdt + f2h(header->off_dt_strings); |
||||||
|
|
||||||
|
for (;;) { |
||||||
|
switch (f2h(pos[0])) { |
||||||
|
case FDT_BEGIN_NODE: { |
||||||
|
char *c = (char *)&pos[1]; |
||||||
|
size_t i; |
||||||
|
|
||||||
|
for (i = 0; c[i]; ++i) |
||||||
|
; |
||||||
|
pos = &pos[2 + (i >> 2)]; |
||||||
|
break; |
||||||
|
} |
||||||
|
case FDT_PROP: { |
||||||
|
struct fdt_property *prop = (struct fdt_property *)pos; |
||||||
|
const char *label = &strings[f2h(prop->nameoff)]; |
||||||
|
efi_status_t ret; |
||||||
|
|
||||||
|
/* Check if this is the property to be returned */ |
||||||
|
if (!efi_st_strcmp_16_8(property, label)) { |
||||||
|
char *str; |
||||||
|
efi_uintn_t len = f2h(prop->len); |
||||||
|
|
||||||
|
if (!len) |
||||||
|
return NULL; |
||||||
|
/*
|
||||||
|
* The string might not be 0 terminated. |
||||||
|
* It is safer to make a copy. |
||||||
|
*/ |
||||||
|
ret = boottime->allocate_pool( |
||||||
|
EFI_LOADER_DATA, len + 1, |
||||||
|
(void **)&str); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_printf("AllocatePool failed\n"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
boottime->copy_mem(str, &pos[3], len); |
||||||
|
str[len] = 0; |
||||||
|
|
||||||
|
return str; |
||||||
|
} |
||||||
|
|
||||||
|
pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)]; |
||||||
|
break; |
||||||
|
} |
||||||
|
case FDT_NOP: |
||||||
|
pos = &pos[1]; |
||||||
|
break; |
||||||
|
default: |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup unit test. |
||||||
|
* |
||||||
|
* @handle: handle of the loaded image |
||||||
|
* @systable: system table |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int setup(const efi_handle_t img_handle, |
||||||
|
const struct efi_system_table *systable) |
||||||
|
{ |
||||||
|
efi_uintn_t i; |
||||||
|
|
||||||
|
boottime = systable->boottime; |
||||||
|
|
||||||
|
/* Find configuration tables */ |
||||||
|
for (i = 0; i < systable->nr_tables; ++i) { |
||||||
|
if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid, |
||||||
|
sizeof(efi_guid_t))) |
||||||
|
fdt = systable->tables[i].table; |
||||||
|
} |
||||||
|
if (!fdt) { |
||||||
|
efi_st_error("Missing device tree\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute unit test. |
||||||
|
* |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int execute(void) |
||||||
|
{ |
||||||
|
char *str; |
||||||
|
efi_status_t ret; |
||||||
|
|
||||||
|
str = get_property(L"compatible"); |
||||||
|
if (str) { |
||||||
|
efi_st_printf("compatible: %s\n", str); |
||||||
|
ret = boottime->free_pool(str); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("FreePool failed\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
} else { |
||||||
|
efi_st_printf("Missing property 'compatible'\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
str = get_property(L"serial-number"); |
||||||
|
if (str) { |
||||||
|
efi_st_printf("serial-number: %s\n", str); |
||||||
|
ret = boottime->free_pool(str); |
||||||
|
if (ret != EFI_SUCCESS) { |
||||||
|
efi_st_error("FreePool failed\n"); |
||||||
|
return EFI_ST_FAILURE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
EFI_UNIT_TEST(fdt) = { |
||||||
|
.name = "device tree", |
||||||
|
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
||||||
|
.setup = setup, |
||||||
|
.execute = execute, |
||||||
|
.on_request = true, |
||||||
|
}; |
@ -0,0 +1,182 @@ |
|||||||
|
/*
|
||||||
|
* efi_selftest_textinput |
||||||
|
* |
||||||
|
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
* |
||||||
|
* Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_PROTOCOL. |
||||||
|
* The unicode character and the scan code are printed for text |
||||||
|
* input. To run the test: |
||||||
|
* |
||||||
|
* setenv efi_selftest text input |
||||||
|
* bootefi selftest |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <efi_selftest.h> |
||||||
|
|
||||||
|
struct translate { |
||||||
|
u16 code; |
||||||
|
u16 *text; |
||||||
|
}; |
||||||
|
|
||||||
|
static struct efi_boot_services *boottime; |
||||||
|
|
||||||
|
static struct translate control_characters[] = { |
||||||
|
{0, L"Null"}, |
||||||
|
{8, L"BS"}, |
||||||
|
{9, L"TAB"}, |
||||||
|
{10, L"LF"}, |
||||||
|
{13, L"CR"}, |
||||||
|
{0, NULL}, |
||||||
|
}; |
||||||
|
|
||||||
|
static u16 ch[] = L"' '"; |
||||||
|
static u16 unknown[] = L"unknown"; |
||||||
|
|
||||||
|
static struct translate scan_codes[] = { |
||||||
|
{0x00, L"Null"}, |
||||||
|
{0x01, L"Up"}, |
||||||
|
{0x02, L"Down"}, |
||||||
|
{0x03, L"Right"}, |
||||||
|
{0x04, L"Left"}, |
||||||
|
{0x05, L"Home"}, |
||||||
|
{0x06, L"End"}, |
||||||
|
{0x07, L"Insert"}, |
||||||
|
{0x08, L"Delete"}, |
||||||
|
{0x09, L"Page Up"}, |
||||||
|
{0x0a, L"Page Down"}, |
||||||
|
{0x0b, L"FN 1"}, |
||||||
|
{0x0c, L"FN 2"}, |
||||||
|
{0x0d, L"FN 3"}, |
||||||
|
{0x0e, L"FN 4"}, |
||||||
|
{0x0f, L"FN 5"}, |
||||||
|
{0x10, L"FN 6"}, |
||||||
|
{0x11, L"FN 7"}, |
||||||
|
{0x12, L"FN 8"}, |
||||||
|
{0x13, L"FN 9"}, |
||||||
|
{0x14, L"FN 10"}, |
||||||
|
{0x15, L"FN 11"}, |
||||||
|
{0x16, L"FN 12"}, |
||||||
|
{0x17, L"Escape"}, |
||||||
|
{0x68, L"FN 13"}, |
||||||
|
{0x69, L"FN 14"}, |
||||||
|
{0x6a, L"FN 15"}, |
||||||
|
{0x6b, L"FN 16"}, |
||||||
|
{0x6c, L"FN 17"}, |
||||||
|
{0x6d, L"FN 18"}, |
||||||
|
{0x6e, L"FN 19"}, |
||||||
|
{0x6f, L"FN 20"}, |
||||||
|
{0x70, L"FN 21"}, |
||||||
|
{0x71, L"FN 22"}, |
||||||
|
{0x72, L"FN 23"}, |
||||||
|
{0x73, L"FN 24"}, |
||||||
|
{0x7f, L"Mute"}, |
||||||
|
{0x80, L"Volume Up"}, |
||||||
|
{0x81, L"Volume Down"}, |
||||||
|
{0x100, L"Brightness Up"}, |
||||||
|
{0x101, L"Brightness Down"}, |
||||||
|
{0x102, L"Suspend"}, |
||||||
|
{0x103, L"Hibernate"}, |
||||||
|
{0x104, L"Toggle Display"}, |
||||||
|
{0x105, L"Recovery"}, |
||||||
|
{0x106, L"Reject"}, |
||||||
|
{0x0, NULL}, |
||||||
|
}; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Translate a unicode character to a string. |
||||||
|
* |
||||||
|
* @code unicode character |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
static u16 *translate_char(u16 code) |
||||||
|
{ |
||||||
|
struct translate *tr; |
||||||
|
|
||||||
|
if (code >= ' ') { |
||||||
|
ch[1] = code; |
||||||
|
return ch; |
||||||
|
} |
||||||
|
for (tr = control_characters; tr->text; ++tr) { |
||||||
|
if (tr->code == code) |
||||||
|
return tr->text; |
||||||
|
} |
||||||
|
return unknown; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Translate a scan code to a human readable string. |
||||||
|
* |
||||||
|
* @code unicode character |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
static u16 *translate_code(u16 code) |
||||||
|
{ |
||||||
|
struct translate *tr; |
||||||
|
|
||||||
|
for (tr = scan_codes; tr->text; ++tr) { |
||||||
|
if (tr->code == code) |
||||||
|
return tr->text; |
||||||
|
} |
||||||
|
return unknown; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup unit test. |
||||||
|
* |
||||||
|
* @handle: handle of the loaded image |
||||||
|
* @systable: system table |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int setup(const efi_handle_t handle, |
||||||
|
const struct efi_system_table *systable) |
||||||
|
{ |
||||||
|
boottime = systable->boottime; |
||||||
|
|
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute unit test. |
||||||
|
* |
||||||
|
* @return: EFI_ST_SUCCESS for success |
||||||
|
*/ |
||||||
|
static int execute(void) |
||||||
|
{ |
||||||
|
struct efi_input_key input_key = {0}; |
||||||
|
efi_status_t ret; |
||||||
|
|
||||||
|
efi_st_printf("Waiting for your input\n"); |
||||||
|
efi_st_printf("To terminate type 'x'\n"); |
||||||
|
|
||||||
|
for (;;) { |
||||||
|
/* Wait for next key */ |
||||||
|
do { |
||||||
|
ret = con_in->read_key_stroke(con_in, &input_key); |
||||||
|
} while (ret == EFI_NOT_READY); |
||||||
|
|
||||||
|
/* Allow 5 minutes until time out */ |
||||||
|
boottime->set_watchdog_timer(300, 0, 0, NULL); |
||||||
|
|
||||||
|
efi_st_printf("Unicode char %u (%ps), scan code %u (%ps)\n", |
||||||
|
(unsigned int)input_key.unicode_char, |
||||||
|
translate_char(input_key.unicode_char), |
||||||
|
(unsigned int)input_key.scan_code, |
||||||
|
translate_code(input_key.scan_code)); |
||||||
|
|
||||||
|
switch (input_key.unicode_char) { |
||||||
|
case 'x': |
||||||
|
case 'X': |
||||||
|
return EFI_ST_SUCCESS; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
EFI_UNIT_TEST(textinput) = { |
||||||
|
.name = "text input", |
||||||
|
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
||||||
|
.setup = setup, |
||||||
|
.execute = execute, |
||||||
|
.on_request = true, |
||||||
|
}; |
Loading…
Reference in new issue