We want to move this out of the AHCI subsystem into the VM system,
since other parts of the kernel may need to perform scatter-gather IO.
We rename the current VM::ScatterGatherList impl that's used in the
virtio subsystem to VM::ScatterGatherRefList, since its distinguishing
feature from the AHCI scatter-gather list is that it doesn't own its
buffers.
We had some inconsistencies before:
- Sometimes "The", sometimes "the"
- Sometimes trailing ".", sometimes no trailing "."
I picked the most common one (lowecase "the", trailing ".") and applied
it to all copyright headers.
By using the exact same string everywhere we can ensure nothing gets
missed during a global search (and replace), and that these
inconsistencies are not spread any further (as copyright headers are
commonly copied to new files).
The overall design is the same, but we change a few things,
like decreasing the amount of blocking forever loops. The goal
is to ensure the kernel won't hang forever when dealing with
buggy hardware.
Also, we reset the channel when initializing it, just in case the
hardware was in bad state before we start use it.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.
See: https://spdx.dev/resources/use/#identifiers
This was done with the `ambr` search and replace tool.
ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
The first one is for disabling the PS2 controller, the other one is for
disabling physical storage enumeration.
We can't be sure any machine will work with our implementation,
therefore this will help us to test more machines.
We need to do it to let real hardware to put the correct voltages
on the wire.
Apparently my ICH7 machine refused to boot, and was reading lots of
garbage from an unconnected IDE channel. It was fixed after I added a
delay of 20 microseconds. It probably can be reduced, I just took a safe
value and it seems to work correctly without any problems :)
Also handle native and compatibility channel modes together, so if only
one IDE channel was set to work on PCI native mode, we need to handle it
separately, so the other channel continue to operate with the legacy IO
ports and interrupt line.
This is a "quirk" I've observed on a Intel ICH7 test machine. Apparently
we need to select the device (master or slave) before starting to work
with the bus master register.
It's very possible that other machines are requiring this step to happen
before the DMA transfer can occur correctly.
Also, when reading with DMA, we should set the transfer direction before
clearing the interrupt status.
For the sake of completeness, I added a few lines in places that I
deemed it to be reasonable to clear the interrupt status there.
Although unlikely to happen, a user can have an IDE controller that
doesn't support bus master capability. If that's the case, we need to
check for this, and create an IDEChannel (not BMIDEChannel) to allow
IO operations with the controller.
If the user requests to force PIO mode, we just create IDEChannel
objects which are capable of sending PIO commands only.
However, if the user doesn't force PIO mode, we create BMIDEChannel
objects, which are sending DMA commands.
This change is somewhat simplifying the code, so each class is
supporting its type of operation - PIO or DMA. The PATADiskDevice
should not care if DMA is enabled or not.
Later on, we could write an IDEChannel class for UDMA modes,
that are available and documented on Intel specifications for their IDE
controllers.
Technically not supported by the original ATA specification, IDE
hot swapping is still in practice possible, so the only sane way
to start support it is with ref-counting the IDEChannel object so if we
remove a PATADiskDevice, it's not gone with it.
An article about IDE limits states that:
"Hard drives over 8.4 GB are supposed to report their geometry as
16383/16/63. This in effect means that the `geometry' is obsolete, and
the total disk size can no longer be computed from the geometry, but is
found in the LBA capacity field returned by the IDENTIFY command.
Hard drives over 137.4 GB are supposed to report an LBA capacity of
0xfffffff = 268435455 sectors (137438952960 bytes). Now the actual disk
size is found in the new 48-capacity field."
(https://tldp.org/HOWTO/Large-Disk-HOWTO-4.html) which is the main
reason to not support CHS as harddrives with less than 8.4 GB capacity
are completely obsolete.
Another good reason is that virtually any harddrive in the last 20 years
or so, supports LBA mode. Therefore, it's probably OK to just ignore CHS
as it's unlikely to encounter a harddrive that doesn't support LBA.
This is somewhat simplifying the IDE initialization and access code.
Also, we should use the ATAIdentifyBlock structure if possible,
so now we do it instead of using macros to calculate offsets.
With the usage of the ATAIdentifyBlock structure, we now use the
48-bit LBA max count if the drive indicates it supports 48-bit LBA mode.
This reverts commit cfc2f33dcb.
We can't actually change the IRQ line value and expect the device
to work with it (this was my mistake).
That register is R/W so the firmware can figure out IRQ routing and put
the correct value and write it to the Interrupt line register.
As a compromise, if the fimrware decided to set the IRQ line to be 7,
or something else we can't deal with, the user can simply force the code
to work with IRQ 11, with the boot argument "force_ahci_irq_11" being
set to "on".
Instead of polling if the device ended the operation, we can just use
interrupts for signalling about end of IO operation.
In similar way, we use interrupts during device detection.
Also, we use the new Work Queue mechanism introduced by @tomuta to allow
better performance and stability :)
We can't use deferred functions for anything that may require preemption,
such as copying from/to user or accessing the disk. For those purposes
we should use a work queue, which is essentially a kernel thread that
may be preempted or blocked.
These errors are classed as fatal, so we need to recover from them.
Found while trying to debug AHCI boot on VMware Player,
where I got TFES.
From the spec: "Fatal errors (signified by the setting of PxIS.HBFS,
PxIS.HBDS, PxIS.IFS, or PxIS.TFES) will cause the HBA to enter
the ERR:Fatal state"
We were already recovering from IFS.
Instead of blindly resetting every AHCI port, let's just reset only the
controller by default. The user can still request to reset everything
with a new kernel boot argument called ahci_reset_mode which is set
by default to "controller", so the code will only invoke an HBA reset.
This kernel boot argument can be set to 3 different values:
1. "controller" - reset the HBA and skip resetting AHCI ports
2. "none" - don't reset anything, so we rely on the firmware to
initialize the AHCI HBA and ports for us.
3. "complete" - reset the AHCI HBA and ports.