memtest86plus/app/config.c
Sam Demeulemeester 5dde13b0a1
Preliminary ECC support for AMD Zen CPUs (#353)
* Initial commit for ECC support. Preliminary support for AMD Zen.

* Clear ECC registers at startup

* Add config flag (enable_ecc_polling) to toggle ECC polling. (Currently disabled by default for v7 release)
2023-11-29 12:53:05 +01:00

994 lines
28 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_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(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, "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, "<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':
set_error_mode(ch - '1');
break;
case 'u':
if (error_mode > 0) {
set_error_mode(error_mode - 1);
}
break;
case 'd':
if (error_mode < 3) {
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;
}
}
}
}