/* * Copyright (C) 2013 Michael Brown . * * 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. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include #include #include /** @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 const 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, const 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 start; unsigned int count; unsigned int scale; int include_start; int include_length; int ignore_nonexistent; unsigned int i; /* Parse settings tag */ start = MEMMAP_START ( setting->tag ); count = MEMMAP_COUNT ( setting->tag ); scale = MEMMAP_SCALE ( setting->tag ); include_start = MEMMAP_INCLUDE_START ( setting->tag ); include_length = MEMMAP_INCLUDE_LENGTH ( setting->tag ); ignore_nonexistent = MEMMAP_IGNORE_NONEXISTENT ( setting->tag ); DBGC ( settings, "MEMMAP start %d count %d %s%s%s%s scale %d\n", start, count, ( include_start ? "start" : "" ), ( ( include_start && include_length ) ? "+" : "" ), ( include_length ? "length" : "" ), ( ignore_nonexistent ? " ignore" : "" ), scale ); /* Fetch memory map */ get_memmap ( &memmap ); /* Extract results from memory map */ for ( i = start ; count-- ; i++ ) { /* Check that region exists */ if ( i >= memmap.count ) { if ( ignore_nonexistent ) { continue; } else { DBGC ( settings, "MEMMAP region %d does not " "exist\n", i ); return -ENOENT; } } /* Extract results from this region */ region = &memmap.regions[i]; if ( include_start ) { result += region->start; DBGC ( settings, "MEMMAP %d start %08llx\n", i, region->start ); } if ( include_length ) { result += ( region->end - region->start ); DBGC ( settings, "MEMMAP %d length %08llx\n", i, ( region->end - region->start ) ); } } /* Scale result */ result >>= scale; /* 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 */ const struct setting memsize_setting __setting ( SETTING_MISC, memsize ) = { .name = "memsize", .description = "Memory size (in MB)", .tag = MEMMAP_TAG ( 0, 0x100, 0, 1, 1, 20 ), .type = &setting_type_int32, .scope = &memmap_settings_scope, };