mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-30 11:03:48 -06:00
222 lines
6.3 KiB
C
222 lines
6.3 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
|
|
|
|
// 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 pattern[MAX_PATTERNS];
|
|
static int num_patterns = 0;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
#define COMBINE_MASK(a,b,c,d) ((a & b & c & d) | (~a & b & ~c & d))
|
|
|
|
/*
|
|
* 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, no fundamental need for this
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* Find the cheapest array index to extend with the given addr/mask pair.
|
|
* Return -1 if nothing below the given minimum cost can be found.
|
|
*/
|
|
static int cheap_index(uintptr_t addr1, uintptr_t mask1, uintptr_t min_cost)
|
|
{
|
|
int i = num_patterns;
|
|
int idx = -1;
|
|
while (i-- > 0) {
|
|
uintptr_t tmp_cost = combi_cost(pattern[i].addr, pattern[i].mask, addr1, mask1);
|
|
if (tmp_cost < min_cost) {
|
|
min_cost = tmp_cost;
|
|
idx = i;
|
|
}
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
/*
|
|
* Try to find a relocation index for idx if it costs nothing.
|
|
* Return -1 if no such index exists.
|
|
*/
|
|
static int relocate_index(int idx)
|
|
{
|
|
uintptr_t addr = pattern[idx].addr;
|
|
uintptr_t mask = pattern[idx].mask;
|
|
pattern[idx].addr = ~pattern[idx].addr; // Never select idx
|
|
int new = cheap_index(addr, mask, 1 + addresses(mask));
|
|
pattern[idx].addr = addr;
|
|
return new;
|
|
}
|
|
|
|
/*
|
|
* Relocate the given index idx only if free of charge.
|
|
* This is useful to combine to `neighbouring' sections to integrate.
|
|
* Inspired on the Buddy memalloc principle in the Linux kernel.
|
|
*/
|
|
static void relocate_if_free(int idx)
|
|
{
|
|
int newidx = relocate_index(idx);
|
|
if (newidx >= 0) {
|
|
uintptr_t caddr, cmask;
|
|
combine(pattern[newidx].addr, pattern[newidx].mask,
|
|
pattern[ idx].addr, pattern[ idx].mask,
|
|
&caddr, &cmask);
|
|
pattern[newidx].addr = caddr;
|
|
pattern[newidx].mask = cmask;
|
|
if (idx < --num_patterns) {
|
|
pattern[idx].addr = pattern[num_patterns].addr;
|
|
pattern[idx].mask = pattern[num_patterns].mask;
|
|
}
|
|
relocate_if_free (newidx);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
void badram_init(void)
|
|
{
|
|
num_patterns = 0;
|
|
}
|
|
|
|
bool badram_insert(uintptr_t addr)
|
|
{
|
|
if (cheap_index(addr, DEFAULT_MASK, 1) != -1) {
|
|
return false;
|
|
}
|
|
|
|
if (num_patterns < MAX_PATTERNS) {
|
|
pattern[num_patterns].addr = addr;
|
|
pattern[num_patterns].mask = DEFAULT_MASK;
|
|
num_patterns++;
|
|
relocate_if_free(num_patterns - 1);
|
|
} else {
|
|
int idx = cheap_index(addr, DEFAULT_MASK, UINTPTR_MAX);
|
|
uintptr_t caddr, cmask;
|
|
combine(pattern[idx].addr, pattern[idx].mask, addr, DEFAULT_MASK, &caddr, &cmask);
|
|
pattern[idx].addr = caddr;
|
|
pattern[idx].mask = cmask;
|
|
relocate_if_free(idx);
|
|
}
|
|
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, pattern[i].addr,
|
|
TESTWORD_DIGITS, pattern[i].mask);
|
|
col += text_width;
|
|
}
|
|
}
|