[syslinux:memdisk-acpi] memdisk: Attempt to export the memdisk as a PnP device via ACPI

syslinux-bot for H. Peter Anvin hpa at linux.intel.com
Thu Jul 15 23:15:04 PDT 2010


Commit-ID:  8f5409186c593083dc1ee73d2b4b2ce77fa703bd
Gitweb:     http://syslinux.zytor.com/commit/8f5409186c593083dc1ee73d2b4b2ce77fa703bd
Author:     H. Peter Anvin <hpa at linux.intel.com>
AuthorDate: Thu, 8 Jul 2010 18:20:05 -0700
Committer:  H. Peter Anvin <hpa at linux.intel.com>
CommitDate: Thu, 8 Jul 2010 18:20:05 -0700

memdisk: Attempt to export the memdisk as a PnP device via ACPI

Attempt to hook into the ACPI namespace and export a pointer to the
MEMDISK.  The PNPID of this device is HPA0001 and it exports two fixed
memory resources: the first one refers to the disk itself, and the
second to the mBFT.

Signed-off-by: H. Peter Anvin <hpa at linux.intel.com>


---
 memdisk/Makefile  |    2 +-
 memdisk/acpi.c    |  233 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 memdisk/conio.c   |   40 +++++++++
 memdisk/memdisk.h |   10 +++
 memdisk/setup.c   |  206 +++++++++++++++++++++++++++++------------------
 5 files changed, 410 insertions(+), 81 deletions(-)

diff --git a/memdisk/Makefile b/memdisk/Makefile
index d2f20c5..8adf281 100644
--- a/memdisk/Makefile
+++ b/memdisk/Makefile
@@ -38,7 +38,7 @@ endif
 # Important: init.o16 must be first!!
 OBJS16   = init.o16 init32.o
 OBJS32   = start32.o setup.o msetup.o e820func.o conio.o memcpy.o memset.o \
-	   memmove.o unzip.o dskprobe.o eltorito.o \
+	   memmove.o unzip.o dskprobe.o eltorito.o acpi.o \
 	   memdisk_chs_512.o memdisk_edd_512.o \
 	   memdisk_iso_512.o memdisk_iso_2048.o
 
