func: static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
  
                                  AHCICmdHdr *cmd, int64_t limit, int32_t offset)
  
  {
  
      uint16_t opts = le16_to_cpu(cmd->opts);
  
      uint16_t prdtl = le16_to_cpu(cmd->prdtl);
  
      uint64_t cfis_addr = le64_to_cpu(cmd->tbl_addr);
  
      uint64_t prdt_addr = cfis_addr + 0x80;
  
      dma_addr_t prdt_len = (prdtl * sizeof(AHCI_SG));
  
      dma_addr_t real_prdt_len = prdt_len;
  
      uint8_t *prdt;
  
      int i;
  
      int r = 0;
  
      uint64_t sum = 0;
  
      int off_idx = -1;
  
      int64_t off_pos = -1;
  
      int tbl_entry_size;
  
      IDEBus *bus = &ad->port;
  
      BusState *qbus = BUS(bus);
  
  
  
      /*
  
       * Note: AHCI PRDT can describe up to 256GiB. SATA/ATA only support
  
       * transactions of up to 32MiB as of ATA8-ACS3 rev 1b, assuming a
  
       * 512 byte sector size. We limit the PRDT in this implementation to
  
       * a reasonably large 2GiB, which can accommodate the maximum transfer
  
       * request for sector sizes up to 32K.
  
       */
  
  
  
      if (!prdtl) {
  
          DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
  
          return -1;
  
      }
  
  
  
      /* map PRDT */
  
      if (!(prdt = dma_memory_map(ad->hba->as, prdt_addr, &prdt_len,
  
                                  DMA_DIRECTION_TO_DEVICE))){
  
          DPRINTF(ad->port_no, "map failed\n");
  
          return -1;
  
      }
  
  
  
      if (prdt_len < real_prdt_len) {
  
          DPRINTF(ad->port_no, "mapped less than expected\n");
  
          r = -1;
  
          goto out;
  
      }
  
  
  
      /* Get entries in the PRDT, init a qemu sglist accordingly */
  
      if (prdtl > 0) {
  
          AHCI_SG *tbl = (AHCI_SG *)prdt;
  
          sum = 0;
  
          for (i = 0; i < prdtl; i++) {
  
              tbl_entry_size = prdt_tbl_entry_size(&tbl[i]);
  
              if (offset < (sum + tbl_entry_size)) {
  
                  off_idx = i;
  
                  off_pos = offset - sum;
  
                  break;
  
              }
  
              sum += tbl_entry_size;
  
          }
  
          if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) {
  
              DPRINTF(ad->port_no, "%s: Incorrect offset! "
  
                              "off_idx: %d, off_pos: %"PRId64"\n",
  
                              __func__, off_idx, off_pos);
  
              r = -1;
  
              goto out;
  
          }
  
  
  
          qemu_sglist_init(sglist, qbus->parent, (prdtl - off_idx),
  
                           ad->hba->as);
  
          qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr) + off_pos,
  
                          MIN(prdt_tbl_entry_size(&tbl[off_idx]) - off_pos,
  
                              limit));
  
  
  
          for (i = off_idx + 1; i < prdtl && sglist->size < limit; i++) {
  
              qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
  
                              MIN(prdt_tbl_entry_size(&tbl[i]),
  
                                  limit - sglist->size));
  
              if (sglist->size > INT32_MAX) {
  
                  error_report("AHCI Physical Region Descriptor Table describes "
  
                               "more than 2 GiB.");
  
                  qemu_sglist_destroy(sglist);
  
                  r = -1;
  
                  goto out;
  
              }
  
          }
  
      }
  
  
  
  out:
  
      dma_memory_unmap(ad->hba->as, prdt, prdt_len,
  
                       DMA_DIRECTION_TO_DEVICE, prdt_len);
  
      return r;
  
  }
  