Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,182 @@
/*
* 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 "app_order_storage.h"
#include "kernel/pbl_malloc.h"
#include "process_management/app_install_manager.h"
#include "services/normal/filesystem/pfs.h"
#include "services/common/system_task.h"
#include "system/logging.h"
#include "system/passert.h"
#define ORDER_FILE "lnc_ord"
typedef struct {
PebbleMutex *order_mutex;
} AppOrderData;
static AppOrderData s_data;
void app_order_storage_init(void) {
s_data.order_mutex = mutex_create();
}
//! Must be called from the App Task
AppMenuOrderStorage *app_order_read_order(void) {
PBL_ASSERT_TASK(PebbleTask_App);
AppMenuOrderStorage *storage = NULL;
bool delete_file = false;
mutex_lock(s_data.order_mutex);
int fd;
if ((fd = pfs_open(ORDER_FILE, OP_FLAG_READ, 0, 0)) < 0) {
PBL_LOG(LOG_LEVEL_ERROR, "Could not open app menu order file");
mutex_unlock(s_data.order_mutex);
return NULL;
}
// Check if it is an valid file
if ((pfs_get_file_size(fd) % sizeof(AppInstallId)) != sizeof(uint8_t)) {
PBL_LOG(LOG_LEVEL_ERROR, "Invalid order storage file");
delete_file = true;
goto cleanup;
}
// Read the number of AppInstallId's listed in the file
uint8_t list_length;
if (pfs_read(fd, &list_length, sizeof(uint8_t)) != sizeof(uint8_t)) {
PBL_LOG(LOG_LEVEL_ERROR, "Could not read app menu order file");
delete_file = true;
goto cleanup;
}
// Allocate room for the order list array. Free'd by the caller of the function.
storage = app_malloc(sizeof(AppMenuOrderStorage) + list_length * sizeof(AppInstallId));
if (!storage) {
PBL_LOG(LOG_LEVEL_ERROR, "Failed to malloc stored order install_id list");
goto cleanup;
}
// read in entire list into array
const int read_size = list_length * sizeof(AppInstallId);
int rd_sz;
if ((rd_sz = pfs_read(fd, (uint8_t *)storage->id_list, read_size)) != read_size) {
PBL_LOG(LOG_LEVEL_ERROR, "Corrupted ordered install_id list (Rd %d of %d bytes)",
rd_sz, read_size);
app_free(storage);
storage = NULL;
delete_file = true;
goto cleanup;
}
// set the list length
storage->list_length = list_length;
cleanup:
pfs_close(fd);
if (delete_file) {
pfs_remove(ORDER_FILE);
}
mutex_unlock(s_data.order_mutex);
return storage;
}
//! Should be called on system task.
static void prv_app_order_write_order(AppMenuOrderStorage *storage) {
mutex_lock(s_data.order_mutex);
int storage_size = sizeof(AppMenuOrderStorage) + (storage->list_length * sizeof(AppInstallId));
int fd = pfs_open(ORDER_FILE, OP_FLAG_OVERWRITE, FILE_TYPE_STATIC, storage_size);
if (fd == E_DOES_NOT_EXIST) {
// File doesn't exist, need to create a new file.
fd = pfs_open(ORDER_FILE, OP_FLAG_WRITE, FILE_TYPE_STATIC, storage_size);
}
if (fd < 0) {
PBL_LOG(LOG_LEVEL_ERROR, "Could not create app menu order file");
goto cleanup;
}
// write back the whole file
int wrote_storage_bytes = pfs_write(fd, (uint8_t *)storage, storage_size);
if (wrote_storage_bytes != storage_size) {
PBL_LOG(LOG_LEVEL_ERROR, "Failed to write all bytes of order list");
}
pfs_close(fd);
cleanup:
kernel_free(storage);
mutex_unlock(s_data.order_mutex);
}
typedef struct {
const Uuid *uuid_list;
uint8_t count;
AppMenuOrderStorage *storage;
} UuidTranslateData;
// search for a UUID in a list of UUID's. Return the index in which it was found or -1 if not found.
int prv_uuid_search(const Uuid *find_me, const Uuid *uuid_list, uint8_t count) {
for (int i = 0; i < count; i++) {
if (uuid_equal(&uuid_list[i], find_me)) {
return i;
}
}
return -1;
}
// if an entry appears in the UUID list, place it's install_id in the correct index of
// storage->id_list
bool prv_enumerate_apps(AppInstallEntry *entry, void *data) {
UuidTranslateData *my_data = (UuidTranslateData *) data;
int idx = prv_uuid_search(&entry->uuid, my_data->uuid_list, my_data->count);
if (idx < 0) {
return true; // continue iterating
}
my_data->storage->id_list[idx] = entry->install_id;
return true; // continue iterating
}
//! Should be called on system task.
void write_uuid_list_to_file(const Uuid *uuid_list, uint8_t count) {
PBL_ASSERT_TASK(PebbleTask_KernelBackground);
int storage_size = sizeof(AppMenuOrderStorage) + (count * sizeof(AppInstallId));
AppMenuOrderStorage *storage = kernel_malloc(storage_size);
memset(storage, 0, storage_size);
UuidTranslateData data = {
.uuid_list = uuid_list,
.count = count,
.storage = storage,
};
// go through all install entries
app_install_enumerate_entries(prv_enumerate_apps, &data);
storage->list_length = count;
prv_app_order_write_order(storage);
}

View file

@ -0,0 +1,33 @@
/*
* 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 "process_management/app_install_manager.h"
#include "util/attributes.h"
typedef struct PACKED AppMenuOrderStorage {
uint8_t list_length;
AppInstallId id_list[];
} AppMenuOrderStorage;
void app_order_storage_init(void);
//! Returns an AppMenuOrderStorage struct on the kernel heap
AppMenuOrderStorage *app_order_read_order(void);
//! Writes a list of UUID's to the order file
void write_uuid_list_to_file(const Uuid *uuid_list, uint8_t count);

View file

@ -0,0 +1,136 @@
/*
* 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 "app_storage.h"
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include "util/uuid.h"
#include "drivers/flash.h"
#include "flash_region/flash_region.h"
#include "process_management/pebble_process_info.h"
#include "resource/resource_storage.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/filesystem/app_file.h"
#include "system/logging.h"
#include "system/passert.h"
#include "system/hexdump.h"
#include "util/build_id.h"
// 64k. Note that both tintin and snowy apps have a maximum size of 64k enforced by the SDK, even
// though there isn't enough memory for load more than 24k in practice on tintin.
static const uint32_t APP_MAX_SIZE = 0x10000;
uint32_t app_storage_get_process_load_size(PebbleProcessInfo *info) {
return (info->load_size + info->num_reloc_entries * 4);
}
AppStorageGetAppInfoResult app_storage_get_process_info(PebbleProcessInfo* app_info,
uint8_t *build_id_out, AppInstallId app_id, PebbleTask task_type) {
char process_name[APP_FILENAME_MAX_LENGTH];
app_storage_get_file_name(process_name, sizeof(process_name), app_id, task_type);
int fd;
if ((fd = pfs_open(process_name, OP_FLAG_READ, 0, 0)) < S_SUCCESS) {
return (GET_APP_INFO_COULD_NOT_READ_FORMAT);
}
if (pfs_read(fd, (uint8_t *)app_info, sizeof(PebbleProcessInfo)) != sizeof(PebbleProcessInfo)) {
pfs_close(fd);
return (GET_APP_INFO_COULD_NOT_READ_FORMAT);
}
if (build_id_out) {
const uint8_t padding_size = sizeof(PebbleProcessInfo) % 4;
// The note.gnu.build-id section seems to have a hard-coded word-alignment requirement...
uint8_t note_buffer[BUILD_ID_TOTAL_EXPECTED_LEN + padding_size];
const ElfExternalNote *note = (const ElfExternalNote *) (note_buffer + padding_size);
int result = pfs_read(fd, note_buffer, sizeof(note_buffer));
if ((result == (int) sizeof(note_buffer)) &&
build_id_contains_gnu_build_id(note)) {
memcpy(build_id_out, note->data + note->name_length, BUILD_ID_EXPECTED_LEN);
} else {
memset(build_id_out, 0, BUILD_ID_EXPECTED_LEN);
}
}
pfs_close(fd);
if (strncmp("PBLAPP", app_info->header, sizeof(app_info->header)) != 0) {
// there isn't a valid app in the bank
return GET_APP_INFO_COULD_NOT_READ_FORMAT;
}
const bool is_sdk_compatible =
(app_info->sdk_version.major == PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR &&
app_info->sdk_version.minor <= PROCESS_INFO_CURRENT_SDK_VERSION_MINOR);
if (is_sdk_compatible == false) {
PBL_LOG(LOG_LEVEL_WARNING, "App requires support for SDK version (%u.%u), we only support version (%u.%u).",
app_info->sdk_version.major, app_info->sdk_version.minor,
PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR, PROCESS_INFO_CURRENT_SDK_VERSION_MINOR);
// The app's is built with an SDK that is incompatible with the running fw
return GET_APP_INFO_INCOMPATIBLE_SDK;
}
if (app_info->virtual_size > APP_MAX_SIZE) {
PBL_LOG(LOG_LEVEL_WARNING, "App size (%u) larger than bank size; invalid app.", app_info->virtual_size);
// The app's metadata indicates an app larger than the maximum bank size
return GET_APP_INFO_APP_TOO_LARGE;
}
return GET_APP_INFO_SUCCESS;
}
void app_storage_delete_app(AppInstallId id) {
PBL_ASSERTN(id > 0);
char process_name[APP_FILENAME_MAX_LENGTH];
// remove worker
app_storage_get_file_name(process_name, sizeof(process_name), id, PebbleTask_Worker);
pfs_remove(process_name);
// remove app too
app_storage_get_file_name(process_name, sizeof(process_name), id, PebbleTask_App);
pfs_remove(process_name);
// remove resources
resource_storage_clear(id);
}
bool app_storage_app_exists(AppInstallId id) {
PBL_ASSERTN(id > 0);
char process_name[APP_FILENAME_MAX_LENGTH];
// check app binary first
app_storage_get_file_name(process_name, sizeof(process_name), id, PebbleTask_App);
int fd = pfs_open(process_name, OP_FLAG_READ, 0, 0);
if (fd < 0) {
return false;
}
pfs_close(fd);
// now check resource bank
return resource_storage_check((ResAppNum)id, 0, NULL);
}
void app_storage_get_file_name(char *name, size_t buf_length, AppInstallId app_id,
PebbleTask task) {
const char *task_str = (task == PebbleTask_App) ? APP_FILE_NAME_SUFFIX
: WORKER_FILE_NAME_SUFFIX;
size_t task_str_len =
(task == PebbleTask_App) ? strlen(APP_FILE_NAME_SUFFIX)
: strlen(WORKER_FILE_NAME_SUFFIX);
app_file_name_make(name, buf_length, app_id, task_str, task_str_len);
}

View file

@ -0,0 +1,76 @@
/*
* 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 "kernel/pebble_tasks.h"
#include "flash_region/flash_region.h"
#include "process_management/pebble_process_info.h"
#include "process_management/app_install_types.h"
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#define APP_FILE_NAME_SUFFIX "app"
#define WORKER_FILE_NAME_SUFFIX "worker"
//! @file app_storage.h
//!
//! Dumping ground for functions for discovering and managing apps stored in SPI flash in the 8 app banks. This will
//! eventually be replaced by app_file.h when we're ready to get rid of the 8-app limit, so this file shouldn't exist
//! in a few months.
#define MAX_APP_BANKS 8
#define APP_FILENAME_MAX_LENGTH 32
//! See app_storage_get_app_info
typedef enum AppStorageGetAppInfoResult {
GET_APP_INFO_SUCCESS,
GET_APP_INFO_COULD_NOT_READ_FORMAT,
GET_APP_INFO_INCOMPATIBLE_SDK,
GET_APP_INFO_APP_TOO_LARGE
} AppStorageGetAppInfoResult;
//! Retrieve the process metadata for a given app_bank and performs sanity checks
//! to make sure that the process in the specified app_bank can be run by the current system.
//! @param app_info[in,out] Structure to be populated with information from flash.
//! @param build_id_out[out] Buffer into which the GNU build ID of the process its executable
//! should be copied. The buffer must be at least BUILD_ID_EXPECTED_LEN bytes. OK to pass NULL.
//! If no build ID was present, the buffer will be filled with zeroes.
//! @param app_id The app id for which the app metadata needs to be fetched.
//! @param task PebbleTask_App or PebbleTask_Worker
//! @return See AppStorageGetAppInfoResult
AppStorageGetAppInfoResult app_storage_get_process_info(PebbleProcessInfo* app_info,
uint8_t *build_id_out, AppInstallId app_id, PebbleTask task);
//! Remove related app files for app bank
void app_storage_delete_app(AppInstallId id);
bool app_storage_app_exists(AppInstallId id);
//! Gives a name to a file given the app bank and type
//! @param name Buffer in which to place the filename in
//! @param buf_length Maximum length of buffer
//! @param app_id The app id for which the app metadata needs to be fetched.
//! @param task PebbleTask_App or PebbleTask_Worker
void app_storage_get_file_name(char *name, size_t buf_length, AppInstallId app_id, PebbleTask task);
//! Returns the size of the executable inside the given PebbleProcessInfo
//! @param info pointer to a valid PebbleProcessInfo struct
//! @return the size of the executable inside the given PebbleProcessInfo
uint32_t app_storage_get_process_load_size(PebbleProcessInfo *info);

View file

@ -0,0 +1,101 @@
/*
* 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 "console/prompt.h"
#include "process_management/app_install_manager.h"
#include "process_management/app_manager.h"
#include "services/normal/app_cache.h"
#include "services/normal/blob_db/app_db.h"
#include "services/normal/filesystem/pfs.h"
//! @file process_commands.c
//!
//! Serial commands for process management
extern AppInstallId app_db_check_next_unique_id(void);
void command_app_remove(const char *id_str) {
int32_t id = atoi(id_str);
if (id == 0) {
prompt_send_response("invalid app number");
return;
}
AppInstallEntry entry;
if (!app_install_get_entry_for_install_id(id, &entry)) {
prompt_send_response("failed to get entry");
return;
}
// should delete from blob db and fire off an event to AppInstallManager that does the rest
app_db_delete((uint8_t *)&entry.uuid, sizeof(Uuid));
prompt_send_response("OK");
}
bool prv_print_app_info(AppInstallEntry *entry, void *data) {
if (app_install_id_from_system(entry->install_id)) {
return true;
}
char buffer[120];
char uuid_buffer[UUID_STRING_BUFFER_LENGTH];
uuid_to_string(&entry->uuid, uuid_buffer);
prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRIi32": %s %s", entry->install_id,
entry->name, uuid_buffer);
return true;
}
void command_app_list(void) {
app_install_enumerate_entries(prv_print_app_info, NULL);
}
void command_app_launch(const char *id_str) {
int32_t id = atoi(id_str);
if (id == 0) {
prompt_send_response("invalid app number");
return;
}
AppInstallEntry entry;
bool success = app_install_get_entry_for_install_id(id, &entry);
if (success) {
app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = id });
prompt_send_response("OK");
} else {
prompt_send_response("No app with id");
}
}
void command_worker_launch(const char *id_str) {
int32_t id = atoi(id_str);
if (id == 0) {
prompt_send_response("invalid app number");
return;
}
AppInstallEntry entry;
bool success = app_install_get_entry_for_install_id(id, &entry);
if (success && app_install_entry_has_worker(&entry)) {
app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = id });
prompt_send_response("OK");
} else {
prompt_send_response("No worker with id");
}
}

View file

@ -0,0 +1,19 @@
/*
* 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.
*/
bool process_loader_load_from_flash(PebbleTask task, const ProcessConfig *config,
const PebbleProcessMd *app_md) {
}

