summaryrefslogtreecommitdiffstats
path: root/src/core/memmap_settings.c
diff options
context:
space:
mode:
authorMichael Brown2013-08-12 14:42:12 +0200
committerMichael Brown2013-08-12 23:00:36 +0200
commitc692a690dafe4c6bdb406c4c5e0ae66bfcb2f6cb (patch)
treed4dc5f8d6d0938404e649f24e79ec91f81548188 /src/core/memmap_settings.c
parent[settings] Allow numeric_setting_value() to handle long setting values (diff)
downloadipxe-c692a690dafe4c6bdb406c4c5e0ae66bfcb2f6cb.tar.gz
ipxe-c692a690dafe4c6bdb406c4c5e0ae66bfcb2f6cb.tar.xz
ipxe-c692a690dafe4c6bdb406c4c5e0ae66bfcb2f6cb.zip
[settings] Expose memory map via settings mechanism
Allow memory map entries to be read using the syntax ${memmap/<region>.<properties>.<scale>} where <region> is the index of the memory region, <properties> is a bitmask where bit 0 represents the start address and bit 1 represents the length (allowing the end address to be encoded by having both bits 0 and 1 set), and <scale> is the number of bits by which to shift the result. This allows for several values of interest to be encoded. For example: ${memmap/<region>.1.0:hexraw} # 64-bit start address of <region> ${memmap/<region>.2.0:hexraw} # 64-bit length of <region>, in bytes ${memmap/<region>.3.0:hexraw} # 64-bit end address of <region> ${memmap/<region>.2.10:int32} # Length of <region>, in kB ${memmap/<region>.2.20:int32} # Length of <region>, in MB The numeric encoding is slightly more sophisticated than described here, allowing a single encoding to cover multiple regions. (See the source code for details.) The primary use case for this feature is to provide the total system memory size (in MB) via the "memsize" predefined setting. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/core/memmap_settings.c')
-rw-r--r--src/core/memmap_settings.c242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/core/memmap_settings.c b/src/core/memmap_settings.c
new file mode 100644
index 00000000..f06b8750
--- /dev/null
+++ b/src/core/memmap_settings.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/init.h>
+#include <ipxe/settings.h>
+#include <ipxe/io.h>
+
+/** @file
+ *
+ * Memory map settings
+ *
+ * Memory map settings are numerically encoded as:
+ *
+ * Bits 31-24 Number of regions, minus one
+ * Bits 23-16 Starting region
+ * Bits 15-11 Unused
+ * Bit 10 Ignore non-existent regions (rather than generating an error)
+ * Bit 9 Include length
+ * Bit 8 Include start address
+ * Bits 7-6 Unused
+ * Bits 5-0 Scale factor (i.e. right shift count)
+ */
+
+/**
+ * Construct memory map setting tag
+ *
+ * @v start Starting region
+ * @v count Number of regions
+ * @v include_start Include start address
+ * @v include_length Include length
+ * @v ignore Ignore non-existent regions
+ * @v scale Scale factor
+ * @ret tag Setting tag
+ */
+#define MEMMAP_TAG( start, count, include_start, include_length, \
+ ignore, scale ) \
+ ( ( (start) << 16 ) | ( ( (count) - 1 ) << 24 ) | \
+ ( (ignore) << 10 ) | ( (include_length) << 9 ) | \
+ ( (include_start) << 8 ) | (scale) )
+
+/**
+ * Extract number of regions from setting tag
+ *
+ * @v tag Setting tag
+ * @ret count Number of regions
+ */
+#define MEMMAP_COUNT( tag ) ( ( ( (tag) >> 24 ) & 0xff ) + 1 )
+
+/**
+ * Extract starting region from setting tag
+ *
+ * @v tag Setting tag
+ * @ret start Starting region
+ */
+#define MEMMAP_START( tag ) ( ( (tag) >> 16 ) & 0xff )
+
+/**
+ * Extract ignore flag from setting tag
+ *
+ * @v tag Setting tag
+ * @ret ignore Ignore non-existent regions
+ */
+#define MEMMAP_IGNORE_NONEXISTENT( tag ) ( (tag) & 0x00000400UL )
+
+/**
+ * Extract length inclusion flag from setting tag
+ *
+ * @v tag Setting tag
+ * @ret include_length Include length
+ */
+#define MEMMAP_INCLUDE_LENGTH( tag ) ( (tag) & 0x00000200UL )
+
+/**
+ * Extract start address inclusion flag from setting tag
+ *
+ * @v tag Setting tag
+ * @ret include_start Include start address
+ */
+#define MEMMAP_INCLUDE_START( tag ) ( (tag) & 0x00000100UL )
+
+/**
+ * Extract scale factor from setting tag
+ *
+ * @v tag Setting tag
+ * @v scale Scale factor
+ */
+#define MEMMAP_SCALE( tag ) ( (tag) & 0x3f )
+
+/** Memory map settings scope */
+static struct settings_scope memmap_settings_scope;
+
+/**
+ * Check applicability of memory map setting
+ *
+ * @v settings Settings block
+ * @v setting Setting
+ * @ret applies Setting applies within this settings block
+ */
+static int memmap_settings_applies ( struct settings *settings __unused,
+ struct setting *setting ) {
+
+ return ( setting->scope == &memmap_settings_scope );
+}
+
+/**
+ * Fetch value of memory map setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int memmap_settings_fetch ( struct settings *settings,
+ struct setting *setting,
+ void *data, size_t len ) {
+ struct memory_map memmap;
+ struct memory_region *region;
+ uint64_t result = 0;
+ unsigned int i;
+ unsigned int count;
+
+ DBGC ( settings, "MEMMAP start %d count %d %s%s%s%s scale %d\n",
+ MEMMAP_START ( setting->tag ), MEMMAP_COUNT ( setting->tag ),
+ ( MEMMAP_INCLUDE_START ( setting->tag ) ? "start" : "" ),
+ ( ( MEMMAP_INCLUDE_START ( setting->tag ) &&
+ MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) ? "+" : "" ),
+ ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ? "length" : "" ),
+ ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ? " ignore" : "" ),
+ MEMMAP_SCALE ( setting->tag ) );
+
+ /* Fetch memory map */
+ get_memmap ( &memmap );
+
+ /* Extract results from memory map */
+ count = MEMMAP_COUNT ( setting->tag );
+ for ( i = MEMMAP_START ( setting->tag ) ; count-- ; i++ ) {
+
+ /* Check that region exists */
+ if ( i >= memmap.count ) {
+ if ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ) {
+ continue;
+ } else {
+ DBGC ( settings, "MEMMAP region %d does not "
+ "exist\n", i );
+ return -ENOENT;
+ }
+ }
+
+ /* Extract results from this region */
+ region = &memmap.regions[i];
+ if ( MEMMAP_INCLUDE_START ( setting->tag ) ) {
+ result += region->start;
+ DBGC ( settings, "MEMMAP %d start %08llx\n",
+ i, region->start );
+ }
+ if ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) {
+ result += ( region->end - region->start );
+ DBGC ( settings, "MEMMAP %d length %08llx\n",
+ i, ( region->end - region->start ) );
+ }
+ }
+
+ /* Scale result */
+ result >>= MEMMAP_SCALE ( setting->tag );
+
+ /* Return result */
+ result = cpu_to_be64 ( result );
+ if ( len > sizeof ( result ) )
+ len = sizeof ( result );
+ memcpy ( data, &result, len );
+
+ /* Set type if not already specified */
+ if ( ! setting->type )
+ setting->type = &setting_type_hexraw;
+
+ return sizeof ( result );
+}
+
+/** Memory map settings operations */
+static struct settings_operations memmap_settings_operations = {
+ .applies = memmap_settings_applies,
+ .fetch = memmap_settings_fetch,
+};
+
+/** Memory map settings */
+static struct settings memmap_settings = {
+ .refcnt = NULL,
+ .siblings = LIST_HEAD_INIT ( memmap_settings.siblings ),
+ .children = LIST_HEAD_INIT ( memmap_settings.children ),
+ .op = &memmap_settings_operations,
+ .default_scope = &memmap_settings_scope,
+};
+
+/** Initialise memory map settings */
+static void memmap_settings_init ( void ) {
+ int rc;
+
+ if ( ( rc = register_settings ( &memmap_settings, NULL,
+ "memmap" ) ) != 0 ) {
+ DBG ( "MEMMAP could not register settings: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+}
+
+/** Memory map settings initialiser */
+struct init_fn memmap_settings_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = memmap_settings_init,
+};
+
+/** Memory map predefined settings */
+struct setting memmap_predefined_settings[] __setting ( SETTING_MISC ) = {
+ {
+ .name = "memsize",
+ .description = "Memory size (in MB)",
+ .tag = MEMMAP_TAG ( 0, 0x100, 0, 1, 1, 20 ),
+ .type = &setting_type_int32,
+ .scope = &memmap_settings_scope,
+ },
+};