blob: f58fcf97e40779c2fe3c487dab627f0b675fa7db [file] [log] [blame]
// Copyright Microsoft and CHERIoT Contributors.
// SPDX-License-Identifier: MIT
#include <errno.h>
#include <queue.h>
#include <ring_buffer.hh>
#include <thread.h>
#include <thread_pool.h>
using namespace CHERI;
namespace
{
/**
* Ring buffer. We use a ticket lock to ensure that things are dispatched
* in order (independent of priority) on the sending side and a flag lock on
* the other side to ensure that the highest-priority thread picks up
* messages as soon as they're ready.
*/
RingBuffer<ThreadPoolMessage, 16, TicketLock, FlagLock> queue;
} // namespace
int thread_pool_async(ThreadPoolCallback fn, void *data)
{
Capability<void> fnCap{reinterpret_cast<void *>(fn)};
Capability<void> dataCap{data};
// The function must be sealed with the type used for export table entries
// for us to be able to invoke it. The data capability doesn't *have* to
// be sealed, but it's a bad idea if it is unsealed because it adds the
// thread pool to the TCB for confidentiality and integrity.
// We want to avoid this being able to make us trap and so we validate that
// the function is cross-compartment entry point and both can be stored in
// the message queue.
if (!fnCap.is_valid() || (fnCap.type() != 9) ||
!fnCap.permissions().contains(Permission::Global) ||
(dataCap.is_valid() && !dataCap.is_sealed()) ||
(dataCap.is_valid() &&
!dataCap.permissions().contains(Permission::Global)))
{
return -EINVAL;
}
queue.push({fn, data});
return 0;
}
void __cheri_compartment("thread_pool") thread_pool_run()
{
while (true)
{
ThreadPoolMessage message = queue.pop();
message.invoke(message.data);
}
}