diff mbox

[6/6,wip] tseg, part2, not (yet) tested

Message ID 1429521560-2743-6-git-send-email-kraxel@redhat.com
State New
Headers show

Commit Message

Gerd Hoffmann April 20, 2015, 9:19 a.m. UTC
add tseg window to smram region, so cpus can access it in smm mode.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/pci-host/q35.c         | 13 +++++++++++++
 include/hw/pci-host/q35.h |  2 +-
 2 files changed, 14 insertions(+), 1 deletion(-)

Comments

Laszlo Ersek April 21, 2015, 2:30 p.m. UTC | #1
On 04/20/15 11:19, Gerd Hoffmann wrote:
> add tseg window to smram region, so cpus can access it in smm mode.
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  hw/pci-host/q35.c         | 13 +++++++++++++
>  include/hw/pci-host/q35.h |  2 +-
>  2 files changed, 14 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
> index 412ff0a..7d21399 100644
> --- a/hw/pci-host/q35.c
> +++ b/hw/pci-host/q35.c
> @@ -345,6 +345,13 @@ static void mch_update_smram(MCHPCIState *mch)
>                                          mch->below_4g_mem_size - tseg_size,
>                                          &mch->tseg_blackhole, 1);
>  
> +    memory_region_set_enabled(&mch->tseg_window, tseg_size);
> +    memory_region_set_size(&mch->tseg_window, tseg_size);
> +    memory_region_set_address(&mch->tseg_window,
> +                              mch->below_4g_mem_size - tseg_size);
> +    memory_region_set_alias_offset(&mch->tseg_window,
> +                                   mch->below_4g_mem_size - tseg_size);
> +
>      memory_region_transaction_commit();
>  }
>  
> @@ -500,6 +507,12 @@ static void mch_realize(PCIDevice *d, Error **errp)
>                                          mch->below_4g_mem_size,
>                                          &mch->tseg_blackhole, 1);
>  
> +    memory_region_init_alias(&mch->tseg_window, OBJECT(mch), "tseg-window",
> +                             mch->ram_memory, mch->below_4g_mem_size, 0);
> +    memory_region_set_enabled(&mch->tseg_window, false);
> +    memory_region_add_subregion(&mch->smram, mch->below_4g_mem_size,
> +                                &mch->tseg_window);
> +
>      init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory,
>               mch->pci_address_space, &mch->pam_regions[0],
>               PAM_BIOS_BASE, PAM_BIOS_SIZE);
> diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
> index ba64c70..23b7700 100644
> --- a/include/hw/pci-host/q35.h
> +++ b/include/hw/pci-host/q35.h
> @@ -55,7 +55,7 @@ typedef struct MCHPCIState {
>      PAMMemoryRegion pam_regions[13];
>      MemoryRegion smram_region, open_high_smram;
>      MemoryRegion smram, low_smram, high_smram;
> -    MemoryRegion tseg_blackhole;
> +    MemoryRegion tseg_blackhole, tseg_window;
>      PcPciInfo pci_info;
>      ram_addr_t below_4g_mem_size;
>      ram_addr_t above_4g_mem_size;
> 

Why is this necessary? If you disable the black hole overlay, the access
will go to the RAM. (Or can't that be done per-CPU?)

I'm thinking, the last 1 / 2 / 8 megabytes should behave as RAM in all
of the following cases:
- no SMRAM programmed (tseg size = 0)
- SMRAM programmed (tseg size > 0), and it is open
- SMRAM programmed (tseg size > 0) and closed, but CPU in SMM

Does any of the above require anything else than simply disabling the
black hole overlay? (Sorry if I'm missing something obvious!) Assuming
that a lockdown prevents the reprogramming of tseg size, I think the
above could all be unified.

... Another question, related to SMM (but not related to SMRAM): Paolo,
am I right to think that we'll be keying off at least two independent
things of SMM-or-not: one is access to SMRAM (tseg), for LockBox and SMM
driver purposes, the other is pflash access (with the MemTxAttrs thing),
for the varstore?

(BTW in the meantime I found out about
EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL too, so at least in *theory* it
is clear what has to be done with / for the flash driver.)

Thanks!
Laszlo
Paolo Bonzini April 21, 2015, 2:38 p.m. UTC | #2
On 21/04/2015 16:30, Laszlo Ersek wrote:
>> > -    MemoryRegion tseg_blackhole;
>> > +    MemoryRegion tseg_blackhole, tseg_window;
>> >      PcPciInfo pci_info;
>> >      ram_addr_t below_4g_mem_size;
>> >      ram_addr_t above_4g_mem_size;
>> > 
> Why is this necessary? If you disable the black hole overlay, the access
> will go to the RAM. (Or can't that be done per-CPU?)

The reason to have two separate MemoryRegions is exactly to allow
per-CPU access.

tseg_blackhole is added on top of address_space_memory to hide TSEG;
tseg_window is included in /machine/smram and TCG adds it to the private
per-CPU address space when it enters system management mode.

> I'm thinking, the last 1 / 2 / 8 megabytes should behave as RAM in all
> of the following cases:
> - no SMRAM programmed (tseg size = 0)
> - SMRAM programmed (tseg size > 0), and it is open
> - SMRAM programmed (tseg size > 0) and closed, but CPU in SMM

Correct.  However, you can have one CPU in SMM and another executing
"normal" code.  It would be a hole to allow that CPU to read (or worse,
write) the TSEG or legacy SMRAM areas.

> ... Another question, related to SMM (but not related to SMRAM): Paolo,
> am I right to think that we'll be keying off at least two independent
> things of SMM-or-not: one is access to SMRAM (tseg), for LockBox and SMM
> driver purposes, the other is pflash access (with the MemTxAttrs thing),
> for the varstore?

Yes.

Paolo
Laszlo Ersek April 21, 2015, 3:05 p.m. UTC | #3
On 04/21/15 16:38, Paolo Bonzini wrote:
> 
> 
> On 21/04/2015 16:30, Laszlo Ersek wrote:
>>>> -    MemoryRegion tseg_blackhole;
>>>> +    MemoryRegion tseg_blackhole, tseg_window;
>>>>      PcPciInfo pci_info;
>>>>      ram_addr_t below_4g_mem_size;
>>>>      ram_addr_t above_4g_mem_size;
>>>>
>> Why is this necessary? If you disable the black hole overlay, the access
>> will go to the RAM. (Or can't that be done per-CPU?)
> 
> The reason to have two separate MemoryRegions is exactly to allow
> per-CPU access.
> 
> tseg_blackhole is added on top of address_space_memory to hide TSEG;
> tseg_window is included in /machine/smram and TCG adds it to the private
> per-CPU address space when it enters system management mode.

Hm, I must have missed this (or not seen it at all) -- should I have
noticed it in Gerd's series somewhere (or in yours)? Or is that by
virtue of mapping mch->tseg_window as a subregion of mch->smram?

(These overlays are pretty confusing, without a graphical visualization :))

>> I'm thinking, the last 1 / 2 / 8 megabytes should behave as RAM in all
>> of the following cases:
>> - no SMRAM programmed (tseg size = 0)
>> - SMRAM programmed (tseg size > 0), and it is open
>> - SMRAM programmed (tseg size > 0) and closed, but CPU in SMM
> 
> Correct.  However, you can have one CPU in SMM and another executing
> "normal" code.  It would be a hole to allow that CPU to read (or worse,
> write) the TSEG or legacy SMRAM areas.
> 
>> ... Another question, related to SMM (but not related to SMRAM): Paolo,
>> am I right to think that we'll be keying off at least two independent
>> things of SMM-or-not: one is access to SMRAM (tseg), for LockBox and SMM
>> driver purposes, the other is pflash access (with the MemTxAttrs thing),
>> for the varstore?
> 
> Yes.

Great, thank you.

Yet another question -- as far as I understand, I should have enough
info (with my pending questions of course) for EFI_SMM_ACCESS2_PROTOCOL.
I've now reviewed EFI_SMM_CONTROL2_PROTOCOL too, and AFAICS the only
thing I need to know for it is "how to raise an SMI, synchronously".
What are the plans for that? An ioport write perhaps? (I skimmed the
ICH9 spec, but whatever I found seemed to be quite inappropriate.)

Thanks
Laszlo
Gerd Hoffmann April 21, 2015, 3:12 p.m. UTC | #4
Hi,

> > I'm thinking, the last 1 / 2 / 8 megabytes should behave as RAM in all
> > of the following cases:
> > - no SMRAM programmed (tseg size = 0)
> > - SMRAM programmed (tseg size > 0), and it is open
> > - SMRAM programmed (tseg size > 0) and closed, but CPU in SMM
> 
> Correct.

Almost.  I think the smram open bit doesn't affect tseg at all.  As tseg
doesn't have the funky overlay properties with the vga window you can
simply initialize tseg memory outside smm mode without any special
tricks, you just need to do it before flipping the tseg enable bit.

cheers,
  Gerd
Gerd Hoffmann April 21, 2015, 3:14 p.m. UTC | #5
> > tseg_blackhole is added on top of address_space_memory to hide TSEG;
> > tseg_window is included in /machine/smram and TCG adds it to the private
> > per-CPU address space when it enters system management mode.
> 
> Hm, I must have missed this (or not seen it at all) -- should I have
> noticed it in Gerd's series somewhere (or in yours)? Or is that by
> virtue of mapping mch->tseg_window as a subregion of mch->smram?

Yes.  mch->smram holds the memory regions accessible in smm mode only.

> (These overlays are pretty confusing, without a graphical visualization :))

Try 'info mtree'.

cheers,
  Gerd
Paolo Bonzini April 21, 2015, 3:21 p.m. UTC | #6
On 21/04/2015 17:05, Laszlo Ersek wrote:
> 
> Yet another question -- as far as I understand, I should have enough
> info (with my pending questions of course) for EFI_SMM_ACCESS2_PROTOCOL.
> I've now reviewed EFI_SMM_CONTROL2_PROTOCOL too, and AFAICS the only
> thing I need to know for it is "how to raise an SMI, synchronously".
> What are the plans for that? An ioport write perhaps? (I skimmed the
> ICH9 spec, but whatever I found seemed to be quite inappropriate.)

You can write to ioport 0xb2 in order to raise the SMI, or you can also
write to the APIC ICR register with LOCAL_APIC_DELIVERY_MODE_SMI.  I am
not sure if the latter is synchronous.

If you use the former, it probably should be protected by some kind of
spinlock (I don't know the details of UEFI multi tasking) and you also
have to set the APMC_EN and GBL_SMI_EN bits of the SMI_EN register.

Paolo
Laszlo Ersek April 21, 2015, 8:31 p.m. UTC | #7
adding edk2-devel

On 04/21/15 17:21, Paolo Bonzini wrote:
> 
> 
> On 21/04/2015 17:05, Laszlo Ersek wrote:
>>
>> Yet another question -- as far as I understand, I should have enough
>> info (with my pending questions of course) for EFI_SMM_ACCESS2_PROTOCOL.
>> I've now reviewed EFI_SMM_CONTROL2_PROTOCOL too, and AFAICS the only
>> thing I need to know for it is "how to raise an SMI, synchronously".
>> What are the plans for that? An ioport write perhaps? (I skimmed the
>> ICH9 spec, but whatever I found seemed to be quite inappropriate.)
> 
> You can write to ioport 0xb2 in order to raise the SMI, or you can also
> write to the APIC ICR register with LOCAL_APIC_DELIVERY_MODE_SMI.  I am
> not sure if the latter is synchronous.
> 
> If you use the former, it probably should be protected by some kind of
> spinlock (I don't know the details of UEFI multi tasking) and you also
> have to set the APMC_EN and GBL_SMI_EN bits of the SMI_EN register.

This raises a number of questions. I'm hoping to get help for the below
from edk2-devel subscribers. :)

(1) About locking. "MdePkg/Library/UefiLib/UefiLib.c" in edk2 has a pair
of functions called EfiAcquireLock() and EfiReleaseLock() -- and some
variation too.

I'm not really sure why these functions (and the underlying data
structure, EFI_LOCK) exist.

------------------------------
typedef enum {
  EfiLockUninitialized = 0,
  EfiLockReleased      = 1,
  EfiLockAcquired      = 2
} EFI_LOCK_STATE;

typedef struct {
  EFI_TPL         Tpl;
  EFI_TPL         OwnerTpl;
  EFI_LOCK_STATE  Lock;
} EFI_LOCK;

VOID
EFIAPI
EfiAcquireLock (
  IN EFI_LOCK  *Lock
  )
{
  ASSERT (Lock != NULL);
  ASSERT (Lock->Lock == EfiLockReleased);

  Lock->OwnerTpl = gBS->RaiseTPL (Lock->Tpl);
  Lock->Lock     = EfiLockAcquired;
}

VOID
EFIAPI
EfiReleaseLock (
  IN EFI_LOCK  *Lock
  )
{
  EFI_TPL Tpl;

  ASSERT (Lock != NULL);
  ASSERT (Lock->Lock == EfiLockAcquired);

  Tpl = Lock->OwnerTpl;

  Lock->Lock = EfiLockReleased;

  gBS->RestoreTPL (Tpl);
}
------------------------------

I understand the stashing of the old TPL (task priority level) for
restoration. I understand that the lock object itself carries new (ie.
masked) TPL as well. The lock state is apparently only used for
enforcing symmetry between locking and unlocking.

But the prototypes of these functions are very misleading. They imply
that you can take separate locks, and that locking one will not prevent
locking another. This is false. There's only one task priority level,
and raising the TPL ensures that various registered callbacks
(TPL_CALLBACK) and asynch notifications (TPL_NOTIFY: in practice always
running in the context of the timer interrupt handler) are masked.

So, if you have two EFI_LOCK objects, Lock1 and Lock2, and Lock1.Tpl ==
TPL_CALLBACK, and Lock2.Tpl == TPL_NOTIFY, and you take Lock2 first,
then an attempt to lock the seemingly independent Lock1 (whose TPL is
TPL_CALLBACK, which is lower than the current TPL, TPL_NOTIFY) will lead
to indeterminate system state. See the description of the RaiseTPL()
boot service:

    If NewTpl is below the current TPL level, then the system behavior
    is indeterminate.

So, I really don't understand why these UefiLib functions exist at all;
to me they seem more confusing than useful.

(2) Again, about locking. Assuming someone explains the above functions
to me, or I just use naked gBS->RaiseTPL() inside
EFI_SMM_CONTROL2_PROTOCOL.Trigger(), I wonder if that's a good idea.

Namely, the hardware should assert the SMI while inside Trigger(),
edk2's SMI handler / dispatcher will run, and call into the appropriate
SMM DXE driver. That driver might do any kind of stuff, like update the
varstore in the pflash, which could take long.

I observe two things:
(a) "taking long" may not be allowed on whatever task priority level we
are at the moment. For example, if we raised the TPL to TPL_NOTIFY in
the "locking", then:

    Blocking is not allowed at this level. Code executes to completion
    and returns. If code requires more processing, it needs to signal
    an event to wait to obtain control again at whatever level it
    requires. This level is typically used to process low level IO to
    or from a device.

I guess it's akin to the Linux kernel's "hardirq" (top half) context. So
I'm not sure all SMM DXE drivers satisfy this, ie. if Trigger() can just
bump the TPL to TPL_NOTIFY indiscriminately.

(b) This is actually the opposite argument. Since SMM is security
sensitive, *and* UEFI is single-processor / single-threaded (well you
can use multiple processors, you just can't run any UEFI code on
anything but the BP), the timer interrupt should be blocked / masked
*anyway* while in SMM, no? Otherwise edk2 could start running a plain
timer event callback in the middle of an SMM handler. Which makes me
think that this locking / interrupt masking must already be in place,
and it's not the responsibility of the individual
EFI_SMM_CONTROL2_PROTOCOL.Trigger() implementation. (PI 1.3 vol4 doesn't
mention this responsibility in any case.)

I've learned about the "EDK II SMM Call Topology" document from a piwg
message:

http://sourceforge.net/projects/edk2/files/General%20Documentation/EDK%20II%20SMM%20call%20topology.pdf/download

It doesn't speak about masking the timer.

Does SMI mask other interrupts "architecturally" perhaps?

...

(3) Oh, this is sad. Well, I am sad. Turns out there's a third UEFI
protocol that OVMF needs to implement: EFI_SMM_CONFIGURATION_PROTOCOL.
Its only interesting member is RegisterSmmEntry(), and that function is
supposed to bind the central entry point (which is already available in
the edk2 tree) to the processor-level SMI handler.

(I'm kind of confused, because last time I experimented with faking
SMRAM / SMM in OVMF, my fake Trigger() function just returned success,
and there was no EFI_SMM_CONFIGURATION_PROTOCOL at all, and things
seemed to work. In retrospect I can't imagine how control was
transferred at all, without actual SMM / SMI support in both QEMU and
OVMF. Hm... looking at occurrences of "SmmEntryPointRegistered", this
may have been intentional in edk2.)

EFI_SMM_CONFIGURATION_PROTOCOL discussed in the "EDK II SMM Call
Topology" document, on the "SmmDriverDispatcher" and especially the
"SMBASE Relocation" pages. It takes a separate CPU driver, and
(obviously) assembly code.

The "SMBASE Relocation" page references "IA32FamilyCpuPkg", which is not
open source. Nothing in edk2 produces gEfiSmmConfigurationProtocol, and
no source file contains the RSM instruction.

The Vlv2TbltDevicePkg package (ValleyView2 Tablet?) makes several
references to IA32FamilyCpuPkg, but those are only references.

I guess IA32FamilyCpuPkg is exactly the kind of chipset code that Intel
has not opened up.

Our "SMM in OVMF" effort just got set back by months. :(

Laszlo
Paolo Bonzini April 21, 2015, 8:58 p.m. UTC | #8
On 21/04/2015 22:31, Laszlo Ersek wrote:
> typedef enum {
>   EfiLockUninitialized = 0,
>   EfiLockReleased      = 1,
>   EfiLockAcquired      = 2
> } EFI_LOCK_STATE;
> 
> typedef struct {
>   EFI_TPL         Tpl;
>   EFI_TPL         OwnerTpl;
>   EFI_LOCK_STATE  Lock;
> } EFI_LOCK;
> 
> VOID
> EFIAPI
> EfiAcquireLock (
>   IN EFI_LOCK  *Lock
>   )
> {
>   ASSERT (Lock != NULL);
>   ASSERT (Lock->Lock == EfiLockReleased);
> 
>   Lock->OwnerTpl = gBS->RaiseTPL (Lock->Tpl);
>   Lock->Lock     = EfiLockAcquired;
> }
> 
> VOID
> EFIAPI
> EfiReleaseLock (
>   IN EFI_LOCK  *Lock
>   )
> {
>   EFI_TPL Tpl;
> 
>   ASSERT (Lock != NULL);
>   ASSERT (Lock->Lock == EfiLockAcquired);
> 
>   Tpl = Lock->OwnerTpl;
> 
>   Lock->Lock = EfiLockReleased;
> 
>   gBS->RestoreTPL (Tpl);
> }

Okay, first of all this is obviously not a spinlock that works on a
multiprocessor system.  But it is actually relatively close.

> But the prototypes of these functions are very misleading. They imply
> that you can take separate locks, and that locking one will not prevent
> locking another. This is false.

No, I think it can work.  You just have to be aware of the TPL of the
locks.   If you assume that you're being called with no lock taken, it's
easy to track that.

The TPL of the lock should be the highest TPL of "things that can
interrupt you".  I guess you can just use TPL_NOTIFY and possibly then
look into relaxing it.

> Does SMI mask other interrupts "architecturally" perhaps?

It masks interrupts just because on entry to SMM the interrupt flag is
cleared.  NMIs are also inhibited on entry to SMM.

> EFI_SMM_CONFIGURATION_PROTOCOL discussed in the "EDK II SMM Call
> Topology" document, on the "SmmDriverDispatcher" and especially the
> "SMBASE Relocation" pages. It takes a separate CPU driver, and
> (obviously) assembly code.

Oh, that's unfortunate.  It's not really bad, as the SMM "trampoline"
code can be really small (SeaBIOS does just "mov %cs, %ax" followed by a
jump to its usual protected mode entry routine, IIRC) and the exit is
really as simple as "rsm".  But it sucks. :(

Oh well, at least it's well documented.

Paolo
Yao, Jiewen April 24, 2015, 11:56 a.m. UTC | #9
Hi Laszlo
Below is my thought.

2) You are right. According to IA32 manual - "Maskable hardware interrupts, exceptions, NMI interrupts, SMI interrupts, A20M interrupts, single-step traps, breakpoint traps, and INIT operations are inhibited when the processor enters SMM." You can also find more detail in "34.6 EXCEPTIONS AND INTERRUPTS WITHIN SMM"

In below doc, the "SMM Timer is initialized" means the timer used by BSP to check if APs are all pulled into SMM for CPU Rendez-vous. A typical implementation is ACPI timer (PCH), or APIC timer (CPU). BSP just check timer count, but the timer does not generate any interrupt.

3) Yes. SmmCoreEntry is useful function as bridge between a real CPU_SMM driver and SMM_CORE.
In real world, when SMI happen, CPU will jump to SM_BASE in real mode. Then it will jump to X64 mode immediately.
The all CPUs will be in election phase. Only one will be elected as BSP.
Just in case, some APIs are not in SMM yet, the BSP will send IPI to try pull those APs into SMM mode. (SMM timer is used at this moment).
Above process is called CPU rendez-vous.

Then BSP will do enter SMM_CORE entry point (registered by SmmCoreEntry), while APs are in loop state.
After BSP returns from SMM_CORE, it will let APs leave loop state, and all CPUs run RSM instruction to return back.

Hope that helps.

BTW: I am not sure how QEMU emulate SMI. Does SMI can be trigger by 0xB2 port? And CPU will run to SMBASE in real mode?


Thank you
Yao Jiewen


-----Original Message-----
From: Laszlo Ersek [mailto:lersek@redhat.com] 
Sent: Wednesday, April 22, 2015 4:32 AM
To: Paolo Bonzini; Gerd Hoffmann
Cc: edk2-devel list; qemu-devel@nongnu.org; mst@redhat.com
Subject: [edk2] implementing EFI_SMM_CONTROL2_PROTOCOL.Trigger() (was: [PATCH 6/6] [wip] tseg, part2, not (yet) tested)


 (b) This is actually the opposite argument. Since SMM is security sensitive, *and* UEFI is single-processor / single-threaded (well you can use multiple processors, you just can't run any UEFI code on anything but the BP), the timer interrupt should be blocked / masked
*anyway* while in SMM, no? Otherwise edk2 could start running a plain timer event callback in the middle of an SMM handler. Which makes me think that this locking / interrupt masking must already be in place, and it's not the responsibility of the individual
EFI_SMM_CONTROL2_PROTOCOL.Trigger() implementation. (PI 1.3 vol4 doesn't mention this responsibility in any case.)



I've learned about the "EDK II SMM Call Topology" document from a piwg
message:

http://sourceforge.net/projects/edk2/files/General%20Documentation/EDK%20II%20SMM%20call%20topology.pdf/download

It doesn't speak about masking the timer.

Does SMI mask other interrupts "architecturally" perhaps?

...

(3) Oh, this is sad. Well, I am sad. Turns out there's a third UEFI protocol that OVMF needs to implement: EFI_SMM_CONFIGURATION_PROTOCOL.
Its only interesting member is RegisterSmmEntry(), and that function is supposed to bind the central entry point (which is already available in the edk2 tree) to the processor-level SMI handler.

(I'm kind of confused, because last time I experimented with faking SMRAM / SMM in OVMF, my fake Trigger() function just returned success, and there was no EFI_SMM_CONFIGURATION_PROTOCOL at all, and things seemed to work. In retrospect I can't imagine how control was transferred at all, without actual SMM / SMI support in both QEMU and OVMF. Hm... looking at occurrences of "SmmEntryPointRegistered", this may have been intentional in edk2.)

EFI_SMM_CONFIGURATION_PROTOCOL discussed in the "EDK II SMM Call Topology" document, on the "SmmDriverDispatcher" and especially the "SMBASE Relocation" pages. It takes a separate CPU driver, and
(obviously) assembly code.

The "SMBASE Relocation" page references "IA32FamilyCpuPkg", which is not open source. Nothing in edk2 produces gEfiSmmConfigurationProtocol, and no source file contains the RSM instruction.

The Vlv2TbltDevicePkg package (ValleyView2 Tablet?) makes several references to IA32FamilyCpuPkg, but those are only references.

I guess IA32FamilyCpuPkg is exactly the kind of chipset code that Intel has not opened up.

Our "SMM in OVMF" effort just got set back by months. :(

Laszlo

------------------------------------------------------------------------------
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT Develop your own process in accordance with the BPMN 2 standard Learn Process modeling best practices with Bonita BPM through live exercises
http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_ source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
Paolo Bonzini April 24, 2015, 1 p.m. UTC | #10
On 24/04/2015 13:56, Yao, Jiewen wrote:
> BTW: I am not sure how QEMU emulate SMI. Does SMI can be trigger by
> 0xB2 port? And CPU will run to SMBASE in real mode?

Yes, operation is the same.

Paolo
Yao, Jiewen April 24, 2015, 1:16 p.m. UTC | #11
Got it. Thanks!

-----Original Message-----
From: Paolo Bonzini [mailto:pbonzini@redhat.com] 
Sent: Friday, April 24, 2015 9:01 PM
To: Yao, Jiewen; edk2-devel@lists.sourceforge.net; Gerd Hoffmann
Cc: qemu-devel@nongnu.org; mst@redhat.com
Subject: Re: [edk2] implementing EFI_SMM_CONTROL2_PROTOCOL.Trigger()



On 24/04/2015 13:56, Yao, Jiewen wrote:
> BTW: I am not sure how QEMU emulate SMI. Does SMI can be trigger by
> 0xB2 port? And CPU will run to SMBASE in real mode?

Yes, operation is the same.

Paolo
Yao, Jiewen April 24, 2015, 2:50 p.m. UTC | #12
Hi Laszlo

I think there is good resource for your reference - Intel Quark.
https://downloadcenter.intel.com/download/23197

You may download "Board_Support_Package_Sources_for_Intel_Quark_v1.1.0.7z", and find "Quark_EDKII_v1.1.0"

IA32FamilyCpuBasePkg\PiSmmCpuDxeSmm - it is CPUSMM driver.
IA32FamilyCpuBasePkg\PiSmmCommunication - it is CommunicationPeim.

PiSmmCpuDxeSmm works for Quark, but I think it should be easy to port to QEMU platform.
PiSmmCommunication should be generic, it might be able to put to UefiCpuPkg later.

Thank you
Yao Jiewen

-----Original Message-----
From: Yao, Jiewen [mailto:jiewen.yao@intel.com] 
Sent: Friday, April 24, 2015 7:56 PM
To: edk2-devel@lists.sourceforge.net; Paolo Bonzini; Gerd Hoffmann
Cc: qemu-devel@nongnu.org; mst@redhat.com
Subject: Re: [edk2] implementing EFI_SMM_CONTROL2_PROTOCOL.Trigger() (was: [PATCH 6/6] [wip] tseg, part2, not (yet) tested)

Hi Laszlo
Below is my thought.

2) You are right. According to IA32 manual - "Maskable hardware interrupts, exceptions, NMI interrupts, SMI interrupts, A20M interrupts, single-step traps, breakpoint traps, and INIT operations are inhibited when the processor enters SMM." You can also find more detail in "34.6 EXCEPTIONS AND INTERRUPTS WITHIN SMM"

In below doc, the "SMM Timer is initialized" means the timer used by BSP to check if APs are all pulled into SMM for CPU Rendez-vous. A typical implementation is ACPI timer (PCH), or APIC timer (CPU). BSP just check timer count, but the timer does not generate any interrupt.

3) Yes. SmmCoreEntry is useful function as bridge between a real CPU_SMM driver and SMM_CORE.
In real world, when SMI happen, CPU will jump to SM_BASE in real mode. Then it will jump to X64 mode immediately.
The all CPUs will be in election phase. Only one will be elected as BSP.
Just in case, some APIs are not in SMM yet, the BSP will send IPI to try pull those APs into SMM mode. (SMM timer is used at this moment).
Above process is called CPU rendez-vous.

Then BSP will do enter SMM_CORE entry point (registered by SmmCoreEntry), while APs are in loop state.
After BSP returns from SMM_CORE, it will let APs leave loop state, and all CPUs run RSM instruction to return back.

Hope that helps.

BTW: I am not sure how QEMU emulate SMI. Does SMI can be trigger by 0xB2 port? And CPU will run to SMBASE in real mode?


Thank you
Yao Jiewen


-----Original Message-----
From: Laszlo Ersek [mailto:lersek@redhat.com] 
Sent: Wednesday, April 22, 2015 4:32 AM
To: Paolo Bonzini; Gerd Hoffmann
Cc: edk2-devel list; qemu-devel@nongnu.org; mst@redhat.com
Subject: [edk2] implementing EFI_SMM_CONTROL2_PROTOCOL.Trigger() (was: [PATCH 6/6] [wip] tseg, part2, not (yet) tested)


 (b) This is actually the opposite argument. Since SMM is security sensitive, *and* UEFI is single-processor / single-threaded (well you can use multiple processors, you just can't run any UEFI code on anything but the BP), the timer interrupt should be blocked / masked
*anyway* while in SMM, no? Otherwise edk2 could start running a plain timer event callback in the middle of an SMM handler. Which makes me think that this locking / interrupt masking must already be in place, and it's not the responsibility of the individual
EFI_SMM_CONTROL2_PROTOCOL.Trigger() implementation. (PI 1.3 vol4 doesn't mention this responsibility in any case.)



I've learned about the "EDK II SMM Call Topology" document from a piwg
message:

http://sourceforge.net/projects/edk2/files/General%20Documentation/EDK%20II%20SMM%20call%20topology.pdf/download

It doesn't speak about masking the timer.

Does SMI mask other interrupts "architecturally" perhaps?

...

(3) Oh, this is sad. Well, I am sad. Turns out there's a third UEFI protocol that OVMF needs to implement: EFI_SMM_CONFIGURATION_PROTOCOL.
Its only interesting member is RegisterSmmEntry(), and that function is supposed to bind the central entry point (which is already available in the edk2 tree) to the processor-level SMI handler.

(I'm kind of confused, because last time I experimented with faking SMRAM / SMM in OVMF, my fake Trigger() function just returned success, and there was no EFI_SMM_CONFIGURATION_PROTOCOL at all, and things seemed to work. In retrospect I can't imagine how control was transferred at all, without actual SMM / SMI support in both QEMU and OVMF. Hm... looking at occurrences of "SmmEntryPointRegistered", this may have been intentional in edk2.)

EFI_SMM_CONFIGURATION_PROTOCOL discussed in the "EDK II SMM Call Topology" document, on the "SmmDriverDispatcher" and especially the "SMBASE Relocation" pages. It takes a separate CPU driver, and
(obviously) assembly code.

The "SMBASE Relocation" page references "IA32FamilyCpuPkg", which is not open source. Nothing in edk2 produces gEfiSmmConfigurationProtocol, and no source file contains the RSM instruction.

The Vlv2TbltDevicePkg package (ValleyView2 Tablet?) makes several references to IA32FamilyCpuPkg, but those are only references.

I guess IA32FamilyCpuPkg is exactly the kind of chipset code that Intel has not opened up.

Our "SMM in OVMF" effort just got set back by months. :(

Laszlo

------------------------------------------------------------------------------
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT Develop your own process in accordance with the BPMN 2 standard Learn Process modeling best practices with Bonita BPM through live exercises
http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_ source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
Laszlo Ersek April 24, 2015, 4:46 p.m. UTC | #13
On 04/24/15 16:50, Yao, Jiewen wrote:
> Hi Laszlo
> 
> I think there is good resource for your reference - Intel Quark.
> https://downloadcenter.intel.com/download/23197
> 
> You may download "Board_Support_Package_Sources_for_Intel_Quark_v1.1.0.7z", and find "Quark_EDKII_v1.1.0"
> 
> IA32FamilyCpuBasePkg\PiSmmCpuDxeSmm - it is CPUSMM driver.
> IA32FamilyCpuBasePkg\PiSmmCommunication - it is CommunicationPeim.
> 
> PiSmmCpuDxeSmm works for Quark, but I think it should be easy to port to QEMU platform.
> PiSmmCommunication should be generic, it might be able to put to UefiCpuPkg later.

Jiewen, thank you so much -- this is perfect. Exactly what I needed. It provides EFI_SMM_CONFIGURATION_PROTOCOL and SMM_S3_RESUME_STATE:

-rw-------. 1 13909 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/CpuS3.c
-rw-------. 1 16290 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/CpuService.c
-rw-------. 1  8100 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/CpuService.h
-rw-------. 1  7221 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/MpFuncs.S
-rw-------. 1  7072 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/MpFuncs.asm
-rw-------. 1  3304 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
-rw-------. 1  3013 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/Semaphore.c
-rw-------. 1  5470 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/SmiEntry.S
-rw-------. 1  5574 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/SmiEntry.asm
-rw-------. 1 38464 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/SmiException.S
-rw-------. 1 26268 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/SmiException.asm
-rw-------. 1  4115 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/SmmInit.S
-rw-------. 1  4331 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/SmmInit.asm
-rw-------. 1  2801 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/SmmProfileArch.c
-rw-------. 1  3553 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/Ia32/SmmProfileArch.h
-rw-------. 1 54847 Apr 24 18:32 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/MpService.c
-rwx------. 1 59571 Apr 24 18:34 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c
-rw-------. 1 25929 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h
-rw-------. 1  5318 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf
-rw-------. 1  8619 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/SmmFeatures.c
-rw-------. 1  5062 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/SmmFeatures.h
-rw-------. 1 40893 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/SmmProfile.c
-rw-------. 1  2290 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/SmmProfile.h
-rw-------. 1  5357 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/SmmProfileInternal.h
-rw-------. 1  3391 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCpuDxeSmm/SyncTimer.c

(~360K)

and even EFI_PEI_SMM_COMMUNICATION_PPI:

-rw-------. 1 15508 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCommunication/PiSmmCommunicationPei.c
-rw-------. 1  2706 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCommunication/PiSmmCommunicationPei.inf
-rw-------. 1  1981 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCommunication/PiSmmCommunicationPrivate.h
-rw-------. 1 13522 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCommunication/PiSmmCommunicationSmm.c
-rw-------. 1  3004 Feb 16 10:55 Quark_EDKII_v1.1.0/IA32FamilyCpuBasePkg/PiSmmCommunication/PiSmmCommunicationSmm.inf

and "Quark_EDKII_v1.1.0/LICENSE" is the 3-clause BSDL. Awesome.

Thanks!
Laszlo
diff mbox

Patch

diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index 412ff0a..7d21399 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -345,6 +345,13 @@  static void mch_update_smram(MCHPCIState *mch)
                                         mch->below_4g_mem_size - tseg_size,
                                         &mch->tseg_blackhole, 1);
 
+    memory_region_set_enabled(&mch->tseg_window, tseg_size);
+    memory_region_set_size(&mch->tseg_window, tseg_size);
+    memory_region_set_address(&mch->tseg_window,
+                              mch->below_4g_mem_size - tseg_size);
+    memory_region_set_alias_offset(&mch->tseg_window,
+                                   mch->below_4g_mem_size - tseg_size);
+
     memory_region_transaction_commit();
 }
 
@@ -500,6 +507,12 @@  static void mch_realize(PCIDevice *d, Error **errp)
                                         mch->below_4g_mem_size,
                                         &mch->tseg_blackhole, 1);
 
+    memory_region_init_alias(&mch->tseg_window, OBJECT(mch), "tseg-window",
+                             mch->ram_memory, mch->below_4g_mem_size, 0);
+    memory_region_set_enabled(&mch->tseg_window, false);
+    memory_region_add_subregion(&mch->smram, mch->below_4g_mem_size,
+                                &mch->tseg_window);
+
     init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory,
              mch->pci_address_space, &mch->pam_regions[0],
              PAM_BIOS_BASE, PAM_BIOS_SIZE);
diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
index ba64c70..23b7700 100644
--- a/include/hw/pci-host/q35.h
+++ b/include/hw/pci-host/q35.h
@@ -55,7 +55,7 @@  typedef struct MCHPCIState {
     PAMMemoryRegion pam_regions[13];
     MemoryRegion smram_region, open_high_smram;
     MemoryRegion smram, low_smram, high_smram;
-    MemoryRegion tseg_blackhole;
+    MemoryRegion tseg_blackhole, tseg_window;
     PcPciInfo pci_info;
     ram_addr_t below_4g_mem_size;
     ram_addr_t above_4g_mem_size;