Patch queue for efi - 2017-10-13

This is the second batch of amazing improvements for efi_loader in 2017.11:
 
   - New self tests to verify our own code
   - A few bug fixes
   - colored text support
   - event and SNP improvements, should get us close to iPXE working
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABAgAGBQJZ4HqlAAoJECszeR4D/txgZ0YQAIwd158lVczf/cMnzf3UcIsH
 e4YUFvQJaGxJw5hccaewaEQJqtPhEEJuHnzc683XDoYISTgvzCyhNEs8o0f7LcX3
 41c50x2bQPz7oFO5N60m58RknHqKiGd5VAC6+r4vBM8C0zFHeNTULo4fzn1qRgb3
 YUHDiRMlToVZXCOkbCeFm+dEJvjkXWmDDDdJpFIgDs6Oj+jOuV+H1GRMF2d56V6r
 Dd4/QPAFl4sRprBAMlvsmuI7obwnGV7/aA4CHIlUqKOVcbddBq2KigX8ZpIYEpaf
 FhRfxRLuWqvDRKpn+ixKfgl4KiBE/CH7sg7F6Un0mIjQVvo8k22Jbetqi9m1j/+x
 YzIFnwzb9ZhQEFOmfHnH8M9+bMmHyW46wkS4gdO4OAd/W0SeyIZ0a0gB1prgb2LK
 RfYQ37WsZM5TRARlcvmJt4H5+EtRYLT8lLf+yPSuyX8/8ubVnYHWtVE1IMLCiCAG
 eVmTwfjLzo3c703RJd9rwDtE13lTQmIHczM4cWybQzooXA6ePLJpSZSWx7EJxdQq
 GC7jNIKyKpu2NjhM9fUssbU+SiYLaIvtUBSOlGrcP1TCVR/j8OQ0Y1eIc9xOprIi
 +VEZVB/Z59D8j2TMTn1Xr+hB9i5h9ZxcO88gdEwfs65uihfvJGVhV1AxTyiaxvNv
 novPhik2UyHG5buRHb2Y
 =PiNi
 -----END PGP SIGNATURE-----

Merge tag 'signed-efi-next' of git://github.com/agraf/u-boot

Patch queue for efi - 2017-10-13

This is the second batch of amazing improvements for efi_loader in 2017.11:

  - New self tests to verify our own code
  - A few bug fixes
  - colored text support
  - event and SNP improvements, should get us close to iPXE working
This commit is contained in:
Tom Rini 2017-10-13 09:53:58 -04:00
commit f855a7bc12
21 changed files with 1688 additions and 258 deletions

View file

@ -127,6 +127,7 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt,
{
struct efi_loaded_image loaded_image_info = {};
struct efi_object loaded_image_info_obj = {};
struct efi_device_path *memdp = NULL;
ulong ret;
ulong (*entry)(void *image_handle, struct efi_system_table *st)
@ -135,6 +136,20 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt,
const efi_guid_t fdt_guid = EFI_FDT_GUID;
bootm_headers_t img = { 0 };
/*
* Special case for efi payload not loaded from disk, such as
* 'bootefi hello' or for example payload loaded directly into
* memory via jtag/etc:
*/
if (!device_path && !image_path) {
printf("WARNING: using memory device/image path, this may confuse some payloads!\n");
/* actual addresses filled in after efi_load_pe() */
memdp = efi_dp_from_mem(0, 0, 0);
device_path = image_path = memdp;
} else {
assert(device_path && image_path);
}
/* Initialize and populate EFI object list */
if (!efi_obj_list_initalized)
efi_init_obj_list();
@ -181,6 +196,14 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt,
goto exit;
}
if (memdp) {
struct efi_device_path_memory *mdp = (void *)memdp;
mdp->memory_type = loaded_image_info.image_code_type;
mdp->start_address = (uintptr_t)loaded_image_info.image_base;
mdp->end_address = mdp->start_address +
loaded_image_info.image_size;
}
/* we don't support much: */
env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
"{ro,boot}(blob)0000000000000000");

View file

@ -63,3 +63,4 @@ CONFIG_USB_KEYBOARD=y
CONFIG_FRAMEBUFFER_SET_VESA_MODE=y
CONFIG_FRAMEBUFFER_VESA_MODE_111=y
CONFIG_CONSOLE_SCROLL_LINES=5
CONFIG_CMD_BOOTEFI_SELFTEST=y

View file

@ -43,3 +43,4 @@ CONFIG_USB_KEYBOARD=y
CONFIG_FRAMEBUFFER_SET_VESA_MODE=y
CONFIG_FRAMEBUFFER_VESA_MODE_111=y
CONFIG_CONSOLE_SCROLL_LINES=5
CONFIG_CMD_BOOTEFI_SELFTEST=y

View file

