/* * 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); }