// System Management Mode support (on emulators)
//
-// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2006 Fabrice Bellard
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "util.h" // smm_setup
#include "x86.h" // wbinvd
-extern u8 smm_code_start, smm_code_end;
-
-ASM32FLAT(
- ".global smm_code_start, smm_code_end\n"
- " .code16gcc\n"
- "smm_code_start:\n"
- " mov %cs, %ax\n"
- " cmp $0xa000, %ax\n"
- " je smm_exit\n"
-
- /* code to relocate SMBASE to 0xa0000 */
- " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7efc, %ebx\n"
- " addr32 movb (%ebx), %al\n" /* revision ID to see if x86_64 or x86 */
- " cmpb $0x64, %al\n"
- " je 1f\n"
- " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7ef8, %ebx\n"
- " jmp 2f\n"
- "1:\n"
- " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7f00, %ebx\n"
- "2:\n"
- " movl $" __stringify(BUILD_SMM_ADDR) " - 0x8000, %eax\n"
- " addr32 movl %eax, (%ebx)\n"
- /* indicate to the BIOS that the SMM code was executed */
- " movb $0x00, %al\n"
- " movw $" __stringify(PORT_SMI_STATUS) ", %dx\n"
- " outb %al, %dx\n"
- "smm_exit:\n"
- " rsm\n"
- "smm_code_end:\n"
- " .code32\n"
- );
+void VISIBLE32FLAT
+handle_smi(u16 cs)
+{
+ u8 cmd = inb(PORT_SMI_CMD);
+ dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x cs=%x\n", cmd, cs);
+
+ void *smbase = MAKE_FLATPTR(cs, 0) + 0x8000;
+ if (smbase == (void*)BUILD_SMM_INIT_ADDR) {
+ // relocate SMBASE to 0xa0000
+ u8 *smrev = smbase + 0x7efc;
+ u32 *newbase = smbase + 0x7ef8;
+ if (*smrev == 0x64)
+ newbase = smbase + 0x7f00;
+ *newbase = BUILD_SMM_ADDR - 0x8000;
+ // indicate to smm_relocate_and_restore() that the SMM code was executed
+ outb(0x00, PORT_SMI_STATUS);
+ return;
+ }
+}
+
+extern void entry_smi(void);
+// movw %cs, %ax; ljmpw $SEG_BIOS, $(entry_smi - BUILD_BIOS_ADDR)
+#define SMI_INSN (0xeac88c | ((u64)SEG_BIOS<<40) \
+ | ((u64)((u32)entry_smi - BUILD_BIOS_ADDR) << 24))
static void
smm_save_and_copy(void)
{
- /* save original memory content */
+ // save original memory content
memcpy((void *)BUILD_SMM_ADDR, (void *)BUILD_SMM_INIT_ADDR, BUILD_SMM_SIZE);
- /* copy the SMM code, which will relocate itself on the first execution */
- memcpy((void *)BUILD_SMM_INIT_ADDR, &smm_code_start,
- &smm_code_end - &smm_code_start);
+ // Setup code entry point.
+ *(u64*)BUILD_SMM_INIT_ADDR = SMI_INSN;
}
static void
/* restore original memory content */
memcpy((void *)BUILD_SMM_INIT_ADDR, (void *)BUILD_SMM_ADDR, BUILD_SMM_SIZE);
- /* copy the SMM code */
- memcpy((void *)BUILD_SMM_ADDR, &smm_code_start
- , &smm_code_end - &smm_code_start);
+ // Setup code entry point.
+ *(u64*)BUILD_SMM_ADDR = SMI_INSN;
wbinvd();
}
// Clobbers: ecx, flags, segment registers, cr0, idt/gdt
DECLFUNC transition32
.code16gcc
+transition32_for_smi:
+ movl %eax, %ecx
+ jmp 1f
transition32:
movl %eax, %ecx
outb %al, $PORT_A20
// Set segment descriptors
- lidtw %cs:pmode_IDT_info
+1: lidtw %cs:pmode_IDT_info
lgdtw %cs:rombios32_gdt_48
// Enable protected mode
movl %eax, %cr0
// start 32bit protected mode code
- ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 1f)
+ ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 2f)
.code32
-1:
// init data segments
- movl $SEG32_MODE32_DS, %eax
+2: movl $SEG32_MODE32_DS, %eax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
* Misc. entry points.
****************************************************************/
+// Entry point for QEMU smi interrupts.
+ DECLFUNC entry_smi
+entry_smi:
+ // Transition to 32bit mode.
+ movl $1f + BUILD_BIOS_ADDR, %edx
+ jmp transition32_for_smi
+ .code32
+1: movl $BUILD_SMM_ADDR, %esp
+ calll _cfunc32flat_handle_smi - BUILD_BIOS_ADDR
+ rsm
+ .code16gcc
+
// Resume (and reboot) entry point - called from entry_post
DECLFUNC entry_resume
entry_resume: