

STMPE Addtions to the STM API

 AddPEVMtempVMCALL:
 AddPEVMpermVMCALL:
 
	Create a protected execution (PE) virtual machine (VM) and load a module to be executed
        !in that VM.
 
 AddPEVMtempVMCALL: 
 
	The module is temporarily loaded in a VM, executed, and the VM torn down afterwards. i.e.
        protected execution.

	Input and Return register values: same as AddPEVMpermVMCALL.
 
 AddPEVMpermVMCALL: 
 
	The module is permanently loaded in a VM and persists.  It is expected that this will be
        allowed only when the MLE is initially brought up to allow for a measurement engine to be
	loaded. This module is executed by the RunPeVmVMCALL.

	Input registers:
 
	EAX = STM_API_ADD_TEMP_PE or STM_API_ADD_PERM_PE_VM
	EBX = low 32 bits of physical address of a caller created structure
              containing module load information (struct module_info)
	ECX = high 32 bits of physical address of a caller created structure
              containing module load information (struct module_info)
 
	Return register values:
 
	CF = 0: No error, EAX set to STM_SUCCESS (0)
	CF = 1: An error occurred, EAX holds relevant error value
	EAX:  Error/success return

 RunPeVmVMCALL

	This call runs the VM that was created via the AddSTMVmVMCALL vmcall.  Entrypoint into the
        module will be that defined during the AddSTMVmVMCALL.

	Input registers:

	EAX = RunPeVmVMCALL
 
	Output registers:
	
	CF = 0: No error, EAX set to STM_SUCCESS (0)
	CF = 1: An error occurred, EAX holds relevant error value EAX = -1 (actual error values 
                TBD)

 EndAddPeVmVMCALL

	Turns off the ability to add a permanent PE VM.  This function is normally
	ran at the end of the MLE's processing to ensure that no permanent
	untrusted entity resides in the STM.

Bit definitions for the additions to the STM API

       STM_API_ADD_TEMP_PE_VM       0x00010009
       STM_API_ADD_PERM_PE_VM       0x0001000a
       STM_API_END_PERM_PE_VM       0x0001000c
       STM_API_RUN_PERM_PE_VM       0x0001000b
       STM_API_ADD_PERM_PE_VM_NORUN 0x0001000d


 Information block used to pass information about the PE module to the STM:

   struct module_info {
      u64 module_address;        - physical address of VM/PE (SMM) module to load
      u64 module_load_address;   - guest-physical load address of module (must be
		                         - within the range of address_space_start and
			                     - address_space_start+addresss_space_size)
      u32 module_size;           - size of module in bytes
      u32 module_entry_point;    - relative offset from start of module
      u64 address_space_start;   - guest-physical address space start of module
      u32 address_space_size;    - module size in bytes
      u32 vmconfig;              - flags specifying how VM supporting module should
                                 - be configured (see below for definitions)
      u64 cr3_load;              - guest-physical address to initialize cr3 to if
		                         - paging enabled
      u64 shared_page;           - guest-physical address of a shared page (must not
                                 - be in SMRAM space).  segment will have R/W
                                 - permission. value placed in RBX register of STM
                                 - module.
      struct region *segment;    - guest-physical address of an array of R/O
                                 - segments terminated with a null element.
                                 - value placed in RCX register of STM module.
      u32 shared_page_size;      - size of shared page
      u32 DoNotClearSize;        - block at beginning of data not to be cleared
      u64 ModuleDataSection;     - start address of the module data section
	} __attribute__((__packed__));

