| =========================== Table of Contents ================================== |
| Introduction |
| |
| Data structures used |
| cache_state |
| cbmem_state |
| table |
| entries |
| |
| Internal Functions |
| timestamp_cache_init |
| timestamp_cache_get |
| timestamp_alloc_cbmem_table |
| timestamp_table_get |
| timestamp_add_table_entry |
| |
| Function APIs |
| timestamp_early_init |
| timestamp_init |
| timestamp_add |
| timestamp_add_now |
| timestamp_sync |
| |
| Use / Test Cases |
| Case 1: Timestamp Region Exists |
| Case 2: No timestamp region, fresh boot, cbmem_initialize called after |
| timestamp_init |
| Case 3: No timestamp region, fresh boot, cbmem_initialize called before |
| timestamp_init |
| Case 4: No timestamp region, resume, cbmem_initialize called after |
| timestamp_init |
| Case 5: No timestamp region, resume, cbmem_initialize called before |
| timestamp_init |
| |
| |
| ============================== Introduction ==================================== |
| |
| The aim of timestamp library is to make it easier for different boards |
| to save timestamps in cbmem / stash (until cbmem is brought up) by providing a |
| simple API to initalize, add and sync timestamps. In order to make the |
| timestamps persistent and accessible from the kernel, we need to ensure that all |
| the saved timestamps end up in cbmem area under the CBMEM_ID_TIMESTAMP |
| tag. However, until the cbmem area is available, the timestamps can be saved to |
| board/SOC-defined _timestamp region or in a local stage-specific stash. The work |
| of identifying the right location for storing timestamps is done by the library |
| and is not exposed to the user. |
| |
| Working of timestamp library from a user perspective can be outlined in |
| the following steps: |
| 1. Initialize the _timestamp cache (if any used) |
| 2. Initialize the base time and reset cbmem timestamp area |
| 3. Start adding timestamps |
| |
| Steps 1) and 2) above can be performed in a single API call as explained |
| in later sections. Behind the scenes, the timestamp library takes care of: |
| 1. Identifying the correct location for storing timestamps (cbmem or _timestamp |
| region or local stash). |
| 2. Once cbmem is up, ensure that all timestamps are synced from _timestamp |
| region or local stash into the cbmem area. |
| 3. Add a new cbmem timestamp area based on whether a reset of cbmem timestamp |
| region is required / not. |
| |
| ========================== Data structures used ================================ |
| |
| The main structure that maintains information about the timestamp cache |
| is: |
| struct __attribute__((__packed__)) timestamp_cache { |
| uint16_t cache_state; |
| uint16_t cbmem_state; |
| struct timestamp_table table; |
| struct timestamp_entry entries[MAX_TIMESTAMP_CACHE]; |
| }; |
| |
| 1. cache_state |
| The state of the cache is maintained by cache_state attribute which can |
| be any one of the following: |
| |
| enum { |
| TIMESTAMP_CACHE_UNINITIALIZED = 0, |
| TIMESTAMP_CACHE_INITIALIZED, |
| TIMESTAMP_CACHE_NOT_NEEDED, |
| }; |
| |
| By default, if the cache is stored in local stash (bss area), then it |
| will be reset to uninitialized state. However, if the cache is stored in |
| _timestamp region, then it might have garbage in any of the attributes. Thus, if |
| _timestamp region is being used by any board, it is important to call |
| timestamp_early_init which provides the initialization of timestamp cache. |
| |
| Once the cache is initialized, its state is set to |
| cache_initialized. Henceforth, the calls to cache i.e. timestamp_add know that |
| the state reflected is valid and timestamps can be directly saved in the cache. |
| |
| When cbmem area is up (i.e. call to cbmem_initialize), we do not need to |
| store the timestamps in local stash / _timestamp area anymore. Thus, the cache |
| state is set to cache_not_needed, which allows timestamp_add to store all |
| timestamps directly into cbmem area. |
| |
| |
| 2. cbmem_state |
| This attribute indicates if we need to reset the timestamp area in |
| cbmem. This needs to be done for fresh boot (when there is no timestamp area |
| allocated in cbmem) or during resume (to clean out timestamps from earlier |
| boot). This can be set to any one of the following values: |
| |
| enum { |
| TIMESTAMP_CBMEM_RESET_NOT_REQD = 0, |
| TIMESTAMP_CBMEM_RESET_REQD, |
| }; |
| |
| If _timestamp region is being used, the call to timestamp_early_init |
| sets this field to reset_reqd. If this region is not being used, then instead |
| the call to timestamp_init sets this field to reset_reqd. Thus, when cbmem |
| initialization is complete, the timestamp library ensures that the cbmem |
| timestamp area is properly set as per the value of this attribute and resets |
| this field to reset_not_reqd. In case of resume path, this field would be set |
| accordingly by timestamp_early_init / timestamp_init thus ensuring that |
| timestamps from earlier boot dont float around into current boot logs. Also, |
| during early ramstage, when cbmem is not yet up, local stash would be used which |
| is in the bss area and thus this field is set to reset_not_reqd (unless |
| timestamp_init is called before or after cbmem_intiialize). Hence, when ramstage |
| brings up cbmem and syncs timestamps, the cbmem timestamp area is not reset. If |
| ramstage is the first stage where timestamp_init is called, then the cbmem area |
| is accordingly reset in timestamp_sync or timestamp_init based on the order in |
| which calls are made. Details are explained in the use cases towards the end. |
| |
| |
| 3. table |
| This field is represented by a structure which provides overall |
| information about the entries in the timestamp area: |
| |
| struct timestamp_table { |
| uint64_t base_time; |
| uint32_t max_entries; |
| uint32_t num_entries; |
| struct timestamp_entry entries[0]; /* Variable number of entries */ |
| } __attribute__((packed)); |
| |
| It indicates the base time for all timestamp entries, maximum number of |
| entries that can be stored, total number of entries that currently exist and an |
| entry structure to hold variable number of entries. |
| |
| |
| 4. entries |
| This field holds the details of each timestamp entry, upto a maximum of |
| MAX_TIMESTAMP_CACHE which is defined as 16 entries. Each entry is defined by: |
| |
| struct timestamp_entry { |
| uint32_t entry_id; |
| uint64_t entry_stamp; |
| } __attribute__((packed)); |
| |
| entry_id holds the timestamp id corresponding to this entry and |
| entry_stamp holds the actual timestamp. |
| |
| |
| For timestamps stored in the cbmem area, a timestamp_table is allocated |
| with space for MAX_TIMESTAMPS equal to 30. Thus, the cbmem area holds base_time, |
| max_entries (which is 30), current number of entries and the actual entries |
| represented by timestamp_entry. |
| |
| |
| ========================== Internal Functions ================================== |
| |
| 1. timestamp_cache_init |
| Initializes the timestamp cache to a known state by setting num_entries |
| to 0, base_entries to MAX_TIMESTAMP_CACHE, base_time to caller-provided base, |
| cache_state to TIMESTAMP_CACHE_INITIALIZED and cbmem_state to caller-provided |
| state. |
| |
| |
| 2. timestamp_cache_get |
| This function returns a pointer to timestamp cache. It basically |
| identifies what cache to use based on following parameters: |
| a. If __PRE_RAM__ is true i.e. we are in pre-ramstage step and _timestamp region |
| is provided, use _timestamp region. |
| b. Otherwise use local stash |
| |
| If using _timestamp region, it ensures that the size of the region is |
| big enough to hold all of timestamp_cache structure and MAX_TIMESTAMP_CACHE |
| timestamp entries. Once the location of cache is decided, it checks to see if |
| cache is not yet initialized, and calls timestamp_cache_init if required. |
| |
| |
| 3. timestamp_alloc_cbmem_table |
| This function exists only in romstage and ramstage since only these two |
| stages access cbmem area. It allocates a new table for MAX_TIMESTAMPS number of |
| entries in the cbmem area using CBMEM_ID_TIMESTAMP tag. It also resets the |
| base_time and num_entries to 0. |
| |
| |
| 4. timestamp_table_get |
| This function returns a pointer to the timestamp table. Based on the |
| value of cache_state in timestamp_cache, it provides a pointer either to the |
| cache (in stash or _timestamp region) or the cbmem timestamp table. If a pointer |
| to the cbmem timestamp table is returned, it checks to see if cbmem_state |
| indicates a reset of the cbmem timestamp area. If required, it makes a call to |
| timestamp_alloc_cbmem_table. |
| |
| |
| 5. timestamp_add_table_entry |
| This function adds a new entry to the timestamp table obtained by making |
| a call to timestamp_table_get. It sets the id based on user-provided id and the |
| stamp is set to user-provided time - table base time. This is to normalize all |
| the timestamps. |
| |
| |
| ============================= Function APIs ==================================== |
| |
| 1. timestamp_early_init |
| It is essential to call this function only if _timestamp region is being |
| used. This should be the first call made to the timestamp library. Since |
| _timestamp region can contain arbitary values in the different fields of the |
| cache, this function initializes the fields properly by calling |
| timestamp_cache_init with the cache pointer, user-provided base time and |
| cbmem_state as TIMESTAMP_CBMEM_RESET_REQD. This will provide an indication that |
| cbmem timestamp area needs to be added (in case of fresh boot) or reset to 0 |
| entries(in case of resume). |
| |
| |
| 2. timestamp_init |
| It is essential to call this function before any call to timestamp_add |
| is made. In case of _timestamp region being used, only a call to |
| timestamp_early_init is required. In all other cases, it is essential to call |
| timestamp_init to set the cbmem_state to TIMESTAMP_CBMEM_RESET_REQD. This will |
| provide an indication that cbmem timestamp area needs to be added (in case of |
| fresh boot) or reset to 0 entries(in case of resume). |
| |
| |
| 3. timestamp_add |
| This function accepts from user a timestamp id and time to record in the |
| timestamp table. Based on the table provided by timestamp_table_get, it stores |
| the entry in the appropriate table in cbmem or _timestamp region or local |
| stash. |
| |
| |
| 4. timestamp_add_now |
| This function calls timestamp_add with user-provided id and current |
| time. |
| |
| |
| 5. timestamp_sync |
| This is one of the most important functions required to ensure the |
| proper functioning of the timestamp library across cbmem, _timestamp region and |
| local stash with minimal interference for the user. It is important to note that |
| this function is called only by cbmem_initialize to ensure that once the cbmem |
| area is ready, we automatically sync all the timestamps saved in the cache to |
| cbmem area. Thus, the user never has to call the timestamp_sync function. |
| |
| This function checks to see if the cache state indicates that a cbmem |
| reset of timestamp area is required or no timestamp area exists in cbmem at |
| all. In that case, it allocates a new cbmem area by calling |
| timestamp_alloc_cbmem_table. |
| |
| Otherwise, it uses cbmem_find to use the timestamp area in cbmem. It |
| then uses timestamp_add_table_entry to move all the entries from cache to cbmem |
| area. Here, two cases can occur with respect to the base time subtraction: |
| |
| a. Newly added cbmem table will have a base time of 0. Thus, no adjustments are |
| required for the timestamps being added from cache to cbmem table. |
| |
| b. Timestamps added to cache before ramstage: In this case, the base time in |
| cache table will be 0 and timestamp_add_table_entry will take care of |
| subtracting the correct base_time. Finally, it resets the timestamp cache to: |
| |
| cache_state: TIMESTAMP_CACHE_NOT_NEEDED |
| cbmem_state: TIMESTAMP_CBMEM_RESET_NOT_REQD |
| num_entries: 0 |
| |
| Also, the base_time in cbmem table is update if it is currently zero |
| indicating that it needs to inherit base time from cache table since |
| cbmem table might be newly allocated. |
| |
| |
| ============================= Use / Test Cases ================================= |
| |
| The following cases have been considered while designing the timestamp |
| library. It is important to ensure that any changes made to this library satisfy |
| each of the following use cases: |
| |
| Case 1: Timestamp Region Exists (Fresh Boot / Resume) |
| |
| |
| 1. timestamp_early_init is called to setup the cache state and cbmem_state is |
| set to reset_reqd. These properties are set in the timestamp cache which is part |
| of _timestamp region. |
| |
| 2. <Any number of timestamp_add / timestamp_add_now> : All saved in _timestamp |
| region |
| |
| 3. Once cbmem_initiliaze is complete, it calls timestamp_sync, which checks the |
| _timestamp region to check the cbmem_state field. Since it says reset_reqd, a |
| new area for timestamp is setup in cbmem by calling cbmem_add. The value of |
| cbmem_state is set to reset_not_reqd in _timestamp region cache. |
| |
| 4. After entering ramstage, the local stash acts as the timestamp cache. Since |
| it is in BSS area, the cache_state field is set to uninitialized and cbmem_state |
| field is set to reset_not_reqd. |
| |
| 5. On any call to timestamp_add / timestamp_add_now, it initializes the cache |
| and sets cache_state field to initialized whereas cbmem_state is still set to |
| reset_not_reqd. |
| |
| 6. Once cbmem_initiliaze is complete, it calls timestamp_sync, which checks the |
| local stash to check the cbmem_state field. Since it says reset_not_reqd, it |
| uses cbmem_find to find already allocated timestamp area in cbmem. If none is |
| found, it allocates a new one. |
| |
| 7. <Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| cbmem |
| |
| |
| Case 2: No timestamp region, fresh boot, cbmem_initialize called after |
| timestamp_init |
| |
| 1. Since timestamp_init is called before cbmem_initialize, it will set the cache |
| field attributes to: cache_state as initialized and cbmem_state as |
| reset_reqd. This ensures that timestamp_sync sets up a new cbmem area for |
| timestamps. |
| |
| 2. < Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| stash |
| |
| 3. Once cbmem_initiliaze is complete, it calls timestamp_sync, which checks the |
| stash cache to check the cbmem_state field. Since it says reset_reqd, a new area |
| for timestamp is setup in cbmem by calling cbmem_add. The value of cbmem_state |
| is set to reset_not_reqd in stash cache. |
| |
| 4. After entering ramstage, a new local stash acts as the timestamp cache. Since |
| it is in BSS area, the cache_state field is set to uninitialized and cbmem_state |
| field is set to reset_not_reqd. |
| |
| 5. On any call to timestamp_add / timestamp_add_now, it initializes the cache |
| and sets cache_state field to initialized whereas cbmem_state is still set to |
| reset_not_reqd. |
| |
| 6. Once cbmem_initiliaze is complete, it calls timestamp_sync, which checks the |
| local stash to check the cbmem_state field. Since it says reset_not_reqd, it |
| uses cbmem_find to find already allocated timestamp area in cbmem. If none is |
| found, it allocates a new one. |
| |
| 7. <Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| cbmem |
| |
| |
| Case 3: No timestamp region, fresh boot, cbmem_initialize called before |
| timestamp_init |
| |
| 1. Since cbmem_initialize is called before timestamp_init, it will call |
| timestamp_sync which checks local stash to check for cbmem_state field. Since, |
| cbmem_state says no_reset_reqd, it uses cbmem_find to look for a timestamp area |
| in cbmem. Since, it is a fresh boot, none will be found and it uses cbmem_add to |
| create a new area on cbmem. |
| |
| 2. timestamp_init is called, which sets local cache state to reset required. It |
| then calls timestamp_table_get which sees that cache_state is cache_not_needed |
| and cbmem_state is reset_reqd. Thus, it calls timestamp_alloc_cbmem_table to |
| reset the cbmem area which is as good as no change in the state of cbmem |
| area. Then, the cbmem_state is set to reset_not_reqd. |
| |
| 3. <Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| cbmem |
| |
| 4. After entering ramstage, a new local stash acts as the timestamp cache. Since |
| it is in BSS area, the cache_state field is set to uninitialized and cbmem_state |
| field is set to reset_not_reqd. |
| |
| 5. On any call to timestamp_add / timestamp_add_now, it initializes the cache |
| and sets cache_state field to initialized whereas cbmem_state is still set to |
| reset_not_reqd. |
| |
| 6. Once cbmem_initiliaze is complete, it calls timestamp_sync, which checks the |
| local stash to check the cbmem_state field. Since it says reset_not_reqd, it |
| uses cbmem_find to find already allocated timestamp area in cbmem. If none is |
| found, it allocates a new one. |
| |
| 7. <Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| cbmem |
| |
| |
| Case 4: No timestamp region, resume, cbmem_initialize called after |
| timestamp_init |
| |
| 1. Since timestamp_init is called before cbmem_initialize, it will set the cache |
| field attributes to: cache_state as initialized and cbmem_state as reset_reqd. |
| |
| 2. < Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| stash |
| |
| 3. Once cbmem_initiliaze is complete, it calls timestamp_sync, which checks the |
| stash cache to check the cbmem_state field. Since it says reset_reqd, |
| timestamp_sync resets cbmem area to num_entries=0, thus flushing timestamps from |
| earlier boot. The value of cbmem_state is set to reset_not_reqd in stash cache. |
| |
| 4. After entering ramstage, a new local stash acts as the timestamp cache. Since |
| it is in BSS area, the cache_state field is set to uninitialized and cbmem_state |
| field is set to reset_not_reqd. |
| |
| 5. On any call to timestamp_add / timestamp_add_now, it initializes the cache |
| and sets cache_state field to initialized whereas cbmem_state is still set to |
| reset_not_reqd. |
| |
| 6. Once cbmem_initiliaze is complete, it calls timestamp_sync, which checks the |
| local stash to check the cbmem_state field. Since it says reset_not_reqd, it |
| uses cbmem_find to find already allocated timestamp area in cbmem. If none is |
| found, it allocates a new one. |
| |
| 7. <Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| cbmem |
| |
| |
| Case 5: No timestamp region, resume, cbmem_initialize called before |
| timestamp_init |
| |
| 1. Since cbmem_initialize is called before timestamp_init, it will call |
| timestamp_sync which checks local stash to check for cbmem_state field. Since, |
| cbmem_state says no_reset_reqd, it uses cbmem_find to look for a timestamp area |
| in cbmem. Since, it is resume, it will find the older cbmem timestamp area from |
| earlier boot. However, nothing is added to cbmem area since timestamp_add / |
| timestamp_add_now is expected to be used only after call to timestamp_init. |
| |
| 2. timestamp_init is called, which sets local cache state to reset required. It |
| then calls timestamp_table_get which sees that cache_state is cache_not_needed |
| and cbmem_state is reset_reqd. Thus, it calls timestamp_alloc_cbmem_table to |
| reset the cbmem area which sets num_entries to 0. Thus, flushing timestamps from |
| earlier boot. Then, the cbmem_state is set to reset_not_reqd. |
| |
| 3. <Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| cbmem |
| |
| 4. After entering ramstage, a new local stash acts as the timestamp cache. Since |
| it is in BSS area, the cache_state field is set to uninitialized and cbmem_state |
| field is set to reset_not_reqd. |
| |
| 5. On any call to timestamp_add / timestamp_add_now, it initializes the cache |
| and sets cache_state field to initialized whereas cbmem_state is still set to |
| reset_not_reqd. |
| |
| 6. Once cbmem_initiliaze is complete, it calls timestamp_sync, which checks the |
| local stash to check the cbmem_state field. Since it says reset_not_reqd, it |
| uses cbmem_find to find already allocated timestamp area in cbmem. If none is |
| found, it allocates a new one. |
| |
| 7. <Any number of calls to timestamp_add / timestamp_add_now> : All saved in |
| cbmem |