/* * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "resource_storage_flash.h" #include "resource_storage_impl.h" #include "drivers/flash.h" #include "kernel/pbl_malloc.h" #include "resource/resource_version.auto.h" #include "services/normal/process_management/app_storage.h" #include "system/bootbits.h" #include "system/logging.h" #include "util/size.h" #include static const SystemResourceBank s_resource_banks[] = { { .begin = FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN, .end = FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END, }, { .begin = FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN, .end = FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END, }, }; //! Index into s_resource_banks static unsigned int s_active_bank = 0; #define BANK s_resource_banks[s_active_bank] //! Set to true if we've scanned the available resource banks and determined one of them had valid //! resources in it. static bool s_valid_resources_found = false; const ResourceStoreImplementation g_system_bank_impl; static void resource_storage_system_bank_init(void) { boot_bit_clear(BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE); ResourceStoreEntry entry = { .id = 0, // resource id 0 means the store itself .impl = &g_system_bank_impl, .length = ENTRY_LENGTH_UNSET }; // Increment s_active_bank and call resource_storage_generic_check for each value to find // a bank that's valid. for (s_active_bank = 0; s_active_bank < ARRAY_LENGTH(s_resource_banks); ++s_active_bank) { PBL_LOG(LOG_LEVEL_INFO, "Checking bank %u for system resources", s_active_bank); if (resource_storage_generic_check(SYSTEM_APP, 0, &entry, &SYSTEM_RESOURCE_VERSION)) { PBL_LOG(LOG_LEVEL_INFO, "Valid system resources found!"); s_valid_resources_found = true; return; } } // Welp, we found nothing. Leave s_valid_resources_found as false and when resource_storage_check // is called as part of system_resource_init we'll complain and handle missing resources. } // TODO PBL-21009: Move this somewhere else. const SystemResourceBank *resource_storage_flash_get_unused_bank(void) { size_t unused_bank_index; if (s_valid_resources_found) { unused_bank_index = (s_active_bank + 1) % ARRAY_LENGTH(s_resource_banks); } else { static int s_unused_bank_index = -1; if (s_unused_bank_index == -1) { // A crude form of wear levelling to try and keep BB2s in infra happy // // For real watches, the only time this should happen is during initial onboarding. (If we // are in normal FW, one of the resource banks _must_ be valid.) We only call this once // because we want to target the same bank when both are unused so features like resumable // resource updates work as expected. We reset the bank on boot to make our watches a little // more resilient to the scenario where one of the resource banks has gone completely bad s_unused_bank_index = rand() % ARRAY_LENGTH(s_resource_banks); } unused_bank_index = s_unused_bank_index; } return &s_resource_banks[unused_bank_index]; } static uint32_t resource_storage_system_bank_metadata_size(ResourceStoreEntry *entry) { return SYSTEM_STORE_METADATA_BYTES; } // PBL-28517 investigation extern uint8_t pbl_28517_flash_impl_get_status_register(uint32_t sector_addr); static uint32_t resource_storage_system_bank_get_crc(ResourceStoreEntry *entry, uint32_t num_bytes, uint32_t entry_offset) { #if (PLATFORM_SNOWY || PLATFORM_SPALDING) && !RELEASE && !UNITTEST // PBL-28517 investigation if (entry_offset == 0) { // We're calculating the CRC of the whole bank. Before we do this, let's save the status // register for each sector so we can see if the flash is in a funny state. for (int i = 0; (i * SECTOR_SIZE_BYTES) < (int) num_bytes; ++i) { const uint32_t addr = (BANK.begin + (i * SECTOR_SIZE_BYTES)) & SECTOR_ADDR_MASK; uint8_t status_reg = pbl_28517_flash_impl_get_status_register(addr); uint32_t crc = flash_calculate_legacy_defective_checksum(addr, SECTOR_SIZE_BYTES); PBL_LOG(LOG_LEVEL_DEBUG, "PBL-28517 Sector 0x%"PRIx32" Status 0x%"PRIx8" CRC 0x%"PRIx32, addr, status_reg, crc); } } #endif uint32_t start_offset = resource_store_get_metadata_size(entry) + entry_offset; return flash_calculate_legacy_defective_checksum( BANK.begin + start_offset, num_bytes); } static uint32_t resource_storage_system_bank_read(ResourceStoreEntry *entry, uint32_t offset, void *data, size_t num_bytes) { flash_read_bytes(data, BANK.begin + offset, num_bytes); return num_bytes; } #if CAPABILITY_HAS_MAPPABLE_FLASH bool resource_storage_flash_bytes_are_readonly(const void *bytes) { return (bytes > (void *)FLASH_MEMORY_MAPPABLE_ADDRESS) && (bytes < (void *)(FLASH_MEMORY_MAPPABLE_ADDRESS + FLASH_MEMORY_MAPPABLE_SIZE)); } static const uint8_t *resource_storage_system_bank_readonly_bytes(ResourceStoreEntry *entry, bool has_privileged_access) { if (!has_privileged_access) { return NULL; } return (uint8_t *)(uintptr_t)(FLASH_MEMORY_MAPPABLE_ADDRESS + BANK.begin + entry->offset); } #else bool resource_storage_flash_bytes_are_readonly(const void *bytes) { return false; } static const uint8_t *resource_storage_system_bank_readonly_bytes(ResourceStoreEntry *entry, bool has_privileged_access) { return NULL; } #endif // CAPABILITY_HAS_MAPPABLE_FLASH static void resource_storage_system_bank_clear(ResourceStoreEntry *entry) { uint8_t buffer[MANIFEST_SIZE] = {0}; flash_write_bytes(buffer, BANK.begin, MANIFEST_SIZE); } bool resource_storage_system_bank_check(ResAppNum app_num, uint32_t resource_id, ResourceStoreEntry *entry, const ResourceVersion *expected_version) { if (!s_valid_resources_found) { // We determined that we had no valid banks during init(), return false. return false; } // Are we checking the store itself? if (resource_id == 0) { // We've already verified that the bank was good at init(), just return true. return true; } // We're checking a specific resource, delegate this to the generic method. return resource_storage_generic_check(app_num, resource_id, entry, expected_version); } static bool resource_storage_system_bank_find_resource(ResourceStoreEntry *entry, ResAppNum app_num, uint32_t resource_id) { return app_num == SYSTEM_APP && s_valid_resources_found; } const ResourceStoreImplementation g_system_bank_impl = { .type = ResourceStoreTypeSystemBank, .init = resource_storage_system_bank_init, .clear = resource_storage_system_bank_clear, .check = resource_storage_system_bank_check, .metadata_size = resource_storage_system_bank_metadata_size, .find_resource = resource_storage_system_bank_find_resource, .get_resource = resource_storage_generic_get_resource, .get_length = resource_storage_generic_get_length, .get_crc = resource_storage_system_bank_get_crc, .write = resource_storage_generic_write, .read = resource_storage_system_bank_read, .readonly_bytes = resource_storage_system_bank_readonly_bytes, .watch = resource_storage_generic_watch, .unwatch = resource_storage_generic_unwatch, };