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,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.
*/
#include "applib/app_message/app_message_internal.h"
#include "process_management/app_install_manager.h"
#include "process_management/app_manager.h"
#include "services/common/analytics/analytics.h"
#include "services/common/comm_session/app_session_capabilities.h"
#include "services/common/comm_session/protocol.h"
#include "services/common/comm_session/session.h"
#include "syscall/syscall_internal.h"
#include <stdbool.h>
#include <stdint.h>
static bool prv_is_endpoint_allowed(uint16_t endpoint_id) {
return (endpoint_id == APP_MESSAGE_ENDPOINT_ID);
}
DEFINE_SYSCALL(CommSession *, sys_app_pp_get_comm_session, void) {
CommSession *app_session = NULL;
comm_session_sanitize_app_session(&app_session);
return app_session;
}
DEFINE_SYSCALL(bool, sys_app_pp_send_data, CommSession *session, uint16_t endpoint_id,
const uint8_t* data, uint16_t length) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(data, length);
}
if (!prv_is_endpoint_allowed(endpoint_id)) {
syscall_failed();
}
comm_session_sanitize_app_session(&session);
if (!session) {
// No session connected that can serve the currently running app
return false;
}
const AppInstallId app_id = app_manager_get_current_app_id();
app_install_mark_prioritized(app_id, true /* can_expire */);
analytics_add(ANALYTICS_APP_METRIC_MSG_BYTE_OUT_COUNT, length, AnalyticsClient_App);
// TODO: apply some heuristic to decide whether to put connection in fast mode or not:
// https://pebbletechnology.atlassian.net/browse/PBL-21538
comm_session_set_responsiveness(session, BtConsumerPpAppMessage, ResponseTimeMin,
MIN_LATENCY_MODE_TIMEOUT_APP_MESSAGE_SECS);
// FIXME: Let the app task wait indefinitely for now
const uint32_t timeout_ms = ~0;
return comm_session_send_data(session, endpoint_id, data, length, timeout_ms);
}
DEFINE_SYSCALL(bool, sys_app_pp_has_capability, CommSessionCapability capability) {
return comm_session_current_app_session_cache_has_capability(capability);
}
DEFINE_SYSCALL(void, sys_app_pp_app_message_analytics_count_drop, void) {
analytics_inc(ANALYTICS_APP_METRIC_MSG_DROP_COUNT, AnalyticsClient_App);
}

View File

@@ -0,0 +1,99 @@
/*
* 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 "applib/app_worker.h"
#include "kernel/event_loop.h"
#include "kernel/ui/modals/modal_manager.h"
#include "kernel/ui/modals/modal_manager_private.h"
#include "process_management/worker_manager.h"
#include "process_management/app_manager.h"
#include "popups/switch_worker_ui.h"
#include "syscall/syscall_internal.h"
// ---------------------------------------------------------------------------------------------------------------
// Determine if the worker for the current app is running
DEFINE_SYSCALL(bool, sys_app_worker_is_running, void) {
const PebbleProcessMd *md = worker_manager_get_current_worker_md();
if (md == NULL) {
return false;
}
return (uuid_equal(&md->uuid, &app_manager_get_current_app_md()->uuid));
}
// ---------------------------------------------------------------------------------------------------------------
// Display the confirmation dialog for switching into the worker
static void prv_switch_worker(void *data) {
AppInstallId install_id = (AppInstallId)data;
WindowStack *window_stack = modal_manager_get_window_stack(ModalPriorityGeneric);
switch_worker_confirm(install_id, false /* do not set as default */, window_stack);
}
// Launch the worker for the current app
DEFINE_SYSCALL(AppWorkerResult, sys_app_worker_launch, void) {
AppInstallId install_id = app_manager_get_task_context()->install_id;
// Make sure there is a worker for this app
if (!app_manager_get_task_context()->app_md->has_worker) {
return APP_WORKER_RESULT_NO_WORKER;
}
// Is there a worker already running?
const PebbleProcessMd *md = worker_manager_get_current_worker_md();
if (md != NULL) {
const PebbleProcessMd *app_process = app_manager_get_current_app_md();
if (uuid_equal(&md->uuid, &app_process->uuid)) {
return APP_WORKER_RESULT_ALREADY_RUNNING;
}
// We have to get confirmation first that it is OK to launch the new worker
launcher_task_add_callback(prv_switch_worker, (void *) install_id);
return APP_WORKER_RESULT_ASKING_CONFIRMATION;
}
worker_manager_put_launch_worker_event(install_id);
worker_manager_set_default_install_id(install_id);
return APP_WORKER_RESULT_SUCCESS;
}
// ---------------------------------------------------------------------------------------------------------------
// Kill the worker for the current app
DEFINE_SYSCALL(AppWorkerResult, sys_app_worker_kill, void) {
const PebbleProcessMd *md = worker_manager_get_current_worker_md();
if (md == NULL) {
return APP_WORKER_RESULT_NOT_RUNNING;
}
if (!uuid_equal(&md->uuid, &app_manager_get_current_app_md()->uuid)) {
return APP_WORKER_RESULT_DIFFERENT_APP;
}
process_manager_put_kill_process_event(PebbleTask_Worker, true /*graceful*/);
return APP_WORKER_RESULT_SUCCESS;
}
// ---------------------------------------------------------------------------------------------------------------
// Launch the app for the current worker
DEFINE_SYSCALL(void, sys_launch_app_for_worker, void) {
app_manager_put_launch_app_event(&(AppLaunchEventConfig) {
.id = worker_manager_get_task_context()->install_id,
.common.reason = APP_LAUNCH_WORKER,
});
}

View File

