3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
25 // Don't use module globals after the SetVirtualAddress map is signaled
27 ESAL_VARIABLE_GLOBAL
*mVariableModuleGlobal
;
30 // This is a temperary function which will be removed
31 // when EfiAcquireLock in UefiLib can handle the
32 // the call in UEFI Runtimer driver in RT phase.
35 AcquireLockOnlyAtBootTime (
39 if (!EfiAtRuntime ()) {
40 EfiAcquireLock (Lock
);
45 // This is a temperary function which will be removed
46 // when EfiAcquireLock in UefiLib can handle the
47 // the call in UEFI Runtimer driver in RT phase.
50 ReleaseLockOnlyAtBootTime (
54 if (!EfiAtRuntime ()) {
55 EfiReleaseLock (Lock
);
70 Determine the length of null terminated char16 array.
74 String Null-terminated CHAR16 array pointer.
78 UINT32 Number of bytes in the string, including the double NULL at the end;
90 while (0 != String
[Count
]) {
94 return (Count
* 2) + 2;
101 IN VARIABLE_HEADER
*Variable
107 This code gets the pointer to the variable data.
111 Variable Pointer to the Variable Header.
115 UINT8* Pointer to Variable Data
119 if (Variable
->StartId
!= VARIABLE_DATA
) {
123 // Be careful about pad size for alignment
125 return (UINT8
*) ((UINTN
) GET_VARIABLE_NAME_PTR (Variable
) + Variable
->NameSize
+ GET_PAD_SIZE (Variable
->NameSize
));
132 IN VARIABLE_HEADER
*Variable
138 This code gets the pointer to the next variable header.
142 Variable Pointer to the Variable Header.
146 VARIABLE_HEADER* Pointer to next variable header.
150 VARIABLE_HEADER
*VarHeader
;
152 if (Variable
->StartId
!= VARIABLE_DATA
) {
156 // Be careful about pad size for alignment
158 VarHeader
= (VARIABLE_HEADER
*) (GetVariableDataPtr (Variable
) + Variable
->DataSize
+ GET_PAD_SIZE (Variable
->DataSize
));
160 if (VarHeader
->StartId
!= VARIABLE_DATA
||
161 (sizeof (VARIABLE_HEADER
) + VarHeader
->DataSize
+ VarHeader
->NameSize
) > MAX_VARIABLE_SIZE
173 IN VARIABLE_STORE_HEADER
*VolHeader
179 This code gets the pointer to the last variable memory pointer byte
183 Variable Pointer to the Variable Header.
187 VARIABLE_HEADER* Pointer to last unavailable Variable Header
192 // The end of variable store
194 return (VARIABLE_HEADER
*) ((UINTN
) VolHeader
+ VolHeader
->Size
);
201 IN CHAR16
*VariableName
,
202 IN EFI_GUID
*VendorGuid
,
203 OUT VARIABLE_POINTER_TRACK
*PtrTrack
,
204 IN VARIABLE_GLOBAL
*Global
210 This code finds variable in storage blocks (Volatile or Non-Volatile)
214 VariableName Name of the variable to be found
215 VendorGuid Vendor GUID to be found.
216 PtrTrack Variable Track Pointer structure that contains
217 Variable Information.
218 Contains the pointer of Variable header.
219 Global VARIABLE_GLOBAL pointer
227 VARIABLE_HEADER
*Variable
[2];
228 VARIABLE_STORE_HEADER
*VariableStoreHeader
[2];
232 // We aquire the lock at the entry of FindVariable as GetVariable, GetNextVariableName
233 // SetVariable all call FindVariable at entry point. Please move "Aquire Lock" to
234 // the correct places if this assumption does not hold TRUE anymore.
236 AcquireLockOnlyAtBootTime(&Global
->VariableServicesLock
);
239 // 0: Non-Volatile, 1: Volatile
241 VariableStoreHeader
[0] = (VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->NonVolatileVariableBase
);
242 VariableStoreHeader
[1] = (VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->VolatileVariableBase
);
245 // Start Pointers for the variable.
246 // Actual Data Pointer where data can be written.
248 Variable
[0] = (VARIABLE_HEADER
*) (VariableStoreHeader
[0] + 1);
249 Variable
[1] = (VARIABLE_HEADER
*) (VariableStoreHeader
[1] + 1);
251 if (VariableName
[0] != 0 && VendorGuid
== NULL
) {
252 return EFI_INVALID_PARAMETER
;
255 // Find the variable by walk through non-volatile and volatile variable store
257 for (Index
= 0; Index
< 2; Index
++) {
258 PtrTrack
->StartPtr
= (VARIABLE_HEADER
*) (VariableStoreHeader
[Index
] + 1);
259 PtrTrack
->EndPtr
= GetEndPointer (VariableStoreHeader
[Index
]);
261 while ((Variable
[Index
] != NULL
) && (Variable
[Index
] <= GetEndPointer (VariableStoreHeader
[Index
]))) {
262 if (Variable
[Index
]->StartId
== VARIABLE_DATA
&& Variable
[Index
]->State
== VAR_ADDED
) {
263 if (!(EfiAtRuntime () && !(Variable
[Index
]->Attributes
& EFI_VARIABLE_RUNTIME_ACCESS
))) {
264 if (VariableName
[0] == 0) {
265 PtrTrack
->CurrPtr
= Variable
[Index
];
266 PtrTrack
->Volatile
= (BOOLEAN
) Index
;
269 if (CompareGuid (VendorGuid
, &Variable
[Index
]->VendorGuid
)) {
270 if (!CompareMem (VariableName
, GET_VARIABLE_NAME_PTR (Variable
[Index
]), ArrayLength (VariableName
))) {
271 PtrTrack
->CurrPtr
= Variable
[Index
];
272 PtrTrack
->Volatile
= (BOOLEAN
) Index
;
280 Variable
[Index
] = GetNextVariablePtr (Variable
[Index
]);
283 PtrTrack
->CurrPtr
= NULL
;
284 return EFI_NOT_FOUND
;
290 IN CHAR16
*VariableName
,
291 IN EFI_GUID
* VendorGuid
,
292 OUT UINT32
*Attributes OPTIONAL
,
293 IN OUT UINTN
*DataSize
,
295 IN VARIABLE_GLOBAL
* Global
,
302 This code finds variable in storage blocks (Volatile or Non-Volatile)
306 VariableName Name of Variable to be found
307 VendorGuid Variable vendor GUID
308 Attributes OPTIONAL Attribute value of the variable found
309 DataSize Size of Data found. If size is less than the
310 data, this value contains the required size.
312 Global Pointer to VARIABLE_GLOBAL structure
313 Instance Instance of the Firmware Volume.
321 VARIABLE_POINTER_TRACK Variable
;
325 if (VariableName
== NULL
|| VendorGuid
== NULL
|| DataSize
== NULL
) {
326 return EFI_INVALID_PARAMETER
;
329 // Find existing variable
331 Status
= FindVariable (VariableName
, VendorGuid
, &Variable
, Global
);
333 if (Variable
.CurrPtr
== NULL
|| EFI_ERROR (Status
)) {
339 VarDataSize
= Variable
.CurrPtr
->DataSize
;
340 if (*DataSize
>= VarDataSize
) {
342 Status
= EFI_INVALID_PARAMETER
;
346 CopyMem (Data
, GetVariableDataPtr (Variable
.CurrPtr
), VarDataSize
);
347 if (Attributes
!= NULL
) {
348 *Attributes
= Variable
.CurrPtr
->Attributes
;
351 *DataSize
= VarDataSize
;
352 Status
= EFI_SUCCESS
;
355 *DataSize
= VarDataSize
;
356 Status
= EFI_BUFFER_TOO_SMALL
;
361 ReleaseLockOnlyAtBootTime (&Global
->VariableServicesLock
);
367 GetNextVariableName (
368 IN OUT UINTN
*VariableNameSize
,
369 IN OUT CHAR16
*VariableName
,
370 IN OUT EFI_GUID
*VendorGuid
,
371 IN VARIABLE_GLOBAL
*Global
,
378 This code Finds the Next available variable
382 VariableNameSize Size of the variable
383 VariableName Pointer to variable name
384 VendorGuid Variable Vendor Guid
385 Global VARIABLE_GLOBAL structure pointer.
394 VARIABLE_POINTER_TRACK Variable
;
398 if (VariableNameSize
== NULL
|| VariableName
== NULL
|| VendorGuid
== NULL
) {
399 return EFI_INVALID_PARAMETER
;
402 Status
= FindVariable (VariableName
, VendorGuid
, &Variable
, Global
);
404 if (Variable
.CurrPtr
== NULL
|| EFI_ERROR (Status
)) {
409 if (VariableName
[0] != 0) {
411 // If variable name is not NULL, get next variable
413 Variable
.CurrPtr
= GetNextVariablePtr (Variable
.CurrPtr
);
416 // If both volatile and non-volatile variable store are parsed,
419 if (Variable
.CurrPtr
>= Variable
.EndPtr
|| Variable
.CurrPtr
== NULL
) {
420 Variable
.Volatile
= (BOOLEAN
) (Variable
.Volatile
^ ((BOOLEAN
) 0x1));
421 if (Variable
.Volatile
) {
422 Variable
.StartPtr
= (VARIABLE_HEADER
*) ((UINTN
) (Global
->VolatileVariableBase
+ sizeof (VARIABLE_STORE_HEADER
)));
423 Variable
.EndPtr
= (VARIABLE_HEADER
*) GetEndPointer ((VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->VolatileVariableBase
));
425 Status
= EFI_NOT_FOUND
;
429 Variable
.CurrPtr
= Variable
.StartPtr
;
430 if (Variable
.CurrPtr
->StartId
!= VARIABLE_DATA
) {
437 if (Variable
.CurrPtr
->StartId
== VARIABLE_DATA
&& Variable
.CurrPtr
->State
== VAR_ADDED
) {
438 if (!(EfiAtRuntime () && !(Variable
.CurrPtr
->Attributes
& EFI_VARIABLE_RUNTIME_ACCESS
))) {
439 VarNameSize
= Variable
.CurrPtr
->NameSize
;
440 if (VarNameSize
<= *VariableNameSize
) {
443 GET_VARIABLE_NAME_PTR (Variable
.CurrPtr
),
448 &Variable
.CurrPtr
->VendorGuid
,
451 Status
= EFI_SUCCESS
;
453 Status
= EFI_BUFFER_TOO_SMALL
;
456 *VariableNameSize
= VarNameSize
;
463 ReleaseLockOnlyAtBootTime (&Global
->VariableServicesLock
);
471 IN CHAR16
*VariableName
,
472 IN EFI_GUID
*VendorGuid
,
473 IN UINT32 Attributes
,
476 IN VARIABLE_GLOBAL
*Global
,
477 IN UINTN
*VolatileOffset
,
478 IN UINTN
*NonVolatileOffset
,
485 This code sets variable in storage blocks (Volatile or Non-Volatile)
489 VariableName Name of Variable to be found
490 VendorGuid Variable vendor GUID
491 Attributes Attribute value of the variable found
492 DataSize Size of Data found. If size is less than the
493 data, this value contains the required size.
495 Global Pointer to VARIABLE_GLOBAL structure
496 VolatileOffset The offset of last volatile variable
497 NonVolatileOffset The offset of last non-volatile variable
498 Instance Instance of the Firmware Volume.
506 VARIABLE_POINTER_TRACK Variable
;
508 VARIABLE_HEADER
*NextVariable
;
514 if (VariableName
== NULL
|| VariableName
[0] == 0 || VendorGuid
== NULL
) {
515 return EFI_INVALID_PARAMETER
;
518 Status
= FindVariable (VariableName
, VendorGuid
, &Variable
, Global
);
520 if (Status
== EFI_INVALID_PARAMETER
) {
522 } else if (!EFI_ERROR (Status
) && Variable
.Volatile
&& EfiAtRuntime()) {
524 // If EfiAtRuntime and the variable is Volatile and Runtime Access,
525 // the volatile is ReadOnly, and SetVariable should be aborted and
526 // return EFI_WRITE_PROTECTED.
528 Status
= EFI_WRITE_PROTECTED
;
530 } else if (sizeof (VARIABLE_HEADER
) + (ArrayLength (VariableName
) + DataSize
) > MAX_VARIABLE_SIZE
) {
532 // The size of the VariableName, including the Unicode Null in bytes plus
533 // the DataSize is limited to maximum size of MAX_VARIABLE_SIZE (1024) bytes.
535 Status
= EFI_INVALID_PARAMETER
;
537 } else if ((Attributes
& (EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS
)) == EFI_VARIABLE_RUNTIME_ACCESS
540 // Make sure if runtime bit is set, boot service bit is set also
542 Status
= EFI_INVALID_PARAMETER
;
544 } else if (EfiAtRuntime () && Attributes
&& !(Attributes
& EFI_VARIABLE_RUNTIME_ACCESS
)) {
546 // Runtime but Attribute is not Runtime
548 Status
= EFI_INVALID_PARAMETER
;
550 } else if (EfiAtRuntime () && Attributes
&& !(Attributes
& EFI_VARIABLE_NON_VOLATILE
)) {
552 // Cannot set volatile variable in Runtime
554 Status
= EFI_INVALID_PARAMETER
;
556 } else if (DataSize
== 0 || (Attributes
& (EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS
)) == 0) {
558 // Setting a data variable with no access, or zero DataSize attributes
559 // specified causes it to be deleted.
561 if (!EFI_ERROR (Status
)) {
562 Variable
.CurrPtr
->State
&= VAR_DELETED
;
563 Status
= EFI_SUCCESS
;
567 Status
= EFI_NOT_FOUND
;
570 if (!EFI_ERROR (Status
)) {
572 // If the variable is marked valid and the same data has been passed in
573 // then return to the caller immediately.
575 if (Variable
.CurrPtr
->DataSize
== DataSize
&&
576 !CompareMem (Data
, GetVariableDataPtr (Variable
.CurrPtr
), DataSize
)
578 Status
= EFI_SUCCESS
;
580 } else if (Variable
.CurrPtr
->State
== VAR_ADDED
) {
582 // Mark the old variable as in delete transition
584 Variable
.CurrPtr
->State
&= VAR_IN_DELETED_TRANSITION
;
588 // Create a new variable and copy the data.
590 VarNameOffset
= sizeof (VARIABLE_HEADER
);
591 VarNameSize
= ArrayLength (VariableName
);
592 VarDataOffset
= VarNameOffset
+ VarNameSize
+ GET_PAD_SIZE (VarNameSize
);
593 VarSize
= VarDataOffset
+ DataSize
+ GET_PAD_SIZE (DataSize
);
595 if (Attributes
& EFI_VARIABLE_NON_VOLATILE
) {
596 if ((UINT32
) (VarSize
+*NonVolatileOffset
) >
597 ((VARIABLE_STORE_HEADER
*) ((UINTN
) (Global
->NonVolatileVariableBase
)))->Size
599 Status
= EFI_OUT_OF_RESOURCES
;
603 NextVariable
= (VARIABLE_HEADER
*) (UINT8
*) (*NonVolatileOffset
+ (UINTN
) Global
->NonVolatileVariableBase
);
604 *NonVolatileOffset
= *NonVolatileOffset
+ VarSize
;
606 if (EfiAtRuntime ()) {
607 Status
= EFI_INVALID_PARAMETER
;
611 if ((UINT32
) (VarSize
+*VolatileOffset
) >
612 ((VARIABLE_STORE_HEADER
*) ((UINTN
) (Global
->VolatileVariableBase
)))->Size
614 Status
= EFI_OUT_OF_RESOURCES
;
618 NextVariable
= (VARIABLE_HEADER
*) (UINT8
*) (*VolatileOffset
+ (UINTN
) Global
->VolatileVariableBase
);
619 *VolatileOffset
= *VolatileOffset
+ VarSize
;
622 NextVariable
->StartId
= VARIABLE_DATA
;
623 NextVariable
->Attributes
= Attributes
;
624 NextVariable
->State
= VAR_ADDED
;
625 NextVariable
->Reserved
= 0;
628 // There will be pad bytes after Data, the NextVariable->NameSize and
629 // NextVariable->NameSize should not include pad size so that variable
630 // service can get actual size in GetVariable
632 NextVariable
->NameSize
= (UINT32
)VarNameSize
;
633 NextVariable
->DataSize
= (UINT32
)DataSize
;
635 CopyMem (&NextVariable
->VendorGuid
, VendorGuid
, sizeof (EFI_GUID
));
637 (UINT8
*) ((UINTN
) NextVariable
+ VarNameOffset
),
642 (UINT8
*) ((UINTN
) NextVariable
+ VarDataOffset
),
648 // Mark the old variable as deleted
650 if (!EFI_ERROR (Status
)) {
651 Variable
.CurrPtr
->State
&= VAR_DELETED
;
655 Status
= EFI_SUCCESS
;
657 ReleaseLockOnlyAtBootTime (&Global
->VariableServicesLock
);
661 #if (EFI_SPECIFICATION_VERSION >= 0x00020000)
665 IN UINT32 Attributes
,
666 OUT UINT64
*MaximumVariableStorageSize
,
667 OUT UINT64
*RemainingVariableStorageSize
,
668 OUT UINT64
*MaximumVariableSize
,
669 IN VARIABLE_GLOBAL
*Global
,
676 This code returns information about the EFI variables.
680 Attributes Attributes bitmask to specify the type of variables
681 on which to return information.
682 MaximumVariableStorageSize Pointer to the maximum size of the storage space available
683 for the EFI variables associated with the attributes specified.
684 RemainingVariableStorageSize Pointer to the remaining size of the storage space available
685 for the EFI variables associated with the attributes specified.
686 MaximumVariableSize Pointer to the maximum size of the individual EFI variables
687 associated with the attributes specified.
688 Global Pointer to VARIABLE_GLOBAL structure.
689 Instance Instance of the Firmware Volume.
694 EFI_INVALID_PARAMETER - An invalid combination of attribute bits was supplied.
695 EFI_SUCCESS - Query successfully.
696 EFI_UNSUPPORTED - The attribute is not supported on this platform.
700 VARIABLE_HEADER
*Variable
;
701 VARIABLE_HEADER
*NextVariable
;
703 VARIABLE_STORE_HEADER
*VariableStoreHeader
;
705 if(MaximumVariableStorageSize
== NULL
|| RemainingVariableStorageSize
== NULL
|| MaximumVariableSize
== NULL
) {
706 return EFI_INVALID_PARAMETER
;
709 if((Attributes
& (EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
)) == 0) {
711 // Make sure the Attributes combination is supported by the platform.
713 return EFI_UNSUPPORTED
;
714 } else if ((Attributes
& (EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS
)) == EFI_VARIABLE_RUNTIME_ACCESS
) {
716 // Make sure if runtime bit is set, boot service bit is set also.
718 return EFI_INVALID_PARAMETER
;
719 } else if (EfiAtRuntime () && !(Attributes
& EFI_VARIABLE_RUNTIME_ACCESS
)) {
721 // Make sure RT Attribute is set if we are in Runtime phase.
723 return EFI_INVALID_PARAMETER
;
724 } else if (EfiAtRuntime () && Attributes
&& !(Attributes
& EFI_VARIABLE_NON_VOLATILE
)) {
726 // Cannot Query volatile variable in Runtime
728 return EFI_INVALID_PARAMETER
;
731 AcquireLockOnlyAtBootTime(&Global
->VariableServicesLock
);
733 if((Attributes
& EFI_VARIABLE_NON_VOLATILE
) == 0) {
735 // Query is Volatile related.
737 VariableStoreHeader
= (VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->VolatileVariableBase
);
740 // Query is Non-Volatile related.
742 VariableStoreHeader
= (VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->NonVolatileVariableBase
);
746 // Now let's fill *MaximumVariableStorageSize *RemainingVariableStorageSize
747 // with the storage size (excluding the storage header size)
749 *MaximumVariableStorageSize
= VariableStoreHeader
->Size
- sizeof (VARIABLE_STORE_HEADER
);
750 *RemainingVariableStorageSize
= VariableStoreHeader
->Size
- sizeof (VARIABLE_STORE_HEADER
);
753 // Let *MaximumVariableSize be MAX_VARIABLE_SIZE
755 *MaximumVariableSize
= MAX_VARIABLE_SIZE
;
758 // Point to the starting address of the variables.
760 Variable
= (VARIABLE_HEADER
*) (VariableStoreHeader
+ 1);
763 // Now walk through the related variable store.
765 while (Variable
< GetEndPointer (VariableStoreHeader
)) {
766 if (Variable
->StartId
!= VARIABLE_DATA
) {
770 NextVariable
= (VARIABLE_HEADER
*) (GetVariableDataPtr (Variable
) + Variable
->DataSize
+ GET_PAD_SIZE (Variable
->DataSize
));
771 VariableSize
= (UINT64
) (UINTN
) NextVariable
- (UINT64
) (UINTN
) Variable
;
773 if (Variable
->State
== VAR_ADDED
) {
774 *RemainingVariableStorageSize
-= VariableSize
;
778 // Go to the next one.
780 Variable
= NextVariable
;
783 ReleaseLockOnlyAtBootTime (&Global
->VariableServicesLock
);
791 InitializeVariableStore (
792 OUT EFI_PHYSICAL_ADDRESS
*VariableBase
,
793 OUT UINTN
*LastVariableOffset
798 This function initializes variable store
806 VARIABLE_STORE_HEADER
*VariableStore
;
809 // Allocate memory for volatile variable store
811 VariableStore
= (VARIABLE_STORE_HEADER
*) AllocateRuntimePool (
814 if (NULL
== VariableStore
) {
815 return EFI_OUT_OF_RESOURCES
;
818 SetMem (VariableStore
, VARIABLE_STORE_SIZE
, 0xff);
821 // Variable Specific Data
823 *VariableBase
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) VariableStore
;
824 *LastVariableOffset
= sizeof (VARIABLE_STORE_HEADER
);
826 VariableStore
->Signature
= VARIABLE_STORE_SIGNATURE
;
827 VariableStore
->Size
= VARIABLE_STORE_SIZE
;
828 VariableStore
->Format
= VARIABLE_STORE_FORMATTED
;
829 VariableStore
->State
= VARIABLE_STORE_HEALTHY
;
830 VariableStore
->Reserved
= 0;
831 VariableStore
->Reserved1
= 0;
838 VariableCommonInitialize (
839 IN EFI_HANDLE ImageHandle
,
840 IN EFI_SYSTEM_TABLE
*SystemTable
845 This function does common initialization for variable services
856 // Allocate memory for mVariableModuleGlobal
858 mVariableModuleGlobal
= (ESAL_VARIABLE_GLOBAL
*) AllocateRuntimePool (
859 sizeof (ESAL_VARIABLE_GLOBAL
)
861 if (NULL
== mVariableModuleGlobal
) {
862 return EFI_OUT_OF_RESOURCES
;
865 EfiInitializeLock(&mVariableModuleGlobal
->VariableGlobal
[Physical
].VariableServicesLock
, EFI_TPL_CALLBACK
);
868 // Intialize volatile variable store
870 Status
= InitializeVariableStore (
871 &mVariableModuleGlobal
->VariableGlobal
[Physical
].VolatileVariableBase
,
872 &mVariableModuleGlobal
->VolatileLastVariableOffset
875 if (EFI_ERROR (Status
)) {
879 // Intialize non volatile variable store
881 Status
= InitializeVariableStore (
882 &mVariableModuleGlobal
->VariableGlobal
[Physical
].NonVolatileVariableBase
,
883 &mVariableModuleGlobal
->NonVolatileLastVariableOffset