Files
memtest86plus/app/config.c
martinwhitaker 9ef7eae74e Improve BadRAM pattern collection and add more condensed error reporting modes (#454)
* badram: Make DEFAULT_MASK depend on ARCH_BITS, not x86_64 architecture.

* badram: Don't display leading zeros for BadRAM addresses.

* badram: Merge patterns immediately when there is no additional cost.

The current algorithm only starts merging patterns when num_patterns
equals MAX_PATTERNS, which can lead to having several patterns that
could have been merged into one at no additional cost. e.g.

patterns = [
  (0x00, 0xfffffffffffffff8),
  (0x08, 0xfffffffffffffff8)
]

can appear, even though

patterns = [
  (0x00, 0xfffffffffffffff0)
]

represents the exact same addresses at the same cost.

So, at the same time as we test whether an address is already covered,
also check if we can cheaply combine the new address with an existing
pattern.

* badram: Factor out code that wraps to a new line when necessary.

In preparation for next commit.

* badram: Add more modes for recording/displaying error maps.

The original code just supported recording and displaying patterns suitable
for use with the Linux BadRAM extension or GRUB badram command. With some
minor changes it can be made to record address ranges and display them
either in the format used by the Linux memmap boot command line option
or as a simple list of memory page numbers (which can be used with the
Windows bcdedit program).

* badram: Increase the number of stored patterns/ranges.

* Update README to include the new error reporting modes.
2024-11-11 23:57:21 +01:00

1007 lines
29 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020-2022 Martin Whitaker.
// Copyright (C) 2004-2022 Sam Demeulemeester.
//
// Derived from memtest86+ config.c:
//
// ----------------------------------------------------
// config.c - MemTest-86 Version 3.4
//
// 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 "cpuinfo.h"
#include "cpuid.h"
#include "hwctrl.h"
#include "keyboard.h"
#include "memsize.h"
#include "pmem.h"
#include "serial.h"
#include "screen.h"
#include "smp.h"
#include "usbhcd.h"
#include "vmem.h"
#include "read.h"
#include "print.h"
#include "string.h"
#include "unistd.h"
#include "display.h"
#include "test.h"
#include "tests.h"
#include "config.h"
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
// Origin and size of the pop-up window.
#define POP_R 3
#define POP_C 21
#define POP_W 38
#define POP_H 18
#define POP_LAST_R (POP_R + POP_H - 1)
#define POP_LAST_C (POP_C + POP_W - 1)
#define POP_REGION POP_R, POP_C, POP_LAST_R, POP_LAST_C
#define POP_LM (POP_C + 3) // Left margin
#define POP_LI (POP_C + 5) // List indent
#define SEL_W 32
#define SEL_H 2
#define SEL_AREA (SEL_W * SEL_H)
//------------------------------------------------------------------------------
// Private Variables
//------------------------------------------------------------------------------
static uint16_t popup_save_buffer[POP_W * POP_H];
//------------------------------------------------------------------------------
// Public Variables
//------------------------------------------------------------------------------
uintptr_t pm_limit_lower = 0;
uintptr_t pm_limit_upper = 0;
uintptr_t num_pages_to_test = 0;
cpu_mode_t cpu_mode = PAR;
error_mode_t error_mode = ERROR_MODE_NONE;
cpu_state_t cpu_state[MAX_CPUS];
core_type_t hybrid_core_type[MAX_CPUS];
bool exclude_ecores = true;
bool smp_enabled = true;
bool enable_big_status = true;
bool enable_temperature = true;
bool enable_trace = false;
bool enable_sm = true;
bool enable_bench = true;
bool enable_mch_read = true;
bool enable_numa = false;
bool enable_ecc_polling = false;
bool pause_at_start = true;
power_save_t power_save = POWER_SAVE_HIGH;
bool enable_tty = false;
uintptr_t tty_address = 0x3F8; // Legacy IO or MMIO Address accepted
int tty_baud_rate = 115200;
int tty_update_period = 2; // Update TTY every 2 seconds (default)
uint32_t tty_mmio_ref_clk = UART_REF_CLK_MMIO; // Reference clock for MMIO (in Hz)
int tty_mmio_stride = 4; // Stride for MMIO (register width in bytes)
bool err_banner_redraw = false; // Redraw banner on new errors
//------------------------------------------------------------------------------
// Private Functions
//------------------------------------------------------------------------------
static void parse_serial_params(const char *params)
{
enable_tty = true;
// No parameters passed (only "console"), use default
if (params == NULL) {
return;
}
// Check if console is MMIO and grab address and stride
uintptr_t mmio_adr = 0;
if (strncmp(params, "mmio,0x", 7) == 0) {
mmio_adr = hexstr2int(params+7);
tty_mmio_stride = 1;
} else if (strncmp(params, "mmio16,0x", 9) == 0) {
mmio_adr = hexstr2int(params+9);
tty_mmio_stride = 2;
} else if (strncmp(params, "mmio32,0x", 9) == 0) {
mmio_adr = hexstr2int(params+9);
tty_mmio_stride = 4;
}
if(strncmp(params, "mmio", 4) == 0) {
if (mmio_adr > 0xFFFF) {
tty_address = mmio_adr;
} else {
enable_tty = false;
}
return;
}
// No TTY port passed, use default ttyS0
if (strncmp(params, "ttyS", 5) == 0) {
return;
}
// Configure TTY port or use default
if (params[4] >= '0' && params[4] <= '3') {
tty_address = serial_io_ports[params[4] - '0'];
} else {
return;
}
// No Baud Rate specified, use default
if (params[5] != ',' || params[6] == '\0') {
return;
}
switch (params[6])
{
default:
return;
case '1':
tty_baud_rate = (params[7] == '9') ? 19200 : 115200;
tty_update_period = (params[7] == '9') ? 4 : 2;
break;
case '2':
tty_baud_rate = 230400;
tty_update_period = 2;
break;
case '3':
tty_baud_rate = 38400;
tty_update_period = 4;
break;
case '5':
tty_baud_rate = 57600;
tty_update_period = 3;
break;
case '7':
tty_baud_rate = 76800;
tty_update_period = 3;
break;
case '9':
tty_baud_rate = 9600;
tty_update_period = 5;
break;
}
}
static void parse_option(const char *option, const char *params)
{
if (option[0] == '\0') return;
if (strncmp(option, "console", 8) == 0) {
parse_serial_params(params);
} else if (strncmp(option, "cpuseqmode", 11) == 0) {
if (strncmp(params, "par", 4) == 0) {
cpu_mode = PAR;
} else if (strncmp(params, "seq", 4) == 0) {
cpu_mode = SEQ;
} else if (strncmp(params, "rr", 3) == 0 || strncmp(params, "one", 4) == 0) {
cpu_mode = ONE;
}
} else if (strncmp(option, "reportmode", 11) == 0) {
if (strncmp(params, "none", 5) == 0) {
error_mode = ERROR_MODE_NONE;
} else if (strncmp(params, "summary", 8) == 0) {
error_mode = ERROR_MODE_SUMMARY;
} else if (strncmp(params, "address", 8) == 0) {
error_mode = ERROR_MODE_ADDRESS;
} else if (strncmp(params, "badram", 7) == 0) {
error_mode = ERROR_MODE_BADRAM;
} else if (strncmp(params, "memmap", 7) == 0) {
error_mode = ERROR_MODE_MEMMAP;
} else if (strncmp(params, "pages", 6) == 0) {
error_mode = ERROR_MODE_PAGES;
}
} else if (strncmp(option, "keyboard", 9) == 0 && params != NULL) {
if (strncmp(params, "legacy", 7) == 0) {
keyboard_types = KT_LEGACY;
} else if (strncmp(params, "usb", 4) == 0) {
keyboard_types = KT_USB;
} else if (strncmp(params, "both", 5) == 0) {
keyboard_types = KT_USB|KT_LEGACY;
}
} else if (strncmp(option, "nobench", 8) == 0) {
enable_bench = false;
} else if (strncmp(option, "nobigstatus", 12) == 0) {
enable_big_status = false;
} else if (strncmp(option, "noehci", 7) == 0) {
usb_init_options |= USB_IGNORE_EHCI;
} else if (strncmp(option, "nomch", 6) == 0) {
enable_mch_read = false;
} else if (strncmp(option, "nopause", 8) == 0) {
pause_at_start = false;
} else if (strncmp(option, "nosm", 5) == 0) {
enable_sm = false;
} else if (strncmp(option, "nosmp", 6) == 0) {
smp_enabled = false;
} else if (strncmp(option, "numa", 5) == 0) {
enable_numa = true;
} else if (strncmp(option, "nonuma", 7) == 0) {
enable_numa = false;
} else if (strncmp(option, "powersave", 10) == 0) {
if (strncmp(params, "off", 4) == 0) {
power_save = POWER_SAVE_OFF;
} else if (strncmp(params, "low", 4) == 0) {
power_save = POWER_SAVE_LOW;
} else if (strncmp(params, "high", 5) == 0) {
power_save = POWER_SAVE_HIGH;
}
} else if (strncmp(option, "trace", 6) == 0) {
enable_trace = true;
} else if (strncmp(option, "usbdebug", 9) == 0) {
usb_init_options |= USB_DEBUG;
} else if (strncmp(option, "usbinit", 8) == 0) {
if (strncmp(params, "1", 2) == 0) {
usb_init_options |= USB_2_STEP_INIT;
} else if (strncmp(params, "2", 2) == 0) {
usb_init_options |= USB_EXTRA_RESET;
} else if (strncmp(params, "3", 2) == 0) {
usb_init_options |= USB_2_STEP_INIT|USB_EXTRA_RESET;
}
}
}
static void parse_command_line(char *cmd_line, int cmd_line_size)
{
const char *option = cmd_line;
const char *params = NULL;
for (int i = 0; i < cmd_line_size; i++) {
switch (cmd_line[i]) {
case '\0':
parse_option(option, params);
return;
case ' ':
cmd_line[i] = '\0';
parse_option(option, params);
option = &cmd_line[i+1];
params = NULL;
break;
case '=':
cmd_line[i] = '\0';
params = &cmd_line[i+1];
break;
default:
break;
}
}
}
static void display_initial_notice(void)
{
if (smp_enabled) {
display_notice("Press <F1> to configure, <F2> to disable SMP, <Enter> to start testing");
} else {
display_notice("Press <F1> to configure, <F2> to enable SMP, <Enter> to start testing ");
}
}
static void update_num_pages_to_test(void)
{
num_pages_to_test = 0;
for (int i = 0; i < pm_map_size; i++) {
if (pm_map[i].start >= pm_limit_lower && pm_map[i].end <= pm_limit_upper) {
num_pages_to_test += pm_map[i].end - pm_map[i].start;
continue;
}
if (pm_map[i].start < pm_limit_lower) {
if (pm_map[i].end < pm_limit_lower) {
continue;
}
if (pm_map[i].end > pm_limit_upper) {
num_pages_to_test += pm_limit_upper - pm_limit_lower;
} else {
num_pages_to_test += pm_map[i].end - pm_limit_lower;
}
continue;
}
if (pm_map[i].end > pm_limit_upper) {
if (pm_map[i].start > pm_limit_upper) {
continue;
}
num_pages_to_test += pm_limit_upper - pm_map[i].start;
}
}
}
static void clear_popup_row(int row)
{
clear_screen_region(row, POP_C, row, POP_LAST_C);
}
static void display_input_message(int row, const char *message)
{
clear_popup_row(row);
prints(row, POP_LM, message);
if (enable_tty) tty_send_region(POP_REGION);
}
static void display_error_message(int row, const char *message)
{
clear_popup_row(row);
set_foreground_colour(YELLOW);
prints(row, POP_LM, message);
set_foreground_colour(WHITE);
if (enable_tty) tty_send_region(POP_REGION);
}
static void display_selection_header(int row, int max_num, int offset)
{
int i;
prints(row, POP_LM, "Current selection:");
if (max_num >= SEL_AREA) {
prints(row, POP_LM+18, " (scroll U D)");
printc(row, POP_LM+28, 0x18);
printc(row, POP_LM+30, 0x19);
}
row++;
printi(row, POP_LM-2, offset, 3, false, false);
offset++;
for (i = 1; i < SEL_W && offset < max_num; i++) {
printc(row, POP_LM+i, i%8 || (max_num < 16) ? 0xc4 : 0xc2);
offset++;
}
if (i == SEL_W) {
int data_rows = (max_num + SEL_W) / SEL_W;
if (data_rows > SEL_H) {
data_rows = SEL_H;
}
row += data_rows + 1;
offset += SEL_W * (data_rows - 2);
for (i = 0; i < (SEL_W - 1) && offset < max_num; i++) {
printc(row, POP_LM+i, i==0 ? 0xc0 : i%8 ? 0xc4 : 0xc1);
offset++;
}
}
printi(row, POP_LM+i, offset, 3, false, true);
}
static void display_enabled(int row, int n, bool enabled)
{
if (n >= 0 && n < SEL_AREA) {
printc(row + (n / SEL_W), POP_LM + (n % SEL_W), enabled ? '*' : '.');
}
}
static bool set_all_tests(bool enabled)
{
clear_popup_row(POP_R+14);
for (int i = 0; i < NUM_TEST_PATTERNS; i++) {
test_list[i].enabled = enabled;
display_enabled(POP_R+12, i, enabled);
}
return true;
}
static bool add_or_remove_test(bool add)
{
display_input_message(POP_R+14, "Enter test #");
int n = read_value(POP_R+14, POP_LM+12, 2, 0);
if (n < 0 || n >= NUM_TEST_PATTERNS) {
display_error_message(POP_R+14, "Invalid test number");
return false;
}
test_list[n].enabled = add;
display_enabled(POP_R+12, n, add);
clear_popup_row(POP_R+14);
return true;
}
static bool add_test_range()
{
display_input_message(POP_R+14, "Enter first test #");
int n1 = read_value(POP_R+14, POP_LM+18, 2, 0);
if (n1 < 0 || n1 >= NUM_TEST_PATTERNS) {
display_error_message(POP_R+14, "Invalid test number");
return false;
}
display_input_message(POP_R+14, "Enter last test #");
int n2 = read_value(POP_R+14, POP_LM+17, 2, 0);
if (n2 < n1 || n2 >= NUM_TEST_PATTERNS) {
display_error_message(POP_R+14, "Invalid test range");
return false;
}
for (int i = n1; i <= n2; i++) {
test_list[i].enabled = true;
display_enabled(POP_R+12, i, true);
}
clear_popup_row(POP_R+14);
return true;
}
static void test_selection_menu(void)
{
clear_screen_region(POP_REGION);
prints(POP_R+1, POP_LM, "Test Selection:");
prints(POP_R+3, POP_LI, "<F1> Clear selection");
prints(POP_R+4, POP_LI, "<F2> Remove one test");
prints(POP_R+5, POP_LI, "<F3> Add one test");
prints(POP_R+6, POP_LI, "<F4> Add test range");
prints(POP_R+7, POP_LI, "<F5> Add all tests");
prints(POP_R+8, POP_LI, "<F10> Exit menu");
display_selection_header(POP_R+10, NUM_TEST_PATTERNS - 1, 0);
for (int i = 0; i < NUM_TEST_PATTERNS; i++) {
display_enabled(POP_R+12, i, test_list[i].enabled);
}
bool tty_update = enable_tty;
bool exit_menu = false;
while (!exit_menu) {
bool changed = false;
if (tty_update) {
tty_send_region(POP_REGION);
}
tty_update = enable_tty;
switch (get_key()) {
case '1':
changed = set_all_tests(false);
break;
case '2':
changed = add_or_remove_test(false);
break;
case '3':
changed = add_or_remove_test(true);
break;
case '4':
changed = add_test_range();
break;
case '5':
changed = set_all_tests(true);
break;
case '0': {
clear_popup_row(POP_R+14);
int num_selected = 0;
for (int i = 0; i < NUM_TEST_PATTERNS; i++) {
if (test_list[i].enabled) {
num_selected++;
}
}
if (num_selected > 0) {
exit_menu = true;
} else {
display_error_message(POP_R+14, "You must select at least one test");
}
} break;
default:
usleep(1000);
tty_update = false;
break;
}
if (changed) {
restart = true;
changed = false;
}
}
clear_screen_region(POP_REGION);
}
static void address_range_menu(void)
{
clear_screen_region(POP_REGION);
prints(POP_R+1, POP_LM, "Address Range:");
prints(POP_R+3, POP_LI, "<F1> Set lower limit");
prints(POP_R+4, POP_LI, "<F2> Set upper limit");
prints(POP_R+5, POP_LI, "<F3> Test all memory");
prints(POP_R+6, POP_LI, "<F10> Exit menu");
printf(POP_R+8, POP_LM, "Current range: %kB - %kB", pm_limit_lower << 2, pm_limit_upper << 2);
bool tty_update = enable_tty;
bool exit_menu = false;
while (!exit_menu) {
bool changed = false;
if (tty_update) {
tty_send_region(POP_REGION);
}
tty_update = enable_tty;
switch (get_key()) {
case '1': {
display_input_message(POP_R+10, "Enter lower limit: ");
uintptr_t page = read_value(POP_R+10, POP_LM+19, 15, -PAGE_SHIFT);
if (page < pm_limit_upper) {
clear_popup_row(POP_R+10);
pm_limit_lower = page;
changed = true;
} else {
display_error_message(POP_R+10, "Lower must be less than upper");
}
} break;
case '2': {
display_input_message(POP_R+10, "Enter upper limit: ");
uintptr_t page = read_value(POP_R+10, POP_LM+19, 15, -PAGE_SHIFT);
if (page > pm_limit_lower) {
clear_popup_row(POP_R+10);
pm_limit_upper = page;
changed = true;
} else {
display_error_message(POP_R+10, "Upper must be greater than lower");
}
} break;
case '3':
clear_popup_row(POP_R+10);
pm_limit_lower = 0;
pm_limit_upper = pm_map[pm_map_size - 1].end;
changed = true;
break;
case '0':
exit_menu = true;
break;
default:
usleep(1000);
tty_update = false;
break;
}
if (changed) {
clear_popup_row(POP_R+8);
printf(POP_R+8, POP_LM, "Current range: %kB - %kB", pm_limit_lower << 2, pm_limit_upper << 2);
update_num_pages_to_test();
restart = true;
changed = false;
}
}
clear_screen_region(POP_REGION);
}
static void set_cpu_mode(cpu_mode_t mode)
{
printc(POP_R+3+cpu_mode, POP_LM, ' ');
cpu_mode = mode;
printc(POP_R+3+cpu_mode, POP_LM, '*');
}
static void cpu_mode_menu(void)
{
clear_screen_region(POP_REGION);
prints(POP_R+1, POP_LM, "CPU Sequencing Mode:");
prints(POP_R+3, POP_LI, "<F1> Parallel (PAR)");
prints(POP_R+4, POP_LI, "<F2> Sequential (SEQ)");
prints(POP_R+5, POP_LI, "<F3> Round robin (RR)");
prints(POP_R+6, POP_LI, "<F10> Exit menu");
printc(POP_R+3+cpu_mode, POP_LM, '*');
bool tty_update = enable_tty;
bool exit_menu = false;
while (!exit_menu) {
int ch = get_key();
if (tty_update) {
tty_send_region(POP_REGION);
}
tty_update = enable_tty;
switch (ch) {
case '1':
case '2':
case '3':
set_cpu_mode(ch - '1');
break;
case 'u':
if (cpu_mode > 0) {
set_cpu_mode(cpu_mode - 1);
}
break;
case 'd':
if (cpu_mode < 2) {
set_cpu_mode(cpu_mode + 1);
}
break;
case '0':
exit_menu = true;
break;
default:
usleep(1000);
tty_update = false;
break;
}
}
clear_screen_region(POP_REGION);
}
static void set_error_mode(error_mode_t mode)
{
printc(POP_R+3+error_mode, POP_LM, ' ');
error_mode = mode;
printc(POP_R+3+error_mode, POP_LM, '*');
}
static void error_mode_menu(void)
{
clear_screen_region(POP_REGION);
prints(POP_R+1, POP_LM, "Error Reporting Mode:");
prints(POP_R+3, POP_LI, "<F1> Error counts only");
prints(POP_R+4, POP_LI, "<F2> Error summary");
prints(POP_R+5, POP_LI, "<F3> Individual errors");
prints(POP_R+6, POP_LI, "<F4> BadRAM patterns");
prints(POP_R+7, POP_LI, "<F5> Linux memmap");
prints(POP_R+8, POP_LI, "<F6> Bad pages");
prints(POP_R+9, POP_LI, "<F10> Exit menu");
printc(POP_R+3+error_mode, POP_LM, '*');
bool tty_update = enable_tty;
bool exit_menu = false;
while (!exit_menu) {
int ch = get_key();
if (tty_update) {
tty_send_region(POP_REGION);
}
tty_update = enable_tty;
switch (ch) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
set_error_mode(ch - '1');
break;
case 'u':
if (error_mode > 0) {
set_error_mode(error_mode - 1);
}
break;
case 'd':
if (error_mode < 5) {
set_error_mode(error_mode + 1);
}
break;
case '0':
exit_menu = true;
break;
default:
usleep(1000);
tty_update = false;
break;
}
}
clear_screen_region(POP_REGION);
}
static bool set_all_cpus(cpu_state_t state, int display_offset)
{
clear_popup_row(POP_R+16);
for (int i = 1; i < num_available_cpus; i++) {
cpu_state[i] = state;
display_enabled(POP_R+12, i - display_offset, state == CPU_STATE_ENABLED);
}
return true;
}
static bool add_or_remove_cpu(bool add, int display_offset)
{
display_input_message(POP_R+16, "Enter CPU #");
int n = read_value(POP_R+16, POP_LM+11, 4, 0);
if (n < 1 || n >= num_available_cpus) {
display_error_message(POP_R+16, "Invalid CPU number");
return false;
}
cpu_state[n] = add ? CPU_STATE_ENABLED : CPU_STATE_DISABLED;
display_enabled(POP_R+12, n - display_offset, add);
clear_popup_row(POP_R+16);
return true;
}
static bool add_cpu_range(int display_offset)
{
display_input_message(POP_R+16, "Enter first CPU #");
int n1 = read_value(POP_R+16, POP_LM+17, 4, 0);
if (n1 < 1 || n1 >= num_available_cpus) {
display_error_message(POP_R+16, "Invalid CPU number");
return false;
}
display_input_message(POP_R+16, "Enter last CPU #");
int n2 = read_value(POP_R+16, POP_LM+16, 4, 0);
if (n2 < n1 || n2 >= num_available_cpus) {
display_error_message(POP_R+16, "Invalid CPU range");
return false;
}
for (int i = n1; i <= n2; i++) {
cpu_state[i] = CPU_STATE_ENABLED;
display_enabled(POP_R+12, i - display_offset, true);
}
clear_popup_row(POP_R+16);
return true;
}
static void display_cpu_selection(int display_offset)
{
clear_screen_region(POP_R+11, POP_C, POP_LAST_R, POP_LAST_C);
display_selection_header(POP_R+10, num_available_cpus - 1, display_offset);
if (display_offset == 0) {
printc(POP_R+12, POP_LM, 'B');
}
for (int i = 1; i < num_available_cpus; i++) {
display_enabled(POP_R+12, i - display_offset, cpu_state[i] == CPU_STATE_ENABLED);
}
}
static void cpu_selection_menu(void)
{
int display_offset = 0;
clear_screen_region(POP_REGION);
prints(POP_R+1, POP_LM, "CPU Selection:");
prints(POP_R+3, POP_LI, "<F1> Clear selection");
prints(POP_R+4, POP_LI, "<F2> Remove one CPU");
prints(POP_R+5, POP_LI, "<F3> Add one CPU");
prints(POP_R+6, POP_LI, "<F4> Add CPU range");
prints(POP_R+7, POP_LI, "<F5> Add all CPUs");
if (cpuid_info.topology.is_hybrid) {
if (exclude_ecores) {
prints(POP_R+8, POP_LI, "<F6> Include E-Cores");
} else {
prints(POP_R+8, POP_LI, "<F6> Exclude E-Cores");
}
}
prints(POP_R+9, POP_LI, "<F10> Exit menu");
display_cpu_selection(display_offset);
bool exit_menu = false;
while (!exit_menu) {
bool changed = false;
switch (get_key()) {
case '1':
changed = set_all_cpus(false, display_offset);
break;
case '2':
changed = add_or_remove_cpu(false, display_offset);
break;
case '3':
changed = add_or_remove_cpu(true, display_offset);
break;
case '4':
changed = add_cpu_range(display_offset);
break;
case '5':
changed = set_all_cpus(true, display_offset);
break;
case '6':
exclude_ecores = !exclude_ecores;
prints(POP_R+8, POP_LI+6, exclude_ecores ? "Exclude" : "Include");
break;
case 'u':
if (display_offset >= SEL_W) {
display_offset -= SEL_W;
display_cpu_selection(display_offset);
}
break;
case 'd':
if (display_offset < (num_available_cpus - SEL_AREA)) {
display_offset += SEL_W;
display_cpu_selection(display_offset);
}
break;
case '0':
clear_popup_row(POP_R+14);
exit_menu = true;
break;
default:
usleep(1000);
break;
}
if (changed) {
restart = true;
changed = false;
}
}
clear_screen_region(POP_REGION);
}
//------------------------------------------------------------------------------
// Public Functions
//------------------------------------------------------------------------------
void config_init(void)
{
pm_limit_lower = 0;
pm_limit_upper = pm_map[pm_map_size - 1].end;
update_num_pages_to_test();
cpu_mode = PAR;
error_mode = ERROR_MODE_ADDRESS;
for (int i = 0; i < MAX_CPUS; i++) {
cpu_state[i] = CPU_STATE_ENABLED;
}
power_save = POWER_SAVE_HIGH;
const boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
uintptr_t cmd_line_addr = boot_params->cmd_line_ptr;
if (cmd_line_addr != 0) {
int cmd_line_size = boot_params->cmd_line_size;
if (cmd_line_size == 0) cmd_line_size = 255;
cmd_line_addr = map_region(cmd_line_addr, cmd_line_size, true);
if (cmd_line_addr != 0) {
parse_command_line((char *)cmd_line_addr, cmd_line_size);
}
}
}
void config_menu(bool initial)
{
save_screen_region(POP_REGION, popup_save_buffer);
set_background_colour(BLACK);
clear_screen_region(POP_REGION);
cpu_mode_t old_cpu_mode = cpu_mode;
bool tty_update = enable_tty;
bool exit_menu = false;
while (!exit_menu) {
prints(POP_R+1, POP_LM, "Settings:");
prints(POP_R+3, POP_LI, "<F1> Test selection");
prints(POP_R+4, POP_LI, "<F2> Address range");
prints(POP_R+5, POP_LI, "<F3> CPU sequencing mode");
prints(POP_R+6, POP_LI, "<F4> Error reporting mode");
if (initial) {
if (!smp_enabled) set_foreground_colour(BOLD+BLACK);
prints(POP_R+7, POP_LI, "<F5> CPU selection");
if (!smp_enabled) set_foreground_colour(WHITE);
//if (no_temperature) set_foreground_colour(BOLD+BLACK);
printf(POP_R+8, POP_LI, "<F6> Temperature %s", enable_temperature ? "disable" : "enable ");
//if (no_temperature) set_foreground_colour(WHITE);
printf(POP_R+9, POP_LI, "<F7> Boot trace %s", enable_trace ? "disable" : "enable ");
prints(POP_R+10, POP_LI, "<F10> Exit menu");
} else {
prints(POP_R+7, POP_LI, "<F5> Skip current test");
prints(POP_R+8 , POP_LI, "<F10> Exit menu");
}
if (tty_update) {
tty_send_region(POP_REGION);
}
tty_update = enable_tty;
switch (get_key()) {
case '1':
test_selection_menu();
break;
case '2':
address_range_menu();
break;
case '3':
cpu_mode_menu();
break;
case '4':
error_mode_menu();
break;
case '5':
if (initial) {
if (smp_enabled) {
cpu_selection_menu();
}
} else {
exit_menu = true;
bail = true;
}
break;
case '6':
if (initial) {
enable_temperature = !enable_temperature;
}
break;
case '7':
if (initial) {
enable_trace = !enable_trace;
}
break;
case '0':
exit_menu = true;
break;
default:
usleep(1000);
tty_update = false;
break;
}
}
restore_screen_region(POP_REGION, popup_save_buffer);
set_background_colour(BLUE);
if (enable_tty) {
tty_send_region(POP_REGION);
}
if (cpu_mode != old_cpu_mode) {
display_cpu_topology();
restart = true;
}
if (restart) {
bail = true;
}
}
void initial_config(void)
{
display_initial_notice();
if (num_available_cpus < 2) {
smp_enabled = false;
}
if (pause_at_start) {
bool got_key = false;
for (int i = 0; i < 3000 && !got_key; i++) {
usleep(1000);
switch (get_key()) {
case ESC:
clear_message_area();
display_notice("Rebooting...");
reboot();
break;
case '1':
config_menu(true);
got_key = true;
break;
case '2':
smp_enabled = !smp_enabled;
display_initial_notice();
i = 0;
break;
case ' ':
toggle_scroll_lock();
break;
case '\n':
got_key = true;
break;
default:
break;
}
}
}
}