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 ------------------------------------------------------------------------------ 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 _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel