From: LeoLiu-oc LeoLiu-oc@zhaoxin.com
zhaoxin inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I40QDN CVE: NA
----------------------------------------------------------------
The ahci spec mentions that when PxCMD.PSC/SSC is cleared to '0', the PxSCTL.LPM field in each port must be programmed to disallow device initiated Partial/Slumber requests.
Signed-off-by: LeoLiu-oc LeoLiu-oc@zhaoxin.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Acked-by: Jackie Liu liuyun01@kylinos.cn Reviewed-by: Jason Yan yanaijie@huawei.com --- drivers/ata/libata-sata.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index c16423e44525..5a722c64b7dd 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -8,6 +8,7 @@ */
#include <linux/kernel.h> +#include <linux/pci.h> #include <linux/module.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -368,6 +369,10 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, bool spm_wakeup) { struct ata_eh_context *ehc = &link->eh_context; + struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL; + struct device *dev = ap ? ap->host->dev : NULL; + struct pci_dev *pdev = (!dev || !dev_is_pci(dev)) ? NULL : to_pci_dev(dev); + bool woken_up = false; u32 scontrol; int rc; @@ -394,10 +399,21 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, case ATA_LPM_MED_POWER_WITH_DIPM: case ATA_LPM_MIN_POWER_WITH_PARTIAL: case ATA_LPM_MIN_POWER: - if (ata_link_nr_enabled(link) > 0) + if (ata_link_nr_enabled(link) > 0) { /* no restrictions on LPM transitions */ scontrol &= ~(0x7 << 8); - else { + + /* if controller does not support partial, then disallows it, + * the same for slumber + */ + if (pdev && pdev->vendor == PCI_VENDOR_ID_ZHAOXIN) { + if (!(link->ap->host->flags & ATA_HOST_PART)) + scontrol |= (0x1 << 8); + + if (!(link->ap->host->flags & ATA_HOST_SSC)) + scontrol |= (0x2 << 8); + } + } else { /* empty port, power off */ scontrol &= ~0xf; scontrol |= (0x1 << 2);