Author: wmb Date: 2009-10-16 01:07:26 +0200 (Fri, 16 Oct 2009) New Revision: 1421
Added: dev/via/smi.fth Modified: cpu/x86/pc/biosints.fth dev/geode/smi.fth Log: Via - initial checkin of SMI support.
Modified: cpu/x86/pc/biosints.fth =================================================================== --- cpu/x86/pc/biosints.fth 2009-10-12 20:41:07 UTC (rev 1420) +++ cpu/x86/pc/biosints.fth 2009-10-15 23:07:26 UTC (rev 1421) @@ -4,23 +4,6 @@ 8 /l* buffer: init-regs h# 80 value bios-boot-dev#
-struct - 2 field >rm-gs - 2 field >rm-fs - 2 field >rm-es - 2 field >rm-ds - 4 field >rm-edi - 4 field >rm-esi - 4 field >rm-ebp - 4 field >rm-exx - 4 field >rm-ebx - 4 field >rm-edx - 4 field >rm-ecx - 4 field >rm-eax - 4 field >rm-retaddr - 2 field >rm-flags -drop - : rm-es@ caller-regs >rm-es w@ ; : rm-es! caller-regs >rm-es w! ;
Modified: dev/geode/smi.fth =================================================================== --- dev/geode/smi.fth 2009-10-12 20:41:07 UTC (rev 1420) +++ dev/geode/smi.fth 2009-10-15 23:07:26 UTC (rev 1421) @@ -1303,8 +1303,20 @@ loop ;
- -\ : caller-regs ( -- adr ) smm-sregs ; -\ : rm-buf ( -- adr ) smm-rmbuf ; - -\ : doit setup-smi disk-name open-dev is disk-ih get-mbr usb-quiet ff 21 pc! h# 380f h# 7c18 w! smi ; +\ Layout of saved registers +struct + 2 field >rm-gs + 2 field >rm-fs + 2 field >rm-es + 2 field >rm-ds + 4 field >rm-edi + 4 field >rm-esi + 4 field >rm-ebp + 4 field >rm-exx + 4 field >rm-ebx + 4 field >rm-edx + 4 field >rm-ecx + 4 field >rm-eax + 4 field >rm-retaddr + 2 field >rm-flags +drop
Added: dev/via/smi.fth =================================================================== --- dev/via/smi.fth (rev 0) +++ dev/via/smi.fth 2009-10-15 23:07:26 UTC (rev 1421) @@ -0,0 +1,693 @@ +purpose: SMI setup and handler for Intel-compatible system management mode + +also assembler definitions +: rsm ( -- ) prefix-0f h# aa asm8, ; +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. + +h# 3.8000 constant smm-base0 \ Default after power up +h# a.0000 constant smm-base \ We switch to this - hidden behind the VGA frame buffer +h# 1.0000 constant smm-size + +: +smm-offset h# 8000 + ; +: +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 + al h# 31 # out iret nop + al h# 32 # out iret nop + al h# 33 # out iret nop + al h# 34 # out iret nop + al h# 35 # out iret nop + al h# 36 # out iret nop + al h# 37 # out iret nop + al h# 38 # out iret nop + al h# 39 # out iret nop + al h# 3a # out iret nop + al h# 3b # out iret nop + al h# 3c # out iret nop + al h# 3d # out iret nop + al h# 3e # out iret nop + al h# 3f # out iret nop +end-code +here int-entry - constant /int-entry + +\ Data structures for the SMM gateway + +h# 28 constant /smm-gdt \ GDT size - 4 entries plus nonce entry at beginning + +\ Protected mode selector values +h# 8 constant smm-c16 +h# 10 constant smm-d16 +h# 18 constant smm-c32 +h# 20 constant smm-d32 + +\ For stuff that grows down - add the offset first +: smm-data ( offset "name" -- offset' ) constant ; +: pm-data ( offset "name" -- offset' ) +smm constant ; + +\ Layout of SMM memory area: +h# 8000 smm-data smm-gdt \ Entry/exit handler code at the entry offset + \ The GDT is embedded in the code wad + \ The handler code takes about h# 160 bytes + +h# f400 pm-data smm-sp0 \ SMM Forth data stack - h# 400 bytes +h# f800 pm-data smm-rp0 \ SMM Forth return stack - h# 400 bytes + +h# fc00 pm-data 'int10-dispatch \ Array of instruction sequences for bouncing INTs through SMI + +\ fcxx - fcff available + +\ These locations are set once at installation time. The entry code reads them. +h# fd00 pm-data smm-pdir \ Page directory pointer so we can enable paging +h# fde4 pm-data smm-forth-base \ Base address of the Forth dictionary +h# fde8 pm-data smm-forth-up \ Base address of the Forth user area +h# fdec pm-data smm-forth-entry \ Entry address of Forth SMI handler +h# fdf0 pm-data smm-save-sp0 \ Exchanged with sp0 user variable +h# fdf4 pm-data smm-save-rp0 \ Exchanged with rp0 user variable + +\ The following locations are for saving/restoring registers + +h# fdf8 pm-data smm-save-cf8 \ Saved value of PCI config index + +\ The following locations are defined by the CPU + +\ h# fe00 .. 7ef7 - reserved + +h# fef8 smm-data smm-smbase +h# fefc smm-data smm-revid +h# fff0 smm-data smm-io-restart +h# ff02 smm-data smm-hlt-restart + +h# ff04 smm-data smm-io-restart-edi +h# ff08 smm-data smm-io-restart-ecx +h# ff0c smm-data smm-io-restart-esi +h# ff10 smm-data smm-io-restart-eip + +h# ff28 smm-data smm-cr4 + +h# ff30 smm-data smm-es-limit +h# ff34 smm-data smm-es-base +h# ff38 smm-data smm-es-access + +h# ff3c smm-data smm-cs-limit +h# ff40 smm-data smm-cs-base +h# ff44 smm-data smm-cs-access + +h# ff48 smm-data smm-ss-limit +h# ff4c smm-data smm-ss-base +h# ff50 smm-data smm-ss-access + +h# ff54 smm-data smm-ds-limit +h# ff58 smm-data smm-ds-base +h# ff5c smm-data smm-ds-access + +h# ff60 smm-data smm-fs-limit +h# ff64 smm-data smm-fs-base +h# ff68 smm-data smm-fs-access + +h# ff6c smm-data smm-gs-limit +h# ff70 smm-data smm-gs-base +h# ff74 smm-data smm-gs-access + +h# ff84 smm-data smm-gdtr-limit + +h# ff88 smm-data smm-gdtr-base +h# ff8c smm-data smm-gdtr-access + +h# ff90 smm-data smm-idtr-limit +h# ff94 smm-data smm-idtr-base +h# ff98 smm-data smm-idtr-access + +h# ff9c smm-data smm-tr-limit +h# ffa0 smm-data smm-tr-base +h# ffa4 smm-data smm-tr-access + +h# ffa8 smm-data smm-es +h# ffac smm-data smm-cs +h# ffb0 smm-data smm-ss +h# ffb4 smm-data smm-ds +h# ffb8 smm-data smm-fs +h# ffbc smm-data smm-gs +h# ffc0 smm-data smm-ldtr +h# ffc4 smm-data smm-tr +h# ffc8 smm-data smm-dr7 +h# ffcc smm-data smm-dr6 +h# ffd0 smm-data smm-eax +h# ffd4 smm-data smm-ecx +h# ffd8 smm-data smm-edx +h# ffdc smm-data smm-ebx +h# ffe0 smm-data smm-esp +h# ffe4 smm-data smm-ebp +h# ffe8 smm-data smm-esi +h# ffec smm-data smm-edi +h# fff0 smm-data smm-eip +h# fff4 smm-data smm-eflags +h# fff8 smm-data smm-cr3 +h# fffc smm-data smm-cr0 + +\ The basic SMI gateway. This code lives at (is copied to) smm-base + h# 8000. +\ 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 + + \ GDT (with jump tucked in at the beginning) + \ We put the GDT right at the beginning and use the first entry (which + \ cannot be used as a selector) for a 2-byte jmp and the 6-byte GDT pointer + here /smm-gdt + #) jmp \ Jump past GDT - 2 bytes + + /smm-gdt 1- w, smm-base +smm-offset l, \ GDT pointer - limit.w base.l + + smm-base smm-size 1- code16 format-descriptor swap l, l, \ 8 - smm-c16 + smm-base smm-size 1- data16 format-descriptor swap l, l, \ 10 - smm-d16 + 0 -1 code32 format-descriptor swap l, l, \ 18 - smm-c32 + 0 -1 data32 format-descriptor swap l, l, \ 20 - smm-d32 + \ End of GDT + + cs ax mov ax ds mov + +wbinvd + +\ Get into protected mode using the same segments +\ Don't bother with the IDT; we won't enable interrupts + op: smm-gdt 2+ #) lgdt + +\ ascii a report + + cr0 ax mov 1 # al or ax cr0 mov \ Enter protected mode + + op: here 7 + smi-handler - +smm +smm-offset smm-c32 #) far jmp + 32-bit + + \ Reload segment registers with protected mode selectors + op: smm-d32 # ax mov + ax ds mov ax es mov ax fs mov ax gs mov ax ss mov + +[ifdef] virtual-mode + \ Turn on paging + smm-pdir #) ax mov ax cr3 mov \ Set Page Directory Base Register + cr4 ax mov h# 0000.0010 # ax or ax cr4 mov \ Turn on PSE bit (allow 4M pages) + cr0 ax mov h# 8000.0000 # ax or ax cr0 mov \ Turn on Paging Enable bit +[then] + + h# cf8 # dx mov \ Save PCI config address + dx ax in + ax smm-save-cf8 #) mov + +\ Beginning of Forth-specific stuff + smm-forth-base #) bx mov + smm-forth-up #) up mov + smm-forth-entry #) ip mov + + \ Exchange the stack and return stack pointer with the smi versions + 'user sp0 sp mov smm-save-sp0 #) sp xchg sp 'user sp0 mov + 'user rp0 rp mov smm-save-rp0 #) rp xchg rp 'user rp0 mov + + 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 + + \ Exchange the stack and return stack pointer with the smi versions + 'user sp0 sp mov smm-save-sp0 #) sp xchg sp 'user sp0 mov + 'user rp0 rp mov smm-save-rp0 #) rp xchg rp 'user rp0 mov + + \ End of Forth-specific stuff + + smm-save-cf8 #) ax mov \ Restore PCI config address + h# cf8 # dx mov + ax dx out + +[ifdef] virtual-mode + \ Turn off paging + cr0 ax mov h# 8000.0000 invert # ax and ax cr0 mov \ Turn off Paging Enable bit +[then] + + here 7 + smi-handler - +smm-offset smm-c16 #) far jmp \ Get into the boosted segment + 16-bit + + \ Now we are in protected mode executing from a 16-bit code segment + \ whose selector has a base of A0000. + + smm-d16 # ax mov ax ss mov ax ds mov \ Reload data and stack segments + + cr0 ax mov 1 invert # al and ax cr0 mov \ Exit protected mode + here 7 + smi-handler - +smm-offset smm-base 4 rshift #) far jmp \ Set CS for real mode + + wbinvd + + rsm +end-code +here smi-handler - constant /smi-handler + +: smm@ ( offset -- n ) +smm @ ; + +\ Address of segment registers +: 'smm-eax ( -- adr ) smm-eax +smm ; + +\ 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 ) +\ smm-cr0 smm@ h# 8000.0000 and 0= if exit then + cr3@ ( vadr pdir ) + over d# 20 >ptable abort" Unmapped" ( vadr ptab ) + over d# 10 >ptable abort" Unmapped" ( vadr pframe ) + swap h# fff and + +; +' (smm>physical) to smm>physical + +: smm-map? ( vadr -- ) smm>physical . ; + +\ Programs that write to the caller's data space should use this, +\ as it works when called from paged V86 mode. +: >caller-physical ( vadr -- padr ) + smm-cr0 smm@ h# 8000.0000 and if (smm>physical) then +; + +\ Turn address translation on or off for the following commands, +\ so they can be used either for non-paged calling code like +\ early bootloaders or for paged code that calls to us via a +\ real mode gateway. + +: 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 +smm-offset ] 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? + +\ 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 + +: smm-eax-c! ( b -- ) 'smm-eax c! ; +: smm-eax-c@ ( -- b ) 'smm-eax c@ ; + +0 value rm-int@ + +defer handle-bios-call + +: rm-sp ( -- adr ) smm-esp +smm w@ smm-ss +smm w@ seg:off> ; + +\ bios-int-smi is for accesses to the 0x30..0x3f I/O port bank that we "steal" for +\ bouncing BIOS INTs into SMM. + +: bios-int-smi ( -- ) + \ Handle BIOS INTs that were bounced into SMM by accessing + \ I/O ports 0x30..0x3f + h# 58 acpi-w@ dup h# fff0 and h# 30 = if \ Via + h# 20 - to rm-int@ + rm-sp la1+ >caller-physical w@ smm-eflags +smm w! + \ Bias by -2 to point to INT instruction +[ifdef] notyet + rm-sp >caller-physical l@ 2- smm-retaddr l! +[then] + handle-bios-call + smm-eflags +smm w@ rm-sp la1+ >caller-physical w! + exit + else + drop + then +; + +\ 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 + +\ 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 +0 value rm-regs + +: #smint ( n -- ) h# 2f acpi-b! ; \ Via + +: rm-run ( 'gregs eip -- ) to rm-entry-adr to rm-regs h# f0 #smint ; + +defer suspend-devices ' noop to suspend-devices +defer resume-devices ' noop to resume-devices + +defer freeze-ps2 ' noop to freeze-ps2 +defer unfreeze-ps2 ' noop to unfreeze-ps2 + +\ 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 ( offset -- ) smm@ .9x ; +: .smis ( offset -- ) smm@ 5 u.r ; + + +\ Displays the saved register values + +: .smiregs ( -- ) + ." EAX EBX ECX EDX ESI EDI EBP ESP" cr + smm-eax .smir smm-ebx .smir smm-ecx .smir smm-edx .smir + smm-esi .smir smm-edi .smir smm-ebp .smir smm-esp .smir + cr + ." CS: " smm-cs .smis + ." DS: " smm-ds .smis + ." ES: " smm-es .smis + ." FS: " smm-fs .smis + ." GS: " smm-gs .smis + ." SS: " smm-ss .smis + cr +; +: .smipc ( -- ) ." SMI at " smm-eip .smir cr ; +' .smipc to sbp-hook + +: .callto ( retadr -- ) + dup ['] smm>physical catch if 2drop exit then ( retadr pretadr ) + 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 smm@ + begin ?dup while ( ebp ) + dup 8 u.r smm>physical ( padr ) + dup la1+ l@ dup 9 u.r .callto cr ( padr ) + l@ ( ebp' ) + repeat +; + +: fixsbp ( -- ) + sbpadr @ if + sbpval @ sbpadr @ smm>physical w! + sbpadr off + then +; + +: 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-eip smm@ smm>physical dis ; + +\ Resume execution of the breakpointed code + +: sgo ( -- ) ( smm-eip smm@ smm-next-eip! ) resume ; + +: l!++ ( adr l -- adr' ) over l! la1+ ; +: w!++ ( adr w -- adr' ) over w! wa1+ ; + +[ifdef] notyet +code rm-lidt ( -- ) smm-rmidt #) lidt c; +[then] + +\ 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 ( -- ) +[ifdef] notyet + rm-entry-adr >seg:off 2>r ( off seg ) + + smm-header h# 30 - ( adr ) + h# 38 l!++ ( adr' ) \ SMM_CTL + 0 l!++ ( adr' ) \ I/O DATA + 0 l!++ ( adr' ) \ I/O ADDRESS, I/O SIZE + h# 938009 l!++ ( adr' ) \ SS_FLAGS, SMM Flags + h# ffff l!++ ( adr' ) \ CS_LIMIT + r@ 4 lshift l!++ ( adr' ) \ CS_BASE + + r> h# 9a wljoin l!++ ( adr' ) \ CS_FLAGS.CS_INDEX + r> l!++ ( adr' ) \ NEXT_IP + 0 l!++ ( adr' ) \ CURRENT_IP + h# 10 l!++ ( adr' ) \ CR0 + h# 2 l!++ ( adr' ) \ EFLAGS + h# 400 l!++ ( adr' ) \ DR7 + drop + + 0 smm-esp +smm l! + + smm-save-seg +smm + h# ffff l!++ h# 9300 l!++ 0 w!++ \ DS + h# ffff l!++ h# 9300 l!++ 0 w!++ \ ES + h# ffff l!++ h# 9300 l!++ 0 w!++ \ FS + h# ffff l!++ h# 9300 l!++ 0 w!++ \ GS + h# ffff l!++ h# 9300 l!++ 0 w!++ \ SS + drop + + h# ffff smm-rmidt w! 0 smm-rmidt wa1+ l! \ Limit and base + + \ Interrupts are off because we are in SMM + rm-lidt +[then] +; +\ : 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 + h# 2f acpi-b@ h# f0 = if + rm-setup + -1 to rm-entry-adr + exit + then + +[ifdef] notyet + smm-pc smm-retaddr l! \ So .caller-regs will work + sbpadr @ if + sbpadr @ smm>physical smm-pc smm>physical <> if + ." Not at SMI breakpoint!" cr + .smipc + .smiregs + else + fixsbp + then + sbp-hook + then +[then] + + smi-interact +; + +\ 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. + +: smi-dispatch ( -- ) + h# 28 acpi-w@ ( events ) + dup h# 0040 and if soft-smi then ( events ) + dup h# 8000 and if bios-int-smi then ( events ) + h# 28 acpi-w! \ Ack all events ( ) + + h# 2d acpi-b@ h# 2d acpi-b! \ Ack the SMI +; +' smi-dispatch is handle-smi + +: .dt ( adr limit -- ) + 1+ 8 max 8 ?do ( gdt-adr ) + dup i + d@ dup if ( gdt-adr descr ) + i 3 u.r space .descriptor cr ( gdt-adr ) + else ( gdt-adr descr ) + 2drop ( gdt-adr ) + then ( gdt-adr ) + 8 +loop ( gdt-adr ) + drop +; + +\ Debugging tool for displaying the saved Global Descriptor Table + +: .smm-gdt ( -- ) + smm-gdtr-base smm@ smm>physical ( 'gdt gdt-adr ) + swap w@ ( gdt-adr gdt-limit ) + .dt ( ) +; + +\ 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 +\ rm-buf ( -- adr ) Base address of a real-mode accessible buffer +\ rm-init-program ( eip -- ) Setup to enter real mode program on next rm-return +\ rm-return ( -- ) Resume execution of real-mode caller. Returns when program does a BIOS INT. +\ Sequence: +\ setup-rm-gateway ( eip ) rm-enter begin handle-bios-call rm-return again + +: setup-rm-gateway ( -- ) \ XXX These config registers probably need to be fixed up during resume + h# 0030 h# 885e config-w! \ Set up PCS1 IO trap on ports 30-3f + h# 8864 config-b@ h# f0 or h# 8864 config-b! \ 16-byte range for PCS1 + h# 8866 config-b@ h# 02 or h# 8866 config-b! \ Enable PCS1 + + \ h# 88e4 config-b@ h# 40 or h# 88e4 config-b! \ Enable PCS0 multifunction + h# 88e5 config-b@ h# 04 or h# 88e5 config-b! \ Enable PCS1 multifunction + + h# 2b acpi-b@ h# 80 or h# 2b acpi-b! \ Enable SMI on access to PCS1 + + int-entry 'int10-dispatch /int-entry move + + \ Prime the interrupt vector table with unused interrupt 1f + h# 100 0 do + 'int10-dispatch h# f la+ i /l* seg:off! + loop + + \ Set interrupts 10-1f to go to entries 0-f in the int10-dispatch table + h# 10 0 do + 'int10-dispatch i la+ h# 10 i + /l* seg:off! + loop +; + +\ Test routine that just puts an 'S' on the serial port when an SMI happens +label show-smi + 16-bit + ascii S report + + \ Via specific + acpi-io-base h# 28 + # dx mov dx al in al dx out \ Ack all events + acpi-io-base h# 2d + # dx mov dx al in al dx out \ ACK SMI + rsm +end-code +here show-smi - constant /show-smi + +label move-smbase + 16-bit +\ ascii s report + + \ Move SMBASE to the a0000 location + op: smm-base # ax mov + op: cs: ax smm-smbase #) mov + + \ Via specific + acpi-io-base h# 28 + # dx mov dx al in al dx out \ Ack all events + acpi-io-base h# 2d + # dx mov dx al in al dx out \ ACK SMI + rsm +end-code +here move-smbase - constant /move-smbase + +: via-relocate-smi ( -- ) +\ show-smi 0 +smm /show-smi move + move-smbase smm-base0 /move-smbase move + h# 2c acpi-w@ 1 or h# 2c acpi-w! \ Global SMI enable + h# 2a acpi-b@ h# 40 or h# 2a acpi-b! \ Enable SMI on access to software SMI register + 0 #smint \ Trigger SMI +; + +\ Call this to enable SMI support +: setup-smi ( -- ) + h# 383 config-b@ 1 or h# 383 config-b! \ Enable A/Bxxxx range as memory instead of frame buffer + + h# 386 config-b@ 1 or h# 386 config-b! \ Enable compatible SMM + h# 8fe6 config-b@ 3 or h# 8fe6 config-b! \ Enable compatible and high SMM + +[ifdef] virtual-mode + cr3@ smm-pdir l! +[then] + + origin smm-forth-base l! + smm-exec smm-forth-entry l! + up@ smm-forth-up l! + smm-sp0 smm-save-sp0 ! + smm-rp0 smm-save-rp0 ! + + smi-handler smm-base +smm-offset /smi-handler move + + \ Relocate the code field of the code word that is embedded in the sequence + ['] (smi-return) smi-handler - +smm +smm-offset ( cfa-adr ) + dup ta1+ swap token! + + via-relocate-smi + + h# 383 config-b@ 1 invert and h# 383 config-b! \ Hide A/Bxxxx range behind frame buffer +; + +0 [if] +: bios-release-smi ( -- ) + h# 2a acpi-b@ h# 20 or h# 2a acpi-b! \ Enable SMI on BIOS Release + 4 4 acpi-w! \ Trigger BIOS Release +; +[then] + +\ Layout of saved registers, used by biosints.fth + +: caller-regs ( -- adr ) smm-es +smm ; + +struct + 4 field >rm-es + 4 field >rm-cs + 4 field >rm-ss + 4 field >rm-ds + 4 field >rm-fs + 4 field >rm-gs + d# 16 + \ LDTR, TR, DR7, DR6 + 4 field >rm-eax + 4 field >rm-edx + 4 field >rm-ecx + 4 field >rm-ebx + 4 field >rm-exx + 4 field >rm-ebp + 4 field >rm-esi + 4 field >rm-edi + 4 field >rm-retaddr \ EIP + 4 field >rm-flags +drop