[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, ®s, 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