mirror of
https://github.com/google/pebble
synced 2026-10-16 16:24:12 +02:00
133 lines
4.3 KiB
C
133 lines
4.3 KiB
C
/*
|
|
* 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);
|
|
}
|