mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2025-02-25 18:55:23 -06:00
* BadRAM: Rename pattern -> patterns * BadRAM: Refactor COMBINE_MASK and add clarifying comment * BadRAM: Extract DEFAULT_MASK into variable * BadRAM: Add is_covered() for checking if pattern is already covered by one of the existing patterns * BadRAM: Initialize patterns to 0 * BadRAM: Change how addr/masks are merged to minimize number of addresses covered by badram Prior to this patch, a list of up to MAX_PATTERNS (=10) addr/mask tuples (aka. pattern) were maintained, adding failing addresses one by one to the list until it was full. When full, space was created by forcing a merge of the new address with the existing pattern that would grow the least (with regards to number of addresses covered by the pattern) by merging it with the new address. This can lead to a great imbalance in the number of addresses covered by the patterns. Consider the following: MAX_PATTERNS=4 (for illustrative purposes). The following addresses are faulted and added to patterns: 0x00, 0x10, 0x20, 0x68, 0xa0, 0xb0, 0xc0, 0xd0 This is the end result with the implementation prior to this commit: patterns = [ (0x00, 0xe8), (0x00, 0x18), (0x68, 0xf8), (0x90, 0x98) ] Total addresses covered: 120. This commit changes how the merges are done, not only considering a merge between the new address and existing patterns, but also between existing patterns. It keeps the patterns in ascending order (by .addr) in patterns, and a new address is always inserted into patterns (even if num_patterns == MAX_PATTERNS, patterns is of MAX_PATTERNS+1 size). Then, if num_patterns > MAX_PATTERNS, we find the pair of patterns (only considering neighbours, assuming for any pattern i, i-1 or i+1 will be the best candidate for a merge) that would be the cheapest to merge (using the same metric as prior to this patch), and merge those. With this commit, this is the result of the exact same sequence of addresses as above: [ (0x00, 0xe0), (0x68, 0xf8), (0xa0, 0xe8), (0xc0, 0xe8) ] Total addresses covered: 72. A drawback of the current implementation (as compared to the prior) is that it does not make any attempt at merging patterns until num_patterns == MAX_PATTERNS, which can lead to having several patterns that could've been merged into one at no additional cost. I.e.: patterns = [ (0x00, 0xf8), (0x08, 0xf8) ] can appear, even if patterns = [ (0x00, 0xf0) ] represents the exact same addresses with one pattern instead of two. * fixup! BadRAM: Change how addr/masks are merged to minimize number of addresses covered by badram Co-authored-by: Anders Wenhaug <anders.wenhaug@solutionseeker.no>
291 lines
8.1 KiB
C
291 lines
8.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2020 Martin Whitaker.
|
|
//
|
|
// Derived from memtest86+ patn.c:
|
|
//
|
|
// MemTest86+ V1.60 Specific code (GPL V2.0)
|
|
// By Samuel DEMEULEMEESTER, sdemeule@memtest.org
|
|
// http://www.x86-secret.com - http://www.memtest.org
|
|
// ----------------------------------------------------
|
|
// Pattern extension for memtest86
|
|
//
|
|
// Generates patterns for the Linux kernel's BadRAM extension that avoids
|
|
// allocation of faulty pages.
|
|
//
|
|
// Released under version 2 of the Gnu Public License.
|
|
//
|
|
// By Rick van Rein, vanrein@zonnet.nl
|
|
//
|
|
// What it does:
|
|
// - Keep track of a number of BadRAM patterns in an array;
|
|
// - Combine new faulty addresses with it whenever possible;
|
|
// - Keep masks as selective as possible by minimising resulting faults;
|
|
// - Print a new pattern only when the pattern array is changed.
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "display.h"
|
|
|
|
#include "badram.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Constants
|
|
//------------------------------------------------------------------------------
|
|
|
|
#define MAX_PATTERNS 10
|
|
#define PATTERNS_SIZE (MAX_PATTERNS + 1)
|
|
|
|
// DEFAULT_MASK covers a uintptr_t, since that is the testing granularity.
|
|
#ifdef __x86_64__
|
|
#define DEFAULT_MASK (UINTPTR_MAX << 3)
|
|
#else
|
|
#define DEFAULT_MASK (UINTPTR_MAX << 2)
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Types
|
|
//------------------------------------------------------------------------------
|
|
|
|
typedef struct {
|
|
uintptr_t addr;
|
|
uintptr_t mask;
|
|
} pattern_t;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Variables
|
|
//------------------------------------------------------------------------------
|
|
|
|
static pattern_t patterns[PATTERNS_SIZE];
|
|
static int num_patterns = 0;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
// New mask is 1 where both masks were 1 (b & d) and the addresses were equal ~(a ^ c).
|
|
// If addresses were unequal the new mask must be 0 to allow for both values.
|
|
#define COMBINE_MASK(a,b,c,d) ((b & d) & ~(a ^ c))
|
|
|
|
/*
|
|
* Combine two addr/mask pairs to one addr/mask pair.
|
|
*/
|
|
static void combine(uintptr_t addr1, uintptr_t mask1, uintptr_t addr2, uintptr_t mask2, uintptr_t *addr, uintptr_t *mask)
|
|
{
|
|
*mask = COMBINE_MASK(addr1, mask1, addr2, mask2);
|
|
|
|
*addr = addr1 | addr2;
|
|
*addr &= *mask; // Normalise to ensure sorting on .addr will work as intended
|
|
}
|
|
|
|
/*
|
|
* Count the number of addresses covered with a mask.
|
|
*/
|
|
static uintptr_t addresses(uintptr_t mask)
|
|
{
|
|
uintptr_t ctr = 1;
|
|
int i = 8*sizeof(uintptr_t);
|
|
while (i-- > 0) {
|
|
if (! (mask & 1)) {
|
|
ctr += ctr;
|
|
}
|
|
mask >>= 1;
|
|
}
|
|
return ctr;
|
|
}
|
|
|
|
/*
|
|
* Count how many more addresses would be covered by addr1/mask1 when combined
|
|
* with addr2/mask2.
|
|
*/
|
|
static uintptr_t combi_cost(uintptr_t addr1, uintptr_t mask1, uintptr_t addr2, uintptr_t mask2)
|
|
{
|
|
uintptr_t cost1 = addresses(mask1);
|
|
uintptr_t tmp, mask;
|
|
combine(addr1, mask1, addr2, mask2, &tmp, &mask);
|
|
return addresses(mask) - cost1;
|
|
}
|
|
|
|
/*
|
|
* Determine if pattern is already covered by an existing pattern.
|
|
* Return true if that's the case, else false.
|
|
*/
|
|
static bool is_covered(pattern_t pattern)
|
|
{
|
|
for (int i = 0; i < num_patterns; i++) {
|
|
if (combi_cost(patterns[i].addr, patterns[i].mask, pattern.addr, pattern.mask) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Find the pair of entries that would be the cheapest to merge.
|
|
* Assumes patterns is sorted by .addr asc and that for each index i, the cheapest entry to merge with is at i-1 or i+1.
|
|
* Return -1 if <= 1 patterns exist, else the index of the first entry of the pair (the other being that + 1).
|
|
*/
|
|
static int cheapest_pair()
|
|
{
|
|
// This is guaranteed to be overwritten with >= 0 as long as num_patterns > 1
|
|
int merge_idx = -1;
|
|
|
|
uintptr_t min_cost = UINTPTR_MAX;
|
|
for (int i = 0; i < num_patterns - 1; i++) {
|
|
uintptr_t tmp_cost = combi_cost(
|
|
patterns[i].addr,
|
|
patterns[i].mask,
|
|
patterns[i+1].addr,
|
|
patterns[i+1].mask
|
|
);
|
|
if (tmp_cost <= min_cost) {
|
|
min_cost = tmp_cost;
|
|
merge_idx = i;
|
|
}
|
|
}
|
|
return merge_idx;
|
|
}
|
|
|
|
/*
|
|
* Remove entries at idx and idx+1.
|
|
*/
|
|
static void remove_pair(int idx)
|
|
{
|
|
for (int i = idx; i < num_patterns - 2; i++) {
|
|
patterns[i] = patterns[i + 2];
|
|
}
|
|
patterns[num_patterns - 1].addr = 0u;
|
|
patterns[num_patterns - 1].mask = 0u;
|
|
patterns[num_patterns - 2].addr = 0u;
|
|
patterns[num_patterns - 2].mask = 0u;
|
|
num_patterns -= 2;
|
|
}
|
|
|
|
/*
|
|
* Get the combined entry of idx1 and idx2.
|
|
*/
|
|
static pattern_t combined_pattern(int idx1, int idx2)
|
|
{
|
|
pattern_t combined;
|
|
combine(
|
|
patterns[idx1].addr,
|
|
patterns[idx1].mask,
|
|
patterns[idx2].addr,
|
|
patterns[idx2].mask,
|
|
&combined.addr,
|
|
&combined.mask
|
|
);
|
|
return combined;
|
|
}
|
|
|
|
/*
|
|
* Insert pattern at index idx, shuffling other entries on index towards the end.
|
|
*/
|
|
static void insert_at(pattern_t pattern, int idx)
|
|
{
|
|
// Move all entries >= idx one index towards the end to make space for the new entry
|
|
for (int i = num_patterns - 1; i >= idx; i--) {
|
|
patterns[i + 1] = patterns[i];
|
|
}
|
|
|
|
patterns[idx] = pattern;
|
|
num_patterns++;
|
|
}
|
|
|
|
/*
|
|
* Insert entry (addr, mask) in patterns in an index i so that patterns[i-1].addr < patterns[i]
|
|
* NOTE: Assumes patterns is already sorted by .addr asc!
|
|
*/
|
|
static void insert_sorted(pattern_t pattern)
|
|
{
|
|
// Normalise to ensure sorting on .addr will work as intended
|
|
pattern.addr &= pattern.mask;
|
|
|
|
// Find index to insert entry into
|
|
int new_idx = num_patterns;
|
|
for (int i = 0; i < num_patterns; i++) {
|
|
if (pattern.addr < patterns[i].addr) {
|
|
new_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
insert_at(pattern, new_idx);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
void badram_init(void)
|
|
{
|
|
num_patterns = 0;
|
|
|
|
for (int idx = 0; idx < PATTERNS_SIZE; idx++) {
|
|
patterns[idx].addr = 0u;
|
|
patterns[idx].mask = 0u;
|
|
}
|
|
}
|
|
|
|
bool badram_insert(uintptr_t addr)
|
|
{
|
|
pattern_t pattern = {
|
|
.addr = addr,
|
|
.mask = DEFAULT_MASK
|
|
};
|
|
|
|
// If covered by existing entry we return immediately
|
|
if (is_covered(pattern)) {
|
|
return false;
|
|
}
|
|
|
|
// Add entry in order sorted by .addr asc
|
|
insert_sorted(pattern);
|
|
|
|
// If we have more patterns than the max we need to force a merge
|
|
if (num_patterns > MAX_PATTERNS) {
|
|
// Find the pair that is the cheapest to merge
|
|
// merge_idx will be -1 if num_patterns < 2, but that means MAX_PATTERNS = 0 which is not a valid state anyway
|
|
int merge_idx = cheapest_pair();
|
|
|
|
pattern_t combined = combined_pattern(merge_idx, merge_idx + 1);
|
|
|
|
// Remove the source pair so that we can maintain order as combined does not necessarily belong in merge_idx
|
|
remove_pair(merge_idx);
|
|
|
|
insert_sorted(combined);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void badram_display(void)
|
|
{
|
|
if (num_patterns == 0) {
|
|
return;
|
|
}
|
|
|
|
check_input();
|
|
|
|
clear_message_area();
|
|
display_pinned_message(0, 0, "BadRAM Patterns");
|
|
display_pinned_message(1, 0, "---------------");
|
|
scroll();
|
|
display_scrolled_message(0, "badram=");
|
|
int col = 7;
|
|
for (int i = 0; i < num_patterns; i++) {
|
|
if (i > 0) {
|
|
display_scrolled_message(col, ",");
|
|
col++;
|
|
}
|
|
int text_width = 2 * (TESTWORD_DIGITS + 2) + 1;
|
|
if (col > (SCREEN_WIDTH - text_width)) {
|
|
scroll();
|
|
col = 7;
|
|
}
|
|
display_scrolled_message(col, "0x%0*x,0x%0*x",
|
|
TESTWORD_DIGITS, patterns[i].addr,
|
|
TESTWORD_DIGITS, patterns[i].mask);
|
|
col += text_width;
|
|
}
|
|
}
|