diff --git a/memdisk/acpi.c b/memdisk/acpi.c
new file mode 100644
index 0000000..cd0431c
--- /dev/null
+++ b/memdisk/acpi.c
@@ -0,0 +1,233 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2010 Intel Corporation; author: H. Peter Anvin
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdint.h>
+#include "compiler.h"
+#include "bda.h"
+#include "dskprobe.h"
+#include "e820.h"
+#include "conio.h"
+#include "version.h"
+#include "memdisk.h"
+#include "acpi.h"
+#include "../version.h"
+
+/*
+ * acpi.c
+ *
+ * Routines to splice in code into the DSDT.
+ *
+ * WARNING: This doesn't currently even attempt to find and patch the
+ * XSDT, even if both the XSDT and the DSDT pointed to by the XSDT are
+ * both below 4 GiB.
+ */
+
+struct acpi_rsdp {
+    uint8_t  magic[8];		/* "RSD PTR " */
+    uint8_t  csum;
+    char     oemid[6];
+    uint8_t  rev;
+    uint32_t rsdt_addr;
+    uint32_t len;
+    uint64_t xsdt_addr;
+    uint8_t  xcsum;
+    uint8_t  rsvd[3];
+};
+
+struct acpi_rsdt {
+    struct acpi_description_header hdr;
+    struct acpi_description_header *entry[0];
+};
+
+static struct acpi_rsdt *rsdt;
+static struct acpi_description_header **dsdt_ptr;
+
+static uint8_t checksum_range(const void *start, uint32_t size)
+{
+    const uint8_t *p = start;
+    uint8_t csum = 0;
+
+    while (size--)
+	csum += *p++;
+
+    return csum;
+}
+
+static void checksum_table(struct acpi_description_header *hdr)
+{
+    hdr->checksum -= checksum_range(hdr, hdr->length);
+}
+
+enum tbl_errs {
+    ERR_NONE,			/* No errors */
+    ERR_CSUM,			/* Invalid checksum */
+    ERR_SIZE,			/* Impossibly large table */
+    ERR_NOSIG			/* No signature */
+};
+
+static enum tbl_errs is_valid_table(const void *ptr)
+{
+    const struct acpi_description_header *hdr = ptr;
+
+    if (hdr->signature[0] == 0)
+	return ERR_NOSIG;
+
+    if (hdr->length < 10 || hdr->length > (1 << 20)) {
+	/* Either insane or too large to dump */
+	return ERR_SIZE;
+    }
+
+    return checksum_range(hdr, hdr->length) == 0 ? ERR_NONE : ERR_CSUM;
+}
+
+
+static struct acpi_rsdp *scan_for_rsdp(uint32_t base, uint32_t end)
+{
+    for (base &= ~15; base < end-20; base += 16) {
+	struct acpi_rsdp *rsdp = (struct acpi_rsdp *)base;
+
+	if (memcmp(rsdp->magic, "RSD PTR ", 8))
+	    continue;
+
+	if (checksum_range(rsdp, 20))
+	    continue;
+
+	if (rsdp->rev > 0) {
+	    if (base + rsdp->len >= end || checksum_range(rsdp, rsdp->len))
+		continue;
+	}
+
+	return rsdp;
+    }
+
+    return NULL;
+}
+
+static struct acpi_rsdp *find_rsdp(void)
+{
+    uint32_t ebda;
+    struct acpi_rsdp *rsdp;
+
+    ebda = (*(uint16_t *)0x40e) << 4;
+    if (ebda >= 0x10000 && ebda < 0xa0000) {
+	rsdp = scan_for_rsdp(ebda, ebda+1024);
+
+	if (rsdp)
+	    return rsdp;
+    }
+
+    return scan_for_rsdp(0xe0000, 0x100000);
+}
+
+/* Generate a unique ID from the mBFT segment address */
+/* This is basically hexadecimal with [A-P] instead of [0-9A-F] */
+static void gen_id(uint8_t *p, const void *mbft)
+{
+    uint16_t mseg = (size_t)mbft >> 4;
+
+    p[0] = ((mseg >> 12) & 15) + 'A';
+    p[1] = ((mseg >>  8) & 15) + 'A';
+    p[2] = ((mseg >>  4) & 15) + 'A';
+    p[3] = ((mseg >>  0) & 15) + 'A';
+}
+
+static const uint8_t aml_template[] =
+{
+    0x10, 0x4c, 0x04, '_', 'S', 'B', '_', 0x5b, 0x82, 0x44,
+    0x04, 'M', 'D', 'S', 'K', 0x08, '_', 'H', 'I', 'D',
+    0x0c, 0x41, 0xd0, 0x0a, 0x05, 0x5b, 0x82, 0x32,
+    'A', 'A', 'A', 'A',    /* Unique identifier for this memdisk device */
+    0x08, '_', 'H', 'I', 'D', 0x0c, 0x22, 0x01, 0x00, 0x01,
+    0x08, '_', 'C', 'R', 'S', 0x11, 0x1d, 0x0a, 0x1a, 0x86,
+    0x09, 0x00, 0x01,
+    0xaa, 0xaa, 0xaa, 0xaa,	/* Memory range 1 base */
+    0xbb, 0xbb, 0xbb, 0xbb,	/* Memory range 1 size */
+    0x86, 0x09, 0x00, 0x01,
+    0xcc, 0xcc, 0xcc, 0xcc,	/* Memory range 2 base */
+    0xdd, 0xdd, 0xdd, 0xdd,	/* Memory range 2 size */
+    0x79, 0x00
+};
+
+/*
+ * Returns the number of bytes we need to reserve for the replacement DSDT,
+ * or 0 on failure
+ */
+size_t acpi_bytes_needed(void)
+{
+    struct acpi_rsdp *rsdp;
+    struct acpi_description_header *dsdt;
+    int i, n;
+
+    rsdp = find_rsdp();
+    if (!rsdp) {
+	printf("ACPI: unable to locate an ACPI RSDP\n");
+	return 0;
+    }
+
+    rsdt = (struct acpi_rsdt *)rsdp->rsdt_addr;
+    if (is_valid_table(rsdt) != ERR_NONE) {
+	printf("ACPI: unable to locate an ACPI RSDT\n");
+	return 0;
+    }
+
+    n = (rsdt->hdr.length - sizeof(rsdt->hdr))/sizeof(rsdt->entry[0]);
+    for (i = 0; i < n; i++) {
+	enum tbl_errs err;
+	dsdt = rsdt->entry[i];
+	err = is_valid_table(dsdt);
+	if (memcmp(dsdt->signature, "DSDT", 4) &&
+	    memcmp(dsdt->signature, "SSDT", 4))
+	    continue;
+	if (err != ERR_NONE)
+	    continue;
+	dsdt_ptr = &rsdt->entry[i];
+	printf("ACPI: found %4.4s at %p, size %u\n",
+	       dsdt->signature, dsdt, dsdt->length);
+	return dsdt->length + sizeof aml_template;
+    }
+
+    printf("ACPI: unable to locate an ACPI DSDT or SSDT\n");
+    return 0;
+}
+
+void acpi_hack_dsdt(void *membuf, const void *mbft, uint32_t mbft_size,
+		    const void *data, uint32_t data_size)
+{
+    struct acpi_description_header *dsdt = *dsdt_ptr;
+    struct acpi_description_header *new_dsdt = membuf;
+    uint8_t *p = membuf;
+    uint8_t *newcode;
+
+    if (!dsdt_ptr)
+	return;			/* No valid DSDT pointer */
+
+    p = mempcpy(p, dsdt, sizeof *dsdt); /* Copy header */
+    p = mempcpy(newcode = p, aml_template, sizeof aml_template);
+    memcpy(p, dsdt+1, dsdt->length - sizeof *dsdt); /* Copy body */
+
+    /* Generate a unique ID from the mBFT segment address */
+    gen_id(newcode+0x1c, mbft);
+
+    /* Patch in the resources */
+    *((uint32_t *)(newcode+0x37)) = (size_t)data;
+    *((uint32_t *)(newcode+0x3b)) = data_size;
+    *((uint32_t *)(newcode+0x43)) = (size_t)mbft;
+    *((uint32_t *)(newcode+0x47)) = mbft_size;
+
+    /* Checksum the new DSDT */
+    new_dsdt->length += sizeof aml_template;
+    checksum_table(new_dsdt);
+
+    /* Patch the RDST */
+    *dsdt_ptr = new_dsdt;
+    checksum_table(&rsdt->hdr);
+}
diff --git a/memdisk/conio.c b/memdisk/conio.c
index d1f0862..50e6750 100644
--- a/memdisk/conio.c
+++ b/memdisk/conio.c
@@ -17,9 +17,47 @@
  */
 
 #include <stdint.h>
