memtest86plus/system/pmem.c
Martin Whitaker 0fded3e321 Store pmem temporary arrays on the stack to reduce memory footprint.
Now we have more stack space available for the BSP during initialisation,
we no longer need to store the temporary data off stack.
2022-01-09 21:27:44 +00:00

265 lines
9.0 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020-2022 Martin Whitaker.
//
// Derived from memtest86+ memsize.c
//
// memsize.c - MemTest-86 Version 3.3
//
// Released under version 2 of the Gnu Public License.
// By Chris Brady
#include <stdbool.h>
#include <stdint.h>
#include "boot.h"
#include "bootparams.h"
#include "memsize.h"
#include "string.h"
#include "pmem.h"
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
// The reserved memory starting at 640KB.
#define RESERVED_MEM_START 0x0a0000
#define RESERVED_MEM_END 0x100000
//------------------------------------------------------------------------------
// Public Variables
//------------------------------------------------------------------------------
pm_map_t pm_map[MAX_MEM_SEGMENTS];
int pm_map_size = 0;
size_t num_pm_pages = 0;
//------------------------------------------------------------------------------
// Private Functions
//------------------------------------------------------------------------------
// Some PC BIOS e820 responses include overlapping entries.
// Here we create a new map with the overlaps removed.
static int sanitize_e820_map(e820_entry_t new_map[], const e820_entry_t orig_map[], int orig_entries)
{
struct change_member {
const e820_entry_t *entry; // pointer to original bios entry
bool start; // true = start addr, false = end addr
uint64_t addr; // address for this change point
};
struct change_member change_point_list[2*E820_MAP_SIZE];
struct change_member *change_point[2*E820_MAP_SIZE];
struct change_member *change_tmp;
const e820_entry_t *overlap_list[E820_MAP_SIZE];
/*
Visually we're performing the following (1,2,3,4 = memory types)...
Sample memory map (w/overlaps):
____22__________________
______________________4_
____1111________________
_44_____________________
11111111________________
____________________33__
___________44___________
__________33333_________
______________22________
___________________2222_
_________111111111______
_____________________11_
_________________4______
Sanitized equivalent (no overlap):
1_______________________
_44_____________________
___1____________________
____22__________________
______11________________
_________1______________
__________3_____________
___________44___________
_____________33_________
_______________2________
________________1_______
_________________4______
___________________2____
____________________33__
______________________4_
*/
// Bail out if we find any unreasonable addresses in the original map.
for (int i = 0; i < orig_entries; i++) {
if (orig_map[i].addr + orig_map[i].size < orig_map[i].addr) {
return 0;
}
}
// Create pointers for initial change-point information (for sorting).
for (int i = 0; i < 2*orig_entries; i++) {
change_point[i] = &change_point_list[i];
}
// Record all known change-points (starting and ending addresses).
int chg_idx = 0;
for (int i = 0; i < orig_entries; i++) {
change_point[chg_idx]->addr = orig_map[i].addr;
change_point[chg_idx]->start = true;
change_point[chg_idx]->entry = &orig_map[i];
chg_idx++;
change_point[chg_idx]->addr = orig_map[i].addr + orig_map[i].size;
change_point[chg_idx]->start = false;
change_point[chg_idx]->entry = &orig_map[i];
chg_idx++;
}
// Sort change-point list by memory addresses (low -> high).
bool still_changing = true;
while (still_changing) {
still_changing = false;
for (int i = 1; i < 2*orig_entries; i++) {
// If current_addr > last_addr or if current_addr = last_addr
// and current is a start addr and last is an end addr, swap.
if ((change_point[i]->addr < change_point[i-1]->addr)
|| ((change_point[i]->addr == change_point[i-1]->addr) && change_point[i]->start && !change_point[i-1]->start)) {
change_tmp = change_point[i];
change_point[i] = change_point[i-1];
change_point[i-1] = change_tmp;
still_changing = true;
}
}
}
// Create a new bios memory map, removing overlaps.
int overlap_entries = 0;
int new_map_entries = 0;
uint64_t last_addr = 0;
uint32_t last_type = E820_NONE;
// Loop through change-points, determining effect on the new map.
for (chg_idx = 0; chg_idx < 2*orig_entries; chg_idx++) {
// Keep track of all overlapping entries.
if (change_point[chg_idx]->start) {
// Add map entry to overlap list (> 1 entry implies an overlap)
overlap_list[overlap_entries++] = change_point[chg_idx]->entry;
} else {
// Remove entry from list (order independent, so swap with last)
for (int i = 0; i < overlap_entries; i++) {
if (overlap_list[i] == change_point[chg_idx]->entry) {
overlap_list[i] = overlap_list[overlap_entries-1];
}
}
overlap_entries--;
}
// If there are overlapping entries, decide which "type" to use
// (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable)
uint32_t current_type = E820_NONE;
for (int i = 0; i < overlap_entries; i++) {
if (overlap_list[i]->type > current_type) {
current_type = overlap_list[i]->type;
}
}
// Continue building up new map based on this information.
if (current_type != last_type) {
if (last_type != E820_NONE) {
new_map[new_map_entries].size = change_point[chg_idx]->addr - last_addr;
// Move forward only if the new size was non-zero
if (new_map[new_map_entries].size != 0) {
if (++new_map_entries >= E820_MAP_SIZE) {
break;
}
}
}
if (current_type != E820_NONE) {
new_map[new_map_entries].addr = change_point[chg_idx]->addr;
new_map[new_map_entries].type = current_type;
last_addr = change_point[chg_idx]->addr;
}
last_type = current_type;
}
}
return new_map_entries;
}
static void init_pm_map(const e820_entry_t e820_map[], int e820_entries)
{
pm_map_size = 0;
for (int i = 0; i < e820_entries; i++) {
if (e820_map[i].type == E820_RAM || e820_map[i].type == E820_ACPI) {
uint64_t start = e820_map[i].addr;
uint64_t end = start + e820_map[i].size;
// Don't ever use memory between 640KB and 1024KB
if (start > RESERVED_MEM_START && start < RESERVED_MEM_END) {
if (end < RESERVED_MEM_END) {
continue;
}
start = RESERVED_MEM_END;
}
if (end > RESERVED_MEM_START && end < RESERVED_MEM_END) {
end = RESERVED_MEM_START;
}
pm_map[pm_map_size].start = (start + PAGE_SIZE - 1) >> PAGE_SHIFT;
pm_map[pm_map_size].end = end >> PAGE_SHIFT;
num_pm_pages += pm_map[pm_map_size].end - pm_map[pm_map_size].start;
if ((pm_map_size > 0) && (pm_map[pm_map_size].start == pm_map[pm_map_size - 1].end)) {
pm_map[pm_map_size - 1].end = pm_map[pm_map_size].end;
} else {
pm_map_size++;
}
}
}
}
static void sort_pm_map(void)
{
// Do an insertion sort on the pm_map. On an already sorted list this should be a O(1) algorithm.
for (int i = 0; i < pm_map_size; i++) {
// Find where to insert the current element.
int j = i - 1;
while (j >= 0) {
if (pm_map[i].start > pm_map[j].start) {
j++;
break;
}
j--;
}
// Insert the current element.
if (i != j) {
pm_map_t temp;
temp = pm_map[i];
memmove(&pm_map[j], &pm_map[j+1], (i - j) * sizeof(temp));
pm_map[j] = temp;
}
}
}
//------------------------------------------------------------------------------
// Public Functions
//------------------------------------------------------------------------------
void pmem_init(void)
{
e820_entry_t sanitized_map[E820_MAP_SIZE];
num_pm_pages = 0;
const boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
int sanitized_entries = sanitize_e820_map(sanitized_map, boot_params->e820_map, boot_params->e820_entries);
init_pm_map(sanitized_map, sanitized_entries);
sort_pm_map();
}