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@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;