+#include <sys/io.h>
 #include "memdisk.h"
 #include "conio.h"
 
+#ifdef DEBUG
+
+enum serial_port_regs {
+    THR = 0,
+    RBR = 0,
+    DLL = 0,
+    DLM = 1,
+    IER = 1,
+    IIR = 2,
+    FCR = 2,
+    LCR = 3,
+    MCR = 4,
+    LSR = 5,
+    MSR = 6,
+    SCR = 7,
+};
+
+#ifndef DEBUG_PORT
+# define DEBUG_PORT 0x03f8	/* I/O base address */
+#endif
+
+static const uint16_t debug_base = DEBUG_PORT;
+
+static void debug_putc(char c)
+{
+    if (c == '\n')
+	debug_putc('\r');
+
+    while ((inb(debug_base + LSR) & 0x20) == 0)
+	cpu_relax();
+    outb(c, debug_base + THR);
+}
+
+#else
+#define debug_putc(x) ((void)(x))
+#endif
+
 int putchar(int ch)
 {
     com32sys_t regs;
@@ -29,6 +67,8 @@ int putchar(int ch)
 	putchar('\r');
     }
 
+    debug_putc(ch);
+
     regs.eax.w[0] = 0x0e00 | (ch & 0xff);
     intcall(0x10, &regs, NULL);
 
