1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:48:11 +00:00
serenity/Kernel/Storage/StorageDevice.h
Liav A 4744ccbff0 Kernel/Storage: Add LUN address to each StorageDevice
LUN address is essentially how people used to address SCSI devices back
in the day we had these devices more in use. However, SCSI was taken as
an abstraction layer for many Unix and Unix-like systems, so it still
common to see LUN addresses in use. In Serenity, we don't really provide
such abstraction layer, and therefore until now, we didn't use LUNs too.
However (again), this changes, as we want to let users to address their
devices under SysFS easily. LUNs make sense in that regard, because they
can be easily adapted to different interfaces besides SCSI.
For example, for legacy ATA hard drive being connected to the first IDE
controller which was enumerated on the PCI bus, and then to the primary
channel as slave device, the LUN address would be 0:0:1.

To make this happen, we add unique ID number to each StorageController,
which increments by 1 for each new instance of StorageController. Then,
we adapt the ATA and NVMe devices to use these numbers and generate LUN
in the construction time.
2022-07-15 12:29:23 +02:00

106 lines
4.2 KiB
C++

/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/IntrusiveList.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Locking/Mutex.h>
#include <Kernel/Storage/Partition/DiskPartition.h>
#include <Kernel/Storage/StorageController.h>
namespace Kernel {
class StorageDevice : public BlockDevice {
friend class StorageManagement;
public:
// Note: this attribute describes the internal command set of a Storage device.
// For example, an ordinary harddrive utilizes the ATA command set, while
// an ATAPI device (e.g. Optical drive) that is connected to the ATA bus,
// is actually using SCSI commands (packets) encapsulated inside an ATA command.
// The IDE controller code being aware of the possibility of ATAPI devices attached
// to the ATA bus, will check whether the Command set is ATA or SCSI and will act
// accordingly.
enum class CommandSet {
PlainMemory,
SCSI,
ATA,
NVMe,
};
// Note: this attribute describes the interface type of a Storage device.
// For example, an ordinary harddrive utilizes the ATA command set, while
// an ATAPI device (e.g. Optical drive) that is connected to the ATA bus,
// is actually using SCSI commands (packets) encapsulated inside an ATA command.
// Therefore, an ATAPI device is still using the ATA interface.
enum class InterfaceType {
PlainMemory,
SCSI,
ATA,
NVMe,
};
// Note: The most reliable way to address this device from userspace interfaces,
// such as SysFS, is to have one way to enumerate everything in the eyes of userspace.
// Therefore, SCSI LUN (logical unit number) addressing seem to be the most generic way to do this.
// For example, on a legacy ATA instance, one might connect an harddrive to the second IDE controller,
// to the Primary channel as a slave device, which translates to LUN 1:0:1.
// On NVMe, for example, connecting a second PCIe NVMe storage device as a sole NVMe namespace translates
// to LUN 1:0:0.
// TODO: LUNs are also useful also when specifying the boot drive on boot. Consider doing that.
struct LUNAddress {
u32 controller_id;
u32 target_id;
u32 disk_id;
};
public:
virtual u64 max_addressable_block() const { return m_max_addressable_block; }
// ^BlockDevice
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override;
virtual bool can_read(OpenFileDescription const&, u64) const override;
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override;
virtual bool can_write(OpenFileDescription const&, u64) const override;
virtual void prepare_for_unplug() { m_partitions.clear(); }
// FIXME: Remove this method after figuring out another scheme for naming.
StringView early_storage_name() const;
NonnullRefPtrVector<DiskPartition> const& partitions() const { return m_partitions; }
void add_partition(NonnullRefPtr<DiskPartition> disk_partition) { MUST(m_partitions.try_append(disk_partition)); }
LUNAddress const& logical_unit_number_address() const { return m_logical_unit_number_address; }
virtual CommandSet command_set() const = 0;
StringView interface_type_to_string_view() const;
StringView command_set_to_string_view() const;
// ^File
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) final;
protected:
StorageDevice(LUNAddress, MajorNumber, MinorNumber, size_t, u64, NonnullOwnPtr<KString>);
// ^DiskDevice
virtual StringView class_name() const override;
private:
virtual InterfaceType interface_type() const = 0;
mutable IntrusiveListNode<StorageDevice, RefPtr<StorageDevice>> m_list_node;
NonnullRefPtrVector<DiskPartition> m_partitions;
// FIXME: Remove this method after figuring out another scheme for naming.
NonnullOwnPtr<KString> m_early_storage_device_name;
LUNAddress const m_logical_unit_number_address;
u64 m_max_addressable_block { 0 };
size_t m_blocks_per_page { 0 };
};
}