[SeaBIOS] [RFC 1/3] Basic NVDIMM PMEM support

Marc Marí markmb at redhat.com
Mon Sep 21 13:14:05 CET 2015


Follows the ACPI NFIT specification.

Signed-off-by: Marc Marí <markmb at redhat.com>
---
 Makefile            |  3 +-
 src/fw/biostables.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/hw/nvdimm.c     | 21 ++++++++++++++
 src/hw/nvdimm.h     | 11 ++++++++
 src/post.c          |  2 ++
 src/std/acpi.h      | 40 +++++++++++++++++++++++++++
 src/util.h          |  2 ++
 7 files changed, 158 insertions(+), 1 deletion(-)
 create mode 100644 src/hw/nvdimm.c
 create mode 100644 src/hw/nvdimm.h

diff --git a/Makefile b/Makefile
index 3a0d2e8..69a85a1 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,8 @@ SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \
     hw/usb.c hw/usb-uhci.c hw/usb-ohci.c hw/usb-ehci.c \
     hw/usb-hid.c hw/usb-msc.c hw/usb-uas.c \
     hw/blockcmd.c hw/floppy.c hw/ata.c hw/ramdisk.c \
-    hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c
+    hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c \
+    hw/nvdimm.c
 SRC16=$(SRCBOTH)
 SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c romfile.c x86.c optionroms.c \
     pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c tcgbios.c sha1.c \
diff --git a/src/fw/biostables.c b/src/fw/biostables.c
index 71a1a0d..cb0f768 100644
--- a/src/fw/biostables.c
+++ b/src/fw/biostables.c
@@ -17,6 +17,7 @@
 #include "string.h" // memcpy
 #include "util.h" // copy_table
 #include "x86.h" // outb
+#include "hw/nvdimm.h"
 
 struct pir_header *PirAddr VARFSEG;
 
@@ -172,6 +173,29 @@ find_resume_vector(void)
     return facs->firmware_waking_vector;
 }
 
+static struct nfit_descriptor *
+find_nfit(void)
+{
+    dprintf(4, "rsdp=%p\n", RsdpAddr);
+    if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE)
+        return NULL;
+    struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address;
+    dprintf(4, "rsdt=%p\n", rsdt);
+    if (!rsdt || rsdt->signature != RSDT_SIGNATURE)
+        return NULL;
+    void *end = (void*)rsdt + rsdt->length;
+    int i;
+    for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
+        struct nfit_descriptor *nfit = (void*)rsdt->table_offset_entry[i];
+        if (!nfit || nfit->signature != NFIT_SIGNATURE)
+            continue;
+        dprintf(4, "nfit=%p\n", nfit);
+        return nfit;
+    }
+    dprintf(4, "no nfit found\n");
+    return NULL;
+}
+
 static struct acpi_20_generic_address acpi_reset_reg;
 static u8 acpi_reset_val;
 u32 acpi_pm1a_cnt VARFSEG;
@@ -456,6 +480,62 @@ smbios_setup(void)
     smbios_legacy_setup();
 }
 
