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