mirror of
https://github.com/google/pebble.git
synced 2025-05-24 12:14:53 +00:00
211 lines
7.9 KiB
C
211 lines
7.9 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
|
|
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,
|
|
};
|