Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/iommu/amd/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 6 kB image not shown  

Quelle  ppr.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2023 Advanced Micro Devices, Inc.
 */


#define pr_fmt(fmt)     "AMD-Vi: " fmt
#define dev_fmt(fmt)    pr_fmt(fmt)

#include <linux/amd-iommu.h>
#include <linux/delay.h>
#include <linux/mmu_notifier.h>

#include <asm/iommu.h>

#include "amd_iommu.h"
#include "amd_iommu_types.h"

#include "../iommu-pages.h"

int __init amd_iommu_alloc_ppr_log(struct amd_iommu *iommu)
{
 iommu->ppr_log = iommu_alloc_4k_pages(iommu, GFP_KERNEL | __GFP_ZERO,
           PPR_LOG_SIZE);
 return iommu->ppr_log ? 0 : -ENOMEM;
}

void amd_iommu_enable_ppr_log(struct amd_iommu *iommu)
{
 u64 entry;

 if (iommu->ppr_log == NULL)
  return;

 iommu_feature_enable(iommu, CONTROL_PPR_EN);

 entry = iommu_virt_to_phys(iommu->ppr_log) | PPR_LOG_SIZE_512;

 memcpy_toio(iommu->mmio_base + MMIO_PPR_LOG_OFFSET,
      &entry, sizeof(entry));

 /* set head and tail to zero manually */
 writel(0x00, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
 writel(0x00, iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);

 iommu_feature_enable(iommu, CONTROL_PPRINT_EN);
 iommu_feature_enable(iommu, CONTROL_PPRLOG_EN);
}

void __init amd_iommu_free_ppr_log(struct amd_iommu *iommu)
{
 iommu_free_pages(iommu->ppr_log);
}

/*
 * This function restarts ppr logging in case the IOMMU experienced
 * PPR log overflow.
 */

void amd_iommu_restart_ppr_log(struct amd_iommu *iommu)
{
 amd_iommu_restart_log(iommu, "PPR", CONTROL_PPRINT_EN,
         CONTROL_PPRLOG_EN, MMIO_STATUS_PPR_RUN_MASK,
         MMIO_STATUS_PPR_OVERFLOW_MASK);
}

static inline u32 ppr_flag_to_fault_perm(u16 flag)
{
 int perm = 0;

 if (flag & PPR_FLAG_READ)
  perm |= IOMMU_FAULT_PERM_READ;
 if (flag & PPR_FLAG_WRITE)
  perm |= IOMMU_FAULT_PERM_WRITE;
 if (flag & PPR_FLAG_EXEC)
  perm |= IOMMU_FAULT_PERM_EXEC;
 if (!(flag & PPR_FLAG_US))
  perm |= IOMMU_FAULT_PERM_PRIV;

 return perm;
}

static bool ppr_is_valid(struct amd_iommu *iommu, u64 *raw)
{
 struct device *dev = iommu->iommu.dev;
 u16 devid = PPR_DEVID(raw[0]);

 if (!(PPR_FLAGS(raw[0]) & PPR_FLAG_GN)) {
  dev_dbg(dev, "PPR logged [Request ignored due to GN=0 (device=%04x:%02x:%02x.%x "
   "pasid=0x%05llx address=0x%llx flags=0x%04llx tag=0x%03llx]\n",
   iommu->pci_seg->id, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
   PPR_PASID(raw[0]), raw[1], PPR_FLAGS(raw[0]), PPR_TAG(raw[0]));
  return false;
 }

 if (PPR_FLAGS(raw[0]) & PPR_FLAG_RVSD) {
  dev_dbg(dev, "PPR logged [Invalid request format (device=%04x:%02x:%02x.%x "
   "pasid=0x%05llx address=0x%llx flags=0x%04llx tag=0x%03llx]\n",
   iommu->pci_seg->id, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
   PPR_PASID(raw[0]), raw[1], PPR_FLAGS(raw[0]), PPR_TAG(raw[0]));
  return false;
 }

 return true;
}

static void iommu_call_iopf_notifier(struct amd_iommu *iommu, u64 *raw)
{
 struct iommu_dev_data *dev_data;
 struct iopf_fault event;
 struct pci_dev *pdev;
 u16 devid = PPR_DEVID(raw[0]);

 if (PPR_REQ_TYPE(raw[0]) != PPR_REQ_FAULT) {
  pr_info_ratelimited("Unknown PPR request received\n");
  return;
 }

 pdev = pci_get_domain_bus_and_slot(iommu->pci_seg->id,
        PCI_BUS_NUM(devid), devid & 0xff);
 if (!pdev)
  return;

 if (!ppr_is_valid(iommu, raw))
  goto out;

 memset(&event, 0, sizeof(struct iopf_fault));

 event.fault.type = IOMMU_FAULT_PAGE_REQ;
 event.fault.prm.perm = ppr_flag_to_fault_perm(PPR_FLAGS(raw[0]));
 event.fault.prm.addr = (u64)(raw[1] & PAGE_MASK);
 event.fault.prm.pasid = PPR_PASID(raw[0]);
 event.fault.prm.grpid = PPR_TAG(raw[0]) & 0x1FF;

 /*
 * PASID zero is used for requests from the I/O device without
 * a PASID
 */

 dev_data = dev_iommu_priv_get(&pdev->dev);
 if (event.fault.prm.pasid == 0 ||
     event.fault.prm.pasid >= dev_data->max_pasids) {
  pr_info_ratelimited("Invalid PASID : 0x%x, device : 0x%x\n",
        event.fault.prm.pasid, pdev->dev.id);
  goto out;
 }

 event.fault.prm.flags |= IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID;
 event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
 if (PPR_TAG(raw[0]) & 0x200)
  event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;

 /* Submit event */
 iommu_report_device_fault(&pdev->dev, &event);

 return;

out:
 /* Nobody cared, abort */
 amd_iommu_complete_ppr(&pdev->dev, PPR_PASID(raw[0]),
          IOMMU_PAGE_RESP_FAILURE,
          PPR_TAG(raw[0]) & 0x1FF);
}

void amd_iommu_poll_ppr_log(struct amd_iommu *iommu)
{
 u32 head, tail;

 if (iommu->ppr_log == NULL)
  return;

 head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
 tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);

 while (head != tail) {
  volatile u64 *raw;
  u64 entry[2];
  int i;

  raw = (u64 *)(iommu->ppr_log + head);

  /*
 * Hardware bug: Interrupt may arrive before the entry is
 * written to memory. If this happens we need to wait for the
 * entry to arrive.
 */

  for (i = 0; i < LOOP_TIMEOUT; ++i) {
   if (PPR_REQ_TYPE(raw[0]) != 0)
    break;
   udelay(1);
  }

  /* Avoid memcpy function-call overhead */
  entry[0] = raw[0];
  entry[1] = raw[1];

  /*
 * To detect the hardware errata 733 we need to clear the
 * entry back to zero. This issue does not exist on SNP
 * enabled system. Also this buffer is not writeable on
 * SNP enabled system.
 */

  if (!amd_iommu_snp_en)
   raw[0] = raw[1] = 0UL;

  /* Update head pointer of hardware ring-buffer */
  head = (head + PPR_ENTRY_SIZE) % PPR_LOG_SIZE;
  writel(head, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);

  /* Handle PPR entry */
  iommu_call_iopf_notifier(iommu, entry);
 }
}