diff --git a/memdisk/memdisk.h b/memdisk/memdisk.h
index b6b277a..66c1d88 100644
--- a/memdisk/memdisk.h
+++ b/memdisk/memdisk.h
@@ -127,6 +127,11 @@ static inline void cli(void)
     asm volatile("cli");
 }
 
+static inline void cpu_relax(void)
+{
+    asm volatile("rep;nop");
+}
+
 /* Decompression */
 extern int check_zip(void *indata, uint32_t size, uint32_t * zbytes_p,
 		     uint32_t * dbytes_p, uint32_t * orig_crc,
@@ -134,4 +139,9 @@ extern int check_zip(void *indata, uint32_t size, uint32_t * zbytes_p,
 extern void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes,
 		   uint32_t orig_crc, void *target);
 
+/* ACPI hacking */
+size_t acpi_bytes_needed(void);
+void acpi_hack_dsdt(void *membuf, const void *mbft, uint32_t mbft_size,
+		    const void *data, uint32_t data_size);
+
 #endif
diff --git a/memdisk/setup.c b/memdisk/setup.c
index 3f69cd3..6fcdf89 100644
--- a/memdisk/setup.c
+++ b/memdisk/setup.c
@@ -114,23 +114,78 @@ static const char *getcmditem(const char *what)
     return CMD_NOTFOUND;
 }
 
+extern const char _end[];		/* Symbol signalling end of data */
+
+static size_t find_memory_region(size_t size, size_t align)
+{
+    uint32_t startrange, endrange;
+    size_t align_mask = align-1;
+    int i;
+
+    /*
+     * Search memory ranges in descending order until we find one
+     * that is legal and fits
+     */
+    for (i = nranges - 1; i >= 0; i--) {
+	/*
+	 * We can't use > 4G memory (32 bits only.)  Truncate to 2^32-1
+	 * so we don't have to deal with funny wraparound issues.
+	 */
+	
+	/* Must be memory */
+	if (ranges[i].type != 1)
+	    continue;
+	
+	/* Range start */
+	if (ranges[i].start >= 0xFFFFFFFF)
+	    continue;
+	
+	startrange = (uint32_t) ranges[i].start;
+	
+	/* Range end (0 for end means 2^64) */
+	endrange = ((ranges[i + 1].start >= 0xFFFFFFFF ||
+		     ranges[i + 1].start == 0)
+		    ? 0xFFFFFFFF : (uint32_t) ranges[i + 1].start);
+	
+	/* Make sure we don't overwrite ourselves */
+	if (startrange < (size_t) _end)
+	    startrange = (size_t) _end;
+	
+	/* Allow for alignment */
+	startrange = (ranges[i].start + align_mask) & ~align_mask;
+	
+	/* In case we just killed the whole range... */
+	if (startrange >= endrange)
+	    continue;
+	
+	/*
+	 * Must be large enough... don't rely on gzwhere for this
+	 * (wraparound)
+	 */
+	if (endrange - startrange < size)
+	    continue;
+
+	/*
+	 * Found an acceptable address...
+	 */
+	return ((endrange - size) & ~align_mask);
+    }
+
+    return 0;			/* Nothing found! */
+}
+
 /*
  * Check to see if this is a gzip image
  */
 #define UNZIP_ALIGN 512
 