vmconfig settings:

	SET_CS_L    (1<<13)      - cs.l set 64bit mode for cs (req. SET_IA32E is set)
	SET_CS_D    (1<<14)      - cs.d default mode (0: 16bit seg, 1: 32-bit)
                           must be set if SET_CS_L is set
	SET_IA32E   (1<<15)      - sets IA32e mode; when set, cr0.pg, cr0.pe, and
                           cr0.pae will be set as well
	SET_CR0_PG  (1<<31)      - set cr0.pg
	SET_CR0_PE  (1<<0)       - set cr0.pe
	SET_CR4_PAE (1<<3)       - set cr4.pae
	SET_PERM_VM (1<<2)       - VM can be re-executed using
	                           STM_API_RUN_VM(RunPeVmVMCALL)

	SET_PERM_VM_RUN_ONCE           (1<<20)   - run perm VM only once, then breakdown
	SET_PERM_VM_CRASH_BREAKDOWN    (1<<21)   - if Perm VM crashes, then breakdown
	SET_PERM_VM_RUN_SMI            (1<<22)   - Run VM/PE via SMI timer (every 16 Seconds)
	SET_VM_CLEAR_MEMORY            (1<<23)   - Clear heap before each run
	SET_VM_TEXT_RW                 (1<<24)   - set VM/PE text space as RW
	SET_VM_EXEC_HEAP               (1<<25)   - allow execution in Heap Space
	SET_PERM_VM_HANDLE_INT         (1<<26)   - allow perm VM/PE to handle internal interrupts

error returns - placed in eax upon return to caller:

	PE_SUCCESS                   0           - PE setup/ran sucessfully
	PE_FAIL                     -1           - catchall PE ERROR
	PE_SPACE_TOO_LARGE           0x80040001  - requested memory space too large
	PE_MODULE_ADDRESS_TOO_LOW    0x80040002  - PE module start below address space start
	PE_MODULE_TOO_LARGE          0x80040003  - PE module too large for address space
	                                           (or located such that it overflows
		                                       the end of the address space
	PE_NO_PT_SPACE               0x80040004  - not enough space left for PE VM
                                                   page tables
	PE_NO_RL_SPACE               0x80040005  - not enough space left for resource
                                                   list
	PE_MEMORY_AC_SETUP_FAILURE   0x80040006  - could not setup accesses to PE space
		                                       (internal error)
	PE_SHARED_MEMORY_SETUP_ERROR 0x80040007  - could not setup shared memory
	PE_MODULE_MAP_FAILURE        0x80040008  - could not map in the address space
	PE_SHARED_MAP_FAILURE        0x80040009  - could not map in the shared page
	PE_VMCS_ALLOC_FAIL           0x8004000A  - could not allocate VMCS memory
	PE_VMLAUNCH_ERROR            0x8004000B  - attempted to launch PE VM with bad
                                                   guest VM state
	PE_VM_BAD_ACCESS             0x8004000C  - PE VM attempted to access protected
                                                   memory out of bounds)
        PE_VM_SETUP_ERROR_D_L        0x8004000D  - CS_D and CS_L cannot be set to one at
                                               the same time
	PE_VM_SETUP_ERROR_IA32E_D    0x8004000E  - SET_IA32E must be set when CS_L is
                                                   set
        PE_VM_TRIPLE_FAULT           0x8004000F  - PE VM crashed with a triple fault
        PE_VM_PAGE_FAULT             0x80040010  - PE VM crashed with a page fault

Region list structure

    struct region {
	u64 address;                           - page aligned physical address
	u32 size;                              - size of segment
	u32 padding;                           - align structure to 64-bits
	} __attribute__((__packed__));


STM/PE and VM/PE interaction

 Note: The current VM/PE application is XHIM (Xen Integrity Monitory) and the interface is biased
 towards supporting that application.  The interface will be modified as other applications are 
 introduced for other VM/PE's.


When the XHIM VM/PE is started, the register contents are as follows:

	RBX - Shared page (low)
	RCX - Shared page (high)
	RDX - Shared STM Page

  Shared Page - (64 bit address) This is a region of memory that is located outside of
  SMRAM.  It is readable/writeable by the VM/PE application.  Generally, used as an
  output buffer for the VM/PE application.

  Shared STM Page - This is a region shared between shared between the VM/PE application.
  The VM/PE application has readonly access.  This region begins at the top of the PE/VM
  memory and is used, in the case of XHIM, to provide the processor state information.


