mirror of
https://github.com/google/pebble.git
synced 2025-05-25 12:44:53 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
16
src/fw/resource/font_resource_table.h
Normal file
16
src/fw/resource/font_resource_table.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
209
src/fw/resource/resource.c
Normal file
209
src/fw/resource/resource.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* 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.h"
|
||||
#include "resource_storage.h"
|
||||
#include "resource_storage_builtin.h"
|
||||
#include "resource_storage_flash.h"
|
||||
|
||||
#include "process_management/app_manager.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "flash_region/flash_region.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "os/mutex.h"
|
||||
#include "services/normal/process_management/app_storage.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
// TODO: this may be replaced once apps become more dynamic
|
||||
|
||||
typedef struct {
|
||||
ListNode list_node;
|
||||
uint32_t id;
|
||||
ResourceStoreEntry stored_resource;
|
||||
} CachedResource;
|
||||
|
||||
PebbleRecursiveMutex *s_resource_mutex = NULL;
|
||||
|
||||
static CachedResource *s_resource_list = NULL;
|
||||
|
||||
static bool prv_resource_filter(ListNode *found_node, void *data) {
|
||||
CachedResource *resource = (CachedResource *)found_node;
|
||||
uint32_t resource_id = (uint32_t)data;
|
||||
|
||||
return (resource->id == resource_id);
|
||||
}
|
||||
|
||||
static void prv_get_resource(ResAppNum app_num, uint32_t id, ResourceStoreEntry *entry) {
|
||||
if (id < 1) {
|
||||
*entry = (ResourceStoreEntry){0};
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock_recursive(s_resource_mutex);
|
||||
|
||||
ListNode *node;
|
||||
if (app_num == SYSTEM_APP &&
|
||||
(node = list_find((ListNode *)s_resource_list, prv_resource_filter, (void *)(uintptr_t)id))) {
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
*entry = ((CachedResource *)node)->stored_resource;
|
||||
return;
|
||||
}
|
||||
|
||||
resource_storage_get_resource(app_num, id, entry);
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
}
|
||||
|
||||
//! initialize components needed for one apps resources
|
||||
bool resource_init_app(ResAppNum app_num, const ResourceVersion *expected_version) {
|
||||
// resource_id is ignored in this case, so we set it to 0
|
||||
mutex_lock_recursive(s_resource_mutex);
|
||||
bool rv = resource_storage_check(app_num, 0, expected_version);
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void resource_init(void) {
|
||||
// see if there's a system bank waiting to be loaded
|
||||
resource_storage_init();
|
||||
|
||||
s_resource_mutex = mutex_create_recursive();
|
||||
}
|
||||
|
||||
uint32_t resource_get_and_cache(ResAppNum app_num, uint32_t resource_id) {
|
||||
PBL_ASSERTN(app_num == SYSTEM_APP);
|
||||
// get from resource store
|
||||
mutex_lock_recursive(s_resource_mutex);
|
||||
ResourceStoreEntry res;
|
||||
resource_storage_get_resource(app_num, resource_id, &res);
|
||||
if (res.id < 1) {
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check if we already have something in cache for this resource
|
||||
CachedResource *cached_resource = (CachedResource *)list_find((ListNode *)s_resource_list,
|
||||
prv_resource_filter, (void *)(uintptr_t)resource_id);
|
||||
if (cached_resource == NULL) {
|
||||
cached_resource = kernel_malloc_check(sizeof(CachedResource));
|
||||
*cached_resource = (CachedResource){};
|
||||
cached_resource->id = resource_id;
|
||||
s_resource_list = (CachedResource *)list_prepend((ListNode *)s_resource_list,
|
||||
(ListNode *)cached_resource);
|
||||
}
|
||||
cached_resource->stored_resource = res;
|
||||
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return resource_id;
|
||||
}
|
||||
|
||||
size_t resource_load_byte_range_system(ResAppNum app_num, uint32_t resource_id,
|
||||
uint32_t offset, uint8_t *buffer, size_t num_bytes) {
|
||||
PBL_ASSERTN(buffer);
|
||||
|
||||
if (!num_bytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock_recursive(s_resource_mutex);
|
||||
ResourceStoreEntry resource;
|
||||
prv_get_resource(app_num, resource_id, &resource);
|
||||
if (resource.id < 1) {
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (offset + num_bytes > resource.length) {
|
||||
if (offset >= resource.length) {
|
||||
// Can't recover from trying to read from beyond the resource. Read nothing.
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return 0;
|
||||
}
|
||||
// We want to stop the FW from doing this, so we added an assert
|
||||
// but in the name of backwards compatibility, we let the app misbehave
|
||||
num_bytes = resource.length - offset;
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Tried to read past end of resource, reading %d bytes",
|
||||
(int)num_bytes);
|
||||
}
|
||||
|
||||
size_t bytes_read = resource_storage_read(&resource, offset, buffer, num_bytes);
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
size_t resource_size(ResAppNum app_num, uint32_t resource_id) {
|
||||
mutex_lock_recursive(s_resource_mutex);
|
||||
ResourceStoreEntry resource;
|
||||
prv_get_resource(app_num, resource_id, &resource);
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return resource.length;
|
||||
}
|
||||
|
||||
bool resource_bytes_are_readonly(void *bytes) {
|
||||
return resource_storage_builtin_bytes_are_readonly(bytes) ||
|
||||
resource_storage_flash_bytes_are_readonly(bytes);
|
||||
}
|
||||
|
||||
const uint8_t *resource_get_readonly_bytes(ResAppNum app_num, uint32_t resource_id,
|
||||
size_t *num_bytes_out, bool has_privileged_access) {
|
||||
// we don't support memory-mapping for resources that don't belong to the system
|
||||
if (app_num != SYSTEM_APP) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock_recursive(s_resource_mutex);
|
||||
|
||||
// FIXME PBL-28781: This operation touches flash. Even though this is the cleanest approach
|
||||
// to detect if the resource is a builtin, it is a slow one. We should instead only search
|
||||
// in the builtin table for the resource_ids and if there are no matches, bail early.
|
||||
ResourceStoreEntry resource;
|
||||
prv_get_resource(app_num, resource_id, &resource);
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
|
||||
if (num_bytes_out) {
|
||||
*num_bytes_out = resource.length;
|
||||
}
|
||||
|
||||
return resource.impl->readonly_bytes(&resource, has_privileged_access);
|
||||
}
|
||||
|
||||
ResourceVersion resource_get_version(ResAppNum app_num, uint32_t resource_id) {
|
||||
mutex_lock_recursive(s_resource_mutex);
|
||||
ResourceVersion v = resource_storage_get_version(app_num, resource_id);
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return v;
|
||||
}
|
||||
|
||||
ResourceVersion resource_get_system_version(void) {
|
||||
return resource_get_version(0, 0);
|
||||
}
|
||||
|
||||
bool resource_is_valid(ResAppNum app_num, uint32_t resource_id) {
|
||||
mutex_lock_recursive(s_resource_mutex);
|
||||
bool rv = resource_storage_check(app_num, resource_id, NULL /* No expected version */);
|
||||
if (rv) {
|
||||
ResourceStoreEntry entry;
|
||||
prv_get_resource(app_num, resource_id, &entry);
|
||||
rv = (entry.id != 0);
|
||||
}
|
||||
mutex_unlock_recursive(s_resource_mutex);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool resource_version_matches(const ResourceVersion *v1, const ResourceVersion *v2) {
|
||||
return (v1->crc == v2->crc);
|
||||
}
|
||||
|
106
src/fw/resource/resource.h
Normal file
106
src/fw/resource/resource.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "applib/applib_resource.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
//! @addtogroup Foundation
|
||||
//! @{
|
||||
//! @addtogroup Resources
|
||||
//! @{
|
||||
|
||||
// TODO: find a cleaner way to do this
|
||||
typedef uint32_t ResAppNum;
|
||||
|
||||
// Needs to be a #define so it can be used in static initializers
|
||||
#define SYSTEM_APP ((ResAppNum)0)
|
||||
|
||||
//! The version information baked into every binary resource pack.
|
||||
typedef struct PACKED {
|
||||
//! The crc of the resource pack between content_start and last_used. See check_bank_crc for how this is calculated.
|
||||
uint32_t crc;
|
||||
//! Just an identifier, not actually compared to anything.
|
||||
uint32_t timestamp;
|
||||
} ResourceVersion;
|
||||
|
||||
|
||||
//! Types used by pfs_watch_resource()
|
||||
typedef void (*ResourceChangedCallback)(void *data);
|
||||
typedef void *ResourceCallbackHandle;
|
||||
|
||||
|
||||
// inits system resources, and sets app resources to an unloaded state
|
||||
void resource_init(void);
|
||||
|
||||
//! @param app_num the resource app to initialize
|
||||
//! @param version Optional parameter that indicates which version information we should check
|
||||
// against. If NULL is provided no version check is performed.
|
||||
//! @return True if the resources are valid, false otherwise.
|
||||
bool resource_init_app(ResAppNum app_num, const ResourceVersion *version);
|
||||
|
||||
size_t resource_size(ResAppNum app_num, uint32_t resource_id);
|
||||
|
||||
//! Check that a resource id actually exists
|
||||
bool resource_is_valid(ResAppNum app_num, uint32_t resource_id);
|
||||
|
||||
//! @internal
|
||||
uint32_t resource_get_and_cache(ResAppNum app_num, uint32_t resource_id);
|
||||
|
||||
//! @internal
|
||||
//! @param buffer[out] a buffer to load the data into. Must be at least max_length in bytes.
|
||||
//! @return Number of bytes actually read. Should be num_bytes for a successful read.
|
||||
// NOTE: Many things don't properly check the return of this.
|
||||
size_t resource_load_byte_range_system(ResAppNum app_num, uint32_t resource_id,
|
||||
uint32_t start_offset, uint8_t *data, size_t num_bytes);
|
||||
|
||||
//! @internal
|
||||
//! Gets a pointer to a data of a built-in resource or memory-addressable resource if possible
|
||||
const uint8_t *resource_get_readonly_bytes(ResAppNum app_num, uint32_t resource_id,
|
||||
size_t *num_bytes_out, bool has_privileged_access);
|
||||
|
||||
//! @internal
|
||||
//! True, if given pointer maps to a built-in resource or memory-addressable read-only resource
|
||||
bool resource_bytes_are_readonly(void *bytes);
|
||||
|
||||
//! @internal
|
||||
//! Retrieve the version of the currently loaded system resources
|
||||
ResourceVersion resource_get_system_version(void);
|
||||
|
||||
//! @internal
|
||||
//! Retrieve the version of the resource
|
||||
ResourceVersion resource_get_version(ResAppNum app_num, uint32_t resource_id);
|
||||
|
||||
//! @internal
|
||||
//! Check that two versions are identical
|
||||
bool resource_version_matches(const ResourceVersion *v1, const ResourceVersion *v2);
|
||||
//
|
||||
//! Watch a resource. The callback is called whenever the given resource is modified.
|
||||
//! NOTE: This currently only supports file-based resources. If the resource is not
|
||||
//! file based, then a NULL PFSCallbackHandle will be returned.
|
||||
ResourceCallbackHandle resource_watch(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceChangedCallback callback, void* data);
|
||||
|
||||
//! Stop watching a resource.
|
||||
void resource_unwatch(ResourceCallbackHandle cb_handle);
|
||||
|
||||
//! @} // end addtogroup Resources
|
||||
//! @} // end addtogroup Foundation
|
11
src/fw/resource/resource_impl.def
Normal file
11
src/fw/resource/resource_impl.def
Normal file
|
@ -0,0 +1,11 @@
|
|||
// This order is significant.
|
||||
// The first implementation that matches the resource/store query gets it.
|
||||
|
||||
#ifndef RECOVERY_FW
|
||||
RESOURCE_IMPL(g_app_file_impl)
|
||||
RESOURCE_IMPL(g_builtin_impl)
|
||||
RESOURCE_IMPL(g_file_impl)
|
||||
RESOURCE_IMPL(g_system_bank_impl)
|
||||
#else
|
||||
RESOURCE_IMPL(g_builtin_impl)
|
||||
#endif
|
41
src/fw/resource/resource_mapped.c
Normal file
41
src/fw/resource/resource_mapped.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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_mapped.h"
|
||||
|
||||
#include "drivers/flash.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#if CAPABILITY_HAS_MAPPABLE_FLASH
|
||||
static uint32_t s_mapped_refcount_for_task[NumPebbleTask];
|
||||
|
||||
void resource_mapped_use(PebbleTask task) {
|
||||
// We keep track of the refcount per task so we can cleanup all resources for a task
|
||||
s_mapped_refcount_for_task[task]++;
|
||||
flash_use();
|
||||
}
|
||||
|
||||
void resource_mapped_release(PebbleTask task) {
|
||||
PBL_ASSERTN(s_mapped_refcount_for_task[task] != 0);
|
||||
s_mapped_refcount_for_task[task]--;
|
||||
flash_release_many(1);
|
||||
}
|
||||
|
||||
void resource_mapped_release_all(PebbleTask task) {
|
||||
flash_release_many(s_mapped_refcount_for_task[task]);
|
||||
s_mapped_refcount_for_task[task] = 0;
|
||||
}
|
||||
#endif
|
24
src/fw/resource/resource_mapped.h
Normal file
24
src/fw/resource/resource_mapped.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 "kernel/pebble_tasks.h"
|
||||
|
||||
|
||||
void resource_mapped_use(PebbleTask task);
|
||||
|
||||
void resource_mapped_release(PebbleTask task);
|
||||
|
||||
void resource_mapped_release_all(PebbleTask task);
|
382
src/fw/resource/resource_storage.c
Normal file
382
src/fw/resource/resource_storage.c
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* 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.h"
|
||||
#include "resource_storage_impl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "services/normal/filesystem/app_file.h"
|
||||
#include "system/hexdump.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/version.h"
|
||||
#include "util/math.h"
|
||||
#include "util/size.h"
|
||||
|
||||
static const ResourceStoreImplementation *s_resource_store_impls[] = {
|
||||
#define RESOURCE_IMPL(impl) &impl,
|
||||
#include "resource_impl.def"
|
||||
#undef RESOURCE_IMPL
|
||||
};
|
||||
|
||||
// Check if our offset+length is within the resource entry's bounds.
|
||||
// Truncate the length if we overrun the ending.
|
||||
static uint32_t prv_check_resource_bounds(ResourceStoreEntry *entry, uint32_t store_offset,
|
||||
size_t num_bytes) {
|
||||
// If we haven't had the length set yet, just assume we're ok.
|
||||
if (entry->length == ENTRY_LENGTH_UNSET) {
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
uint32_t resource_offset = store_offset - entry->offset;
|
||||
|
||||
if (entry->length < resource_offset) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Resource offset past its own ending.");
|
||||
return 0;
|
||||
} else if ((resource_offset + num_bytes) > entry->length) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "offset + length > resource size, truncated.");
|
||||
return entry->length - resource_offset;
|
||||
} else {
|
||||
return num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t prv_read(ResourceStoreEntry *entry, uint32_t offset, void *data, size_t num_bytes) {
|
||||
num_bytes = prv_check_resource_bounds(entry, offset, num_bytes);
|
||||
if (!num_bytes) {
|
||||
return 0;
|
||||
}
|
||||
return entry->impl->read(entry, offset, data, num_bytes);
|
||||
}
|
||||
|
||||
static void prv_get_manifest(ResourceStoreEntry *entry, ResourceManifest *manifest) {
|
||||
if (prv_read(entry, 0, manifest, sizeof(ResourceManifest)) != sizeof(ResourceManifest)) {
|
||||
*manifest = (ResourceManifest){0};
|
||||
}
|
||||
}
|
||||
|
||||
static bool prv_read_res_table_entry(ResTableEntry *res_entry, ResourceStoreEntry *entry,
|
||||
uint32_t index) {
|
||||
uint32_t addr = MANIFEST_SIZE + index * TABLE_ENTRY_SIZE;
|
||||
|
||||
return prv_read(entry, addr, res_entry, sizeof(ResTableEntry)) == sizeof(ResTableEntry);
|
||||
}
|
||||
|
||||
static uint32_t prv_get_length(ResourceStoreEntry *entry) {
|
||||
return entry->impl->get_length(entry);
|
||||
}
|
||||
|
||||
// entry_offset is the offset of the resource of interest.
|
||||
// If we're doing the whole store, that ends up being 0.
|
||||
static uint32_t prv_get_crc(ResourceStoreEntry *entry, uint32_t num_bytes, uint32_t entry_offset) {
|
||||
num_bytes = prv_check_resource_bounds(entry, entry_offset, num_bytes);
|
||||
if (!num_bytes) {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
return entry->impl->get_crc(entry, num_bytes, entry_offset);
|
||||
}
|
||||
|
||||
T_STATIC uint32_t prv_get_store_length(ResourceStoreEntry *entry, ResourceManifest *manifest) {
|
||||
// Get the resource entry for the last entry
|
||||
ResTableEntry res_entry = {0};
|
||||
if (!prv_read_res_table_entry(&res_entry, entry, manifest->num_resources - 1)) {
|
||||
return 0;
|
||||
}
|
||||
// Get the full ending offset of the last resource.
|
||||
uint32_t resource_end_offset = res_entry.offset + res_entry.length;
|
||||
// Add the store's metadata size to the end address of the last resource.
|
||||
uint32_t store_length = resource_end_offset + resource_store_get_metadata_size(entry);
|
||||
|
||||
// Catch an overflow if the store is enormous (unlikely unless corrupted)
|
||||
if (store_length < resource_end_offset) {
|
||||
// overflow
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Overflow while validating resource");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make sure the store's calculated length is not past the end of the store.
|
||||
if (prv_get_length(entry) < store_length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return the length of the store's resource data
|
||||
return resource_end_offset;
|
||||
}
|
||||
|
||||
static bool prv_validate_store(ResourceManifest *manifest, ResourceStoreEntry *entry,
|
||||
ResAppNum app_num) {
|
||||
uint32_t num_bytes;
|
||||
|
||||
if ((num_bytes = prv_get_store_length(entry, manifest)) == 0) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Resource table check failed. Table or manifest may be corrupted");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t calculated_crc = prv_get_crc(entry, num_bytes, 0);
|
||||
if (calculated_crc != manifest->version.crc) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Resource crc mismatch for app %"PRIu32".", app_num);
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "0x%"PRIx32" != 0x%"PRIx32, calculated_crc, manifest->version.crc);
|
||||
|
||||
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "PBL-28517: If you see this please let Brad know");
|
||||
|
||||
const uint32_t calculated_crc_again = prv_get_crc(entry, num_bytes, 0);
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Num bytes is %"PRIu32, num_bytes);
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Calculated the CRC again, got 0x%"PRIx32, calculated_crc_again);
|
||||
|
||||
return calculated_crc_again == manifest->version.crc;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prv_get_store_entry(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceStoreEntry *entry) {
|
||||
*entry = (ResourceStoreEntry){
|
||||
.id = resource_id,
|
||||
.length = ENTRY_LENGTH_UNSET,
|
||||
};
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH(s_resource_store_impls); i++) {
|
||||
entry->impl = s_resource_store_impls[i];
|
||||
PBL_ASSERTN(entry->impl->find_resource);
|
||||
if (entry->impl->find_resource(entry, app_num, resource_id)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
PBL_LOG(LOG_LEVEL_WARNING,
|
||||
"get_store_entry(%"PRIu32",%"PRIu32") failed to find appropriate store",
|
||||
app_num, resource_id);
|
||||
entry->impl = NULL;
|
||||
}
|
||||
|
||||
static bool prv_validate_entry(ResourceStoreEntry *entry, ResourceManifest *manifest,
|
||||
uint32_t resource_id) {
|
||||
if (entry->id > manifest->num_resources) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Out of bound resource %"PRId32" vs %"PRId32,
|
||||
entry->id, manifest->num_resources);
|
||||
return false;
|
||||
}
|
||||
|
||||
ResTableEntry table_entry = {0};
|
||||
if (!prv_read_res_table_entry(&table_entry, entry, entry->id - 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry->id != table_entry.resource_id) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Resource table entry for %" PRIx32 " is corrupt!"
|
||||
"(%"PRIx32" != %"PRIx32")", resource_id, entry->id, table_entry.resource_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t resource_crc = prv_get_crc(entry, table_entry.length, table_entry.offset);
|
||||
if (resource_crc != table_entry.crc) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Bad resource CRC for %" PRIx32 ", %" PRIx32
|
||||
" vs %" PRIx32, resource_id, resource_crc, table_entry.crc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t resource_store_get_metadata_size(ResourceStoreEntry *entry) {
|
||||
return entry->impl->metadata_size(entry);
|
||||
}
|
||||
|
||||
void resource_storage_clear(ResAppNum app_num) {
|
||||
ResourceStoreEntry entry;
|
||||
prv_get_store_entry(app_num, 0, &entry);
|
||||
if (entry.impl) {
|
||||
entry.impl->clear(&entry);
|
||||
}
|
||||
}
|
||||
|
||||
static bool prv_get_manifest_by_id(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceManifest *manifest) {
|
||||
ResourceStoreEntry entry;
|
||||
prv_get_store_entry(app_num, resource_id, &entry);
|
||||
if (!entry.impl) {
|
||||
return false;
|
||||
}
|
||||
prv_get_manifest(&entry, manifest);
|
||||
return true;
|
||||
}
|
||||
|
||||
ResourceVersion resource_storage_get_version(ResAppNum app_num, uint32_t resource_id) {
|
||||
ResourceManifest manifest;
|
||||
if (!prv_get_manifest_by_id(app_num, resource_id, &manifest)) {
|
||||
return (ResourceVersion) {0};
|
||||
}
|
||||
return manifest.version;
|
||||
}
|
||||
|
||||
uint32_t resource_storage_get_num_entries(ResAppNum app_num, uint32_t resource_id) {
|
||||
ResourceManifest manifest;
|
||||
if (!prv_get_manifest_by_id(app_num, resource_id, &manifest)) {
|
||||
return 0;
|
||||
}
|
||||
return manifest.num_resources;
|
||||
}
|
||||
|
||||
// if resource_id == 0 then check all of resource storage, else just validate
|
||||
// that the resource requested is valid
|
||||
bool resource_storage_check(ResAppNum app_num, uint32_t resource_id,
|
||||
const ResourceVersion *expected_version) {
|
||||
ResourceStoreEntry entry;
|
||||
prv_get_store_entry(app_num, resource_id, &entry);
|
||||
if (!entry.impl) {
|
||||
return false;
|
||||
}
|
||||
return entry.impl->check(app_num, resource_id, &entry, expected_version);
|
||||
}
|
||||
|
||||
void resource_storage_init(void) {
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH(s_resource_store_impls); i++) {
|
||||
if (s_resource_store_impls[i]->init) {
|
||||
s_resource_store_impls[i]->init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t resource_storage_read(ResourceStoreEntry *entry, uint32_t offset, void *data,
|
||||
size_t num_bytes) {
|
||||
return prv_read(entry, offset + entry->offset, data, num_bytes);
|
||||
}
|
||||
|
||||
void resource_storage_get_resource(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceStoreEntry *entry) {
|
||||
prv_get_store_entry(app_num, resource_id, entry);
|
||||
if (!entry->impl) {
|
||||
*entry = (ResourceStoreEntry){0};
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry->impl->get_resource(entry)) {
|
||||
*entry = (ResourceStoreEntry){0};
|
||||
return;
|
||||
}
|
||||
PBL_ASSERTN(entry->length != ENTRY_LENGTH_UNSET);
|
||||
}
|
||||
|
||||
ResourceCallbackHandle resource_watch(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceChangedCallback callback, void* data) {
|
||||
ResourceStoreEntry entry;
|
||||
prv_get_store_entry(app_num, resource_id, &entry);
|
||||
if (!entry.impl) {
|
||||
return NULL;
|
||||
}
|
||||
return entry.impl->watch(&entry, callback, data);
|
||||
}
|
||||
|
||||
void resource_unwatch(ResourceCallbackHandle cb_handle) {
|
||||
#ifndef RECOVERY_FW
|
||||
// TODO: Support unwatching not-files.
|
||||
g_file_impl.unwatch(cb_handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
void resource_storage_get_file_name(char *name, size_t buf_length, ResAppNum resource_bank) {
|
||||
app_file_name_make(name, buf_length, resource_bank, APP_RESOURCES_FILENAME_SUFFIX,
|
||||
strlen(APP_RESOURCES_FILENAME_SUFFIX));
|
||||
}
|
||||
|
||||
|
||||
void resource_storage_generic_init(void) {
|
||||
}
|
||||
|
||||
void resource_storage_generic_clear(ResourceStoreEntry *entry) {
|
||||
}
|
||||
|
||||
bool resource_storage_generic_check(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceStoreEntry *entry,
|
||||
const ResourceVersion *expected_version) {
|
||||
ResourceManifest manifest;
|
||||
prv_get_manifest(entry, &manifest);
|
||||
if (expected_version && !resource_version_matches(&manifest.version, expected_version)) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "expected version <%#010"PRIx32", %"PRIu32">,",
|
||||
expected_version->crc, expected_version->timestamp);
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "got <%#010"PRIx32", %"PRIu32">,",
|
||||
manifest.version.crc, manifest.version.timestamp);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (manifest.num_resources == 0) {
|
||||
// no resources, no need to read anything more
|
||||
return true;
|
||||
}
|
||||
|
||||
if (resource_id == 0) {
|
||||
return (prv_validate_store(&manifest, entry, app_num));
|
||||
} else if (!prv_validate_entry(entry, &manifest, resource_id)) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Resource %"PRId32" check for App %"PRIu32" failed",
|
||||
resource_id, app_num);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t resource_storage_generic_metadata_size(ResourceStoreEntry *entry) {
|
||||
return RESOURCE_STORE_METADATA_BYTES;
|
||||
}
|
||||
|
||||
bool resource_storage_generic_get_resource(ResourceStoreEntry *entry) {
|
||||
ResourceManifest manifest;
|
||||
prv_get_manifest(entry, &manifest);
|
||||
if (entry->id > manifest.num_resources) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResTableEntry table_entry;
|
||||
if (!prv_read_res_table_entry(&table_entry, entry, entry->id - 1)) {
|
||||
return false;
|
||||
}
|
||||
if ((table_entry.resource_id != entry->id) ||
|
||||
(table_entry.length == 0)) {
|
||||
// empty resource
|
||||
return false;
|
||||
}
|
||||
entry->offset = resource_store_get_metadata_size(entry) + table_entry.offset;
|
||||
entry->length = table_entry.length;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t resource_storage_generic_get_length(ResourceStoreEntry *entry) {
|
||||
return entry->length;
|
||||
}
|
||||
|
||||
uint32_t resource_storage_generic_get_crc(ResourceStoreEntry *entry, uint32_t num_bytes,
|
||||
uint32_t entry_offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t resource_storage_generic_write(ResourceStoreEntry *entry, uint32_t offset, void *data,
|
||||
size_t num_bytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResourceCallbackHandle resource_storage_generic_watch(ResourceStoreEntry *entry,
|
||||
ResourceChangedCallback callback,
|
||||
void* data) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "resource_watch not supported for resource type %d.",
|
||||
entry->impl->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool resource_storage_generic_unwatch(ResourceCallbackHandle cb_handle) {
|
||||
return false;
|
||||
}
|
100
src/fw/resource/resource_storage.h
Normal file
100
src/fw/resource/resource_storage.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
typedef enum {
|
||||
InvalidResourceStore = 0,
|
||||
/* System Bank */
|
||||
ResourceStoreTypeSystemBank,
|
||||
/* App Banks in PFS */
|
||||
ResourceStoreTypeAppFile,
|
||||
/* Baked in FW. E.g. Fallback Font */
|
||||
ResourceStoreTypeBuiltIn,
|
||||
/* Filesystem stored resources */
|
||||
ResourceStoreTypeFile,
|
||||
} ResourceStoreType;
|
||||
|
||||
struct ResourceStoreImplementation;
|
||||
|
||||
typedef struct {
|
||||
uint32_t id; // Used when the store implementation needs to permute the resource_id
|
||||
const struct ResourceStoreImplementation *impl;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
const void *store_data;
|
||||
} ResourceStoreEntry;
|
||||
|
||||
// Used to flag that the ResourceStoreEntry hasn't had its length filled yet.
|
||||
#define ENTRY_LENGTH_UNSET ((uint32_t)~0)
|
||||
|
||||
// The filename suffix we use to represent a resource file
|
||||
#define APP_RESOURCES_FILENAME_SUFFIX "res"
|
||||
|
||||
typedef struct ResourceStoreImplementation {
|
||||
ResourceStoreType type;
|
||||
// None of these callbacks may be NULL. There are generic implementations of most that can be
|
||||
// used if nothing 'unique' needs to be done.
|
||||
|
||||
void (*init)(void);
|
||||
void (*clear)(ResourceStoreEntry *entry);
|
||||
// if resource_id == 0 then check all of resource storage, else just validate
|
||||
// that the resource requested is valid
|
||||
bool (*check)(ResAppNum app_num, uint32_t resource_id, ResourceStoreEntry *entry,
|
||||
const ResourceVersion *expected_version);
|
||||
|
||||
uint32_t (*metadata_size)(ResourceStoreEntry *entry);
|
||||
bool (*find_resource)(ResourceStoreEntry *entry, ResAppNum app_num, uint32_t resource_id);
|
||||
bool (*get_resource)(ResourceStoreEntry *entry);
|
||||
|
||||
uint32_t (*get_length)(ResourceStoreEntry *entry);
|
||||
uint32_t (*get_crc)(ResourceStoreEntry *entry, uint32_t num_bytes, uint32_t entry_offset);
|
||||
uint32_t (*write)(ResourceStoreEntry *entry, uint32_t offset, void *data, size_t num_bytes);
|
||||
uint32_t (*read)(ResourceStoreEntry *entry, uint32_t offset, void *data, size_t num_bytes);
|
||||
const uint8_t *(*readonly_bytes)(ResourceStoreEntry *entry, bool has_privileged_access);
|
||||
|
||||
ResourceCallbackHandle (*watch)(ResourceStoreEntry *entry, ResourceChangedCallback callback,
|
||||
void* data);
|
||||
bool (*unwatch)(ResourceCallbackHandle cb_handle);
|
||||
} ResourceStoreImplementation;
|
||||
|
||||
void resource_storage_init(void);
|
||||
|
||||
void resource_storage_clear(ResAppNum app_num);
|
||||
|
||||
bool resource_storage_check(ResAppNum app_num, uint32_t resource_id,
|
||||
const ResourceVersion *expected_version);
|
||||
|
||||
ResourceVersion resource_storage_get_version(ResAppNum app_num, uint32_t resource_id);
|
||||
|
||||
uint32_t resource_storage_get_num_entries(ResAppNum app_num, uint32_t resource_id);
|
||||
|
||||
void resource_storage_get_resource(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceStoreEntry *entry);
|
||||
|
||||
uint32_t resource_storage_read(ResourceStoreEntry *entry, uint32_t offset, void *data,
|
||||
size_t num_bytes);
|
||||
|
||||
uint32_t resource_store_get_metadata_size(ResourceStoreEntry *entry);
|
||||
|
||||
// TODO PBL-21009: Move this somewhere else.
|
||||
void resource_storage_get_file_name(char *name, size_t buf_length, ResAppNum app_num);
|
120
src/fw/resource/resource_storage_builtin.c
Normal file
120
src/fw/resource/resource_storage_builtin.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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_builtin.h"
|
||||
#include "resource_storage_impl.h"
|
||||
|
||||
#include "kernel/memory_layout.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "services/normal/process_management/app_storage.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern const BuiltInResourceData g_builtin_resources[];
|
||||
extern const uint32_t g_num_builtin_resources;
|
||||
|
||||
static uint32_t resource_storage_builtin_read(ResourceStoreEntry *entry, uint32_t offset,
|
||||
void *data, size_t num_bytes) {
|
||||
const BuiltInResourceData *builtin = entry->store_data;
|
||||
if (!builtin) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(data, builtin->address + offset, num_bytes);
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
bool resource_storage_builtin_bytes_are_readonly(const void *bytes) {
|
||||
if (bytes == NULL) {
|
||||
return false;
|
||||
}
|
||||
return memory_layout_is_pointer_in_region(memory_layout_get_microflash_region(), bytes);
|
||||
}
|
||||
|
||||
static const uint8_t *resource_storage_builtin_readonly_bytes(ResourceStoreEntry *entry,
|
||||
bool has_privileged_access) {
|
||||
const BuiltInResourceData *builtin = entry->store_data;
|
||||
if (!builtin) {
|
||||
return NULL;
|
||||
}
|
||||
return builtin->address;
|
||||
}
|
||||
|
||||
static bool resource_storage_builtin_find_resource(ResourceStoreEntry *entry, ResAppNum app_num,
|
||||
uint32_t resource_id) {
|
||||
if (app_num != SYSTEM_APP) {
|
||||
return false;
|
||||
}
|
||||
// Story time! This is closely related to PBL-14367
|
||||
// resource_id == 0 means get the store.
|
||||
// HOWEVER, both builtin and flash stores respond to (app_num,rsrc_id) == (SYSTEM_APP,*)
|
||||
// When we ask for (SYSTEM_APP,0), we always want to actually be getting the flash store.
|
||||
// As a result, we should return false when rsrc_id == 0.
|
||||
// In the future, we need to change this hideously gross behavior.
|
||||
// BUTTTTTTT, PRF only has builtin, so we _need_ to say yes on PRF!
|
||||
if (resource_id == 0) {
|
||||
#if RECOVERY_FW
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
for (unsigned int i = 0; i < g_num_builtin_resources; ++i) {
|
||||
if (g_builtin_resources[i].resource_id == resource_id) {
|
||||
entry->store_data = &g_builtin_resources[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool resource_storage_builtin_get_resource(ResourceStoreEntry *entry) {
|
||||
const BuiltInResourceData *builtin = entry->store_data;
|
||||
if (!builtin) {
|
||||
return false;
|
||||
}
|
||||
entry->offset = 0;
|
||||
entry->length = builtin->num_bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resource_storage_builtin_check(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceStoreEntry *entry,
|
||||
const ResourceVersion *expected_version) {
|
||||
// Builtins don't have manifests and can't be corrupted because they're built into the micro
|
||||
// flash image.
|
||||
return true;
|
||||
}
|
||||
|
||||
const ResourceStoreImplementation g_builtin_impl = {
|
||||
.type = ResourceStoreTypeBuiltIn,
|
||||
|
||||
.init = resource_storage_generic_init,
|
||||
.clear = resource_storage_generic_clear,
|
||||
.check = resource_storage_builtin_check,
|
||||
|
||||
.metadata_size = resource_storage_generic_metadata_size,
|
||||
.find_resource = resource_storage_builtin_find_resource,
|
||||
.get_resource = resource_storage_builtin_get_resource,
|
||||
|
||||
.get_length = resource_storage_generic_get_length,
|
||||
.get_crc = resource_storage_generic_get_crc,
|
||||
.write = resource_storage_generic_write,
|
||||
.read = resource_storage_builtin_read,
|
||||
.readonly_bytes = resource_storage_builtin_readonly_bytes,
|
||||
|
||||
.watch = resource_storage_generic_watch,
|
||||
.unwatch = resource_storage_generic_unwatch,
|
||||
};
|
30
src/fw/resource/resource_storage_builtin.h
Normal file
30
src/fw/resource/resource_storage_builtin.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//! @file resource_storage_builtin.h
|
||||
|
||||
typedef struct {
|
||||
uint32_t resource_id;
|
||||
const uint8_t *address;
|
||||
uint32_t num_bytes;
|
||||
} BuiltInResourceData;
|
||||
|
||||
bool resource_storage_builtin_bytes_are_readonly(const void *bytes);
|
276
src/fw/resource/resource_storage_file.c
Normal file
276
src/fw/resource/resource_storage_file.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* 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_impl.h"
|
||||
#include "resource_storage_file.h"
|
||||
|
||||
#include "kernel/util/sleep.h"
|
||||
#include "services/normal/filesystem/pfs.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern const FileResourceData g_file_resource_stores[];
|
||||
extern const uint32_t g_num_file_resource_stores;
|
||||
|
||||
// Common helpers functions
|
||||
//
|
||||
// These functions are highly coupled to the ones that call them but they're just for code
|
||||
// deduplication and not actually intended to be reusable or provide encapsulation.
|
||||
|
||||
static uint32_t prv_file_common_get_length_and_close(int fd) {
|
||||
if (fd < 0) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t length = pfs_get_file_size(fd);
|
||||
pfs_close(fd);
|
||||
return length;
|
||||
}
|
||||
|
||||
static uint32_t prv_file_common_get_crc(int fd, uint32_t num_bytes, uint32_t entry_offset) {
|
||||
if (fd < 0) {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
uint32_t crc = pfs_crc_calculate_file(fd, RESOURCE_STORE_METADATA_BYTES + entry_offset,
|
||||
num_bytes);
|
||||
pfs_close(fd);
|
||||
return crc;
|
||||
}
|
||||
|
||||
static uint32_t prv_file_common_read(int fd, uint32_t offset, void *data, size_t num_bytes) {
|
||||
if (fd < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bytes_read = 0;
|
||||
|
||||
if (pfs_seek(fd, offset, FSeekSet) >= 0) {
|
||||
bytes_read = pfs_read(fd, data, num_bytes);
|
||||
|
||||
// prevent invalid resource API return values
|
||||
if (bytes_read < 0) {
|
||||
bytes_read = 0;
|
||||
}
|
||||
}
|
||||
pfs_close(fd);
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ResourceStoreTypeFile implementation
|
||||
|
||||
static int prv_file_open_by_name(const char *name, uint8_t op_flags) {
|
||||
int fd = pfs_open(name, op_flags, FILE_TYPE_STATIC, 0);
|
||||
|
||||
if ((fd < 0) && (fd != E_DOES_NOT_EXIST)) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Could not open resource pfs file <%s>, fd: %d", name, fd);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int prv_file_open(ResourceStoreEntry *entry, uint8_t op_flags) {
|
||||
return prv_file_open_by_name(((FileResourceData *) entry->store_data)->name, op_flags);
|
||||
}
|
||||
|
||||
static uint32_t resource_storage_file_get_length(ResourceStoreEntry *entry) {
|
||||
const uint8_t op_flags = OP_FLAG_READ | OP_FLAG_SKIP_HDR_CRC_CHECK | OP_FLAG_USE_PAGE_CACHE;
|
||||
return prv_file_common_get_length_and_close(prv_file_open(entry, op_flags));
|
||||
}
|
||||
|
||||
static uint32_t resource_storage_file_get_crc(ResourceStoreEntry *entry, uint32_t num_bytes,
|
||||
uint32_t entry_offset) {
|
||||
const uint8_t op_flags = OP_FLAG_READ;
|
||||
return prv_file_common_get_crc(prv_file_open(entry, op_flags), num_bytes, entry_offset);
|
||||
}
|
||||
|
||||
static uint32_t resource_storage_file_read(ResourceStoreEntry *entry, uint32_t offset, void *data,
|
||||
size_t num_bytes) {
|
||||
const uint8_t op_flags = OP_FLAG_READ | OP_FLAG_SKIP_HDR_CRC_CHECK | OP_FLAG_USE_PAGE_CACHE;
|
||||
return prv_file_common_read(prv_file_open(entry, op_flags), offset, data, num_bytes);
|
||||
}
|
||||
|
||||
static const uint8_t *resource_storage_file_readonly_bytes_unsupported(ResourceStoreEntry *entry,
|
||||
bool has_privileged_access) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool resource_storage_file_find_resource(ResourceStoreEntry *entry, ResAppNum app_num,
|
||||
uint32_t resource_id) {
|
||||
if (app_num != SYSTEM_APP) {
|
||||
return false;
|
||||
}
|
||||
for (unsigned int i = 0; i < g_num_file_resource_stores; ++i) {
|
||||
if (g_file_resource_stores[i].first_resource_id > resource_id) {
|
||||
break;
|
||||
} else if (g_file_resource_stores[i].last_resource_id >= resource_id) {
|
||||
const FileResourceData *file = &g_file_resource_stores[i];
|
||||
entry->store_data = file;
|
||||
entry->id -= file->resource_id_offset;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static ResourceCallbackHandle resource_storage_file_watch(ResourceStoreEntry *entry,
|
||||
ResourceChangedCallback callback,
|
||||
void* data) {
|
||||
const FileResourceData *file = entry->store_data;
|
||||
if (!file) {
|
||||
return NULL;
|
||||
}
|
||||
PFSCallbackHandle cb_handle = pfs_watch_file(file->name, callback, FILE_CHANGED_EVENT_ALL, data);
|
||||
return cb_handle;
|
||||
}
|
||||
|
||||
static bool resource_storage_file_unwatch(ResourceCallbackHandle cb_handle) {
|
||||
pfs_unwatch_file(cb_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void resource_storage_file_init(void) {
|
||||
// Make sure the files we have are valid
|
||||
for (unsigned int i = 0; i < g_num_file_resource_stores; ++i) {
|
||||
// The only way we can check this file is valid is by making sure each resource in each file
|
||||
// is valid.
|
||||
|
||||
// Get the length of the file to see if we're checking a large file
|
||||
const char *name = g_file_resource_stores[i].name;
|
||||
const uint8_t op_flags = OP_FLAG_READ | OP_FLAG_SKIP_HDR_CRC_CHECK | OP_FLAG_USE_PAGE_CACHE;
|
||||
const int fd = prv_file_open_by_name(name, op_flags);
|
||||
|
||||
const uint32_t file_length = prv_file_common_get_length_and_close(fd);
|
||||
PBL_LOG(LOG_LEVEL_INFO, "File %s has length %"PRIu32, name, file_length);
|
||||
|
||||
// Now check each entry in the file
|
||||
for (uint32_t resource_id = g_file_resource_stores[i].first_resource_id;
|
||||
resource_id <= g_file_resource_stores[i].last_resource_id; resource_id++) {
|
||||
// TODO PBL-21402
|
||||
if (!resource_storage_check(SYSTEM_APP, resource_id, NULL)) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "System resource file %"PRIu32" corrupt!!!", resource_id);
|
||||
}
|
||||
|
||||
const uint32_t large_file_size_threshold = 200 * 1024;
|
||||
if (file_length > large_file_size_threshold) {
|
||||
// If this file is over 200KB, it's going to take a while to CRC. Let's sleep a bit
|
||||
// between entries so we don't starve out our background task. See PBL-24560 for a real
|
||||
// long term fix.
|
||||
psleep(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ResourceStoreImplementation g_file_impl = {
|
||||
.type = ResourceStoreTypeFile,
|
||||
|
||||
.init = resource_storage_file_init,
|
||||
.clear = resource_storage_generic_clear,
|
||||
.check = resource_storage_generic_check,
|
||||
|
||||
.metadata_size = resource_storage_generic_metadata_size,
|
||||
.find_resource = resource_storage_file_find_resource,
|
||||
.get_resource = resource_storage_generic_get_resource,
|
||||
|
||||
.get_length = resource_storage_file_get_length,
|
||||
.get_crc = resource_storage_file_get_crc,
|
||||
.write = resource_storage_generic_write,
|
||||
.read = resource_storage_file_read,
|
||||
.readonly_bytes = resource_storage_file_readonly_bytes_unsupported,
|
||||
|
||||
.watch = resource_storage_file_watch,
|
||||
.unwatch = resource_storage_file_unwatch,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ResourceStoreTypeAppFile implementation
|
||||
|
||||
static int prv_app_file_open(ResourceStoreEntry *entry, uint8_t op_flags) {
|
||||
ResAppNum app_num = (ResAppNum)entry->store_data;
|
||||
if (app_num == SYSTEM_APP) {
|
||||
return -1;
|
||||
}
|
||||
char filename[APP_RESOURCE_FILENAME_MAX_LENGTH + 1]; // extra for null terminator
|
||||
resource_storage_get_file_name(filename, sizeof(filename), app_num);
|
||||
int fd = pfs_open(filename, op_flags, FILE_TYPE_STATIC, 0);
|
||||
|
||||
if ((fd < 0) && (fd != E_DOES_NOT_EXIST)) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Could not open resource pfs file <%s>, fd: %d", filename, fd);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool resource_storage_app_file_find_resource(ResourceStoreEntry *entry, ResAppNum app_num,
|
||||
uint32_t resource_id) {
|
||||
if (app_num == SYSTEM_APP) {
|
||||
return false;
|
||||
}
|
||||
// Need to cast to uintptr_t first to make test compiling on 64-bit happy
|
||||
entry->store_data = (void*)(uintptr_t)app_num;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void resource_storage_app_file_clear(ResourceStoreEntry *entry) {
|
||||
ResAppNum app_num = (ResAppNum)entry->store_data;
|
||||
if (app_num == SYSTEM_APP) {
|
||||
return;
|
||||
}
|
||||
char filename[APP_RESOURCE_FILENAME_MAX_LENGTH + 1]; // extra for null terminator
|
||||
resource_storage_get_file_name(filename, sizeof(filename), app_num);
|
||||
pfs_remove(filename);
|
||||
}
|
||||
|
||||
static uint32_t resource_storage_app_file_get_length(ResourceStoreEntry *entry) {
|
||||
const uint8_t op_flags = OP_FLAG_READ | OP_FLAG_SKIP_HDR_CRC_CHECK | OP_FLAG_USE_PAGE_CACHE;
|
||||
return prv_file_common_get_length_and_close(prv_app_file_open(entry, op_flags));
|
||||
}
|
||||
|
||||
static uint32_t resource_storage_app_file_get_crc(ResourceStoreEntry *entry, uint32_t num_bytes,
|
||||
uint32_t entry_offset) {
|
||||
const uint8_t op_flags = OP_FLAG_READ;
|
||||
return prv_file_common_get_crc(prv_app_file_open(entry, op_flags), num_bytes, entry_offset);
|
||||
}
|
||||
|
||||
static uint32_t resource_storage_app_file_read(ResourceStoreEntry *entry, uint32_t offset,
|
||||
void *data, size_t num_bytes) {
|
||||
const uint8_t op_flags = OP_FLAG_READ | OP_FLAG_SKIP_HDR_CRC_CHECK | OP_FLAG_USE_PAGE_CACHE;
|
||||
return prv_file_common_read(prv_app_file_open(entry, op_flags), offset, data, num_bytes);
|
||||
}
|
||||
|
||||
const ResourceStoreImplementation g_app_file_impl = {
|
||||
.type = ResourceStoreTypeAppFile,
|
||||
|
||||
.init = resource_storage_generic_init,
|
||||
.clear = resource_storage_app_file_clear,
|
||||
.check = resource_storage_generic_check,
|
||||
|
||||
.metadata_size = resource_storage_generic_metadata_size,
|
||||
.find_resource = resource_storage_app_file_find_resource,
|
||||
.get_resource = resource_storage_generic_get_resource,
|
||||
|
||||
.get_length = resource_storage_app_file_get_length,
|
||||
.get_crc = resource_storage_app_file_get_crc,
|
||||
.write = resource_storage_generic_write,
|
||||
.read = resource_storage_app_file_read,
|
||||
.readonly_bytes = resource_storage_file_readonly_bytes_unsupported,
|
||||
|
||||
.watch = resource_storage_generic_watch,
|
||||
.unwatch = resource_storage_generic_unwatch,
|
||||
};
|
31
src/fw/resource/resource_storage_file.h
Normal file
31
src/fw/resource/resource_storage_file.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "resource_storage.h"
|
||||
|
||||
//! @file resource_storage_file.h
|
||||
|
||||
typedef struct {
|
||||
uint32_t first_resource_id;
|
||||
uint32_t last_resource_id;
|
||||
uint32_t resource_id_offset;
|
||||
const char *name;
|
||||
} FileResourceData;
|
||||
|
||||
// TODO PBL-21009: Move this somewhere else.
|
||||
#define APP_RESOURCE_FILENAME_MAX_LENGTH 24
|
211
src/fw/resource/resource_storage_flash.c
Normal file
211
src/fw/resource/resource_storage_flash.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* 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,
|
||||
};
|
35
src/fw/resource/resource_storage_flash.h
Normal file
35
src/fw/resource/resource_storage_flash.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "resource_storage.h"
|
||||
|
||||
//! @file resource_storage_flash.h
|
||||
//!
|
||||
//! Functions for flash-based resource storage implementations
|
||||
|
||||
typedef struct SystemResourceBank {
|
||||
uint32_t begin;
|
||||
uint32_t end;
|
||||
} SystemResourceBank;
|
||||
|
||||
//! Get the extents of a resource storage bank which is not currently in use by
|
||||
//! the system.
|
||||
// TODO PBL-21009: Move this somewhere else.
|
||||
const SystemResourceBank *resource_storage_flash_get_unused_bank(void);
|
||||
|
||||
bool resource_storage_flash_bytes_are_readonly(const void *bytes);
|
87
src/fw/resource/resource_storage_impl.h
Normal file
87
src/fw/resource/resource_storage_impl.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include "resource.h"
|
||||
#include "resource_storage.h"
|
||||
|
||||
//! @file resource_storage_impl.h
|
||||
//!
|
||||
//! Shared functionality that all the different ResourceStoreImplemention's need.
|
||||
|
||||
// TODO PBL-21382: Abstract these details out of the resource storage implementation.
|
||||
|
||||
// Apart from builtins which do not have a header at all, the resource stores
|
||||
// are structured as follow:
|
||||
//
|
||||
// +----------------------------------------------------------------+
|
||||
// | ResourceManifest | ResTableEntry (n-times) | Raw resource data |
|
||||
// +----------------------------------------------------------------+
|
||||
//
|
||||
// Each ResTableEntry contains metadata about resources, and an offset in the
|
||||
// raw resource data blob.
|
||||
//
|
||||
// More info at:
|
||||
// https://pebbletechnology.atlassian.net/wiki/display/DEV/Pebble+Resource+Pack+Format
|
||||
|
||||
//! Actually baked into the flash storage format.
|
||||
//! Do not change this without changing the associated tooling!
|
||||
typedef struct PACKED {
|
||||
uint32_t num_resources;
|
||||
ResourceVersion version;
|
||||
} ResourceManifest;
|
||||
|
||||
//! Actually baked into the flash storage format.
|
||||
//! Do not change this without changing the associated tooling!
|
||||
typedef struct PACKED {
|
||||
uint32_t resource_id;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
uint32_t crc;
|
||||
} ResTableEntry;
|
||||
|
||||
#define MAX_RESOURCES_PER_STORE 256
|
||||
#define MAX_RESOURCES_FOR_SYSTEM_STORE 512
|
||||
#define MANIFEST_SIZE (sizeof(ResourceManifest))
|
||||
#define TABLE_ENTRY_SIZE (sizeof(ResTableEntry))
|
||||
#define RESOURCE_STORE_METADATA_BYTES \
|
||||
(MANIFEST_SIZE + MAX_RESOURCES_PER_STORE * TABLE_ENTRY_SIZE)
|
||||
#define SYSTEM_STORE_METADATA_BYTES \
|
||||
(MANIFEST_SIZE + MAX_RESOURCES_FOR_SYSTEM_STORE * TABLE_ENTRY_SIZE)
|
||||
|
||||
void resource_storage_generic_init(void);
|
||||
void resource_storage_generic_clear(ResourceStoreEntry *entry);
|
||||
bool resource_storage_generic_check(ResAppNum app_num, uint32_t resource_id,
|
||||
ResourceStoreEntry *entry,
|
||||
const ResourceVersion *expected_version);
|
||||
uint32_t resource_storage_generic_metadata_size(ResourceStoreEntry *entry);
|
||||
bool resource_storage_generic_get_resource(ResourceStoreEntry *entry);
|
||||
uint32_t resource_storage_generic_get_length(ResourceStoreEntry *entry);
|
||||
uint32_t resource_storage_generic_get_crc(ResourceStoreEntry *entry, uint32_t num_bytes,
|
||||
uint32_t entry_offset);
|
||||
uint32_t resource_storage_generic_write(ResourceStoreEntry *entry, uint32_t offset, void *data,
|
||||
size_t num_bytes);
|
||||
ResourceCallbackHandle resource_storage_generic_watch(ResourceStoreEntry *entry,
|
||||
ResourceChangedCallback callback,
|
||||
void* data);
|
||||
bool resource_storage_generic_unwatch(ResourceCallbackHandle cb_handle);
|
||||
|
||||
#define RESOURCE_IMPL(impl) extern const ResourceStoreImplementation impl;
|
||||
#include "resource_impl.def"
|
||||
#undef RESOURCE_IMPL
|
93
src/fw/resource/resource_syscalls.c
Normal file
93
src/fw/resource/resource_syscalls.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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.h"
|
||||
#include "resource_mapped.h"
|
||||
|
||||
#include "process_management/app_manager.h"
|
||||
#include "kernel/memory_layout.h"
|
||||
#include "syscall/syscall_internal.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
//! @file resource_syscalls.c
|
||||
//! The landing place for untrusted code to use resources.
|
||||
|
||||
DEFINE_SYSCALL(size_t, sys_resource_size, ResAppNum app_num, uint32_t resource_id) {
|
||||
if (PRIVILEGE_WAS_ELEVATED) {
|
||||
if (pebble_task_get_current() == PebbleTask_Worker) {
|
||||
// Not allowed from workers
|
||||
syscall_failed();
|
||||
}
|
||||
}
|
||||
|
||||
return resource_size(app_num, resource_id);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(size_t, sys_resource_load_range, ResAppNum app_num,
|
||||
uint32_t id, uint32_t start_offset, uint8_t *data, size_t num_bytes) {
|
||||
|
||||
if (PRIVILEGE_WAS_ELEVATED) {
|
||||
if (pebble_task_get_current() == PebbleTask_Worker) {
|
||||
// Not allowed from workers
|
||||
syscall_failed();
|
||||
}
|
||||
|
||||
syscall_assert_userspace_buffer(data, num_bytes);
|
||||
}
|
||||
|
||||
return resource_load_byte_range_system(app_num, id, start_offset, data, num_bytes);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(bool, sys_resource_bytes_are_readonly, void *ptr) {
|
||||
return resource_bytes_are_readonly(ptr);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(const uint8_t *, sys_resource_read_only_bytes, ResAppNum app_num, uint32_t
|
||||
resource_id, size_t *num_bytes_out) {
|
||||
bool caller_is_privileged = true;
|
||||
if (PRIVILEGE_WAS_ELEVATED) {
|
||||
caller_is_privileged = false;
|
||||
if (pebble_task_get_current() == PebbleTask_Worker) {
|
||||
// Not allowed from workers
|
||||
syscall_failed();
|
||||
}
|
||||
|
||||
// num_bytes_out is optional, so it's perfectly safe for an app to pass in NULL here.
|
||||
if (num_bytes_out) {
|
||||
syscall_assert_userspace_buffer(num_bytes_out, sizeof(*num_bytes_out));
|
||||
}
|
||||
}
|
||||
|
||||
return resource_get_readonly_bytes(app_num, resource_id, num_bytes_out, caller_is_privileged);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(bool, sys_resource_is_valid, ResAppNum app_num, uint32_t resource_id) {
|
||||
return resource_is_valid(app_num, resource_id);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(uint32_t, sys_resource_get_and_cache, ResAppNum app_num, uint32_t resource_id) {
|
||||
return resource_get_and_cache(app_num, resource_id);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(void, sys_resource_mapped_use) {
|
||||
PebbleTask task = pebble_task_get_current();
|
||||
resource_mapped_use(task);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(void, sys_resource_mapped_release) {
|
||||
PebbleTask task = pebble_task_get_current();
|
||||
resource_mapped_release(task);
|
||||
}
|
123
src/fw/resource/system_resource.c
Normal file
123
src/fw/resource/system_resource.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 "system_resource.h"
|
||||
|
||||
#include "applib/fonts/fonts.h"
|
||||
#include "applib/graphics/text_resources.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/memory_layout.h"
|
||||
#include "kernel/panic.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "kernel/util/fw_reset.h"
|
||||
#include "pebble_errors.h"
|
||||
#include "resource/resource.h"
|
||||
#include "resource/resource_storage.h"
|
||||
#include "syscall/syscall_internal.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/testinfra.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "resource/resource_version.auto.h"
|
||||
#include "font_resource_table.auto.h"
|
||||
|
||||
void system_resource_init(void) {
|
||||
if (!resource_init_app(SYSTEM_APP, &SYSTEM_RESOURCE_VERSION)) {
|
||||
// System resources are missing!
|
||||
#if defined(IS_BIGBOARD)
|
||||
pbl_log(LOG_LEVEL_ERROR, __FILE_NAME__, __LINE__,
|
||||
"System resources are missing or corrupt, time to sad watch");
|
||||
launcher_panic(ERROR_BAD_RESOURCES);
|
||||
#else
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "System resources are missing or corrupt! Going to PRF");
|
||||
fw_reset_into_prf();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool system_resource_is_valid(void) {
|
||||
return resource_init_app(SYSTEM_APP, &SYSTEM_RESOURCE_VERSION);
|
||||
}
|
||||
|
||||
#define NUM_SYSTEM_FONTS ARRAY_LENGTH(s_font_resource_keys)
|
||||
|
||||
// Total number of fonts = NUM_SYSTEM_FONTS + 1 for the fallback font
|
||||
FontInfo s_system_fonts_info_table[NUM_SYSTEM_FONTS + 1] KERNEL_READONLY_DATA;
|
||||
|
||||
static GFont prv_load_system_font(const char *font_key) {
|
||||
if (font_key == NULL) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "GETTING FALLBACK FONT");
|
||||
// load fallback font
|
||||
if (!s_system_fonts_info_table[NUM_SYSTEM_FONTS].loaded) {
|
||||
PBL_ASSERTN(text_resources_init_font(SYSTEM_APP, RESOURCE_ID_FONT_FALLBACK_INTERNAL, 0,
|
||||
&s_system_fonts_info_table[NUM_SYSTEM_FONTS]));
|
||||
}
|
||||
return &s_system_fonts_info_table[NUM_SYSTEM_FONTS];
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int) NUM_SYSTEM_FONTS; ++i) {
|
||||
if (0 == strcmp(font_key, s_font_resource_keys[i].key_name)) {
|
||||
FontInfo *fontinfo = &s_system_fonts_info_table[i];
|
||||
uint32_t resource = s_font_resource_keys[i].resource_id;
|
||||
uint32_t extension = s_font_resource_keys[i].extension_id;
|
||||
// if the font has not been initialized yet
|
||||
if (!fontinfo->loaded) {
|
||||
if (!text_resources_init_font(SYSTEM_APP,
|
||||
resource, extension, &s_system_fonts_info_table[i])) {
|
||||
// Can't initialize the font for some reason
|
||||
return NULL;
|
||||
}
|
||||
resource_get_and_cache(SYSTEM_APP, resource);
|
||||
resource_get_and_cache(SYSTEM_APP, extension);
|
||||
}
|
||||
return &s_system_fonts_info_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find the given font, invalid key.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GFont system_resource_get_font(const char *font_key) {
|
||||
GFont result = prv_load_system_font(font_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(GFont, sys_font_get_system_font, const char *font_key) {
|
||||
if (font_key && PRIVILEGE_WAS_ELEVATED) {
|
||||
if (!memory_layout_is_cstring_in_region(memory_layout_get_app_region(), font_key, 100) &&
|
||||
!memory_layout_is_cstring_in_region(memory_layout_get_microflash_region(), font_key, 100)) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Pointer %p not in app or microflash region", font_key);
|
||||
syscall_failed();
|
||||
}
|
||||
}
|
||||
|
||||
return system_resource_get_font(font_key);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(void, sys_font_reload_font, FontInfo *fontinfo) {
|
||||
if (PRIVILEGE_WAS_ELEVATED) {
|
||||
if (!memory_layout_is_pointer_in_region(memory_layout_get_readonly_bss_region(), fontinfo)) {
|
||||
syscall_failed();
|
||||
}
|
||||
}
|
||||
|
||||
text_resources_init_font(fontinfo->base.app_num, fontinfo->base.resource_id,
|
||||
fontinfo->extension.resource_id, fontinfo);
|
||||
}
|
||||
|
26
src/fw/resource/system_resource.h
Normal file
26
src/fw/resource/system_resource.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/fonts/fonts.h"
|
||||
|
||||
void system_resource_init(void);
|
||||
|
||||
bool system_resource_is_valid(void);
|
||||
|
||||
GFont system_resource_get_font(const char *font_key);
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue