Skip to content

Commit 28e3187

Browse files
committed
Add support for unmapped and userspace buffers in BUSDMA
1 parent 447dc4e commit 28e3187

File tree

4 files changed

+161
-104
lines changed

4 files changed

+161
-104
lines changed

sys/arm/arm/busdma_machdep-v6.c

Lines changed: 67 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ __FBSDID("$FreeBSD$");
6161

6262
#include <machine/atomic.h>
6363
#include <machine/bus.h>
64-
#include <machine/cpufunc.h>
64+
#include <machine/cpu-v6.h>
6565
#include <machine/md_var.h>
6666

6767
#define MAX_BPAGES 64
@@ -110,8 +110,8 @@ struct bounce_page {
110110
};
111111

112112
struct sync_list {
113-
vm_offset_t vaddr; /* kva of bounce buffer */
114-
bus_addr_t busaddr; /* Physical address */
113+
vm_offset_t vaddr; /* kva of client data */
114+
bus_addr_t busaddr; /* client physical address */
115115
bus_size_t datacount; /* client data count */
116116
};
117117

@@ -625,7 +625,8 @@ bus_dma_tag_destroy(bus_dma_tag_t dmat)
625625
return (error);
626626
}
627627

628-
static int allocate_bz_and_pages(bus_dma_tag_t dmat, bus_dmamap_t mapp)
628+
static int
629+
allocate_bz_and_pages(bus_dma_tag_t dmat, bus_dmamap_t mapp)
629630
{
630631
struct bounce_zone *bz;
631632
int maxpages;
@@ -1043,6 +1044,7 @@ _bus_dmamap_load_phys(bus_dma_tag_t dmat,
10431044
{
10441045
bus_addr_t curaddr;
10451046
bus_size_t sgsize;
1047+
struct sync_list *sl;
10461048
int error;
10471049

10481050
if (segs == NULL)
@@ -1051,7 +1053,7 @@ _bus_dmamap_load_phys(bus_dma_tag_t dmat,
10511053
counter_u64_add(maploads_total, 1);
10521054
counter_u64_add(maploads_physmem, 1);
10531055

1054-
if (might_bounce(dmat, map, buflen, buflen)) {
1056+
if (might_bounce(dmat, map, (bus_addr_t)buf, buflen)) {
10551057
_bus_dmamap_count_phys(dmat, map, buf, buflen, flags);
10561058
if (map->pagesneeded != 0) {
10571059
counter_u64_add(maploads_bounced, 1);
@@ -1069,6 +1071,18 @@ _bus_dmamap_load_phys(bus_dma_tag_t dmat,
10691071
sgsize = MIN(sgsize, PAGE_SIZE);
10701072
curaddr = add_bounce_page(dmat, map, 0, curaddr,
10711073
sgsize);
1074+
} else {
1075+
sl = &map->slist[map->sync_count - 1];
1076+
if (map->sync_count == 0 ||
1077+
curaddr != sl->busaddr + sl->datacount) {
1078+
if (++map->sync_count > dmat->nsegments)
1079+
goto cleanup;
1080+
sl++;
1081+
sl->vaddr = 0;
1082+
sl->datacount = sgsize;
1083+
sl->busaddr = curaddr;
1084+
} else
1085+
sl->datacount += sgsize;
10721086
}
10731087
sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
10741088
segp);
@@ -1078,6 +1092,7 @@ _bus_dmamap_load_phys(bus_dma_tag_t dmat,
10781092
buflen -= sgsize;
10791093
}
10801094

1095+
cleanup:
10811096
/*
10821097
* Did we fit?
10831098
*/
@@ -1171,9 +1186,7 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dmat,
11711186
} else {
11721187
sl = &map->slist[map->sync_count - 1];
11731188
if (map->sync_count == 0 ||
1174-
#ifdef ARM_L2_PIPT
11751189
curaddr != sl->busaddr + sl->datacount ||
1176-
#endif
11771190
vaddr != sl->vaddr + sl->datacount) {
11781191
if (++map->sync_count > dmat->nsegments)
11791192
goto cleanup;
@@ -1251,58 +1264,21 @@ _bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
12511264
map->flags &= ~DMAMAP_MBUF;
12521265
}
12531266

1254-
#ifdef notyetbounceuser
1255-
/* If busdma uses user pages, then the interrupt handler could
1256-
* be use the kernel vm mapping. Both bounce pages and sync list
1257-
* do not cross page boundaries.
1258-
* Below is a rough sequence that a person would do to fix the
1259-
* user page reference in the kernel vmspace. This would be
1260-
* done in the dma post routine.
1261-
*/
1262-
void
1263-
_bus_dmamap_fix_user(vm_offset_t buf, bus_size_t len,
1264-
pmap_t pmap, int op)
1267+
static void
1268+
dma_dcache_ops(vm_paddr_t pa, vm_size_t size, int ops)
12651269
{
1266-
bus_size_t sgsize;
1267-
bus_addr_t curaddr;
1268-
vm_offset_t va;
1269-
1270-
/*
1271-
* each synclist entry is contained within a single page.
1272-
* this would be needed if BUS_DMASYNC_POSTxxxx was implemented
1273-
*/
1274-
curaddr = pmap_extract(pmap, buf);
1275-
va = pmap_dma_map(curaddr);
1276-
switch (op) {
1277-
case SYNC_USER_INV:
1278-
cpu_dcache_wb_range(va, sgsize);
1279-
break;
1280-
1281-
case SYNC_USER_COPYTO:
1282-
bcopy((void *)va, (void *)bounce, sgsize);
1283-
break;
1284-
1285-
case SYNC_USER_COPYFROM:
1286-
bcopy((void *) bounce, (void *)va, sgsize);
1287-
break;
1288-
1289-
default:
1290-
break;
1270+
uint32_t len, offset;
1271+
vm_page_t m;
1272+
1273+
offset = pa & PAGE_MASK;
1274+
for ( ; size != 0; size -= len, pa += len, offset = 0) {
1275+
len = min(PAGE_SIZE - offset, size);
1276+
m = PHYS_TO_VM_PAGE(pa);
1277+
KASSERT(m != NULL, ("%s: vm_page_t is null for %#x",
1278+
__func__, pa));
1279+
pmap_dma_dcache_ops(pa, len, m->md.pat_mode, ops);
12911280
}
1292-
1293-
pmap_dma_unmap(va);
12941281
}
1295-
#endif
1296-
1297-
#ifdef ARM_L2_PIPT
1298-
#define l2cache_wb_range(va, pa, size) cpu_l2cache_wb_range(pa, size)
1299-
#define l2cache_wbinv_range(va, pa, size) cpu_l2cache_wbinv_range(pa, size)
1300-
#define l2cache_inv_range(va, pa, size) cpu_l2cache_inv_range(pa, size)
1301-
#else
1302-
#define l2cache_wb_range(va, pa, size) cpu_l2cache_wb_range(va, size)
1303-
#define l2cache_wbinv_range(va, pa, size) cpu_l2cache_wbinv_range(va, size)
1304-
#define l2cache_inv_range(va, pa, size) cpu_l2cache_inv_range(va, size)
1305-
#endif
13061282

13071283
void
13081284
_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
@@ -1316,9 +1292,6 @@ _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
13161292
* we're able to test direct userland dma, panic on a map mismatch.
13171293
*/
13181294
if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
1319-
if (!pmap_dmap_iscurrent(map->pmap))
1320-
panic("_bus_dmamap_sync: wrong user map for bounce sync.");
1321-
13221295
CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
13231296
"performing bounce", __func__, dmat, dmat->flags, op);
13241297

@@ -1328,19 +1301,17 @@ _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
13281301
*/
13291302
if (op & BUS_DMASYNC_PREWRITE) {
13301303
while (bpage != NULL) {
1331-
if (bpage->datavaddr != 0)
1304+
if (bpage->datavaddr != 0 &&
1305+
pmap_dmap_iscurrent(map->pmap))
13321306
bcopy((void *)bpage->datavaddr,
13331307
(void *)bpage->vaddr,
13341308
bpage->datacount);
13351309
else
13361310
physcopyout(bpage->dataaddr,
13371311
(void *)bpage->vaddr,
13381312
bpage->datacount);
1339-
cpu_dcache_wb_range((vm_offset_t)bpage->vaddr,
1340-
bpage->datacount);
1341-
l2cache_wb_range((vm_offset_t)bpage->vaddr,
1342-
(vm_offset_t)bpage->busaddr,
1343-
bpage->datacount);
1313+
dcache_wb_poc(bpage->vaddr,
1314+
(vm_paddr_t)bpage->busaddr, bpage->datacount);
13441315
bpage = STAILQ_NEXT(bpage, links);
13451316
}
13461317
dmat->bounce_zone->total_bounced++;
@@ -1360,11 +1331,8 @@ _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
13601331
if ((op & BUS_DMASYNC_PREREAD) && !(op & BUS_DMASYNC_PREWRITE)) {
13611332
bpage = STAILQ_FIRST(&map->bpages);
13621333
while (bpage != NULL) {
1363-
cpu_dcache_inv_range((vm_offset_t)bpage->vaddr,
1364-
bpage->datacount);
1365-
l2cache_inv_range((vm_offset_t)bpage->vaddr,
1366-
(vm_offset_t)bpage->busaddr,
1367-
bpage->datacount);
1334+
dcache_dma_preread(bpage->vaddr,
1335+
(vm_paddr_t)bpage->busaddr, bpage->datacount);
13681336
bpage = STAILQ_NEXT(bpage, links);
13691337
}
13701338
}
@@ -1380,23 +1348,10 @@ _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
13801348
*/
13811349
if (op & BUS_DMASYNC_POSTREAD) {
13821350
while (bpage != NULL) {
1383-
vm_offset_t startv;
1384-
vm_paddr_t startp;
1385-
int len;
1386-
1387-
startv = bpage->vaddr &~ arm_dcache_align_mask;
1388-
startp = bpage->busaddr &~ arm_dcache_align_mask;
1389-
len = bpage->datacount;
1390-
1391-
if (startv != bpage->vaddr)
1392-
len += bpage->vaddr & arm_dcache_align_mask;
1393-
if (len & arm_dcache_align_mask)
1394-
len = (len -
1395-
(len & arm_dcache_align_mask)) +
1396-
arm_dcache_align;
1397-
l2cache_inv_range(startv, startp, len);
1398-
cpu_dcache_inv_range(startv, len);
1399-
if (bpage->datavaddr != 0)
1351+
dcache_inv_poc(bpage->vaddr,
1352+
(vm_paddr_t)bpage->busaddr, bpage->datacount);
1353+
if (bpage->datavaddr != 0 &&
1354+
pmap_dmap_iscurrent(map->pmap))
14001355
bcopy((void *)bpage->vaddr,
14011356
(void *)bpage->datavaddr,
14021357
bpage->datacount);
@@ -1434,9 +1389,6 @@ _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
14341389
* outer-to-inner for POSTREAD invalidation is not a mistake.
14351390
*/
14361391
if (map->sync_count != 0) {
1437-
if (!pmap_dmap_iscurrent(map->pmap))
1438-
panic("_bus_dmamap_sync: wrong user map for sync.");
1439-
14401392
sl = &map->slist[0];
14411393
end = &map->slist[map->sync_count];
14421394
CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
@@ -1446,9 +1398,14 @@ _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
14461398
case BUS_DMASYNC_PREWRITE:
14471399
case BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD:
14481400
while (sl != end) {
1449-
cpu_dcache_wb_range(sl->vaddr, sl->datacount);
1450-
l2cache_wb_range(sl->vaddr, sl->busaddr,
1451-
sl->datacount);
1401+
if (sl->vaddr != 0 &&
1402+
pmap_dmap_iscurrent(map->pmap))
1403+
dcache_wb_poc(sl->vaddr,
1404+
(vm_paddr_t)sl->busaddr,
1405+
sl->datacount);
1406+
else
1407+
dma_dcache_ops((vm_paddr_t)sl->busaddr,
1408+
sl->datacount, DDO_WB_POC);
14521409
sl++;
14531410
}
14541411
break;
@@ -1465,16 +1422,18 @@ _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
14651422
* they are not aligned to a cacheline.
14661423
*/
14671424
while (sl != end) {
1468-
if (sl->vaddr & arm_dcache_align_mask) {
1425+
if (sl->busaddr & cpuinfo.dcache_line_mask) {
14691426
KASSERT(map->flags & DMAMAP_MBUF,
14701427
("unaligned buffer is not an mbuf"));
1471-
cpu_dcache_wb_range(sl->vaddr, 1);
1472-
l2cache_wb_range(sl->vaddr,
1473-
sl->busaddr, 1);
14741428
}
1475-
cpu_dcache_inv_range(sl->vaddr, sl->datacount);
1476-
l2cache_inv_range(sl->vaddr, sl->busaddr,
1477-
sl->datacount);
1429+
if (sl->vaddr != 0 &&
1430+
pmap_dmap_iscurrent(map->pmap))
1431+
dcache_dma_preread_safe(sl->vaddr,
1432+
(vm_paddr_t)sl->busaddr,
1433+
sl->datacount);
1434+
else
1435+
dma_dcache_ops((vm_paddr_t)sl->busaddr,
1436+
sl->datacount, DDO_PREREAD_SAFE);
14781437
sl++;
14791438
}
14801439
break;
@@ -1485,9 +1444,14 @@ _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
14851444
case BUS_DMASYNC_POSTREAD:
14861445
case BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE:
14871446
while (sl != end) {
1488-
l2cache_inv_range(sl->vaddr, sl->busaddr,
1489-
sl->datacount);
1490-
cpu_dcache_inv_range(sl->vaddr, sl->datacount);
1447+
if (sl->vaddr != 0 &&
1448+
pmap_dmap_iscurrent(map->pmap))
1449+
dcache_inv_poc(sl->vaddr,
1450+
(vm_paddr_t)sl->busaddr,
1451+
sl->datacount);
1452+
else
1453+
dma_dcache_ops((vm_paddr_t)sl->busaddr,
1454+
sl->datacount, DDO_INV_POC);
14911455
sl++;
14921456
}
14931457
break;

sys/arm/arm/pmap-v6-new.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6076,6 +6076,48 @@ pmap_sync_icache(pmap_t pmap, vm_offset_t va, vm_size_t size)
60766076
icache_inv_all();
60776077
}
60786078

6079+
/*
6080+
* Do DMA ops on data caches range by physical address.
6081+
* The range must be within a single page.
6082+
*/
6083+
void
6084+
pmap_dma_dcache_ops(vm_paddr_t pa, vm_size_t size, vm_memattr_t ma, int ops)
6085+
{
6086+
struct sysmaps *sysmaps;
6087+
vm_offset_t va;
6088+
6089+
KASSERT(((pa & PAGE_MASK) + size) <= PAGE_SIZE,
6090+
("%s: not on single page", __func__));
6091+
6092+
sched_pin();
6093+
sysmaps = &sysmaps_pcpu[PCPU_GET(cpuid)];
6094+
mtx_lock(&sysmaps->lock);
6095+
if (*sysmaps->CMAP3)
6096+
panic("%s: CMAP3 busy", __func__);
6097+
pte2_store(sysmaps->CMAP3, PTE2_KERN_NG(pa, PTE2_AP_KRW, ma));
6098+
tlb_flush_local((vm_offset_t)sysmaps->CADDR3);
6099+
va = (vm_offset_t)sysmaps->CADDR3 + (pa & PAGE_MASK);
6100+
switch (ops) {
6101+
case DDO_WB_POC:
6102+
dcache_wb_poc(va, pa, size);
6103+
break;
6104+
case DDO_INV_POC:
6105+
dcache_inv_poc(va, pa, size);
6106+
break;
6107+
case DDO_PREREAD:
6108+
dcache_dma_preread(va, pa, size);
6109+
break;
6110+
case DDO_PREREAD_SAFE:
6111+
dcache_dma_preread_safe(va, pa, size);
6112+
break;
6113+
default:
6114+
panic("%s: unknown ops %d", __func__, ops);
6115+
}
6116+
pte2_clear(sysmaps->CMAP3);
6117+
sched_unpin();
6118+
mtx_unlock(&sysmaps->lock);
6119+
}
6120+
60796121
/*
60806122
* The implementation of pmap_fault() uses IN_RANGE2() macro which
60816123
* depends on the fact that given range size is a power of 2.

0 commit comments

Comments
 (0)