View file

@ -0,0 +1,194 @@
/*
* 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 "process_management/process_loader.h"
#include "drivers/flash.h"
#include "kernel/util/segment.h"
#include "process_management/pebble_process_md.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/process_management/app_storage.h"
#include "system/logging.h"
#include "system/passert.h"
#include "util/legacy_checksum.h"
#include <string.h>
//! This comes from the generated pebble.auto.c with all the exported functions in it.
extern const void* const g_pbl_system_tbl[];
// ----------------------------------------------------------------------------------------------
static bool prv_verify_checksum(const PebbleProcessInfo* app_info, const uint8_t* data) {
const uint8_t header_size = sizeof(PebbleProcessInfo);
const uint8_t *crc_data = data + header_size;
const uint32_t app_size = app_info->load_size - header_size;
uint32_t calculated_crc = legacy_defective_checksum_memory(crc_data,
app_size);
if (app_info->crc != calculated_crc) {
PBL_LOG(LOG_LEVEL_WARNING, "Calculated App CRC is 0x%"PRIx32", expected 0x%"PRIx32"!",
calculated_crc, app_info->crc);
return false;
} else {
return true;
}
}
static void * prv_offset_to_address(MemorySegment *segment, size_t offset) {
return (char *)segment->start + offset;
}
// ---------------------------------------------------------------------------------------------
static bool prv_intialize_sdk_process(PebbleTask task, const PebbleProcessInfo *info,
MemorySegment *destination) {
if (!prv_verify_checksum(info, destination->start)) {
PBL_LOG(LOG_LEVEL_DEBUG, "Calculated CRC does not match, aborting...");
return false;
}
// Poke in the address of the OS's API jump table to an address known by the shims
uint32_t *pbl_jump_table_addr = prv_offset_to_address(destination, info->sym_table_addr);
*pbl_jump_table_addr = (uint32_t)&g_pbl_system_tbl;
//
// offset any relative addresses, as indicated by the reloc table
// TODO PBL-1627: insert link to the wiki page I'm about to write about PIC and relocatable
// values
//
// an array of app-relative pointers to addresses needing an offset
uint32_t *reloc_array = prv_offset_to_address(destination, info->load_size);
for (uint32_t i = 0; i < info->num_reloc_entries; ++i) {
// an absolute pointer to an app-relative pointer which needs to be offset
uintptr_t *addr_to_change = prv_offset_to_address(destination, reloc_array[i]);
*addr_to_change = (uintptr_t) prv_offset_to_address(destination, *addr_to_change);
}
// Now fix up the part of RAM where the relocation table overwrote .bss. We don't need the table
// anymore so restore the zero values.
memset(reloc_array, 0, info->num_reloc_entries * 4);
return true;
}
// ----------------------------------------------------------------------------------------------
static bool prv_load_from_flash(const PebbleProcessMd *app_md, PebbleTask task,
MemorySegment *destination) {
PebbleProcessInfo info;
AppStorageGetAppInfoResult result;
AppInstallId app_id = process_metadata_get_code_bank_num(app_md);
result = app_storage_get_process_info(&info, NULL, app_id, task);
if (result != GET_APP_INFO_SUCCESS) {
// Failed to load the app out of flash, this function will have already printed an error.
return false;
}
// We load the full binary (.text + .data) into ram as well as the relocation entries. These
// relocation entries will overlap with the .bss section of the loaded app, but we'll fix that
// up later.
const size_t load_size = app_storage_get_process_load_size(&info);
if (load_size > memory_segment_get_size(destination)) {
PBL_LOG(LOG_LEVEL_ERROR,
"App/Worker exceeds available program space: %"PRIu16" + (%"PRIu32" * 4) = %zu",
info.load_size, info.num_reloc_entries, load_size);
return false;
}
// load the process from the pfs file appX or workerX
char process_name[APP_FILENAME_MAX_LENGTH];
int fd;
app_storage_get_file_name(process_name, sizeof(process_name), app_id, task);
if ((fd = pfs_open(process_name, OP_FLAG_READ, 0, 0)) < S_SUCCESS) {
PBL_LOG(LOG_LEVEL_ERROR, "Process open failed for process %s, fd = %d", process_name, fd);
return (false);
}
if (pfs_read(fd, destination->start, load_size) != (int)load_size) {
PBL_LOG(LOG_LEVEL_ERROR, "Process read failed for process %s, fd = %d", process_name, fd);
pfs_close(fd);
return (false);
}
pfs_close(fd);
return prv_intialize_sdk_process(task, &info, destination);
}
// ----------------------------------------------------------------------------------------------
static bool prv_load_from_resource(const PebbleProcessMdResource *app_md,
PebbleTask task,
MemorySegment *destination) {
PebbleProcessInfo info;
PBL_ASSERTN(resource_load_byte_range_system(SYSTEM_APP, app_md->bin_resource_id, 0,
(uint8_t *)&info, sizeof(info)) == sizeof(info));
// We load the full binary (.text + .data) into ram as well as the relocation entries. These
// relocation entries will overlap with the .bss section of the loaded app, but we'll fix that
// up later.
const size_t load_size = app_storage_get_process_load_size(&info);
if (load_size > memory_segment_get_size(destination)) {
PBL_LOG(LOG_LEVEL_ERROR,
"App/Worker exceeds available program space: %"PRIu16" + (%"PRIu32" * 4) = %zu",
info.load_size, info.num_reloc_entries, load_size);
return false;
}
// load the process from the resource
PBL_ASSERTN(resource_load_byte_range_system(SYSTEM_APP, app_md->bin_resource_id, 0,
destination->start, load_size) == load_size);
// Process the relocation entries
return prv_intialize_sdk_process(task, &info, destination);
}
void * process_loader_load(const PebbleProcessMd *app_md, PebbleTask task,
MemorySegment *destination) {
if (app_md->process_storage == ProcessStorageFlash) {
if (!prv_load_from_flash(app_md, task, destination)) {
return NULL;
}
} else if (app_md->process_storage == ProcessStorageResource) {
PebbleProcessMdResource *res_app_md = (PebbleProcessMdResource *)app_md;
if (!prv_load_from_resource(res_app_md, task, destination)) {
return NULL;
}
}
// The final process image size may be smaller than the amount of
// memory required to load it, (the relocation table needs to be
// loaded into memory during load but is not needed after) so the
// memory segment is split only after loading completes.
size_t loaded_size = process_metadata_get_size_bytes(app_md);
if (loaded_size) {
void *main_func = prv_offset_to_address(
destination, (uintptr_t)app_md->main_func);
if (!memory_segment_split(destination, NULL, loaded_size)) {
return NULL;
}
// Set the THUMB bit on the function pointer.
return (void *)((uintptr_t)main_func | 1);
} else {
// No loaded size; must be builtin. The entry point address is
// already a physical address.
return app_md->main_func;
}
}