-extern void _end;		/* Symbol signalling end of data */
-
-void unzip_if_needed(uint32_t * where_p, uint32_t * size_p)
+static void unzip_if_needed(uint32_t * where_p, uint32_t * size_p)
 {
     uint32_t where = *where_p;
     uint32_t size = *size_p;
     uint32_t zbytes;
-    uint32_t startrange, endrange;
     uint32_t gzdatasize, gzwhere;
     uint32_t orig_crc, offset;
-    uint32_t target = 0;
-    int i, okmem;
 
     /* Is it a gzip image? */
     if (check_zip((void *)where, size, &zbytes, &gzdatasize,
@@ -148,87 +203,48 @@ void unzip_if_needed(uint32_t * where_p, uint32_t * size_p)
 	 * Find a good place to put it: search memory ranges in descending
 	 * order until we find one that is legal and fits
 	 */
-	okmem = 0;
-	for (i = nranges - 1; i >= 0; i--) {
-	    /*
-	     * We can't use > 4G memory (32 bits only.)  Truncate to 2^32-1
-	     * so we don't have to deal with funny wraparound issues.
-	     */
-
-	    /* Must be memory */
-	    if (ranges[i].type != 1)
-		continue;
-
-	    /* Range start */
-	    if (ranges[i].start >= 0xFFFFFFFF)
-		continue;
-
-	    startrange = (uint32_t) ranges[i].start;
+	gzwhere = find_memory_region(gzdatasize, UNZIP_ALIGN);
+	if (!gzwhere)
+	    goto nomem;
 
-	    /* Range end (0 for end means 2^64) */
-	    endrange = ((ranges[i + 1].start >= 0xFFFFFFFF ||
-			 ranges[i + 1].start == 0)
-			? 0xFFFFFFFF : (uint32_t) ranges[i + 1].start);
-
-	    /* Make sure we don't overwrite ourselves */
-	    if (startrange < (uint32_t) & _end)
-		startrange = (uint32_t) & _end;
-
-	    /* Allow for alignment */
-	    startrange =
-		(ranges[i].start + (UNZIP_ALIGN - 1)) & ~(UNZIP_ALIGN - 1);
-
-	    /* In case we just killed the whole range... */
-	    if (startrange >= endrange)
-		continue;
+	/* Mark the memory reserved */
+	insertrange(gzwhere, gzdatasize, 2);
 
+	/* Cast to uint64_t just in case we're flush with the top byte */
+	if ((uint64_t) where + size >= gzwhere &&
+	    where < (uint64_t)gzwhere + gzdatasize) {
 	    /*
-	     * Must be large enough... don't rely on gzwhere for this
-	     * (wraparound)
+	     * Need to move source data to avoid compressed/uncompressed
+	     * overlap
 	     */
-	    if (endrange - startrange < gzdatasize)
-		continue;
-
-	    /*
-	     * This is where the gz image would be put if we put it in this
-	     * range...
-	     */
-	    gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN - 1);
-
-	    /* Cast to uint64_t just in case we're flush with the top byte */
-	    if ((uint64_t) where + size >= gzwhere && where < endrange) {
-		/*
-		 * Need to move source data to avoid compressed/uncompressed
-		 * overlap
-		 */
-		uint32_t newwhere;
-
-		if (gzwhere - startrange < size)
-		    continue;	/* Can't fit both old and new */
-
-		newwhere = (gzwhere - size) & ~(UNZIP_ALIGN - 1);
-		printf("Moving compressed data from 0x%08x to 0x%08x\n",
-		       where, newwhere);
-
-		memmove((void *)newwhere, (void *)where, size);
-		where = newwhere;
-	    }
-
-	    target = gzwhere;
-	    okmem = 1;
-	    break;
+	    uint32_t newwhere;
+	    
+	    newwhere = find_memory_region(size, UNZIP_ALIGN);
+	    if (!newwhere)
+		goto nomem;
+	    
+	    printf("Moving compressed data from 0x%08x to 0x%08x\n",
+		   where, newwhere);
+	    
+	    memmove((void *)newwhere, (void *)where, size);
+	    where = newwhere;
 	}
 
-	if (!okmem)
-	    die("Not enough memory to decompress image (need 0x%08x bytes)\n",
-		gzdatasize);
-
 	printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
-	       target, gzdatasize);
+	       gzwhere, gzdatasize);
 
 	*size_p = gzdatasize;
 	*where_p = (uint32_t) unzip((void *)(where + offset), zbytes,
-				    gzdatasize, orig_crc, (void *)target);
+				    gzdatasize, orig_crc, (void *)gzwhere);
+	return;
+
+    nomem:
+	die("Not enough memory to decompress image (need 0x%08x bytes)\n",
+	    gzdatasize);
+    } else {
+	/* Just reserve the uncompressed data */
+	insertrange(where, size, 2);
+	return;
     }
 }
 
