Serenity Operating System
1/*
2 * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <Kernel/Arch/CPU.h>
8#include <Kernel/Arch/Delay.h>
9#include <Kernel/Storage/ATA/ATADiskDevice.h>
10#include <Kernel/Storage/ATA/ATAPort.h>
11#include <Kernel/Storage/ATA/Definitions.h>
12#include <Kernel/WorkQueue.h>
13
14namespace Kernel {
15
16class ATAPortInterruptDisabler {
17public:
18 ATAPortInterruptDisabler(ATAPort& port)
19 : m_port(port)
20 {
21 (void)port.disable_interrupts();
22 }
23
24 ~ATAPortInterruptDisabler()
25 {
26 (void)m_port->enable_interrupts();
27 };
28
29private:
30 LockRefPtr<ATAPort> m_port;
31};
32
33class ATAPortInterruptCleaner {
34public:
35 ATAPortInterruptCleaner(ATAPort& port)
36 : m_port(port)
37 {
38 }
39
40 ~ATAPortInterruptCleaner()
41 {
42 (void)m_port->force_clear_interrupts();
43 };
44
45private:
46 LockRefPtr<ATAPort> m_port;
47};
48
49void ATAPort::fix_name_string_in_identify_device_block()
50{
51 VERIFY(m_lock.is_locked());
52 auto* wbuf = (u16*)m_ata_identify_data_buffer->data();
53 auto* bbuf = m_ata_identify_data_buffer->data() + 27 * 2;
54 for (size_t word_index = 27; word_index < 47; word_index++) {
55 u16 data = wbuf[word_index];
56 *(bbuf++) = MSB(data);
57 *(bbuf++) = LSB(data);
58 }
59}
60
61ErrorOr<void> ATAPort::detect_connected_devices()
62{
63 MutexLocker locker(m_lock);
64 for (size_t device_index = 0; device_index < max_possible_devices_connected(); device_index++) {
65 TRY(device_select(device_index));
66 auto device_presence = TRY(detect_presence_on_selected_device());
67 if (!device_presence)
68 continue;
69
70 TaskFile identify_taskfile;
71 memset(&identify_taskfile, 0, sizeof(TaskFile));
72 identify_taskfile.command = ATA_CMD_IDENTIFY;
73 auto buffer = UserOrKernelBuffer::for_kernel_buffer(m_ata_identify_data_buffer->data());
74 {
75 auto result = execute_polled_command(TransactionDirection::Read, LBAMode::None, identify_taskfile, buffer, 0, 256, 100, 100);
76 if (result.is_error()) {
77 continue;
78 }
79 }
80 ATAIdentifyBlock volatile& identify_block = (ATAIdentifyBlock volatile&)(*m_ata_identify_data_buffer->data());
81 u16 capabilities = identify_block.capabilities[0];
82
83 StringView device_name = StringView((char const*)const_cast<u16*>(identify_block.model_number), 40);
84 fix_name_string_in_identify_device_block();
85
86 u64 max_addressable_block = identify_block.max_28_bit_addressable_logical_sector;
87 dbgln("ATAPort: device found: Name={}, Capacity={}, Capabilities={:#04x}", device_name.trim_whitespace(), max_addressable_block * 512, capabilities);
88 // If the drive is so old that it doesn't support LBA, ignore it.
89 if (!(capabilities & ATA_CAP_LBA)) {
90 dbgln("ATAPort: device found but without LBA support (what kind of dinosaur we see here?)");
91 continue;
92 }
93 // if we support 48-bit LBA, use that value instead.
94 if (identify_block.commands_and_feature_sets_supported[1] & (1 << 10))
95 max_addressable_block = identify_block.user_addressable_logical_sectors_count;
96 // FIXME: Don't assume all drives will have logical sector size of 512 bytes.
97 ATADevice::Address address = { m_port_index, static_cast<u8>(device_index) };
98 m_ata_devices.append(ATADiskDevice::create(m_parent_ata_controller, address, capabilities, 512, max_addressable_block));
99 }
100 return {};
101}
102
103LockRefPtr<StorageDevice> ATAPort::connected_device(size_t device_index) const
104{
105 MutexLocker locker(m_lock);
106 if (m_ata_devices.size() > device_index)
107 return m_ata_devices[device_index];
108 return {};
109}
110
111ErrorOr<void> ATAPort::start_request(ATADevice const& associated_device, AsyncBlockDeviceRequest& request)
112{
113 MutexLocker locker(m_lock);
114 VERIFY(m_current_request.is_null());
115 VERIFY(pio_capable() || dma_capable());
116
117 dbgln_if(ATA_DEBUG, "ATAPort::start_request");
118
119 m_current_request = request;
120 m_current_request_block_index = 0;
121 m_current_request_flushing_cache = false;
122
123 if (dma_capable()) {
124 TRY(prepare_and_initiate_dma_transaction(associated_device));
125 return {};
126 }
127 TRY(prepare_and_initiate_pio_transaction(associated_device));
128 return {};
129}
130
131void ATAPort::complete_pio_transaction(AsyncDeviceRequest::RequestResult result)
132{
133 VERIFY(m_current_request);
134
135 // Now schedule reading back the buffer as soon as we leave the irq handler.
136 // This is important so that we can safely write the buffer back,
137 // which could cause page faults. Note that this may be called immediately
138 // before Processor::deferred_call_queue returns!
139 auto work_item_creation_result = g_io_work->try_queue([this, result]() {
140 dbgln_if(ATA_DEBUG, "ATAPort::complete_pio_transaction result: {}", (int)result);
141 MutexLocker locker(m_lock);
142 VERIFY(m_current_request);
143 auto current_request = m_current_request;
144 m_current_request.clear();
145 current_request->complete(result);
146 });
147 if (work_item_creation_result.is_error()) {
148 auto current_request = m_current_request;
149 m_current_request.clear();
150 current_request->complete(AsyncDeviceRequest::OutOfMemory);
151 }
152}
153
154void ATAPort::complete_dma_transaction(AsyncDeviceRequest::RequestResult result)
155{
156 // NOTE: this may be called from the interrupt handler!
157 VERIFY(m_current_request);
158 VERIFY(m_lock.is_locked());
159
160 // Now schedule reading back the buffer as soon as we leave the irq handler.
161 // This is important so that we can safely write the buffer back,
162 // which could cause page faults. Note that this may be called immediately
163 // before Processor::deferred_call_queue returns!
164 auto work_item_creation_result = g_io_work->try_queue([this, result]() {
165 dbgln_if(ATA_DEBUG, "ATAPort::complete_dma_transaction result: {}", (int)result);
166 MutexLocker locker(m_lock);
167 if (!m_current_request)
168 return;
169 auto current_request = m_current_request;
170 m_current_request.clear();
171
172 if (result == AsyncDeviceRequest::Success) {
173 {
174 auto result = force_busmastering_status_clean();
175 if (result.is_error()) {
176 locker.unlock();
177 current_request->complete(AsyncDeviceRequest::Failure);
178 return;
179 }
180 }
181
182 if (current_request->request_type() == AsyncBlockDeviceRequest::Read) {
183 if (auto result = current_request->write_to_buffer(current_request->buffer(), m_dma_buffer_region->vaddr().as_ptr(), 512 * current_request->block_count()); result.is_error()) {
184 locker.unlock();
185 current_request->complete(AsyncDeviceRequest::MemoryFault);
186 return;
187 }
188 }
189 }
190 locker.unlock();
191 current_request->complete(result);
192 });
193 if (work_item_creation_result.is_error()) {
194 auto current_request = m_current_request;
195 m_current_request.clear();
196 current_request->complete(AsyncDeviceRequest::OutOfMemory);
197 }
198}
199
200static void print_ata_status(u8 status)
201{
202 dbgln("ATAPort: print_status: DRQ={} BSY={}, DRDY={}, DSC={}, DF={}, CORR={}, IDX={}, ERR={}",
203 (status & ATA_SR_DRQ) != 0,
204 (status & ATA_SR_BSY) != 0,
205 (status & ATA_SR_DRDY) != 0,
206 (status & ATA_SR_DSC) != 0,
207 (status & ATA_SR_DF) != 0,
208 (status & ATA_SR_CORR) != 0,
209 (status & ATA_SR_IDX) != 0,
210 (status & ATA_SR_ERR) != 0);
211}
212
213static void try_disambiguate_ata_error(u8 error)
214{
215 dbgln("ATAPort: Error cause:");
216
217 switch (error) {
218 case ATA_ER_BBK:
219 dbgln("ATAPort: - Bad block");
220 break;
221 case ATA_ER_UNC:
222 dbgln("ATAPort: - Uncorrectable data");
223 break;
224 case ATA_ER_MC:
225 dbgln("ATAPort: - Media changed");
226 break;
227 case ATA_ER_IDNF:
228 dbgln("ATAPort: - ID mark not found");
229 break;
230 case ATA_ER_MCR:
231 dbgln("ATAPort: - Media change request");
232 break;
233 case ATA_ER_ABRT:
234 dbgln("ATAPort: - Command aborted");
235 break;
236 case ATA_ER_TK0NF:
237 dbgln("ATAPort: - Track 0 not found");
238 break;
239 case ATA_ER_AMNF:
240 dbgln("ATAPort: - No address mark");
241 break;
242 default:
243 dbgln("ATAPort: - No one knows");
244 break;
245 }
246}
247
248ErrorOr<bool> ATAPort::handle_interrupt_after_dma_transaction()
249{
250 if (!dma_capable())
251 return false;
252 u8 bstatus = TRY(busmastering_status());
253 if (!(bstatus & 0x4)) {
254 // interrupt not from this device, ignore
255 dbgln_if(ATA_DEBUG, "ATAPort: ignore interrupt");
256 return false;
257 }
258 auto work_item_creation_result = g_ata_work->try_queue([this]() -> void {
259 MutexLocker locker(m_lock);
260 u8 status = task_file_status().release_value();
261
262 m_entropy_source.add_random_event(status);
263 // clear bus master interrupt status
264 {
265 auto result = force_busmastering_status_clean();
266 if (result.is_error()) {
267 complete_dma_transaction(AsyncDeviceRequest::Failure);
268 return;
269 }
270 }
271
272 SpinlockLocker lock(m_hard_lock);
273 dbgln_if(ATA_DEBUG, "ATAPort: interrupt: DRQ={}, BSY={}, DRDY={}",
274 (status & ATA_SR_DRQ) != 0,
275 (status & ATA_SR_BSY) != 0,
276 (status & ATA_SR_DRDY) != 0);
277
278 if (!m_current_request) {
279 dbgln("ATAPort: IRQ but no pending request!");
280 return;
281 }
282
283 if (status & ATA_SR_ERR) {
284 print_ata_status(status);
285 auto device_error = task_file_error().release_value();
286 dbgln("ATAPort: Error {:#02x}!", (u8)device_error);
287 try_disambiguate_ata_error(device_error);
288 complete_dma_transaction(AsyncDeviceRequest::Failure);
289 return;
290 }
291 complete_dma_transaction(AsyncDeviceRequest::Success);
292 return;
293 });
294 if (work_item_creation_result.is_error()) {
295 auto current_request = m_current_request;
296 m_current_request.clear();
297 current_request->complete(AsyncDeviceRequest::OutOfMemory);
298 return Error::from_errno(ENOMEM);
299 }
300 return true;
301}
302
303ErrorOr<void> ATAPort::prepare_and_initiate_dma_transaction(ATADevice const& associated_device)
304{
305 VERIFY(m_lock.is_locked());
306 VERIFY(!m_current_request.is_null());
307 VERIFY(m_current_request->block_count() <= 256);
308
309 // Note: We might be called here from an interrupt handler (like the page fault handler), so queue a read afterwards.
310 auto work_item_creation_result = g_ata_work->try_queue([this, &associated_device]() -> void {
311 MutexLocker locker(m_lock);
312 dbgln_if(ATA_DEBUG, "ATAPort::prepare_and_initiate_dma_transaction ({} x {})", m_current_request->block_index(), m_current_request->block_count());
313
314 VERIFY(!m_current_request.is_null());
315 VERIFY(m_current_request->block_count() <= 256);
316 {
317 auto result = device_select(associated_device.ata_address().subport);
318 if (result.is_error()) {
319 complete_dma_transaction(AsyncDeviceRequest::Failure);
320 return;
321 }
322 }
323
324 if (m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write) {
325 if (auto result = m_current_request->read_from_buffer(m_current_request->buffer(), m_dma_buffer_region->vaddr().as_ptr(), 512 * m_current_request->block_count()); result.is_error()) {
326 complete_dma_transaction(AsyncDeviceRequest::MemoryFault);
327 return;
328 }
329 }
330
331 prdt().offset = m_dma_buffer_page->paddr().get();
332 prdt().size = 512 * m_current_request->block_count();
333
334 VERIFY(prdt().size <= PAGE_SIZE);
335
336 SpinlockLocker hard_lock_locker(m_hard_lock);
337
338 {
339 auto result = stop_busmastering();
340 if (result.is_error()) {
341 complete_dma_transaction(AsyncDeviceRequest::Failure);
342 return;
343 }
344 }
345
346 if (m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write) {
347 auto result = prepare_transaction_with_busmastering(TransactionDirection::Write, m_prdt_page->paddr());
348 if (result.is_error()) {
349 complete_dma_transaction(AsyncDeviceRequest::Failure);
350 return;
351 }
352 } else {
353 auto result = prepare_transaction_with_busmastering(TransactionDirection::Read, m_prdt_page->paddr());
354 if (result.is_error()) {
355 complete_dma_transaction(AsyncDeviceRequest::Failure);
356 return;
357 }
358 }
359
360 TaskFile taskfile;
361 LBAMode lba_mode = LBAMode::TwentyEightBit;
362 auto lba = m_current_request->block_index();
363 if ((lba + m_current_request->block_count()) >= 0x10000000) {
364 lba_mode = LBAMode::FortyEightBit;
365 }
366 memset(&taskfile, 0, sizeof(TaskFile));
367 taskfile.lba_low[0] = (lba & 0x000000FF) >> 0;
368 taskfile.lba_low[1] = (lba & 0x0000FF00) >> 8;
369 taskfile.lba_low[2] = (lba & 0x00FF0000) >> 16;
370 taskfile.lba_high[0] = (lba & 0xFF000000) >> 24;
371 taskfile.lba_high[1] = (lba & 0xFF00000000ull) >> 32;
372 taskfile.lba_high[2] = (lba & 0xFF0000000000ull) >> 40;
373 taskfile.count = m_current_request->block_count();
374 if (lba_mode == LBAMode::TwentyEightBit)
375 taskfile.command = m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write ? ATA_CMD_WRITE_DMA : ATA_CMD_READ_DMA;
376 else
377 taskfile.command = m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write ? ATA_CMD_WRITE_DMA_EXT : ATA_CMD_READ_DMA_EXT;
378
379 {
380 auto result = load_taskfile_into_registers(taskfile, lba_mode, 1000);
381 if (result.is_error()) {
382 complete_dma_transaction(AsyncDeviceRequest::Failure);
383 return;
384 }
385 }
386
387 if (m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write) {
388 auto result = start_busmastering(TransactionDirection::Write);
389 if (result.is_error()) {
390 complete_dma_transaction(AsyncDeviceRequest::Failure);
391 return;
392 }
393 }
394
395 else {
396 auto result = start_busmastering(TransactionDirection::Read);
397 if (result.is_error()) {
398 complete_dma_transaction(AsyncDeviceRequest::Failure);
399 return;
400 }
401 }
402 });
403 if (work_item_creation_result.is_error()) {
404 auto current_request = m_current_request;
405 m_current_request.clear();
406 current_request->complete(AsyncDeviceRequest::OutOfMemory);
407 return Error::from_errno(ENOMEM);
408 }
409 return {};
410}
411
412ErrorOr<void> ATAPort::prepare_and_initiate_pio_transaction(ATADevice const& associated_device)
413{
414 VERIFY(m_lock.is_locked());
415 VERIFY(!m_current_request.is_null());
416 VERIFY(m_current_request->block_count() <= 256);
417 dbgln_if(ATA_DEBUG, "ATAPort::prepare_and_initiate_pio_transaction ({} x {})", m_current_request->block_index(), m_current_request->block_count());
418 // Note: We might be called here from an interrupt handler (like the page fault handler), so queue a read afterwards.
419 auto work_item_creation_result = g_ata_work->try_queue([this, &associated_device]() -> void {
420 MutexLocker locker(m_lock);
421 {
422 auto result = device_select(associated_device.ata_address().subport);
423 if (result.is_error()) {
424 complete_pio_transaction(AsyncDeviceRequest::Failure);
425 return;
426 }
427 }
428 for (size_t block_index = 0; block_index < m_current_request->block_count(); block_index++) {
429 TaskFile taskfile;
430 LBAMode lba_mode = LBAMode::TwentyEightBit;
431 auto lba = m_current_request->block_index() + block_index;
432 if (lba >= 0x10000000) {
433 lba_mode = LBAMode::FortyEightBit;
434 }
435 memset(&taskfile, 0, sizeof(TaskFile));
436 taskfile.lba_low[0] = (lba & 0x000000FF) >> 0;
437 taskfile.lba_low[1] = (lba & 0x0000FF00) >> 8;
438 taskfile.lba_low[2] = (lba & 0x00FF0000) >> 16;
439 taskfile.lba_high[0] = (lba & 0xFF000000) >> 24;
440 taskfile.lba_high[1] = (lba & 0xFF00000000ull) >> 32;
441 taskfile.lba_high[2] = (lba & 0xFF0000000000ull) >> 40;
442 taskfile.count = 1;
443 if (lba_mode == LBAMode::TwentyEightBit)
444 taskfile.command = m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write ? ATA_CMD_WRITE_PIO : ATA_CMD_READ_PIO;
445 else
446 taskfile.command = m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write ? ATA_CMD_WRITE_PIO_EXT : ATA_CMD_READ_PIO_EXT;
447
448 if (m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Read) {
449 auto result = execute_polled_command(TransactionDirection::Read, lba_mode, taskfile, m_current_request->buffer(), block_index, 256, 100, 100);
450 if (result.is_error()) {
451 complete_pio_transaction(AsyncDeviceRequest::Failure);
452 return;
453 }
454
455 } else {
456 auto result = execute_polled_command(TransactionDirection::Write, lba_mode, taskfile, m_current_request->buffer(), block_index, 256, 100, 100);
457 if (result.is_error()) {
458 complete_pio_transaction(AsyncDeviceRequest::Failure);
459 return;
460 }
461 }
462 }
463 complete_pio_transaction(AsyncDeviceRequest::Success);
464 });
465 if (work_item_creation_result.is_error()) {
466 auto current_request = m_current_request;
467 m_current_request.clear();
468 current_request->complete(AsyncDeviceRequest::OutOfMemory);
469 return Error::from_errno(ENOMEM);
470 }
471 return {};
472}
473
474ErrorOr<void> ATAPort::execute_polled_command(TransactionDirection direction, LBAMode lba_mode, TaskFile const& taskfile, UserOrKernelBuffer& buffer, size_t block_offset, size_t words_count, size_t preparation_timeout_in_milliseconds, size_t completion_timeout_in_milliseconds)
475{
476 // Disable interrupts temporarily, just in case we have that enabled,
477 // remember the value to re-enable (and clean) later if needed.
478 ATAPortInterruptDisabler disabler(*this);
479 ATAPortInterruptCleaner cleaner(*this);
480 MutexLocker locker(m_lock);
481 {
482 SpinlockLocker hard_locker(m_hard_lock);
483
484 // Wait for device to be not busy or timeout
485 TRY(wait_if_busy_until_timeout(preparation_timeout_in_milliseconds));
486
487 // Send command, wait for result or timeout
488 TRY(load_taskfile_into_registers(taskfile, lba_mode, preparation_timeout_in_milliseconds));
489
490 size_t milliseconds_elapsed = 0;
491 for (;;) {
492 if (milliseconds_elapsed > completion_timeout_in_milliseconds)
493 break;
494 u8 status = task_file_status().release_value();
495 if (status & ATA_SR_ERR) {
496 return Error::from_errno(EINVAL);
497 }
498
499 if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) {
500 break;
501 }
502
503 microseconds_delay(1000);
504 milliseconds_elapsed++;
505 }
506 if (milliseconds_elapsed > completion_timeout_in_milliseconds) {
507 critical_dmesgln("ATAPort: device state unknown. Timeout exceeded.");
508 return Error::from_errno(EINVAL);
509 }
510 }
511
512 VERIFY_INTERRUPTS_ENABLED();
513 if (direction == TransactionDirection::Read)
514 TRY(read_pio_data_to_buffer(buffer, block_offset, words_count));
515 else
516 TRY(write_pio_data_from_buffer(buffer, block_offset, words_count));
517 return {};
518}
519
520}