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_dma_tag_dmar *tag;
struct bus_dmamap_dmar *map; struct bus_dmamap_dmar *map;
vm_page_t *ma; vm_page_t *ma, fma;
vm_paddr_t pstart, pend; 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; tag = (struct bus_dma_tag_dmar *)dmat;
map = (struct bus_dmamap_dmar *)map1; 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); pend = round_page(buf + buflen);
offset = buf & PAGE_MASK; offset = buf & PAGE_MASK;
ma_cnt = OFF_TO_IDX(pend - pstart); ma_cnt = OFF_TO_IDX(pend - pstart);
ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, map->cansleep ? mflags = map->cansleep ? M_WAITOK : M_NOWAIT;
M_WAITOK : M_NOWAIT); ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, mflags);
if (ma == NULL) if (ma == NULL)
return (ENOMEM); return (ENOMEM);
for (i = 0; i < ma_cnt; i++) fma = NULL;
ma[i] = PHYS_TO_VM_PAGE(pstart + i * PAGE_SIZE); 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, error = dmar_bus_dmamap_load_something(tag, map, ma, offset, buflen,
flags, segs, segp); flags, segs, segp);
free(fma, M_DEVBUF);
free(ma, M_DEVBUF); free(ma, M_DEVBUF);
return (error); 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; struct bus_dmamap_dmar *map;
vm_page_t *ma, fma; vm_page_t *ma, fma;
vm_paddr_t pstart, pend, paddr; 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; tag = (struct bus_dma_tag_dmar *)dmat;
map = (struct bus_dmamap_dmar *)map1; 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); pend = round_page((vm_offset_t)buf + buflen);
offset = (vm_offset_t)buf & PAGE_MASK; offset = (vm_offset_t)buf & PAGE_MASK;
ma_cnt = OFF_TO_IDX(pend - pstart); ma_cnt = OFF_TO_IDX(pend - pstart);
ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, map->cansleep ? mflags = map->cansleep ? M_WAITOK : M_NOWAIT;
M_WAITOK : M_NOWAIT); ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, mflags);
if (ma == NULL) if (ma == NULL)
return (ENOMEM); return (ENOMEM);
if (dumping) { fma = NULL;
/* for (i = 0; i < ma_cnt; i++, pstart += PAGE_SIZE) {
* If dumping, do not attempt to call if (pmap == kernel_pmap)
* 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) {
paddr = pmap_kextract(pstart); 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); vm_page_initfake(&fma[i], paddr, VM_MEMATTR_DEFAULT);
ma[i] = &fma[i]; 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, error = dmar_bus_dmamap_load_something(tag, map, ma, offset, buflen,
flags, segs, segp); flags, segs, segp);