serialize objects in NVMEM
am: 21756127fd

Change-Id: I3ec3888ba566dc15bd81084da00fe9e581e9296c
diff --git a/Implementation.h b/Implementation.h
index 0d12c45..c7dc5c5 100644
--- a/Implementation.h
+++ b/Implementation.h
@@ -262,8 +262,12 @@
 #define NV_MEMORY_SIZE                    16076
 // Versioning NV storage format will allow to smoothly migrate NVRAM contents.
 // Versions:
-// 1 - full non-serialized objects in NVRAM, max SHA digest is SHA-256
-// 2 - full non-serialized objects in NVRAM, max SHA digest is SHA-512
+// 1 - full non-serialized objects in NVMEM, max SHA digest is SHA-256
+// 2 - a mix of serialized and non-serialized objects in NVMEM, max SHA digest
+//     is SHA-512. Eviction objects can be stored either serialized or
+//     non-serialized. The size of the stored entity smaller than
+//     sizeof(OBJECT) is considered an indication of the serialized form.
+
 #define NV_FORMAT_VERSION                 2
 #else
 #define NV_MEMORY_SIZE                    16384
diff --git a/NV.c b/NV.c
index f714c91..0099c19 100644
--- a/NV.c
+++ b/NV.c
@@ -1101,6 +1101,53 @@
     else
         return FALSE;
 }
+
+//
+//
+//           NvUnmarshalObject()
+//
+//      This function accepts a buffer containing a marshaled OBJECT
+//      structure, a pointer to the area where the input data should be
+//      unmarshaled, and a pointer to the size of the output area.
+//
+//      No error checking is performed, unmarshaled data is guaranteed not to
+//      spill over the allocated space.
+//
+static TPM_RC NvUnmarshalObject(OBJECT *o, BYTE **buf, INT32 *size)
+{
+    TPM_RC result;
+
+    // There is no generated function to unmarshal the attributes field, do it
+    // by hand.
+    MemoryCopy(&o->attributes, *buf, sizeof(o->attributes), *size);
+    *buf += sizeof(o->attributes);
+    *size -= sizeof(o->attributes);
+
+    result = TPMT_PUBLIC_Unmarshal(&o->publicArea, buf, size);
+    if (result != TPM_RC_SUCCESS)
+        return result;
+
+    result = TPMT_SENSITIVE_Unmarshal(&o->sensitive, buf, size);
+    if (result != TPM_RC_SUCCESS)
+        return result;
+
+#ifdef TPM_ALG_RSA
+    result = TPM2B_PUBLIC_KEY_RSA_Unmarshal(&o->privateExponent, buf, size);
+    if (result != TPM_RC_SUCCESS)
+        return result;
+#endif
+
+    result = TPM2B_NAME_Unmarshal(&o->qualifiedName, buf, size);
+    if (result != TPM_RC_SUCCESS)
+        return result;
+
+    result = TPMI_DH_OBJECT_Unmarshal(&o->evictHandle, buf, size, TRUE);
+    if (result != TPM_RC_SUCCESS)
+        return result;
+
+    return TPM2B_NAME_Unmarshal(&o->name, buf, size);
+}
+
 //
 //
 //           NvGetEvictObject()
@@ -1123,13 +1170,34 @@
     // Find the address of evict object
     entityAddr = NvFindHandle(handle);
     // If handle is not found, return an error
-    if(entityAddr == 0)
+    if(entityAddr == 0) {
         result = TPM_RC_HANDLE;
-    else
-        // Read evict object
-        _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE),
-                             sizeof(OBJECT),
-                             object);
+    } else {
+        UINT32   storedSize;
+        UINT32   nextEntryAddr;
+
+        // Let's calculate the size of object as stored in NVMEM.
+        _plat__NvMemoryRead(entityAddr - sizeof(UINT32),
+                            sizeof(UINT32), &nextEntryAddr);
+
+        storedSize = nextEntryAddr - entityAddr;
+
+        if (storedSize == (sizeof(TPM_HANDLE) + sizeof(OBJECT))) {
+            // Read evict object stored unmarshaled.
+            _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE),
+                                sizeof(OBJECT),
+                                object);
+        } else {
+            // Must be stored marshaled, let's unmarshal it.
+            BYTE marshaled[sizeof(OBJECT)];
+            INT32 max_size = sizeof(marshaled);
+            BYTE *marshaledPtr = marshaled;
+
+            _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE),
+                                storedSize, marshaled);
+            result = NvUnmarshalObject(object,  &marshaledPtr, &max_size);
+        }
+    }
     // whether there is an error or not, make sure that the evict
     // status of the object is set so that the slot will get freed on exit
     object->attributes.evict = SET;
@@ -1494,6 +1562,51 @@
        NvAddRAM(publicArea->nvIndex, publicArea->dataSize);
    return TPM_RC_SUCCESS;
 }
