[SeaBIOS] [RFC 3/3] Add NVDIMM booting support

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


Detect NVDIMMs, check for Linux kernel and copy the code to low memory, so the
kernel can be booted.

Signed-off-by: Marc Marí <markmb at redhat.com>
---
 src/boot.c      |  18 +++++++++
 src/hw/nvdimm.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/hw/nvdimm.h |   1 +
 src/romlayout.S |  20 ++++++++++
 src/util.h      |   1 +
 5 files changed, 149 insertions(+), 2 deletions(-)

diff --git a/src/boot.c b/src/boot.c
index e0f73a3..3e4dc93 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -20,6 +20,7 @@
 #include "string.h" // memset
 #include "util.h" // irqtimer_calc
 #include "tcgbios.h" // tpm_*
+#include "hw/nvdimm.h"
 
 
 /****************************************************************
@@ -304,6 +305,7 @@ static struct hlist_head BootList VARVERIFY32INIT;
 #define IPL_TYPE_HARDDISK    0x02
 #define IPL_TYPE_CDROM       0x03
 #define IPL_TYPE_CBFS        0x20
+#define IPL_TYPE_NVDIMM      0x21
 #define IPL_TYPE_BEV         0x80
 #define IPL_TYPE_BCV         0x81
 #define IPL_TYPE_HALT        0xf0
@@ -398,6 +400,12 @@ boot_add_cbfs(void *data, const char *desc, int prio)
     bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
 }
 
+void
+boot_add_nvdimm(void *data, const char *desc, int prio)
+{
+    bootentry_add(IPL_TYPE_NVDIMM, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
+}
+
 
 /****************************************************************
  * Keyboard calls
@@ -674,6 +682,13 @@ boot_cbfs(struct cbfs_file *file)
     cbfs_run_payload(file);
 }
 
+static void
+boot_nvdimm(struct nvdimm_addr* NvdimmAddr)
+{
+    printf("Booting from NVDIMM...\n");
+    nvdimm_boot(NvdimmAddr);
+}
+
 // Boot from a BEV entry on an optionrom.
 static void
 boot_rom(u32 vector)
@@ -731,6 +746,9 @@ do_boot(int seq_nr)
     case IPL_TYPE_CBFS:
         boot_cbfs((void*)ie->vector);
         break;
+    case IPL_TYPE_NVDIMM:
+        boot_nvdimm((void *)ie->vector);
+        break;
     case IPL_TYPE_BEV:
         boot_rom(ie->vector);
         break;
diff --git a/src/hw/nvdimm.c b/src/hw/nvdimm.c
index f7c91a1..6022ce0 100644
--- a/src/hw/nvdimm.c
+++ b/src/hw/nvdimm.c
@@ -7,8 +7,65 @@
 #include "std/acpi.h"
 #include "util.h"
 #include "output.h"
-#include "memmap.h"
+#include "stacks.h"
+#include "x86.h"
+#include "string.h"
+#include "bregs.h"
+#include "farptr.h"
 #include "malloc.h"
+#include "nvdimm.h"
+
+void *page_table;
+
+static u32 nvdimm_check(struct nvdimm_addr *NvdimmAddr)
+{
+    u32 eax;
+
+    // Registers are 32 bits. Pass through stack
+    asm volatile(
+        ".code64\n"
+        "movq %1, %%rdx\n"
+        "movq 0x202(%%rdx), %%rax\n"
+        "subq $0x53726448, %%rax\n" // Check HdrS signature
+        ".code32\n"
+        : "=a"(eax)
+        : "m"(NvdimmAddr->addr)
+        : "edx");
+
+    if (!eax) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static void nvdimm_copy(struct nvdimm_addr *NvdimmAddr)
+{
+    u32 real_addr = 0x10000, prot_addr = 0x100000;
+
+    asm volatile(
+        ".code64\n"
+        "movq %0, %%rdx\n"
+        "xorq %%rbx, %%rbx\n"
+        "movb 0x1f1(%%rdx), %%bl\n"
+        "addq $1, %%rbx\n"
+        "shlq $9, %%rbx\n"
+        "movq %%rbx, %%rcx\n" // Setup size
+        "movq %%rdx, %%rsi\n" // Address from
+        "movq %2, %%rdi\n" // Address to
+        "rep movsb\n" // Copy setup section to "real_addr"
+        "movq %1, %%rcx\n"
+        "subq %%rbx, %%rcx\n" // Kernel size
+        "movq %%rdx, %%rsi\n"
+        "addq %%rbx, %%rsi\n" // Address from
+        "movq %3, %%rdi\n" // Address to
+        "rep movsb\n" // Copy rest of the kernel to "prot_addr"
+        ".code32\n"
+        :
+        : "m"(NvdimmAddr->addr), "g"(NvdimmAddr->length),
+            "g"(real_addr), "g"(prot_addr)
+        : "ebx", "ecx", "edx", "edi", "esi", "memory");
+}
 
 void nvdimm_setup(void)
 {
@@ -17,5 +74,55 @@ void nvdimm_setup(void)
         return;
     }
 
-    dprintf(1, "NVDIMMs found\n");
+    u64 top_addr = 0x100000000ULL;
+    struct nvdimm_addr *NvdimmAddr = nfit_get_pmem_addr();
+
+    int i = 0;
+    while(NvdimmAddr[i].addr != 0) {
+        if (NvdimmAddr[i].addr + NvdimmAddr[i].length > top_addr) {
+            top_addr = NvdimmAddr[i].addr + NvdimmAddr[i].length;
+        }
+
+        ++i;
+    }
+
+    page_table = gen_identity_page_table(top_addr);
+
+    i = 0;
+    while(NvdimmAddr[i].addr != 0) {
+        if (NvdimmAddr[i].length > 0x300) {
+            if (call64(page_table, (void *)nvdimm_check, (u32)&NvdimmAddr[i])) {
+                boot_add_nvdimm(&NvdimmAddr[i], "NVDIMM", 0);
+            }
+        }
+        ++i;
+    }
+}
+
+void nvdimm_boot(struct nvdimm_addr *NvdimmAddr)
+{
+    dprintf(1, "Loading kernel from NVDIMM\n");
+
+    u32 real_addr = 0x10000, cmdline_addr = 0x20000;
+
+    call64(page_table, (void *)nvdimm_copy, (u32)NvdimmAddr);
+
+    writel((void *)cmdline_addr, 0);
+
+    // Last configurations
+    writeb((void *)real_addr + 0x210, 0xB0);
+    writeb((void *)real_addr + 0x211, readb((void *)real_addr + 0x211) | 0x80);
+    writel((void *)real_addr + 0x218, 0);
+    writel((void *)real_addr + 0x21c, 0);
+    writew((void *)real_addr + 0x224, cmdline_addr - real_addr - 0x200);
+    writel((void *)real_addr + 0x228, cmdline_addr);
+
+    struct bregs br;
+    memset(&br, 0, sizeof(br));
+    extern void kernel_stub(void);
+    br.ebx = real_addr >> 4;
+    br.edx = cmdline_addr - real_addr - 16;
+    br.code = SEGOFF(SEG_BIOS, (u32)kernel_stub - BUILD_BIOS_ADDR);
+
+    farcall16big(&br);
 }
diff --git a/src/hw/nvdimm.h b/src/hw/nvdimm.h
index 1591b97..385c1e4 100644
--- a/src/hw/nvdimm.h
+++ b/src/hw/nvdimm.h
@@ -7,5 +7,6 @@ struct nvdimm_addr {
 };
 
 void nvdimm_setup(void);
+void nvdimm_boot(struct nvdimm_addr *NvdimmAddr);
 
 #endif
diff --git a/src/romlayout.S b/src/romlayout.S
index 4cb9f4c..305c7c3 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -285,6 +285,26 @@ __farcall16:
         IRQ_TRAMPOLINE 1c
         IRQ_TRAMPOLINE 4a
 
+        DECLFUNC kernel_stub
+kernel_stub:
+        movw %bx, %ds
+        movw %bx, %es
+        movw %bx, %fs
+        movw %bx, %gs
+        movw %bx, %ss
+        movl %edx, %esp
+        addw $0x20, %bx
+        pushw %bx    // push CS
+        pushw $0     // push IP
+        xorl %eax, %eax
+        xorl %ebx, %ebx
+        xorl %ecx, %ecx
+        xorl %edx, %edx
+        xorl %edi, %edi
+        xorl %esi, %esi
+        xorl %ebp, %ebp
+        lretw
+
 
 /****************************************************************
  * Misc. entry points.
diff --git a/src/util.h b/src/util.h
index 84478f8..524b724 100644
--- a/src/util.h
+++ b/src/util.h
@@ -25,6 +25,7 @@ void boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio);
 void boot_add_hd(struct drive_s *drive_g, const char *desc, int prio);
 void boot_add_cd(struct drive_s *drive_g, const char *desc, int prio);
 void boot_add_cbfs(void *data, const char *desc, int prio);
+void boot_add_nvdimm(void *data, const char *desc, int prio);
 void interactive_bootmenu(void);
 void bcv_prepboot(void);
 struct pci_device;
-- 
2.4.3




More information about the SeaBIOS mailing list