summaryrefslogtreecommitdiffstats
path: root/tboot/efimemmap.c
blob: a9ff517ef3be8b6db6077fec7748ac5d86581632 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*
 * efimemmap.c: EFI memory map processing and storage.
 *
 * Copyright (c) 2017 Assured Information Security.
 *
 * Ross Philipson <philipsonr@ainfosec.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <config.h>
#include <efibase.h>
#include <stdbool.h>
#include <string.h>
#include <atomic.h>
#include <page.h>
#include <printk.h>
#include <eficore.h>
#include <eficonfig.h>
#include <pe.h>
#include <tb_error.h>
#include <tboot.h>
#include <txt/txt.h>

#define MAX_RAMMEM_BLOCKS 128
#define MEM_NONE  0
#define MEM_RAM   1
#define MEM_OTHER 2

typedef struct mem_map {
    uint64_t addr;
    uint64_t length;
    uint64_t type;
} mem_map_t;

static __data mem_map_t g_ram_map[MAX_RAMMEM_BLOCKS];
static __data uint64_t g_ram_map_count = 0;

/* minimum size of RAM (type 1) region that cannot be marked as reserved even
   if it comes after a reserved region; 0 for no minimum (i.e. current
   behavior) */
/* TODO this needs some evaluation - preserve for now. If using this causes the
 * PMRs to not cover the MLE then game over. See the reloc code in boot.c.
 */
uint32_t g_min_ram = 0;

/*
 * All we really care about are conventional RAM regions. This will include
 * coalesced RAM, EFI loader and boot services memory.
 */
bool efi_scan_memory_map(void)
{
    const EFI_MEMORY_DESCRIPTOR *desc;
    const void *memory_map;
    uint64_t size, size_desc, length, type, last = MEM_NONE, i;
    mem_map_t *entry = g_ram_map - 1;

    memory_map = efi_get_memory_map(&size, &size_desc);
    if (!memory_map || size == 0 ||
         size_desc < sizeof(EFI_MEMORY_DESCRIPTOR)) {
        printk(TBOOT_ERR"System memory map invalid?!\n");
        return false;
    }

    memset(g_ram_map, 0, sizeof(mem_map_t)*MAX_RAMMEM_BLOCKS);

    printk(TBOOT_DETA"EFI memory map:\n");
    for (i = 0; i < size; i += size_desc) {
        desc = memory_map + i;
        length = desc->NumberOfPages << EFI_PAGE_SHIFT;

        printk(TBOOT_DETA" %016llx - %016llx type=%u attr=%016llx\n",
               desc->PhysicalStart, desc->PhysicalStart + length - 1,
               desc->Type, desc->Attribute);
        switch (desc->Type)
        {
        case EfiBootServicesCode:
        case EfiBootServicesData:
        case EfiLoaderCode:
        case EfiLoaderData:
        case EfiConventionalMemory:
            if (desc->Attribute & EFI_MEMORY_WB)
                type = MEM_RAM;
            else
                type = MEM_OTHER;
            break;
        default:
            type = MEM_OTHER;
        };

        if (g_ram_map_count >= MAX_RAMMEM_BLOCKS) {
            printk(TBOOT_ERR"Exhausted RAM memory blocks\n");
            return false;
        }

        if (g_ram_map_count && type == last &&
             desc->PhysicalStart == entry->addr + entry->length) {
            entry->length += length;
        }
        else {
            ++entry;
            entry->addr = desc->PhysicalStart;
            entry->length = length;
            entry->type = type;
            ++g_ram_map_count;
        }

        last = type;
    }

    entry = g_ram_map;
    printk(TBOOT_DETA"RAM map:\n");
    for (i = 0; i < g_ram_map_count; i++, entry++) {
        printk(TBOOT_DETA" %016llx - %016llx  type: %s\n",
               entry->addr, entry->addr + entry->length,
               (entry->type == MEM_RAM ? "RAM" : "OTHER"));
    }

    return true;
}

bool efi_add_resmap_entry(uint64_t addr, uint64_t length)
{
    /*
     * TODO Xen will have to sort things out like e820_reserve_ram in its
     * copy of the E820 it gives to dom0. We don't do E820 around these parts.
     */

    if (_tboot_shared.reserve_map_count >= TB_RESMEM_BLOCKS) {
        printk(TBOOT_ERR"Exhausted RES memory blocks\n");
        return false;
    }

    _tboot_shared.reserve_map[++_tboot_shared.reserve_map_count].addr = addr;
    _tboot_shared.reserve_map[_tboot_shared.reserve_map_count].length = length;

    return true;
}

