Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sysfs: add /sys/firmware/memmap

This patch adds /sys/firmware/memmap interface that represents the BIOS
(or Firmware) provided memory map. The tree looks like:

/sys/firmware/memmap/0/start (hex number)
end (hex number)
type (string)
... /1/start
end
type

With the following shell snippet one can print the memory map in the same form
the kernel prints itself when booting on x86 (the E820 map).

--------- 8< --------------------------
#!/bin/sh
cd /sys/firmware/memmap
for dir in * ; do
start=$(cat $dir/start)
end=$(cat $dir/end)
type=$(cat $dir/type)
printf "%016x-%016x (%s)\n" $start $[ $end +1] "$type"
done
--------- >8 --------------------------

That patch only provides the needed interface:

1. The sysfs interface.
2. The structure and enumeration definition.
3. The function firmware_map_add() and firmware_map_add_early()
that should be called from architecture code (E820/EFI, for
example) to add the contents to the interface.

If the kernel is compiled without CONFIG_FIRMWARE_MEMMAP, the interface does
nothing without cluttering the architecture-specific code with #ifdef's.

The purpose of the new interface is kexec: While /proc/iomem represents
the *used* memory map (e.g. modified via kernel parameters like 'memmap'
and 'mem'), the /sys/firmware/memmap tree represents the unmodified memory
map provided via the firmware. So kexec can:

- use the original memory map for rebooting,
- use the /proc/iomem for setting up the ELF core headers for kdump
case that should only represent the memory of the system.

The patch has been tested on i386 and x86_64.

Signed-off-by: Bernhard Walle <bwalle@suse.de>
Acked-by: Greg KH <gregkh@suse.de>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Cc: kexec@lists.infradead.org
Cc: yhlu.kernel@gmail.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by

Bernhard Walle and committed by
Ingo Molnar
69ac9cd6 6247943d

