mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-14 15:13:07 +00:00
Revert "Kernel: Implement an asynchronous device request stack"
This reverts commit 2fd5ce1eb0
.
This broke booting without SMP. (PR was #3921)
This commit is contained in:
parent
70eaadc1cd
commit
501cef2bd7
Notes:
sideshowbarker
2024-07-19 01:33:15 +09:00
Author: https://github.com/awesomekling
Commit: 501cef2bd7
17 changed files with 245 additions and 803 deletions
|
@ -14,7 +14,6 @@ set(KERNEL_SOURCES
|
|||
CMOS.cpp
|
||||
CommandLine.cpp
|
||||
Console.cpp
|
||||
Devices/AsyncDeviceRequest.cpp
|
||||
Devices/BXVGADevice.cpp
|
||||
Devices/BlockDevice.cpp
|
||||
Devices/CharacterDevice.cpp
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, The SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <Kernel/Devices/AsyncDeviceRequest.h>
|
||||
#include <Kernel/Devices/Device.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
AsyncDeviceRequest::AsyncDeviceRequest(Device& device)
|
||||
: m_device(device)
|
||||
, m_process(*Process::current())
|
||||
{
|
||||
}
|
||||
|
||||
AsyncDeviceRequest::~AsyncDeviceRequest()
|
||||
{
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
ASSERT(is_completed_result(m_result));
|
||||
ASSERT(m_sub_requests_pending.is_empty());
|
||||
}
|
||||
|
||||
// We should not need any locking here anymore. The destructor should
|
||||
// only be called until either wait() or cancel() (once implemented) returned.
|
||||
// At that point no sub-request should be adding more requests and all
|
||||
// sub-requests should be completed (either succeeded, failed, or cancelled).
|
||||
// Which means there should be no more pending sub-requests and the
|
||||
// entire AsyncDeviceRequest hirarchy should be immutable.
|
||||
for (auto& sub_request : m_sub_requests_complete) {
|
||||
ASSERT(is_completed_result(sub_request.m_result)); // Shouldn't need any locking anymore
|
||||
ASSERT(sub_request.m_parent_request == this);
|
||||
sub_request.m_parent_request = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncDeviceRequest::request_finished()
|
||||
{
|
||||
if (m_parent_request)
|
||||
m_parent_request->sub_request_finished(*this);
|
||||
|
||||
// Trigger processing the next request
|
||||
m_device.process_next_queued_request({}, *this);
|
||||
|
||||
// Wake anyone who may be waiting
|
||||
m_queue.wake_all();
|
||||
}
|
||||
|
||||
auto AsyncDeviceRequest::wait(timeval* timeout) -> RequestWaitResult
|
||||
{
|
||||
ASSERT(!m_parent_request);
|
||||
auto request_result = get_request_result();
|
||||
if (is_completed_result(request_result))
|
||||
return { request_result, Thread::BlockResult::NotBlocked };
|
||||
auto wait_result = Thread::current()->wait_on(m_queue, name(), timeout);
|
||||
return { get_request_result(), wait_result };
|
||||
}
|
||||
|
||||
auto AsyncDeviceRequest::get_request_result() const -> RequestResult
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
return m_result;
|
||||
}
|
||||
|
||||
void AsyncDeviceRequest::add_sub_request(NonnullRefPtr<AsyncDeviceRequest> sub_request)
|
||||
{
|
||||
// Sub-requests cannot be for the same device
|
||||
ASSERT(&m_device != &sub_request->m_device);
|
||||
ASSERT(sub_request->m_parent_request == nullptr);
|
||||
sub_request->m_parent_request = this;
|
||||
|
||||
bool should_start;
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
ASSERT(!is_completed_result(m_result));
|
||||
m_sub_requests_pending.append(sub_request);
|
||||
should_start = (m_result == Started);
|
||||
}
|
||||
if (should_start)
|
||||
sub_request->do_start();
|
||||
}
|
||||
|
||||
void AsyncDeviceRequest::sub_request_finished(AsyncDeviceRequest& sub_request)
|
||||
{
|
||||
bool all_completed;
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
ASSERT(m_result == Started);
|
||||
size_t index;
|
||||
for (index = 0; index < m_sub_requests_pending.size(); index++) {
|
||||
if (&m_sub_requests_pending[index] == &sub_request) {
|
||||
NonnullRefPtr<AsyncDeviceRequest> request(m_sub_requests_pending[index]);
|
||||
m_sub_requests_pending.remove(index);
|
||||
m_sub_requests_complete.append(move(request));
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(index < m_sub_requests_pending.size());
|
||||
all_completed = m_sub_requests_pending.is_empty();
|
||||
if (all_completed) {
|
||||
// Aggregate any errors
|
||||
bool any_failures = false;
|
||||
bool any_memory_faults = false;
|
||||
for (index = 0; index < m_sub_requests_complete.size(); index++) {
|
||||
auto& sub_request = m_sub_requests_complete[index];
|
||||
auto sub_result = sub_request.get_request_result();
|
||||
ASSERT(is_completed_result(sub_result));
|
||||
switch (sub_result) {
|
||||
case Failure:
|
||||
any_failures = true;
|
||||
break;
|
||||
case MemoryFault:
|
||||
any_memory_faults = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (any_failures && any_memory_faults)
|
||||
break; // Stop checking if all error conditions were found
|
||||
}
|
||||
if (any_failures)
|
||||
m_result = Failure;
|
||||
else if (any_memory_faults)
|
||||
m_result = MemoryFault;
|
||||
else
|
||||
m_result = Success;
|
||||
}
|
||||
}
|
||||
if (all_completed)
|
||||
request_finished();
|
||||
}
|
||||
|
||||
void AsyncDeviceRequest::complete(RequestResult result)
|
||||
{
|
||||
ASSERT(result == Success || result == Failure || result == MemoryFault);
|
||||
ScopedCritical critical;
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
ASSERT(m_result == Started);
|
||||
m_result = result;
|
||||
}
|
||||
if (Processor::current().in_irq()) {
|
||||
ref(); // Make sure we don't get freed
|
||||
Processor::deferred_call_queue([this]() {
|
||||
request_finished();
|
||||
unref();
|
||||
});
|
||||
} else {
|
||||
request_finished();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, The SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/Thread.h>
|
||||
#include <Kernel/UserOrKernelBuffer.h>
|
||||
#include <Kernel/VM/ProcessPagingScope.h>
|
||||
#include <Kernel/WaitQueue.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Device;
|
||||
|
||||
class AsyncDeviceRequest : public RefCounted<AsyncDeviceRequest> {
|
||||
AK_MAKE_NONCOPYABLE(AsyncDeviceRequest);
|
||||
AK_MAKE_NONMOVABLE(AsyncDeviceRequest);
|
||||
|
||||
public:
|
||||
enum RequestResult {
|
||||
Pending = 0,
|
||||
Started,
|
||||
Success,
|
||||
Failure,
|
||||
MemoryFault,
|
||||
Cancelled
|
||||
};
|
||||
|
||||
class RequestWaitResult {
|
||||
friend class AsyncDeviceRequest;
|
||||
|
||||
public:
|
||||
RequestResult request_result() const { return m_request_result; }
|
||||
Thread::BlockResult wait_result() const { return m_wait_result; }
|
||||
|
||||
private:
|
||||
RequestWaitResult(RequestResult request_result, Thread::BlockResult wait_result)
|
||||
: m_request_result(request_result)
|
||||
, m_wait_result(wait_result)
|
||||
{
|
||||
}
|
||||
|
||||
RequestResult m_request_result;
|
||||
Thread::BlockResult m_wait_result;
|
||||
};
|
||||
|
||||
virtual ~AsyncDeviceRequest();
|
||||
|
||||
virtual const char* name() const = 0;
|
||||
virtual void start() = 0;
|
||||
|
||||
void add_sub_request(NonnullRefPtr<AsyncDeviceRequest>);
|
||||
|
||||
[[nodiscard]] RequestWaitResult wait(timeval* = nullptr);
|
||||
|
||||
void do_start(Badge<Device>)
|
||||
{
|
||||
do_start();
|
||||
}
|
||||
|
||||
void complete(RequestResult result);
|
||||
|
||||
void set_private(void* priv)
|
||||
{
|
||||
ASSERT(!m_private || !priv);
|
||||
m_private = priv;
|
||||
}
|
||||
void* get_private() const { return m_private; }
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] bool write_to_buffer(UserOrKernelBuffer& buffer, Args... args)
|
||||
{
|
||||
if (in_target_context(buffer))
|
||||
return buffer.write(forward<Args>(args)...);
|
||||
ProcessPagingScope paging_scope(m_process);
|
||||
return buffer.write(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<size_t BUFFER_BYTES, typename... Args>
|
||||
[[nodiscard]] bool write_to_buffer_buffered(UserOrKernelBuffer& buffer, Args... args)
|
||||
{
|
||||
if (in_target_context(buffer))
|
||||
return buffer.write_buffered<BUFFER_BYTES>(forward<Args>(args)...);
|
||||
ProcessPagingScope paging_scope(m_process);
|
||||
return buffer.write_buffered<BUFFER_BYTES>(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] bool read_from_buffer(const UserOrKernelBuffer& buffer, Args... args)
|
||||
{
|
||||
if (in_target_context(buffer))
|
||||
return buffer.read(forward<Args>(args)...);
|
||||
ProcessPagingScope paging_scope(m_process);
|
||||
return buffer.read(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<size_t BUFFER_BYTES, typename... Args>
|
||||
[[nodiscard]] bool read_from_buffer_buffered(const UserOrKernelBuffer& buffer, Args... args)
|
||||
{
|
||||
if (in_target_context(buffer))
|
||||
return buffer.read_buffered<BUFFER_BYTES>(forward<Args>(args)...);
|
||||
ProcessPagingScope paging_scope(m_process);
|
||||
return buffer.read_buffered<BUFFER_BYTES>(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
protected:
|
||||
AsyncDeviceRequest(Device&);
|
||||
|
||||
RequestResult get_request_result() const;
|
||||
|
||||
private:
|
||||
void sub_request_finished(AsyncDeviceRequest&);
|
||||
void request_finished();
|
||||
|
||||
void do_start()
|
||||
{
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
if (is_completed_result(m_result))
|
||||
return;
|
||||
m_result = Started;
|
||||
}
|
||||
start();
|
||||
}
|
||||
|
||||
bool in_target_context(const UserOrKernelBuffer& buffer) const
|
||||
{
|
||||
if (buffer.is_kernel_buffer())
|
||||
return true;
|
||||
return m_process == Process::current();
|
||||
}
|
||||
|
||||
static bool is_completed_result(RequestResult result)
|
||||
{
|
||||
return result > Started;
|
||||
}
|
||||
|
||||
Device& m_device;
|
||||
|
||||
AsyncDeviceRequest* m_parent_request { nullptr };
|
||||
RequestResult m_result { Pending };
|
||||
NonnullRefPtrVector<AsyncDeviceRequest> m_sub_requests_pending;
|
||||
NonnullRefPtrVector<AsyncDeviceRequest> m_sub_requests_complete;
|
||||
WaitQueue m_queue;
|
||||
NonnullRefPtr<Process> m_process;
|
||||
void* m_private { nullptr };
|
||||
mutable SpinLock<u8> m_lock;
|
||||
};
|
||||
|
||||
}
|
|
@ -48,9 +48,10 @@ private:
|
|||
virtual const char* class_name() const override { return "BXVGA"; }
|
||||
virtual bool can_read(const FileDescription&, size_t) const override { return true; }
|
||||
virtual bool can_write(const FileDescription&, size_t) const override { return true; }
|
||||
virtual void start_request(AsyncBlockDeviceRequest& request) override { request.complete(AsyncDeviceRequest::Failure); }
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return -EINVAL; }
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override { return -EINVAL; }
|
||||
virtual bool read_blocks(unsigned, u16, UserOrKernelBuffer&) override { return false; }
|
||||
virtual bool write_blocks(unsigned, u16, const UserOrKernelBuffer&) override { return false; }
|
||||
|
||||
void set_safe_resolution();
|
||||
|
||||
|
|
|
@ -28,66 +28,18 @@
|
|||
|
||||
namespace Kernel {
|
||||
|
||||
AsyncBlockDeviceRequest::AsyncBlockDeviceRequest(Device& block_device, RequestType request_type, u32 block_index, u32 block_count, const UserOrKernelBuffer& buffer, size_t buffer_size)
|
||||
: AsyncDeviceRequest(block_device)
|
||||
, m_block_device(static_cast<BlockDevice&>(block_device))
|
||||
, m_request_type(request_type)
|
||||
, m_block_index(block_index)
|
||||
, m_block_count(block_count)
|
||||
, m_buffer(buffer)
|
||||
, m_buffer_size(buffer_size)
|
||||
{
|
||||
}
|
||||
|
||||
void AsyncBlockDeviceRequest::start()
|
||||
{
|
||||
m_block_device.start_request(*this);
|
||||
}
|
||||
|
||||
BlockDevice::~BlockDevice()
|
||||
{
|
||||
}
|
||||
|
||||
bool BlockDevice::read_block(unsigned index, UserOrKernelBuffer& buffer)
|
||||
bool BlockDevice::read_block(unsigned index, UserOrKernelBuffer& buffer) const
|
||||
{
|
||||
auto read_request = make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read, index, 1, buffer, 512);
|
||||
switch (read_request->wait().request_result()) {
|
||||
case AsyncDeviceRequest::Success:
|
||||
return true;
|
||||
case AsyncDeviceRequest::Failure:
|
||||
dbg() << "BlockDevice::read_block(" << index << ") IO error";
|
||||
break;
|
||||
case AsyncDeviceRequest::MemoryFault:
|
||||
dbg() << "BlockDevice::read_block(" << index << ") EFAULT";
|
||||
break;
|
||||
case AsyncDeviceRequest::Cancelled:
|
||||
dbg() << "BlockDevice::read_block(" << index << ") cancelled";
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
return false;
|
||||
return const_cast<BlockDevice*>(this)->read_blocks(index, 1, buffer);
|
||||
}
|
||||
|
||||
bool BlockDevice::write_block(unsigned index, const UserOrKernelBuffer& buffer)
|
||||
bool BlockDevice::write_block(unsigned index, const UserOrKernelBuffer& data)
|
||||
{
|
||||
auto write_request = make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Write, index, 1, buffer, 512);
|
||||
switch (write_request->wait().request_result()) {
|
||||
case AsyncDeviceRequest::Success:
|
||||
return true;
|
||||
case AsyncDeviceRequest::Failure:
|
||||
dbg() << "BlockDevice::write_block(" << index << ") IO error";
|
||||
break;
|
||||
case AsyncDeviceRequest::MemoryFault:
|
||||
dbg() << "BlockDevice::write_block(" << index << ") EFAULT";
|
||||
break;
|
||||
case AsyncDeviceRequest::Cancelled:
|
||||
dbg() << "BlockDevice::write_block(" << index << ") cancelled";
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
return false;
|
||||
return write_blocks(index, 1, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,46 +30,6 @@
|
|||
|
||||
namespace Kernel {
|
||||
|
||||
class BlockDevice;
|
||||
|
||||
class AsyncBlockDeviceRequest : public AsyncDeviceRequest {
|
||||
public:
|
||||
enum RequestType {
|
||||
Read,
|
||||
Write
|
||||
};
|
||||
AsyncBlockDeviceRequest(Device& block_device, RequestType request_type,
|
||||
u32 block_index, u32 block_count, const UserOrKernelBuffer& buffer, size_t buffer_size);
|
||||
|
||||
RequestType request_type() const { return m_request_type; }
|
||||
u32 block_index() const { return m_block_index; }
|
||||
u32 block_count() const { return m_block_count; }
|
||||
UserOrKernelBuffer& buffer() { return m_buffer; }
|
||||
const UserOrKernelBuffer& buffer() const { return m_buffer; }
|
||||
size_t buffer_size() const { return m_buffer_size; }
|
||||
|
||||
virtual void start() override;
|
||||
virtual const char* name() const override
|
||||
{
|
||||
switch (m_request_type) {
|
||||
case Read:
|
||||
return "BlockDeviceRequest (read)";
|
||||
case Write:
|
||||
return "BlockDeviceRequest (read)";
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BlockDevice& m_block_device;
|
||||
const RequestType m_request_type;
|
||||
const u32 m_block_index;
|
||||
const u32 m_block_count;
|
||||
UserOrKernelBuffer m_buffer;
|
||||
const size_t m_buffer_size;
|
||||
};
|
||||
|
||||
class BlockDevice : public Device {
|
||||
public:
|
||||
virtual ~BlockDevice() override;
|
||||
|
@ -77,10 +37,11 @@ public:
|
|||
size_t block_size() const { return m_block_size; }
|
||||
virtual bool is_seekable() const override { return true; }
|
||||
|
||||
bool read_block(unsigned index, UserOrKernelBuffer&);
|
||||
bool read_block(unsigned index, UserOrKernelBuffer&) const;
|
||||
bool write_block(unsigned index, const UserOrKernelBuffer&);
|
||||
|
||||
virtual void start_request(AsyncBlockDeviceRequest&) = 0;
|
||||
virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) = 0;
|
||||
virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) = 0;
|
||||
|
||||
protected:
|
||||
BlockDevice(unsigned major, unsigned minor, size_t block_size = PAGE_SIZE)
|
||||
|
|
|
@ -80,21 +80,4 @@ String Device::absolute_path(const FileDescription&) const
|
|||
return absolute_path();
|
||||
}
|
||||
|
||||
void Device::process_next_queued_request(Badge<AsyncDeviceRequest>, const AsyncDeviceRequest& completed_request)
|
||||
{
|
||||
AsyncDeviceRequest* next_request = nullptr;
|
||||
|
||||
{
|
||||
ScopedSpinLock lock(m_requests_lock);
|
||||
ASSERT(!m_requests.is_empty());
|
||||
ASSERT(m_requests.first().ptr() == &completed_request);
|
||||
m_requests.remove(m_requests.begin());
|
||||
if (!m_requests.is_empty())
|
||||
next_request = m_requests.first().ptr();
|
||||
}
|
||||
|
||||
if (next_request)
|
||||
next_request->start();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,12 +34,10 @@
|
|||
// There are two main subclasses:
|
||||
// - BlockDevice (random access)
|
||||
// - CharacterDevice (sequential)
|
||||
#include <AK/DoublyLinkedList.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <Kernel/Devices/AsyncDeviceRequest.h>
|
||||
#include <Kernel/Arch/i386/CPU.h>
|
||||
#include <Kernel/FileSystem/File.h>
|
||||
#include <Kernel/Lock.h>
|
||||
#include <Kernel/UnixTypes.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
@ -63,23 +61,6 @@ public:
|
|||
static void for_each(Function<void(Device&)>);
|
||||
static Device* get_device(unsigned major, unsigned minor);
|
||||
|
||||
void process_next_queued_request(Badge<AsyncDeviceRequest>, const AsyncDeviceRequest&);
|
||||
|
||||
template<typename AsyncRequestType, typename... Args>
|
||||
NonnullRefPtr<AsyncRequestType> make_request(Args&&... args)
|
||||
{
|
||||
auto request = adopt(*new AsyncRequestType(*this, forward<Args>(args)...));
|
||||
bool was_empty;
|
||||
{
|
||||
ScopedSpinLock lock(m_requests_lock);
|
||||
was_empty = m_requests.is_empty();
|
||||
m_requests.append(request);
|
||||
}
|
||||
if (was_empty)
|
||||
request->do_start({});
|
||||
return request;
|
||||
}
|
||||
|
||||
protected:
|
||||
Device(unsigned major, unsigned minor);
|
||||
void set_uid(uid_t uid) { m_uid = uid; }
|
||||
|
@ -92,9 +73,6 @@ private:
|
|||
unsigned m_minor { 0 };
|
||||
uid_t m_uid { 0 };
|
||||
gid_t m_gid { 0 };
|
||||
|
||||
SpinLock<u8> m_requests_lock;
|
||||
DoublyLinkedList<RefPtr<AsyncDeviceRequest>> m_requests;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -48,12 +48,6 @@ DiskPartition::~DiskPartition()
|
|||
{
|
||||
}
|
||||
|
||||
void DiskPartition::start_request(AsyncBlockDeviceRequest& request)
|
||||
{
|
||||
request.add_sub_request(m_device->make_request<AsyncBlockDeviceRequest>(request.request_type(),
|
||||
request.block_index() + m_block_offset, request.block_count(), request.buffer(), request.buffer_size()));
|
||||
}
|
||||
|
||||
KResultOr<size_t> DiskPartition::read(FileDescription& fd, size_t offset, UserOrKernelBuffer& outbuf, size_t len)
|
||||
{
|
||||
unsigned adjust = m_block_offset * block_size();
|
||||
|
@ -98,6 +92,24 @@ bool DiskPartition::can_write(const FileDescription& fd, size_t offset) const
|
|||
return m_device->can_write(fd, offset + adjust);
|
||||
}
|
||||
|
||||
bool DiskPartition::read_blocks(unsigned index, u16 count, UserOrKernelBuffer& out)
|
||||
{
|
||||
#ifdef OFFD_DEBUG
|
||||
klog() << "DiskPartition::read_blocks " << index << " (really: " << (m_block_offset + index) << ") count=" << count;
|
||||
#endif
|
||||
|
||||
return m_device->read_blocks(m_block_offset + index, count, out);
|
||||
}
|
||||
|
||||
bool DiskPartition::write_blocks(unsigned index, u16 count, const UserOrKernelBuffer& data)
|
||||
{
|
||||
#ifdef OFFD_DEBUG
|
||||
klog() << "DiskPartition::write_blocks " << index << " (really: " << (m_block_offset + index) << ") count=" << count;
|
||||
#endif
|
||||
|
||||
return m_device->write_blocks(m_block_offset + index, count, data);
|
||||
}
|
||||
|
||||
const char* DiskPartition::class_name() const
|
||||
{
|
||||
return "DiskPartition";
|
||||
|
|
|
@ -36,7 +36,8 @@ public:
|
|||
static NonnullRefPtr<DiskPartition> create(BlockDevice&, unsigned block_offset, unsigned block_limit);
|
||||
virtual ~DiskPartition();
|
||||
|
||||
virtual void start_request(AsyncBlockDeviceRequest&) override;
|
||||
virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) override;
|
||||
virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) override;
|
||||
|
||||
// ^BlockDevice
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override;
|
||||
|
|
|
@ -63,9 +63,6 @@ int EBRPartitionTable::index_of_ebr_container() const
|
|||
|
||||
bool EBRPartitionTable::initialize()
|
||||
{
|
||||
auto mbr_header_request = m_device->make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read,
|
||||
0, 1, UserOrKernelBuffer::for_kernel_buffer(m_cached_mbr_header), sizeof(m_cached_mbr_header));
|
||||
|
||||
auto mbr_header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_mbr_header);
|
||||
if (!m_device->read_block(0, mbr_header_buffer)) {
|
||||
return false;
|
||||
|
|
|
@ -84,7 +84,7 @@ RefPtr<DiskPartition> GPTPartitionTable::partition(unsigned index)
|
|||
|
||||
GPTPartitionEntry entries[entries_per_sector];
|
||||
auto entries_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&entries);
|
||||
this->m_device->read_block(lba, entries_buffer);
|
||||
this->m_device->read_blocks(lba, 1, entries_buffer);
|
||||
GPTPartitionEntry& entry = entries[((index - 1) % entries_per_sector)];
|
||||
|
||||
#ifdef GPT_DEBUG
|
||||
|
|
|
@ -49,7 +49,8 @@ private:
|
|||
virtual bool can_write(const FileDescription&, size_t) const override { return true; }
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return -EINVAL; }
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override { return -EINVAL; }
|
||||
virtual void start_request(AsyncBlockDeviceRequest& request) override { request.complete(AsyncDeviceRequest::Failure); }
|
||||
virtual bool read_blocks(unsigned, u16, UserOrKernelBuffer&) override { return false; }
|
||||
virtual bool write_blocks(unsigned, u16, const UserOrKernelBuffer&) override { return false; }
|
||||
|
||||
size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; }
|
||||
|
||||
|
|
|
@ -108,6 +108,13 @@ namespace Kernel {
|
|||
#define PCI_Mass_Storage_Class 0x1
|
||||
#define PCI_IDE_Controller_Subclass 0x1
|
||||
|
||||
static AK::Singleton<Lock> s_pata_lock;
|
||||
|
||||
static Lock& s_lock()
|
||||
{
|
||||
return *s_pata_lock;
|
||||
};
|
||||
|
||||
OwnPtr<PATAChannel> PATAChannel::create(ChannelType type, bool force_pio)
|
||||
{
|
||||
PCI::Address pci_address;
|
||||
|
@ -141,59 +148,10 @@ PATAChannel::~PATAChannel()
|
|||
{
|
||||
}
|
||||
|
||||
void PATAChannel::start_request(AsyncBlockDeviceRequest& request, bool use_dma, bool is_slave)
|
||||
void PATAChannel::prepare_for_irq()
|
||||
{
|
||||
m_current_request = &request;
|
||||
m_current_request_block_index = 0;
|
||||
m_current_request_uses_dma = use_dma;
|
||||
m_current_request_flushing_cache = false;
|
||||
|
||||
if (request.request_type() == AsyncBlockDeviceRequest::Read) {
|
||||
if (use_dma)
|
||||
ata_read_sectors_with_dma(is_slave);
|
||||
else
|
||||
ata_read_sectors(is_slave);
|
||||
} else {
|
||||
if (use_dma)
|
||||
ata_write_sectors_with_dma(is_slave);
|
||||
else
|
||||
ata_write_sectors(is_slave);
|
||||
}
|
||||
}
|
||||
|
||||
void PATAChannel::complete_current_request(AsyncDeviceRequest::RequestResult result)
|
||||
{
|
||||
// NOTE: this may be called from the interrupt handler!
|
||||
ASSERT(m_current_request);
|
||||
|
||||
// Now schedule reading back the buffer as soon as we leave the irq handler.
|
||||
// This is important so that we can safely write the buffer back,
|
||||
// which could cause page faults. Note that this may be called immediately
|
||||
// before Processor::deferred_call_queue returns!
|
||||
Processor::deferred_call_queue([this, result]() {
|
||||
#ifdef PATA_DEBUG
|
||||
dbg() << "PATAChannel::complete_current_request result: " << result;
|
||||
#endif
|
||||
ASSERT(m_current_request);
|
||||
auto& request = *m_current_request;
|
||||
m_current_request = nullptr;
|
||||
|
||||
if (m_current_request_uses_dma) {
|
||||
if (result == AsyncDeviceRequest::Success) {
|
||||
if (request.request_type() == AsyncBlockDeviceRequest::Read) {
|
||||
if (!request.write_to_buffer(request.buffer(), m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * request.block_count())) {
|
||||
request.complete(AsyncDeviceRequest::MemoryFault);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// I read somewhere that this may trigger a cache flush so let's do it.
|
||||
m_bus_master_base.offset(2).out<u8>(m_bus_master_base.offset(2).in<u8>() | 0x6);
|
||||
}
|
||||
}
|
||||
|
||||
request.complete(result);
|
||||
});
|
||||
cli();
|
||||
enable_irq();
|
||||
}
|
||||
|
||||
void PATAChannel::initialize(bool force_pio)
|
||||
|
@ -217,6 +175,12 @@ static void print_ide_status(u8 status)
|
|||
klog() << "PATAChannel: print_ide_status: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0) << " DSC=" << ((status & ATA_SR_DSC) != 0) << " DF=" << ((status & ATA_SR_DF) != 0) << " CORR=" << ((status & ATA_SR_CORR) != 0) << " IDX=" << ((status & ATA_SR_IDX) != 0) << " ERR=" << ((status & ATA_SR_ERR) != 0);
|
||||
}
|
||||
|
||||
void PATAChannel::wait_for_irq()
|
||||
{
|
||||
Thread::current()->wait_on(m_irq_queue, "PATAChannel");
|
||||
disable_irq();
|
||||
}
|
||||
|
||||
void PATAChannel::handle_irq(const RegisterState&)
|
||||
{
|
||||
u8 status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
|
||||
|
@ -232,63 +196,16 @@ void PATAChannel::handle_irq(const RegisterState&)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef PATA_DEBUG
|
||||
klog() << "PATAChannel: interrupt: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0);
|
||||
#endif
|
||||
|
||||
bool received_all_irqs = m_current_request_uses_dma || m_current_request_block_index + 1 >= m_current_request->block_count();
|
||||
|
||||
disable_irq();
|
||||
|
||||
if (status & ATA_SR_ERR) {
|
||||
print_ide_status(status);
|
||||
m_device_error = m_io_base.offset(ATA_REG_ERROR).in<u8>();
|
||||
klog() << "PATAChannel: Error " << String::format("%b", m_device_error) << "!";
|
||||
complete_current_request(AsyncDeviceRequest::Failure);
|
||||
return;
|
||||
}
|
||||
|
||||
m_device_error = 0;
|
||||
if (received_all_irqs) {
|
||||
complete_current_request(AsyncDeviceRequest::Success);
|
||||
} else {
|
||||
ASSERT(!m_current_request_uses_dma);
|
||||
|
||||
// Now schedule reading/writing the buffer as soon as we leave the irq handler.
|
||||
// This is important so that we can safely access the buffers, which could
|
||||
// trigger page faults
|
||||
Processor::deferred_call_queue([this]() {
|
||||
if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) {
|
||||
dbg() << "PATAChannel: Read block " << m_current_request_block_index << "/" << m_current_request->block_count();
|
||||
if (ata_do_read_sector()) {
|
||||
if (++m_current_request_block_index >= m_current_request->block_count()) {
|
||||
complete_current_request(AsyncDeviceRequest::Success);
|
||||
return;
|
||||
}
|
||||
// Wait for the next block
|
||||
enable_irq();
|
||||
}
|
||||
} else {
|
||||
if (!m_current_request_flushing_cache) {
|
||||
dbg() << "PATAChannel: Wrote block " << m_current_request_block_index << "/" << m_current_request->block_count();
|
||||
if (++m_current_request_block_index >= m_current_request->block_count()) {
|
||||
// We read the last block, flush cache
|
||||
ASSERT(!m_current_request_flushing_cache);
|
||||
m_current_request_flushing_cache = true;
|
||||
enable_irq();
|
||||
m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_CACHE_FLUSH);
|
||||
} else {
|
||||
// Read next block
|
||||
enable_irq();
|
||||
ata_do_write_sector();
|
||||
}
|
||||
} else {
|
||||
complete_current_request(AsyncDeviceRequest::Success);
|
||||
}
|
||||
}
|
||||
});
|
||||
m_device_error = 0;
|
||||
}
|
||||
|
||||
#ifdef PATA_DEBUG
|
||||
klog() << "PATAChannel: interrupt: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0);
|
||||
#endif
|
||||
m_irq_queue.wake_all();
|
||||
}
|
||||
|
||||
|
@ -357,16 +274,15 @@ void PATAChannel::detect_disks()
|
|||
}
|
||||
}
|
||||
|
||||
void PATAChannel::ata_read_sectors_with_dma(bool slave_request)
|
||||
bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer& outbuf, bool slave_request)
|
||||
{
|
||||
auto& request = *m_current_request;
|
||||
u32 lba = request.block_index();
|
||||
LOCKER(s_lock());
|
||||
#ifdef PATA_DEBUG
|
||||
dbg() << "PATAChannel::ata_read_sectors_with_dma (" << lba << " x" << request.block_count() << ")";
|
||||
dbg() << "PATAChannel::ata_read_sectors_with_dma (" << lba << " x" << count << ") -> " << outbuf.user_or_kernel_ptr();
|
||||
#endif
|
||||
|
||||
prdt().offset = m_dma_buffer_page->paddr();
|
||||
prdt().size = 512 * request.block_count();
|
||||
prdt().size = 512 * count;
|
||||
|
||||
ASSERT(prdt().size <= PAGE_SIZE);
|
||||
|
||||
|
@ -396,7 +312,7 @@ void PATAChannel::ata_read_sectors_with_dma(bool slave_request)
|
|||
m_io_base.offset(ATA_REG_LBA1).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_LBA2).out<u8>(0);
|
||||
|
||||
m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
|
||||
m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(count);
|
||||
m_io_base.offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
|
||||
m_io_base.offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
|
||||
m_io_base.offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
|
||||
|
@ -410,89 +326,35 @@ void PATAChannel::ata_read_sectors_with_dma(bool slave_request)
|
|||
m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_READ_DMA_EXT);
|
||||
io_delay();
|
||||
|
||||
enable_irq();
|
||||
prepare_for_irq();
|
||||
// Start bus master
|
||||
m_bus_master_base.out<u8>(0x9);
|
||||
}
|
||||
|
||||
bool PATAChannel::ata_do_read_sector()
|
||||
{
|
||||
auto& request = *m_current_request;
|
||||
auto out_buffer = request.buffer().offset(m_current_request_block_index * 512);
|
||||
ssize_t nwritten = request.write_to_buffer_buffered<512>(out_buffer, 512, [&](u8* buffer, size_t buffer_bytes) {
|
||||
for (size_t i = 0; i < buffer_bytes; i += sizeof(u16))
|
||||
*(u16*)&buffer[i] = IO::in16(m_io_base.offset(ATA_REG_DATA).get());
|
||||
return (ssize_t)buffer_bytes;
|
||||
});
|
||||
if (nwritten < 0) {
|
||||
// TODO: Do we need to abort the PATA read if this wasn't the last block?
|
||||
complete_current_request(AsyncDeviceRequest::MemoryFault);
|
||||
wait_for_irq();
|
||||
|
||||
if (m_device_error)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!outbuf.write(m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * count))
|
||||
return false; // TODO: -EFAULT
|
||||
|
||||
// I read somewhere that this may trigger a cache flush so let's do it.
|
||||
m_bus_master_base.offset(2).out<u8>(m_bus_master_base.offset(2).in<u8>() | 0x6);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PATAChannel::ata_read_sectors(bool slave_request)
|
||||
bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer& inbuf, bool slave_request)
|
||||
{
|
||||
auto& request = *m_current_request;
|
||||
ASSERT(request.block_count() <= 256);
|
||||
LOCKER(s_lock());
|
||||
#ifdef PATA_DEBUG
|
||||
dbg() << "PATAChannel::ata_read_sectors";
|
||||
#endif
|
||||
|
||||
while (m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
|
||||
;
|
||||
|
||||
auto lba = request.block_index();
|
||||
#ifdef PATA_DEBUG
|
||||
klog() << "PATAChannel: Reading " << request.block_count() << " sector(s) @ LBA " << lba;
|
||||
#endif
|
||||
|
||||
u8 devsel = 0xe0;
|
||||
if (slave_request)
|
||||
devsel |= 0x10;
|
||||
|
||||
m_control_base.offset(ATA_CTL_CONTROL).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_HDDEVSEL).out<u8>(devsel | (static_cast<u8>(slave_request) << 4) | 0x40);
|
||||
io_delay();
|
||||
|
||||
m_io_base.offset(ATA_REG_FEATURES).out<u8>(0);
|
||||
|
||||
m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_LBA0).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_LBA1).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_LBA2).out<u8>(0);
|
||||
|
||||
m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
|
||||
m_io_base.offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
|
||||
m_io_base.offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
|
||||
m_io_base.offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
|
||||
|
||||
for (;;) {
|
||||
auto status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
|
||||
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
|
||||
break;
|
||||
}
|
||||
|
||||
enable_irq();
|
||||
m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_READ_PIO);
|
||||
}
|
||||
|
||||
void PATAChannel::ata_write_sectors_with_dma(bool slave_request)
|
||||
{
|
||||
auto& request = *m_current_request;
|
||||
u32 lba = request.block_index();
|
||||
#ifdef PATA_DEBUG
|
||||
dbg() << "PATAChannel::ata_write_sectors_with_dma (" << lba << " x" << request.block_count() << ")";
|
||||
dbg() << "PATAChannel::ata_write_sectors_with_dma (" << lba << " x" << count << ") <- " << inbuf.user_or_kernel_ptr();
|
||||
#endif
|
||||
|
||||
prdt().offset = m_dma_buffer_page->paddr();
|
||||
prdt().size = 512 * request.block_count();
|
||||
prdt().size = 512 * count;
|
||||
|
||||
if (!request.read_from_buffer(request.buffer(), m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * request.block_count())) {
|
||||
complete_current_request(AsyncDeviceRequest::MemoryFault);
|
||||
return;
|
||||
}
|
||||
if (!inbuf.read(m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * count))
|
||||
return false; // TODO: -EFAULT
|
||||
|
||||
ASSERT(prdt().size <= PAGE_SIZE);
|
||||
|
||||
|
@ -519,7 +381,7 @@ void PATAChannel::ata_write_sectors_with_dma(bool slave_request)
|
|||
m_io_base.offset(ATA_REG_LBA1).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_LBA2).out<u8>(0);
|
||||
|
||||
m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
|
||||
m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(count);
|
||||
m_io_base.offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
|
||||
m_io_base.offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
|
||||
m_io_base.offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
|
||||
|
@ -533,42 +395,100 @@ void PATAChannel::ata_write_sectors_with_dma(bool slave_request)
|
|||
m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_WRITE_DMA_EXT);
|
||||
io_delay();
|
||||
|
||||
enable_irq();
|
||||
prepare_for_irq();
|
||||
// Start bus master
|
||||
m_bus_master_base.out<u8>(0x1);
|
||||
wait_for_irq();
|
||||
|
||||
if (m_device_error)
|
||||
return false;
|
||||
|
||||
// I read somewhere that this may trigger a cache flush so let's do it.
|
||||
m_bus_master_base.offset(2).out<u8>(m_bus_master_base.offset(2).in<u8>() | 0x6);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PATAChannel::ata_do_write_sector()
|
||||
bool PATAChannel::ata_read_sectors(u32 lba, u16 count, UserOrKernelBuffer& outbuf, bool slave_request)
|
||||
{
|
||||
auto& request = *m_current_request;
|
||||
ASSERT(count <= 256);
|
||||
LOCKER(s_lock());
|
||||
#ifdef PATA_DEBUG
|
||||
dbg() << "PATAChannel::ata_read_sectors request (" << count << " sector(s) @ " << lba << " into " << outbuf.user_or_kernel_ptr() << ")";
|
||||
#endif
|
||||
|
||||
io_delay();
|
||||
while ((m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY) || !(m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRQ))
|
||||
while (m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
|
||||
;
|
||||
|
||||
u8 status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
|
||||
ASSERT(status & ATA_SR_DRQ);
|
||||
|
||||
auto in_buffer = request.buffer().offset(m_current_request_block_index * 512);
|
||||
#ifdef PATA_DEBUG
|
||||
dbg() << "PATAChannel: Writing 512 bytes (part " << m_current_request_block_index << ") (status=" << String::format("%b", status) << ")...";
|
||||
klog() << "PATAChannel: Reading " << count << " sector(s) @ LBA " << lba;
|
||||
#endif
|
||||
ssize_t nread = request.read_from_buffer_buffered<512>(in_buffer, 512, [&](const u8* buffer, size_t buffer_bytes) {
|
||||
for (size_t i = 0; i < buffer_bytes; i += sizeof(u16))
|
||||
IO::out16(m_io_base.offset(ATA_REG_DATA).get(), *(const u16*)&buffer[i]);
|
||||
return (ssize_t)buffer_bytes;
|
||||
});
|
||||
if (nread < 0)
|
||||
complete_current_request(AsyncDeviceRequest::MemoryFault);
|
||||
|
||||
u8 devsel = 0xe0;
|
||||
if (slave_request)
|
||||
devsel |= 0x10;
|
||||
|
||||
m_control_base.offset(ATA_CTL_CONTROL).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_HDDEVSEL).out<u8>(devsel | (static_cast<u8>(slave_request) << 4) | 0x40);
|
||||
io_delay();
|
||||
|
||||
m_io_base.offset(ATA_REG_FEATURES).out<u8>(0);
|
||||
|
||||
m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_LBA0).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_LBA1).out<u8>(0);
|
||||
m_io_base.offset(ATA_REG_LBA2).out<u8>(0);
|
||||
|
||||
m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(count);
|
||||
m_io_base.offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
|
||||
m_io_base.offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
|
||||
m_io_base.offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
|
||||
|
||||
for (;;) {
|
||||
auto status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
|
||||
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
|
||||
break;
|
||||
}
|
||||
|
||||
prepare_for_irq();
|
||||
m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_READ_PIO);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i > 0)
|
||||
prepare_for_irq();
|
||||
wait_for_irq();
|
||||
if (m_device_error)
|
||||
return false;
|
||||
|
||||
u8 status = m_control_base.offset(ATA_CTL_ALTSTATUS).in<u8>();
|
||||
ASSERT(!(status & ATA_SR_BSY));
|
||||
|
||||
auto out = outbuf.offset(i * 512);
|
||||
#ifdef PATA_DEBUG
|
||||
dbg() << "PATAChannel: Retrieving 512 bytes (part " << i << ") (status=" << String::format("%b", status) << "), outbuf=(" << out.user_or_kernel_ptr() << ")...";
|
||||
#endif
|
||||
prepare_for_irq();
|
||||
|
||||
ssize_t nwritten = out.write_buffered<512>(512, [&](u8* buffer, size_t buffer_bytes) {
|
||||
for (size_t i = 0; i < buffer_bytes; i += sizeof(u16))
|
||||
*(u16*)&buffer[i] = IO::in16(m_io_base.offset(ATA_REG_DATA).get());
|
||||
return (ssize_t)buffer_bytes;
|
||||
});
|
||||
if (nwritten < 0) {
|
||||
sti();
|
||||
disable_irq();
|
||||
return false; // TODO: -EFAULT
|
||||
}
|
||||
}
|
||||
|
||||
sti();
|
||||
disable_irq();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PATAChannel::ata_write_sectors(bool slave_request)
|
||||
bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const UserOrKernelBuffer& inbuf, bool slave_request)
|
||||
{
|
||||
auto& request = *m_current_request;
|
||||
|
||||
ASSERT(request.block_count() <= 256);
|
||||
u32 start_sector = request.block_index();
|
||||
u32 count = request.block_count();
|
||||
ASSERT(count <= 256);
|
||||
LOCKER(s_lock());
|
||||
#ifdef PATA_DEBUG
|
||||
klog() << "PATAChannel::ata_write_sectors request (" << count << " sector(s) @ " << start_sector << ")";
|
||||
#endif
|
||||
|
@ -596,12 +516,37 @@ void PATAChannel::ata_write_sectors(bool slave_request)
|
|||
|
||||
m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_WRITE_PIO);
|
||||
|
||||
io_delay();
|
||||
while ((m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY) || !(m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRQ))
|
||||
;
|
||||
for (int i = 0; i < count; i++) {
|
||||
io_delay();
|
||||
while ((m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY) || !(m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRQ))
|
||||
;
|
||||
|
||||
enable_irq();
|
||||
ata_do_write_sector();
|
||||
u8 status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
|
||||
ASSERT(status & ATA_SR_DRQ);
|
||||
|
||||
auto in = inbuf.offset(i * 512);
|
||||
#ifdef PATA_DEBUG
|
||||
dbg() << "PATAChannel: Writing 512 bytes (part " << i << ") (status=" << String::format("%b", status) << "), inbuf=(" << in.user_or_kernel_ptr() << ")...";
|
||||
#endif
|
||||
prepare_for_irq();
|
||||
ssize_t nread = in.read_buffered<512>(512, [&](const u8* buffer, size_t buffer_bytes) {
|
||||
for (size_t i = 0; i < buffer_bytes; i += sizeof(u16))
|
||||
IO::out16(m_io_base.offset(ATA_REG_DATA).get(), *(const u16*)&buffer[i]);
|
||||
return (ssize_t)buffer_bytes;
|
||||
});
|
||||
wait_for_irq();
|
||||
status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
|
||||
ASSERT(!(status & ATA_SR_BSY));
|
||||
if (nread < 0)
|
||||
return false; // TODO: -EFAULT
|
||||
}
|
||||
prepare_for_irq();
|
||||
m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_CACHE_FLUSH);
|
||||
wait_for_irq();
|
||||
u8 status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
|
||||
ASSERT(!(status & ATA_SR_BSY));
|
||||
|
||||
return !m_device_error;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <Kernel/Devices/Device.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/Lock.h>
|
||||
#include <Kernel/PCI/Access.h>
|
||||
|
@ -51,8 +50,6 @@
|
|||
|
||||
namespace Kernel {
|
||||
|
||||
class AsyncBlockDeviceRequest;
|
||||
|
||||
struct PhysicalRegionDescriptor {
|
||||
PhysicalAddress offset;
|
||||
u16 size { 0 };
|
||||
|
@ -86,15 +83,13 @@ private:
|
|||
void initialize(bool force_pio);
|
||||
void detect_disks();
|
||||
|
||||
void start_request(AsyncBlockDeviceRequest&, bool, bool);
|
||||
void complete_current_request(AsyncDeviceRequest::RequestResult);
|
||||
void wait_for_irq();
|
||||
bool ata_read_sectors_with_dma(u32, u16, UserOrKernelBuffer&, bool);
|
||||
bool ata_write_sectors_with_dma(u32, u16, const UserOrKernelBuffer&, bool);
|
||||
bool ata_read_sectors(u32, u16, UserOrKernelBuffer&, bool);
|
||||
bool ata_write_sectors(u32, u16, const UserOrKernelBuffer&, bool);
|
||||
|
||||
void ata_read_sectors_with_dma(bool);
|
||||
void ata_read_sectors(bool);
|
||||
bool ata_do_read_sector();
|
||||
void ata_write_sectors_with_dma(bool);
|
||||
void ata_write_sectors(bool);
|
||||
void ata_do_write_sector();
|
||||
inline void prepare_for_irq();
|
||||
|
||||
// Data members
|
||||
u8 m_channel_number { 0 }; // Channel number. 0 = master, 1 = slave
|
||||
|
@ -113,10 +108,5 @@ private:
|
|||
|
||||
RefPtr<PATADiskDevice> m_master;
|
||||
RefPtr<PATADiskDevice> m_slave;
|
||||
|
||||
AsyncBlockDeviceRequest* m_current_request { nullptr };
|
||||
u32 m_current_request_block_index { 0 };
|
||||
bool m_current_request_uses_dma { false };
|
||||
bool m_current_request_flushing_cache { false };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -55,10 +55,22 @@ const char* PATADiskDevice::class_name() const
|
|||
return "PATADiskDevice";
|
||||
}
|
||||
|
||||
void PATADiskDevice::start_request(AsyncBlockDeviceRequest& request)
|
||||
bool PATADiskDevice::read_blocks(unsigned index, u16 count, UserOrKernelBuffer& out)
|
||||
{
|
||||
bool use_dma = !m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource();
|
||||
m_channel.start_request(request, use_dma, is_slave());
|
||||
if (!m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource())
|
||||
return read_sectors_with_dma(index, count, out);
|
||||
return read_sectors(index, count, out);
|
||||
}
|
||||
|
||||
bool PATADiskDevice::write_blocks(unsigned index, u16 count, const UserOrKernelBuffer& data)
|
||||
{
|
||||
if (!m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource())
|
||||
return write_sectors_with_dma(index, count, data);
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
if (!write_sectors(index + i, 1, data.offset(i * 512)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PATADiskDevice::set_drive_geometry(u16 cyls, u16 heads, u16 spt)
|
||||
|
@ -88,19 +100,8 @@ KResultOr<size_t> PATADiskDevice::read(FileDescription&, size_t offset, UserOrKe
|
|||
#endif
|
||||
|
||||
if (whole_blocks > 0) {
|
||||
auto read_request = make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read, index, whole_blocks, outbuf, whole_blocks * block_size());
|
||||
auto result = read_request->wait();
|
||||
if (result.wait_result().was_interrupted())
|
||||
return KResult(-EINTR);
|
||||
switch (result.request_result()) {
|
||||
case AsyncDeviceRequest::Failure:
|
||||
case AsyncDeviceRequest::Cancelled:
|
||||
return KResult(-EIO);
|
||||
case AsyncDeviceRequest::MemoryFault:
|
||||
return KResult(-EFAULT);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!read_blocks(index, whole_blocks, outbuf))
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t pos = whole_blocks * block_size();
|
||||
|
@ -108,21 +109,8 @@ KResultOr<size_t> PATADiskDevice::read(FileDescription&, size_t offset, UserOrKe
|
|||
if (remaining > 0) {
|
||||
auto data = ByteBuffer::create_uninitialized(block_size());
|
||||
auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data.data());
|
||||
auto read_request = make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read, index + whole_blocks, 1, data_buffer, block_size());
|
||||
auto result = read_request->wait();
|
||||
if (result.wait_result().was_interrupted())
|
||||
return KResult(-EINTR);
|
||||
switch (result.request_result()) {
|
||||
case AsyncDeviceRequest::Failure:
|
||||
if (!read_blocks(index + whole_blocks, 1, data_buffer))
|
||||
return pos;
|
||||
case AsyncDeviceRequest::Cancelled:
|
||||
return KResult(-EIO);
|
||||
case AsyncDeviceRequest::MemoryFault:
|
||||
// This should never happen, we're writing to a kernel buffer!
|
||||
ASSERT_NOT_REACHED();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!outbuf.write(data.data(), pos, remaining))
|
||||
return KResult(-EFAULT);
|
||||
}
|
||||
|
@ -155,19 +143,8 @@ KResultOr<size_t> PATADiskDevice::write(FileDescription&, size_t offset, const U
|
|||
#endif
|
||||
|
||||
if (whole_blocks > 0) {
|
||||
auto write_request = make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Write, index, whole_blocks, inbuf, whole_blocks * block_size());
|
||||
auto result = write_request->wait();
|
||||
if (result.wait_result().was_interrupted())
|
||||
return KResult(-EINTR);
|
||||
switch (result.request_result()) {
|
||||
case AsyncDeviceRequest::Failure:
|
||||
case AsyncDeviceRequest::Cancelled:
|
||||
return KResult(-EIO);
|
||||
case AsyncDeviceRequest::MemoryFault:
|
||||
return KResult(-EFAULT);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!write_blocks(index, whole_blocks, inbuf))
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t pos = whole_blocks * block_size();
|
||||
|
@ -178,45 +155,12 @@ KResultOr<size_t> PATADiskDevice::write(FileDescription&, size_t offset, const U
|
|||
if (remaining > 0) {
|
||||
auto data = ByteBuffer::create_zeroed(block_size());
|
||||
auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data.data());
|
||||
|
||||
{
|
||||
auto read_request = make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read, index + whole_blocks, 1, data_buffer, block_size());
|
||||
auto result = read_request->wait();
|
||||
if (result.wait_result().was_interrupted())
|
||||
return KResult(-EINTR);
|
||||
switch (result.request_result()) {
|
||||
case AsyncDeviceRequest::Failure:
|
||||
return pos;
|
||||
case AsyncDeviceRequest::Cancelled:
|
||||
return KResult(-EIO);
|
||||
case AsyncDeviceRequest::MemoryFault:
|
||||
// This should never happen, we're writing to a kernel buffer!
|
||||
ASSERT_NOT_REACHED();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!read_blocks(index + whole_blocks, 1, data_buffer))
|
||||
return pos;
|
||||
if (!inbuf.read(data.data(), pos, remaining))
|
||||
return KResult(-EFAULT);
|
||||
|
||||
{
|
||||
auto write_request = make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Write, index + whole_blocks, 1, data_buffer, block_size());
|
||||
auto result = write_request->wait();
|
||||
if (result.wait_result().was_interrupted())
|
||||
return KResult(-EINTR);
|
||||
switch (result.request_result()) {
|
||||
case AsyncDeviceRequest::Failure:
|
||||
return pos;
|
||||
case AsyncDeviceRequest::Cancelled:
|
||||
return KResult(-EIO);
|
||||
case AsyncDeviceRequest::MemoryFault:
|
||||
// This should never happen, we're writing to a kernel buffer!
|
||||
ASSERT_NOT_REACHED();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!write_blocks(index + whole_blocks, 1, data_buffer))
|
||||
return pos;
|
||||
}
|
||||
|
||||
return pos + remaining;
|
||||
|
@ -227,6 +171,26 @@ bool PATADiskDevice::can_write(const FileDescription&, size_t offset) const
|
|||
return offset < (m_cylinders * m_heads * m_sectors_per_track * block_size());
|
||||
}
|
||||
|
||||
bool PATADiskDevice::read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer& outbuf)
|
||||
{
|
||||
return m_channel.ata_read_sectors_with_dma(lba, count, outbuf, is_slave());
|
||||
}
|
||||
|
||||
bool PATADiskDevice::read_sectors(u32 start_sector, u16 count, UserOrKernelBuffer& outbuf)
|
||||
{
|
||||
return m_channel.ata_read_sectors(start_sector, count, outbuf, is_slave());
|
||||
}
|
||||
|
||||
bool PATADiskDevice::write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer& inbuf)
|
||||
{
|
||||
return m_channel.ata_write_sectors_with_dma(lba, count, inbuf, is_slave());
|
||||
}
|
||||
|
||||
bool PATADiskDevice::write_sectors(u32 start_sector, u16 count, const UserOrKernelBuffer& inbuf)
|
||||
{
|
||||
return m_channel.ata_write_sectors(start_sector, count, inbuf, is_slave());
|
||||
}
|
||||
|
||||
bool PATADiskDevice::is_slave() const
|
||||
{
|
||||
return m_drive_type == DriveType::Slave;
|
||||
|
|
|
@ -54,10 +54,13 @@ public:
|
|||
static NonnullRefPtr<PATADiskDevice> create(PATAChannel&, DriveType, int major, int minor);
|
||||
virtual ~PATADiskDevice() override;
|
||||
|
||||
// ^DiskDevice
|
||||
virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) override;
|
||||
virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) override;
|
||||
|
||||
void set_drive_geometry(u16, u16, u16);
|
||||
|
||||
// ^BlockDevice
|
||||
virtual void start_request(AsyncBlockDeviceRequest&) override;
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override;
|
||||
virtual bool can_read(const FileDescription&, size_t) const override;
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override;
|
||||
|
@ -70,6 +73,11 @@ private:
|
|||
// ^DiskDevice
|
||||
virtual const char* class_name() const override;
|
||||
|
||||
bool wait_for_irq();
|
||||
bool read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer&);
|
||||
bool write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer&);
|
||||
bool read_sectors(u32 lba, u16 count, UserOrKernelBuffer& buffer);
|
||||
bool write_sectors(u32 lba, u16 count, const UserOrKernelBuffer& data);
|
||||
bool is_slave() const;
|
||||
|
||||
Lock m_lock { "IDEDiskDevice" };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue