All of lore.kernel.org
 help / color / mirror / Atom feed
From: Vladimir Serbinenko <phcoder@gmail.com>
To: grub-devel@gnu.org
Cc: Vladimir Serbinenko <phcoder@gmail.com>
Subject: [PATCH vRESEND] Improve cbfs detection
Date: Fri, 17 May 2024 10:52:17 +0300	[thread overview]
Message-ID: <20240517075217.2741-1-phcoder@gmail.com> (raw)

With FMAP and non-continuous SPI old way of reading CBFS pointer
is no longer reliable. Hence use new lbio tags to detect the correct
cbfs layout

Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
---
 grub-core/fs/cbfs.c          | 131 ++++++++++++++++++++++++++++-------
 include/grub/coreboot/lbio.h |  25 ++++++-
 2 files changed, 131 insertions(+), 25 deletions(-)

diff --git a/grub-core/fs/cbfs.c b/grub-core/fs/cbfs.c
index 8ab7106af..7234da7ee 100644
--- a/grub-core/fs/cbfs.c
+++ b/grub-core/fs/cbfs.c
@@ -26,6 +26,7 @@
 #include <grub/dl.h>
 #include <grub/i18n.h>
 #include <grub/cbfs_core.h>
+#include <grub/coreboot/lbio.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -273,7 +274,8 @@ grub_cbfs_close (grub_file_t file)
 #if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) \
   && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN)
 
-static char *cbfsdisk_addr;
+static struct grub_linuxbios_flash_mmap_window *cbfsdisk_map;
+static grub_uint32_t cbfsdisk_map_size;
 static grub_off_t cbfsdisk_size = 0;
 
 static int
@@ -289,10 +291,10 @@ grub_cbfsdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
 static grub_err_t
 grub_cbfsdisk_open (const char *name, grub_disk_t disk)
 {
-  if (grub_strcmp (name, "cbfsdisk"))
+  if (grub_strcmp (name, "cbfsdisk") && cbfsdisk_size > 0)
       return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a cbfsdisk");
 
-  disk->total_sectors = cbfsdisk_size / GRUB_DISK_SECTOR_SIZE;
+  disk->total_sectors = cbfsdisk_size >> GRUB_DISK_SECTOR_BITS;
   disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
   disk->id = 0;
 
@@ -307,10 +309,40 @@ grub_cbfsdisk_close (grub_disk_t disk __attribute((unused)))
 static grub_err_t
 grub_cbfsdisk_read (grub_disk_t disk __attribute((unused)),
 		    grub_disk_addr_t sector,
-		    grub_size_t size, char *buf)
+		    grub_size_t size_sectors, char *buf)
 {
-  grub_memcpy (buf, cbfsdisk_addr + (sector << GRUB_DISK_SECTOR_BITS),
-	       size << GRUB_DISK_SECTOR_BITS);
+  grub_off_t off = sector << GRUB_DISK_SECTOR_BITS;
+  grub_size_t size = size_sectors << GRUB_DISK_SECTOR_BITS;
+  while (size > 0)
+    {
+      unsigned int i;
+      grub_size_t to_read;
+      grub_off_t reg_offset;
+      for (i = 0; i < cbfsdisk_map_size; i++)
+	if (cbfsdisk_map[i].flash_base <= off && off < cbfsdisk_map[i].flash_base + cbfsdisk_map[i].size)
+	  break;
+      if (i == cbfsdisk_map_size)
+	{
+	  grub_off_t next = -1;
+	  for (i = 0; i < cbfsdisk_map_size; i++)
+	    if (cbfsdisk_map[i].flash_base > off && next > cbfsdisk_map[i].flash_base)
+	      next = cbfsdisk_map[i].flash_base;
+	  to_read = grub_min (size, next - off);
+	  grub_memset (buf, 0xff, to_read);
+	  grub_dprintf("cbfs", "Filling 0x%x bytes\n", (unsigned) to_read);
+	  buf += to_read;
+	  size -= to_read;
+	  off += to_read;
+	  continue;
+      }
+      reg_offset = off - cbfsdisk_map[i].flash_base;
+      to_read = grub_min (size, cbfsdisk_map[i].size - reg_offset);
+      grub_memcpy (buf, (void *) (grub_addr_t) (cbfsdisk_map[i].host_base + reg_offset), to_read);
+      grub_dprintf("cbfs", "Copying %p,0x%x bytes\n", (void *) (grub_addr_t) (cbfsdisk_map[i].host_base + reg_offset), (unsigned) to_read);
+      buf += to_read;
+      size -= to_read;
+      off += to_read;
+    }
   return 0;
 }
 
@@ -336,29 +368,80 @@ static struct grub_disk_dev grub_cbfsdisk_dev =
     .next = 0
   };
 
