/* * This file is part of the coreboot project. * * Copyright (C) 2008-2009 coresystems GmbH * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; version 2 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA */ #include <arch/io.h> #include <console/console.h> #include <cpu/x86/cache.h> #include <cpu/x86/smm.h> #if !CONFIG_SMM_TSEG /* TSEG handler locks in assembly */ typedef enum { SMI_LOCKED, SMI_UNLOCKED } smi_semaphore; /* SMI multiprocessing semaphore */ static volatile smi_semaphore smi_handler_status __attribute__ ((aligned (4))) = SMI_UNLOCKED; static int smi_obtain_lock(void) { u8 ret = SMI_LOCKED; asm volatile ( "movb %2, %%al\n" "xchgb %%al, %1\n" "movb %%al, %0\n" : "=g" (ret), "=m" (smi_handler_status) : "g" (SMI_LOCKED) : "eax" ); return (ret == SMI_UNLOCKED); } void smi_release_lock(void) { asm volatile ( "movb %1, %%al\n" "xchgb %%al, %0\n" : "=m" (smi_handler_status) : "g" (SMI_UNLOCKED) : "eax" ); } #endif #define LAPIC_ID 0xfee00020 static inline __attribute__((always_inline)) unsigned long nodeid(void) { return (*((volatile unsigned long *)(LAPIC_ID)) >> 24); } void io_trap_handler(int smif) { /* If a handler function handled a given IO trap, it * shall return a non-zero value */ printk(BIOS_DEBUG, "SMI function trap 0x%x: ", smif); if (southbridge_io_trap_handler(smif)) return; if (mainboard_io_trap_handler(smif)) return; printk(BIOS_DEBUG, "Unknown function\n"); } /** * @brief Set the EOS bit */ static void smi_set_eos(void) { southbridge_smi_set_eos(); } static u32 pci_orig; /** * @brief Backup PCI address to make sure we do not mess up the OS */ static void smi_backup_pci_address(void) { pci_orig = inl(0xcf8); } /** * @brief Restore PCI address previously backed up */ static void smi_restore_pci_address(void) { outl(pci_orig, 0xcf8); } static inline void *smm_save_state(u32 base, int arch_offset, int node) { base += SMM_SAVE_STATE_BEGIN(arch_offset) - (node * 0x400); return (void *)base; } /** * @brief Interrupt handler for SMI# * * @param smm_revision revision of the smm state save map */ void smi_handler(u32 smm_revision) { unsigned int node; smm_state_save_area_t state_save; u32 smm_base = 0xa0000; /* ASEG */ #if CONFIG_SMM_TSEG /* Update global variable TSEG base */ if (!smi_get_tseg_base()) return; smm_base = smi_get_tseg_base(); #else /* Are we ok to execute the handler? */ if (!smi_obtain_lock()) { /* For security reasons we don't release the other CPUs * until the CPU with the lock is actually done */ while (smi_handler_status == SMI_LOCKED) { asm volatile ( ".byte 0xf3, 0x90\n" /* hint a CPU we are in spinlock (PAUSE instruction, REP NOP) */ ); } return; } #endif smi_backup_pci_address(); node=nodeid(); console_init(); printk(BIOS_SPEW, "\nSMI# #%d\n", node); switch (smm_revision) { case 0x00030002: case 0x00030007: state_save.type = LEGACY; state_save.legacy_state_save = smm_save_state(smm_base, 0x7e00, node); break; case 0x00030100: state_save.type = EM64T; state_save.em64t_state_save = smm_save_state(smm_base, 0x7d00, node); case 0x00030101: /* SandyBridge, IvyBridge, and Haswell */ state_save.type = EM64T101; state_save.em64t101_state_save = smm_save_state(smm_base, SMM_EM64T101_ARCH_OFFSET, node); break; case 0x00030064: state_save.type = AMD64; state_save.amd64_state_save = smm_save_state(smm_base, 0x7e00, node); break; default: printk(BIOS_DEBUG, "smm_revision: 0x%08x\n", smm_revision); printk(BIOS_DEBUG, "SMI# not supported on your CPU\n"); /* Don't release lock, so no further SMI will happen, * if we don't handle it anyways. */ return; } /* Call chipset specific SMI handlers. */ if (cpu_smi_handler) cpu_smi_handler(node, &state_save); if (northbridge_smi_handler) northbridge_smi_handler(node, &state_save); if (southbridge_smi_handler) southbridge_smi_handler(node, &state_save); smi_restore_pci_address(); #if !CONFIG_SMM_TSEG smi_release_lock(); #endif /* De-assert SMI# signal to allow another SMI */ smi_set_eos(); }