]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
x86: make 'mem=' option to work for efi platform
authorWen Congyang <wency@cn.fujitsu.com>
Thu, 15 Nov 2012 02:37:05 +0000 (13:37 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 15 Nov 2012 06:25:24 +0000 (17:25 +1100)
Current mem boot option only can work for non efi environment.  If the
user specifies add_efi_memmap, it cannot work for efi environment.  In the
efi environment, we call e820_add_region() to add the memory map.  So we
can modify __e820_add_region() and the mem boot option can work for efi
environment.

Note: Only E820_RAM is limited, and BOOT_SERVICES_{CODE,DATA} are always
mapped(If its address >= mem_limit, the memory won't be freed in
efi_free_boot_services()).

Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
Cc: Matt Fleming <matt.fleming@intel.com>
Cc: Rob Landley <rob@landley.net>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Yasuaki ISIMATU <isimatu.yasuaki@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
arch/x86/include/asm/e820.h
arch/x86/kernel/e820.c
arch/x86/platform/efi/efi.c

index 37782566af241dca3d6e0c8264935dc3e0a16258..c1f31eead45dc78572cc0825f262b5dd3993def7 100644 (file)
@@ -84,6 +84,8 @@ extern unsigned long pci_mem_start;
 extern int e820_any_mapped(u64 start, u64 end, unsigned type);
 extern int e820_all_mapped(u64 start, u64 end, unsigned type);
 extern void e820_add_region(u64 start, u64 size, int type);
+extern void e820_add_limit_region(u64 start, u64 size, int type);
+extern void e820_adjust_region(u64 *start, u64 *size);
 extern void e820_print_map(char *who);
 extern int
 sanitize_e820_map(struct e820entry *biosmap, int max_nr_map, u32 *pnr_map);
index df06ade26bef8485af1a66d797370385d0012e52..7578c01bad1ad5ffe083265b90d5b9fdf555cfda 100644 (file)
@@ -47,6 +47,7 @@ unsigned long pci_mem_start = 0xaeedbabe;
 #ifdef CONFIG_PCI
 EXPORT_SYMBOL(pci_mem_start);
 #endif
+static u64 mem_limit = ~0ULL;
 
 /*
  * This function checks if any part of the range <start,end> is mapped
@@ -108,7 +109,7 @@ int __init e820_all_mapped(u64 start, u64 end, unsigned type)
  * Add a memory region to the kernel e820 map.
  */
 static void __init __e820_add_region(struct e820map *e820x, u64 start, u64 size,
-                                        int type)
+                                        int type, bool limited)
 {
        int x = e820x->nr_map;
 
@@ -119,6 +120,22 @@ static void __init __e820_add_region(struct e820map *e820x, u64 start, u64 size,
                return;
        }
 
+       if (limited) {
+               if (start >= mem_limit) {
+                       printk(KERN_ERR "e820: ignoring [mem %#010llx-%#010llx]\n",
+                              (unsigned long long)start,
+                              (unsigned long long)(start + size - 1));
+                       return;
+               }
+
+               if (mem_limit - start < size) {
+                       printk(KERN_ERR "e820: ignoring [mem %#010llx-%#010llx]\n",
+                              (unsigned long long)mem_limit,
+                              (unsigned long long)(start + size - 1));
+                       size = mem_limit - start;
+               }
+       }
+
        e820x->map[x].addr = start;
        e820x->map[x].size = size;
        e820x->map[x].type = type;
@@ -127,7 +144,37 @@ static void __init __e820_add_region(struct e820map *e820x, u64 start, u64 size,
 
 void __init e820_add_region(u64 start, u64 size, int type)
 {
-       __e820_add_region(&e820, start, size, type);
+       __e820_add_region(&e820, start, size, type, false);
+}
+
+/*
+ * do_add_efi_memmap() calls this function().
+ *
+ * Note: BOOT_SERVICES_{CODE,DATA} regions on some efi machines are marked
+ * as E820_RAM, and they are needed to be mapped. Please use e820_add_region()
+ * to add BOOT_SERVICES_{CODE,DATA} regions.
+ */
+void __init e820_add_limit_region(u64 start, u64 size, int type)
+{
+       /*
+        * efi_init() is called after finish_e820_parsing(), so we should
+        * check whether [start, start + size) contains address above
+        * mem_limit if the type is E820_RAM.
+        */
+       __e820_add_region(&e820, start, size, type, type == E820_RAM);
+}
+
+void __init e820_adjust_region(u64 *start, u64 *size)
+{
+       if (*start >= mem_limit) {
+               *size = 0;
+               return;
+       }
+
+       if (mem_limit - *start < *size)
+               *size = mem_limit - *start;
+
+       return;
 }
 
 static void __init e820_print_type(u32 type)
@@ -455,8 +502,9 @@ static u64 __init __e820_update_range(struct e820map *e820x, u64 start,
 
                /* new range is totally covered? */
                if (ei->addr < start && ei_end > end) {
-                       __e820_add_region(e820x, start, size, new_type);
-                       __e820_add_region(e820x, end, ei_end - end, ei->type);
+                       __e820_add_region(e820x, start, size, new_type, false);
+                       __e820_add_region(e820x, end, ei_end - end, ei->type,
+                                         false);
                        ei->size = start - ei->addr;
                        real_updated_size += size;
                        continue;
@@ -469,7 +517,7 @@ static u64 __init __e820_update_range(struct e820map *e820x, u64 start,
                        continue;
 
                __e820_add_region(e820x, final_start, final_end - final_start,
-                                 new_type);
+                                 new_type, false);
 
                real_updated_size += final_end - final_start;
 
@@ -809,7 +857,7 @@ static int userdef __initdata;
 /* "mem=nopentium" disables the 4MB page tables. */
 static int __init parse_memopt(char *p)
 {
-       u64 mem_size;
+       char *oldp;
 
        if (!p)
                return -EINVAL;
@@ -825,11 +873,11 @@ static int __init parse_memopt(char *p)
        }
 
        userdef = 1;
-       mem_size = memparse(p, &p);
+       oldp = p;
+       mem_limit = memparse(p, &p);
        /* don't remove all of memory when handling "mem={invalid}" param */
-       if (mem_size == 0)
+       if (mem_limit == 0 || p == oldp)
                return -EINVAL;
-       e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1);
 
        return 0;
 }
@@ -881,6 +929,12 @@ early_param("memmap", parse_memmap_opt);
 
 void __init finish_e820_parsing(void)
 {
+       if (mem_limit != ~0ULL) {
+               userdef = 1;
+               e820_remove_range(mem_limit, ULLONG_MAX - mem_limit,
+                                 E820_RAM, 1);
+       }
+
        if (userdef) {
                u32 nr = e820.nr_map;
 
index ad4439145f858314dfe518cf7cd9336c5fe9c96d..759216373a4327b27f0d5f26b98554c545bf59f3 100644 (file)
@@ -314,10 +314,17 @@ static void __init do_add_efi_memmap(void)
                int e820_type;
 
                switch (md->type) {
-               case EFI_LOADER_CODE:
-               case EFI_LOADER_DATA:
                case EFI_BOOT_SERVICES_CODE:
                case EFI_BOOT_SERVICES_DATA:
+                       /* EFI_BOOT_SERVICES_{CODE,DATA} needs to be mapped */
+                       if (md->attribute & EFI_MEMORY_WB)
+                               e820_type = E820_RAM;
+                       else
+                               e820_type = E820_RESERVED;
+                       e820_add_region(start, size, e820_type);
+                       continue;
+               case EFI_LOADER_CODE:
+               case EFI_LOADER_DATA:
                case EFI_CONVENTIONAL_MEMORY:
                        if (md->attribute & EFI_MEMORY_WB)
                                e820_type = E820_RAM;
@@ -342,7 +349,7 @@ static void __init do_add_efi_memmap(void)
                        e820_type = E820_RESERVED;
                        break;
                }
-               e820_add_region(start, size, e820_type);
+               e820_add_limit_region(start, size, e820_type);
        }
        sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
 }
@@ -448,6 +455,8 @@ void __init efi_free_boot_services(void)
                    md->type != EFI_BOOT_SERVICES_DATA)
                        continue;
 
+               e820_adjust_region(&start, &size);
+
                /* Could not reserve boot area */
                if (!size)
                        continue;