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