mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-27 10:00:17 -06:00
f6da06b117
There is an unavoidable race between one core halting after decrementing the barrier count and another core sending it the wakeup NMI. This can only occur if the core sending the wakeup is running at many times the speed of the core halting, but it has been observed on an Intel Icelake mobile processor.
203 lines
6.1 KiB
C
203 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2020-2022 Martin Whitaker.
|
|
//
|
|
// Derived from extract of memtest86+ lib.c:
|
|
//
|
|
// lib.c - MemTest-86 Version 3.4
|
|
//
|
|
// Released under version 2 of the Gnu Public License.
|
|
// By Chris Brady
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "cpuid.h"
|
|
#include "hwctrl.h"
|
|
#include "keyboard.h"
|
|
#include "screen.h"
|
|
#include "smp.h"
|
|
|
|
#include "error.h"
|
|
#include "display.h"
|
|
|
|
#include "interrupt.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Constants
|
|
//------------------------------------------------------------------------------
|
|
|
|
#define HLT_OPCODE 0xf4
|
|
#define JE_OPCODE 0x74
|
|
|
|
#ifdef __x86_64__
|
|
#define REG_PREFIX "r"
|
|
#define REG_DIGITS "16"
|
|
#define ADR_DIGITS "12"
|
|
#else
|
|
#define REG_PREFIX "e"
|
|
#define REG_DIGITS "8"
|
|
#define ADR_DIGITS "8"
|
|
#endif
|
|
|
|
static const char *codes[] = {
|
|
"Divide by 0",
|
|
"Debug",
|
|
"NMI",
|
|
"Breakpoint",
|
|
"Overflow",
|
|
"Bounds",
|
|
"Invalid Op",
|
|
"No FPU",
|
|
"Double fault",
|
|
"Seg overrun",
|
|
"Invalid TSS",
|
|
"Seg fault",
|
|
"Stack fault",
|
|
"Gen prot.",
|
|
"Page fault",
|
|
"Reserved",
|
|
"FPU error",
|
|
"Alignment",
|
|
"Machine chk",
|
|
"SIMD FPE"
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Types
|
|
//------------------------------------------------------------------------------
|
|
|
|
#ifdef __x86_64__
|
|
typedef uint64_t reg_t;
|
|
#else
|
|
typedef uint32_t reg_t;
|
|
#endif
|
|
|
|
struct trap_regs {
|
|
reg_t ds;
|
|
reg_t es;
|
|
reg_t ss;
|
|
reg_t ax;
|
|
reg_t bx;
|
|
reg_t cx;
|
|
reg_t dx;
|
|
reg_t di;
|
|
reg_t si;
|
|
#ifndef __x86_64__
|
|
reg_t reserved1;
|
|
reg_t reserved2;
|
|
reg_t sp;
|
|
#endif
|
|
reg_t bp;
|
|
reg_t vect;
|
|
reg_t code;
|
|
reg_t ip;
|
|
reg_t cs;
|
|
reg_t flags;
|
|
#ifdef __x86_64__
|
|
reg_t sp;
|
|
#endif
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
void interrupt(struct trap_regs *trap_regs)
|
|
{
|
|
// Get the page fault address.
|
|
uintptr_t address = 0;
|
|
if (trap_regs->vect == 14) {
|
|
#ifdef __x86_64__
|
|
__asm__(
|
|
"movq %%cr2, %0"
|
|
:"=r" (address)
|
|
);
|
|
#else
|
|
__asm__(
|
|
"movl %%cr2, %0"
|
|
:"=r" (address)
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (trap_regs->vect == 2) {
|
|
uint8_t *pc = (uint8_t *)trap_regs->ip;
|
|
if (pc[-1] == HLT_OPCODE) {
|
|
// Assume this is a barrier wakeup signal sent via IPI.
|
|
return;
|
|
}
|
|
// Catch the rare case that a core will fail to reach the HLT instruction before
|
|
// its wakeup signal arrives. The barrier code contains an atomic decrement, a JE
|
|
// instruction (two bytes), and a HLT instruction (one byte). The atomic decrement
|
|
// must have completed if another core has reached the point of sending the wakeup
|
|
// signals, so we should find the HLT opcode either at pc[0] or at pc[2]. If we find
|
|
// it, adjust the interrupt return address to point to the following instruction.
|
|
if (pc[0] == HLT_OPCODE || (pc[0] == JE_OPCODE && pc[2] == HLT_OPCODE)) {
|
|
uintptr_t *return_addr;
|
|
if (cpuid_info.flags.lm == 1) {
|
|
return_addr = (uintptr_t *)(trap_regs->sp - 40);
|
|
} else {
|
|
return_addr = (uintptr_t *)(trap_regs->sp - 12);
|
|
}
|
|
if (pc[2] == HLT_OPCODE) {
|
|
*return_addr += 3;
|
|
} else {
|
|
*return_addr += 1;
|
|
}
|
|
return;
|
|
}
|
|
#if REPORT_PARITY_ERRORS
|
|
parity_error();
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
spin_lock(error_mutex);
|
|
|
|
clear_message_area();
|
|
|
|
display_pinned_message(0, 0, "Unexpected interrupt on CPU %i", smp_my_cpu_num());
|
|
if (trap_regs->vect <= 19) {
|
|
display_pinned_message(2, 0, "Type: %s", codes[trap_regs->vect]);
|
|
} else {
|
|
display_pinned_message(2, 0, "Type: %i", trap_regs->vect);
|
|
}
|
|
display_pinned_message(3, 0, " IP: %0" REG_DIGITS "x", (uintptr_t)trap_regs->ip);
|
|
display_pinned_message(4, 0, " CS: %0" REG_DIGITS "x", (uintptr_t)trap_regs->cs);
|
|
display_pinned_message(5, 0, "Flag: %0" REG_DIGITS "x", (uintptr_t)trap_regs->flags);
|
|
display_pinned_message(6, 0, "Code: %0" REG_DIGITS "x", (uintptr_t)trap_regs->code);
|
|
display_pinned_message(7, 0, " DS: %0" REG_DIGITS "x", (uintptr_t)trap_regs->ds);
|
|
display_pinned_message(8, 0, " ES: %0" REG_DIGITS "x", (uintptr_t)trap_regs->es);
|
|
display_pinned_message(9, 0, " SS: %0" REG_DIGITS "x", (uintptr_t)trap_regs->ss);
|
|
if (trap_regs->vect == 14) {
|
|
display_pinned_message(9, 0, " Addr: %0" REG_DIGITS "x", address);
|
|
}
|
|
|
|
display_pinned_message(2, 25, REG_PREFIX "ax: %0" REG_DIGITS "x", (uintptr_t)trap_regs->ax);
|
|
display_pinned_message(3, 25, REG_PREFIX "bx: %0" REG_DIGITS "x", (uintptr_t)trap_regs->bx);
|
|
display_pinned_message(4, 25, REG_PREFIX "cx: %0" REG_DIGITS "x", (uintptr_t)trap_regs->cx);
|
|
display_pinned_message(5, 25, REG_PREFIX "dx: %0" REG_DIGITS "x", (uintptr_t)trap_regs->dx);
|
|
display_pinned_message(6, 25, REG_PREFIX "di: %0" REG_DIGITS "x", (uintptr_t)trap_regs->di);
|
|
display_pinned_message(7, 25, REG_PREFIX "si: %0" REG_DIGITS "x", (uintptr_t)trap_regs->si);
|
|
display_pinned_message(8, 25, REG_PREFIX "bp: %0" REG_DIGITS "x", (uintptr_t)trap_regs->bp);
|
|
display_pinned_message(9, 25, REG_PREFIX "sp: %0" REG_DIGITS "x", (uintptr_t)trap_regs->sp);
|
|
|
|
display_pinned_message(0, 50, "Stack:");
|
|
for (int i = 0; i < 12; i++) {
|
|
uintptr_t addr = trap_regs->sp + sizeof(reg_t)*(11 - i);
|
|
reg_t data = *(reg_t *)addr;
|
|
display_pinned_message(1 + i, 50, "%0" ADR_DIGITS "x %0" REG_DIGITS "x", addr, (uintptr_t)data);
|
|
}
|
|
|
|
display_pinned_message(11, 0, "CS:IP:");
|
|
uint8_t *pp = (uint8_t *)((uintptr_t)trap_regs->ip);
|
|
for (int i = 0; i < 12; i++) {
|
|
display_pinned_message(11, 7 + 3*i, "%02x", (uintptr_t)pp[i]);
|
|
}
|
|
|
|
clear_screen_region(ROW_FOOTER, 0, ROW_FOOTER, SCREEN_WIDTH - 1);
|
|
prints(ROW_FOOTER, 0, "Press any key to reboot...");
|
|
|
|
while (get_key() == 0) { }
|
|
reboot();
|
|
}
|