Serenity Operating System
1/*
2 * Copyright (c) 2021, Patrick Meyer <git@the-space.agency>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Assertions.h>
8#include <AK/NonnullOwnPtr.h>
9#include <Kernel/API/Ioctl.h>
10#include <Kernel/Devices/DeviceManagement.h>
11#include <Kernel/Devices/KCOVDevice.h>
12#include <Kernel/Devices/KCOVInstance.h>
13#include <Kernel/FileSystem/OpenFileDescription.h>
14
15#include <Kernel/Panic.h>
16
17namespace Kernel {
18
19HashMap<ProcessID, KCOVInstance*>* KCOVDevice::proc_instance;
20HashMap<ThreadID, KCOVInstance*>* KCOVDevice::thread_instance;
21
22UNMAP_AFTER_INIT NonnullLockRefPtr<KCOVDevice> KCOVDevice::must_create()
23{
24 auto kcov_device_or_error = DeviceManagement::try_create_device<KCOVDevice>();
25 // FIXME: Find a way to propagate errors
26 VERIFY(!kcov_device_or_error.is_error());
27 return kcov_device_or_error.release_value();
28}
29
30UNMAP_AFTER_INIT KCOVDevice::KCOVDevice()
31 : CharacterDevice(30, 0)
32{
33 proc_instance = new HashMap<ProcessID, KCOVInstance*>();
34 thread_instance = new HashMap<ThreadID, KCOVInstance*>();
35 dbgln("KCOVDevice created");
36}
37
38void KCOVDevice::free_thread()
39{
40 auto thread = Thread::current();
41 auto tid = thread->tid();
42
43 auto maybe_kcov_instance = thread_instance->get(tid);
44 if (!maybe_kcov_instance.has_value())
45 return;
46
47 auto kcov_instance = maybe_kcov_instance.value();
48 VERIFY(kcov_instance->state() == KCOVInstance::TRACING);
49 kcov_instance->set_state(KCOVInstance::OPENED);
50 thread_instance->remove(tid);
51}
52
53void KCOVDevice::free_process()
54{
55 auto pid = Process::current().pid();
56
57 auto maybe_kcov_instance = proc_instance->get(pid);
58 if (!maybe_kcov_instance.has_value())
59 return;
60
61 auto kcov_instance = maybe_kcov_instance.value();
62 VERIFY(kcov_instance->state() == KCOVInstance::OPENED);
63 kcov_instance->set_state(KCOVInstance::UNUSED);
64 proc_instance->remove(pid);
65 delete kcov_instance;
66}
67
68ErrorOr<NonnullRefPtr<OpenFileDescription>> KCOVDevice::open(int options)
69{
70 auto pid = Process::current().pid();
71 if (proc_instance->get(pid).has_value())
72 return EBUSY; // This process already open()ed the kcov device
73 auto kcov_instance = new KCOVInstance(pid);
74 kcov_instance->set_state(KCOVInstance::OPENED);
75 proc_instance->set(pid, kcov_instance);
76
77 return Device::open(options);
78}
79
80ErrorOr<void> KCOVDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
81{
82 auto thread = Thread::current();
83 auto tid = thread->tid();
84 auto pid = thread->pid();
85 auto maybe_kcov_instance = proc_instance->get(pid);
86 if (!maybe_kcov_instance.has_value())
87 return ENXIO; // This proc hasn't opened the kcov dev yet
88 auto kcov_instance = maybe_kcov_instance.value();
89
90 SpinlockLocker locker(kcov_instance->spinlock());
91 switch (request) {
92 case KCOV_SETBUFSIZE:
93 if (kcov_instance->state() >= KCOVInstance::TRACING)
94 return EBUSY;
95 return kcov_instance->buffer_allocate((FlatPtr)arg.unsafe_userspace_ptr());
96 case KCOV_ENABLE:
97 if (kcov_instance->state() >= KCOVInstance::TRACING)
98 return EBUSY;
99 if (!kcov_instance->has_buffer())
100 return ENOBUFS;
101 VERIFY(kcov_instance->state() == KCOVInstance::OPENED);
102 kcov_instance->set_state(KCOVInstance::TRACING);
103 thread_instance->set(tid, kcov_instance);
104 return {};
105 case KCOV_DISABLE: {
106 auto maybe_kcov_instance = thread_instance->get(tid);
107 if (!maybe_kcov_instance.has_value())
108 return ENOENT;
109 VERIFY(kcov_instance->state() == KCOVInstance::TRACING);
110 kcov_instance->set_state(KCOVInstance::OPENED);
111 thread_instance->remove(tid);
112 return {};
113 }
114 default:
115 return EINVAL;
116 }
117}
118
119ErrorOr<NonnullLockRefPtr<Memory::VMObject>> KCOVDevice::vmobject_for_mmap(Process& process, Memory::VirtualRange const&, u64&, bool)
120{
121 auto pid = process.pid();
122 auto maybe_kcov_instance = proc_instance->get(pid);
123 VERIFY(maybe_kcov_instance.has_value()); // Should happen on fd open()
124 auto kcov_instance = maybe_kcov_instance.value();
125
126 if (!kcov_instance->vmobject())
127 return ENOBUFS; // mmaped, before KCOV_SETBUFSIZE
128
129 return *kcov_instance->vmobject();
130}
131
132}