+/****************************************************************
+ * NFIT
+ ****************************************************************/
+
+#define NVDIMM_MAX 10 /* To avoid using dynamic arrays. Any nicer solution? */
+struct nfit_descriptor *NfitAddr;
+struct nvdimm_addr NvdimmAddr[NVDIMM_MAX + 1];
+
+int nfit_setup(void) {
+    if (NfitAddr) {
+        return 1;
+    }
+
+    NfitAddr = find_nfit();
+
+    if (!NfitAddr) {
+        return 0;
+    }
+
+    void *addr = (void *)NfitAddr + sizeof(*NfitAddr);
+    struct nfit_spa *spa;
+    int index_nvdimm = 0;
+
+    while (addr < ((void *)NfitAddr + NfitAddr->length)) {
+        u16 type = *((u16 *)addr);
+        u16 length = *((u16 *)(addr + 2));
+        if (type == NFIT_TABLE_SPA) {
+            spa = addr;
+
+            if (*(u64 *)(&spa->type_guid[0]) == *(u64 *)(&type_guid_pmem[0]) &&
+                *(u64 *)(&spa->type_guid[8]) == *(u64 *)(&type_guid_pmem[8])) {
+
+                NvdimmAddr[index_nvdimm].addr = spa->spa_base;
+                NvdimmAddr[index_nvdimm].length = spa->spa_length;
+                dprintf(1, "Found NVDIMM at address 0x%llx, size %llx\n",
+                                            spa->spa_base, spa->spa_length);
+                ++index_nvdimm;
+
+                if (index_nvdimm == NVDIMM_MAX) {
+                    dprintf(1, "Too many NVDIMMs. No more will be processed\n");
+                    return 1;
+                }
+            }
+        }
+
+        addr += length;
+    }
+
+    return 1;
+}
+
+struct nvdimm_addr *nfit_get_pmem_addr(void)
+{
+    return NvdimmAddr;
+}
+
 void
 copy_table(void *pos)
 {
diff --git a/src/hw/nvdimm.c b/src/hw/nvdimm.c
new file mode 100644
index 0000000..f7c91a1
--- /dev/null
+++ b/src/hw/nvdimm.c
@@ -0,0 +1,21 @@
+// Support for finding and booting from NVDIMM
+//
+// Copyright (C) 2015  Marc Marí <markmb at redhat.com>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "std/acpi.h"
+#include "util.h"
+#include "output.h"
+#include "memmap.h"
+#include "malloc.h"
+
+void nvdimm_setup(void)
+{
+    if (!nfit_setup()) {
+        dprintf(1, "No NVDIMMs found\n");
+        return;
+    }
+
+    dprintf(1, "NVDIMMs found\n");
+}
diff --git a/src/hw/nvdimm.h b/src/hw/nvdimm.h
new file mode 100644
index 0000000..1591b97
--- /dev/null
+++ b/src/hw/nvdimm.h
@@ -0,0 +1,11 @@
+#ifndef __NVDIMM_H
+#define __NVDIMM_H
+
+struct nvdimm_addr {
+    u64 addr;
+    u64 length;
+};
+
+void nvdimm_setup(void);
+
+#endif
diff --git a/src/post.c b/src/post.c
index 6803585..a4d9737 100644
--- a/src/post.c
+++ b/src/post.c
@@ -23,6 +23,7 @@
 #include "hw/usb.h" // usb_setup
 #include "hw/virtio-blk.h" // virtio_blk_setup
 #include "hw/virtio-scsi.h" // virtio_scsi_setup
+#include "hw/nvdimm.h" // nvdimm setup
 #include "malloc.h" // malloc_init
 #include "memmap.h" // add_e820
 #include "output.h" // dprintf
@@ -153,6 +154,7 @@ device_hardware_setup(void)
     esp_scsi_setup();
     megasas_setup();
     pvscsi_setup();
+    nvdimm_setup();
 }
 
 static void
diff --git a/src/std/acpi.h b/src/std/acpi.h
index b672bbe..2562976 100644
--- a/src/std/acpi.h
+++ b/src/std/acpi.h
@@ -313,5 +313,45 @@ struct tcpa_descriptor_rev2
 #define TCPA_ACPI_CLASS_CLIENT          0
 #define TCPA_ACPI_CLASS_SERVER          1
 
+/*
+ * NFIT (NVDIMM firmware interface) table
+ */
+
+enum {
+    NFIT_TABLE_SPA = 0,
+    NFIT_TABLE_MEM = 1,
+    NFIT_TABLE_IDT = 2,
+    NFIT_TABLE_SMBIOS = 3,
+    NFIT_TABLE_DCR = 4,
+    NFIT_TABLE_BDW = 5,
+    NFIT_TABLE_FLUSH = 6,
+};
+
+#define NFIT_SIGNATURE 0x5449464E // NFIT
+struct nfit_descriptor
+{
+    ACPI_TABLE_HEADER_DEF
+    u32 reserved;
+} PACKED;
+
+/*
+ * struct nfit_spa - System Physical Address Range Structure
+ */
+struct nfit_spa {
+    u16 type;
+    u16 length;
+    u16 spa_index;
+    u16 flags;
+    u32 reserved;
+    u32 proximity_domain;
+    u8 type_guid[16];
+    u64 spa_base;
+    u64 spa_length;
+    u64 mem_attr;
+} PACKED;
+
+/* Converted into little endian */
+static const u8 type_guid_pmem[16] = {0x79, 0xD3, 0xF0, 0x66, 0xF3, 0xB4, 0x74, 0x40,
+                                0xAC, 0x43, 0x0D, 0x33, 0x18, 0xB7, 0x8C, 0xDB};
 
 #endif // acpi.h
diff --git a/src/util.h b/src/util.h
index 327abeb..84478f8 100644
--- a/src/util.h
+++ b/src/util.h
@@ -80,6 +80,8 @@ void copy_smbios(void *pos);
 void display_uuid(void);
 void copy_table(void *pos);
 void smbios_setup(void);
+int nfit_setup(void);
+struct nvdimm_addr *nfit_get_pmem_addr(void);
 
 // fw/coreboot.c
 extern const char *CBvendor, *CBpart;
-- 
2.4.3




More information about the SeaBIOS mailing list