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.
36 AcquireLockOnlyAtBootTime (
40 if (!EfiAtRuntime ()) {
41 EfiAcquireLock (Lock
);
46 // This is a temperary function which will be removed
47 // when EfiAcquireLock in UefiLib can handle the
48 // the call in UEFI Runtimer driver in RT phase.
52 ReleaseLockOnlyAtBootTime (
56 if (!EfiAtRuntime ()) {
57 EfiReleaseLock (Lock
);
72 Determine the length of null terminated char16 array.
76 String Null-terminated CHAR16 array pointer.
80 UINT32 Number of bytes in the string, including the double NULL at the end;
92 while (0 != String
[Count
]) {
96 return (Count
* 2) + 2;
103 IN VARIABLE_HEADER
*Variable
109 This code gets the pointer to the variable data.
113 Variable Pointer to the Variable Header.
117 UINT8* Pointer to Variable Data
121 if (Variable
->StartId
!= VARIABLE_DATA
) {
125 // Be careful about pad size for alignment
127 return (UINT8
*) ((UINTN
) GET_VARIABLE_NAME_PTR (Variable
) + Variable
->NameSize
+ GET_PAD_SIZE (Variable
->NameSize
));
134 IN VARIABLE_HEADER
*Variable
140 This code gets the pointer to the next variable header.
144 Variable Pointer to the Variable Header.
148 VARIABLE_HEADER* Pointer to next variable header.
152 VARIABLE_HEADER
*VarHeader
;
154 if (Variable
->StartId
!= VARIABLE_DATA
) {
158 // Be careful about pad size for alignment
160 VarHeader
= (VARIABLE_HEADER
*) (GetVariableDataPtr (Variable
) + Variable
->DataSize
+ GET_PAD_SIZE (Variable
->DataSize
));
162 if (VarHeader
->StartId
!= VARIABLE_DATA
||
163 (sizeof (VARIABLE_HEADER
) + VarHeader
->DataSize
+ VarHeader
->NameSize
) > MAX_VARIABLE_SIZE
175 IN VARIABLE_STORE_HEADER
*VolHeader
181 This code gets the pointer to the last variable memory pointer byte
185 Variable Pointer to the Variable Header.
189 VARIABLE_HEADER* Pointer to last unavailable Variable Header
194 // The end of variable store
196 return (VARIABLE_HEADER
*) ((UINTN
) VolHeader
+ VolHeader
->Size
);
203 IN CHAR16
*VariableName
,
204 IN EFI_GUID
*VendorGuid
,
205 OUT VARIABLE_POINTER_TRACK
*PtrTrack
,
206 IN VARIABLE_GLOBAL
*Global
212 This code finds variable in storage blocks (Volatile or Non-Volatile)
216 VariableName Name of the variable to be found
217 VendorGuid Vendor GUID to be found.
218 PtrTrack Variable Track Pointer structure that contains
219 Variable Information.
220 Contains the pointer of Variable header.
221 Global VARIABLE_GLOBAL pointer
229 VARIABLE_HEADER
*Variable
[2];
230 VARIABLE_STORE_HEADER
*VariableStoreHeader
[2];
234 // We aquire the lock at the entry of FindVariable as GetVariable, GetNextVariableName
235 // SetVariable all call FindVariable at entry point. Please move "Aquire Lock" to
236 // the correct places if this assumption does not hold TRUE anymore.
238 AcquireLockOnlyAtBootTime(&Global
->VariableServicesLock
);
241 // 0: Non-Volatile, 1: Volatile
243 VariableStoreHeader
[0] = (VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->NonVolatileVariableBase
);
244 VariableStoreHeader
[1] = (VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->VolatileVariableBase
);
247 // Start Pointers for the variable.
248 // Actual Data Pointer where data can be written.
250 Variable
[0] = (VARIABLE_HEADER
*) (VariableStoreHeader
[0] + 1);
251 Variable
[1] = (VARIABLE_HEADER
*) (VariableStoreHeader
[1] + 1);
253 if (VariableName
[0] != 0 && VendorGuid
== NULL
) {
254 return EFI_INVALID_PARAMETER
;
257 // Find the variable by walk through non-volatile and volatile variable store
259 for (Index
= 0; Index
< 2; Index
++) {
260 PtrTrack
->StartPtr
= (VARIABLE_HEADER
*) (VariableStoreHeader
[Index
] + 1);
261 PtrTrack
->EndPtr
= GetEndPointer (VariableStoreHeader
[Index
]);
263 while ((Variable
[Index
] != NULL
) && (Variable
[Index
] <= GetEndPointer (VariableStoreHeader
[Index
]))) {
264 if (Variable
[Index
]->StartId
== VARIABLE_DATA
&& Variable
[Index
]->State
== VAR_ADDED
) {
265 if (!(EfiAtRuntime () && !(Variable
[Index
]->Attributes
& EFI_VARIABLE_RUNTIME_ACCESS
))) {
266 if (VariableName
[0] == 0) {
267 PtrTrack
->CurrPtr
= Variable
[Index
];
268 PtrTrack
->Volatile
= (BOOLEAN
) Index
;
271 if (CompareGuid (VendorGuid
, &Variable
[Index
]->VendorGuid
)) {
272 if (!CompareMem (VariableName
, GET_VARIABLE_NAME_PTR (Variable
[Index
]), ArrayLength (VariableName
))) {
273 PtrTrack
->CurrPtr
= Variable
[Index
];
274 PtrTrack
->Volatile
= (BOOLEAN
) Index
;
282 Variable
[Index
] = GetNextVariablePtr (Variable
[Index
]);
285 PtrTrack
->CurrPtr
= NULL
;
286 return EFI_NOT_FOUND
;
292 IN CHAR16
*VariableName
,
293 IN EFI_GUID
* VendorGuid
,
294 OUT UINT32
*Attributes OPTIONAL
,
295 IN OUT UINTN
*DataSize
,
297 IN VARIABLE_GLOBAL
* Global
,
304 This code finds variable in storage blocks (Volatile or Non-Volatile)
308 VariableName Name of Variable to be found
309 VendorGuid Variable vendor GUID
310 Attributes OPTIONAL Attribute value of the variable found
311 DataSize Size of Data found. If size is less than the
312 data, this value contains the required size.
314 Global Pointer to VARIABLE_GLOBAL structure
315 Instance Instance of the Firmware Volume.
323 VARIABLE_POINTER_TRACK Variable
;
327 if (VariableName
== NULL
|| VendorGuid
== NULL
|| DataSize
== NULL
) {
328 return EFI_INVALID_PARAMETER
;
331 // Find existing variable
333 Status
= FindVariable (VariableName
, VendorGuid
, &Variable
, Global
);
335 if (Variable
.CurrPtr
== NULL
|| EFI_ERROR (Status
)) {
341 VarDataSize
= Variable
.CurrPtr
->DataSize
;
342 if (*DataSize
>= VarDataSize
) {
344 Status
= EFI_INVALID_PARAMETER
;
348 CopyMem (Data
, GetVariableDataPtr (Variable
.CurrPtr
), VarDataSize
);
349 if (Attributes
!= NULL
) {
350 *Attributes
= Variable
.CurrPtr
->Attributes
;
353 *DataSize
= VarDataSize
;
354 Status
= EFI_SUCCESS
;
357 *DataSize
= VarDataSize
;
358 Status
= EFI_BUFFER_TOO_SMALL
;
363 ReleaseLockOnlyAtBootTime (&Global
->VariableServicesLock
);
369 GetNextVariableName (
370 IN OUT UINTN
*VariableNameSize
,
371 IN OUT CHAR16
*VariableName
,
372 IN OUT EFI_GUID
*VendorGuid
,
373 IN VARIABLE_GLOBAL
*Global
,
380 This code Finds the Next available variable
384 VariableNameSize Size of the variable
385 VariableName Pointer to variable name
386 VendorGuid Variable Vendor Guid
387 Global VARIABLE_GLOBAL structure pointer.
396 VARIABLE_POINTER_TRACK Variable
;
400 if (VariableNameSize
== NULL
|| VariableName
== NULL
|| VendorGuid
== NULL
) {
401 return EFI_INVALID_PARAMETER
;
404 Status
= FindVariable (VariableName
, VendorGuid
, &Variable
, Global
);
406 if (Variable
.CurrPtr
== NULL
|| EFI_ERROR (Status
)) {
411 if (VariableName
[0] != 0) {
413 // If variable name is not NULL, get next variable
415 Variable
.CurrPtr
= GetNextVariablePtr (Variable
.CurrPtr
);
418 // If both volatile and non-volatile variable store are parsed,
421 if (Variable
.CurrPtr
>= Variable
.EndPtr
|| Variable
.CurrPtr
== NULL
) {
422 Variable
.Volatile
= (BOOLEAN
) (Variable
.Volatile
^ ((BOOLEAN
) 0x1));
423 if (Variable
.Volatile
) {
424 Variable
.StartPtr
= (VARIABLE_HEADER
*) ((UINTN
) (Global
->VolatileVariableBase
+ sizeof (VARIABLE_STORE_HEADER
)));
425 Variable
.EndPtr
= (VARIABLE_HEADER
*) GetEndPointer ((VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->VolatileVariableBase
));
427 Status
= EFI_NOT_FOUND
;
431 Variable
.CurrPtr
= Variable
.StartPtr
;
432 if (Variable
.CurrPtr
->StartId
!= VARIABLE_DATA
) {
439 if (Variable
.CurrPtr
->StartId
== VARIABLE_DATA
&& Variable
.CurrPtr
->State
== VAR_ADDED
) {
440 if (!(EfiAtRuntime () && !(Variable
.CurrPtr
->Attributes
& EFI_VARIABLE_RUNTIME_ACCESS
))) {
441 VarNameSize
= Variable
.CurrPtr
->NameSize
;
442 if (VarNameSize
<= *VariableNameSize
) {
445 GET_VARIABLE_NAME_PTR (Variable
.CurrPtr
),
450 &Variable
.CurrPtr
->VendorGuid
,
453 Status
= EFI_SUCCESS
;
455 Status
= EFI_BUFFER_TOO_SMALL
;
458 *VariableNameSize
= VarNameSize
;
465 ReleaseLockOnlyAtBootTime (&Global
->VariableServicesLock
);
473 IN CHAR16
*VariableName
,
474 IN EFI_GUID
*VendorGuid
,
475 IN UINT32 Attributes
,
478 IN VARIABLE_GLOBAL
*Global
,
479 IN UINTN
*VolatileOffset
,
480 IN UINTN
*NonVolatileOffset
,
487 This code sets variable in storage blocks (Volatile or Non-Volatile)
491 VariableName Name of Variable to be found
492 VendorGuid Variable vendor GUID
493 Attributes Attribute value of the variable found
494 DataSize Size of Data found. If size is less than the
495 data, this value contains the required size.
497 Global Pointer to VARIABLE_GLOBAL structure
498 VolatileOffset The offset of last volatile variable
499 NonVolatileOffset The offset of last non-volatile variable
500 Instance Instance of the Firmware Volume.
508 VARIABLE_POINTER_TRACK Variable
;
510 VARIABLE_HEADER
*NextVariable
;
516 if (VariableName
== NULL
|| VariableName
[0] == 0 || VendorGuid
== NULL
) {
517 return EFI_INVALID_PARAMETER
;
520 Status
= FindVariable (VariableName
, VendorGuid
, &Variable
, Global
);
522 if (Status
== EFI_INVALID_PARAMETER
) {
524 } else if (!EFI_ERROR (Status
) && Variable
.Volatile
&& EfiAtRuntime()) {
526 // If EfiAtRuntime and the variable is Volatile and Runtime Access,
527 // the volatile is ReadOnly, and SetVariable should be aborted and
528 // return EFI_WRITE_PROTECTED.
530 Status
= EFI_WRITE_PROTECTED
;
532 } else if (sizeof (VARIABLE_HEADER
) + (ArrayLength (VariableName
) + DataSize
) > MAX_VARIABLE_SIZE
) {
534 // The size of the VariableName, including the Unicode Null in bytes plus
535 // the DataSize is limited to maximum size of MAX_VARIABLE_SIZE (1024) bytes.
537 Status
= EFI_INVALID_PARAMETER
;
539 } else if ((Attributes
& (EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS
)) == EFI_VARIABLE_RUNTIME_ACCESS
542 // Make sure if runtime bit is set, boot service bit is set also
544 Status
= EFI_INVALID_PARAMETER
;
546 } else if (EfiAtRuntime () && Attributes
&& !(Attributes
& EFI_VARIABLE_RUNTIME_ACCESS
)) {
548 // Runtime but Attribute is not Runtime
550 Status
= EFI_INVALID_PARAMETER
;
552 } else if (EfiAtRuntime () && Attributes
&& !(Attributes
& EFI_VARIABLE_NON_VOLATILE
)) {
554 // Cannot set volatile variable in Runtime
556 Status
= EFI_INVALID_PARAMETER
;
558 } else if (DataSize
== 0 || (Attributes
& (EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS
)) == 0) {
560 // Setting a data variable with no access, or zero DataSize attributes
561 // specified causes it to be deleted.
563 if (!EFI_ERROR (Status
)) {
564 Variable
.CurrPtr
->State
&= VAR_DELETED
;
565 Status
= EFI_SUCCESS
;
569 Status
= EFI_NOT_FOUND
;
572 if (!EFI_ERROR (Status
)) {
574 // If the variable is marked valid and the same data has been passed in
575 // then return to the caller immediately.
577 if (Variable
.CurrPtr
->DataSize
== DataSize
&&
578 !CompareMem (Data
, GetVariableDataPtr (Variable
.CurrPtr
), DataSize
)
580 Status
= EFI_SUCCESS
;
582 } else if (Variable
.CurrPtr
->State
== VAR_ADDED
) {
584 // Mark the old variable as in delete transition
586 Variable
.CurrPtr
->State
&= VAR_IN_DELETED_TRANSITION
;
590 // Create a new variable and copy the data.
592 VarNameOffset
= sizeof (VARIABLE_HEADER
);
593 VarNameSize
= ArrayLength (VariableName
);
594 VarDataOffset
= VarNameOffset
+ VarNameSize
+ GET_PAD_SIZE (VarNameSize
);
595 VarSize
= VarDataOffset
+ DataSize
+ GET_PAD_SIZE (DataSize
);
597 if (Attributes
& EFI_VARIABLE_NON_VOLATILE
) {
598 if ((UINT32
) (VarSize
+*NonVolatileOffset
) >
599 ((VARIABLE_STORE_HEADER
*) ((UINTN
) (Global
->NonVolatileVariableBase
)))->Size
601 Status
= EFI_OUT_OF_RESOURCES
;
605 NextVariable
= (VARIABLE_HEADER
*) (UINT8
*) (*NonVolatileOffset
+ (UINTN
) Global
->NonVolatileVariableBase
);
606 *NonVolatileOffset
= *NonVolatileOffset
+ VarSize
;
608 if (EfiAtRuntime ()) {
609 Status
= EFI_INVALID_PARAMETER
;
613 if ((UINT32
) (VarSize
+*VolatileOffset
) >
614 ((VARIABLE_STORE_HEADER
*) ((UINTN
) (Global
->VolatileVariableBase
)))->Size
616 Status
= EFI_OUT_OF_RESOURCES
;
620 NextVariable
= (VARIABLE_HEADER
*) (UINT8
*) (*VolatileOffset
+ (UINTN
) Global
->VolatileVariableBase
);
621 *VolatileOffset
= *VolatileOffset
+ VarSize
;
624 NextVariable
->StartId
= VARIABLE_DATA
;
625 NextVariable
->Attributes
= Attributes
;
626 NextVariable
->State
= VAR_ADDED
;
627 NextVariable
->Reserved
= 0;
630 // There will be pad bytes after Data, the NextVariable->NameSize and
631 // NextVariable->NameSize should not include pad size so that variable
632 // service can get actual size in GetVariable
634 NextVariable
->NameSize
= (UINT32
)VarNameSize
;
635 NextVariable
->DataSize
= (UINT32
)DataSize
;
637 CopyMem (&NextVariable
->VendorGuid
, VendorGuid
, sizeof (EFI_GUID
));
639 (UINT8
*) ((UINTN
) NextVariable
+ VarNameOffset
),
644 (UINT8
*) ((UINTN
) NextVariable
+ VarDataOffset
),
650 // Mark the old variable as deleted
652 if (!EFI_ERROR (Status
)) {
653 Variable
.CurrPtr
->State
&= VAR_DELETED
;
657 Status
= EFI_SUCCESS
;
659 ReleaseLockOnlyAtBootTime (&Global
->VariableServicesLock
);
663 #if (EFI_SPECIFICATION_VERSION >= 0x00020000)
667 IN UINT32 Attributes
,
668 OUT UINT64
*MaximumVariableStorageSize
,
669 OUT UINT64
*RemainingVariableStorageSize
,
670 OUT UINT64
*MaximumVariableSize
,
671 IN VARIABLE_GLOBAL
*Global
,
678 This code returns information about the EFI variables.
682 Attributes Attributes bitmask to specify the type of variables
683 on which to return information.
684 MaximumVariableStorageSize Pointer to the maximum size of the storage space available
685 for the EFI variables associated with the attributes specified.
686 RemainingVariableStorageSize Pointer to the remaining size of the storage space available
687 for the EFI variables associated with the attributes specified.
688 MaximumVariableSize Pointer to the maximum size of the individual EFI variables
689 associated with the attributes specified.
690 Global Pointer to VARIABLE_GLOBAL structure.
691 Instance Instance of the Firmware Volume.
696 EFI_INVALID_PARAMETER - An invalid combination of attribute bits was supplied.
697 EFI_SUCCESS - Query successfully.
698 EFI_UNSUPPORTED - The attribute is not supported on this platform.
702 VARIABLE_HEADER
*Variable
;
703 VARIABLE_HEADER
*NextVariable
;
705 VARIABLE_STORE_HEADER
*VariableStoreHeader
;
707 if(MaximumVariableStorageSize
== NULL
|| RemainingVariableStorageSize
== NULL
|| MaximumVariableSize
== NULL
) {
708 return EFI_INVALID_PARAMETER
;
711 if((Attributes
& (EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
)) == 0) {
713 // Make sure the Attributes combination is supported by the platform.
715 return EFI_UNSUPPORTED
;
716 } else if ((Attributes
& (EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS
)) == EFI_VARIABLE_RUNTIME_ACCESS
) {
718 // Make sure if runtime bit is set, boot service bit is set also.
720 return EFI_INVALID_PARAMETER
;
721 } else if (EfiAtRuntime () && !(Attributes
& EFI_VARIABLE_RUNTIME_ACCESS
)) {
723 // Make sure RT Attribute is set if we are in Runtime phase.
725 return EFI_INVALID_PARAMETER
;
726 } else if (EfiAtRuntime () && Attributes
&& !(Attributes
& EFI_VARIABLE_NON_VOLATILE
)) {
728 // Cannot Query volatile variable in Runtime
730 return EFI_INVALID_PARAMETER
;
733 AcquireLockOnlyAtBootTime(&Global
->VariableServicesLock
);
735 if((Attributes
& EFI_VARIABLE_NON_VOLATILE
) == 0) {
737 // Query is Volatile related.
739 VariableStoreHeader
= (VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->VolatileVariableBase
);
742 // Query is Non-Volatile related.
744 VariableStoreHeader
= (VARIABLE_STORE_HEADER
*) ((UINTN
) Global
->NonVolatileVariableBase
);
748 // Now let's fill *MaximumVariableStorageSize *RemainingVariableStorageSize
749 // with the storage size (excluding the storage header size)
751 *MaximumVariableStorageSize
= VariableStoreHeader
->Size
- sizeof (VARIABLE_STORE_HEADER
);
752 *RemainingVariableStorageSize
= VariableStoreHeader
->Size
- sizeof (VARIABLE_STORE_HEADER
);
755 // Let *MaximumVariableSize be MAX_VARIABLE_SIZE
757 *MaximumVariableSize
= MAX_VARIABLE_SIZE
;
760 // Point to the starting address of the variables.
762 Variable
= (VARIABLE_HEADER
*) (VariableStoreHeader
+ 1);
765 // Now walk through the related variable store.
767 while (Variable
< GetEndPointer (VariableStoreHeader
)) {
768 if (Variable
->StartId
!= VARIABLE_DATA
) {
772 NextVariable
= (VARIABLE_HEADER
*) (GetVariableDataPtr (Variable
) + Variable
->DataSize
+ GET_PAD_SIZE (Variable
->DataSize
));
773 VariableSize
= (UINT64
) (UINTN
) NextVariable
- (UINT64
) (UINTN
) Variable
;
775 if (Variable
->State
== VAR_ADDED
) {
776 *RemainingVariableStorageSize
-= VariableSize
;
780 // Go to the next one.
782 Variable
= NextVariable
;
785 ReleaseLockOnlyAtBootTime (&Global
->VariableServicesLock
);
793 InitializeVariableStore (
794 OUT EFI_PHYSICAL_ADDRESS
*VariableBase
,
795 OUT UINTN
*LastVariableOffset
800 This function initializes variable store
808 VARIABLE_STORE_HEADER
*VariableStore
;
811 // Allocate memory for volatile variable store
813 VariableStore
= (VARIABLE_STORE_HEADER
*) AllocateRuntimePool (
816 if (NULL
== VariableStore
) {
817 return EFI_OUT_OF_RESOURCES
;
820 SetMem (VariableStore
, VARIABLE_STORE_SIZE
, 0xff);
823 // Variable Specific Data
825 *VariableBase
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) VariableStore
;
826 *LastVariableOffset
= sizeof (VARIABLE_STORE_HEADER
);
828 VariableStore
->Signature
= VARIABLE_STORE_SIGNATURE
;
829 VariableStore
->Size
= VARIABLE_STORE_SIZE
;
830 VariableStore
->Format
= VARIABLE_STORE_FORMATTED
;
831 VariableStore
->State
= VARIABLE_STORE_HEALTHY
;
832 VariableStore
->Reserved
= 0;
833 VariableStore
->Reserved1
= 0;
840 VariableCommonInitialize (
841 IN EFI_HANDLE ImageHandle
,
842 IN EFI_SYSTEM_TABLE
*SystemTable
847 This function does common initialization for variable services
858 // Allocate memory for mVariableModuleGlobal
860 mVariableModuleGlobal
= (ESAL_VARIABLE_GLOBAL
*) AllocateRuntimePool (
861 sizeof (ESAL_VARIABLE_GLOBAL
)
863 if (NULL
== mVariableModuleGlobal
) {
864 return EFI_OUT_OF_RESOURCES
;
867 EfiInitializeLock(&mVariableModuleGlobal
->VariableGlobal
[Physical
].VariableServicesLock
, EFI_TPL_CALLBACK
);
870 // Intialize volatile variable store
872 Status
= InitializeVariableStore (
873 &mVariableModuleGlobal
->VariableGlobal
[Physical
].VolatileVariableBase
,
874 &mVariableModuleGlobal
->VolatileLastVariableOffset
877 if (EFI_ERROR (Status
)) {
881 // Intialize non volatile variable store
883 Status
= InitializeVariableStore (
884 &mVariableModuleGlobal
->VariableGlobal
[Physical
].NonVolatileVariableBase
,
885 &mVariableModuleGlobal
->NonVolatileLastVariableOffset