// 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 #include #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(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 to configure, to disable SMP, to start testing"); } else { display_notice("Press to configure, to enable SMP, 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, " Clear selection"); prints(POP_R+4, POP_LI, " Remove one test"); prints(POP_R+5, POP_LI, " Add one test"); prints(POP_R+6, POP_LI, " Add test range"); prints(POP_R+7, POP_LI, " Add all tests"); prints(POP_R+8, POP_LI, " 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, " Set lower limit"); prints(POP_R+4, POP_LI, " Set upper limit"); prints(POP_R+5, POP_LI, " Test all memory"); prints(POP_R+6, POP_LI, " 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, " Parallel (PAR)"); prints(POP_R+4, POP_LI, " Sequential (SEQ)"); prints(POP_R+5, POP_LI, " Round robin (RR)"); prints(POP_R+6, POP_LI, " 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, " Error counts only"); prints(POP_R+4, POP_LI, " Error summary"); prints(POP_R+5, POP_LI, " Individual errors"); prints(POP_R+6, POP_LI, " BadRAM patterns"); prints(POP_R+7, POP_LI, " 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, " Clear selection"); prints(POP_R+4, POP_LI, " Remove one CPU"); prints(POP_R+5, POP_LI, " Add one CPU"); prints(POP_R+6, POP_LI, " Add CPU range"); prints(POP_R+7, POP_LI, " Add all CPUs"); if (cpuid_info.topology.is_hybrid) { if (exclude_ecores) { prints(POP_R+8, POP_LI, " Include E-Cores"); } else { prints(POP_R+8, POP_LI, " Exclude E-Cores"); } } prints(POP_R+9, POP_LI, " 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, " Test selection"); prints(POP_R+4, POP_LI, " Address range"); prints(POP_R+5, POP_LI, " CPU sequencing mode"); prints(POP_R+6, POP_LI, " Error reporting mode"); if (initial) { if (!smp_enabled) set_foreground_colour(BOLD+BLACK); prints(POP_R+7, POP_LI, " CPU selection"); if (!smp_enabled) set_foreground_colour(WHITE); //if (no_temperature) set_foreground_colour(BOLD+BLACK); printf(POP_R+8, POP_LI, " Temperature %s", enable_temperature ? "disable" : "enable "); //if (no_temperature) set_foreground_colour(WHITE); printf(POP_R+9, POP_LI, " Boot trace %s", enable_trace ? "disable" : "enable "); prints(POP_R+10, POP_LI, " Exit menu"); } else { prints(POP_R+7, POP_LI, " Skip current test"); prints(POP_R+8 , POP_LI, " 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; } } } }