@ -9,7 +9,7 @@
#ifndef __CHARSET_H_
#define __CHARSET_H_
#define MAX_UTF8_PER_UTF16 4
#define MAX_UTF8_PER_UTF16 3
/**
* utf16_strlen() - Get the length of an utf16 string
@ -52,7 +52,7 @@ uint16_t *utf16_strdup(const uint16_t *s);
* Converts 'size' characters of the utf16 string 'src' to utf8
* written to the 'dest' buffer.
*
* NOTE that a single utf16 character can generate up to 4 utf8
* NOTE that a single utf16 character can generate up to 3 utf8
* characters. See MAX_UTF8_PER_UTF16.
*
* @dest the destination buffer to write the utf8 characters

View file

@ -71,30 +71,31 @@ struct efi_boot_services {
enum efi_timer_delay type,
uint64_t trigger_time);
efi_status_t (EFIAPI *wait_for_event)(unsigned long number_of_events,
struct efi_event **event, unsigned long *index);
struct efi_event **event, size_t *index);
efi_status_t (EFIAPI *signal_event)(struct efi_event *event);
efi_status_t (EFIAPI *close_event)(struct efi_event *event);
efi_status_t (EFIAPI *check_event)(struct efi_event *event);
#define EFI_NATIVE_INTERFACE 0x00000000
efi_status_t (EFIAPI *install_protocol_interface)(
void **handle, efi_guid_t *protocol,
void **handle, const efi_guid_t *protocol,
int protocol_interface_type, void *protocol_interface);
efi_status_t (EFIAPI *reinstall_protocol_interface)(
void *handle, efi_guid_t *protocol,
void *handle, const efi_guid_t *protocol,
void *old_interface, void *new_interface);
efi_status_t (EFIAPI *uninstall_protocol_interface)(void *handle,
efi_guid_t *protocol, void *protocol_interface);
efi_status_t (EFIAPI *handle_protocol)(efi_handle_t, efi_guid_t *,
void **);
const efi_guid_t *protocol, void *protocol_interface);
efi_status_t (EFIAPI *handle_protocol)(efi_handle_t,
const efi_guid_t *protocol,
void **protocol_interface);
void *reserved;
efi_status_t (EFIAPI *register_protocol_notify)(
efi_guid_t *protocol, struct efi_event *event,
const efi_guid_t *protocol, struct efi_event *event,
void **registration);
efi_status_t (EFIAPI *locate_handle)(
enum efi_locate_search_type search_type,
efi_guid_t *protocol, void *search_key,
const efi_guid_t *protocol, void *search_key,
unsigned long *buffer_size, efi_handle_t *buffer);
efi_status_t (EFIAPI *locate_device_path)(efi_guid_t *protocol,
efi_status_t (EFIAPI *locate_device_path)(const efi_guid_t *protocol,
struct efi_device_path **device_path,
efi_handle_t *device);
efi_status_t (EFIAPI *install_configuration_table)(
@ -131,14 +132,14 @@ struct efi_boot_services {
#define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010
#define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020
efi_status_t (EFIAPI *open_protocol)(efi_handle_t handle,
efi_guid_t *protocol, void **interface,
const efi_guid_t *protocol, void **interface,
efi_handle_t agent_handle,
efi_handle_t controller_handle, u32 attributes);
efi_status_t (EFIAPI *close_protocol)(void *handle,
efi_guid_t *protocol, void *agent_handle,
const efi_guid_t *protocol, void *agent_handle,
void *controller_handle);
efi_status_t(EFIAPI *open_protocol_information)(efi_handle_t handle,
efi_guid_t *protocol,
const efi_guid_t *protocol,
struct efi_open_protocol_info_entry **entry_buffer,
unsigned long *entry_count);
efi_status_t (EFIAPI *protocols_per_handle)(efi_handle_t handle,
@ -146,9 +147,9 @@ struct efi_boot_services {
unsigned long *protocols_buffer_count);
efi_status_t (EFIAPI *locate_handle_buffer) (
enum efi_locate_search_type search_type,
efi_guid_t *protocol, void *search_key,
const efi_guid_t *protocol, void *search_key,
unsigned long *no_handles, efi_handle_t **buffer);
efi_status_t (EFIAPI *locate_protocol)(efi_guid_t *protocol,
efi_status_t (EFIAPI *locate_protocol)(const efi_guid_t *protocol,
void *registration, void **protocol_interface);
efi_status_t (EFIAPI *install_multiple_protocol_interfaces)(
void **handle, ...);
@ -156,10 +157,9 @@ struct efi_boot_services {
void *handle, ...);
efi_status_t (EFIAPI *calculate_crc32)(void *data,
unsigned long data_size, uint32_t *crc32);
void (EFIAPI *copy_mem)(void *destination, void *source,
unsigned long length);
void (EFIAPI *set_mem)(void *buffer, unsigned long size,
uint8_t value);
void (EFIAPI *copy_mem)(void *destination, const void *source,
size_t length);
void (EFIAPI *set_mem)(void *buffer, size_t size, uint8_t value);
void *create_event_ex;
};
@ -297,8 +297,16 @@ struct efi_mac_addr {
} __packed;
#define DEVICE_PATH_TYPE_HARDWARE_DEVICE 0x01
# define DEVICE_PATH_SUB_TYPE_MEMORY 0x03
# define DEVICE_PATH_SUB_TYPE_VENDOR 0x04
struct efi_device_path_memory {
struct efi_device_path dp;
u32 memory_type;
u64 start_address;
u64 end_address;
} __packed;
struct efi_device_path_vendor {
struct efi_device_path dp;
efi_guid_t guid;
@ -425,6 +433,39 @@ struct simple_text_output_mode {
EFI_GUID(0x387477c2, 0x69c7, 0x11d2, \
0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_BLACK 0x00
#define EFI_BLUE 0x01
#define EFI_GREEN 0x02
#define EFI_CYAN 0x03
#define EFI_RED 0x04
#define EFI_MAGENTA 0x05
#define EFI_BROWN 0x06
#define EFI_LIGHTGRAY 0x07
#define EFI_BRIGHT 0x08
#define EFI_DARKGRAY 0x08
#define EFI_LIGHTBLUE 0x09
#define EFI_LIGHTGREEN 0x0a
#define EFI_LIGHTCYAN 0x0b
#define EFI_LIGHTRED 0x0c
#define EFI_LIGHTMAGENTA 0x0d
#define EFI_YELLOW 0x0e
#define EFI_WHITE 0x0f
#define EFI_BACKGROUND_BLACK 0x00
#define EFI_BACKGROUND_BLUE 0x10
#define EFI_BACKGROUND_GREEN 0x20
#define EFI_BACKGROUND_CYAN 0x30
#define EFI_BACKGROUND_RED 0x40
#define EFI_BACKGROUND_MAGENTA 0x50
#define EFI_BACKGROUND_BROWN 0x60
#define EFI_BACKGROUND_LIGHTGRAY 0x70
/* extract foreground color from EFI attribute */
#define EFI_ATTR_FG(attr) ((attr) & 0x07)
/* treat high bit of FG as bright/bold (similar to edk2) */
#define EFI_ATTR_BOLD(attr) (((attr) >> 3) & 0x01)
/* extract background color from EFI attribute */
#define EFI_ATTR_BG(attr) (((attr) >> 4) & 0x7)
struct efi_simple_text_output_protocol {
void *reset;
efi_status_t (EFIAPI *output_string)(
@ -593,11 +634,21 @@ struct efi_simple_network_mode {
u8 media_present;
};
#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01,
#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02,
#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04,
#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08,
#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10,
/* receive_filters bit mask */
#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01
#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02
#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04
#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08
#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10
/* interrupt status bit mask */
#define EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT 0x01
#define EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT 0x02
#define EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT 0x04
#define EFI_SIMPLE_NETWORK_SOFTWARE_INTERRUPT 0x08
/* revision of the simple network protocol */
#define EFI_SIMPLE_NETWORK_PROTOCOL_REVISION 0x00010000
struct efi_simple_network
{
@ -626,14 +677,14 @@ struct efi_simple_network
efi_status_t (EFIAPI *get_status)(struct efi_simple_network *this,
u32 *int_status, void **txbuf);
efi_status_t (EFIAPI *transmit)(struct efi_simple_network *this,
ulong header_size, ulong buffer_size, void *buffer,
size_t header_size, size_t buffer_size, void *buffer,
struct efi_mac_address *src_addr,
struct efi_mac_address *dest_addr, u16 *protocol);
efi_status_t (EFIAPI *receive)(struct efi_simple_network *this,
ulong *header_size, ulong *buffer_size, void *buffer,
size_t *header_size, size_t *buffer_size, void *buffer,
struct efi_mac_address *src_addr,
struct efi_mac_address *dest_addr, u16 *protocol);
void (EFIAPI *waitforpacket)(void);
struct efi_event *wait_for_packet;
struct efi_simple_network_mode *mode;
};

View file

@ -112,8 +112,8 @@ struct efi_handler {
struct efi_object {
/* Every UEFI object is part of a global object list */
struct list_head link;
/* We support up to 8 "protocols" an object can be accessed through */
struct efi_handler protocols[8];
/* We support up to 16 "protocols" an object can be accessed through */
struct efi_handler protocols[16];
/* The object spawner can either use this for data or as identifier */
void *handle;
};
@ -136,8 +136,8 @@ struct efi_object {
* @nofify_function: Function to call when the event is triggered
* @notify_context: Data to be passed to the notify function
* @trigger_type: Type of timer, see efi_set_timer
* @queued: The notification functionis queued
* @signaled: The event occured
* @queued: The notification function is queued
* @signaled: The event occurred. The event is in the signaled state.
*/
struct efi_event {
uint32_t type;
@ -147,8 +147,8 @@ struct efi_event {
u64 trigger_next;
u64 trigger_time;
enum efi_timer_delay trigger_type;
int queued;
int signaled;
bool is_queued;
bool is_signaled;
};
@ -259,6 +259,9 @@ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part);
struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
const char *path);
struct efi_device_path *efi_dp_from_eth(void);
struct efi_device_path *efi_dp_from_mem(uint32_t mem_type,
uint64_t start_address,
uint64_t end_address);
void efi_dp_split_file_path(struct efi_device_path *full_path,
struct efi_device_path **device_path,
struct efi_device_path **file_path);

View file

@ -14,14 +14,17 @@
#include <efi_api.h>
#include <linker_lists.h>
#define EFI_ST_SUCCESS 0
#define EFI_ST_FAILURE 1
/*
* Prints an error message.
*
* @... format string followed by fields to print
*/
#define efi_st_error(...) \
efi_st_printf("%s(%u):\nERROR: ", __FILE__, __LINE__); \
efi_st_printf(__VA_ARGS__) \
(efi_st_printf("%s(%u):\nERROR: ", __FILE__, __LINE__), \
efi_st_printf(__VA_ARGS__)) \
/*
* A test may be setup and executed at boottime,
@ -57,6 +60,17 @@ void efi_st_exit_boot_services(void);
void efi_st_printf(const char *fmt, ...)
__attribute__ ((format (__printf__, 1, 2)));
/*
* Compare memory.
* We cannot use lib/string.c due to different CFLAGS values.
*
* @buf1: first buffer
* @buf2: second buffer
* @length: number of bytes to compare
* @return: 0 if both buffers contain the same bytes
*/
int efi_st_memcmp(const void *buf1, const void *buf2, size_t length);
/*
* Reads an Unicode character from the input device.
*

File diff suppressed because it is too large Load diff

View file

@ -307,14 +307,37 @@ static efi_status_t EFIAPI efi_cout_set_mode(
return EFI_EXIT(EFI_SUCCESS);
}
static const struct {
unsigned int fg;
unsigned int bg;
} color[] = {
{ 30, 40 }, /* 0: black */
{ 34, 44 }, /* 1: blue */
{ 32, 42 }, /* 2: green */
{ 36, 46 }, /* 3: cyan */
{ 31, 41 }, /* 4: red */
{ 35, 45 }, /* 5: magenta */
{ 33, 43 }, /* 6: brown, map to yellow as edk2 does*/
{ 37, 47 }, /* 7: light grey, map to white */
};
/* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */
static efi_status_t EFIAPI efi_cout_set_attribute(
struct efi_simple_text_output_protocol *this,
unsigned long attribute)
{
unsigned int bold = EFI_ATTR_BOLD(attribute);
unsigned int fg = EFI_ATTR_FG(attribute);
unsigned int bg = EFI_ATTR_BG(attribute);
EFI_ENTRY("%p, %lx", this, attribute);
/* Just ignore attributes (colors) for now */
return EFI_EXIT(EFI_UNSUPPORTED);
if (attribute)
printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg);
else
printf(ESC"[0;37;40m");
return EFI_EXIT(EFI_SUCCESS);
}
static efi_status_t EFIAPI efi_cout_clear_screen(
@ -460,7 +483,7 @@ static void EFIAPI efi_console_timer_notify(struct efi_event *event,
{
EFI_ENTRY("%p, %p", event, context);
if (tstc()) {
efi_con_in.wait_for_key->signaled = 1;
efi_con_in.wait_for_key->is_signaled = true;
efi_signal_event(efi_con_in.wait_for_key);
}
EFI_EXIT(EFI_SUCCESS);

View file

@ -538,6 +538,30 @@ struct efi_device_path *efi_dp_from_eth(void)
}
#endif
/* Construct a device-path for memory-mapped image */
struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
uint64_t start_address,
uint64_t end_address)
{
struct efi_device_path_memory *mdp;
void *buf, *start;
start = buf = dp_alloc(sizeof(*mdp) + sizeof(END));
mdp = buf;
mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
mdp->dp.length = sizeof(*mdp);
mdp->memory_type = memory_type;
mdp->start_address = start_address;
mdp->end_address = end_address;
buf = &mdp[1];
*((struct efi_device_path *)buf) = END;
return start;
}
/*
* Helper to split a full device path (containing both device and file
* parts) into it's constituent parts.

View file

@ -24,6 +24,15 @@ static char *dp_unknown(char *s, struct efi_device_path *dp)
static char *dp_hardware(char *s, struct efi_device_path *dp)
{
switch (dp->sub_type) {
case DEVICE_PATH_SUB_TYPE_MEMORY: {
struct efi_device_path_memory *mdp =
(struct efi_device_path_memory *)dp;
s += sprintf(s, "/MemoryMapped(0x%x,0x%llx,0x%llx)",
mdp->memory_type,
mdp->start_address,
mdp->end_address);
break;
}
case DEVICE_PATH_SUB_TYPE_VENDOR: {
struct efi_device_path_vendor *vdp =
(struct efi_device_path_vendor *)dp;

View file

@ -254,18 +254,19 @@ static int efi_disk_create_eltorito(struct blk_desc *desc,
#if CONFIG_IS_ENABLED(ISO_PARTITION)
char devname[32] = { 0 }; /* dp->str is u16[32] long */
disk_partition_t info;
int part = 1;
int part;
if (desc->part_type != PART_TYPE_ISO)
return 0;
/* and devices for each partition: */
while (!part_get_info(desc, part, &info)) {
for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
if (part_get_info(desc, part, &info))
continue;
snprintf(devname, sizeof(devname), "%s:%d", pdevname,
part);
efi_disk_add_dev(devname, if_typename, desc, diskid,
info.start, part);
part++;
disks++;
}
@ -299,15 +300,16 @@ int efi_disk_register(void)
struct blk_desc *desc = dev_get_uclass_platdata(dev);
const char *if_typename = dev->driver->name;
disk_partition_t info;
int part = 1;
int part;
printf("Scanning disk %s...\n", dev->name);
/* add devices for each partition: */
while (!part_get_info(desc, part, &info)) {
for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
if (part_get_info(desc, part, &info))
continue;
efi_disk_add_dev(dev->name, if_typename, desc,
desc->devnum, 0, part);
part++;
}
/* ... and add block device: */
@ -340,6 +342,8 @@ int efi_disk_register(void)
for (i = 0; i < 4; i++) {
struct blk_desc *desc;
char devname[32] = { 0 }; /* dp->str is u16[32] long */
disk_partition_t info;
int part;
desc = blk_get_devnum_by_type(if_type, i);
if (!desc)
@ -349,6 +353,16 @@ int efi_disk_register(void)
snprintf(devname, sizeof(devname), "%s%d",
if_typename, i);
/* add devices for each partition: */
for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
if (part_get_info(desc, part, &info))
continue;
efi_disk_add_dev(devname, if_typename, desc,
i, 0, part);
}
/* ... and add block device: */
efi_disk_add_dev(devname, if_typename, desc, i, 0, 0);
disks++;

View file

@ -19,6 +19,15 @@ static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID;
static struct efi_pxe_packet *dhcp_ack;
static bool new_rx_packet;
static void *new_tx_packet;
/*
* The notification function of this event is called in every timer cycle
* to check if a new network packet has been received.
*/
static struct efi_event *network_timer_event;
/*
* This event is signaled when a packet has been received.
*/
static struct efi_event *wait_for_packet;
struct efi_net_obj {
/* Generic EFI object parent class data */
@ -78,9 +87,7 @@ static efi_status_t EFIAPI efi_net_receive_filters(
EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
reset_mcast_filter, mcast_filter_count, mcast_filter);
/* XXX Do we care? */
return EFI_EXIT(EFI_SUCCESS);
return EFI_EXIT(EFI_UNSUPPORTED);
}
static efi_status_t EFIAPI efi_net_station_address(
@ -89,7 +96,7 @@ static efi_status_t EFIAPI efi_net_station_address(
{
EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
return EFI_EXIT(EFI_INVALID_PARAMETER);
return EFI_EXIT(EFI_UNSUPPORTED);
}
static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
@ -98,7 +105,7 @@ static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
{
EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table);
return EFI_EXIT(EFI_INVALID_PARAMETER);
return EFI_EXIT(EFI_UNSUPPORTED);
}
static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
@ -118,7 +125,7 @@ static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this,
EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size,
buffer);
return EFI_EXIT(EFI_INVALID_PARAMETER);
return EFI_EXIT(EFI_UNSUPPORTED);
}
static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
@ -126,9 +133,14 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
{
EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
/* We send packets synchronously, so nothing is outstanding */
if (int_status)
*int_status = 0;
efi_timer_check();
if (int_status) {
/* We send packets synchronously, so nothing is outstanding */
*int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
if (new_rx_packet)
*int_status |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
}
if (txbuf)
*txbuf = new_tx_packet;
@ -138,12 +150,15 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
}
static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
ulong header_size, ulong buffer_size, void *buffer,
size_t header_size, size_t buffer_size, void *buffer,
struct efi_mac_address *src_addr,
struct efi_mac_address *dest_addr, u16 *protocol)
{
EFI_ENTRY("%p, %lx, %lx, %p, %p, %p, %p", this, header_size,
buffer_size, buffer, src_addr, dest_addr, protocol);
EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
(unsigned long)header_size, (unsigned long)buffer_size,
buffer, src_addr, dest_addr, protocol);
efi_timer_check();
if (header_size) {
/* We would need to create the header if header_size != 0 */
@ -166,29 +181,66 @@ static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
static void efi_net_push(void *pkt, int len)
{
new_rx_packet = true;
wait_for_packet->is_signaled = true;
}
/*
* Receive a packet from a network interface.
*
* This function implements the Receive service of the Simple Network Protocol.
* See the UEFI spec for details.
*
* @this the instance of the Simple Network Protocol
* @header_size size of the media header
* @buffer_size size of the buffer to receive the packet
* @buffer buffer to receive the packet
* @src_addr source MAC address
* @dest_addr destination MAC address
* @protocol protocol
* @return status code
*/
static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this,
ulong *header_size, ulong *buffer_size, void *buffer,
size_t *header_size, size_t *buffer_size, void *buffer,
struct efi_mac_address *src_addr,
struct efi_mac_address *dest_addr, u16 *protocol)
{
struct ethernet_hdr *eth_hdr;
size_t hdr_size = sizeof(struct ethernet_hdr);
u16 protlen;
EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
buffer_size, buffer, src_addr, dest_addr, protocol);
push_packet = efi_net_push;
eth_rx();
push_packet = NULL;
efi_timer_check();
if (!new_rx_packet)
return EFI_EXIT(EFI_NOT_READY);
/* Check that we at least received an Ethernet header */
if (net_rx_packet_len < sizeof(struct ethernet_hdr)) {
new_rx_packet = false;
return EFI_EXIT(EFI_NOT_READY);
}
/* Fill export parameters */
eth_hdr = (struct ethernet_hdr *)net_rx_packet;
protlen = ntohs(eth_hdr->et_protlen);
if (protlen == 0x8100) {
hdr_size += 4;
protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]);
}
if (header_size)
*header_size = hdr_size;
if (dest_addr)
memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
if (src_addr)
memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
if (protocol)
*protocol = protlen;
if (*buffer_size < net_rx_packet_len) {
/* Packet doesn't fit, try again with bigger buf */
*buffer_size = net_rx_packet_len;
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
}
/* Copy packet */
memcpy(buffer, net_rx_packet, net_rx_packet_len);
*buffer_size = net_rx_packet_len;
new_rx_packet = false;
@ -206,10 +258,32 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
memcpy(dhcp_ack, pkt, min(len, maxsize));
}
/*
* Check if a new network packet has been received.
*
* This notification function is called in every timer cycle.
*
* @event the event for which this notification function is registered
* @context event context - not used in this function
*/
static void EFIAPI efi_network_timer_notify(struct efi_event *event,
void *context)
{
EFI_ENTRY("%p, %p", event, context);
if (!new_rx_packet) {
push_packet = efi_net_push;
eth_rx();
push_packet = NULL;
}
EFI_EXIT(EFI_SUCCESS);
}
/* This gets called from do_bootefi_exec(). */
int efi_net_register(void)
{
struct efi_net_obj *netobj;
efi_status_t r;
if (!eth_get_dev()) {
/* No eth device active, don't expose any */
@ -228,6 +302,7 @@ int efi_net_register(void)
netobj->parent.protocols[2].guid = &efi_pxe_guid;
netobj->parent.protocols[2].protocol_interface = &netobj->pxe;
netobj->parent.handle = &netobj->net;
netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
netobj->net.start = efi_net_start;
netobj->net.stop = efi_net_stop;
netobj->net.initialize = efi_net_initialize;
@ -244,6 +319,7 @@ int efi_net_register(void)
netobj->net.mode = &netobj->net_mode;
netobj->net_mode.state = EFI_NETWORK_STARTED;
memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
netobj->net_mode.hwaddr_size = ARP_HLEN;
netobj->net_mode.max_packet_size = PKTSIZE;
netobj->pxe.mode = &netobj->pxe_mode;
@ -253,5 +329,36 @@ int efi_net_register(void)
/* Hook net up to the device list */
list_add_tail(&netobj->parent.link, &efi_obj_list);
/*
* Create WaitForPacket event.
*/
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
efi_network_timer_notify, NULL,
&wait_for_packet);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register network event\n");
return r;
}
netobj->net.wait_for_packet = wait_for_packet;
/*
* Create a timer event.
*
* The notification function is used to check if a new network packet
* has been received.
*/
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
efi_network_timer_notify, NULL,
&network_timer_event);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register network event\n");
return r;
}
/* Network is time critical, create event in every timer cyle */
r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to set network timer\n");
return r;
}
return 0;
}

