mirror of
https://github.com/google/pebble.git
synced 2025-05-27 13:33:12 +00:00
209 lines
6.7 KiB
C
209 lines
6.7 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.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);
|
|
}
|
|
|