Author: wmb Date: 2008-11-18 00:50:36 +0100 (Tue, 18 Nov 2008) New Revision: 1008
Modified: dev/geode/smi.fth Log: Commented the Geode SMI code.
Modified: dev/geode/smi.fth =================================================================== --- dev/geode/smi.fth 2008-11-17 20:53:53 UTC (rev 1007) +++ dev/geode/smi.fth 2008-11-17 23:50:36 UTC (rev 1008) @@ -1,14 +1,16 @@ purpose: SMI setup and handler for Geode LX
+\ Extend the assembler with some Geode-specific (I think) instructions +\ related to SMI handling. + also assembler definitions - : smint ( -- ) prefix-0f h# 38 asm8, ; : rsm ( -- ) prefix-0f h# aa asm8, ; : svdc ( sr m80 -- ) prefix-0f h# 78 asm8, rot r/m, ; : rsdc ( m80 sr -- ) prefix-0f h# 79 asm8, r/m, ; - previous definitions
+\ Location of the SMM handler code... \ The general naming convention here is that smm-* refers to \ addresses within the memory that is set aside for SMI handling. \ smi-* refers to stuff in the Forth domain. @@ -18,6 +20,13 @@ : +smm ( segment-relative-adr -- adr ) smm-base + ; : -smm ( adr -- segment-relative-adr ) smm-base - ;
+\ This is a trick for using SMM to handle BIOS INTs. The problem it solves +\ is that Windows sometimes calls the BIOS from V86 mode instead of real mode. +\ V86 mode prevents easy entry into protected mode (and we want to run OFW +\ in protected mode), so we first trap into SMM by accessing some emulated +\ registers, and run OFW code from SMM protected mode. The following is a +\ table of "INT handler" instruction sequences indexed by the INT number. + label int-entry 16-bit al h# 30 # out iret nop @@ -40,6 +49,8 @@ here int-entry - constant /int-entry
+\ Data structures for the SMM gateway + h# 28 constant /smm-gdt h# 8 constant smm-c16 h# 10 constant smm-d16 @@ -101,6 +112,13 @@ h# 400 pm-stack smm-rp0 \ SMM Forth return stack drop
+ +\ The basic SMI gateway. This code lives at (is copied to) smm-base. +\ It executes when the processor enters System Management Mode (SMM) +\ for whatever reason. It saves a bunch of state, sets up the world +\ so Forth code can run (in 32-bit protected mode), and runs the Forth +\ handler - typically "smi-dispatch" (via smm-exec and handle-smi). + label smi-handler 16-bit
@@ -178,6 +196,10 @@
cld c; + +\ When the Forth SMI handler finishes, it calls (smi-return) to return +\ to the context that invoked the SMI. This is the inverse of smi-handler. + code (smi-return) \ This code field must be relocated after copying to SMM memory cli
@@ -231,6 +253,10 @@ end-code here smi-handler - constant /smi-handler
+\ Access words for fields within the processor state that the +\ Geode CPU saves on entry to SMM. See the description of the +\ RSM instruction in the Geode LX databook (section 8.3.4.7). + : smm-dr7 ( -- l ) smm-header h# 4 - l@ ; : smm-eflags ( -- l ) smm-header h# 8 - l@ ; : smm-cr0 ( -- l ) smm-header h# c - l@ ; @@ -256,11 +282,16 @@ : smm-eax ( -- adr ) smm-gregs 7 la+ ; : smm-ebp ( -- adr ) smm-gregs 2 la+ ;
+\ Finds a page table or page directory entry +\ Implementation factor of (smm>physical) : >ptable ( table vadr shift -- table' unmapped? ) rshift h# ffc and + l@ dup h# fff invert and swap 1 and 0= ;
+\ Converts a virtual address to a physical address via the page tables +\ This is used by debugging tools, so that we can look at OS resources +\ via their virtual addresses while we are running with paging disabled. \ XXX need to handle mapped-at-pde-level defer smm>physical : (smm>physical) ( vadr -- padr ) @@ -288,15 +319,23 @@ : use-physical ( -- ) ['] noop to smm>physical ; : use-virtual ( -- ) ['] (smm>physical) to smm>physical ;
+\ Some simple glue code to help make the transition from assembly language +\ to Forth and back.
: smi-return ( -- ) [ ' (smi-return) smi-handler - +smm ] literal execute ; defer handle-smi ' noop is handle-smi create smm-exec ] handle-smi smi-return [
+\ Set to true to display brief messages showing every entry to SMM false value smi-debug? + +\ Set to true to invoke the Forth debugger when the OS tries to suspend (S3) false value resume-debug?
+\ Set to true to show all the OS's PCI config space accesses false value vpci-debug? + +\ Turns on PCI virtualization (called from setup-smi) : enable-virtual-pci ( -- ) \ Virtualize devices f and 1, or all devices if debugging vpci-debug? if h# ffff else h# 8002 then >r @@ -304,6 +343,9 @@ h# 5000.2002 msr@ swap 8 invert and swap h# 5000.2002 msr! \ Enable SSMI for config accesses ;
+\ Some tools for dispatching to the right sub-handler, by reading and ACKing +\ the various MSRs that tell you the cause of this SMI. + \ : msr-ack ( msr# -- ) >r r@ msr@ r> msr! ; code msr-ack ( msr# -- ) cx pop rdmsr wrmsr c; code msr@! ( msr# -- d.value ) cx pop rdmsr wrmsr ax push dx push c; @@ -337,6 +379,12 @@ c;
alias msr. .msr + +\ Turns on I/O register virtualization (called by setup-smi) +\ We virtualize ACPI registers and the 0xAC1C "virtual register". +\ The "virtual register" is not a legacy hardware register, but +\ rather an API to call the VSA module to do various things. + : enable-io-smis ( -- ) \ XXX these settings need to be folded into the MSR table for resume h# 0000.0009.c00fffc0. h# 5101.00e4 msr! \ Virtualize ACPI registers @@ -361,6 +409,10 @@ h# ff00 h# 4000.0082 msr-clr h# ff00 h# 4000.0083 msr-clr h# 38. h# 1301 msr! ; + +\ Some debugging tools to display MSRs related to SMI dispatch +\ These are useful for figuring out how to turn on SMIs for various things. + \ 10002002 records ac1c accesses in bit 0 (1) \ 51010002 records 9c00 accesses in bit 32 (1.0000.0000) \ 51000002 records 9c00 accesses in bit 18 ( 4.0000) @@ -390,6 +442,7 @@ h# 4000 .msr4 ." " h# 1301 msr.. cr ; +\ Ack all the SMI dispatch MSRs : ma h# 10002002 msr-ack h# 10002003 msr-ack h# 40002002 msr-ack h# 40002003 msr-ack @@ -400,15 +453,31 @@ \ h# 51010083 msr-ack \ This one has status bits, but they are RO ;
+\ This is a stub SMI handler that just invokes the Forth command +\ interpreter so you can poke around. Normally handle-smi calls +\ smi-dispatch to do all the virtualization work, instead of this. + : smi-interact ( -- ) ." In SMI" cr interact ; ' smi-interact is handle-smi
: vr-spoof? ( -- handled? ) false ;
+\ Handler for PCI config space virtualization. +\ This determines the PCI config register number and the access size +\ from the information saved by the SMI gateway, then calls OFW's +\ PCI config access functions to do the work. + : pci-smi ( event-mask -- ) smi-debug? if ." PCI" cr then + + \ Error checks 8 and 0= if exit then smm-io-port 3 invert and h# cfc <> if exit then + + \ If we are tracing all PCI config accesses, not just the ones that + \ don't really exist, we must turn off virtualization while Forth + \ is accessing the "real" PCI config registers, otherwise we'll + \ hang due to recursion. vpci-debug? if h# 5000.2012 msr@ 2>r 0. h# 5000.2012 msr! then
\ The existing Forth config spoofer does the hard work @@ -429,10 +498,18 @@ h# f of config-l@ smm-eax l! endof endcase then + + \ Reinstate virtualization vpci-debug? if 2r> h# 5000.2012 msr! then ; + : smm-eax-c! ( b -- ) smm-eax c! ; : smm-eax-c@ ( -- b ) smm-eax c@ ; + +\ "SoftVG" display functions (subset) as documented in +\ _AMD Geode(TM) GX Processors Graphics Software Specification" +\ (available to OEMs via AMD's restricted web site) + 0 value requested-mode 0 value color-depth \ 1:8bpp 2:XRGB444 3:RGB555 4:RGB565 5:24bpp 0 value refresh-rate @@ -501,7 +578,16 @@ ; [then]
+\ Partial implementation of 0xAC1C Virtual Registers as documented in +\ _AMD Geode(TM) GX and LX Processor Based Systems Virtual Register Specification_ +\ (available to OEMs via AMD's restricted web site) +\ Ironically, none of the VR's that we actually implement are actually +\ described in that document; most are described in the Graphics Software +\ spec mentioned earlier. + 0 value vr-index + +\ Set to true to display accesses to 0xAC1C Virtual Registers false value vr-debug?
true value vr-locked? @@ -544,10 +630,17 @@ endcase then ; + +\ The GLIU0 SMI encompasses Virtual Register (0xAC1C) accesses and +\ also accesses to the 0x30..0x3f I/O port bank that we "steal" for +\ bouncing BIOS INTs into SMM. + : gliu0-smi ( event-mask -- ) smi-debug? if ." GLIU" cr then 1 and 0= if exit then \ We only care about virtual register accesses
+ \ Handle BIOS INTs that were bounced into SMM by accessing + \ I/O ports 0x30..0x3f smm-io-port h# fff0 and h# 30 = if smm-io-port h# 20 - to rm-int@ rm-sp la1+ >caller-physical w@ smm-rmeflags w! @@ -560,14 +653,23 @@ do-vr ;
+\ This hook can be set to turn off devices that the firmware uses. +\ It is called when the OS takes responsibility for power management +\ by writing a 1 to bit 0 of the ACPI PM1_CNT . + defer quiesce-devices ' noop to quiesce-devices
\ We just discard the event about I/O registers because we handle it in sb-smi . \ We don't have to deal with statistics counters because we don't enable them : cgliu-smi ( event-mask -- ) smi-debug? if ." CGLIU" cr then drop ;
+\ Trap into SMM from Forth code smi smint c;
+\ This is a gateway to get into real mode by going through SMM . +\ It's used to implement the ACPI "resume from S3" semantics that +\ require jumping to a given address in real mode. + -1 value rm-entry-adr : rm-run ( eip -- ) to rm-entry-adr smi ;
@@ -629,6 +731,8 @@ smi-return ;
+\ Call this to enable SMI support + : setup-smi ( -- ) \ This is how you would map the SMM region to physical memory at 4000.0000 \ This is a Base Mask Offset descriptor - the base address @@ -675,6 +779,10 @@ enable-io-smis ;
+\ Implement the ACPI S3 (suspend to RAM) semantics using the OFW +\ "S3 looks like a subroutine call" semantics. This requires +\ some fancy mode switching. + : win-s3 ( -- ) \ The trick here is to transfer to "non-smi-s3" while leaving \ System Management Mode. We won't need to return to the caller @@ -689,6 +797,14 @@ facs-adr h# c + l@ rm-run ;
+\ Emulate the ACPI PM_CNT power management control register semantics; +\ the 5536 partially implements that register in hardware, but software +\ must handle a lot of the details, particularly bits 0 and 12:10 +\ The way we do this is by telling the OS that the register bank starts +\ at port 9c00, but it really starts at port 1840. We trap accesses +\ to the 9cxx range, run the emulation code, then the emulation code +\ accesses the real register as needed. + : power-mode ( value offset -- ) over 1 and if quiesce-devices then ( value offset )
@@ -703,11 +819,15 @@ then ( ) ;
+\ Handler for emulated port 92 for system reset + : divil-smi ( event-mask -- ) smi-debug? if ." DIVIL" cr then h# 80 and if bye then ;
+\ Handler for southbridge SMIs - ACPI registers + : sb-smi ( event-mask -- ) smi-debug? if ." SB" cr then 4 and 0= if exit then \ We only care about virtualized I/O registers @@ -785,12 +905,18 @@ then ;
+\ SMI breakpoints - simple (but very useful) debugging tool. + variable sbpval variable sbpadr sbpadr off defer sbp-hook
: .9x push-hex 9 u.r pop-base ; : .smir ( n -- ) smm-gregs swap la+ l@ .9x ; + + +\ Displays the saved register values + : .smiregs ( -- ) ." EAX EBX ECX EDX ESI EDI EBP ESP" cr 7 .smir 4 .smir 6 .smir 5 .smir 1 .smir 0 .smir 2 .smir smm-save-esp +smm l@ .9x @@ -811,6 +937,10 @@ dup 5 - c@ h# e8 <> if 2drop exit then ( retadr pretadr ) 4 - l@ + 9 u.r ; + +\ Displays a subroutine call backtrace. This pretty dependent on compiler +\ code generation rules, so it might not work in some cases. + : smm-trace ( -- ) ." EBP RETADR CALLTO" cr smm-ebp @@ -829,14 +959,28 @@ ;
: unboost ( adr -- adr' ) h# 7fff.ffff and ; + +\ Set an SMI breakpoint at "adr". You can only set one breakpoint. +\ "adr" is either virtual or physical depending on whether you have +\ previously executed "use-physical" or "use-virtual" + : sbp ( adr -- ) fixsbp dup smm>physical w@ sbpval ! dup sbpadr ! h# 380f swap smm>physical w! ; + +\ Disassemble starting from the breakpoint address + : sdis ( -- ) smm-pc smm>physical dis ; + +\ Resume execution of the breakpointed code + : sgo ( -- ) smm-pc smm-next-eip! resume ;
+\ This is a workaround for a problem with video mode setting from V86 mode +\ The problem was eventually fixed properly so this probably isn't needed + h# 806f1a41 constant 'bioscall : hack-fix-mode ( -- ) 'bioscall smm>physical 5 h# 90 fill \ Nop-out "call hal!HalpBiosCall" that dies @@ -848,6 +992,11 @@
code rm-lidt ( -- ) smm-rmidt #) lidt c;
+\ rm-setup fudges the saved SMM state so that, instead of returning +\ to the context that invoked the SMI, the next exit from SMM returns +\ to the address "eip" in real mode. This is an implementation +\ factor of the "rm-run" mechanism. + : rm-setup ( eip -- ) >seg:off 2>r ( r: off seg )
@@ -884,6 +1033,10 @@ ; \ : rm-init-program ( eip -- ) rm-init-program rm-return ;
+\ Handler for software SMIs, i.e. for explicit execution of the +\ SMINT instruction. It's used for things like the rm-run facility +\ and SMI breakpoints. + : soft-smi ( -- ) smi-debug? if ." SOFT" cr then rm-entry-adr -1 <> if @@ -908,6 +1061,10 @@ then ;
+\ smi-dispatch is the top-level dispatcher for SMIs. It looks +\ at various MSRs to determine the SMI cause and invokes the +\ corresponding subordinate handlers. + \ smm-flags values in various cases: \ 8080 (VGA) - ac1c read (VR), extended CRT register (dc-smi) \ 8020 (Mem read) - 9cxx (power management) register (sb-smi) @@ -952,6 +1109,8 @@ drop ;
+\ Debugging tool for displaying the saved Global Descriptor Table + : .smm-gdt ( -- ) smm-save-gdt +smm dup 2+ l@ smm>physical ( 'gdt gdt-adr ) swap w@ ( gdt-adr gdt-limit ) @@ -1056,6 +1215,9 @@
[then]
+\ setup-rm-gateway initializes the real mode interrupt vector table +\ so that real mode code can call BIOS INTs. + \ setup-rm-gateway ( -- ) Init this module \ caller-regs ( -- adr ) Base address of incoming registers \ rm-int@ ( -- n ) Incoming interrupt number