View file

@ -15,12 +15,18 @@ CFLAGS_efi_selftest_events.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_events.o := $(CFLAGS_NON_EFI)
CFLAGS_efi_selftest_exitbootservices.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_exitbootservices.o := $(CFLAGS_NON_EFI)
CFLAGS_efi_selftest_snp.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_snp.o := $(CFLAGS_NON_EFI)
CFLAGS_efi_selftest_tpl.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_tpl.o := $(CFLAGS_NON_EFI)
CFLAGS_efi_selftest_util.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_util.o := $(CFLAGS_NON_EFI)
obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
efi_selftest.o \
efi_selftest_console.o \
efi_selftest_events.o \
efi_selftest_exitbootservices.o \
efi_selftest_tpl.o
efi_selftest_snp.o \
efi_selftest_tpl.o \
efi_selftest_util.o

View file

@ -35,8 +35,8 @@ void efi_st_exit_boot_services(void)
ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
&desc_version);
if (ret != EFI_BUFFER_TOO_SMALL) {
efi_st_printf("ERROR: GetMemoryMap did not return "
"EFI_BUFFER_TOO_SMALL\n");
efi_st_error(
"GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
return;
}
/* Allocate extra space for newly allocated memory */
@ -44,21 +44,18 @@ void efi_st_exit_boot_services(void)
ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
(void **)&memory_map);
if (ret != EFI_SUCCESS) {
efi_st_printf("ERROR: AllocatePool did not return "
"EFI_SUCCESS\n");
efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
return;
}
ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
&desc_size, &desc_version);
if (ret != EFI_SUCCESS) {
efi_st_printf("ERROR: GetMemoryMap did not return "
"EFI_SUCCESS\n");
efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
return;
}
ret = boottime->exit_boot_services(handle, map_key);
if (ret != EFI_SUCCESS) {
efi_st_printf("ERROR: ExitBootServices did not return "
"EFI_SUCCESS\n");
efi_st_error("ExitBootServices did not return EFI_SUCCESS\n");
return;
}
efi_st_printf("\nBoot services terminated\n");
@ -69,17 +66,18 @@ void efi_st_exit_boot_services(void)
*
* @test the test to be executed
* @failures counter that will be incremented if a failure occurs
* @return EFI_ST_SUCCESS for success
*/
static int setup(struct efi_unit_test *test, unsigned int *failures)
{
int ret;
if (!test->setup)
return 0;
return EFI_ST_SUCCESS;
efi_st_printf("\nSetting up '%s'\n", test->name);
ret = test->setup(handle, systable);
if (ret) {
efi_st_printf("ERROR: Setting up '%s' failed\n", test->name);
if (ret != EFI_ST_SUCCESS) {
efi_st_error("Setting up '%s' failed\n", test->name);
++*failures;
} else {
efi_st_printf("Setting up '%s' succeeded\n", test->name);
@ -92,17 +90,18 @@ static int setup(struct efi_unit_test *test, unsigned int *failures)
*
* @test the test to be executed
* @failures counter that will be incremented if a failure occurs
* @return EFI_ST_SUCCESS for success
*/
static int execute(struct efi_unit_test *test, unsigned int *failures)
{
int ret;
if (!test->execute)
return 0;
return EFI_ST_SUCCESS;
efi_st_printf("\nExecuting '%s'\n", test->name);
ret = test->execute();
if (ret) {
efi_st_printf("ERROR: Executing '%s' failed\n", test->name);
if (ret != EFI_ST_SUCCESS) {
efi_st_error("Executing '%s' failed\n", test->name);
++*failures;
} else {
efi_st_printf("Executing '%s' succeeded\n", test->name);
@ -115,17 +114,18 @@ static int execute(struct efi_unit_test *test, unsigned int *failures)
*
* @test the test to be torn down
* @failures counter that will be incremented if a failure occurs
* @return EFI_ST_SUCCESS for success
*/
static int teardown(struct efi_unit_test *test, unsigned int *failures)
{
int ret;
if (!test->teardown)
return 0;
return EFI_ST_SUCCESS;
efi_st_printf("\nTearing down '%s'\n", test->name);
ret = test->teardown();
if (ret) {
efi_st_printf("ERROR: Tearing down '%s' failed\n", test->name);
if (ret != EFI_ST_SUCCESS) {
efi_st_error("Tearing down '%s' failed\n", test->name);
++*failures;
} else {
efi_st_printf("Tearing down '%s' succeeded\n", test->name);
@ -213,7 +213,8 @@ efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
efi_st_get_key();
runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
sizeof(reset_message), reset_message);
efi_st_printf("\nERROR: reset failed.\n");
efi_st_printf("\n");
efi_st_error("Reset failed.\n");
return EFI_UNSUPPORTED;
}

View file

@ -12,6 +12,37 @@
struct efi_simple_text_output_protocol *con_out;
struct efi_simple_input_interface *con_in;
/*
* Print a MAC address to an u16 string
*
* @pointer: mac address
* @buf: pointer to buffer address
* on return position of terminating zero word
*/
static void mac(void *pointer, u16 **buf)
{
int i, j;
u16 c;
u8 *p = (u8 *)pointer;
u8 byte;
u16 *pos = *buf;
for (i = 0; i < ARP_HLEN; ++i) {
if (i)
*pos++ = ':';
byte = p[i];
for (j = 4; j >= 0; j -= 4) {
c = (byte >> j) & 0x0f;
c += '0';
if (c > '9')
c += 'a' - '9' - 1;
*pos++ = c;
}
}
*pos = 0;
*buf = pos;
}
/*
* Print a pointer to an u16 string
*
@ -146,7 +177,15 @@ void efi_st_printf(const char *fmt, ...)
int2dec(va_arg(args, s32), &pos);
break;
case 'p':
pointer(va_arg(args, void*), &pos);
++c;
switch (*c) {
case 'm':
mac(va_arg(args, void*), &pos);
break;
default:
--c;
pointer(va_arg(args, void*), &pos);
}
break;
case 's':
s = va_arg(args, const char *);

View file

@ -14,20 +14,22 @@
static struct efi_event *event_notify;
static struct efi_event *event_wait;
static unsigned int counter;
static unsigned int timer_ticks;
static struct efi_boot_services *boottime;
/*
* Notification function, increments a counter.
* Notification function, increments the notfication count if parameter
* context is provided.
*
* @event notified event
* @context pointer to the counter
* @context pointer to the notification count
*/
static void EFIAPI notify(struct efi_event *event, void *context)
{
if (!context)
return;
++*(unsigned int *)context;
unsigned int *count = context;
if (count)
++*count;
}
/*
@ -38,6 +40,7 @@ static void EFIAPI notify(struct efi_event *event, void *context)
*
* @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)
@ -47,25 +50,27 @@ static int setup(const efi_handle_t handle,
boottime = systable->boottime;
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK, notify, (void *)&counter,
TPL_CALLBACK, notify, (void *)&timer_ticks,
&event_notify);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
TPL_CALLBACK, notify, NULL, &event_wait);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
return EFI_ST_FAILURE;
}
return 0;
return EFI_ST_SUCCESS;
}
/*
* Tear down unit test.
*
* Close the events created in setup.
*
* @return: EFI_ST_SUCCESS for success
*/
static int teardown(void)
{
@ -76,7 +81,7 @@ static int teardown(void)
event_notify = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
return EFI_ST_FAILURE;
}
}
if (event_wait) {
@ -84,10 +89,10 @@ static int teardown(void)
event_wait = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
return EFI_ST_FAILURE;
}
}
return 0;
return EFI_ST_SUCCESS;
}
/*
@ -98,92 +103,95 @@ static int teardown(void)
*
* Run a 100 ms single shot timer and check that it is called once
* while waiting for 100 ms periodic timer for two periods.
*
* @return: EFI_ST_SUCCESS for success
*/
static int execute(void)
{
unsigned long index;
size_t index;
efi_status_t ret;
/* Set 10 ms timer */
counter = 0;
timer_ticks = 0;
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
/* Set 100 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
/* Set some arbitrary non-zero value to make change detectable. */
index = 5;
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->check_event(event_wait);
if (ret != EFI_NOT_READY) {
efi_st_error("Signaled state was not cleared.\n");
efi_st_printf("ret = %u\n", (unsigned int)ret);
return 1;
return EFI_ST_FAILURE;
}
if (index != 0) {
efi_st_error("WaitForEvent returned wrong index\n");
return 1;
return EFI_ST_FAILURE;
}
efi_st_printf("Counter periodic: %u\n", counter);
if (counter < 8 || counter > 12) {
efi_st_printf("Notification count periodic: %u\n", timer_ticks);
if (timer_ticks < 8 || timer_ticks > 12) {
efi_st_error("Incorrect timing of events\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
if (index != 0) {
efi_st_error("Could not cancel timer\n");
return 1;
return EFI_ST_FAILURE;
}
/* Set 10 ms timer */
counter = 0;
timer_ticks = 0;
ret = boottime->set_timer(event_notify, EFI_TIMER_RELATIVE, 100000);
if (index != 0) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
/* Set 100 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_PERIODIC, 1000000);
if (index != 0) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
return EFI_ST_FAILURE;
}
efi_st_printf("Counter single shot: %u\n", counter);
if (counter != 1) {
efi_st_printf("Notification count single shot: %u\n", timer_ticks);
if (timer_ticks != 1) {
efi_st_error("Single shot timer failed\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
return EFI_ST_FAILURE;
}
efi_st_printf("Stopped counter: %u\n", counter);
if (counter != 1) {
efi_st_printf("Notification count stopped timer: %u\n", timer_ticks);
if (timer_ticks != 1) {
efi_st_error("Stopped timer fired\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
if (index != 0) {
if (ret != EFI_SUCCESS) {
efi_st_error("Could not cancel timer\n");
return 1;
return EFI_ST_FAILURE;
}
return 0;
return EFI_ST_SUCCESS;
}
EFI_UNIT_TEST(events) = {

View file

@ -1,5 +1,5 @@
/*
* efi_selftest_events
* efi_selftest_exitbootservices
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
@ -13,19 +13,19 @@
static struct efi_boot_services *boottime;
static struct efi_event *event_notify;
static unsigned int counter;
static unsigned int notification_count;
/*
* Notification function, increments a counter.
* Notification function, increments the notification count.
*
* @event notified event
* @context pointer to the counter
* @context pointer to the notification count
*/
static void EFIAPI notify(struct efi_event *event, void *context)
{
if (!context)
return;
++*(unsigned int *)context;
unsigned int *count = context;
++*count;
}
/*
@ -35,6 +35,7 @@ static void EFIAPI notify(struct efi_event *event, void *context)
*
* @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)
@ -43,21 +44,24 @@ static int setup(const efi_handle_t handle,
boottime = systable->boottime;
counter = 0;
notification_count = 0;
ret = boottime->create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_CALLBACK, notify, (void *)&counter,
TPL_CALLBACK, notify,
(void *)&notification_count,
&event_notify);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
return EFI_ST_FAILURE;
}
return 0;
return EFI_ST_SUCCESS;
}
/*
* Tear down unit test.
*
* Close the event created in setup.
*
* @return: EFI_ST_SUCCESS for success
*/
static int teardown(void)
{
@ -68,10 +72,10 @@ static int teardown(void)
event_notify = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
return EFI_ST_FAILURE;
}
}
return 0;
return EFI_ST_SUCCESS;
}
/*
@ -82,19 +86,21 @@ static int teardown(void)
*
* Call ExitBootServices again and check that the notification function is
* not called again.
*
* @return: EFI_ST_SUCCESS for success
*/
static int execute(void)
{
if (counter != 1) {
efi_st_error("ExitBootServices was not notified");
return 1;
if (notification_count != 1) {
efi_st_error("ExitBootServices was not notified\n");
return EFI_ST_FAILURE;
}
efi_st_exit_boot_services();
if (counter != 1) {
efi_st_error("ExitBootServices was notified twice");
return 1;
if (notification_count != 1) {
efi_st_error("ExitBootServices was notified twice\n");
return EFI_ST_FAILURE;
}
return 0;
return EFI_ST_SUCCESS;
}
EFI_UNIT_TEST(exitbootservices) = {

View file

@ -0,0 +1,431 @@
/*
* efi_selftest_snp
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This unit test covers the Simple Network Protocol as well as
* the CopyMem and SetMem boottime services.
*
* A DHCP discover message is sent. The test is successful if a
* DHCP reply is received.
*
* TODO: Once ConnectController and DisconnectController are implemented
* we should connect our code as controller.
*/
#include <efi_selftest.h>
/*
* MAC address for broadcasts
*/
static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
struct dhcp_hdr {
u8 op;
#define BOOTREQUEST 1
#define BOOTREPLY 2
u8 htype;
# define HWT_ETHER 1
u8 hlen;
# define HWL_ETHER 6
u8 hops;
u32 xid;
u16 secs;
u16 flags;
#define DHCP_FLAGS_UNICAST 0x0000
#define DHCP_FLAGS_BROADCAST 0x0080
u32 ciaddr;
u32 yiaddr;
u32 siaddr;
u32 giaddr;
u8 chaddr[16];
u8 sname[64];
u8 file[128];
};
/*
* Message type option.
*/
#define DHCP_MESSAGE_TYPE 0x35
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
struct dhcp {
struct ethernet_hdr eth_hdr;
struct ip_udp_hdr ip_udp;
struct dhcp_hdr dhcp_hdr;
u8 opt[128];
} __packed;
static struct efi_boot_services *boottime;
static struct efi_simple_network *net;
static struct efi_event *timer;
static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
/* IP packet ID */
static unsigned int net_ip_id;
/*
* Compute the checksum of the IP header. We cover even values of length only.
* We cannot use net/checksum.c due to different CFLAGS values.
*
* @buf: IP header
* @len: length of header in bytes
* @return: checksum
*/
static unsigned int efi_ip_checksum(const void *buf, size_t len)
{
size_t i;
u32 sum = 0;
const u16 *pos = buf;
for (i = 0; i < len; i += 2)
sum += *pos++;
sum = (sum >> 16) + (sum & 0xffff);
sum += sum >> 16;
sum = ~sum & 0xffff;
return sum;
}
/*
* Transmit a DHCPDISCOVER message.
*/
static efi_status_t send_dhcp_discover(void)
{
efi_status_t ret;
struct dhcp p = {};
/*
* Fill ethernet header
*/
boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN);
boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address,
ARP_HLEN);
p.eth_hdr.et_protlen = htons(PROT_IP);
/*
* Fill IP header
*/
p.ip_udp.ip_hl_v = 0x45;
p.ip_udp.ip_len = htons(sizeof(struct dhcp) -
sizeof(struct ethernet_hdr));
p.ip_udp.ip_id = htons(++net_ip_id);
p.ip_udp.ip_off = htons(IP_FLAGS_DFRAG);
p.ip_udp.ip_ttl = 0xff; /* time to live */
p.ip_udp.ip_p = IPPROTO_UDP;
boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff);
p.ip_udp.ip_sum = efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE);
/*
* Fill UDP header
*/
p.ip_udp.udp_src = htons(68);
p.ip_udp.udp_dst = htons(67);
p.ip_udp.udp_len = htons(sizeof(struct dhcp) -
sizeof(struct ethernet_hdr) -
sizeof(struct ip_hdr));
/*
* Fill DHCP header
*/
p.dhcp_hdr.op = BOOTREQUEST;
p.dhcp_hdr.htype = HWT_ETHER;
p.dhcp_hdr.hlen = HWL_ETHER;
p.dhcp_hdr.flags = htons(DHCP_FLAGS_UNICAST);
boottime->copy_mem(&p.dhcp_hdr.chaddr,
&net->mode->current_address, ARP_HLEN);
/*
* Fill options
*/
p.opt[0] = 0x63; /* DHCP magic cookie */
p.opt[1] = 0x82;
p.opt[2] = 0x53;
p.opt[3] = 0x63;
p.opt[4] = DHCP_MESSAGE_TYPE;
p.opt[5] = 0x01; /* length */
p.opt[6] = DHCPDISCOVER;
p.opt[7] = 0x39; /* maximum message size */
p.opt[8] = 0x02; /* length */
p.opt[9] = 0x02; /* 576 bytes */
p.opt[10] = 0x40;
p.opt[11] = 0xff; /* end of options */
/*
* Transmit DHCPDISCOVER message.
*/
ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0);
if (ret != EFI_SUCCESS)
efi_st_error("Sending a DHCP request failed\n");
else
efi_st_printf("DHCP Discover\n");
return ret;
}
/*
* Setup unit test.
*
* Create a 1 s periodic timer.
* Start the network driver.
*
* @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;
boottime = systable->boottime;
/*
* Create a timer event.
*/
ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL,
&timer);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to create event\n");
return EFI_ST_FAILURE;
}
/*
* Set timer period to 1s.
*/
ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to set timer\n");
return EFI_ST_FAILURE;
}
/*
* Find an interface implementing the SNP protocol.
*/
ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net);
if (ret != EFI_SUCCESS) {
net = NULL;
efi_st_error("Failed to locate simple network protocol\n");
return EFI_ST_FAILURE;
}
/*
* Check hardware address size.
*/
if (!net->mode) {
efi_st_error("Mode not provided\n");
return EFI_ST_FAILURE;
}
if (net->mode->hwaddr_size != ARP_HLEN) {
efi_st_error("HwAddressSize = %u, expected %u\n",
net->mode->hwaddr_size, ARP_HLEN);
return EFI_ST_FAILURE;
}
/*
* Check that WaitForPacket event exists.
*/
if (!net->wait_for_packet) {
efi_st_error("WaitForPacket event missing\n");
return EFI_ST_FAILURE;
}
/*
* Initialize network adapter.
*/
ret = net->initialize(net, 0, 0);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to initialize network adapter\n");
return EFI_ST_FAILURE;
}
/*
* Start network adapter.
*/
ret = net->start(net);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to start network adapter\n");
return EFI_ST_FAILURE;
}
return EFI_ST_SUCCESS;
}
/*
* Execute unit test.
*
* A DHCP discover message is sent. The test is successful if a
* DHCP reply is received within 10 seconds.
*
* @return: EFI_ST_SUCCESS for success
*/
static int execute(void)
{
efi_status_t ret;
struct efi_event *events[2];
size_t index;
union {
struct dhcp p;
u8 b[PKTSIZE];
} buffer;
struct efi_mac_address srcaddr;
struct efi_mac_address destaddr;
size_t buffer_size;
u8 *addr;
/*
* The timeout is to occur after 10 s.
*/
unsigned int timeout = 10;
/* Setup may have failed */
if (!net || !timer) {
efi_st_error("Cannot execute test after setup failure\n");
return EFI_ST_FAILURE;
}
/*
* Send DHCP discover message
*/
ret = send_dhcp_discover();
if (ret != EFI_SUCCESS)
return EFI_ST_FAILURE;
/*
* If we would call WaitForEvent only with the WaitForPacket event,
* our code would block until a packet is received which might never
* occur. By calling WaitFor event with both a timer event and the
* WaitForPacket event we can escape this blocking situation.
*
* If the timer event occurs before we have received a DHCP reply
* a further DHCP discover message is sent.
*/
events[0] = timer;
events[1] = net->wait_for_packet;
for (;;) {
/*
* Wait for packet to be received or timer event.
*/
boottime->wait_for_event(2, events, &index);
if (index == 0) {
/*
* The timer event occurred. Check for timeout.
*/
--timeout;
if (!timeout) {
efi_st_error("Timeout occurred\n");
return EFI_ST_FAILURE;
}
/*
* Send further DHCP discover message
*/
ret = send_dhcp_discover();
if (ret != EFI_SUCCESS)
return EFI_ST_FAILURE;
continue;
}
/*
* Receive packet
*/
buffer_size = sizeof(buffer);
net->receive(net, NULL, &buffer_size, &buffer,
&srcaddr, &destaddr, NULL);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to receive packet");
return EFI_ST_FAILURE;
}
/*
* Check the packet is meant for this system.
* Unfortunately QEMU ignores the broadcast flag.
* So we have to check for broadcasts too.
*/
if (efi_st_memcmp(&destaddr, &net->mode->current_address,
ARP_HLEN) &&
efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
continue;
/*
* Check this is a DHCP reply
*/
if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
buffer.p.ip_udp.ip_hl_v != 0x45 ||
buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
buffer.p.ip_udp.udp_src != ntohs(67) ||
buffer.p.ip_udp.udp_dst != ntohs(68) ||
buffer.p.dhcp_hdr.op != BOOTREPLY)
continue;
/*
* We successfully received a DHCP reply.
*/
break;
}
/*
* Write a log message.
*/
addr = (u8 *)&buffer.p.ip_udp.ip_src;
efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
addr[0], addr[1], addr[2], addr[3], &srcaddr);
if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
efi_st_printf("as broadcast message.\n");
else
efi_st_printf("as unicast message.\n");
return EFI_ST_SUCCESS;
}
/*
* Tear down unit test.
*
* Close the timer event created in setup.
* Shut down the network adapter.
*
* @return: EFI_ST_SUCCESS for success
*/
static int teardown(void)
{
efi_status_t ret;
int exit_status = EFI_ST_SUCCESS;
if (timer) {
/*
* Stop timer.
*/
ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to stop timer");
exit_status = EFI_ST_FAILURE;
}
/*
* Close timer event.
*/
ret = boottime->close_event(timer);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to close event");
exit_status = EFI_ST_FAILURE;
}
}
if (net) {
/*
* Stop network adapter.
*/
ret = net->stop(net);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to stop network adapter\n");
exit_status = EFI_ST_FAILURE;
}
/*
* Shut down network adapter.
*/
ret = net->shutdown(net);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to shut down network adapter\n");
exit_status = EFI_ST_FAILURE;
}
}
return exit_status;
}
EFI_UNIT_TEST(snp) = {
.name = "simple network protocol",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
};