+struct cbtable_iter_ctxt {
+  int has_coreboot;
+  struct grub_linuxbios_table_spi_flash *spi;
+  struct grub_linuxbios_table_boot_media *boot_media;
+};
+
+static int cbtable_iter (grub_linuxbios_table_item_t item,
+			 void *ctxt_in)
+{
+  struct cbtable_iter_ctxt *ctxt = ctxt_in;
+
+  ctxt->has_coreboot = 1;
+
+  if (item->tag == GRUB_LINUXBIOS_MEMBER_SPI_FLASH)
+    ctxt->spi = (void *) (item + 1);
+
+  if (item->tag == GRUB_LINUXBIOS_MEMBER_BOOT_MEDIA)
+    ctxt->boot_media = (void *) (item + 1);
+
+  return 0;
+}
+
 static void
 init_cbfsdisk (void)
 {
-  grub_uint32_t ptr;
-  struct cbfs_header *head;
-
-  ptr = *((grub_uint32_t *) grub_absolute_pointer (0xfffffffc));
-  head = (struct cbfs_header *) (grub_addr_t) ptr;
-  grub_dprintf ("cbfs", "head=%p\n", head);
-
-  /* coreboot current supports only ROMs <= 16 MiB. Bigger ROMs will
-     have problems as RCBA is 18 MiB below end of 32-bit typically,
-     so either memory map would have to be rearranged or we'd need to support
-     reading ROMs through controller directly.
-   */
-  if (ptr < 0xff000000
-      || 0xffffffff - ptr < (grub_uint32_t) sizeof (*head) + 0xf
-      || !validate_head (head))
+  struct cbtable_iter_ctxt ctxt = {
+    0, 0, 0
+  };
+
+  grub_linuxbios_table_iterate (cbtable_iter, &ctxt);
+
+  if (!ctxt.has_coreboot)
     return;
 
-  cbfsdisk_size = ALIGN_UP (grub_be_to_cpu32 (head->romsize),
-			    GRUB_DISK_SECTOR_SIZE);
-  cbfsdisk_addr = (void *) (grub_addr_t) (0x100000000ULL - cbfsdisk_size);
+  if (ctxt.spi)
+    {
+      cbfsdisk_map = ctxt.spi->mmap_table;
+      cbfsdisk_map_size = ctxt.spi->mmap_count;
+      cbfsdisk_size = ctxt.spi->flash_size;
+    }
+  else
+    {
+      if (ctxt.boot_media)
+	cbfsdisk_size = ALIGN_UP (grub_be_to_cpu32 (ctxt.boot_media->boot_media_size),
+				  GRUB_DISK_SECTOR_SIZE);
+      else
+	{
+	  grub_uint32_t ptr;
+	  struct cbfs_header *head;
+
+	  ptr = *((grub_uint32_t *) grub_absolute_pointer (0xfffffffc));
+	  head = (struct cbfs_header *) (grub_addr_t) ptr;
+	  grub_dprintf ("cbfs", "head=%p\n", head);
+
+	  /* coreboot current supports only ROMs <= 16 MiB. Bigger ROMs will
+	     have problems as RCBA is 18 MiB below end of 32-bit typically,
+	     so either memory map would have to be rearranged or we'd need to support
+	     reading ROMs through controller directly.
+	  */
+	  if (ptr < 0xff000000
+	      || 0xffffffff - ptr < (grub_uint32_t) sizeof (*head) + 0xf
+	      || !validate_head (head))
+	    return;
+
+	  cbfsdisk_size = ALIGN_UP (grub_be_to_cpu32 (head->romsize),
+				    GRUB_DISK_SECTOR_SIZE);
+	}
+      cbfsdisk_map_size = 1;
+      static struct grub_linuxbios_flash_mmap_window singleton;
+      singleton.flash_base = 0;
+      singleton.host_base = 0x100000000ULL - cbfsdisk_size;
+      singleton.size = cbfsdisk_size;
+      cbfsdisk_map = &singleton;
+  }
 
   grub_disk_dev_register (&grub_cbfsdisk_dev);
 }
diff --git a/include/grub/coreboot/lbio.h b/include/grub/coreboot/lbio.h
index 5076d36c7..f8641f463 100644
--- a/include/grub/coreboot/lbio.h
+++ b/include/grub/coreboot/lbio.h
@@ -71,7 +71,9 @@ enum
     GRUB_LINUXBIOS_MEMBER_LINK        = 0x11,
     GRUB_LINUXBIOS_MEMBER_FRAMEBUFFER = 0x12,
     GRUB_LINUXBIOS_MEMBER_TIMESTAMPS  = 0x16,
-    GRUB_LINUXBIOS_MEMBER_CBMEMC      = 0x17
+    GRUB_LINUXBIOS_MEMBER_CBMEMC      = 0x17,
+    GRUB_LINUXBIOS_MEMBER_SPI_FLASH   = 0x29,
+    GRUB_LINUXBIOS_MEMBER_BOOT_MEDIA  = 0x30,
   };
 
 struct grub_linuxbios_table_framebuffer {
@@ -100,6 +102,27 @@ struct grub_linuxbios_mem_region
 } GRUB_PACKED;
 typedef struct grub_linuxbios_mem_region *mem_region_t;
 
+struct grub_linuxbios_flash_mmap_window {
+  grub_uint32_t flash_base;
+  grub_uint32_t host_base;
+  grub_uint32_t size;
+} GRUB_PACKED;
+
+struct grub_linuxbios_table_spi_flash {
+  grub_uint32_t flash_size;
+  grub_uint32_t sector_size;
+  grub_uint32_t erase_cmd;
+  grub_uint32_t mmap_count;
+  struct grub_linuxbios_flash_mmap_window mmap_table[0];
+} GRUB_PACKED;
+
+struct grub_linuxbios_table_boot_media {
+  grub_uint64_t fmap_offset;
+  grub_uint64_t cbfs_offset;
+  grub_uint64_t cbfs_size;
+  grub_uint64_t boot_media_size;
+} GRUB_PACKED;
+
 grub_err_t
 EXPORT_FUNC(grub_linuxbios_table_iterate) (int (*hook) (grub_linuxbios_table_item_t,
 					   void *),
-- 
2.39.2


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

                 reply	other threads:[~2024-05-17  7:52 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240517075217.2741-1-phcoder@gmail.com \
    --to=phcoder@gmail.com \
    --cc=grub-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.