Serenity Operating System
1/*
2 * Copyright (c) 2020, the SerenityOS developers.
3 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#pragma once
9
10#include <AK/Types.h>
11#include <AK/Userspace.h>
12#include <Kernel/API/POSIX/errno.h>
13#include <Kernel/Memory/MemoryManager.h>
14#include <Kernel/StdLib.h>
15#include <Kernel/UnixTypes.h>
16
17namespace Kernel {
18
19class [[nodiscard]] UserOrKernelBuffer {
20public:
21 UserOrKernelBuffer() = delete;
22
23 static UserOrKernelBuffer for_kernel_buffer(u8* kernel_buffer)
24 {
25 VERIFY(!kernel_buffer || !Memory::is_user_address(VirtualAddress(kernel_buffer)));
26 return UserOrKernelBuffer(kernel_buffer);
27 }
28
29 static ErrorOr<UserOrKernelBuffer> for_user_buffer(u8* user_buffer, size_t size)
30 {
31 if (user_buffer && !Memory::is_user_range(VirtualAddress(user_buffer), size))
32 return Error::from_errno(EFAULT);
33 return UserOrKernelBuffer(user_buffer);
34 }
35
36 template<typename UserspaceType>
37 static ErrorOr<UserOrKernelBuffer> for_user_buffer(UserspaceType userspace, size_t size)
38 {
39 if (!Memory::is_user_range(VirtualAddress(userspace.unsafe_userspace_ptr()), size))
40 return Error::from_errno(EFAULT);
41 return UserOrKernelBuffer(const_cast<u8*>((u8 const*)userspace.unsafe_userspace_ptr()));
42 }
43
44 [[nodiscard]] bool is_kernel_buffer() const;
45 [[nodiscard]] void const* user_or_kernel_ptr() const { return m_buffer; }
46
47 [[nodiscard]] UserOrKernelBuffer offset(size_t offset) const
48 {
49 if (!m_buffer)
50 return *this;
51 UserOrKernelBuffer offset_buffer = *this;
52 offset_buffer.m_buffer += offset;
53 VERIFY(offset_buffer.is_kernel_buffer() == is_kernel_buffer());
54 return offset_buffer;
55 }
56
57 ErrorOr<NonnullOwnPtr<KString>> try_copy_into_kstring(size_t) const;
58 ErrorOr<void> write(void const* src, size_t offset, size_t len);
59 ErrorOr<void> write(void const* src, size_t len)
60 {
61 return write(src, 0, len);
62 }
63 ErrorOr<void> write(ReadonlyBytes bytes)
64 {
65 return write(bytes.data(), bytes.size());
66 }
67
68 ErrorOr<void> read(void* dest, size_t offset, size_t len) const;
69 ErrorOr<void> read(void* dest, size_t len) const
70 {
71 return read(dest, 0, len);
72 }
73
74 ErrorOr<void> read(Bytes bytes) const
75 {
76 return read(bytes.data(), bytes.size());
77 }
78
79 ErrorOr<void> memset(int value, size_t offset, size_t len);
80 ErrorOr<void> memset(int value, size_t len)
81 {
82 return memset(value, 0, len);
83 }
84
85 template<size_t BUFFER_BYTES, typename F>
86 ErrorOr<size_t> write_buffered(size_t offset, size_t len, F f)
87 {
88 if (!m_buffer)
89 return EFAULT;
90 if (is_kernel_buffer()) {
91 // We're transferring directly to a kernel buffer, bypass
92 Bytes bytes { m_buffer + offset, len };
93 return f(bytes);
94 }
95
96 // The purpose of using a buffer on the stack is that we can
97 // avoid a bunch of small (e.g. 1-byte) copy_to_user calls
98 u8 buffer[BUFFER_BYTES];
99 size_t nwritten = 0;
100 while (nwritten < len) {
101 auto to_copy = min(sizeof(buffer), len - nwritten);
102 Bytes bytes { buffer, to_copy };
103 ErrorOr<size_t> copied_or_error = f(bytes);
104 if (copied_or_error.is_error())
105 return copied_or_error.release_error();
106 auto copied = copied_or_error.release_value();
107 VERIFY(copied <= to_copy);
108 TRY(write(buffer, nwritten, copied));
109 nwritten += copied;
110 if (copied < to_copy)
111 break;
112 }
113 return nwritten;
114 }
115 template<size_t BUFFER_BYTES, typename F>
116 ErrorOr<size_t> write_buffered(size_t len, F f)
117 {
118 return write_buffered<BUFFER_BYTES, F>(0, len, f);
119 }
120
121 template<size_t BUFFER_BYTES, typename F>
122 ErrorOr<size_t> read_buffered(size_t offset, size_t len, F f) const
123 {
124 if (!m_buffer)
125 return EFAULT;
126 if (is_kernel_buffer()) {
127 // We're transferring directly from a kernel buffer, bypass
128 return f({ m_buffer + offset, len });
129 }
130
131 // The purpose of using a buffer on the stack is that we can
132 // avoid a bunch of small (e.g. 1-byte) copy_from_user calls
133 u8 buffer[BUFFER_BYTES];
134 size_t nread = 0;
135 while (nread < len) {
136 auto to_copy = min(sizeof(buffer), len - nread);
137 TRY(read(buffer, nread, to_copy));
138 ReadonlyBytes read_only_bytes { buffer, to_copy };
139 ErrorOr<size_t> copied_or_error = f(read_only_bytes);
140 if (copied_or_error.is_error())
141 return copied_or_error.release_error();
142 auto copied = copied_or_error.release_value();
143 VERIFY(copied <= to_copy);
144 nread += copied;
145 if (copied < to_copy)
146 break;
147 }
148 return nread;
149 }
150 template<size_t BUFFER_BYTES, typename F>
151 ErrorOr<size_t> read_buffered(size_t len, F f) const
152 {
153 return read_buffered<BUFFER_BYTES, F>(0, len, f);
154 }
155
156private:
157 explicit UserOrKernelBuffer(u8* buffer)
158 : m_buffer(buffer)
159 {
160 }
161
162 u8* m_buffer;
163};
164
165}