/* find highest (< <limit>) RAM region of at least <size> bytes */
static void get_highest_sized_ram(uint64_t size, uint64_t limit,
                                  uint64_t *ram_base, uint64_t *ram_size)
{
    uint64_t last_fit_base = 0, last_fit_size = 0, i;
    mem_map_t *entry = g_ram_map;

    if ( ram_base == NULL || ram_size == NULL )
        return;

    for ( i = 0; i < g_ram_map_count; i++, entry++ ) {
        /* over 4GB so use the last region that fit */
        if ( entry->addr + entry->length > limit )
            break;
        if ( size <= entry->length ) {
            last_fit_base = entry->addr;
            last_fit_size = entry->length;
        }
    }

    *ram_base = last_fit_base;
    *ram_size = last_fit_size;
}

bool efi_get_ram_ranges(uint64_t *min_lo_ram, uint64_t *max_lo_ram,
                        uint64_t *min_hi_ram, uint64_t *max_hi_ram)
{
    bool found_reserved_region = false;
    uint64_t last_min_ram_base = 0, last_min_ram_size = 0, i;
    mem_map_t *entry = g_ram_map;

    if ( min_lo_ram == NULL || max_lo_ram == NULL ||
         min_hi_ram == NULL || max_hi_ram == NULL )
        return false;

    *min_lo_ram = *min_hi_ram = ~0ULL;
    *max_lo_ram = *max_hi_ram = 0;

    /* 
     * if g_min_ram > 0, we will never mark a region > g_min_ram in size
     * as reserved even if it is after a reserved region (effectively
     * we ignore reserved regions below the last type 1 region
     * > g_min_ram in size)
     * so in order to reserve RAM regions above this last region, we need
     * to find it first so that we can tell when we have passed it
     */
    if ( g_min_ram > 0 ) {
        get_highest_sized_ram(g_min_ram, 0x100000000ULL, &last_min_ram_base,
                              &last_min_ram_size);
        printk(TBOOT_DETA"highest min_ram (0x%x) region found: base=0x%Lx, size=0x%Lx\n",
               g_min_ram, last_min_ram_base, last_min_ram_size);
    }

    for ( i = 0; i < g_ram_map_count; i++, entry++ ) {
        uint64_t base = entry->addr;
        uint64_t limit = entry->addr + entry->length;

        if ( entry->type == MEM_RAM ) {
            /* if range straddles 4GB boundary, that is an error */
            if ( base < 0x100000000ULL && limit > 0x100000000ULL ) {
                printk(TBOOT_ERR"e820 memory range straddles 4GB boundary\n");
                return false;
            }

            /*
             * some BIOSes put legacy USB buffers in reserved regions <4GB,
             * which if DMA protected cause SMM to hang, so make sure that
             * we don't overlap any of these even if that wastes RAM
             * ...unless min_ram was specified
             */
            if ( !found_reserved_region || base <= last_min_ram_base ) {
                if ( base < 0x100000000ULL && base < *min_lo_ram )
                    *min_lo_ram = base;
                if ( limit <= 0x100000000ULL && limit > *max_lo_ram )
                    *max_lo_ram = limit;
            }
            else {     /* need to reserve low RAM above reserved regions */
                if ( base < 0x100000000ULL ) {
                    if (txt_is_launched()) {
                        printk(TBOOT_DETA"discarding RAM above reserved"
                               "regions: 0x%Lx - 0x%Lx\n", base, limit);
                        if ( !efi_add_resmap_entry(base, limit - base) )
                            return false;
                    }
                }
            }

            if ( base >= 0x100000000ULL && base < *min_hi_ram )
                *min_hi_ram = base;
            if ( limit > 0x100000000ULL && limit > *max_hi_ram )
                *max_hi_ram = limit;
        }
        else {
            /* parts of low memory may be reserved for cseg, ISA hole,
               etc. but these seem OK to DMA protect, so ignore reserved
               regions <0x100000 */
            if ( *min_lo_ram != ~0ULL && limit > 0x100000ULL )
                found_reserved_region = true;
        }
    }

    /* no low RAM found */
    if ( *min_lo_ram >= *max_lo_ram ) {
        printk(TBOOT_ERR"no low ram in e820 map\n");
        return false;
    }
    /* no high RAM found */
    if ( *min_hi_ram >= *max_hi_ram )
        *min_hi_ram = *max_hi_ram = 0;

    return true;
}