The Data Byte Count (DBC) field of a Physical Region Descriptor

Table is 22 bits, with the bit 31 being the interrupt-on-completion
bit.

OpenBSD and UEFI set this bit, resulting in large block i/o lengths
being sent to bhyve and coredumping the process. Fix by masking off
the relevant 22 bits when using the DBC field as a length.

Reviewed by:	Zhixiang Yu
Discussed with:	Tycho Nightingale (tycho.nightingale@pluribusnetworks.com)
MFC after:	10.0
This commit is contained in:
Peter Grehan 2013-11-26 03:00:54 +00:00
parent 3287361e38
commit 4b48ea6ab2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=258614

View File

@ -165,6 +165,7 @@ struct ahci_cmd_hdr {
struct ahci_prdt_entry {
uint64_t dba;
uint32_t reserved;
#define DBCMASK 0x3fffff
uint32_t dbc;
};
@ -461,10 +462,13 @@ ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done,
* Build up the iovec based on the prdt
*/
for (i = 0; i < iovcnt; i++) {
uint32_t dbcsz;
dbcsz = (prdt->dbc & DBCMASK) + 1;
breq->br_iov[i].iov_base = paddr_guest2host(ahci_ctx(sc),
prdt->dba, prdt->dbc + 1);
breq->br_iov[i].iov_len = prdt->dbc + 1;
aior->done += (prdt->dbc + 1);
prdt->dba, dbcsz);
breq->br_iov[i].iov_len = dbcsz;
aior->done += dbcsz;
prdt++;
}
if (readop)
@ -513,11 +517,14 @@ write_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
from = buf;
prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
for (i = 0; i < hdr->prdtl && len; i++) {
uint8_t *ptr = paddr_guest2host(ahci_ctx(p->pr_sc),
prdt->dba, prdt->dbc + 1);
memcpy(ptr, from, prdt->dbc + 1);
len -= (prdt->dbc + 1);
from += (prdt->dbc + 1);
uint8_t *ptr;
uint32_t dbcsz;
dbcsz = (prdt->dbc & DBCMASK) + 1;
ptr = paddr_guest2host(ahci_ctx(p->pr_sc), prdt->dba, dbcsz);
memcpy(ptr, from, dbcsz);
len -= dbcsz;
from += dbcsz;
prdt++;
}
hdr->prdbc = size - len;
@ -908,10 +915,13 @@ atapi_read(struct ahci_port *p, int slot, uint8_t *cfis,
* Build up the iovec based on the prdt
*/
for (i = 0; i < iovcnt; i++) {
uint32_t dbcsz;
dbcsz = (prdt->dbc & DBCMASK) + 1;
breq->br_iov[i].iov_base = paddr_guest2host(ahci_ctx(sc),
prdt->dba, prdt->dbc + 1);
breq->br_iov[i].iov_len = prdt->dbc + 1;
aior->done += (prdt->dbc + 1);
prdt->dba, dbcsz);
breq->br_iov[i].iov_len = dbcsz;
aior->done += dbcsz;
prdt++;
}
err = blockif_read(p->bctx, breq);