linux/drivers/firmware/efi/fake_mem.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * fake_mem.c
   4 *
   5 * Copyright (C) 2015 FUJITSU LIMITED
   6 * Author: Taku Izumi <izumi.taku@jp.fujitsu.com>
   7 *
   8 * This code introduces new boot option named "efi_fake_mem"
   9 * By specifying this parameter, you can add arbitrary attribute to
  10 * specific memory range by updating original (firmware provided) EFI
  11 * memmap.
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/efi.h>
  16#include <linux/init.h>
  17#include <linux/memblock.h>
  18#include <linux/types.h>
  19#include <linux/sort.h>
  20#include "fake_mem.h"
  21
  22struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM];
  23int nr_fake_mem;
  24
  25static int __init cmp_fake_mem(const void *x1, const void *x2)
  26{
  27        const struct efi_mem_range *m1 = x1;
  28        const struct efi_mem_range *m2 = x2;
  29
  30        if (m1->range.start < m2->range.start)
  31                return -1;
  32        if (m1->range.start > m2->range.start)
  33                return 1;
  34        return 0;
  35}
  36
  37void __init efi_fake_memmap(void)
  38{
  39        int new_nr_map = efi.memmap.nr_map;
  40        efi_memory_desc_t *md;
  41        phys_addr_t new_memmap_phy;
  42        void *new_memmap;
  43        int i;
  44
  45        if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem)
  46                return;
  47
  48        /* count up the number of EFI memory descriptor */
  49        for (i = 0; i < nr_fake_mem; i++) {
  50                for_each_efi_memory_desc(md) {
  51                        struct range *r = &efi_fake_mems[i].range;
  52
  53                        new_nr_map += efi_memmap_split_count(md, r);
  54                }
  55        }
  56
  57        /* allocate memory for new EFI memmap */
  58        new_memmap_phy = efi_memmap_alloc(new_nr_map);
  59        if (!new_memmap_phy)
  60                return;
  61
  62        /* create new EFI memmap */
  63        new_memmap = early_memremap(new_memmap_phy,
  64                                    efi.memmap.desc_size * new_nr_map);
  65        if (!new_memmap) {
  66                memblock_free(new_memmap_phy, efi.memmap.desc_size * new_nr_map);
  67                return;
  68        }
  69
  70        for (i = 0; i < nr_fake_mem; i++)
  71                efi_memmap_insert(&efi.memmap, new_memmap, &efi_fake_mems[i]);
  72
  73        /* swap into new EFI memmap */
  74        early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map);
  75
  76        efi_memmap_install(new_memmap_phy, new_nr_map);
  77
  78        /* print new EFI memmap */
  79        efi_print_memmap();
  80}
  81
  82static int __init setup_fake_mem(char *p)
  83{
  84        u64 start = 0, mem_size = 0, attribute = 0;
  85        int i;
  86
  87        if (!p)
  88                return -EINVAL;
  89
  90        while (*p != '\0') {
  91                mem_size = memparse(p, &p);
  92                if (*p == '@')
  93                        start = memparse(p+1, &p);
  94                else
  95                        break;
  96
  97                if (*p == ':')
  98                        attribute = simple_strtoull(p+1, &p, 0);
  99                else
 100                        break;
 101
 102                if (nr_fake_mem >= EFI_MAX_FAKEMEM)
 103                        break;
 104
 105                efi_fake_mems[nr_fake_mem].range.start = start;
 106                efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1;
 107                efi_fake_mems[nr_fake_mem].attribute = attribute;
 108                nr_fake_mem++;
 109
 110                if (*p == ',')
 111                        p++;
 112        }
 113
 114        sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
 115             cmp_fake_mem, NULL);
 116
 117        for (i = 0; i < nr_fake_mem; i++)
 118                pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
 119                        efi_fake_mems[i].attribute, efi_fake_mems[i].range.start,
 120                        efi_fake_mems[i].range.end);
 121
 122        return *p == '\0' ? 0 : -EINVAL;
 123}
 124
 125early_param("efi_fake_mem", setup_fake_mem);
 126