Kernel: Implement AHCI driver
SATA drives can now be used with banan-os. This allows much faster disk access (writing 10 MiB from 30s to 1.5s). This can definitely be optimized but the main slow down is probably the whole disk structure in the os. AHCI drive is now the default when running qemu.
This commit is contained in:
		
							parent
							
								
									d3e5c8e0aa
								
							
						
					
					
						commit
						a2b5e71654
					
				| 
						 | 
				
			
			@ -53,6 +53,8 @@ set(KERNEL_SOURCES
 | 
			
		|||
	kernel/Semaphore.cpp
 | 
			
		||||
	kernel/SpinLock.cpp
 | 
			
		||||
	kernel/SSP.cpp
 | 
			
		||||
	kernel/Storage/ATA/AHCI/Controller.cpp
 | 
			
		||||
	kernel/Storage/ATA/AHCI/Device.cpp
 | 
			
		||||
	kernel/Storage/ATA/ATABus.cpp
 | 
			
		||||
	kernel/Storage/ATA/ATAController.cpp
 | 
			
		||||
	kernel/Storage/ATA/ATADevice.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <BAN/Array.h>
 | 
			
		||||
#include <BAN/RefPtr.h>
 | 
			
		||||
#include <kernel/InterruptController.h>
 | 
			
		||||
#include <kernel/Memory/DMARegion.h>
 | 
			
		||||
#include <kernel/PCI.h>
 | 
			
		||||
#include <kernel/Storage/ATA/AHCI/Definitions.h>
 | 
			
		||||
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	class AHCIController final : public StorageController, public Interruptable
 | 
			
		||||
	{
 | 
			
		||||
		BAN_NON_COPYABLE(AHCIController);
 | 
			
		||||
		BAN_NON_MOVABLE(AHCIController);
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		~AHCIController();
 | 
			
		||||
 | 
			
		||||
		virtual void handle_irq() override;
 | 
			
		||||
 | 
			
		||||
		uint32_t command_slot_count() const { return m_command_slot_count; }
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		AHCIController(PCI::Device& pci_device)
 | 
			
		||||
			: m_pci_device(pci_device)
 | 
			
		||||
		{ }
 | 
			
		||||
		BAN::ErrorOr<void> initialize();
 | 
			
		||||
		BAN::Optional<AHCIPortType> check_port_type(volatile HBAPortMemorySpace&);
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		PCI::Device& m_pci_device;
 | 
			
		||||
		BAN::UniqPtr<PCI::BarRegion> m_abar;
 | 
			
		||||
 | 
			
		||||
		BAN::Array<AHCIDevice*, 32> m_devices;
 | 
			
		||||
 | 
			
		||||
		uint32_t m_command_slot_count { 0 };
 | 
			
		||||
 | 
			
		||||
		friend class ATAController;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,294 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define FIS_TYPE_REGISTER_H2D		0x27
 | 
			
		||||
#define FIS_TYPE_REGISTER_D2H		0x34
 | 
			
		||||
#define FIS_TYPE_DMA_ACT			0x39
 | 
			
		||||
#define FIS_TYPE_DMA_SETUP			0x41
 | 
			
		||||
#define FIS_TYPE_DATA				0x46
 | 
			
		||||
#define FIS_TYPE_BIST				0x58
 | 
			
		||||
#define FIS_TYPE_PIO_SETUP			0x5F
 | 
			
		||||
#define FIS_TYPE_SET_DEVIVE_BITS	0xA1
 | 
			
		||||
 | 
			
		||||
#define SATA_CAP_SUPPORTS64	(1 << 31)
 | 
			
		||||
 | 
			
		||||
#define SATA_GHC_AHCI_ENABLE		(1 << 31)
 | 
			
		||||
#define SATA_GHC_INTERRUPT_ENABLE	(1 << 1)
 | 
			
		||||
 | 
			
		||||
#define	SATA_SIG_ATA	0x00000101
 | 
			
		||||
#define	SATA_SIG_ATAPI	0xEB140101
 | 
			
		||||
#define	SATA_SIG_SEMB	0xC33C0101
 | 
			
		||||
#define	SATA_SIG_PM		0x96690101
 | 
			
		||||
 | 
			
		||||
#define HBA_PORT_IPM_ACTIVE		1
 | 
			
		||||
#define HBA_PORT_DET_PRESENT	3
 | 
			
		||||
 | 
			
		||||
#define HBA_PxCMD_ST	0x0001
 | 
			
		||||
#define HBA_PxCMD_FRE	0x0010
 | 
			
		||||
#define HBA_PxCMD_FR	0x4000
 | 
			
		||||
#define HBA_PxCMD_CR	0x8000
 | 
			
		||||
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	static constexpr uint32_t s_hba_prdt_count { 8 };
 | 
			
		||||
 | 
			
		||||
	struct FISRegisterH2D
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t  fis_type;			// FIS_TYPE_REGISTER_H2D
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  pm_port : 4;		// Port multiplier
 | 
			
		||||
		uint8_t  __reserved0 : 3;
 | 
			
		||||
		uint8_t  c : 1;				// 1: Command, 0: Control
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  command;
 | 
			
		||||
		uint8_t  feature_lo;		// Feature register, 7:0
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  lba0;				// LBA low register, 7:0
 | 
			
		||||
		uint8_t  lba1;				// LBA mid register, 15:8
 | 
			
		||||
		uint8_t  lba2;				// LBA high register, 23:16
 | 
			
		||||
		uint8_t  device;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  lba3;				// LBA register, 31:24
 | 
			
		||||
		uint8_t  lba4;				// LBA register, 39:32
 | 
			
		||||
		uint8_t  lba5;				// LBA register, 47:40
 | 
			
		||||
		uint8_t  feature_hi;		// Feature register, 15:8
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  count_lo;			// Count register, 7:0
 | 
			
		||||
		uint8_t  count_hi;			// Count register, 15:8
 | 
			
		||||
		uint8_t  icc;				// Isochronous command completion
 | 
			
		||||
		uint8_t  control;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  __reserved1[4];
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct FISRegisterD2H
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t  fis_type;			// FIS_TYPE_REGISTER_D2H
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  pm_port : 4;		// Port multiplier
 | 
			
		||||
		uint8_t  __reserved0 : 2;
 | 
			
		||||
		uint8_t  i : 1;				// Interrupt bit
 | 
			
		||||
		uint8_t  __reserved1 : 1;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  status;
 | 
			
		||||
		uint8_t  error;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  lba0;				// LBA low register, 7:0
 | 
			
		||||
		uint8_t  lba1;				// LBA mid register, 15:8
 | 
			
		||||
		uint8_t  lba2;				// LBA high register, 23:16
 | 
			
		||||
		uint8_t  device;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  lba3;				// LBA register, 31:24
 | 
			
		||||
		uint8_t  lba4;				// LBA register, 39:32
 | 
			
		||||
		uint8_t  lba5;				// LBA register, 47:40
 | 
			
		||||
		uint8_t  __reserved2;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  count_lo;			// Count register, 7:0
 | 
			
		||||
		uint8_t  count_hi;			// Count register, 15:8
 | 
			
		||||
		uint8_t  __reserved3[2];
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  __reserved4[4];
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct FISDataBI
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t  fis_type;			// FIS_TYPE_DATA
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  pm_port : 4;		// Port multiplier
 | 
			
		||||
		uint8_t  __reserved0 : 4;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  __reserved1[2];
 | 
			
		||||
 | 
			
		||||
		uint32_t data[0];			// Payload (1 - 2048 dwords)
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct SetDeviceBitsD2H
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t fis_type;		// FIS_TYPE_SET_DEVICE_BITS
 | 
			
		||||
 | 
			
		||||
		uint8_t pm_port : 4;	// Port multiplier
 | 
			
		||||
		uint8_t __reserved0 : 2;
 | 
			
		||||
		uint8_t i : 1;			// Interrupt bit
 | 
			
		||||
		uint8_t n : 1;			// Notification bit
 | 
			
		||||
 | 
			
		||||
		uint8_t status;
 | 
			
		||||
		uint8_t error;
 | 
			
		||||
 | 
			
		||||
		uint32_t __reserved1;
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct PIOSetupD2H
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t  fis_type;			// FIS_TYPE_PIO_SETUP
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  pm_port : 4;		// Port multiplier
 | 
			
		||||
		uint8_t  __reserved0 : 1;
 | 
			
		||||
		uint8_t  d : 1;				// Data transfer direction, 1 - device to host
 | 
			
		||||
		uint8_t  i : 1;				// Interrupt bit
 | 
			
		||||
		uint8_t  __reserved1 : 1;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  status;
 | 
			
		||||
		uint8_t  error;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  lba0;				// LBA low register, 7:0
 | 
			
		||||
		uint8_t  lba1;				// LBA mid register, 15:8
 | 
			
		||||
		uint8_t  lba2;				// LBA high register, 23:16
 | 
			
		||||
		uint8_t  device;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  lba3;				// LBA register, 31:24
 | 
			
		||||
		uint8_t  lba4;				// LBA register, 39:32
 | 
			
		||||
		uint8_t  lba5;				// LBA register, 47:40
 | 
			
		||||
		uint8_t  __reserved2;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  count_lo;			// Count register, 7:0
 | 
			
		||||
		uint8_t  count_hi;			// Count register, 15:8
 | 
			
		||||
		uint8_t  __reserved3;
 | 
			
		||||
		uint8_t  e_status;			// New value of status register
 | 
			
		||||
	
 | 
			
		||||
		uint16_t tc;				// Transfer count
 | 
			
		||||
		uint8_t  __reserved4[2];
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct DMASetupBI
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t  fis_type;				// FIS_TYPE_DMA_SETUP
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  pm_port : 4;			// Port multiplier
 | 
			
		||||
		uint8_t  __reserved0 : 1;
 | 
			
		||||
		uint8_t  d : 1;					// Data transfer direction, 1 - device to host
 | 
			
		||||
		uint8_t  i : 1;					// Interrupt bit
 | 
			
		||||
		uint8_t  a : 1;					// Auto-activate. Specifies if DMA Activate FIS is needed
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  __reserved1[2];
 | 
			
		||||
 | 
			
		||||
		uint64_t dma_buffer_id;			// DMA Buffer Identifier. Used to Identify DMA buffer in host memory.
 | 
			
		||||
										// SATA Spec says host specific and not in Spec. Trying AHCI spec might work.
 | 
			
		||||
 | 
			
		||||
		uint32_t __reserved2;
 | 
			
		||||
 | 
			
		||||
		uint32_t dma_buffer_offset;		// Byte offset into buffer. First 2 bits must be 0
 | 
			
		||||
 | 
			
		||||
		uint32_t dma_transfer_count;	// Number of bytes to transfer. Bit 0 must be 0
 | 
			
		||||
 | 
			
		||||
		uint32_t __reserved3;
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct HBAPortMemorySpace
 | 
			
		||||
	{
 | 
			
		||||
		uint32_t clb;		// command list base address, 1K-byte aligned
 | 
			
		||||
		uint32_t clbu;		// command list base address upper 32 bits
 | 
			
		||||
		uint32_t fb;		// FIS base address, 256-byte aligned
 | 
			
		||||
		uint32_t fbu;		// FIS base address upper 32 bits
 | 
			
		||||
		uint32_t is;		// interrupt status
 | 
			
		||||
		uint32_t ie;		// interrupt enable
 | 
			
		||||
		uint32_t cmd;		// command and status
 | 
			
		||||
		uint32_t __reserved0;
 | 
			
		||||
		uint32_t tfd;		// task file data
 | 
			
		||||
		uint32_t sig;		// signature
 | 
			
		||||
		uint32_t ssts;		// SATA status (SCR0:SStatus)
 | 
			
		||||
		uint32_t sctl;		// SATA control (SCR2:SControl)
 | 
			
		||||
		uint32_t serr;		// SATA error (SCR1:SError)
 | 
			
		||||
		uint32_t sact;		// SATA active (SCR3:SActive)
 | 
			
		||||
		uint32_t ci;		// command issue
 | 
			
		||||
		uint32_t sntf;		// SATA notification (SCR4:SNotification)
 | 
			
		||||
		uint32_t fbs;		// FIS-based switch control
 | 
			
		||||
		uint32_t __reserved1[11];
 | 
			
		||||
		uint32_t vendor[4];
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct HBAGeneralMemorySpace
 | 
			
		||||
	{
 | 
			
		||||
		uint32_t cap;		// Host capability
 | 
			
		||||
		uint32_t ghc;		// Global host control
 | 
			
		||||
		uint32_t is;		// Interrupt status
 | 
			
		||||
		uint32_t pi;		// Port implemented
 | 
			
		||||
		uint32_t vs;		// Version
 | 
			
		||||
		uint32_t ccc_ctl;	// Command completion coalescing control
 | 
			
		||||
		uint32_t ccc_pts;	// Command completion coalescing ports
 | 
			
		||||
		uint32_t em_loc;	// 0x1C, Enclosure management location
 | 
			
		||||
		uint32_t em_ctl;	// 0x20, Enclosure management control
 | 
			
		||||
		uint32_t cap2;		// 0x24, Host capabilities extended
 | 
			
		||||
		uint32_t bohc;		// 0x28, BIOS/OS handoff control and status
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  __reserved0[0xA0-0x2C];
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  vendor[0x100-0xA0];
 | 
			
		||||
	
 | 
			
		||||
		HBAPortMemorySpace ports[0]; // 1 - 32 ports
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct ReceivedFIS
 | 
			
		||||
	{
 | 
			
		||||
		DMASetupBI			dsfis;
 | 
			
		||||
		uint8_t				pad0[4];
 | 
			
		||||
	
 | 
			
		||||
		PIOSetupD2H			psfis;
 | 
			
		||||
		uint8_t				pad1[12];
 | 
			
		||||
	
 | 
			
		||||
		FISRegisterD2H		rfis;
 | 
			
		||||
		uint8_t				pad2[4];
 | 
			
		||||
	
 | 
			
		||||
		SetDeviceBitsD2H	sdbfis;
 | 
			
		||||
	
 | 
			
		||||
		uint8_t				ufis[64];
 | 
			
		||||
	
 | 
			
		||||
		uint8_t				__reserved[0x100-0xA0];
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct HBACommandHeader
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t  cfl : 5;	// Command FIS length in DWORDS, 2 ~ 16
 | 
			
		||||
		uint8_t  a : 1;		// ATAPI
 | 
			
		||||
		uint8_t  w : 1;		// Write, 1: H2D, 0: D2H
 | 
			
		||||
		uint8_t  p : 1;		// Prefetchable
 | 
			
		||||
	
 | 
			
		||||
		uint8_t  r : 1;		// Reset
 | 
			
		||||
		uint8_t  b : 1;		// BIST
 | 
			
		||||
		uint8_t  c : 1;		// Clear busy upon R_OK
 | 
			
		||||
		uint8_t  __reserved0 : 1;
 | 
			
		||||
		uint8_t  pmp : 4;	// Port multiplier port
 | 
			
		||||
	
 | 
			
		||||
		uint16_t prdtl;		// Physical region descriptor table length in entries
 | 
			
		||||
	
 | 
			
		||||
		volatile uint32_t prdbc;	// Physical region descriptor byte count transferred
 | 
			
		||||
	
 | 
			
		||||
		uint32_t ctba;		// Command table descriptor base address
 | 
			
		||||
		uint32_t ctbau;		// Command table descriptor base address upper 32 bits
 | 
			
		||||
	
 | 
			
		||||
		uint32_t __reserved1[4];
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct HBAPRDTEntry
 | 
			
		||||
	{
 | 
			
		||||
		uint32_t dba;			// Data base address
 | 
			
		||||
		uint32_t dbau;			// Data base address upper 32 bits
 | 
			
		||||
		uint32_t __reserved0;
 | 
			
		||||
 | 
			
		||||
		uint32_t dbc : 22;		// Byte count, 4M max
 | 
			
		||||
		uint32_t __reserved1 : 9;
 | 
			
		||||
		uint32_t i : 1;			// Interrupt on completion
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	struct HBACommandTable
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t cfis[64];
 | 
			
		||||
		uint8_t acmd[16];
 | 
			
		||||
		uint8_t __reserved[48];
 | 
			
		||||
		HBAPRDTEntry prdt_entry[s_hba_prdt_count];
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
	enum class AHCIPortType
 | 
			
		||||
	{
 | 
			
		||||
		NONE,
 | 
			
		||||
		SATA,
 | 
			
		||||
		SATAPI,
 | 
			
		||||
		SEMB,
 | 
			
		||||
		PM
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class AHCIController;
 | 
			
		||||
	class AHCIDevice;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <kernel/Semaphore.h>
 | 
			
		||||
#include <kernel/Storage/ATA/AHCI/Definitions.h>
 | 
			
		||||
#include <kernel/Storage/ATA/ATADevice.h>
 | 
			
		||||
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	class AHCIDevice final : public detail::ATABaseDevice
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		static BAN::ErrorOr<BAN::RefPtr<AHCIDevice>> create(BAN::RefPtr<AHCIController>, volatile HBAPortMemorySpace*);
 | 
			
		||||
		~AHCIDevice() = default;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		AHCIDevice(BAN::RefPtr<AHCIController> controller, volatile HBAPortMemorySpace* port)
 | 
			
		||||
			: m_controller(controller)
 | 
			
		||||
			, m_port(port)
 | 
			
		||||
		{ }
 | 
			
		||||
		BAN::ErrorOr<void> initialize();
 | 
			
		||||
		BAN::ErrorOr<void> allocate_buffers();
 | 
			
		||||
		BAN::ErrorOr<void> rebase();
 | 
			
		||||
		BAN::ErrorOr<void> read_identify_data();
 | 
			
		||||
 | 
			
		||||
		virtual BAN::ErrorOr<void> read_sectors_impl(uint64_t lba, uint64_t sector_count, uint8_t* buffer) override;
 | 
			
		||||
		virtual BAN::ErrorOr<void> write_sectors_impl(uint64_t lba, uint64_t sector_count, const uint8_t* buffer) override;
 | 
			
		||||
		BAN::ErrorOr<void> send_command_and_block(uint64_t lba, uint64_t sector_count, Command command);
 | 
			
		||||
 | 
			
		||||
		BAN::Optional<uint32_t> find_free_command_slot();
 | 
			
		||||
 | 
			
		||||
		void handle_irq();
 | 
			
		||||
		void block_until_irq();
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		BAN::RefPtr<AHCIController> m_controller;
 | 
			
		||||
		volatile HBAPortMemorySpace* const m_port;
 | 
			
		||||
		
 | 
			
		||||
		BAN::UniqPtr<DMARegion> m_dma_region;
 | 
			
		||||
		// Intermediate read/write buffer
 | 
			
		||||
		// TODO: can we read straight to user buffer?
 | 
			
		||||
		BAN::UniqPtr<DMARegion> m_data_dma_region;
 | 
			
		||||
 | 
			
		||||
		volatile bool m_has_got_irq { false };
 | 
			
		||||
 | 
			
		||||
		friend class AHCIController;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,11 @@
 | 
			
		|||
#define ATA_STATUS_BSY				0x80
 | 
			
		||||
 | 
			
		||||
#define ATA_COMMAND_READ_SECTORS	0x20
 | 
			
		||||
#define ATA_COMMAND_READ_DMA		0xC8
 | 
			
		||||
#define ATA_COMMAND_READ_DMA_EXT	0x25
 | 
			
		||||
#define ATA_COMMAND_WRITE_SECTORS	0x30
 | 
			
		||||
#define ATA_COMMAND_WRITE_DMA		0xCA
 | 
			
		||||
#define ATA_COMMAND_WRITE_DMA_EXT	0x35
 | 
			
		||||
#define ATA_COMMAND_IDENTIFY_PACKET	0xA1
 | 
			
		||||
#define ATA_COMMAND_CACHE_FLUSH		0xE7
 | 
			
		||||
#define ATA_COMMAND_IDENTIFY		0xEC
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,120 @@
 | 
			
		|||
#include <kernel/Memory/Heap.h>
 | 
			
		||||
#include <kernel/Memory/PageTable.h>
 | 
			
		||||
#include <kernel/Storage/ATA/AHCI/Controller.h>
 | 
			
		||||
#include <kernel/Storage/ATA/AHCI/Definitions.h>
 | 
			
		||||
#include <kernel/Storage/ATA/AHCI/Device.h>
 | 
			
		||||
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> AHCIController::initialize()
 | 
			
		||||
	{
 | 
			
		||||
		m_abar = TRY(m_pci_device.allocate_bar_region(5));
 | 
			
		||||
		if (m_abar->type() != PCI::BarType::MEM)
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("ABAR not MMIO");
 | 
			
		||||
			return BAN::Error::from_errno(EINVAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto& abar_mem = *(volatile HBAGeneralMemorySpace*)m_abar->vaddr();
 | 
			
		||||
		if (!(abar_mem.ghc & SATA_GHC_AHCI_ENABLE))
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("Controller not in AHCI mode");
 | 
			
		||||
			return BAN::Error::from_errno(EINVAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Enable interrupts and bus mastering
 | 
			
		||||
		m_pci_device.enable_bus_mastering();
 | 
			
		||||
		m_pci_device.enable_pin_interrupts();
 | 
			
		||||
		set_irq(TRY(m_pci_device.get_irq()));
 | 
			
		||||
		enable_interrupt();
 | 
			
		||||
		abar_mem.ghc = abar_mem.ghc | SATA_GHC_INTERRUPT_ENABLE;
 | 
			
		||||
 | 
			
		||||
		m_command_slot_count = ((abar_mem.cap >> 8) & 0x1F) + 1;
 | 
			
		||||
 | 
			
		||||
		uint32_t pi = abar_mem.pi;
 | 
			
		||||
		for (uint32_t i = 0; i < 32 && pi; i++, pi >>= 1)
 | 
			
		||||
		{
 | 
			
		||||
			// Verify we don't access abar outside of its bounds
 | 
			
		||||
			if (sizeof(HBAGeneralMemorySpace) + i * sizeof(HBAPortMemorySpace) > m_abar->size())
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			if (!(pi & 1))
 | 
			
		||||
				continue;
 | 
			
		||||
			
 | 
			
		||||
			auto type = check_port_type(abar_mem.ports[i]);
 | 
			
		||||
			if (!type.has_value())
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (type.value() != AHCIPortType::SATA)
 | 
			
		||||
			{
 | 
			
		||||
				dprintln("Non-SATA devices not supported");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			auto device = AHCIDevice::create(this, &abar_mem.ports[i]);
 | 
			
		||||
			if (device.is_error())
 | 
			
		||||
			{
 | 
			
		||||
				dprintln("{}", device.error());
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			m_devices[i] = device.value().ptr();
 | 
			
		||||
			if (auto ret = m_devices[i]->initialize(); ret.is_error())
 | 
			
		||||
			{
 | 
			
		||||
				dprintln("{}", ret.error());
 | 
			
		||||
				m_devices[i] = nullptr;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AHCIController::~AHCIController()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AHCIController::handle_irq()
 | 
			
		||||
	{
 | 
			
		||||
		auto& abar_mem = *(volatile HBAGeneralMemorySpace*)m_abar->vaddr();
 | 
			
		||||
 | 
			
		||||
		uint32_t is = abar_mem.is;
 | 
			
		||||
		for (uint8_t i = 0; i < 32; i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (is & (1 << i))
 | 
			
		||||
			{
 | 
			
		||||
				ASSERT(m_devices[i]);
 | 
			
		||||
				m_devices[i]->handle_irq();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		abar_mem.is = is;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::Optional<AHCIPortType> AHCIController::check_port_type(volatile HBAPortMemorySpace& port)
 | 
			
		||||
	{
 | 
			
		||||
		uint32_t ssts = port.ssts;
 | 
			
		||||
		uint8_t ipm = (ssts >> 8) & 0x0F;
 | 
			
		||||
		uint8_t det = (ssts >> 0) & 0x0F;
 | 
			
		||||
 | 
			
		||||
		if (det != HBA_PORT_DET_PRESENT)
 | 
			
		||||
			return {};
 | 
			
		||||
		if (ipm != HBA_PORT_IPM_ACTIVE)
 | 
			
		||||
			return {};
 | 
			
		||||
 | 
			
		||||
		switch (port.sig)
 | 
			
		||||
		{
 | 
			
		||||
			case SATA_SIG_ATA:
 | 
			
		||||
				return AHCIPortType::SATA;
 | 
			
		||||
			case SATA_SIG_ATAPI:
 | 
			
		||||
				return AHCIPortType::SATAPI;
 | 
			
		||||
			case SATA_SIG_PM:
 | 
			
		||||
				return AHCIPortType::PM;
 | 
			
		||||
			case SATA_SIG_SEMB:
 | 
			
		||||
				return AHCIPortType::SEMB;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,276 @@
 | 
			
		|||
#include <kernel/Storage/ATA/AHCI/Controller.h>
 | 
			
		||||
#include <kernel/Storage/ATA/AHCI/Device.h>
 | 
			
		||||
#include <kernel/Storage/ATA/ATADefinitions.h>
 | 
			
		||||
#include <kernel/Thread.h>
 | 
			
		||||
#include <kernel/Timer/Timer.h>
 | 
			
		||||
 | 
			
		||||
namespace Kernel
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	static void start_cmd(volatile HBAPortMemorySpace* port)
 | 
			
		||||
	{
 | 
			
		||||
		while (port->cmd & HBA_PxCMD_CR)
 | 
			
		||||
			continue;
 | 
			
		||||
		port->cmd = port->cmd | HBA_PxCMD_FRE;
 | 
			
		||||
		port->cmd = port->cmd | HBA_PxCMD_ST; 
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	static void stop_cmd(volatile HBAPortMemorySpace* port)
 | 
			
		||||
	{
 | 
			
		||||
		port->cmd = port->cmd & ~HBA_PxCMD_ST;
 | 
			
		||||
		port->cmd = port->cmd & ~HBA_PxCMD_FRE;
 | 
			
		||||
		while (port->cmd & (HBA_PxCMD_FR | HBA_PxCMD_CR))
 | 
			
		||||
			continue;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<BAN::RefPtr<AHCIDevice>> AHCIDevice::create(BAN::RefPtr<AHCIController> controller, volatile HBAPortMemorySpace* port)
 | 
			
		||||
	{
 | 
			
		||||
		auto* device_ptr = new AHCIDevice(controller, port);
 | 
			
		||||
		if (device_ptr == nullptr)
 | 
			
		||||
			return BAN::Error::from_errno(ENOMEM);
 | 
			
		||||
		return BAN::RefPtr<AHCIDevice>::adopt(device_ptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> AHCIDevice::initialize()
 | 
			
		||||
	{
 | 
			
		||||
		TRY(allocate_buffers());
 | 
			
		||||
		TRY(rebase());
 | 
			
		||||
 | 
			
		||||
		// enable interrupts
 | 
			
		||||
		m_port->ie = 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
		TRY(read_identify_data());
 | 
			
		||||
		TRY(detail::ATABaseDevice::initialize({ (const uint16_t*)m_data_dma_region->vaddr(), m_data_dma_region->size() }));
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> AHCIDevice::allocate_buffers()
 | 
			
		||||
	{
 | 
			
		||||
		uint32_t command_slot_count = m_controller->command_slot_count();
 | 
			
		||||
		size_t needed_bytes = (sizeof(HBACommandHeader) + sizeof(HBACommandTable)) * command_slot_count + sizeof(ReceivedFIS);
 | 
			
		||||
 | 
			
		||||
		m_dma_region = TRY(DMARegion::create(needed_bytes));
 | 
			
		||||
		memset((void*)m_dma_region->vaddr(), 0x00, m_dma_region->size());
 | 
			
		||||
 | 
			
		||||
		m_data_dma_region = TRY(DMARegion::create(PAGE_SIZE));
 | 
			
		||||
		memset((void*)m_data_dma_region->vaddr(), 0x00, m_data_dma_region->size());
 | 
			
		||||
		
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> AHCIDevice::rebase()
 | 
			
		||||
	{
 | 
			
		||||
		ASSERT(m_dma_region);
 | 
			
		||||
 | 
			
		||||
		uint32_t command_slot_count = m_controller->command_slot_count();
 | 
			
		||||
 | 
			
		||||
		stop_cmd(m_port);
 | 
			
		||||
 | 
			
		||||
		paddr_t fis_paddr = m_dma_region->paddr();
 | 
			
		||||
		m_port->fb = fis_paddr & 0xFFFFFFFF;
 | 
			
		||||
		m_port->fbu = fis_paddr >> 32;
 | 
			
		||||
 | 
			
		||||
		paddr_t command_list_paddr = fis_paddr + sizeof(ReceivedFIS);
 | 
			
		||||
		m_port->clb = command_list_paddr & 0xFFFFFFFF;
 | 
			
		||||
		m_port->clbu = command_list_paddr >> 32;
 | 
			
		||||
 | 
			
		||||
		auto* command_headers = (HBACommandHeader*)m_dma_region->paddr_to_vaddr(command_list_paddr);
 | 
			
		||||
		paddr_t command_table_paddr = command_list_paddr + command_slot_count * sizeof(HBACommandHeader);
 | 
			
		||||
		for (uint32_t i = 0; i < command_slot_count; i++)
 | 
			
		||||
		{
 | 
			
		||||
			uint64_t command_table_entry_paddr = command_table_paddr + i * sizeof(HBACommandTable);
 | 
			
		||||
			command_headers[i].prdtl = s_hba_prdt_count;
 | 
			
		||||
			command_headers[i].ctba = command_table_entry_paddr & 0xFFFFFFFF;
 | 
			
		||||
			command_headers[i].ctbau = command_table_entry_paddr >> 32;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		start_cmd(m_port);
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> AHCIDevice::read_identify_data()
 | 
			
		||||
	{
 | 
			
		||||
		ASSERT(m_data_dma_region);
 | 
			
		||||
 | 
			
		||||
		m_port->is = ~(uint32_t)0;
 | 
			
		||||
 | 
			
		||||
		auto slot = find_free_command_slot();
 | 
			
		||||
		ASSERT(slot.has_value());
 | 
			
		||||
 | 
			
		||||
		auto& command_header = ((HBACommandHeader*)m_dma_region->paddr_to_vaddr(m_port->clb))[slot.value()];
 | 
			
		||||
		command_header.cfl = sizeof(FISRegisterH2D) / sizeof(uint32_t);
 | 
			
		||||
		command_header.w = 0;
 | 
			
		||||
		command_header.prdtl = 1;
 | 
			
		||||
 | 
			
		||||
		auto& command_table = *(HBACommandTable*)m_dma_region->paddr_to_vaddr(command_header.ctba);
 | 
			
		||||
		memset(&command_table, 0x00, sizeof(HBACommandTable));
 | 
			
		||||
		command_table.prdt_entry[0].dba = m_data_dma_region->paddr();
 | 
			
		||||
		command_table.prdt_entry[0].dbc = 511;
 | 
			
		||||
		command_table.prdt_entry[0].i = 1;
 | 
			
		||||
 | 
			
		||||
		auto& command = *(FISRegisterH2D*)command_table.cfis;
 | 
			
		||||
		command.fis_type = FIS_TYPE_REGISTER_H2D;
 | 
			
		||||
		command.c = 1;
 | 
			
		||||
		command.command = ATA_COMMAND_IDENTIFY;
 | 
			
		||||
 | 
			
		||||
		while (m_port->tfd & (ATA_STATUS_BSY | ATA_STATUS_DRQ))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		m_port->ci = 1 << slot.value();
 | 
			
		||||
 | 
			
		||||
		// FIXME: timeout
 | 
			
		||||
		do { block_until_irq(); } while (m_port->ci & (1 << slot.value()));
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void print_error(uint16_t error)
 | 
			
		||||
	{
 | 
			
		||||
		dprintln("Disk error:");
 | 
			
		||||
		if (error & (1 << 11))
 | 
			
		||||
			dprintln("  Internal Error");
 | 
			
		||||
		if (error & (1 << 10))
 | 
			
		||||
			dprintln("  Protocol Error");
 | 
			
		||||
		if (error & (1 << 9))
 | 
			
		||||
			dprintln("  Persistent Communication or Data Integrity Error");
 | 
			
		||||
		if (error & (1 << 8))
 | 
			
		||||
			dprintln("  Transient Data Integrity Error");
 | 
			
		||||
		if (error & (1 << 1))
 | 
			
		||||
			dprintln("  Recovered Communications Error");
 | 
			
		||||
		if (error & (1 << 0))
 | 
			
		||||
			dprintln("  Recovered Data Integrity Error");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AHCIDevice::handle_irq()
 | 
			
		||||
	{
 | 
			
		||||
		ASSERT(!m_has_got_irq);
 | 
			
		||||
		uint16_t err = m_port->serr & 0xFFFF;
 | 
			
		||||
		if (err)
 | 
			
		||||
			print_error(err);
 | 
			
		||||
		m_has_got_irq = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AHCIDevice::block_until_irq()
 | 
			
		||||
	{
 | 
			
		||||
		while (!__sync_bool_compare_and_swap(&m_has_got_irq, true, false))
 | 
			
		||||
			__builtin_ia32_pause();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> AHCIDevice::read_sectors_impl(uint64_t lba, uint64_t sector_count, uint8_t* buffer)
 | 
			
		||||
	{
 | 
			
		||||
		const size_t sectors_per_page = PAGE_SIZE / sector_size();
 | 
			
		||||
		for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page)
 | 
			
		||||
		{
 | 
			
		||||
			uint64_t to_read = BAN::Math::min<uint64_t>(sector_count - sector_off, sectors_per_page);
 | 
			
		||||
 | 
			
		||||
			TRY(send_command_and_block(lba + sector_off, to_read, Command::Read));
 | 
			
		||||
			memcpy(buffer + sector_off * sector_size(), (void*)m_data_dma_region->vaddr(), to_read * sector_size());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> AHCIDevice::write_sectors_impl(uint64_t lba, uint64_t sector_count, const uint8_t* buffer)
 | 
			
		||||
	{
 | 
			
		||||
		const size_t sectors_per_page = PAGE_SIZE / sector_size();
 | 
			
		||||
		for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page)
 | 
			
		||||
		{
 | 
			
		||||
			uint64_t to_read = BAN::Math::min<uint64_t>(sector_count - sector_off, sectors_per_page);
 | 
			
		||||
 | 
			
		||||
			memcpy((void*)m_data_dma_region->vaddr(), buffer + sector_off * sector_size(), to_read * sector_size());
 | 
			
		||||
			TRY(send_command_and_block(lba + sector_off, to_read, Command::Write));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> AHCIDevice::send_command_and_block(uint64_t lba, uint64_t sector_count, Command command)
 | 
			
		||||
	{
 | 
			
		||||
		ASSERT(m_dma_region);
 | 
			
		||||
		ASSERT(m_data_dma_region);
 | 
			
		||||
		ASSERT(0 < sector_count && sector_count <= 0xFFFF + 1);
 | 
			
		||||
		
 | 
			
		||||
		ASSERT(sector_count * sector_size() <= m_data_dma_region->size());
 | 
			
		||||
 | 
			
		||||
		m_port->is = ~(uint32_t)0;
 | 
			
		||||
 | 
			
		||||
		auto slot = find_free_command_slot();
 | 
			
		||||
		ASSERT(slot.has_value());
 | 
			
		||||
		
 | 
			
		||||
		auto& command_header = ((HBACommandHeader*)m_dma_region->paddr_to_vaddr(m_port->clb))[slot.value()];
 | 
			
		||||
		command_header.cfl = sizeof(FISRegisterH2D) / sizeof(uint32_t);
 | 
			
		||||
		command_header.prdtl = 1;
 | 
			
		||||
		switch (command)
 | 
			
		||||
		{
 | 
			
		||||
			case Command::Read:
 | 
			
		||||
				command_header.w = 0;
 | 
			
		||||
				break;
 | 
			
		||||
			case Command::Write:
 | 
			
		||||
				command_header.w = 1;
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				ASSERT_NOT_REACHED();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto& command_table = *(HBACommandTable*)m_dma_region->paddr_to_vaddr(command_header.ctba);
 | 
			
		||||
		memset(&command_table, 0x00, sizeof(HBACommandTable));
 | 
			
		||||
		command_table.prdt_entry[0].dba = m_data_dma_region->paddr() & 0xFFFFFFFF;
 | 
			
		||||
		command_table.prdt_entry[0].dbau = m_data_dma_region->paddr() >> 32;
 | 
			
		||||
		command_table.prdt_entry[0].dbc = sector_count * sector_size() - 1;
 | 
			
		||||
		command_table.prdt_entry[0].i = 1;
 | 
			
		||||
 | 
			
		||||
		auto& fis_command = *(FISRegisterH2D*)command_table.cfis;
 | 
			
		||||
		memset(&fis_command, 0x00, sizeof(FISRegisterH2D));
 | 
			
		||||
		fis_command.fis_type = FIS_TYPE_REGISTER_H2D;
 | 
			
		||||
		fis_command.c = 1;
 | 
			
		||||
 | 
			
		||||
		bool need_extended = lba >= (1 << 28) || sector_count > 0xFF;
 | 
			
		||||
		ASSERT (!need_extended || (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED));
 | 
			
		||||
 | 
			
		||||
		switch (command)
 | 
			
		||||
		{
 | 
			
		||||
			case Command::Read:
 | 
			
		||||
				fis_command.command = need_extended ? ATA_COMMAND_READ_DMA_EXT : ATA_COMMAND_READ_DMA;
 | 
			
		||||
				break;
 | 
			
		||||
			case Command::Write:
 | 
			
		||||
				fis_command.command = need_extended ? ATA_COMMAND_WRITE_DMA_EXT : ATA_COMMAND_WRITE_DMA;
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				ASSERT_NOT_REACHED();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fis_command.lba0 = (lba >>  0) & 0xFF;
 | 
			
		||||
		fis_command.lba1 = (lba >>  8) & 0xFF;
 | 
			
		||||
		fis_command.lba2 = (lba >> 16) & 0xFF;
 | 
			
		||||
		fis_command.device = 1 << 6;	// LBA mode
 | 
			
		||||
	
 | 
			
		||||
		fis_command.lba3 = (lba >> 24) & 0xFF;
 | 
			
		||||
		fis_command.lba4 = (lba >> 32) & 0xFF;
 | 
			
		||||
		fis_command.lba5 = (lba >> 40) & 0xFF;
 | 
			
		||||
	
 | 
			
		||||
		fis_command.count_lo = (sector_count >> 0) & 0xFF;
 | 
			
		||||
		fis_command.count_hi = (sector_count >> 8) & 0xFF;
 | 
			
		||||
 | 
			
		||||
		while (m_port->tfd & (ATA_STATUS_BSY | ATA_STATUS_DRQ))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		m_port->ci = 1 << slot.value();
 | 
			
		||||
 | 
			
		||||
		// FIXME: timeout
 | 
			
		||||
		do { block_until_irq(); } while (m_port->ci & (1 << slot.value()));
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::Optional<uint32_t> AHCIDevice::find_free_command_slot()
 | 
			
		||||
	{
 | 
			
		||||
		uint32_t slots = m_port->sact | m_port->ci;
 | 
			
		||||
		for (uint32_t i = 0; i < m_controller->command_slot_count(); i++, slots >>= 1)
 | 
			
		||||
			if (!(slots & 1))
 | 
			
		||||
				return i;
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#include <kernel/FS/DevFS/FileSystem.h>
 | 
			
		||||
#include <kernel/Storage/ATA/AHCI/Controller.h>
 | 
			
		||||
#include <kernel/Storage/ATA/ATABus.h>
 | 
			
		||||
#include <kernel/Storage/ATA/ATAController.h>
 | 
			
		||||
#include <kernel/Storage/ATA/ATADefinitions.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -20,8 +21,8 @@ namespace Kernel
 | 
			
		|||
				dwarnln("unsupported DMA ATA Controller");
 | 
			
		||||
				return BAN::Error::from_errno(ENOTSUP);
 | 
			
		||||
			case 0x06:
 | 
			
		||||
				dwarnln("unsupported SATA Controller");
 | 
			
		||||
				return BAN::Error::from_errno(ENOTSUP);
 | 
			
		||||
				controller_ptr = new AHCIController(pci_device);
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				ASSERT_NOT_REACHED();
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								qemu.sh
								
								
								
								
							
							
						
						
									
										12
									
								
								qemu.sh
								
								
								
								
							| 
						 | 
				
			
			@ -1,8 +1,10 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
qemu-system-$BANAN_ARCH										\
 | 
			
		||||
	-m 128													\
 | 
			
		||||
	-smp 2													\
 | 
			
		||||
	-drive format=raw,media=disk,file=${DISK_IMAGE_PATH}	\
 | 
			
		||||
	$@														\
 | 
			
		||||
qemu-system-$BANAN_ARCH											\
 | 
			
		||||
	-m 128														\
 | 
			
		||||
	-smp 2														\
 | 
			
		||||
	-drive format=raw,id=disk,file=${DISK_IMAGE_PATH},if=none	\
 | 
			
		||||
	-device ahci,id=ahci										\
 | 
			
		||||
	-device ide-hd,drive=disk,bus=ahci.0						\
 | 
			
		||||
	$@															\
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue