Serenity Operating System
1/*
2 * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/StringView.h>
8#include <Kernel/API/Ioctl.h>
9#include <Kernel/Debug.h>
10#include <Kernel/Devices/DeviceManagement.h>
11#include <Kernel/FileSystem/OpenFileDescription.h>
12#include <Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/BlockDevicesDirectory.h>
13#include <Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/SymbolicLinkDeviceComponent.h>
14#include <Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/DeviceDirectory.h>
15#include <Kernel/FileSystem/SysFS/Subsystems/Devices/Storage/Directory.h>
16#include <Kernel/Storage/StorageDevice.h>
17#include <Kernel/Storage/StorageManagement.h>
18
19namespace Kernel {
20
21StorageDevice::StorageDevice(LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, size_t sector_size, u64 max_addressable_block)
22 : BlockDevice(StorageManagement::storage_type_major_number(), StorageManagement::generate_storage_minor_number(), sector_size)
23 , m_logical_unit_number_address(logical_unit_number_address)
24 , m_hardware_relative_controller_id(hardware_relative_controller_id)
25 , m_max_addressable_block(max_addressable_block)
26 , m_blocks_per_page(PAGE_SIZE / block_size())
27{
28}
29
30StorageDevice::StorageDevice(Badge<RamdiskDevice>, LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, MajorNumber major, MinorNumber minor, size_t sector_size, u64 max_addressable_block)
31 : BlockDevice(major, minor, sector_size)
32 , m_logical_unit_number_address(logical_unit_number_address)
33 , m_hardware_relative_controller_id(hardware_relative_controller_id)
34 , m_max_addressable_block(max_addressable_block)
35 , m_blocks_per_page(PAGE_SIZE / block_size())
36{
37}
38
39ErrorOr<void> StorageDevice::after_inserting()
40{
41 after_inserting_add_to_device_management();
42 auto sysfs_storage_device_directory = StorageDeviceSysFSDirectory::create(SysFSStorageDirectory::the(), *this);
43 m_sysfs_device_directory = sysfs_storage_device_directory;
44 SysFSStorageDirectory::the().plug({}, *sysfs_storage_device_directory);
45 VERIFY(!m_symlink_sysfs_component);
46 auto sys_fs_component = TRY(SysFSSymbolicLinkDeviceComponent::try_create(SysFSBlockDevicesDirectory::the(), *this, *m_sysfs_device_directory));
47 m_symlink_sysfs_component = sys_fs_component;
48 after_inserting_add_symlink_to_device_identifier_directory();
49 return {};
50}
51
52void StorageDevice::will_be_destroyed()
53{
54 // NOTE: We check if m_symlink_sysfs_component is not null, because if we failed
55 // in StorageDevice::after_inserting(), then that method will not set m_symlink_sysfs_component.
56 if (m_symlink_sysfs_component) {
57 before_will_be_destroyed_remove_symlink_from_device_identifier_directory();
58 m_symlink_sysfs_component.clear();
59 }
60 SysFSStorageDirectory::the().unplug({}, *m_sysfs_device_directory);
61 before_will_be_destroyed_remove_from_device_management();
62}
63
64StringView StorageDevice::class_name() const
65{
66 return "StorageDevice"sv;
67}
68
69StringView StorageDevice::command_set_to_string_view() const
70{
71 switch (command_set()) {
72 case CommandSet::PlainMemory:
73 return "memory"sv;
74 case CommandSet::SCSI:
75 return "scsi"sv;
76 case CommandSet::ATA:
77 return "ata"sv;
78 case CommandSet::NVMe:
79 return "nvme"sv;
80 default:
81 break;
82 }
83 VERIFY_NOT_REACHED();
84}
85
86ErrorOr<size_t> StorageDevice::read(OpenFileDescription&, u64 offset, UserOrKernelBuffer& outbuf, size_t len)
87{
88 u64 index = offset >> block_size_log();
89 off_t offset_within_block = 0;
90 size_t whole_blocks = len >> block_size_log();
91 size_t remaining = len - (whole_blocks << block_size_log());
92
93 // PATAChannel will chuck a wobbly if we try to read more than PAGE_SIZE
94 // at a time, because it uses a single page for its DMA buffer.
95 if (whole_blocks >= m_blocks_per_page) {
96 whole_blocks = m_blocks_per_page;
97 remaining = 0;
98 }
99
100 if (len < block_size())
101 offset_within_block = offset - (index << block_size_log());
102
103 dbgln_if(STORAGE_DEVICE_DEBUG, "StorageDevice::read() index={}, whole_blocks={}, remaining={}", index, whole_blocks, remaining);
104
105 if (whole_blocks > 0) {
106 auto read_request = TRY(try_make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read, index, whole_blocks, outbuf, whole_blocks * block_size()));
107 auto result = read_request->wait();
108 if (result.wait_result().was_interrupted())
109 return EINTR;
110 switch (result.request_result()) {
111 case AsyncDeviceRequest::Failure:
112 case AsyncDeviceRequest::Cancelled:
113 return EIO;
114 case AsyncDeviceRequest::MemoryFault:
115 return EFAULT;
116 default:
117 break;
118 }
119 }
120
121 off_t pos = whole_blocks * block_size();
122
123 if (remaining > 0) {
124 auto data = TRY(ByteBuffer::create_uninitialized(block_size()));
125 auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data.data());
126 auto read_request = TRY(try_make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read, index + whole_blocks, 1, data_buffer, block_size()));
127 auto result = read_request->wait();
128 if (result.wait_result().was_interrupted())
129 return EINTR;
130 switch (result.request_result()) {
131 case AsyncDeviceRequest::Failure:
132 return pos;
133 case AsyncDeviceRequest::Cancelled:
134 return EIO;
135 case AsyncDeviceRequest::MemoryFault:
136 // This should never happen, we're writing to a kernel buffer!
137 VERIFY_NOT_REACHED();
138 default:
139 break;
140 }
141 TRY(outbuf.write(data.offset_pointer(offset_within_block), pos, remaining));
142 }
143
144 return pos + remaining;
145}
146
147bool StorageDevice::can_read(OpenFileDescription const&, u64 offset) const
148{
149 return offset < (max_addressable_block() * block_size());
150}
151
152ErrorOr<size_t> StorageDevice::write(OpenFileDescription&, u64 offset, UserOrKernelBuffer const& inbuf, size_t len)
153{
154 u64 index = offset >> block_size_log();
155 off_t offset_within_block = 0;
156 size_t whole_blocks = len >> block_size_log();
157 size_t remaining = len - (whole_blocks << block_size_log());
158
159 // PATAChannel will chuck a wobbly if we try to write more than PAGE_SIZE
160 // at a time, because it uses a single page for its DMA buffer.
161 if (whole_blocks >= m_blocks_per_page) {
162 whole_blocks = m_blocks_per_page;
163 remaining = 0;
164 }
165
166 if (len < block_size())
167 offset_within_block = offset - (index << block_size_log());
168
169 // We try to allocate the temporary block buffer for partial writes *before* we start any full block writes,
170 // to try and prevent partial writes
171 Optional<ByteBuffer> partial_write_block;
172 if (remaining > 0)
173 partial_write_block = TRY(ByteBuffer::create_zeroed(block_size()));
174
175 dbgln_if(STORAGE_DEVICE_DEBUG, "StorageDevice::write() index={}, whole_blocks={}, remaining={}", index, whole_blocks, remaining);
176
177 if (whole_blocks > 0) {
178 auto write_request = TRY(try_make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Write, index, whole_blocks, inbuf, whole_blocks * block_size()));
179 auto result = write_request->wait();
180 if (result.wait_result().was_interrupted())
181 return EINTR;
182 switch (result.request_result()) {
183 case AsyncDeviceRequest::Failure:
184 case AsyncDeviceRequest::Cancelled:
185 return EIO;
186 case AsyncDeviceRequest::MemoryFault:
187 return EFAULT;
188 default:
189 break;
190 }
191 }
192
193 off_t pos = whole_blocks * block_size();
194
195 // since we can only write in block_size() increments, if we want to do a
196 // partial write, we have to read the block's content first, modify it,
197 // then write the whole block back to the disk.
198 if (remaining > 0) {
199 auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(partial_write_block->data());
200 {
201 auto read_request = TRY(try_make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read, index + whole_blocks, 1, data_buffer, block_size()));
202 auto result = read_request->wait();
203 if (result.wait_result().was_interrupted())
204 return EINTR;
205 switch (result.request_result()) {
206 case AsyncDeviceRequest::Failure:
207 return pos;
208 case AsyncDeviceRequest::Cancelled:
209 return EIO;
210 case AsyncDeviceRequest::MemoryFault:
211 // This should never happen, we're writing to a kernel buffer!
212 VERIFY_NOT_REACHED();
213 default:
214 break;
215 }
216 }
217
218 TRY(inbuf.read(partial_write_block->offset_pointer(offset_within_block), pos, remaining));
219
220 {
221 auto write_request = TRY(try_make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Write, index + whole_blocks, 1, data_buffer, block_size()));
222 auto result = write_request->wait();
223 if (result.wait_result().was_interrupted())
224 return EINTR;
225 switch (result.request_result()) {
226 case AsyncDeviceRequest::Failure:
227 return pos;
228 case AsyncDeviceRequest::Cancelled:
229 return EIO;
230 case AsyncDeviceRequest::MemoryFault:
231 // This should never happen, we're writing to a kernel buffer!
232 VERIFY_NOT_REACHED();
233 default:
234 break;
235 }
236 }
237 }
238
239 return pos + remaining;
240}
241
242bool StorageDevice::can_write(OpenFileDescription const&, u64 offset) const
243{
244 return offset < (max_addressable_block() * block_size());
245}
246
247ErrorOr<void> StorageDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
248{
249 switch (request) {
250 case STORAGE_DEVICE_GET_SIZE: {
251 u64 disk_size = m_max_addressable_block * block_size();
252 return copy_to_user(static_ptr_cast<u64*>(arg), &disk_size);
253 break;
254 }
255 case STORAGE_DEVICE_GET_BLOCK_SIZE: {
256 size_t size = block_size();
257 return copy_to_user(static_ptr_cast<size_t*>(arg), &size);
258 break;
259 }
260 default:
261 return EINVAL;
262 }
263}
264
265}