/* * 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 "pebble_process_md.h" #include "process_loader.h" #include "board/display.h" #include ////////////////////// // Md Field Accessors ////////////////////// const char* process_metadata_get_name(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ((const PebbleProcessMdFlash*) md)->name; } else if (md->process_storage == ProcessStorageResource) { return ((const PebbleProcessMdResource*) md)->name; } return ((const PebbleProcessMdSystem*) md)->name; } uint32_t process_metadata_get_size_bytes(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ((const PebbleProcessMdFlash*) md)->size_bytes; } else if (md->process_storage == ProcessStorageResource) { return ((const PebbleProcessMdResource*) md)->size_bytes; } return 0; } Version process_metadata_get_process_version(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ((const PebbleProcessMdFlash*) md)->process_version; } return (Version) { 0, 0 }; } Version process_metadata_get_sdk_version(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ((const PebbleProcessMdFlash*) md)->sdk_version; } return (Version) { PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR, PROCESS_INFO_CURRENT_SDK_VERSION_MINOR }; } ProcessAppRunLevel process_metadata_get_run_level(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ProcessAppRunLevelNormal; } else if (md->process_storage == ProcessStorageResource) { return ProcessAppRunLevelNormal; } return ((const PebbleProcessMdSystem*) md)->run_level; } int process_metadata_get_code_bank_num(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ((const PebbleProcessMdFlash*) md)->code_bank_num; } return 0; } int process_metadata_get_res_bank_num(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ((const PebbleProcessMdFlash*) md)->res_bank_num; } return 0; } ResourceVersion process_metadata_get_res_version(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ((const PebbleProcessMdFlash*) md)->res_version; } return (ResourceVersion) { 0, 0 } ; } const uint8_t *process_metadata_get_build_id(const PebbleProcessMd *md) { if (md->process_storage == ProcessStorageFlash) { return ((const PebbleProcessMdFlash*) md)->build_id; } return NULL; } ////////////////////// // Md Builders ////////////////////// static void prv_init_from_info_common(PebbleProcessMd *common, const PebbleProcessInfo *info, PebbleTask task, ProcessStorage process_storage) { memcpy(&common->uuid, &info->uuid, sizeof(Uuid)); common->process_storage = process_storage; // Flags common->process_type = process_metadata_flags_process_type(info->flags, task); common->visibility = process_metadata_flags_visibility(info->flags); common->allow_js = process_metadata_flags_allow_js(info->flags); common->has_worker = process_metadata_flags_has_worker(info->flags); common->is_rocky_app = process_metadata_flags_rocky_app(info->flags); common->stored_sdk_platform = process_metadata_flags_stored_sdk_platform(info->flags); common->is_unprivileged = true; // We don't know the load address of the process until the process is // actually loaded, so we can't convert the entry point's offset into // an address until it's actually been loaded into that address. // Just shove the unmodified offset into the struct and let the // process loader convert it into an absolute address. // TODO: refactor the PebbleProcessMd stuff so that metadata that // isn't needed before load or can't be fully resolved until then // is left out of PebbleProcessMdCommon. common->main_func = (void *)(uintptr_t)info->offset; } void process_metadata_init_with_flash_header(PebbleProcessMdFlash *md, const PebbleProcessInfo *info, int code_bank_num, PebbleTask task, uint8_t *build_id_buffer) { *md = (PebbleProcessMdFlash){}; prv_init_from_info_common(&md->common, info, task, ProcessStorageFlash); // Flash app specific fields strncpy(md->name, info->name, sizeof(md->name)); md->name[sizeof(md->name) - 1] = 0; md->size_bytes = info->virtual_size; md->process_version = info->process_version; md->sdk_version = info->sdk_version; md->code_bank_num = code_bank_num; md->res_bank_num = code_bank_num; md->res_version = (ResourceVersion) { .crc = info->resource_crc, .timestamp = info->resource_timestamp }; if (build_id_buffer) { memcpy(md->build_id, build_id_buffer, BUILD_ID_EXPECTED_LEN); } } void process_metadata_init_with_resource_header(PebbleProcessMdResource *md, const PebbleProcessInfo *info, int bin_resource_id, PebbleTask task) { *md = (PebbleProcessMdResource){}; prv_init_from_info_common(&md->common, info, task, ProcessStorageResource); // Resource app specific fields strncpy(md->name, info->name, sizeof(md->name)); md->name[sizeof(md->name) - 1] = 0; md->size_bytes = info->virtual_size; md->bin_resource_id = bin_resource_id; } ////////////////////////////////// // PebbleProcessInfoFlags Helpers ////////////////////////////////// ProcessVisibility process_metadata_flags_visibility(PebbleProcessInfoFlags flags) { if (flags & PROCESS_INFO_VISIBILITY_HIDDEN) { return ProcessVisibilityHidden; } else if (flags & PROCESS_INFO_VISIBILITY_SHOWN_ON_COMMUNICATION) { return ProcessVisibilityShownOnCommunication; } else { return ProcessVisibilityShown; } } ProcessType process_metadata_flags_process_type(PebbleProcessInfoFlags flags, PebbleTask task) { // Flags if (flags & PROCESS_INFO_WATCH_FACE) { return ProcessTypeWatchface; } else if (task == PebbleTask_Worker) { // BG_TODO: Set a bit in the PebbleProcessInfo to indicate it's a worker instead of having to // pass in the is_worker argument. Need to update process_metadata_get_flags_bitfield() to // match as well. return ProcessTypeWorker; } else { return ProcessTypeApp; } } bool process_metadata_flags_allow_js(PebbleProcessInfoFlags flags) { return ((flags & PROCESS_INFO_ALLOW_JS) != 0); } bool process_metadata_flags_has_worker(PebbleProcessInfoFlags flags) { return ((flags & PROCESS_INFO_HAS_WORKER) != 0); } bool process_metadata_flags_rocky_app(PebbleProcessInfoFlags flags) { return ((flags & PROCESS_INFO_ROCKY_APP) != 0); } uint16_t process_metadata_flags_stored_sdk_platform(PebbleProcessInfoFlags flags) { return (flags & PROCESS_INFO_PLATFORM_MASK); } static const Version first_3x_version = { PROCESS_INFO_FIRST_3X_SDK_VERSION_MAJOR, PROCESS_INFO_FIRST_3X_SDK_VERSION_MINOR, }; static const Version first_4x_version = { PROCESS_INFO_FIRST_4X_SDK_VERSION_MAJOR, PROCESS_INFO_FIRST_4X_SDK_VERSION_MINOR, }; static const Version first_4_2_version = { PROCESS_INFO_FIRST_4_2_X_SDK_VERSION_MAJOR, PROCESS_INFO_FIRST_4_2_X_SDK_VERSION_MINOR, }; PlatformType process_metadata_get_app_sdk_platform(const PebbleProcessMd *md) { if (!md->is_unprivileged) { return PBL_PLATFORM_TYPE_CURRENT; } const Version app_sdk_version = process_metadata_get_sdk_version(md); // 2.0 <= SDK < 3.0 if (version_compare(app_sdk_version, first_3x_version) < 0) { // 2.x SDKs didn't support anything but Aplite return PlatformTypeAplite; } // 3.0 <= SDK < 4.0 if (version_compare(app_sdk_version, first_4x_version) < 0) { return PBL_PLATFORM_SWITCH(PBL_PLATFORM_TYPE_CURRENT, /* aplite */ PlatformTypeAplite, // unreachable, since we don't build for Tintin anymore /* basalt */ PlatformTypeBasalt, /* chalk */ PlatformTypeChalk, /* diorite */ PlatformTypeAplite, // there's was no Diorite SDK prior to 4.0 /* emery */ PlatformTypeBasalt); } // 4.0 <= SDK < 4.2 if (version_compare(app_sdk_version, first_4_2_version) < 0) { return PBL_PLATFORM_SWITCH(PBL_PLATFORM_TYPE_CURRENT, /* aplite */ PlatformTypeAplite, // unreachable, since we don't build for Tintin anymore /* basalt */ PlatformTypeBasalt, /* chalk */ PlatformTypeChalk, /* diorite */ PlatformTypeDiorite, // there's was no Aplite SDK after 4.0 /* emery */ PlatformTypeBasalt); } // 4.2 <= SDK --> the flags should be filled correctly. const uint32_t stored_value = md->stored_sdk_platform; switch (stored_value) { case PROCESS_INFO_PLATFORM_APLITE: return PlatformTypeAplite; case PROCESS_INFO_PLATFORM_BASALT: return PlatformTypeBasalt; case PROCESS_INFO_PLATFORM_CHALK: return PlatformTypeChalk; case PROCESS_INFO_PLATFORM_DIORITE: return PlatformTypeDiorite; case PROCESS_INFO_PLATFORM_EMERY: return PlatformTypeEmery; default: { // If we encounter an unknown platform, we assume that it's meant for the current platform // (as it's most-likely a system-app). This is not a security risk as developers could always // patch the binaries as they wish anyway return PBL_PLATFORM_TYPE_CURRENT; } } } ProcessAppSDKType process_metadata_get_app_sdk_type(const PebbleProcessMd *md) { if (!md->is_unprivileged) { return ProcessAppSDKType_System; } else { const Version app_sdk_version = process_metadata_get_sdk_version(md); if (version_compare(app_sdk_version, first_3x_version) < 0) { return ProcessAppSDKType_Legacy2x; } else if (version_compare(app_sdk_version, first_4x_version) < 0) { return ProcessAppSDKType_Legacy3x; } else { return ProcessAppSDKType_4x; } } }