blob: be385b67201b5b19914722f291491a458bbff48b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Allows setting and excluding memory regions that need to be cleared.
*
* Copyright 2018 Google LLC
*/
#define LOG_CATEGORY LOGC_VBOOT
#include <common.h>
#include <inttypes.h>
#include <log.h>
#include <malloc.h>
#include <physmem.h>
#include <cros/cros_common.h>
#include <cros/memwipe.h>
/*
* This implementation tracks regions of memory that need to be wiped by
* filling them with zeroes. It does that by keeping a linked list of the
* edges between regions where memory should be wiped and not wiped. New
* regions take precedence over older regions they overlap with. With
* increasing addresses, the regions of memory alternate between needing to be
* wiped and needing to be left alone. Edges similarly alternate between
* starting a wipe region and starting a not-wiped region.
*/
static void memwipe_insert_between(struct memwipe_edge *before,
struct memwipe_edge *after, phys_addr_t pos)
{
struct memwipe_edge *new_edge = malloc(sizeof(*new_edge));
assert(new_edge);
assert(before != after);
new_edge->next = after;
new_edge->pos = pos;
before->next = new_edge;
}
void memwipe_init(struct memwipe *wipe)
{
wipe->head.next = NULL;
wipe->head.pos = 0;
}
static void memwipe_set_region_to(struct memwipe *wipe_info, phys_addr_t start,
phys_addr_t end, int new_wiped)
{
/* whether the current region was originally going to be wiped */
int wipe = 0;
assert(start != end);
/* prev is never NULL, but cur might be */
struct memwipe_edge *prev = &wipe_info->head;
struct memwipe_edge *cur = prev->next;
/*
* Find the start of the new region. After this loop, prev will be
* before the start of the new region, and cur will be after it or
* overlapping start. If they overlap, this ensures that the existing
* edge is deleted and we don't end up with two edges in the same spot.
*/
while (cur && cur->pos < start) {
prev = cur;
cur = cur->next;
wipe = !wipe;
}
/* Add the 'start' edge between prev and cur, if needed */
if (new_wiped != wipe) {
memwipe_insert_between(prev, cur, start);
prev = prev->next;
}
/*
* Delete any edges obscured by the new region. After this loop, prev
* will be before the end of the new region or overlapping it, and cur
* will be after if, if there is a edge after it. For the same
* reason as above, we want to ensure that we end up with one edge if
* there's an overlap.
*/
while (cur && cur->pos <= end) {
cur = cur->next;
free(prev->next);
prev->next = cur;
wipe = !wipe;
}
/* Add the 'end' edge between prev and cur, if needed */
if (wipe != new_wiped)
memwipe_insert_between(prev, cur, end);
}
/* Set a region to 'wiped' */
void memwipe_add(struct memwipe *wipe, phys_addr_t start, phys_addr_t end)
{
log_debug("start=%" PRIx64 ", end=%" PRIx64 "\n", (u64)start,
(u64)end);
memwipe_set_region_to(wipe, start, end, 1);
}
/* Set a region to 'not wiped' */
void memwipe_sub(struct memwipe *wipe, phys_addr_t start, phys_addr_t end)
{
log_debug("start=%" PRIx64 ", end=%" PRIx64 "\n", (u64)start, (u64)end);
memwipe_set_region_to(wipe, start, end, 0);
}
/* Actually wipe memory */
void memwipe_execute(struct memwipe *wipe)
{
struct memwipe_edge *cur;
log_debug("Wipe memory regions:\n");
for (cur = wipe->head.next; cur; cur = cur->next->next) {
phys_addr_t start, end;
if (!cur->next) {
log_debug("Odd number of region edges!\n");
return;
}
start = cur->pos;
end = cur->next->pos;
log_debug("\t[%#016llx, %#016llx)\n",
(unsigned long long)start, (unsigned long long)end);
arch_phys_memset(start, 0, end - start);
}
}