mirror of
https://github.com/google/pebble.git
synced 2025-07-10 00:20:27 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
182
src/fw/services/normal/process_management/app_order_storage.c
Normal file
182
src/fw/services/normal/process_management/app_order_storage.c
Normal 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);
|
||||
}
|
|
@ -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);
|
136
src/fw/services/normal/process_management/app_storage.c
Normal file
136
src/fw/services/normal/process_management/app_storage.c
Normal 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);
|
||||
}
|
76
src/fw/services/normal/process_management/app_storage.h
Normal file
76
src/fw/services/normal/process_management/app_storage.h
Normal 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);
|
||||
|
101
src/fw/services/normal/process_management/process_commands.c
Normal file
101
src/fw/services/normal/process_management/process_commands.c
Normal 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");
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue