The Pedigree Project 0.1
AtaDisk-paging.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7#include "pedigree/kernel/LockGuard.h"
8#include "pedigree/kernel/panic.h"
9#include "pedigree/kernel/process/TerminationDeferral.h"
10#include "pedigree/kernel/utilities/new"
11#include "pedigree/kernel/utilities/utility.h"
12
13#include "AtaDisk.h"
14#include "modules/drivers/common/scsi/ScsiController.h"
15
16class AtaPagingTransport final : public PagingTransport {
17 public:
18 explicit AtaPagingTransport(AtaDisk& disk)
19 : m_Disk(disk), m_Controller(nullptr), m_Active(false), m_Request{} {}
20
21 PagingStatus prepare() {
22 TerminationDeferral lifetime;
23 LockGuard<Mutex> guard(m_Lock);
24 if (m_Active)
25 return PagingStatus::Busy;
26 m_Controller = m_Disk.acquirePagingController(m_ControllerUse);
27 if (!m_Controller)
28 return PagingStatus::Closed;
29 m_Active = true;
30 return PagingStatus::Success;
31 }
32
33 PagingStatus transfer(PagingOperation operation, uint64_t offset, void* page) override {
34 // Rejection must precede taking the serialization mutex: the controller
35 // worker can be the thread which an existing transfer is waiting for.
36 if (!m_Controller || !m_Controller->canWaitForCompletion())
37 return PagingStatus::Busy;
38 TerminationDeferral lifetime;
39 LockGuard<Mutex> guard(m_Lock);
40 if (!m_Active)
41 return PagingStatus::Closed;
42 if (operation != PagingOperation::Flush && !page)
43 return PagingStatus::Invalid;
44 if (operation == PagingOperation::Write)
45 MemoryCopy(m_Page, page, sizeof(m_Page));
46 m_Request = {operation, offset, m_Page, PagingStatus::Closed};
47 const auto admitted = m_Controller->publishPreallocated(
48 m_Token, 0, SCSI_REQUEST_PAGING, reinterpret_cast<uintptr_t>(&m_Disk),
49 reinterpret_cast<uintptr_t>(&m_Request));
50 if (admitted != RequestQueue::PreallocatedPublishResult::Accepted)
51 return admitted == RequestQueue::PreallocatedPublishResult::TokenBusy ? PagingStatus::Busy
52 : PagingStatus::Closed;
53 // Both success and cancellation retire the permanent request token. No
54 // callback can retain the caller's page or free this transport.
55 if (!m_Controller->waitForPreallocated(m_Token))
56 panic("Admitted paging request could not join its completion");
57 if (m_Request.result == PagingStatus::Success && operation == PagingOperation::Read)
58 MemoryCopy(page, m_Page, sizeof(m_Page));
59 return m_Request.result;
60 }
61
62 void release() override {
63 TerminationDeferral lifetime;
64 LockGuard<Mutex> guard(m_Lock);
65 m_Active = false;
66 m_ControllerUse = OperationBarrier::Lease();
67 m_Controller = nullptr;
68 }
69
70 private:
71 AtaDisk& m_Disk;
72 ScsiController* m_Controller;
73 Mutex m_Lock;
74 OperationBarrier::Lease m_ControllerUse;
76 bool m_Active;
77 PagingRequest m_Request;
78 alignas(16) uint8_t m_Page[PagingChannel::PageBytes];
79};
80
81void AtaDisk::preparePagingStorage() {
82#if !CRIPPLE_HDD
83 if (m_AtaDiskType == NotPacket && getNativeBlockSize() == 512 && !m_Paging)
84 m_Paging = new AtaPagingTransport(*this);
85#endif
86}
87void AtaDisk::releasePagingStorage() {
89 delete m_Paging;
90 m_Paging = nullptr;
91}
93 transport = nullptr;
94#if CRIPPLE_HDD
95 return PagingStatus::Unsupported;
96#else
97 if (m_AtaDiskType != NotPacket || getNativeBlockSize() != 512 ||
98 getSize() < PagingChannel::PageBytes || getSize() % PagingChannel::PageBytes ||
99 (m_pIdent.__raw[83] & 0xC000) != 0x4000 ||
100 (!m_pIdent.data.command_sets_support.flush_cache &&
101 !(m_SupportsLBA48 && m_pIdent.data.command_sets_support.flush_cache_ext)))
102 return PagingStatus::Unsupported;
103 if (!m_Paging)
104 return PagingStatus::NoMemory;
105 PagingStatus status = m_Paging->prepare();
106 if (status != PagingStatus::Success)
107 return status;
108 status = preparePagingCache(*m_Paging);
109 if (status != PagingStatus::Success) {
110 m_Paging->release();
111 return status;
112 }
113 transport = m_Paging;
114 return PagingStatus::Success;
115#endif
116}
117
118PagingStatus AtaDisk::doPagingTransfer(PagingOperation operation, uint64_t offset, void* page) {
119#if CRIPPLE_HDD
120 return PagingStatus::Unsupported;
121#else
122 if (m_AtaDiskType != NotPacket || getNativeBlockSize() != 512)
123 return PagingStatus::Unsupported;
124 if (operation == PagingOperation::Flush)
125 return doSync(SyncWholeDevice) ? PagingStatus::Success : PagingStatus::IoError;
126 if (!page || (offset % PagingChannel::PageBytes) || offset > getSize() ||
127 PagingChannel::PageBytes > getSize() - offset || !m_CommandRegs || !m_ControlRegs)
128 return PagingStatus::Invalid;
129 constexpr uint32_t sectors = PagingChannel::PageBytes / 512;
130 const uint64_t sector = offset / 512;
131 if ((!m_SupportsLBA48 && sector > 0x0fffffff - (sectors - 1)) ||
132 sector > 0xffffffffffffULL - (sectors - 1))
133 return PagingStatus::Unsupported;
134
135 // The controller worker serializes this polled command with ordinary I/O.
136 // No cache, DMA descriptor, IRQ completion, or heap storage is involved.
137 m_ControlRegs->write8(2, 2);
138 AtaStatus status = ataWait(m_CommandRegs, m_ControlRegs);
139 if (status.reg.bsy || status.reg.drq)
140 return PagingStatus::IoError;
141 m_CommandRegs->write8(m_IsMaster ? 0xe0 : 0xf0, 6);
142 status = ataWait(m_CommandRegs, m_ControlRegs);
143 if (status.reg.bsy || status.reg.drq || status.reg.df || !status.reg.drdy)
144 return PagingStatus::IoError;
145 // The setup helpers convert byte offsets to sectors internally.
146 if (m_SupportsLBA48)
147 setupLBA48(offset, sectors);
148 else
149 setupLBA28(offset, sectors);
150 AtaPioPollBudget budget = {Time::getTicks(), 30 * Time::Multiplier::Second, 0, 30000000};
151 const bool write = operation == PagingOperation::Write;
152 m_CommandRegs->write8(write ? (m_SupportsLBA48 ? 0x34 : 0x30) : (m_SupportsLBA48 ? 0x24 : 0x20),
153 7);
154 bool succeeded =
155 write ? ataPioWrite512ByteSectors(m_CommandRegs, m_ControlRegs, static_cast<uint16_t*>(page),
156 sectors, budget, status)
157 : ataPioRead512ByteSectors(m_CommandRegs, m_ControlRegs, static_cast<uint16_t*>(page),
158 sectors, budget, status);
159 return succeeded ? PagingStatus::Success : PagingStatus::IoError;
160#endif
161}
IoBase * m_CommandRegs
Definition AtaDisk.h:211
PagingStatus preparePagingTransport(PagingTransport *&transport) override
IdentifyData m_pIdent
Definition AtaDisk.h:177
AtaDiskType m_AtaDiskType
Definition AtaDisk.h:198
void setupLBA28(uint64_t n, uint32_t nSectors)
Definition AtaDisk.cc:1368
bool m_IsMaster
Definition AtaDisk.h:174
virtual size_t getNativeBlockSize() const
Definition AtaDisk.cc:1471
void setupLBA48(uint64_t n, uint32_t nSectors)
Definition AtaDisk.cc:1391
virtual size_t getSize() const
Gets the size of the disk.
Definition AtaDisk.cc:1430
bool m_SupportsLBA48
Definition AtaDisk.h:187
void retireEndpoint()
Definition Disk.cc:133
virtual void write8(uint8_t value, size_t offset=0)=0
Definition Mutex.h:56
MUST_USE_RESULT bool waitForPreallocated(PreallocatedRequest &request)
bool canWaitForCompletion()
MUST_USE_RESULT PreallocatedPublishResult publishPreallocated(PreallocatedRequest &request, size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
virtual void write(uint64_t location)
Definition ScsiDisk.cc:919
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117