@@ -737,6 +753,7 @@ void setup(const struct real_mode_args *rm_args_ptr)
     int no_bpt;			/* No valid BPT presented */
     uint32_t boot_seg = 0;	/* Meaning 0000:7C00 */
     uint32_t boot_len = 512;	/* One sector */
+    size_t acpi_bytes = 0, acpi_base = 0;
 
     /* We need to copy the rm_args into their proper place */
     memcpy(&rm_args, rm_args_ptr, sizeof rm_args);
@@ -795,8 +812,20 @@ void setup(const struct real_mode_args *rm_args_ptr)
 	}
     }
 
-    /* Reserve the ramdisk memory */
-    insertrange(ramdisk_image, ramdisk_size, 2);
+    /* Allocate ACPI memory, if needed */
+    if (getcmditem("noacpi") == CMD_NOTFOUND) {
+	acpi_bytes = acpi_bytes_needed();
+	if (acpi_bytes) {
+	    acpi_base = find_memory_region(acpi_bytes, 16);
+	    if (!acpi_base) {
+		printf("ACPI: cannot allocate memory, skipping ACPI hook\n");
+		acpi_bytes = 0;
+	    } else {
+		insertrange(acpi_base, acpi_bytes, 4);
+	    }
+	}
+    }
+
     parse_mem();		/* Recompute variables */
 
     /* Figure out where it needs to go */
@@ -1002,6 +1031,18 @@ void setup(const struct real_mode_args *rm_args_ptr)
 	ranges[--nranges].type = -1;
     }
 
+    int i;
+
+    for (i = 0; i < nranges; i++)
+	if (ranges[i].type) {
+	    uint64_t base = ranges[i].start;
+	    uint64_t size = ranges[i+1].start - ranges[i].start;
+	    printf("e820: %08x%08x %08x%08x %d\n",
+		   (uint32_t) (base >> 32), (uint32_t) base,
+		   (uint32_t) (size >> 32), (uint32_t) size,
+		   ranges[i].type);
+	}
+
     if (getcmditem("nopassany") != CMD_NOTFOUND) {
 	printf("nopassany specified - we're the only drive of any kind\n");
 	bios_drives = 0;
@@ -1149,6 +1190,12 @@ void setup(const struct real_mode_args *rm_args_ptr)
     printf("new: int13 = %08x  int15 = %08x  int1e = %08x\n",
 	   rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
 
+    /* Hook ACPI */
+    if (acpi_bytes) {
+	acpi_hack_dsdt((void *)acpi_base, mbft, mbft->acpi.length,
+		       (const void *)ramdisk_image, ramdisk_size);
+    }
+
     /* Figure out entry point */
     if (!boot_seg) {
 	boot_base = 0x7c00;
@@ -1186,4 +1233,3 @@ void setup(const struct real_mode_args *rm_args_ptr)
     shdr->esdi = pnp_install_check();
     shdr->edx = geometry->driveno;
 }
-



More information about the Syslinux-commits mailing list