diff options
-rw-r--r-- | configs/qemu-riscv32_defconfig | 1 | ||||
-rw-r--r-- | configs/qemu-riscv32_smode_defconfig | 1 | ||||
-rw-r--r-- | configs/qemu-riscv64_defconfig | 1 | ||||
-rw-r--r-- | configs/qemu-riscv64_smode_defconfig | 1 | ||||
-rw-r--r-- | doc/api/efi.rst | 33 | ||||
-rw-r--r-- | include/efi_api.h | 4 | ||||
-rw-r--r-- | include/efi_loader.h | 4 | ||||
-rw-r--r-- | lib/efi_loader/efi_console.c | 33 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path.c | 2 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path_to_text.c | 31 | ||||
-rw-r--r-- | lib/efi_loader/efi_disk.c | 60 | ||||
-rw-r--r-- | lib/efi_loader/efi_gop.c | 2 | ||||
-rw-r--r-- | lib/efi_loader/efi_memory.c | 82 | ||||
-rw-r--r-- | lib/efi_loader/efi_net.c | 193 | ||||
-rw-r--r-- | lib/efi_selftest/efi_selftest_snp.c | 64 |
15 files changed, 410 insertions, 102 deletions
diff --git a/configs/qemu-riscv32_defconfig b/configs/qemu-riscv32_defconfig index d5b33b5c2b..fe09a0da88 100644 --- a/configs/qemu-riscv32_defconfig +++ b/configs/qemu-riscv32_defconfig @@ -5,5 +5,6 @@ CONFIG_DISTRO_DEFAULTS=y CONFIG_FIT=y CONFIG_DISPLAY_CPUINFO=y CONFIG_DISPLAY_BOARDINFO=y +CONFIG_CMD_BOOTEFI_SELFTEST=y # CONFIG_CMD_MII is not set CONFIG_OF_PRIOR_STAGE=y diff --git a/configs/qemu-riscv32_smode_defconfig b/configs/qemu-riscv32_smode_defconfig index a80e68b8c7..7103324421 100644 --- a/configs/qemu-riscv32_smode_defconfig +++ b/configs/qemu-riscv32_smode_defconfig @@ -6,5 +6,6 @@ CONFIG_DISTRO_DEFAULTS=y CONFIG_FIT=y CONFIG_DISPLAY_CPUINFO=y CONFIG_DISPLAY_BOARDINFO=y +CONFIG_CMD_BOOTEFI_SELFTEST=y # CONFIG_CMD_MII is not set CONFIG_OF_PRIOR_STAGE=y diff --git a/configs/qemu-riscv64_defconfig b/configs/qemu-riscv64_defconfig index 19a5849226..ef84dfded2 100644 --- a/configs/qemu-riscv64_defconfig +++ b/configs/qemu-riscv64_defconfig @@ -6,5 +6,6 @@ CONFIG_DISTRO_DEFAULTS=y CONFIG_FIT=y CONFIG_DISPLAY_CPUINFO=y CONFIG_DISPLAY_BOARDINFO=y +CONFIG_CMD_BOOTEFI_SELFTEST=y # CONFIG_CMD_MII is not set CONFIG_OF_PRIOR_STAGE=y diff --git a/configs/qemu-riscv64_smode_defconfig b/configs/qemu-riscv64_smode_defconfig index 74743a5ebe..1c7a2d150c 100644 --- a/configs/qemu-riscv64_smode_defconfig +++ b/configs/qemu-riscv64_smode_defconfig @@ -7,5 +7,6 @@ CONFIG_DISTRO_DEFAULTS=y CONFIG_FIT=y CONFIG_DISPLAY_CPUINFO=y CONFIG_DISPLAY_BOARDINFO=y +CONFIG_CMD_BOOTEFI_SELFTEST=y # CONFIG_CMD_MII is not set CONFIG_OF_PRIOR_STAGE=y diff --git a/doc/api/efi.rst b/doc/api/efi.rst index 39e2dbae0b..2ca344932e 100644 --- a/doc/api/efi.rst +++ b/doc/api/efi.rst @@ -103,3 +103,36 @@ Block device driver .. kernel-doc:: lib/efi_driver/efi_block_device.c :internal: + +Protocols +--------- + +Block IO protocol +~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: lib/efi_loader/efi_disk.c + :internal: + +File protocol +~~~~~~~~~~~~~ + +.. kernel-doc:: lib/efi_loader/efi_file.c + :internal: + +Graphical output protocol +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: lib/efi_loader/efi_gop.c + :internal: + +Network protocols +~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: lib/efi_loader/efi_net.c + :internal: + +Text IO protocols +~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: lib/efi_loader/efi_console.c + :internal: diff --git a/include/efi_api.h b/include/efi_api.h index 43778197af..cb895f31e5 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -483,7 +483,7 @@ struct efi_device_path_cdrom_path { struct efi_device_path dp; u32 boot_entry; u64 partition_start; - u64 partition_end; + u64 partition_size; } __packed; struct efi_device_path_file_path { @@ -1281,6 +1281,8 @@ struct efi_simple_network { struct efi_mac_address *dest_addr, u16 *protocol); struct efi_event *wait_for_packet; struct efi_simple_network_mode *mode; + /* private fields */ + u32 int_status; }; #define EFI_PXE_BASE_CODE_PROTOCOL_GUID \ diff --git a/include/efi_loader.h b/include/efi_loader.h index 5298ea7997..00eba8afa4 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -478,6 +478,10 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, /* Adds a range into the EFI memory map */ efi_status_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, bool overlap_only_ram); +/* Adds a conventional range into the EFI memory map */ +efi_status_t efi_add_conventional_memory_map(u64 ram_start, u64 ram_end, + u64 ram_top); + /* Called by board init to initialize the EFI drivers */ efi_status_t efi_driver_init(void); /* Called by board init to initialize the EFI memory map */ diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index d4765afb98..a55e4b33ec 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -156,13 +156,14 @@ static efi_status_t EFIAPI efi_cout_output_string( * Update the cursor position. * * The UEFI spec provides advance rules for U+0000, U+0008, U+000A, - * and U000D. All other characters, including control characters - * U+0007 (BEL) and U+0009 (TAB), have to increase the column by one. + * and U000D. All other control characters are ignored. Any non-control + * character increase the column by one. */ for (p = string; *p; ++p) { switch (*p) { case '\b': /* U+0008, backspace */ - con->cursor_column = max(0, con->cursor_column - 1); + if (con->cursor_column) + con->cursor_column--; break; case '\n': /* U+000A, newline */ con->cursor_column = 0; @@ -178,14 +179,21 @@ static efi_status_t EFIAPI efi_cout_output_string( */ break; default: - con->cursor_column++; + /* Exclude control codes */ + if (*p > 0x1f) + con->cursor_column++; break; } if (con->cursor_column >= mode->columns) { con->cursor_column = 0; con->cursor_row++; } - con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1); + /* + * When we exceed the row count the terminal will scroll up one + * line. We have to adjust the cursor position. + */ + if (con->cursor_row >= mode->rows && con->cursor_row) + con->cursor_row--; } out: @@ -211,9 +219,9 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols) /** * query_console_serial() - query console size * - * @rows pointer to return number of rows - * @columns pointer to return number of columns - * Returns 0 on success + * @rows: pointer to return number of rows + * @cols: pointer to return number of columns + * Returns: 0 on success */ static int query_console_serial(int *rows, int *cols) { @@ -371,6 +379,10 @@ static efi_status_t EFIAPI efi_cout_set_mode( if (mode_number >= efi_con_mode.max_mode) return EFI_EXIT(EFI_UNSUPPORTED); + + if (!efi_cout_modes[mode_number].present) + return EFI_EXIT(EFI_UNSUPPORTED); + efi_con_mode.mode = mode_number; EFI_CALL(efi_cout_clear_screen(this)); @@ -452,7 +464,7 @@ struct efi_simple_text_output_protocol efi_con_out = { * struct efi_cin_notify_function - registered console input notify function * * @link: link to list - * @data: key to notify + * @key: key to notify * @function: function to call */ struct efi_cin_notify_function { @@ -470,6 +482,7 @@ static LIST_HEAD(cin_notify_functions); * set_shift_mask() - set shift mask * * @mod: Xterm shift mask + * @key_state: receives the state of the shift, alt, control, and logo keys */ void set_shift_mask(int mod, struct efi_key_state *key_state) { @@ -492,7 +505,7 @@ void set_shift_mask(int mod, struct efi_key_state *key_state) * * This gets called when we have already parsed CSI. * - * @modifiers: bit mask (shift, alt, ctrl) + * @key_state: receives the state of the shift, alt, control, and logo keys * @return: the unmodified code */ static int analyze_modifiers(struct efi_key_state *key_state) diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index eeeb806836..ea39f13b73 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -665,7 +665,7 @@ static void *dp_part_node(void *buf, struct blk_desc *desc, int part) cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH; cddp->dp.length = sizeof(*cddp); cddp->partition_start = info.start; - cddp->partition_end = info.size; + cddp->partition_size = info.size; buf = &cddp[1]; } else { diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index 96fd08971b..b20b7c097c 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -60,9 +60,18 @@ static char *dp_hardware(char *s, struct efi_device_path *dp) break; } case DEVICE_PATH_SUB_TYPE_VENDOR: { + int i, n; struct efi_device_path_vendor *vdp = (struct efi_device_path_vendor *)dp; - s += sprintf(s, "VenHw(%pUl)", &vdp->guid); + + s += sprintf(s, "VenHw(%pUl", &vdp->guid); + n = (int)vdp->dp.length - sizeof(struct efi_device_path_vendor); + if (n > 0) { + s += sprintf(s, ","); + for (i = 0; i < n; ++i) + s += sprintf(s, "%02x", vdp->vendor_data[i]); + } + s += sprintf(s, ")"); break; } default: @@ -115,17 +124,16 @@ static char *dp_msging(char *s, struct efi_device_path *dp) break; } case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: { + int i, n = sizeof(struct efi_mac_addr); struct efi_device_path_mac_addr *mdp = (struct efi_device_path_mac_addr *)dp; - if (mdp->if_type != 0 && mdp->if_type != 1) - break; - - s += sprintf(s, "MAC(%02x%02x%02x%02x%02x%02x,0x%1x)", - mdp->mac.addr[0], mdp->mac.addr[1], - mdp->mac.addr[2], mdp->mac.addr[3], - mdp->mac.addr[4], mdp->mac.addr[5], - mdp->if_type); + if (mdp->if_type <= 1) + n = 6; + s += sprintf(s, "MAC("); + for (i = 0; i < n; ++i) + s += sprintf(s, "%02x", mdp->mac.addr[i]); + s += sprintf(s, ",%u)", mdp->if_type); break; } @@ -133,7 +141,7 @@ static char *dp_msging(char *s, struct efi_device_path *dp) struct efi_device_path_usb_class *ucdp = (struct efi_device_path_usb_class *)dp; - s += sprintf(s, "USBClass(%x,%x,%x,%x,%x)", + s += sprintf(s, "UsbClass(0x%x,0x%x,0x%x,0x%x,0x%x)", ucdp->vendor_id, ucdp->product_id, ucdp->device_class, ucdp->device_subclass, ucdp->device_protocol); @@ -206,7 +214,8 @@ static char *dp_media(char *s, struct efi_device_path *dp) case DEVICE_PATH_SUB_TYPE_CDROM_PATH: { struct efi_device_path_cdrom_path *cddp = (struct efi_device_path_cdrom_path *)dp; - s += sprintf(s, "CDROM(0x%x)", cddp->boot_entry); + s += sprintf(s, "CDROM(%u,0x%llx,0x%llx)", cddp->boot_entry, + cddp->partition_start, cddp->partition_size); break; } case DEVICE_PATH_SUB_TYPE_FILE_PATH: { diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 7a6b06821a..9007a5f77f 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -41,11 +41,26 @@ struct efi_disk_obj { struct blk_desc *desc; }; +/** + * efi_disk_reset() - reset block device + * + * This function implements the Reset service of the EFI_BLOCK_IO_PROTOCOL. + * + * As U-Boot's block devices do not have a reset function simply return + * EFI_SUCCESS. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: pointer to the BLOCK_IO_PROTOCOL + * @extended_verification: extended verification + * Return: status code + */ static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this, char extended_verification) { EFI_ENTRY("%p, %x", this, extended_verification); - return EFI_EXIT(EFI_DEVICE_ERROR); + return EFI_EXIT(EFI_SUCCESS); } enum efi_disk_direction { @@ -69,12 +84,12 @@ static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this, blocks = buffer_size / blksz; lba += diskobj->offset; - debug("EFI: %s:%d blocks=%x lba=%llx blksz=%x dir=%d\n", __func__, - __LINE__, blocks, lba, blksz, direction); + EFI_PRINT("blocks=%x lba=%llx blksz=%x dir=%d\n", + blocks, lba, blksz, direction); /* We only support full block access */ if (buffer_size & (blksz - 1)) - return EFI_DEVICE_ERROR; + return EFI_BAD_BUFFER_SIZE; if (direction == EFI_DISK_READ) n = blk_dread(desc, lba, blocks, buffer); @@ -84,7 +99,7 @@ static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this, /* We don't do interrupts, so check for timers cooperatively */ efi_timer_check(); - debug("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks); + EFI_PRINT("n=%lx blocks=%x\n", n, blocks); if (n != blocks) return EFI_DEVICE_ERROR; @@ -99,6 +114,20 @@ static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this, void *real_buffer = buffer; efi_status_t r; + if (!this) + return EFI_INVALID_PARAMETER; + /* TODO: check for media changes */ + if (media_id != this->media->media_id) + return EFI_MEDIA_CHANGED; + if (!this->media->media_present) + return EFI_NO_MEDIA; + /* media->io_align is a power of 2 */ + if ((uintptr_t)buffer & (this->media->io_align - 1)) + return EFI_INVALID_PARAMETER; + if (lba * this->media->block_size + buffer_size > + this->media->last_block * this->media->block_size) + return EFI_INVALID_PARAMETER; + #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) { r = efi_disk_read_blocks(this, media_id, lba, @@ -134,6 +163,22 @@ static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this, void *real_buffer = buffer; efi_status_t r; + if (!this) + return EFI_INVALID_PARAMETER; + if (this->media->read_only) + return EFI_WRITE_PROTECTED; + /* TODO: check for media changes */ + if (media_id != this->media->media_id) + return EFI_MEDIA_CHANGED; + if (!this->media->media_present) + return EFI_NO_MEDIA; + /* media->io_align is a power of 2 */ + if ((uintptr_t)buffer & (this->media->io_align - 1)) + return EFI_INVALID_PARAMETER; + if (lba * this->media->block_size + buffer_size > + this->media->last_block * this->media->block_size) + return EFI_INVALID_PARAMETER; + #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) { r = efi_disk_write_blocks(this, media_id, lba, @@ -288,6 +333,11 @@ static efi_status_t efi_disk_add_dev( /* Fill in EFI IO Media info (for read/write callbacks) */ diskobj->media.removable_media = desc->removable; diskobj->media.media_present = 1; + /* + * MediaID is just an arbitrary counter. + * We have to change it if the medium is removed or changed. + */ + diskobj->media.media_id = 1; diskobj->media.block_size = desc->blksz; diskobj->media.io_align = desc->blksz; diskobj->media.last_block = desc->lba - offset; diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index cad509bfea..1511e3bdb4 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -319,7 +319,7 @@ static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this, * details. * * @this: the graphical output protocol - * @model_number: the mode to be set + * @mode_number: the mode to be set * Return: status code */ static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number) diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index b5775e0399..83cbc9154f 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -655,6 +655,54 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, return EFI_SUCCESS; } +/** + * efi_add_conventional_memory_map() - add a RAM memory area to the map + * + * @ram_start: start address of a RAM memory area + * @ram_end: end address of a RAM memory area + * @ram_top: max address to be used as conventional memory + * Return: status code + */ +efi_status_t efi_add_conventional_memory_map(u64 ram_start, u64 ram_end, + u64 ram_top) +{ + u64 pages; + + /* Remove partial pages */ + ram_end &= ~EFI_PAGE_MASK; + ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + + if (ram_end <= ram_start) { + /* Invalid mapping */ + return EFI_INVALID_PARAMETER; + } + + pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT; + + efi_add_memory_map(ram_start, pages, + EFI_CONVENTIONAL_MEMORY, false); + + /* + * Boards may indicate to the U-Boot memory core that they + * can not support memory above ram_top. Let's honor this + * in the efi_loader subsystem too by declaring any memory + * above ram_top as "already occupied by firmware". + */ + if (ram_top < ram_start) { + /* ram_top is before this region, reserve all */ + efi_add_memory_map(ram_start, pages, + EFI_BOOT_SERVICES_DATA, true); + } else if ((ram_top >= ram_start) && (ram_top < ram_end)) { + /* ram_top is inside this region, reserve parts */ + pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT; + + efi_add_memory_map(ram_top, pages, + EFI_BOOT_SERVICES_DATA, true); + } + + return EFI_SUCCESS; +} + __weak void efi_add_known_memory(void) { u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK; @@ -672,42 +720,12 @@ __weak void efi_add_known_memory(void) /* Add RAM */ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { - u64 ram_end, ram_start, pages; + u64 ram_end, ram_start; ram_start = (uintptr_t)map_sysmem(gd->bd->bi_dram[i].start, 0); ram_end = ram_start + gd->bd->bi_dram[i].size; - /* Remove partial pages */ - ram_end &= ~EFI_PAGE_MASK; - ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; - - if (ram_end <= ram_start) { - /* Invalid mapping, keep going. */ - continue; - } - - pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT; - - efi_add_memory_map(ram_start, pages, - EFI_CONVENTIONAL_MEMORY, false); - - /* - * Boards may indicate to the U-Boot memory core that they - * can not support memory above ram_top. Let's honor this - * in the efi_loader subsystem too by declaring any memory - * above ram_top as "already occupied by firmware". - */ - if (ram_top < ram_start) { - /* ram_top is before this region, reserve all */ - efi_add_memory_map(ram_start, pages, - EFI_BOOT_SERVICES_DATA, true); - } else if ((ram_top >= ram_start) && (ram_top < ram_end)) { - /* ram_top is inside this region, reserve parts */ - pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT; - - efi_add_memory_map(ram_top, pages, - EFI_BOOT_SERVICES_DATA, true); - } + efi_add_conventional_memory_map(ram_start, ram_end, ram_top); } } diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 825e064f9a..82d2595847 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -1,8 +1,18 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * EFI application network access support + * Simple network protocol + * PXE base code protocol * - * Copyright (c) 2016 Alexander Graf + * Copyright (c) 2016 Alexander Graf + * + * The simple network protocol has the following statuses and services + * to move between them: + * + * Start(): EfiSimpleNetworkStopped -> EfiSimpleNetworkStarted + * Initialize(): EfiSimpleNetworkStarted -> EfiSimpleNetworkInitialized + * Shutdown(): EfiSimpleNetworkInitialized -> EfiSimpleNetworkStarted + * Stop(): EfiSimpleNetworkStarted -> EfiSimpleNetworkStopped + * Reset(): EfiSimpleNetworkInitialized -> EfiSimpleNetworkInitialized */ #include <common.h> @@ -66,10 +76,13 @@ static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this) goto out; } - if (this->mode->state != EFI_NETWORK_STOPPED) + if (this->mode->state != EFI_NETWORK_STOPPED) { ret = EFI_ALREADY_STARTED; - else + } else { + this->int_status = 0; + wait_for_packet->is_signaled = false; this->mode->state = EFI_NETWORK_STARTED; + } out: return EFI_EXIT(ret); } @@ -96,10 +109,13 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) goto out; } - if (this->mode->state == EFI_NETWORK_STOPPED) + if (this->mode->state == EFI_NETWORK_STOPPED) { ret = EFI_NOT_STARTED; - else + } else { + /* Disable hardware and put it into the reset state */ + eth_halt(); this->mode->state = EFI_NETWORK_STOPPED; + } out: return EFI_EXIT(ret); } @@ -130,6 +146,15 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, goto out; } + switch (this->mode->state) { + case EFI_NETWORK_INITIALIZED: + case EFI_NETWORK_STARTED: + break; + default: + r = EFI_NOT_STARTED; + goto out; + } + /* Setup packet buffers */ net_init(); /* Disable hardware and put it into the reset state */ @@ -144,6 +169,8 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, r = EFI_DEVICE_ERROR; goto out; } else { + this->int_status = 0; + wait_for_packet->is_signaled = false; this->mode->state = EFI_NETWORK_INITIALIZED; } out: @@ -164,9 +191,31 @@ out: static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this, int extended_verification) { + efi_status_t ret; + EFI_ENTRY("%p, %x", this, extended_verification); - return EFI_EXIT(EFI_CALL(efi_net_initialize(this, 0, 0))); + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + switch (this->mode->state) { + case EFI_NETWORK_INITIALIZED: + break; + case EFI_NETWORK_STOPPED: + ret = EFI_NOT_STARTED; + goto out; + default: + ret = EFI_DEVICE_ERROR; + goto out; + } + + this->mode->state = EFI_NETWORK_STARTED; + ret = EFI_CALL(efi_net_initialize(this, 0, 0)); +out: + return EFI_EXIT(ret); } /* @@ -191,8 +240,21 @@ static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this) goto out; } + switch (this->mode->state) { + case EFI_NETWORK_INITIALIZED: + break; + case EFI_NETWORK_STOPPED: + ret = EFI_NOT_STARTED; + goto out; + default: + ret = EFI_DEVICE_ERROR; + goto out; + } + eth_halt(); - this->mode->state = EFI_NETWORK_STOPPED; + this->int_status = 0; + wait_for_packet->is_signaled = false; + this->mode->state = EFI_NETWORK_STARTED; out: return EFI_EXIT(ret); @@ -270,7 +332,7 @@ static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this, /* * efi_net_mcastiptomac() - translate multicast IP address to MAC address * - * This function implements the Statistics service of the + * This function implements the MCastIPtoMAC service of the * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface * (UEFI) specification for details. * @@ -285,9 +347,49 @@ static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this, struct efi_ip_address *ip, struct efi_mac_address *mac) { + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac); - return EFI_EXIT(EFI_INVALID_PARAMETER); + if (!this || !ip || !mac) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (ipv6) { + ret = EFI_UNSUPPORTED; + goto out; + } + + /* Multi-cast addresses are in the range 224.0.0.0 - 239.255.255.255 */ + if ((ip->ip_addr[0] & 0xf0) != 0xe0) { + ret = EFI_INVALID_PARAMETER; + goto out; + }; + + switch (this->mode->state) { + case EFI_NETWORK_INITIALIZED: + case EFI_NETWORK_STARTED: + break; + default: + ret = EFI_NOT_STARTED; + goto out; + } + + memset(mac, 0, sizeof(struct efi_mac_address)); + + /* + * Copy lower 23 bits of IPv4 multi-cast address + * RFC 1112, RFC 7042 2.1.1. + */ + mac->mac_addr[0] = 0x01; + mac->mac_addr[1] = 0x00; + mac->mac_addr[2] = 0x5E; + mac->mac_addr[3] = ip->ip_addr[1] & 0x7F; + mac->mac_addr[4] = ip->ip_addr[2]; + mac->mac_addr[5] = ip->ip_addr[3]; +out: + return EFI_EXIT(ret); } /** @@ -297,7 +399,7 @@ static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this, * Protocol. See the UEFI spec for details. * * @this: the instance of the Simple Network Protocol - * @readwrite: true for read, false for write + * @read_write: true for read, false for write * @offset: offset in NVRAM * @buffer_size: size of buffer * @buffer: buffer @@ -350,10 +452,8 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, } 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; + *int_status = this->int_status; + this->int_status = 0; } if (txbuf) *txbuf = new_tx_packet; @@ -404,15 +504,33 @@ static efi_status_t EFIAPI efi_net_transmit goto out; } - if (header_size) { - /* - * TODO: We would need to create the header - * if header_size != 0 - */ - ret = EFI_UNSUPPORTED; + /* At least the IP header has to fit into the buffer */ + if (buffer_size < this->mode->media_header_size) { + ret = EFI_BUFFER_TOO_SMALL; goto out; } + /* + * TODO: + * Support VLANs. Use net_set_ether() for copying the header. Use a + * U_BOOT_ENV_CALLBACK to update the media header size. + */ + if (header_size) { + struct ethernet_hdr *header = buffer; + + if (!dest_addr || !protocol || + header_size != this->mode->media_header_size) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + if (!src_addr) + src_addr = &this->mode->current_address; + + memcpy(header->et_dest, dest_addr, ARP_HLEN); + memcpy(header->et_src, src_addr, ARP_HLEN); + header->et_protlen = htons(*protocol); + } + switch (this->mode->state) { case EFI_NETWORK_STOPPED: ret = EFI_NOT_STARTED; @@ -429,7 +547,7 @@ static efi_status_t EFIAPI efi_net_transmit net_send_packet(transmit_buffer, buffer_size); new_tx_packet = buffer; - + this->int_status |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; out: return EFI_EXIT(ret); } @@ -487,12 +605,6 @@ static efi_status_t EFIAPI efi_net_receive ret = EFI_NOT_READY; goto out; } - /* Check that we at least received an Ethernet header */ - if (net_rx_packet_len < sizeof(struct ethernet_hdr)) { - new_rx_packet = false; - ret = EFI_NOT_READY; - goto out; - } /* Fill export parameters */ eth_hdr = (struct ethernet_hdr *)net_rx_packet; protlen = ntohs(eth_hdr->et_protlen); @@ -517,7 +629,8 @@ static efi_status_t EFIAPI efi_net_receive /* Copy packet */ memcpy(buffer, net_rx_packet, net_rx_packet_len); *buffer_size = net_rx_packet_len; - new_rx_packet = false; + new_rx_packet = 0; + this->int_status &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; out: return EFI_EXIT(ret); } @@ -526,6 +639,9 @@ out: * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address * * This function is called by dhcp_handler(). + * + * @pkt: packet received by dhcp_handler() + * @len: length of the packet received */ void efi_net_set_dhcp_ack(void *pkt, int len) { @@ -548,7 +664,6 @@ void efi_net_set_dhcp_ack(void *pkt, int len) static void efi_net_push(void *pkt, int len) { new_rx_packet = true; - wait_for_packet->is_signaled = true; } /** @@ -556,8 +671,8 @@ static void efi_net_push(void *pkt, int len) * * 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 + * @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) @@ -577,6 +692,17 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event, push_packet = efi_net_push; eth_rx(); push_packet = NULL; + if (new_rx_packet) { + /* Check that we at least received an Ethernet header */ + if (net_rx_packet_len >= + sizeof(struct ethernet_hdr)) { + this->int_status |= + EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + wait_for_packet->is_signaled = true; + } else { + new_rx_packet = 0; + } + } } out: EFI_EXIT(EFI_SUCCESS); @@ -751,9 +877,10 @@ efi_status_t efi_net_register(void) netobj->net.transmit = efi_net_transmit; netobj->net.receive = efi_net_receive; netobj->net.mode = &netobj->net_mode; - netobj->net_mode.state = EFI_NETWORK_STARTED; + netobj->net_mode.state = EFI_NETWORK_STOPPED; memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6); netobj->net_mode.hwaddr_size = ARP_HLEN; + netobj->net_mode.media_header_size = ETHER_HDR_SIZE; netobj->net_mode.max_packet_size = PKTSIZE; netobj->net_mode.if_type = ARP_ETHER; diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c index 4c26619001..9797ecaf42 100644 --- a/lib/efi_selftest/efi_selftest_snp.c +++ b/lib/efi_selftest/efi_selftest_snp.c @@ -228,6 +228,26 @@ static int setup(const efi_handle_t handle, efi_st_error("WaitForPacket event missing\n"); return EFI_ST_FAILURE; } + if (net->mode->state == EFI_NETWORK_INITIALIZED) { + /* + * Shut down network adapter. + */ + ret = net->shutdown(net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to shut down network adapter\n"); + return EFI_ST_FAILURE; + } + } + if (net->mode->state == EFI_NETWORK_STARTED) { + /* + * Stop network adapter. + */ + ret = net->stop(net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to stop network adapter\n"); + return EFI_ST_FAILURE; + } + } /* * Start network adapter. */ @@ -236,6 +256,10 @@ static int setup(const efi_handle_t handle, efi_st_error("Failed to start network adapter\n"); return EFI_ST_FAILURE; } + if (net->mode->state != EFI_NETWORK_STARTED) { + efi_st_error("Failed to start network adapter\n"); + return EFI_ST_FAILURE; + } /* * Initialize network adapter. */ @@ -244,6 +268,10 @@ static int setup(const efi_handle_t handle, efi_st_error("Failed to initialize network adapter\n"); return EFI_ST_FAILURE; } + if (net->mode->state != EFI_NETWORK_INITIALIZED) { + efi_st_error("Failed to initialize network adapter\n"); + return EFI_ST_FAILURE; + } return EFI_ST_SUCCESS; } @@ -268,6 +296,7 @@ static int execute(void) struct efi_mac_address destaddr; size_t buffer_size; u8 *addr; + /* * The timeout is to occur after 10 s. */ @@ -298,6 +327,8 @@ static int execute(void) events[0] = timer; events[1] = net->wait_for_packet; for (;;) { + u32 int_status; + /* * Wait for packet to be received or timer event. */ @@ -323,8 +354,17 @@ static int execute(void) * Receive packet */ buffer_size = sizeof(buffer); - net->receive(net, NULL, &buffer_size, &buffer, - &srcaddr, &destaddr, NULL); + ret = net->get_status(net, &int_status, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to get status"); + return EFI_ST_FAILURE; + } + if (!(int_status & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT)) { + efi_st_error("RX interrupt not set"); + return EFI_ST_FAILURE; + } + ret = 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; @@ -400,21 +440,29 @@ static int teardown(void) } if (net) { /* - * Stop network adapter. + * Shut down network adapter. */ - ret = net->stop(net); + ret = net->shutdown(net); if (ret != EFI_SUCCESS) { - efi_st_error("Failed to stop network adapter\n"); + efi_st_error("Failed to shut down network adapter\n"); exit_status = EFI_ST_FAILURE; } + if (net->mode->state != EFI_NETWORK_STARTED) { + efi_st_error("Failed to shutdown network adapter\n"); + return EFI_ST_FAILURE; + } /* - * Shut down network adapter. + * Stop network adapter. */ - ret = net->shutdown(net); + ret = net->stop(net); if (ret != EFI_SUCCESS) { - efi_st_error("Failed to shut down network adapter\n"); + efi_st_error("Failed to stop network adapter\n"); exit_status = EFI_ST_FAILURE; } + if (net->mode->state != EFI_NETWORK_STOPPED) { + efi_st_error("Failed to stop network adapter\n"); + return EFI_ST_FAILURE; + } } return exit_status; |