@@ -0,0 +1,285 @@
/*
* 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 "syscall/syscall_internal.h"
#include "applib/bluetooth/ble_client.h"
#include "comm/ble/gap_le.h"
#include "comm/ble/gap_le_advert.h"
#include "comm/ble/gap_le_connection.h"
#include "comm/ble/gap_le_connect.h"
#include "comm/ble/gap_le_scan.h"
#include "comm/ble/gatt_client_accessors.h"
#include "comm/ble/gatt_client_discovery.h"
#include "comm/ble/gatt_client_operations.h"
#include "comm/ble/gatt_client_subscriptions.h"
// -----------------------------------------------------------------------------
// ble_scan.h
DEFINE_SYSCALL(bool, sys_ble_scan_start, void) {
return gap_le_start_scan();
}
DEFINE_SYSCALL(bool, sys_ble_scan_stop, void) {
return gap_le_stop_scan();
}
DEFINE_SYSCALL(bool, sys_ble_scan_is_scanning, void) {
return gap_le_is_scanning();
}
DEFINE_SYSCALL(bool, sys_ble_consume_scan_results, uint8_t *buffer, uint16_t *size_in_out) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(size_in_out, sizeof(*size_in_out));
syscall_assert_userspace_buffer(buffer, *size_in_out);
}
return gap_le_consume_scan_results(buffer, size_in_out);
}
// -----------------------------------------------------------------------------
// ble_ad_parse.h
DEFINE_SYSCALL(int8_t, sys_ble_get_advertising_tx_power, void) {
return gap_le_advert_get_tx_power();
}
// -----------------------------------------------------------------------------
// ble_central.h
DEFINE_SYSCALL(BTErrno, sys_ble_central_connect, BTDeviceInternal device,
bool auto_reconnect, bool is_pairing_required) {
return gap_le_connect_connect(&device, auto_reconnect, is_pairing_required,
GAPLEClientApp);
}
DEFINE_SYSCALL(BTErrno, sys_ble_central_cancel_connect, BTDeviceInternal device) {
return gap_le_connect_cancel(&device, GAPLEClientApp);
}
// -----------------------------------------------------------------------------
// ble_client.h
DEFINE_SYSCALL(BTErrno, sys_ble_client_discover_services_and_characteristics,
BTDeviceInternal device) {
return gatt_client_discovery_discover_all(&device);
}
DEFINE_SYSCALL(uint8_t, sys_ble_client_copy_services, BTDeviceInternal device,
BLEService services[], uint8_t num_services) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(services, sizeof(BLEService) * num_services);
}
return gatt_client_copy_service_refs(&device, services, num_services);
}
DEFINE_SYSCALL(uint16_t, sys_ble_client_get_maximum_value_length, BTDeviceInternal device) {
return gap_le_connection_get_gatt_mtu(&device);
}
DEFINE_SYSCALL(BTErrno, sys_ble_client_read, BLECharacteristic characteristic) {
return gatt_client_op_read(characteristic, GAPLEClientApp);
}
DEFINE_SYSCALL(bool, sys_ble_client_get_notification_value_length,
BLECharacteristic *characteristic_out,
uint16_t *value_length_out) {
if (PRIVILEGE_WAS_ELEVATED) {
if (characteristic_out) {
syscall_assert_userspace_buffer(characteristic_out, sizeof(*characteristic_out));
}
if (value_length_out) {
syscall_assert_userspace_buffer(value_length_out, sizeof(*value_length_out));
}
}
GATTBufferedNotificationHeader header;
const bool has_notification = gatt_client_subscriptions_get_notification_header(GAPLEClientApp,
&header);
if (has_notification) {
if (characteristic_out) {
*characteristic_out = header.characteristic;
}
if (value_length_out) {
*value_length_out = header.value_length;
}
}
return has_notification;
}
DEFINE_SYSCALL(void, sys_ble_client_consume_read, uintptr_t object_ref,
uint8_t value_out[],
uint16_t *value_length_in_out) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(value_length_in_out, sizeof(*value_length_in_out));
syscall_assert_userspace_buffer(value_out, *value_length_in_out);
}
gatt_client_consume_read_response(object_ref,
value_out, *value_length_in_out,
GAPLEClientApp);
}
DEFINE_SYSCALL(bool, sys_ble_client_consume_notification, uintptr_t *object_ref_out,
uint8_t value_out[],
uint16_t *value_length_in_out,
bool *has_more_out) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(object_ref_out, sizeof(*object_ref_out));
syscall_assert_userspace_buffer(value_length_in_out, sizeof(*value_length_in_out));
syscall_assert_userspace_buffer(value_out, *value_length_in_out);
syscall_assert_userspace_buffer(has_more_out, sizeof(*has_more_out));
}
return gatt_client_subscriptions_consume_notification(object_ref_out,
value_out, value_length_in_out,
GAPLEClientApp, has_more_out);
}
DEFINE_SYSCALL(BTErrno, sys_ble_client_write, BLECharacteristic characteristic,
const uint8_t *value,
size_t value_length) {
return gatt_client_op_write(characteristic, value, value_length, GAPLEClientApp);
}
DEFINE_SYSCALL(BTErrno, sys_ble_client_write_without_response,
BLECharacteristic characteristic,
const uint8_t *value,
size_t value_length) {
return gatt_client_op_write_without_response(characteristic,
value, value_length, GAPLEClientApp);
}
DEFINE_SYSCALL(BTErrno, sys_ble_client_subscribe, BLECharacteristic characteristic,
BLESubscription subscription_type) {
return gatt_client_subscriptions_subscribe(characteristic,
subscription_type,
GAPLEClientApp);
}
DEFINE_SYSCALL(BTErrno, sys_ble_client_write_descriptor, BLEDescriptor descriptor,
const uint8_t *value,
size_t value_length) {
return gatt_client_op_write_descriptor(descriptor,
value, value_length, GAPLEClientApp);
}
DEFINE_SYSCALL(BTErrno, sys_ble_client_read_descriptor, BLEDescriptor descriptor) {
return gatt_client_op_read_descriptor(descriptor, GAPLEClientApp);
}
// -----------------------------------------------------------------------------
// ble_service.h
DEFINE_SYSCALL(uint8_t, sys_ble_service_get_characteristics, BLEService service_ref,
BLECharacteristic characteristics_out[],
uint8_t num_characteristics) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(characteristics_out,
sizeof(BLECharacteristic) * num_characteristics);
}
return gatt_client_service_get_characteristics(service_ref,
characteristics_out,
num_characteristics);
}
DEFINE_SYSCALL(void, sys_ble_service_get_uuid, Uuid *uuid, BLEService service_ref) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(uuid, sizeof(*uuid));
}
*uuid = gatt_client_service_get_uuid(service_ref);
}
DEFINE_SYSCALL(void, sys_ble_service_get_device, BTDeviceInternal *device, BLEService service) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(device, sizeof(*device));
}
*device = gatt_client_service_get_device(service);
}
DEFINE_SYSCALL(uint8_t, sys_ble_service_get_included_services, BLEService service_ref,
BLEService included_services_out[],
uint8_t num_services) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(included_services_out,
sizeof(BLEService) * num_services);
}
return gatt_client_service_get_included_services(service_ref,
included_services_out,
num_services);
}
// -----------------------------------------------------------------------------
// ble_characteristic.h
DEFINE_SYSCALL(void, sys_ble_characteristic_get_uuid,
Uuid *uuid, BLECharacteristic characteristic) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(uuid, sizeof(*uuid));
}
*uuid = gatt_client_characteristic_get_uuid(characteristic);
}
DEFINE_SYSCALL(BLEAttributeProperty, sys_ble_characteristic_get_properties,
BLECharacteristic characteristic) {
return gatt_client_characteristic_get_properties(characteristic);
}
DEFINE_SYSCALL(BLEService, sys_ble_characteristic_get_service, BLECharacteristic characteristic) {
return gatt_client_characteristic_get_service(characteristic);
}
DEFINE_SYSCALL(void, sys_ble_characteristic_get_device,
BTDevice *device, BLECharacteristic characteristic) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(device, sizeof(*device));
}
*device = gatt_client_characteristic_get_device(characteristic).opaque;
}
DEFINE_SYSCALL(uint8_t, sys_ble_characteristic_get_descriptors,
BLECharacteristic characteristic,
BLEDescriptor descriptors_out[],
uint8_t num_descriptors) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(descriptors_out,
sizeof(BLEDescriptor) * num_descriptors);
}
return gatt_client_characteristic_get_descriptors(characteristic,
descriptors_out,
num_descriptors);
}
// -----------------------------------------------------------------------------
// ble_descriptor.h
DEFINE_SYSCALL(void, sys_ble_descriptor_get_uuid, Uuid *uuid, BLEDescriptor descriptor) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(uuid, sizeof(*uuid));
}
*uuid = gatt_client_descriptor_get_uuid(descriptor);
}
DEFINE_SYSCALL(BLECharacteristic, sys_ble_descriptor_get_characteristic, BLEDescriptor descriptor) {
return gatt_client_descriptor_get_characteristic(descriptor);
}

View File

@@ -0,0 +1,132 @@
/*
* 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/memory_layout.h"
#include "process_management/app_manager.h"
#include "process_management/worker_manager.h"
#include "process_state/app_state/app_state.h"
#include "services/common/compositor/compositor.h"
#include "services/common/event_service.h"
#include "syscall/syscall_internal.h"
#include "system/logging.h"
#include "system/passert.h"
static void prv_put_event_from_process(PebbleTask task, PebbleEvent *event) {
if (!event_try_put_from_process(task, event)) {
PBL_LOG(LOG_LEVEL_WARNING, "%s: From app queue is full! Dropped %p! Killing App",
(task == PebbleTask_App ? "App" : "Worker"), event);
syscall_failed();
}
}
DEFINE_SYSCALL(void, sys_send_pebble_event_to_kernel, PebbleEvent* event) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(event, sizeof(*event));
}
PebbleTask task = pebble_task_get_current();
if (task == PebbleTask_App || task == PebbleTask_Worker) {
prv_put_event_from_process(task, event);
} else {
event_put(event);
}
}
DEFINE_SYSCALL(void, sys_current_process_schedule_callback,
CallbackEventCallback async_cb, void *ctx) {
// No userspace buffer assertion for ctx needed, because it won't be accessed by the kernel.
PebbleEvent event = {
.type = PEBBLE_CALLBACK_EVENT,
.callback = {
.callback = async_cb,
.data = ctx,
},
};
const PebbleTask task = pebble_task_get_current();
PBL_ASSERTN(task == PebbleTask_App || task == PebbleTask_Worker);
process_manager_send_event_to_process(task, &event);
}
DEFINE_SYSCALL(uint32_t, sys_process_events_waiting, PebbleTask task) {
return process_manager_process_events_waiting(task);
}
DEFINE_SYSCALL(void, sys_event_service_client_subscribe, EventServiceInfo *handler) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(handler, sizeof(*handler));
}
PebbleTask task = pebble_task_get_current();
// Get info
QueueHandle_t *event_queue;
if (task == PebbleTask_App) {
event_queue = app_manager_get_task_context()->to_process_event_queue;
} else if (task == PebbleTask_Worker) {
event_queue = worker_manager_get_task_context()->to_process_event_queue;
} else if (task == PebbleTask_KernelMain) {
// The event service always runs from KernelMain
event_queue = event_kernel_to_kernel_event_queue();
} else {
WTF;
}
// Subscribe to the service!
PebbleEvent event = {
.type = PEBBLE_SUBSCRIPTION_EVENT,
.subscription = {
.subscribe = true,
.task = task,
.event_queue = event_queue,
.event_type = handler->type,
},
};
if (task == PebbleTask_KernelMain) {
// The client is also KernelMain, just subscribe immediately without putting an event
event_service_subscribe_from_kernel_main(&event.subscription);
} else {
prv_put_event_from_process(task, &event);
}
}
DEFINE_SYSCALL(void, sys_event_service_client_unsubscribe, EventServiceInfo *state,
EventServiceInfo *handler) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(handler, sizeof(*handler));
syscall_assert_userspace_buffer(state, sizeof(*state));
}
// Remove from handlers list
list_remove(&handler->list_node, NULL, NULL);
if (list_find(&state->list_node, event_service_filter, (void *) handler->type)) {
// there are other handlers for this task, don't unsubscribe it
return;
}
// Get info
PebbleTask task = pebble_task_get_current();
// Unsubscribe from the service!
PebbleEvent event = {
.type = PEBBLE_SUBSCRIPTION_EVENT,
.subscription = {
.subscribe = false,
.task = task,
.event_type = handler->type,
},
};
prv_put_event_from_process(task, &event);
}

View File

@@ -0,0 +1,82 @@
/*
* 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 <inttypes.h>
#include "syscall/syscall_internal.h"
#include "system/profiler.h"
#define CMSIS_COMPATIBLE
#include <mcu.h>
// ------------------------------------------------------------------------------------
// Find node by ptr
static bool prv_ptr_list_filter(ListNode* list_node, void* data) {
ProfilerNode* node = (ProfilerNode*)list_node;
return (node == data);
}
ProfilerNode *prv_find_node(ProfilerNode *find_node) {
ListNode* node = list_find(g_profiler.nodes, prv_ptr_list_filter, (void*)find_node);
return (ProfilerNode *)node;
}
DEFINE_SYSCALL(void, sys_profiler_init, void) {
profiler_init();
}
DEFINE_SYSCALL(void, sys_profiler_start, void) {
profiler_start();
}
DEFINE_SYSCALL(void, sys_profiler_stop, void) {
profiler_stop();
}
DEFINE_SYSCALL(void, sys_profiler_print_stats, void) {
profiler_print_stats();
}
DEFINE_SYSCALL(void, sys_profiler_node_start, ProfilerNode *node) {
if (PRIVILEGE_WAS_ELEVATED) {
if (!list_contains(g_profiler.nodes, (ListNode *)node)) {
// Instead of calling syscall_failed(), simply return. If PROFILE_INIT has not been
// executed yet, there won't be any nodes in the list.
return;
}
}
node->start = DWT->CYCCNT;
}
DEFINE_SYSCALL(void, sys_profiler_node_stop, ProfilerNode *node) {
// Capture the cycle count as soon as possible, before we validate the node argument
uint32_t dwt_cyc_cnt = DWT->CYCCNT;
if (PRIVILEGE_WAS_ELEVATED) {
if (!list_contains(g_profiler.nodes, (ListNode *)node)) {
// Instead of calling syscall_failed(), simply return. If PROFILE_INIT has not been
// executed yet, there won't be any nodes in the list.
return;
}
}
profiler_node_stop(node, dwt_cyc_cnt);
}

View File

@@ -0,0 +1,22 @@
/*
* 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/util/stack_info.h"
#include "syscall/syscall_internal.h"
DEFINE_SYSCALL(uint32_t, sys_stack_free_bytes, void) {
return stack_free_bytes();
}

99
src/fw/syscall/syscall.c Normal file
View File

@@ -0,0 +1,99 @@
/*
* 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 "time.h"
#include "syscall.h"
#include "syscall_internal.h"
#include "drivers/rtc.h"
#include "kernel/memory_layout.h"
#include "kernel/pebble_tasks.h"
#include "mcu/privilege.h"
#include "os/tick.h"
#include "process_management/app_manager.h"
#include "process_management/worker_manager.h"
#include "services/common/comm_session/session.h"
#include "system/logging.h"
#include "system/passert.h"
#include "util/string.h"
#include "FreeRTOS.h"
#include "task.h"
DEFINE_SYSCALL(int, sys_test, int arg) {
uint32_t ipsr;
__asm volatile("mrs %0, ipsr" : "=r" (ipsr));
PBL_LOG(LOG_LEVEL_DEBUG, "Inside test kernel function! Privileged? %s Arg %u IPSR: %"PRIu32,
bool_to_str(mcu_state_is_privileged()), arg, ipsr);
return arg * 2;
}
DEFINE_SYSCALL(time_t, sys_get_time, void) {
return rtc_get_time();
}
DEFINE_SYSCALL(void, sys_get_time_ms, time_t *t, uint16_t *out_ms) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(t, sizeof(*t));
syscall_assert_userspace_buffer(out_ms, sizeof(*out_ms));
}
rtc_get_time_ms(t, out_ms);
}
DEFINE_SYSCALL(RtcTicks, sys_get_ticks, void) {
return rtc_get_ticks();
}
DEFINE_SYSCALL(void, sys_pbl_log, LogBinaryMessage* log_message, bool async) {
kernel_pbl_log(log_message, async);
}
DEFINE_SYSCALL(void, sys_copy_timezone_abbr, char* timezone_abbr, time_t time) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(timezone_abbr, TZ_LEN);
}
time_get_timezone_abbr(timezone_abbr, time);
}
DEFINE_SYSCALL(struct tm*, sys_gmtime_r, const time_t *timep, struct tm *result) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(timep, sizeof(*timep));
syscall_assert_userspace_buffer(result, sizeof(*result));
}
return gmtime_r(timep, result);
}
DEFINE_SYSCALL(struct tm*, sys_localtime_r, const time_t *timep, struct tm *result) {
if (PRIVILEGE_WAS_ELEVATED) {
syscall_assert_userspace_buffer(timep, sizeof(*timep));
syscall_assert_userspace_buffer(result, sizeof(*result));
}
return localtime_r(timep, result);
}
//! System call to exit an application gracefully.
DEFINE_SYSCALL(NORETURN, sys_exit, void) {
process_manager_task_exit();
}
DEFINE_SYSCALL(void, sys_psleep, int millis) {
vTaskDelay(milliseconds_to_ticks(millis));
}

317
src/fw/syscall/syscall.h Normal file
View File

@@ -0,0 +1,317 @@
/*
* 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/app_comm.h"
#include "applib/app_exit_reason.h"
#include "applib/app_inbox.h"
#include "applib/app_outbox.h"
#include "applib/app_logging.h"
#include "applib/app_timer.h"
#include "applib/app_watch_info.h"
#include "applib/app_worker.h"
#include "applib/bluetooth/ble_client.h"
#include "applib/data_logging.h"
#include "applib/event_service_client.h"
#include "applib/fonts/fonts.h"
#include "applib/ui/window_stack_animation.h"
#include "comm/ble/gap_le_scan.h"
#include "drivers/mag.h"
#include "drivers/rtc.h"
#include "kernel/events.h"
#include "kernel/logging_private.h"
#include "services/normal/wakeup.h"
#include "services/common/analytics/analytics.h"
#include "services/common/analytics/analytics_event.h"
#include "services/common/comm_session/session.h"
#include "services/common/evented_timer.h"
#include "services/normal/activity/activity.h"
#include "services/normal/app_glances/app_glance_service.h"
#include "process_management/pebble_process_info.h"
#include "util/attributes.h"
#include <bluetooth/bluetooth_types.h>
//! @internal
//! Just a dummy syscall that we use in the user mode test app. Remove eventually.
int sys_test(int arg);
time_t sys_get_time(void);
void sys_get_time_ms(time_t *t, uint16_t *out_ms);
RtcTicks sys_get_ticks(void);
struct tm* sys_gmtime_r(const time_t *timep, struct tm *result);
struct tm* sys_localtime_r(const time_t *timep, struct tm *result);
void sys_copy_timezone_abbr(char* timezone_abbr, time_t time);
time_t sys_time_start_of_today(void);
void sys_evented_timer_consume(TimerID timer_id, EventedTimerCallback* out_cb, void** out_cb_data);
void sys_send_pebble_event_to_kernel(PebbleEvent* event);
void sys_current_process_schedule_callback(CallbackEventCallback async_cb, void *ctx);
uint32_t sys_process_events_waiting(PebbleTask task);
void sys_get_pebble_event(PebbleEvent* event);
void sys_pbl_log(LogBinaryMessage* log_message, bool async);
NORETURN sys_app_fault(uint32_t stashed_lr);
bool sys_resource_is_valid(ResAppNum app_num, uint32_t resource_id);
size_t sys_resource_size(ResAppNum app_num, uint32_t handle);
size_t sys_resource_load_range(ResAppNum app_num, uint32_t h, uint32_t start_bytes, uint8_t *buffer, size_t num_bytes);
void sys_resource_mapped_use(void);
void sys_resource_mapped_release(void);
bool sys_resource_bytes_are_readonly(void *bytes);
const uint8_t * sys_resource_read_only_bytes(ResAppNum app_num, uint32_t resource_id,
size_t *num_bytes_out);
void sys_resource_mapped_use(void);
void sys_resource_mapped_release_many(uint32_t num);
uint32_t sys_resource_get_and_cache(ResAppNum app_num, uint32_t resource_id);
NORETURN sys_exit(void);
GFont sys_font_get_system_font(const char *font_key);
void sys_font_reload_font(FontInfo *fontinfo);
bool sys_vibe_pattern_enqueue_step_raw(uint32_t step_duration_ms, int32_t strength);
bool sys_vibe_pattern_enqueue_step(uint32_t step_duration_ms, bool on);
void sys_vibe_pattern_trigger_start(void);
void sys_vibe_pattern_clear(void);
void sys_vibe_history_start_collecting(void);
void sys_vibe_history_stop_collecting(void);
bool sys_vibe_history_was_vibrating(uint64_t time_search);
int32_t sys_vibe_get_vibe_strength(void);
void sys_get_app_uuid(Uuid *uuid);
bool sys_app_is_watchface(void);
AppInstallId sys_app_manager_get_current_app_id(void);
AppInstallId sys_worker_manager_get_current_worker_id(void);
//! Return the resource number of the current context. If the kernel is asking, SYSTEM_APP is
//! returned. If the app is asking, the current bank is returned.
ResAppNum sys_get_current_resource_num(void);
Version sys_get_current_app_sdk_version(void);
PlatformType sys_get_current_app_sdk_platform(void);
bool sys_get_current_app_is_js_allowed(void);
bool sys_get_current_app_is_rocky_app(void);
void sys_app_log(size_t length, void *log_buffer);
void sys_event_service_client_subscribe(EventServiceInfo *handler);
void sys_event_service_client_unsubscribe(EventServiceInfo *state, EventServiceInfo *handler);
void sys_event_service_cleanup(PebbleEvent *e);
int sys_ble_scan_start(void);
int sys_ble_scan_stop(void);
bool sys_ble_scan_is_scanning(void);
bool sys_ble_consume_scan_results(uint8_t *buffer, uint16_t *size_in_out);
int8_t sys_ble_get_advertising_tx_power(void);
BTErrno sys_ble_central_connect(BTDevice device, bool auto_reconnect, bool is_pairing_required);
BTErrno sys_ble_central_cancel_connect(BTDevice device);
BTErrno sys_ble_client_discover_services_and_characteristics(BTDevice device);
uint8_t sys_ble_client_copy_services(BTDeviceInternal device,
BLEService services[], uint8_t num_services);
uint16_t sys_ble_client_get_maximum_value_length(BTDevice device);
BTErrno sys_ble_client_read(BLECharacteristic characteristic);
bool sys_ble_client_get_notification_value_length(uint16_t *value_length_out);
void sys_ble_client_consume_read(uintptr_t object_ref,
uint8_t value_out[],
uint16_t *value_length_in_out);
bool sys_ble_client_consume_notification(uintptr_t *object_ref_out,
uint8_t value_out[],
uint16_t *value_length_in_out,
bool *has_more_out);
BTErrno sys_ble_client_write(BLECharacteristic characteristic,
const uint8_t *value,
size_t value_length);
BTErrno sys_ble_client_write_without_response(BLECharacteristic characteristic,
const uint8_t *value,
size_t value_length);
BTErrno sys_ble_client_subscribe(BLECharacteristic characteristic,
BLESubscription subscription_type);
BTErrno sys_ble_client_write_descriptor(BLEDescriptor descriptor,
const uint8_t *value,
size_t value_length);
BTErrno sys_ble_client_read_descriptor(BLEDescriptor descriptor);
uint8_t sys_ble_service_get_characteristics(BLEService service,
BLECharacteristic characteristics_out[],
uint8_t num_characteristics);
void sys_ble_service_get_uuid(Uuid *uuid, BLEService service);
void sys_ble_service_get_device(BTDevice *device, BLEService service);
uint8_t sys_ble_service_get_included_services(BLEService service,
BLEService included_services_out[],
uint8_t num_services);
void sys_ble_characteristic_get_uuid(Uuid *uuid, BLECharacteristic characteristic);
BLEAttributeProperty sys_ble_characteristic_get_properties(BLECharacteristic characteristic);
BLEService sys_ble_characteristic_get_service(BLECharacteristic characteristic);
void sys_ble_characteristic_get_device(BTDevice *device, BLECharacteristic characteristic);
uint8_t sys_ble_characteristic_get_descriptors(BLECharacteristic characteristic,
BLEDescriptor descriptors_out[],
uint8_t num_descriptors);
void sys_ble_descriptor_get_uuid(Uuid *uuid, BLEDescriptor descriptor);
BLECharacteristic sys_ble_descriptor_get_characteristic(BLEDescriptor descriptor);
int16_t sys_event_service_get_plugin_service_index(const Uuid * uuid);
DataLoggingSessionRef sys_data_logging_create(uint32_t tag, DataLoggingItemType type,
uint16_t item_size, void *buffer, bool resume);
void sys_data_logging_finish(DataLoggingSessionRef logging_session);
DataLoggingResult sys_data_logging_log(DataLoggingSessionRef logging_session, const void *data, uint32_t num_items);
bool sys_clock_is_24h_style(void);
int sys_strftime(char* s, size_t maxsize, const char* format, const struct tm* tim_p, char *locale);
BatteryChargeState sys_battery_get_charge_state(void);
bool sys_activity_get_metric(ActivityMetric metric, uint32_t history_len, int32_t *history);
bool sys_activity_get_minute_history(HealthMinuteData *minute_data, uint32_t *num_records,
time_t *utc_start);
bool sys_activity_get_step_averages(DayInWeek day_of_week, ActivityMetricAverages *averages);
bool sys_activity_get_sessions(uint32_t *session_entries, ActivitySession *sessions);
bool sys_activity_sessions_is_session_type_ongoing(ActivitySessionType type);
bool sys_activity_prefs_heart_rate_is_enabled(void);
void sys_app_comm_set_responsiveness(SniffInterval interval);
SniffInterval sys_app_comm_get_sniff_interval(void);
void sys_light_enable_interaction(void);
void sys_light_enable(bool enable);
void sys_light_enable_respect_settings(bool enable);
void sys_light_reset_to_timed_mode(void);
bool sys_mobile_app_is_connected_debounced(void);
bool sys_pebblekit_is_connected_debounced(void);
bool sys_app_inbox_service_register(uint8_t *storage, size_t storage_size,
AppInboxMessageHandler message_handler,
AppInboxDroppedHandler dropped_handler);
uint32_t sys_app_inbox_service_unregister(uint8_t *storage);
void sys_app_inbox_service_consume(AppInboxConsumerInfo *consumer_info);
void sys_app_outbox_send(const uint8_t *data, size_t length,
AppOutboxSentHandler sent_handler, void *cb_ctx);
void sys_app_pp_app_message_analytics_count_drop(void);
bool sys_app_pp_send_data(CommSession *session, uint16_t endpoint_id,
const uint8_t* data, uint16_t length);
CommSession * sys_app_pp_get_comm_session(void);
bool sys_app_pp_has_capability(CommSessionCapability capability);
bool sys_system_pp_has_capability(CommSessionCapability capability);
bool sys_app_glance_update(const Uuid *uuid, const AppGlance *glance);
//! Waits for a certain amount of milliseconds
//! @param millis The number of milliseconds to wait for
void sys_psleep(int millis);
void sys_analytics_set(AnalyticsMetric metric, uint64_t value, AnalyticsClient client);
void sys_analytics_set_entire_array(AnalyticsMetric metric, const void *value, AnalyticsClient client);
void sys_analytics_add(AnalyticsMetric metric, uint64_t increment, AnalyticsClient client);
void sys_analytics_inc(AnalyticsMetric metric, AnalyticsClient client);
void sys_analytics_max(AnalyticsMetric metric, int64_t val, AnalyticsClient client);
void sys_analytics_stopwatch_start(AnalyticsMetric metric, AnalyticsClient client);
void sys_analytics_stopwatch_stop(AnalyticsMetric metric);
void sys_analytics_logging_log_event(AnalyticsEventBlob *event_blob);
bool sys_app_worker_is_running(void);
AppWorkerResult sys_app_worker_launch(void);
AppWorkerResult sys_app_worker_kill(void);
void sys_launch_app_for_worker(void);
WakeupId sys_wakeup_schedule(time_t timestamp, int32_t reason, bool notify_if_missed);
void sys_wakeup_delete(WakeupId wakeup_id);
void sys_wakeup_cancel_all_for_app(void);
time_t sys_wakeup_query(WakeupId wakeup_id);
AppLaunchReason sys_process_get_launch_reason(void);
ButtonId sys_process_get_launch_button(void);
uint32_t sys_process_get_launch_args(void);
AppExitReason sys_process_get_exit_reason(void);
void sys_process_set_exit_reason(AppExitReason exit_reason);
void sys_process_get_wakeup_info(WakeupInfo *info);
//! Get the meta-data for the current process
const PebbleProcessMd* sys_process_manager_get_current_process_md(void);
//! Copy UUID for the current process.
//! @return True if the UUID was succesfully copied.
bool sys_process_manager_get_current_process_uuid(Uuid *uuid_out);
//! Get the AppInstallId for the current process
AppInstallId sys_process_manager_get_current_process_id(void);
uint32_t sys_stack_free_bytes(void);
void sys_i18n_get_locale(char *buf);
void sys_i18n_get_with_buffer(const char *string, char *buffer, size_t length);
size_t sys_i18n_get_length(const char *string);
//! @addtogroup Foundation
//! @{
//! @addtogroup WallTime
//! @{
//! If timezone is set, copies the current timezone long name (e.g. America/Chicago)
//! to user-provided buffer.
//! @param timezone A pointer to the buffer to copy the timezone long name into
//! @param buffer_size Size of the allocated buffer to copy the timezone long name into
//! @note timezone buffer should be at least TIMEZONE_NAME_LENGTH bytes
void sys_clock_get_timezone(char *timezone, const size_t buffer_size);
//! @} // end addtogroup WallTime
//! @} // end addtogroup Foundation
//! @addtogroup Foundation
//! @{
//! @addtogroup WatchInfo
//! @{
//! Provides the color of the watch.
//! @return {@link WatchInfoColor} representing the color of the watch.
WatchInfoColor sys_watch_info_get_color(void);
//! @} // end addtogroup WatchInfo
//! @} // end addtogroup Foundation
//! @addtogroup Preferences
//! Users can toggle Quiet Time manually or on schedule. Watchfaces and apps should respect this
//! choice and avoid disturbing actions such as vibration if quiet time is active.
//! @return True, if Quiet Time is currently active.
bool sys_do_not_disturb_is_active(void);
//! @} // end addtogroup Preferences

View File

@@ -0,0 +1,181 @@
/*
* 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 "syscall_internal.h"
#include "applib/app_logging.h"
#include "kernel/memory_layout.h"
#include "mcu/privilege.h"
#include "process_management/process_loader.h"
#include "process_management/process_manager.h"
#include "syscall/syscall.h"
#include "system/logging.h"
#include "system/passert.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdint.h>
#include <inttypes.h>
// Indices into FreeRTOS thread local storage
#define TLS_SYSCALL_LR_IDX 0
#define TLS_SYSCALL_SP_IDX 1
// Helper functions to access FreeRTOS TLS
static uintptr_t prv_get_syscall_sp(void) {
return (uintptr_t)pvTaskGetThreadLocalStoragePointer(NULL, TLS_SYSCALL_SP_IDX);
}
static void prv_set_syscall_sp(uintptr_t new_sp) {
vTaskSetThreadLocalStoragePointer(NULL, TLS_SYSCALL_SP_IDX, (void *)new_sp);
}
USED uintptr_t get_syscall_lr(void) {
return (uintptr_t)pvTaskGetThreadLocalStoragePointer(NULL, TLS_SYSCALL_LR_IDX);
}
static void prv_set_syscall_lr(uintptr_t new_lr) {
vTaskSetThreadLocalStoragePointer(NULL, TLS_SYSCALL_LR_IDX, (void *)new_lr);
}
NORETURN syscall_failed(void) {
register uint32_t lr __asm("lr");
uint32_t saved_lr = lr;
PBL_ASSERT(mcu_state_is_privileged(), "Insufficient Privileges!");
PBL_LOG(LOG_LEVEL_WARNING, "Bad syscall!");
sys_app_fault(saved_lr);
// sys_die is no return, but it's a syscall so I don't want to mark it with that attribute
while(1) { }
}
void syscall_assert_userspace_buffer(const void* buf, size_t num_bytes) {
PebbleTask task = pebble_task_get_current();
void *user_stack_end = (void *)prv_get_syscall_sp();
if (process_manager_is_address_in_region(task, buf, user_stack_end)
&& process_manager_is_address_in_region(
task, (uint8_t *)buf + num_bytes -1, user_stack_end)) {
return;
}
APP_LOG(APP_LOG_LEVEL_ERROR, "syscall failure! %p..%p is not in app space.", buf, (char *)buf + num_bytes);
PBL_LOG(LOG_LEVEL_ERROR, "syscall failure! %p..%p is not in app space.", buf, (char *)buf + num_bytes);
syscall_failed();
}
// Drop privileges and return to the address stored in thread local storage
// Has to preserve r0 and r1 so the syscall's return value is passed through
EXTERNALLY_VISIBLE void NAKED_FUNC USED prv_drop_privilege(void) {
__asm volatile (
" push {r0, r1} \n"
" bl process_manager_handle_syscall_exit \n"
" bl get_syscall_lr \n"
" push { r0 } \n" // push the correct lr onto the stack
" mov r0, #0 \n" // mcu_state_set_thread_privilege(false)
" bl mcu_state_set_thread_privilege \n"
" pop {lr} \n" // Pop correct return address
" pop {r0, r1} \n" // Restore the return values of the syscall
" bx lr \n" // Leave the syscall
);
}
// Just jump straight into the drop privilege code
EXTERNALLY_VISIBLE void NAKED_FUNC USED prv_drop_privilege_wrapper(void) {
__asm volatile("b prv_drop_privilege\n");
}
// This function needs to preserve the argument registers and stack exactly as they were on
// entry, so the arguments are passed correctly into the syscall. The key purpose of this
// function is to determine whether or not the caller is privileged. If the caller is
// unprivileged, this function returns normally to the syscall wrapper, and svc 2 is
// called elevating privileges. If the caller was already privileged, this function
// returns past the svc 2 instruction so privileges are not elevated.
void NAKED_FUNC USED syscall_internal_maybe_skip_privilege(void) {
__asm volatile (
// Save argument registers
" push {r0-r3, lr} \n"
" bl mcu_state_is_privileged \n"
" cmp r0, #1 \n" // Were we privileged?
" pop {r0-r3, lr} \n" // Restore state
" it eq \n" // If we were privileged, return past the svc function
" addeq lr, #2 \n" // svc 2 is 2 bytes long
// Store our return address in ip, which isn't caller or callee saved
// since the linker can modify it
" mov ip, lr \n"
// Set lr to the wrapper's return address. This saves code space so the
// wrapper doesn't have to do this itself. Also we need to check this value
// here.
" pop {lr} \n"
" push {ip} \n" // Save the wrapper address on the stack
// The following can occur with nested syscalls, when the 2nd syscall is at
// the end of the first. Since PRIVILEGE_WAS_ELEVATED depends on the return
// address of the function being equal to syscall_internal_drop_privilege,
// changing to the wrapper prevents a false positive in the nested syscall
// if lr == syscall_internal_drop_privilege,
// lr = syscall_internal_drop_privilege_wrapper
" ldr ip, =prv_drop_privilege \n"
" cmp lr, ip \n"
" it eq \n"
" ldreq lr, =prv_drop_privilege_wrapper \n"
" pop {pc} \n" // Return to the wrapper
);
}
// This is more space efficient than inlining the equality
// expression into every syscall since the address literal
// only needs to be stored at the end of this one function
bool syscall_internal_check_return_address(void * ret_addr) {
return ret_addr == &prv_drop_privilege;
}
// This function is called by the SVC handler with the pre-syscall
// stack pointer, and a pointer to the saved LR on the stack.
// It then stores the SP and LR in thread local storage,
// and updates the saved LR to point at the drop privilege code.
void vSetupSyscallRegisters(uintptr_t orig_sp, uintptr_t *lr_ptr) {
// These calls should be safe because the scheduler needs to call the svc handler
// before the current task is changed. Since this function is called from the svc
// handler, modifying the current task will always finish before a context switch
// Save the correct return address so the drop privilege code knows where to
// return to.
prv_set_syscall_lr(*lr_ptr);
// Save the value of the SP before entry into the syscall so
// syscall_assert_userspace_buffer can ensure that a user provided buffer doesn't
// point into the syscall's stack frame, and that the syscall has enough space
prv_set_syscall_sp(orig_sp);
// Set the return address of the syscall to be the drop privilege code
*lr_ptr = (uintptr_t)&prv_drop_privilege;
}

View File

@@ -0,0 +1,71 @@
/*
* 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 <stdbool.h>
#include <stdint.h>
#include <stddef.h>
//! Any function defined with this macro will be privileged.
//! Privileges are raised upon entry to the syscall, and dropped
//! once the syscall is exited (unless the caller was originally privileged).
#define DEFINE_SYSCALL(retType, funcName, ...) \
retType NAKED_FUNC SECTION(".syscall_text." #funcName) funcName(__VA_ARGS__) { \
__asm volatile (\
"push { lr } \n" \
"bl syscall_internal_maybe_skip_privilege \n" \
"svc 2 \n" \
"b __" #funcName "\n" \
);\
}\
EXTERNALLY_VISIBLE retType USED __##funcName(__VA_ARGS__)
//! Useful function for checking syscall privileges.
//! @return True if the most recent syscall originated from userspace, resulting in a privilege escalation.
//! It can only be called from a function created with DEFINE_SYSCALL
#define PRIVILEGE_WAS_ELEVATED (syscall_internal_check_return_address(__builtin_return_address(0)))
//! Check if ret_addr points at the drop_privilege code
bool syscall_internal_check_return_address(void * ret_addr);
//! Call this from privileged mode whenever a syscall did something wrong. This will kick out the misbehaving app.
NORETURN syscall_failed(void);
//! Call this from privileged mode when entering a syscall to ensure that provided
//! pointers are in the app's memory space, rather than in the kernel. If the buffer is not,
//! syscall_failed is called.
void syscall_assert_userspace_buffer(const void* buf, size_t num_bytes);
// Used to implement DEFINE_SYSCALL
void syscall_internal_maybe_skip_privilege(void);
// Test overrides.
// TODO: really implement privilege escalation in unit tests. See PBL-9688
#if defined(UNITTEST)
# undef DEFINE_SYSCALL
# define DEFINE_SYSCALL(retType, funcName, ...) \
retType funcName(__VA_ARGS__)
# if !UNITTEST_WITH_SYSCALL_PRIVILEGES
# undef PRIVILEGE_WAS_ELEVATED
# define PRIVILEGE_WAS_ELEVATED (0)
# endif
#endif