Merge tag 'efi-2019-10-rc4-3' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
authorTom Rini <trini@konsulko.com>
Fri, 6 Sep 2019 12:04:08 +0000 (08:04 -0400)
committerTom Rini <trini@konsulko.com>
Fri, 6 Sep 2019 12:04:08 +0000 (08:04 -0400)
Pull request for UEFI sub-system for v2019.10-rc4 (3)

This includes the patches from
Pull request for UEFI sub-system for v2019.10-rc4 (2)

Fix UEFI specification compliance issues in the simple network protocol:

* Correctly set and reset the interrupt status.
* Support filling the header in the Transmit() service.
* Correct the checking and setting of the network state.
* Implement the MCastIPtoMAC() service.
* Adjust the simple network protocol unit test.

Fix UEFI specification compliance issues in the protocol.

Fix UEFI specification compliance issues in the simple text output protocol:
* Avoid out of bounds cursor position.
* Do not set illegal screen mode.

Fix UEFI specification compliance issues in the  block IO protocol:
* Check parameters.
* Return correct status code if buffer is unaligned.

Refactor initialization of EFI memory in preparation of support for
> 3GB memory on x86.

15 files changed:
configs/qemu-riscv32_defconfig
configs/qemu-riscv32_smode_defconfig
configs/qemu-riscv64_defconfig
configs/qemu-riscv64_smode_defconfig
doc/api/efi.rst
include/efi_api.h
include/efi_loader.h
lib/efi_loader/efi_console.c
lib/efi_loader/efi_device_path.c
lib/efi_loader/efi_device_path_to_text.c
lib/efi_loader/efi_disk.c
lib/efi_loader/efi_gop.c
lib/efi_loader/efi_memory.c
lib/efi_loader/efi_net.c
lib/efi_selftest/efi_selftest_snp.c

index d5b33b5c2b4dc460e535b5c1718438e96e29981f..fe09a0da88db7b51ec8f05d7ba2df2d40a8efe6e 100644 (file)
@@ -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
index a80e68b8c71e253aba221791e3de2d76135fe807..710332442134703f11886d893b0ed73d1deac283 100644 (file)
@@ -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
index 19a5849226aae2df532c7daa5935c84b20388d0b..ef84dfded2edbdfd911fbddc50b3675c5e4fc4e0 100644 (file)
@@ -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
index 74743a5ebe21fd14ea2abb72863d41cae8d97fa2..1c7a2d150cde44a84eafe16b60d6fd99b048d58e 100644 (file)
@@ -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
index 39e2dbae0bc5312c88e7058f52b1dd3aa3554229..2ca344932e46d094b22d1eb6da56aa5aa88fb16e 100644 (file)
@@ -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:
index 43778197af09bf8c7a117d532dfb42176ce05675..cb895f31e5eb1f1502eb7b757a54b46659aae66d 100644 (file)
@@ -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 \
index 5298ea7997f7428160d45d534c8f45030c8c9525..00eba8afa4de92f1a0324973cc5978dee0562b87 100644 (file)
@@ -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 */
index d4765afb98498600b9d97cd2c4a3454046eb057d..a55e4b33eced265b12a699ea5a078f1aba578414 100644 (file)
@@ -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)
index eeeb80683607117a34ab9b7313ebdc306e7fb53d..ea39f13b735cc52828c06fd2bfaf8300c3714a0d 100644 (file)
@@ -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 {
index 96fd08971b73eb05c55fc5011664b28f68c8dd34..b20b7c097c98227b25c21e1d6c7c7c268df06f53 100644 (file)
@@ -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: {
index 7a6b06821a477895354807722c6d0dd36c046c04..9007a5f77f3d05950cf37e066c03f43ff1c295e7 100644 (file)
@@ -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;
index cad509bfea02ad5b5c47288dac8b34c47c96eb4c..1511e3bdb4220ce8a61be4ee4aa56b456ccad0f9 100644 (file)
@@ -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)
index b5775e0399ea517a7653f7f4ffcf088a37aa2487..83cbc9154f3f47fc4f9cd478ad7630f551021a61 100644 (file)
@@ -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);
        }
 }
 
index 825e064f9aa83f99dd051c72ae836d21aa11ee9d..82d2595847a8cf0f86f18f6ef8f196d01c33a7d6 100644 (file)
@@ -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;
 
index 4c266190012385e99cdb96e9379f2541172d7d54..9797ecaf42ee053c0f872e18369acf2bb86cf513 100644 (file)
@@ -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;