/* * Copyright (c) 2006-2008 Advanced Micro Devices,Inc. ("AMD"). * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This code 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ //***************************************************************************** //* Implements the handlers for top-level SMI source //***************************************************************************** #include "VSA2.H" #include "CHIPSET.H" #include "PROTOS.H" #include "SYSMGR.H" #include "VPCI.H" #include "PCI.H" // External variables extern SmiHeader SMM_Header; extern SmiHeader Nested_Header; extern ULONG Saved_EAX, Saved_EBX, Saved_ECX; extern ULONG MsgPacket[]; extern ULONG VSM_ListHead; extern ULONG Virtualized_PCI_Devices; extern ULONG Stats_Sources; extern ULONG IRQ_Mask; extern ULONG MPCI_NB; extern ULONG VSM_Buffer; extern ULONG Nested_Flag; extern ULONG Video_Sources; extern USHORT Audio_Sources; extern Hardware HardwareInfo; extern EVENT_ENTRY Events[]; extern PCI_HEADER_ENTRY ISA_Hdr[]; extern PCI_HEADER_ENTRY HostBridge_Hdr[]; // External function prototypes extern void pascal Timer_Handler(USHORT); extern void CS5536_GPIO_Handler(ULONG); extern void INT_Return(void); extern void Send_OHCI_Event(UCHAR); extern void VR_Handler(SmiHeader *); extern void ACPI_Workaround(SmiHeader *, USHORT); extern void Remove_RTC_Fix(void); extern void set_reset_state(void); extern void pascal Unblock_VSM(ULONG); extern void pascal Return_Virtual_Value(SmiHeader *, ULONG); extern SmiHeader * pascal Get_Header_Params(ULONG); extern ULONG Get_ACPI_Status(ULONG *); extern USHORT Get_Timeout(ULONG, UCHAR *); extern USHORT CS5536_MFGPT_Handler(void); extern void ReInit_Descriptors(void); extern void Init_SysMgr(); extern void A20_Sync(void); extern void Update_VSMs_CR0(void); ULONG INT_Vectors[MAX_INT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xF000F859}; ULONG VSM_Ptrs[VSM_MAX_TYPE+1]; ULONG Audio_IRQ = 0; ULONG NativeAudioStatus = 0x4F0; // Defaults to 0040:00F0 UCHAR End_of_POST = 0; void Broadcast_SysMgr_Msg (MSG Message, UCHAR Param1) { MsgPacket[0] = (ULONG)Param1; MsgPacket[1] = MsgPacket[2] = 0; Broadcast_Message(Message, VSM_ANY, 0x00000000); } //*********************************************************************** // Sends a message for an asynchronous event //*********************************************************************** void pascal Send_Asynchronous_Event(EVENT Event) { MsgPacket[1] = MsgPacket[3] = MsgPacket[4] = 00000000; if (Event != EVENT_IO_TIMEOUT) { MsgPacket[2] = 00000000; } Send_Event(Event, 0x00000000); } //*********************************************************************** // Sends a message for a synchronous event // Returns TRUE if the event was registered. //*********************************************************************** USHORT pascal Send_Synchronous_Event(EVENT Event, SmiHeader * SmiHdr) { ULONG Vsm; USHORT EventRegistered; if ((USHORT)SmiHdr == 0x0000 || (USHORT)SmiHdr == (USHORT)&SMM_Header) { Vsm = SysMgr_VSM; } else { Vsm = Current_VSM; } EventRegistered = Send_Event(Event, Vsm); if (!EventRegistered) { // No VSM is registered for this event // If nested event, change the VSM's state from 'Blocked' to 'Ready' if (Nested_Flag) { Unblock_VSM(Vsm); } } return EventRegistered; } //*********************************************************************** // This routine walks the VSM linked list, recording ptrs to VSMs that // have special requirements. //*********************************************************************** void Record_VSM_Locations(void) { ULONG VSM_Ptr; UCHAR VSM_Type; for (VSM_Type=0; VSM_Type<=VSM_MAX_TYPE; VSM_Type++) { VSM_Ptrs[VSM_Type] = 0x00000000; } VSM_Ptr = VSM_ListHead; while (VSM_Ptr) { VSM_Type = Get_VSM_Type(VSM_Ptr); if (VSM_Type < sizeof(VSM_Ptrs)/4) { VSM_Ptrs[VSM_Type] = VSM_Ptr; } if (VSM_Type == VSM_RTC) { Remove_RTC_Fix(); } VSM_Ptr = GetFlink(VSM_Ptr); } } //*********************************************************************** // This routine handles software SMI events. //*********************************************************************** void pascal SMINT_Handler(USHORT Code) { int i; // Handle return from INT callback if (VSM_Buffer) { INT_Return(); return; } switch (Code) { case SYS_BIOS_INIT: // If installing VSA from DOS, restore descriptors to default state if (Saved_EBX != 0) { ReInit_Descriptors(); } // VSA Initialization Init_SysMgr(); if (Saved_EBX == 0) { break; } case SYS_END_OF_POST: Current_VSM = 0; End_of_POST = 1; // // Take a snapshot of the interrupt vectors // for (i = 0; i < MAX_INT; i++) { if (INT_Vectors[i] == 0) { INT_Vectors[i] = read_flat((ULONG) i << 2 ); } } // Entry point to video ROM INT_Vectors[1] = 0xC0000003; // // The BIOS may have enabled the changed the cache setting // since early init, so update each VSM's CR0 field // Update_VSMs_CR0(); // Send a phase 1 initialization message to each VSM Broadcast_SysMgr_Msg(MSG_INITIALIZE, END_OF_POST_INIT); break; case SYS_REMOVE: Unregister_VSM_Events(Saved_ECX); break; case SYS_VSM_INSTALL: // EBX points to the new VSM // ECX points to the old VSM Unregister_VSM_Events(Saved_ECX); Record_VSM_Locations(); // // Send both phase 0 & 1 initialization messages to the new VSM // MsgPacket[0] = EARLY_INIT; MsgPacket[1] = 1; Send_Message(SysMgr_VSM, Saved_EBX, MSG_INITIALIZE); MsgPacket[0] = END_OF_POST_INIT; MsgPacket[1] = 1; Send_Message(SysMgr_VSM, Saved_EBX, MSG_INITIALIZE); break; default: // // Send event to appropriate VSM // MsgPacket[1] = (ULONG)Code; MsgPacket[2] = Saved_EBX; MsgPacket[3] = Saved_ECX; Send_Synchronous_Event(EVENT_SOFTWARE_SMI, 0); break; } // end switch } //*********************************************************************** // This routine handles graphics events. //*********************************************************************** void VG_Handler(void) { register SmiHeader * SmiHdr; SmiHdr = Get_Header_Params(0); // Set bit 24 of MsgPacket[2] if I/O write if (SmiHdr->SMI_Flags.IO_Write) { MsgPacket[2] |= 0x01000000L; } MsgPacket[1] = Video_Sources; Send_Synchronous_Event(EVENT_GRAPHICS, SmiHdr); // Reset video event flags Video_Sources = 0; } //*********************************************************************** // This routine handles A20 //*********************************************************************** void A20_Handler(void) { A20_Sync(); // Send event so it will be recorded in the history buffer // Send_Synchronous_Event(EVENT_A20, SMM_Header); } //*********************************************************************** // This routine handles reset //*********************************************************************** void Reset_Handler(void) { // Schedule reset routine after VSMs have processes MSG_SHUTDOWN Schedule_VSM((ULONG)((USHORT)set_reset_state)); // Tell each VSM to get ready for cold boot // No VSMs use this message at this time, so don't broadcast message // (to keep overhead down) // Broadcast_SysMgr_Msg(MSG_SHUTDOWN, 0); } //*********************************************************************** // This routine handles NMI //*********************************************************************** void NMI_Handler(void) { Send_Asynchronous_Event(EVENT_NMI); } //*********************************************************************** // This routine handles trapped PCI events. // The event may come from an SSMI (CPU) or an external SMI (chipset) //*********************************************************************** void PCI_Handler(void) { ULONG ID_Select, Data; USHORT Address; UCHAR Size; register SmiHeader * SmiHdr; SmiHdr = Get_Header_Params(SMI_SRC_PCI_TRAP); Address = (USHORT)MsgPacket[2]; Size = (UCHAR)SmiHdr->data_size; ID_Select = 1L << ((Address >> 11) & 0x1F); if (SmiHdr->SMI_Flags.IO_Write) { // // Trapped PCI header WRITE // Data = MsgPacket[3]; // Is it is a totally virtual PCI header ? if (ID_Select & Virtualized_PCI_Devices) { MsgPacket[3] = Virtual_PCI_Write_Handler(Address, Size, Data); Address &= 0xFFFC; Size = DWORD_IO; } // Set the write flag Size |= IO_WRITE; } else { // // Trapped PCI header READ // // Is it is a totally virtual PCI header ? if (ID_Select & Virtualized_PCI_Devices) { Data = Virtual_PCI_Read_Handler(Address); } else { Trap_PCI_IDSEL(Address, 0); out_32(PCI_CONFIG_ADDRESS, 0x80000000 | Address); Data = in_32(PCI_CONFIG_DATA); Trap_PCI_IDSEL(Address, 1); } // Return virtualized PCI device value to the right environment Return_Virtual_Value(SmiHdr, Data); MsgPacket[3] = Data; } // Repackage the parameters MsgPacket[2] = MsgPacket[1] << 16; MsgPacket[1] = 0x80000000 + (USHORT)Address; (USHORT)MsgPacket[2] = Size; // Send EVENT_PCI_TRAP message if (!Send_Synchronous_Event(EVENT_PCI_TRAP, SmiHdr)) { // This PCI register was not trapped. // Re-issue configuration writes to real PCI hardware devices. if (Size & IO_WRITE) { if (!(ID_Select & Virtualized_PCI_Devices)) { USHORT PCI_Data_Reg; // Disable trapping for this device Trap_PCI_IDSEL(Address, 0); // Re-issue the configuration write to the h/w device out_32(PCI_CONFIG_ADDRESS, 0x80000000 | Address); PCI_Data_Reg = PCI_CONFIG_DATA + (Address & 3); switch (Size & ~IO_WRITE) { case BYTE_IO: out_8(PCI_Data_Reg, (UCHAR)Data); break; case WORD_IO: out_16(PCI_Data_Reg, (USHORT)Data); break; case DWORD_IO: out_32(PCI_Data_Reg, Data); break; } // Re-enable trapping for this device Trap_PCI_IDSEL(Address, 1); } } } } //*********************************************************************** // This routine handles USB1 events. //*********************************************************************** void USB1_Handler(void) { Send_OHCI_Event(1); } //*********************************************************************** // This routine handles USB2 events. //*********************************************************************** void USB2_Handler(void) { Send_OHCI_Event(2); } //*********************************************************************** // This routine handles the CS5536's KEL events //*********************************************************************** void KEL_Handler(void) { Send_OHCI_Event(1); } //*********************************************************************** // This routine handles a BLOCKIO event (PIO to ATA during UDMA). //*********************************************************************** void BLOCKIO_Handler(void) { SmiHeader * SmiHdr; SmiHdr = Get_Header_Params(SMI_SRC_BLOCKIO); Send_Synchronous_Event(EVENT_BLOCKIO, SmiHdr); } //*********************************************************************** // This routine handles hits on MBus descriptors. //*********************************************************************** void Descr_Hit_Handler(void) { USHORT Address; SmiHeader * SmiHdr; SmiHdr = Get_Header_Params(SMI_SRC_DESCR_HIT); // Ignore if one of the other sources of SSMI_FLAGS if (!SmiHdr->SMI_Flags.Ext_IO_Trap && !SmiHdr->SMI_Flags.IO_Trap) { return; } Address = (USHORT)SmiHdr->IO_addr; // Handle virtual registers if ((Address & 0xFFFC) == (HostBridge_Hdr[BAR0/4].Value_LO)) { if (SmiHdr == &SMM_Header) { // Handle virtual register VR_Handler(SmiHdr); } else { Report_VSM_Error(ERR_NESTED_ACCESS, 0, 0); } return; } // Handle workaround for PM Support registers if ((Address & (USHORT)ISA_Hdr[BAR4/4].Mask) == (ISA_Hdr[BAR4/4].Value_LO)) { ACPI_Workaround(SmiHdr, 0); } // Handle workaround for ACPI registers if ((Address & 0xFFE0) == (ISA_Hdr[BAR5/4].Value_LO)) { ACPI_Workaround(SmiHdr, 1); } // Send the event Send_Synchronous_Event(EVENT_IO_TRAP, SmiHdr); } //*********************************************************************** // This routine handles statistic counter ASMIs //*********************************************************************** void StatCntr_Handler(void) { UCHAR StartIndex = 0; USHORT Address; ULONG SFlag; while (Stats_Sources) { SFlag = 1L << BitScanForward(Stats_Sources); Address = Get_Timeout(SFlag, &StartIndex); if (Address) { (USHORT)MsgPacket[2] = Address; Send_Asynchronous_Event(EVENT_IO_TIMEOUT); } else { // Clear status bit Stats_Sources &= ~SFlag; } } } //*********************************************************************** // This routine handles the Southbridge's PIC events //*********************************************************************** void PIC_Handler(void) { USHORT ExpiredTimerMask, Timer; // Need to read PIC registers to determine if one of: // USB1, USB2, S/W Generated, RTC Alarm, Audio, PM, NAND Flash, // SMB, KEL, UART1, UART2, MFGPT comparator, or GPIO. CS5536_GPIO_Handler(0); // Check if any MFGPT events occurred ExpiredTimerMask = CS5536_MFGPT_Handler(); Timer = 0; while (ExpiredTimerMask) { if (ExpiredTimerMask & 1) { Timer_Handler(Timer); } Timer++; ExpiredTimerMask >>= 1; } } //*********************************************************************** // This routine handles the CS5536's ACPI events //*********************************************************************** void ACPI_Handler(void) { SmiHeader * SmiHdr; SmiHdr = Get_Header_Params(0); // Handle mis-aligned access to the PM1_CNT register while ((UCHAR)SmiHdr->IO_addr != 0x08) { (UCHAR)SmiHdr->IO_addr++; (UCHAR)SmiHdr->data_size >>= 1; SmiHdr->write_data >>= 8; } if ((UCHAR)SmiHdr->data_size == 0x07) { (UCHAR)SmiHdr->data_size = WORD_IO; } Send_Synchronous_Event(EVENT_ACPI, SmiHdr); } //*********************************************************************** // This routine handles the CS5536's Power Management Events //*********************************************************************** void PME_Handler(void) { // Handle GPIOs that are routed to PM logic CS5536_GPIO_Handler(0); // Filter any false event caused by enabling PME if (Get_ACPI_Status(MsgPacket)) { Send_Synchronous_Event(EVENT_PME, 0); } } //*********************************************************************** // This routine handles events for which no other handler applies. //*********************************************************************** void Leftover_Handler(void) { // Report that the event was not handled. Log_Error("Unhandled event"); } //************************************************************************* // // The SMI_Sources table is used for determining the proper handler for // a top-level SMI source. Note that a single handler may process multiple // SMI sources. The order of entries in this table governs the order that // the handlers will be invoked. This is NOT the order that the VSMs will // be given control. Therefore, the order of entries is unimportant with // respect to controlling priority. However, in terms of finding a match // more quickly, the more frequent SMI events should be placed earlier in // the table. // //************************************************************************* SMI_ENTRY Handler_Table[] = { PCI_Handler, SMI_SRC_PCI_TRAP, VG_Handler, SMI_SRC_VG, USB1_Handler, SMI_SRC_USB1, USB2_Handler, SMI_SRC_USB2, A20_Handler, SMI_SRC_A20, Reset_Handler, SMI_SRC_RESET, NMI_Handler, SMI_SRC_NMI, Descr_Hit_Handler, SMI_SRC_DESCR_HIT, PIC_Handler, SMI_SRC_PIC, StatCntr_Handler, SMI_SRC_STAT, KEL_Handler, SMI_SRC_KEL, ACPI_Handler, SMI_SRC_ACPI, PME_Handler, SMI_SRC_PME, BLOCKIO_Handler, SMI_SRC_BLOCKIO, Leftover_Handler, 0xFFFFFFFF, };