+361
+71
Documentation/ABI/testing/sysfs-firmware-memmap
··· 1 + What: /sys/firmware/memmap/ 2 + Date: June 2008 3 + Contact: Bernhard Walle <bwalle@suse.de> 4 + Description: 5 + On all platforms, the firmware provides a memory map which the 6 + kernel reads. The resources from that memory map are registered 7 + in the kernel resource tree and exposed to userspace via 8 + /proc/iomem (together with other resources). 9 + 10 + However, on most architectures that firmware-provided memory 11 + map is modified afterwards by the kernel itself, either because 12 + the kernel merges that memory map with other information or 13 + just because the user overwrites that memory map via command 14 + line. 15 + 16 + kexec needs the raw firmware-provided memory map to setup the 17 + parameter segment of the kernel that should be booted with 18 + kexec. Also, the raw memory map is useful for debugging. For 19 + that reason, /sys/firmware/memmap is an interface that provides 20 + the raw memory map to userspace. 21 + 22 + The structure is as follows: Under /sys/firmware/memmap there 23 + are subdirectories with the number of the entry as their name: 24 + 25 + /sys/firmware/memmap/0 26 + /sys/firmware/memmap/1 27 + /sys/firmware/memmap/2 28 + /sys/firmware/memmap/3 29 + ... 30 + 31 + The maximum depends on the number of memory map entries provided 32 + by the firmware. The order is just the order that the firmware 33 + provides. 34 + 35 + Each directory contains three files: 36 + 37 + start : The start address (as hexadecimal number with the 38 + '0x' prefix). 39 + end : The end address, inclusive (regardless whether the 40 + firmware provides inclusive or exclusive ranges). 41 + type : Type of the entry as string. See below for a list of 42 + valid types. 43 + 44 + So, for example: 45 + 46 + /sys/firmware/memmap/0/start 47 + /sys/firmware/memmap/0/end 48 + /sys/firmware/memmap/0/type 49 + /sys/firmware/memmap/1/start 50 + ... 51 + 52 + Currently following types exist: 53 + 54 + - System RAM 55 + - ACPI Tables 56 + - ACPI Non-volatile Storage 57 + - reserved 58 + 59 + Following shell snippet can be used to display that memory 60 + map in a human-readable format: 61 + 62 + -------------------- 8< ---------------------------------------- 63 + #!/bin/bash 64 + cd /sys/firmware/memmap 65 + for dir in * ; do 66 + start=$(cat $dir/start) 67 + end=$(cat $dir/end) 68 + type=$(cat $dir/type) 69 + printf "%016x-%016x (%s)\n" $start $[ $end +1] "$type" 70 + done 71 + -------------------- >8 ----------------------------------------
+10
drivers/firmware/Kconfig
··· 26 26 kernel. Say N if you want EDD enabled by default. EDD can be dynamically set 27 27 using the kernel parameter 'edd={on|skipmbr|off}'. 28 28 29 + config FIRMWARE_MEMMAP 30 + bool "Add firmware-provided memory map to sysfs" if EMBEDDED 31 + default (X86_64 || X86_32) 32 + help 33 + Add the firmware-provided (unmodified) memory map to /sys/firmware/memmap. 34 + That memory map is used for example by kexec to set up parameter area 35 + for the next kernel, but can also be used for debugging purposes. 36 + 37 + See also Documentation/ABI/testing/sysfs-firmware-memmap. 38 + 29 39 config EFI_VARS 30 40 tristate "EFI Variable Support via sysfs" 31 41 depends on EFI
+1
drivers/firmware/Makefile
··· 10 10 obj-$(CONFIG_DMIID) += dmi-id.o 11 11 obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o 12 12 obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o 13 + obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
+205
drivers/firmware/memmap.c
··· 1 + /* 2 + * linux/drivers/firmware/memmap.c 3 + * Copyright (C) 2008 SUSE LINUX Products GmbH 4 + * by Bernhard Walle <bwalle@suse.de> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License v2.0 as published by 8 + * the Free Software Foundation 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + */ 16 + 17 + #include <linux/string.h> 18 + #include <linux/firmware-map.h> 19 + #include <linux/kernel.h> 20 + #include <linux/module.h> 21 + #include <linux/types.h> 22 + #include <linux/bootmem.h> 23 + 24 + /* 25 + * Data types ------------------------------------------------------------------ 26 + */ 27 + 28 + /* 29 + * Firmware map entry. Because firmware memory maps are flat and not 30 + * hierarchical, it's ok to organise them in a linked list. No parent 31 + * information is necessary as for the resource tree. 32 + */ 33 + struct firmware_map_entry { 34 + resource_size_t start; /* start of the memory range */ 35 + resource_size_t end; /* end of the memory range (incl.) */ 36 + const char *type; /* type of the memory range */ 37 + struct list_head list; /* entry for the linked list */ 38 + struct kobject kobj; /* kobject for each entry */ 39 + }; 40 + 41 + /* 42 + * Forward declarations -------------------------------------------------------- 43 + */ 44 + static ssize_t memmap_attr_show(struct kobject *kobj, 45 + struct attribute *attr, char *buf); 46 + static ssize_t start_show(struct firmware_map_entry *entry, char *buf); 47 + static ssize_t end_show(struct firmware_map_entry *entry, char *buf); 48 + static ssize_t type_show(struct firmware_map_entry *entry, char *buf); 49 + 50 + /* 51 + * Static data ----------------------------------------------------------------- 52 + */ 53 + 54 + struct memmap_attribute { 55 + struct attribute attr; 56 + ssize_t (*show)(struct firmware_map_entry *entry, char *buf); 57 + }; 58 + 59 + struct memmap_attribute memmap_start_attr = __ATTR_RO(start); 60 + struct memmap_attribute memmap_end_attr = __ATTR_RO(end); 61 + struct memmap_attribute memmap_type_attr = __ATTR_RO(type); 62 + 63 + /* 64 + * These are default attributes that are added for every memmap entry. 65 + */ 66 + static struct attribute *def_attrs[] = { 67 + &memmap_start_attr.attr, 68 + &memmap_end_attr.attr, 69 + &memmap_type_attr.attr, 70 + NULL 71 + }; 72 + 73 + static struct sysfs_ops memmap_attr_ops = { 74 + .show = memmap_attr_show, 75 + }; 76 + 77 + static struct kobj_type memmap_ktype = { 78 + .sysfs_ops = &memmap_attr_ops, 79 + .default_attrs = def_attrs, 80 + }; 81 + 82 + /* 83 + * Registration functions ------------------------------------------------------ 84 + */ 85 + 86 + /* 87 + * Firmware memory map entries 88 + */ 89 + static LIST_HEAD(map_entries); 90 + 91 + /** 92 + * Common implementation of firmware_map_add() and firmware_map_add_early() 93 + * which expects a pre-allocated struct firmware_map_entry. 94 + * 95 + * @start: Start of the memory range. 96 + * @end: End of the memory range (inclusive). 97 + * @type: Type of the memory range. 98 + * @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised 99 + * entry. 100 + */ 101 + static int firmware_map_add_entry(resource_size_t start, resource_size_t end, 102 + const char *type, 103 + struct firmware_map_entry *entry) 104 + { 105 + BUG_ON(start > end); 106 + 107 + entry->start = start; 108 + entry->end = end; 109 + entry->type = type; 110 + INIT_LIST_HEAD(&entry->list); 111 + kobject_init(&entry->kobj, &memmap_ktype); 112 + 113 + list_add_tail(&entry->list, &map_entries); 114 + 115 + return 0; 116 + } 117 + 118 + /* 119 + * See <linux/firmware-map.h> for documentation. 120 + */ 121 + int firmware_map_add(resource_size_t start, resource_size_t end, 122 + const char *type) 123 + { 124 + struct firmware_map_entry *entry; 125 + 126 + entry = kmalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); 127 + WARN_ON(!entry); 128 + if (!entry) 129 + return -ENOMEM; 130 + 131 + return firmware_map_add_entry(start, end, type, entry); 132 + } 133 + 134 + /* 135 + * See <linux/firmware-map.h> for documentation. 136 + */ 137 + int __init firmware_map_add_early(resource_size_t start, resource_size_t end, 138 + const char *type) 139 + { 140 + struct firmware_map_entry *entry; 141 + 142 + entry = alloc_bootmem_low(sizeof(struct firmware_map_entry)); 143 + WARN_ON(!entry); 144 + if (!entry) 145 + return -ENOMEM; 146 + 147 + return firmware_map_add_entry(start, end, type, entry); 148 + } 149 + 150 + /* 151 + * Sysfs functions ------------------------------------------------------------- 152 + */ 153 + 154 + static ssize_t start_show(struct firmware_map_entry *entry, char *buf) 155 + { 156 + return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->start); 157 + } 158 + 159 + static ssize_t end_show(struct firmware_map_entry *entry, char *buf) 160 + { 161 + return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->end); 162 + } 163 + 164 + static ssize_t type_show(struct firmware_map_entry *entry, char *buf) 165 + { 166 + return snprintf(buf, PAGE_SIZE, "%s\n", entry->type); 167 + } 168 + 169 + #define to_memmap_attr(_attr) container_of(_attr, struct memmap_attribute, attr) 170 + #define to_memmap_entry(obj) container_of(obj, struct firmware_map_entry, kobj) 171 + 172 + static ssize_t memmap_attr_show(struct kobject *kobj, 173 + struct attribute *attr, char *buf) 174 + { 175 + struct firmware_map_entry *entry = to_memmap_entry(kobj); 176 + struct memmap_attribute *memmap_attr = to_memmap_attr(attr); 177 + 178 + return memmap_attr->show(entry, buf); 179 + } 180 + 181 + /* 182 + * Initialises stuff and adds the entries in the map_entries list to 183 + * sysfs. Important is that firmware_map_add() and firmware_map_add_early() 184 + * must be called before late_initcall. 185 + */ 186 + static int __init memmap_init(void) 187 + { 188 + int i = 0; 189 + struct firmware_map_entry *entry; 190 + struct kset *memmap_kset; 191 + 192 + memmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj); 193 + WARN_ON(!memmap_kset); 194 + if (!memmap_kset) 195 + return -ENOMEM; 196 + 197 + list_for_each_entry(entry, &map_entries, list) { 198 + entry->kobj.kset = memmap_kset; 199 + kobject_add(&entry->kobj, NULL, "%d", i++); 200 + } 201 + 202 + return 0; 203 + } 204 + late_initcall(memmap_init); 205 +
+74
include/linux/firmware-map.h
··· 1 + /* 2 + * include/linux/firmware-map.h: 3 + * Copyright (C) 2008 SUSE LINUX Products GmbH 4 + * by Bernhard Walle <bwalle@suse.de> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License v2.0 as published by 8 + * the Free Software Foundation 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + */ 16 + #ifndef _LINUX_FIRMWARE_MAP_H 17 + #define _LINUX_FIRMWARE_MAP_H 18 + 19 + #include <linux/list.h> 20 + #include <linux/kobject.h> 21 + 22 + /* 23 + * provide a dummy interface if CONFIG_FIRMWARE_MEMMAP is disabled 24 + */ 25 + #ifdef CONFIG_FIRMWARE_MEMMAP 26 + 27 + /** 28 + * Adds a firmware mapping entry. This function uses kmalloc() for memory 29 + * allocation. Use firmware_map_add_early() if you want to use the bootmem 30 + * allocator. 31 + * 32 + * That function must be called before late_initcall. 33 + * 34 + * @start: Start of the memory range. 35 + * @end: End of the memory range (inclusive). 36 + * @type: Type of the memory range. 37 + * 38 + * Returns 0 on success, or -ENOMEM if no memory could be allocated. 39 + */ 40 + int firmware_map_add(resource_size_t start, resource_size_t end, 41 + const char *type); 42 + 43 + /** 44 + * Adds a firmware mapping entry. This function uses the bootmem allocator 45 + * for memory allocation. Use firmware_map_add() if you want to use kmalloc(). 46 + * 47 + * That function must be called before late_initcall. 48 + * 49 + * @start: Start of the memory range. 50 + * @end: End of the memory range (inclusive). 51 + * @type: Type of the memory range. 52 + * 53 + * Returns 0 on success, or -ENOMEM if no memory could be allocated. 54 + */ 55 + int firmware_map_add_early(resource_size_t start, resource_size_t end, 56 + const char *type); 57 + 58 + #else /* CONFIG_FIRMWARE_MEMMAP */ 59 + 60 + static inline int firmware_map_add(resource_size_t start, resource_size_t end, 61 + const char *type) 62 + { 63 + return 0; 64 + } 65 + 66 + static inline int firmware_map_add_early(resource_size_t start, 67 + resource_size_t end, const char *type) 68 + { 69 + return 0; 70 + } 71 + 72 + #endif /* CONFIG_FIRMWARE_MEMMAP */ 73 + 74 + #endif /* _LINUX_FIRMWARE_MAP_H */