View file

@ -13,20 +13,21 @@
static struct efi_event *event_notify;
static struct efi_event *event_wait;
static unsigned int counter;
static unsigned int notification_count;
static struct efi_boot_services *boottime;
/*
* Notification function, increments a counter.
* Notification function, increments the notification count.
*
* @event notified event
* @context pointer to the counter
* @context pointer to the notification count
*/
static void EFIAPI notify(struct efi_event *event, void *context)
{
if (!context)
return;
++*(unsigned int *)context;
unsigned int *count = context;
if (count)
++*count;
}
/*
@ -37,6 +38,7 @@ static void EFIAPI notify(struct efi_event *event, void *context)
*
* @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)
@ -46,25 +48,28 @@ static int setup(const efi_handle_t handle,
boottime = systable->boottime;
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK, notify, (void *)&counter,
TPL_CALLBACK, notify,
(void *)&notification_count,
&event_notify);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
TPL_HIGH_LEVEL, notify, NULL, &event_wait);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
return EFI_ST_FAILURE;
}
return 0;
return EFI_ST_SUCCESS;
}
/*
* Tear down unit test.
*
* Close the events created in setup.
*
* @return: EFI_ST_SUCCESS for success
*/
static int teardown(void)
{
@ -75,7 +80,7 @@ static int teardown(void)
event_notify = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
return EFI_ST_FAILURE;
}
}
if (event_wait) {
@ -83,11 +88,11 @@ static int teardown(void)
event_wait = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
return EFI_ST_FAILURE;
}
}
boottime->restore_tpl(TPL_APPLICATION);
return 0;
return EFI_ST_SUCCESS;
}
/*
@ -101,108 +106,113 @@ static int teardown(void)
*
* Lower the TPL level and check that the queued notification
* function is called.
*
* @return: EFI_ST_SUCCESS for success
*/
static int execute(void)
{
unsigned long index;
size_t index;
efi_status_t ret;
UINTN old_tpl;
/* Set 10 ms timer */
counter = 0;
notification_count = 0;
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
/* Set 100 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
index = 5;
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->check_event(event_wait);
if (ret != EFI_NOT_READY) {
efi_st_error("Signaled state was not cleared.\n");
efi_st_printf("ret = %u\n", (unsigned int)ret);
return 1;
return EFI_ST_FAILURE;
}
if (index != 0) {
efi_st_error("WaitForEvent returned wrong index\n");
return 1;
return EFI_ST_FAILURE;
}
efi_st_printf("Counter with TPL level TPL_APPLICATION: %u\n", counter);
if (counter < 8 || counter > 12) {
efi_st_printf("Notification count with TPL level TPL_APPLICATION: %u\n",
notification_count);
if (notification_count < 8 || notification_count > 12) {
efi_st_error("Incorrect timing of events\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
if (index != 0) {
efi_st_error("Could not cancel timer\n");
return 1;
return EFI_ST_FAILURE;
}
/* Raise TPL level */
old_tpl = boottime->raise_tpl(TPL_CALLBACK);
if (old_tpl != TPL_APPLICATION) {
efi_st_error("Initial TPL level was not TPL_APPLICATION");
return 1;
return EFI_ST_FAILURE;
}
/* Set 10 ms timer */
counter = 0;
notification_count = 0;
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
if (index != 0) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
/* Set 100 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
do {
ret = boottime->check_event(event_wait);
} while (ret == EFI_NOT_READY);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not check event\n");
return 1;
return EFI_ST_FAILURE;
}
efi_st_printf("Counter with TPL level TPL_CALLBACK: %u\n", counter);
if (counter != 0) {
efi_st_printf("Notification count with TPL level TPL_CALLBACK: %u\n",
notification_count);
if (notification_count != 0) {
efi_st_error("Suppressed timer fired\n");
return 1;
return EFI_ST_FAILURE;
}
/* Set 1 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
return EFI_ST_FAILURE;
}
/* Restore the old TPL level */
boottime->restore_tpl(TPL_APPLICATION);
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
return EFI_ST_FAILURE;
}
efi_st_printf("Counter with TPL level TPL_APPLICATION: %u\n", counter);
if (counter < 1) {
efi_st_printf("Notification count with TPL level TPL_APPLICATION: %u\n",
notification_count);
if (notification_count < 1) {
efi_st_error("Queued timer event did not fire\n");
return 1;
return EFI_ST_FAILURE;
}
ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
if (index != 0) {
if (ret != EFI_SUCCESS) {
efi_st_error("Could not cancel timer\n");
return 1;
return EFI_ST_FAILURE;
}
return 0;
return EFI_ST_SUCCESS;
}
EFI_UNIT_TEST(tpl) = {

View file

@ -0,0 +1,25 @@
/*
* efi_selftest_util
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* Utility functions
*/
#include <efi_selftest.h>
int efi_st_memcmp(const void *buf1, const void *buf2, size_t length)
{
const u8 *pos1 = buf1;
const u8 *pos2 = buf2;
for (; length; --length) {
if (*pos1 != *pos2)
return *pos1 - *pos2;
++pos1;
++pos2;
}
return EFI_ST_SUCCESS;
}