grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2 1/2] efi: SPI NOR flash support


From: Michael Lawnick
Subject: Re: [PATCH v2 1/2] efi: SPI NOR flash support
Date: Thu, 11 Feb 2021 08:36:28 +0100
User-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Thunderbird/78.7.1


Hi,

seven days of silence. In the end no interest for extending EFI support?

KR
Michael

Am 05.02.2021 um 09:58 schrieb Michael Lawnick:
Add EFI SPI NOR driver

Use UEFI interface for accessing SPI NOR flashes.
If supported the implementation of UEFI boot software abstracts
away all those ugly H/W details like SPI controller or protocol.
Provided functions:
grub_efi_spi_nor_
        init
        erase
        write
        read
        flash_size
        flash_id
        erase_block_size

This driver might be used for further abstraction to a common
(SPI) flash interface.

Signed-off-by: Michael Lawnick <michael.lawnick@nokia.com>
---
[Patch v2 1/2] : fix flaw in EFI header, wrong sequence of methods.
---
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 68b9e9f68..4d775e5f6 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -446,7 +446,7 @@ image = {
     i386_pc = boot/i386/pc/boot.S;

     cppflags = '-DHYBRID_BOOT=1';
-
+
     i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
     i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';

@@ -656,6 +656,12 @@ module = {
     enable = i386_multiboot;
   };

+module = {
+  name = efi_spi_nor;
+  common = bus/spi/efi_spi_nor.c;
+  enable = efi;
+};
+
   module = {
     name = nativedisk;
     common = commands/nativedisk.c;
diff --git a/grub-core/bus/spi/efi_spi_nor.c
b/grub-core/bus/spi/efi_spi_nor.c
new file mode 100644
index 000000000..0e073b436
--- /dev/null
+++ b/grub-core/bus/spi/efi_spi_nor.c
@@ -0,0 +1,298 @@
+/*  efi_spi_nor.c  - Give access to SPI NOR flash through UEFI interface.
+ *  Copyright 2021 Nokia
+ *  Licensed under the GNU General Public License v3.0 only
+ *  SPDX-License-Identifier: GPL-3.0-only
+ *
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <grub/command.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/efi_spi_nor.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define EFI_SPI_NOR_FLASH_PROTOCOL_GUID \
+       { 0xb57ec3fe, 0xf833, 0x4ba6, \
+               {0x85, 0x78, 0x2a, 0x7d, 0x6a, 0x87, 0x44, 0x4b} \
+       }
+
+#define EFI_FLASHID_LEN 3
+
+struct efi_spi_nor_flash_protocol {
+       struct spi_nor  *spi_peripheral;
+       grub_efi_uint32_t       flash_size;
+       grub_efi_uint8_t        device_id[EFI_FLASHID_LEN];
+       grub_efi_uint32_t       erase_block_size;
+
+       grub_efi_status_t (* get_flash_id)(struct efi_spi_nor_flash_protocol
*this,
+                                            grub_uint8_t *buffer);
+       grub_efi_status_t (* read_data)(struct efi_spi_nor_flash_protocol *this,
+                                         grub_uint32_t offset, grub_uint32_t 
len, grub_uint8_t *data);
+       grub_efi_status_t (* lf_read_data)(struct efi_spi_nor_flash_protocol
*this,
+                                            grub_uint32_t offset, 
grub_uint32_t len, grub_uint8_t *data);
+       grub_efi_status_t (* read_status)(struct efi_spi_nor_flash_protocol 
*this,
+                                           grub_uint32_t num_bytes, 
grub_uint8_t *status);
+       grub_efi_status_t (* write_status)(struct efi_spi_nor_flash_protocol
*this,
+                                            grub_uint32_t num_bytes, 
grub_uint8_t *status);
+       grub_efi_status_t (* write_data)(struct efi_spi_nor_flash_protocol 
*this,
+                                          grub_uint32_t offset, grub_uint32_t 
len, grub_uint8_t *data);
+       grub_efi_status_t (* erase_blocks)(struct efi_spi_nor_flash_protocol
*this,
+                                            grub_uint32_t offset, 
grub_uint32_t blk_count);
+};
+
+/* grub_efi_spi_nor_init - initialize access to SPI NOR flash device
+ *
+ * Search pool of SPI NOR flash devices known to underlying EFI bootware.
+ * Use <flash_id> and <instance> to filter out devices.
+ *
+ * IN: flash_id     - optional, pointer to max 3 bytes
(EFI_FLASHID_LEN) to match against
+ *                    SPI flash JEDEC ID, use NULL if no filtering.
+ * IN: num_id_bytes - number of bytes in flash_id. Maximum 3 bytes
+ *                    are used for comparison.
+ * IN: instance     - number of device occurances to skip
+ *
+ * returns : pointer to flash device or NULL on failure
+ */
+void *
+grub_efi_spi_nor_init(grub_uint8_t *flash_id, grub_uint32_t
num_id_bytes, grub_uint32_t instance)
+{
+       grub_efi_guid_t efi_guid_spi_nor_flash_protocol =
EFI_SPI_NOR_FLASH_PROTOCOL_GUID;
+       grub_efi_status_t ret;
+       grub_efi_uintn_t num_handles;
+       grub_efi_handle_t *handles;
+       grub_uint8_t found_id[EFI_FLASHID_LEN];
+       grub_uint32_t idx, match_cnt=0;
+       struct efi_spi_nor_flash_protocol *spi_nor;
+
+       handles = grub_efi_locate_handle(GRUB_EFI_BY_PROTOCOL,
+                               &efi_guid_spi_nor_flash_protocol, 0,
+                               &num_handles);
+       grub_boot_time("found %ld SPI NOR flash devices\n", num_handles);
+
+       if ((num_handles == 0) || (num_handles < instance + 1))
+               return NULL;
+
+       for (idx = 0; idx < num_handles; idx++) {
+               spi_nor = grub_efi_open_protocol(handles[idx],
+                                               
&efi_guid_spi_nor_flash_protocol,
+                                               
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+               if (! spi_nor) {
+                       grub_error(GRUB_ERR_UNKNOWN_DEVICE, "Failed to open device 
protocol\n");
+                       grub_free(handles);
+                       return NULL;
+               }
+
+               ret = spi_nor->get_flash_id(spi_nor, found_id);
+               if (ret != GRUB_EFI_SUCCESS) {
+                       grub_error(GRUB_ERR_READ_ERROR, "Failed to read 
flash_id\n");
+                       grub_free(handles);
+                       return NULL;
+               }
+
+               /* if caller requests filtering by id */
+               if (flash_id != NULL) {
+                       grub_uint32_t id;
+
+                       if (num_id_bytes > EFI_FLASHID_LEN)
+                               num_id_bytes = EFI_FLASHID_LEN;
+
+                       for (id = 0; id < num_id_bytes; id++)
+                               if (flash_id[id] != found_id[id])
+                                       break;
+
+                       if (id != num_id_bytes)
+                               continue;
+               }
+
+               if (match_cnt < instance) {
+                       match_cnt++;
+                       continue;
+               }
+
+               grub_boot_time("Found flash with ID 0x%02x 0x%02x 0x%02x\n",
+                                               found_id[0], found_id[1], 
found_id[2]);
+
+               grub_free(handles);
+               return spi_nor;
+       }
+
+       grub_free(handles);
+       return NULL;
+}
+
+/* grub_efi_spi_nor_flash_size - get memory size of SPI NOR flash device
+ *
+ * IN: efi_flash_dev - device identifier returned by grub_efi_spi_nor_init.
+ *
+ * returns : memory size of flash device or 0 on failure
+ */
+grub_uint32_t
+grub_efi_spi_nor_flash_size(void *efi_flash_dev)
+{
+       struct efi_spi_nor_flash_protocol *spi_nor = efi_flash_dev;
+
+       if (spi_nor == NULL)
+               return 0;
+
+       return spi_nor->flash_size;
+}
+
+/* grub_efi_spi_nor_device_id - get three byte JEDEC ID of SPI NOR
flash device
+ *
+ * IN: efi_flash_dev - device identifier returned by grub_efi_spi_nor_init.
+ *
+ * returns : three byte JEDEC ID as a uint32 or 0 on failure
+ */
+grub_uint32_t
+grub_efi_spi_nor_device_id(void *efi_flash_dev)
+{
+       struct efi_spi_nor_flash_protocol *spi_nor = efi_flash_dev;
+       grub_uint32_t devId = 0;
+       grub_efi_status_t ret;
+       grub_uint8_t device_id[3];
+       int i;
+
+       if (spi_nor == NULL)
+               return 0;
+
+       ret = spi_nor->get_flash_id(spi_nor, device_id);
+       if (ret != GRUB_EFI_SUCCESS)
+               return 0;
+
+       for(i=0; i<3;i++)
+               devId = (devId<<8) + device_id[i];
+
+       return devId;
+}
+
+/* grub_efi_spi_nor_erase_block_size - get erase block size of SPI NOR
flash device
+ *
+ * Parameters for calls to grub_efi_spi_nor_erase() need to be erase
block size
+ * aligned.
+ *
+ * IN: efi_flash_dev - device identifier returned by grub_efi_spi_nor_init.
+ *
+ * returns : size of erase block of flash device or 0 on failure
+ */
+grub_uint32_t
+grub_efi_spi_nor_erase_block_size(void *efi_flash_dev)
+{
+       struct efi_spi_nor_flash_protocol *spi_nor = efi_flash_dev;
+
+       if (spi_nor == NULL)
+               return 0;
+
+       return spi_nor->erase_block_size;
+}
+
+/* grub_efi_spi_nor_read - read from SPI NOR flash device
+ *
+ * IN:  efi_flash_dev - device identifier returned by
grub_efi_spi_nor_init.
+ * OUT: buffer        - pointer to an preallocated buffer of min. bytes
size
+ * IN:  offset        - starting point where to start to read from
+ * IN:  bytes         - number of bytes to read from flash and write to
buffer
+ *
+ * returns : GRUB_EFI_SUCCESS if all ok
+ *           GRUB_EFI_INVALID_PARAMETER if an argument is bad
+ *
+ */
+grub_err_t
+grub_efi_spi_nor_read(void *efi_flash_dev, grub_uint8_t *buffer,
grub_uint32_t offset, grub_uint32_t bytes)
+{
+       struct efi_spi_nor_flash_protocol *spi_nor = efi_flash_dev;
+       grub_efi_status_t ret;
+
+       if (spi_nor == NULL || buffer == NULL)
+               return GRUB_ERR_BAD_ARGUMENT;
+
+       ret = (spi_nor->read_data(spi_nor, offset, bytes, buffer));
+       if (ret != GRUB_EFI_SUCCESS)
+               return grub_error(GRUB_ERR_READ_ERROR, "Failed to read data 
@0x%x\n",
offset);
+
+       return GRUB_ERR_NONE;
+}
+
+/* grub_efi_spi_nor_write - write to SPI NOR flash device
+ *
+ * IN: efi_flash_dev - device identifier returned by grub_efi_spi_nor_init.
+ * IN: buffer        - pointer to buffer containig min. bytes size data
to write
+ * IN: offset        - starting point where to start to write to
+ * IN: bytes         - number of bytes to write to buffer
+ *
+ * returns : GRUB_EFI_SUCCESS if all ok
+ *           GRUB_EFI_INVALID_PARAMETER if an argument is bad
+ *
+ */
+grub_err_t
+grub_efi_spi_nor_write(void *efi_flash_dev, grub_uint8_t *buffer,
grub_uint32_t offset, grub_uint32_t bytes)
+{
+       struct efi_spi_nor_flash_protocol *spi_nor = efi_flash_dev;
+       grub_efi_status_t ret;
+
+       if (spi_nor == NULL || buffer == NULL)
+               return GRUB_ERR_BAD_ARGUMENT;
+
+       ret = (spi_nor->write_data(spi_nor, offset, bytes, buffer));
+       if (ret != GRUB_EFI_SUCCESS)
+               return grub_error(GRUB_ERR_WRITE_ERROR, "Failed to write data
@0x%x\n", offset);
+
+       return GRUB_ERR_NONE;
+}
+
+/* grub_efi_spi_nor_erase - erase sectors on SPI NOR flash device
+ *
+ * Parameters offset and bytes need to be erase sector aligned, i.e.
+ * multiples of the size returned by function
grub_efi_spi_nor_erase_block_size()
+ *
+ * IN: efi_flash_dev - device identifier returned by grub_efi_spi_nor_init.
+ * IN: offset        - offset of first sector to erase
+ * IN: bytes         - length of region to erase
+ *
+ * returns : GRUB_EFI_SUCCESS if all ok
+ *           GRUB_EFI_INVALID_PARAMETER if an argument is bad
+ *
+ */
+grub_err_t
+grub_efi_spi_nor_erase(void *efi_flash_dev, grub_uint32_t offset,
grub_uint32_t bytes)
+{
+       struct efi_spi_nor_flash_protocol *spi_nor = efi_flash_dev;
+       grub_efi_status_t ret;
+       grub_uint32_t sect_sz;
+       grub_uint32_t sect_mask;
+
+       if (spi_nor == NULL || bytes == 0)
+               return GRUB_ERR_BAD_ARGUMENT;
+
+       sect_sz = grub_efi_spi_nor_erase_block_size(spi_nor);
+       sect_mask = sect_sz - 1;
+       if ((offset & sect_mask) != 0)
+               return grub_error(GRUB_ERR_BAD_ARGUMENT, "SPI NOR erase offset 
not at
sector boundary");
+
+       if (((offset + bytes) & sect_mask) != 0)
+               return grub_error(GRUB_ERR_BAD_ARGUMENT, "SPI NOR erase end not 
at
sector boundary");
+
+       ret = spi_nor->erase_blocks(spi_nor, offset, (bytes-1) / sect_sz + 1);
+
+       if (ret != GRUB_EFI_SUCCESS)
+               return grub_error(GRUB_ERR_WRITE_ERROR, "SPI NOR erase operation
failed");
+
+       return GRUB_ERR_NONE;
+}
diff --git a/include/grub/efi/efi_spi_nor.h b/include/grub/efi/efi_spi_nor.h
new file mode 100644
index 000000000..d09f0c9b7
--- /dev/null
+++ b/include/grub/efi/efi_spi_nor.h
@@ -0,0 +1,37 @@
+/*  efi_spi_nor.h  - Give access to SPI NOR flash through UEFI interface.
+ *  Copyright 2021 Nokia
+ *  Licensed under the GNU General Public License v3.0 only
+ *  SPDX-License-Identifier: GPL-3.0-only
+ *
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GRUB_EFI_SPI_NOR_HEADER
+#define GRUB_EFI_SPI_NOR_HEADER
+
+#include <grub/types.h>
+
+void *grub_efi_spi_nor_init(grub_uint8_t *flash_id, grub_uint32_t
num_id_bytes, grub_uint32_t instance);
+
+grub_uint32_t grub_efi_spi_nor_flash_size(void *efi_flash_dev);
+grub_uint32_t grub_efi_spi_nor_device_id(void *efi_flash_dev);
+grub_uint32_t grub_efi_spi_nor_erase_block_size(void *efi_flash_dev);
+
+grub_err_t grub_efi_spi_nor_read(void *efi_flash_dev, grub_uint8_t
*buffer, grub_uint32_t offset, grub_uint32_t bytes);
+grub_err_t grub_efi_spi_nor_write(void *efi_flash_dev, grub_uint8_t
*buffer, grub_uint32_t offset, grub_uint32_t bytes);
+grub_err_t grub_efi_spi_nor_erase(void *efi_flash_dev, grub_uint32_t
offset, grub_uint32_t bytes);
+
+#endif /*GRUB_EFISPINOR*/

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel





reply via email to

[Prev in Thread] Current Thread [Next in Thread]