DMAR driver assumes all physical addresses are backed by a fully

initialized struct vm_page.

Reviewed by:	kib
Sponsored by:	Dell EMC Isilon
Differential Revision:	https://reviews.freebsd.org/D19753
This commit is contained in:
Tycho Nightingale 2019-04-02 18:50:49 +00:00
parent 1b3fc371b7
commit 9708c3a2b8

View File

@ -665,9 +665,9 @@ dmar_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map1,
{
struct bus_dma_tag_dmar *tag;
struct bus_dmamap_dmar *map;
vm_page_t *ma;
vm_paddr_t pstart, pend;
int error, i, ma_cnt, offset;
vm_page_t *ma, fma;
vm_paddr_t pstart, pend, paddr;
int error, i, ma_cnt, mflags, offset;
tag = (struct bus_dma_tag_dmar *)dmat;
map = (struct bus_dmamap_dmar *)map1;
@ -675,14 +675,36 @@ dmar_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map1,
pend = round_page(buf + buflen);
offset = buf & PAGE_MASK;
ma_cnt = OFF_TO_IDX(pend - pstart);
ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, map->cansleep ?
M_WAITOK : M_NOWAIT);
mflags = map->cansleep ? M_WAITOK : M_NOWAIT;
ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, mflags);
if (ma == NULL)
return (ENOMEM);
for (i = 0; i < ma_cnt; i++)
ma[i] = PHYS_TO_VM_PAGE(pstart + i * PAGE_SIZE);
fma = NULL;
for (i = 0; i < ma_cnt; i++) {
paddr = pstart + i * PAGE_SIZE;
ma[i] = PHYS_TO_VM_PAGE(paddr);
if (ma[i] == NULL || VM_PAGE_TO_PHYS(ma[i]) != paddr) {
/*
* If PHYS_TO_VM_PAGE() returned NULL or the
* vm_page was not initialized we'll use a
* fake page.
*/
if (fma == NULL) {
fma = malloc(sizeof(struct vm_page) * ma_cnt,
M_DEVBUF, mflags);
if (fma == NULL) {
free(ma, M_DEVBUF);
return (ENOMEM);
}
}
vm_page_initfake(&fma[i], pstart + i * PAGE_SIZE,
VM_MEMATTR_DEFAULT);
ma[i] = &fma[i];
}
}
error = dmar_bus_dmamap_load_something(tag, map, ma, offset, buflen,
flags, segs, segp);
free(fma, M_DEVBUF);
free(ma, M_DEVBUF);
return (error);
}
@ -696,7 +718,7 @@ dmar_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map1, void *buf,
struct bus_dmamap_dmar *map;
vm_page_t *ma, fma;
vm_paddr_t pstart, pend, paddr;
int error, i, ma_cnt, offset;
int error, i, ma_cnt, mflags, offset;
tag = (struct bus_dma_tag_dmar *)dmat;
map = (struct bus_dmamap_dmar *)map1;
@ -704,42 +726,34 @@ dmar_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map1, void *buf,
pend = round_page((vm_offset_t)buf + buflen);
offset = (vm_offset_t)buf & PAGE_MASK;
ma_cnt = OFF_TO_IDX(pend - pstart);
ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, map->cansleep ?
M_WAITOK : M_NOWAIT);
mflags = map->cansleep ? M_WAITOK : M_NOWAIT;
ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, mflags);
if (ma == NULL)
return (ENOMEM);
if (dumping) {
/*
* If dumping, do not attempt to call
* PHYS_TO_VM_PAGE() at all. It may return non-NULL
* but the vm_page returned might be not initialized,
* e.g. for the kernel itself.
*/
KASSERT(pmap == kernel_pmap, ("non-kernel address write"));
fma = malloc(sizeof(struct vm_page) * ma_cnt, M_DEVBUF,
M_ZERO | (map->cansleep ? M_WAITOK : M_NOWAIT));
if (fma == NULL) {
free(ma, M_DEVBUF);
return (ENOMEM);
}
for (i = 0; i < ma_cnt; i++, pstart += PAGE_SIZE) {
fma = NULL;
for (i = 0; i < ma_cnt; i++, pstart += PAGE_SIZE) {
if (pmap == kernel_pmap)
paddr = pmap_kextract(pstart);
else
paddr = pmap_extract(pmap, pstart);
ma[i] = PHYS_TO_VM_PAGE(paddr);
if (ma[i] == NULL || VM_PAGE_TO_PHYS(ma[i]) != paddr) {
/*
* If PHYS_TO_VM_PAGE() returned NULL or the
* vm_page was not initialized we'll use a
* fake page.
*/
if (fma == NULL) {
fma = malloc(sizeof(struct vm_page) * ma_cnt,
M_DEVBUF, mflags);
if (fma == NULL) {
free(ma, M_DEVBUF);
return (ENOMEM);
}
}
vm_page_initfake(&fma[i], paddr, VM_MEMATTR_DEFAULT);
ma[i] = &fma[i];
}
} else {
fma = NULL;
for (i = 0; i < ma_cnt; i++, pstart += PAGE_SIZE) {
if (pmap == kernel_pmap)
paddr = pmap_kextract(pstart);
else
paddr = pmap_extract(pmap, pstart);
ma[i] = PHYS_TO_VM_PAGE(paddr);
KASSERT(VM_PAGE_TO_PHYS(ma[i]) == paddr,
("PHYS_TO_VM_PAGE failed %jx %jx m %p",
(uintmax_t)paddr, (uintmax_t)VM_PAGE_TO_PHYS(ma[i]),
ma[i]));
}
}
error = dmar_bus_dmamap_load_something(tag, map, ma, offset, buflen,
flags, segs, segp);