VM/PE VMCALLS for XHIM


 STM_API_MAP_ADDRESS_RANGE - Maps a non-SMRAM address range into the address space accessible
 by the PE/VM.  This address range is read-only for the VM/PE.

                           RBX - Address of parameter block (Upper)
                           RCX - Address of parameter block (Lower)

                    Format of parameter block: 

                           u64 - Physical address to be mapped
                           u64 - Number of pages to be mapped

                    Status return

                           RFLAGS_CF = 0 success
                           RAX = STM_SUCCESS

                           RFLAGS_CF = 1 fail
                           RAX = ERROR_STM_CACHE_TYPE_NOT_SUPPORTED
                                 ERROR_STM_PAGE_NOT_FOUND
                                 ERROR_STM_SECURITY_VIOLATION

 STM_API_GET_VMCS_MAP - Provides the VMCS map.  The VMCS map is created when the VM/PE is initially
 setup.  It is a mapping between the relative VMCS location and the associated VMCS data field.
 Caution must be exercised when using this mapping as the x86 processors cache the VMCS and in-
 memory values may be stale as the current value may be in the processor cache.

                           RBX - Address for VMCS map (Upper)
                           RCX - Address for VMCS map (Lower)

                                 One page should be allocated for this map to prevent overrun of
                                 the VM/PE memory.

                                 Use the VMCS field encoding as an index into a 16-bit array, where
                                 each array element holds the offset  of the data in the VMCS.
                                 Note: this data may become stale due to processor caching.

                    Status return

                          RFLAGS_CF = 0 success
                          RAX = STM_SUCCESS

                          RFLAGS_CF = 1 fail
                          RAX = ERROR_STM_CACHE_TYPE_NOT_SUPPORTED
                                ERROR_STM_PAGE_NOT_FOUND
                                ERROR_STM_SECURITY_VIOLATION


END VMCALLS

Processor state information provided by the STM for each XHIM run.

 When a VMEXIT occurs, each processor's state information is contained within the guest portion of
 the SMM-transfer VMCS.  The SMM-transfer VMCS is created during the STM initial startup and is the
 current VMCS when the STM gains control after the VMEXIT into SMM.  There is one per processor and
 is contained within the STM's address space. The STM extracts this state information and places it
 into the STM shared page for XHIM to access.

 When an SMM VMEXIT occurs, the processor is in either non-root operation or in root operation.  
 This operational mode determines how the executive-VMCS pointer field is set. If the VMM was in
 non-root operation the current-VMCS pointer is placed into the executive-VMCS field. Otherwise if
 the VMM is in root operation the VMXON pointer is placed there.

 The STM uses these values to determine if each operational mode of each processor. And this result
 is placed into the VmxState field of the ROOT_VMX_STATE data structure for each processor, which
 has two possible values: VMX_STATE_ROOT (1) and VMX_STATE_GUEST (2).

 There is an additional calculation that STM does based on the VMCS link pointer, which sets the
 VmcsType field. When VmState is VMX_STATE_ROOT, the VmcsType is 1 when there is no link VMCS and 2
 when there is a link VMCS. This represents the current VMCS, which that processor may soon do a
 vmlaunch (or vmresume).

 VmcsType equal to 3 represents VmxState as VMX_STATE_GUEST that has been interrupted. Note: the
 VMCS link field for this VmcsType may have some application for nested virtualization, but is not
 calculated. The VMCS link field is provided as LinkVMCS.

Finding the Xen CR3 for each processor.

 For processors that are in VMX_STATE_ROOT the guest CR3 should have the proper value.  However, 
 this gets complicated by the possibility a processor can be executing either a para-virtualized
 (PV) machine or is running idle or managing a guest virtual machine. The idle case would have an
 idle page table associated with it. Each PV would also have an unique page table associated with
 it.  However, there should be a 64MB region of the PV address space that is linked into Xen's
 address space. Note: the protection of this space should checked.

 For processors that are in VMX_STATE_GUEST, the Xen CR3 is found in the host CR3 as the guest CR3
 is local to the virtual machine's address space.

 Note: Ihost values may be be present in the VMX_STATE_ROOT data structures. Don't use these values
 as they seem to be residue values when the processor was in VMX_STATE_GUEST. They are meaningless
 in root operation.

