[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