/**************************************************************
 *
 * IOPF handling stuff
 */


/* Setup per-IOMMU IOPF queue if not exist. */
int amd_iommu_iopf_init(struct amd_iommu *iommu)
{
 int ret = 0;

 if (iommu->iopf_queue)
  return ret;

 snprintf(iommu->iopfq_name, sizeof(iommu->iopfq_name), "amdvi-%#x",
   PCI_SEG_DEVID_TO_SBDF(iommu->pci_seg->id, iommu->devid));

 iommu->iopf_queue = iopf_queue_alloc(iommu->iopfq_name);
 if (!iommu->iopf_queue)
  ret = -ENOMEM;

 return ret;
}

/* Destroy per-IOMMU IOPF queue if no longer needed. */
void amd_iommu_iopf_uninit(struct amd_iommu *iommu)
{
 iopf_queue_free(iommu->iopf_queue);
 iommu->iopf_queue = NULL;
}

void amd_iommu_page_response(struct device *dev, struct iopf_fault *evt,
        struct iommu_page_response *resp)
{
 amd_iommu_complete_ppr(dev, resp->pasid, resp->code, resp->grpid);
}

int amd_iommu_iopf_add_device(struct amd_iommu *iommu,
         struct iommu_dev_data *dev_data)
{
 int ret = 0;

 if (!dev_data->pri_enabled)
  return ret;

 if (!iommu->iopf_queue)
  return -EINVAL;

 ret = iopf_queue_add_device(iommu->iopf_queue, dev_data->dev);
 if (ret)
  return ret;

 dev_data->ppr = true;
 return 0;
}

/* Its assumed that caller has verified that device was added to iopf queue */
void amd_iommu_iopf_remove_device(struct amd_iommu *iommu,
      struct iommu_dev_data *dev_data)
{
 iopf_queue_remove_device(iommu->iopf_queue, dev_data->dev);
 dev_data->ppr = false;
}

Messung V0.5
C=95 H=97 G=95

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.