Example of VMX_STATE_GUEST/VmcsType=3

 Note: the (3) is the VmcsType. When the type is 3, H_CR3 is the Xen CR3 (actually idle_pg_table).

  (STM) 9 PrintVmxState (3): execVMCS is guest VMCS: 0x000000102FEB3000 using Executive VMCS
  (STM) 9 PrintVmxState (3) HostRootVmcs 0x000000102FEB3000
          G_CR0 80050033
          G_CR3 7CEE6000
          G_CR4 3626F0
          G_GDTR FFFF9E7A4940C000:7F
          G_IDTR FFFFFFFFFF528000:FFFFFFFF
          G_RSP FFFF9E79BDB2F5C8
          G_RIP FFFFFFFFA4B9E8F5
          VMXON 10493F0000
          ExecutiveVMCS 102FEB3000
          LinkVMCS FFFFFFFFFFFFFFFF
          H_CR0 80050033
          H_CR3 102FFCA000
          H_CR4 3526E0
          H_GDTR FFFF8310755F3000
          H_IDTR FFFF8310493F7000
          H_RSP FFFF8310493FFF70
          H_RIP FFFF82D08031A1D0
          H_EPT 102FFF801E

 This next example is of VMX_STATE_ROOT and note that the VmcsType=1. This is also the processor
 that XHIM is running on. XHIM will always execute on the same processor. This is done in order to
 get away from some OS issues with a processor disappearing during extended SMM, so that the 
 processor has to be "unplugged." Normally, under Xen this is processor #1. However, when there are
 a lot of processors like on a 16 processor server, that selection is not an invariant and any
 processor except for processor 0 can be used. So, to use the XHIM processor values, XHIM needs to 
 figure out which processor it is on or the STM can provide the processor number when the STM
 passes the processor state information to XHIM.

  (STM) 1 PrintVmxState (1): execVMCS is vmxon: 0x0000001075605000 using VMCS_LINK_POINTER
  (STM) 1 PrintVmxState (1) HostRootVmcs 0x0000001075605000
          G_CR0 8005003B
          G_CR3 7DA4A000
          G_CR4 3526E0
          G_GDTR FFFF8310755FF000:EFFF
          G_IDTR FFFF83107560B000:FFFFFFFF
          G_RSP FFFF831075617EA8
          G_RIP FFFF82D0802CE6E8
          VMXON 1075606000
          ExecutiveVMCS 1075606000
          LinkVMCS 1075605000
          H_CR0 0
          H_CR3 0
          H_CR4 0
          H_GDTR 0
          H_IDTR 0
          H_RSP 0
          H_RIP 0
          H_EPT 0


Notes:

  Interrupts and faults:

  The VM/PE module can be configured at setup time to have the STM inject faults.  In this case,
  the VM/PE module must setup an IDT along with the necessary fault handlers.  Otherwise, the STM
  will detect the lack of an IDT and abort the VM/PE when a fault occurs.  Also, when STM is not
  configured to inject faults, the VM/PE will be terminated when a fault occurs.
 
  The VM/PE will not receive external interrupts.

 
  Memory allocation errors: 
  
  Allocation errors for the PE module most likely mean that there is not enough 
  contiguous memory to fit the PE module in.  The memory allocation could be 
  modified to allocate memory in smaller blocks from the STM heap, but as more 
  pages are allocated, the higher the overhead necessary for page tables.  
  Memory allocation errors for the overhead really mean that you should consider
  reducing the size of the PE module.  As at this point heap memory may
  be getting so limited as the affect the operation of the STM itself.
  To prevent this, the size of the PE module should be limited to
  ensure that there is enough heap space for the STM to function.

 
  MSR and I/O access: 
  
  Read and write access to the IA32_EFER_MSR is allowed, write access 
  to other MSRs are ignored and read attempts to other MSRs will return 0.


  IO ports: 
  
  Access attempts to I/O ports are ignored except for 0x3D8 and 0x3F8.

  0x3D8 and 0x3F8 (aka COM2 and COM1 respectively) can be used to send character
  strings through the STM console port.  
  
  No formatting, etc is done (any byte combinations that can pose a security
  issue will be delt with as necessary).  However, the STM will prefix each line with a '(VM/PE)'
  and route the character string to either/both a configured serial port and to the coreboot
  CBMEM console.

  for debugging a VM/PE debugging output can be sent through:

        RDX:    port -   0x3F8 or 0x3D8
        RCX:    number of bytes (maximun length is 200.  Stings longer than that 
                    will be truncated)
        DS:ESI  location in PE/VM where output is located
	            Use either instruction OUTSB/OUTSW/OUTSD  (0x6E or Ox6F)
	            Note: do not use a loop with a rep statement (which is what is normally done)