+
+//
+//
+//           NvMarshalObject()
+//
+//      This function marshals the passed in OBJECT structure into a buffer. A
+//      pointer to pointer to the buffer and a pointer to the size of the
+//      buffer are passed in for this function to update as appropriate.
+//
+//      On top of marshaling the object, this function also modifies one of
+//      the object's properties and sets the evictHandle field of the
+//      marshaled object to the requested value.
+//
+//      Returns
+//
+//      Marshaled size of the object.
+//
+static UINT16 NvMarshalObject(OBJECT *o, TPMI_DH_OBJECT evictHandle,
+                              BYTE **buf, INT32 *size)
+{
+    UINT16 marshaledSize;
+    OBJECT_ATTRIBUTES stored_attributes;
+
+    stored_attributes = o->attributes;
+    stored_attributes.evict = SET;
+    marshaledSize = sizeof(stored_attributes);
+    MemoryCopy(*buf, &stored_attributes, marshaledSize, *size);
+    *buf += marshaledSize;
+    *size -= marshaledSize;
+
+    marshaledSize += TPMT_PUBLIC_Marshal(&o->publicArea, buf, size);
+    marshaledSize += TPMT_SENSITIVE_Marshal(&o->sensitive, buf, size);
+#ifdef TPM_ALG_RSA
+    marshaledSize += TPM2B_PUBLIC_KEY_RSA_Marshal(&o->privateExponent,
+                                                  buf, size);
+#endif
+    marshaledSize += TPM2B_NAME_Marshal(&o->qualifiedName, buf, size);
+
+    // Use the supplied handle instead of the object contents.
+    marshaledSize += TPMI_DH_OBJECT_Marshal(&evictHandle, buf, size);
+    marshaledSize += TPM2B_NAME_Marshal(&o->name, buf, size);
+
+    return marshaledSize;
+}
+
 //
 //
 //           NvAddEvictObject()
@@ -1514,35 +1627,34 @@
 {
     // The buffer to be written to NV memory
     BYTE            nvBuffer[sizeof(TPM_HANDLE) + sizeof(OBJECT)];
-    OBJECT              *nvObject;                // a pointer to the OBJECT data in
-                                                  // nvBuffer
     UINT16              entrySize;                // size of entry
+    BYTE                *marshalSpace;
+    INT32               marshalRoom;
+
     // evict handle type should match the object hierarchy
     pAssert(   (   NvIsPlatformPersistentHandle(evictHandle)
                 && object->attributes.ppsHierarchy == SET)
             || (   NvIsOwnerPersistentHandle(evictHandle)
                 && (   object->attributes.spsHierarchy == SET
                     || object->attributes.epsHierarchy == SET)));
-    // An evict needs 4 bytes of handle + sizeof OBJECT
-    entrySize = sizeof(TPM_HANDLE) + sizeof(OBJECT);
-    // Check if we have enough space to add the evict object
-    // An evict object needs 8 bytes in index table + sizeof OBJECT
-    // In this implementation, the only resource limitation is the available NV
-    // space. Other implementation may have other limitation on evict object
-    // handle space
-    if(!NvTestSpace(entrySize, FALSE)) return TPM_RC_NV_SPACE;
-    // Allocate a new evict handle
+
+    // Do not attemp storing a duplicate handle.
     if(!NvIsUndefinedEvictHandle(evictHandle))
         return TPM_RC_NV_DEFINED;
-    // Copy evict object to nvBuffer
+
         // Copy handle
-    memcpy(nvBuffer, &evictHandle, sizeof(TPM_HANDLE));
-        // Copy OBJECT
-    nvObject = (OBJECT *) (nvBuffer + sizeof(TPM_HANDLE));
-    *nvObject = *object;
-    // Set evict attribute and handle
-    nvObject->attributes.evict = SET;
-    nvObject->evictHandle = evictHandle;
+    entrySize = sizeof(TPM_HANDLE);
+    memcpy(nvBuffer, &evictHandle, entrySize);
+
+    // Let's serialize the object before storing it in NVMEM
+    marshalSpace = nvBuffer + entrySize;
+    marshalRoom = sizeof(nvBuffer) - entrySize;
+    entrySize += NvMarshalObject(object, evictHandle,
+                                 &marshalSpace, &marshalRoom);
+
+    // Check if we have enough space to add this evict object
+    if(!NvTestSpace(entrySize, FALSE)) return TPM_RC_NV_SPACE;
+
     // Add evict to NV memory
     NvAdd(entrySize, entrySize, nvBuffer);
     return TPM_RC_SUCCESS;
diff --git a/Object.c b/Object.c
index 06d717f..14ec018 100644
--- a/Object.c
+++ b/Object.c
@@ -268,8 +268,8 @@
     if(i == MAX_LOADED_OBJECTS) return FALSE;
     *handle = i + TRANSIENT_FIRST;
     *object = &s_objects[i].object.entity;
-    // Initialize the object attributes
-    MemorySet(&((*object)->attributes), 0, sizeof(OBJECT_ATTRIBUTES));
+    // Initialize the container.
+    MemorySet(*object, 0, sizeof(**object));
     return TRUE;
 }
 //