This commit is contained in:
Peter 2023-02-06 18:13:09 +03:00 committed by GitHub
commit 593c84c91e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 113 additions and 87 deletions

View File

@ -334,7 +334,7 @@ service at_wini
15 # Controller 1 15 # Controller 1
; ;
system system
UMAP # 14 VUMAP # 18
IRQCTL # 19 IRQCTL # 19
DEVIO # 21 DEVIO # 21
SDEVIO # 22 SDEVIO # 22

View File

@ -1052,8 +1052,9 @@ static ssize_t w_transfer(
if (do_dma) { if (do_dma) {
stop_dma(wn); stop_dma(wn);
if (!setup_dma(&nbytes, proc_nr, iov, addr_offset, do_write)) { r = setup_dma(&nbytes, proc_nr, iov, addr_offset, do_write);
do_dma = 0; if (r == 0) {
return -errno;
} }
#if 0 #if 0
printf("nbytes = %d\n", nbytes); printf("nbytes = %d\n", nbytes);
@ -1324,97 +1325,121 @@ static int setup_dma(
endpoint_t proc_nr, endpoint_t proc_nr,
iovec_t *iov, iovec_t *iov,
size_t addr_offset, size_t addr_offset,
int UNUSED(do_write) int do_write
) ){
{ /* vvec cannot be bigger than prdt, so I think it is a suitable size
phys_bytes user_phys; * Should not exhaust the stack */
unsigned n, offset, size; struct vumap_vir vvec[N_PRDTE];
int i, j, r; struct vumap_phys pvec[N_PRDTE];
unsigned bytes, offset, size;
int i, j, vvec_len, r, pcount;
u32_t v; u32_t v;
struct wini *wn = w_wn; struct wini *wn = w_wn;
/* First try direct scatter/gather to the supplied buffers */ size = *sizep;
size= *sizep; i = 0; /* pvec index */
i= 0; /* iov index */ j = 0; /* prdt index */
j= 0; /* prdt index */ vvec_len = 0;
offset= 0; /* Offset in current iov */
#if VERBOSE_DMA /* Step 1: copy data from iov to vvec */
printf("at_wini: setup_dma: proc_nr %d\n", proc_nr); while (size > 0) {
#endif if (proc_nr == SELF) {
vvec[vvec_len].vv_addr = iov[vvec_len].iov_addr;
}
else {
vvec[vvec_len].vv_grant = (cp_grant_id_t)iov[vvec_len].iov_addr;
}
while (size > 0) if (size < iov[vvec_len].iov_size) {
{ vvec[vvec_len].vv_size = size;
#if VERBOSE_DMA
printf(
"at_wini: setup_dma: iov[%d]: addr 0x%lx, size %ld offset %d, size %d\n",
i, iov[i].iov_addr, iov[i].iov_size, offset, size);
#endif
n= iov[i].iov_size-offset;
if (n > size)
n= size;
if (n == 0 || (n & 1))
panic("bad size in iov: 0x%lx", iov[i].iov_size);
if(proc_nr != SELF) {
r= sys_umap(proc_nr, VM_GRANT, iov[i].iov_addr, n,
&user_phys);
if (r != 0)
panic("can't map user buffer (VM_GRANT): %d", r);
user_phys += offset + addr_offset;
} else { } else {
r= sys_umap(proc_nr, VM_D, vvec[vvec_len].vv_size = iov[vvec_len].iov_size;
iov[i].iov_addr+offset+addr_offset, n,
&user_phys);
if (r != 0)
panic("can't map user buffer (VM_D): %d", r);
} }
if (user_phys & 1) size -= vvec[vvec_len].vv_size;
{ vvec_len++;
/* Buffer is not aligned */ }
pcount = vvec_len;
/* Step 2: sys_vumap */
/* sys_vumap should resolve all our addresses in one go,
* since caller is required to provide us with physically
* contiguous buffers. If sys_vumap fails to do so,
* we can just return EINVAL */
r = sys_vumap(proc_nr, vvec, vvec_len, addr_offset,
do_write? VUA_READ : VUA_WRITE, pvec, &pcount);
if (r != OK) {
printf("setup_dma: can't map user buffer: %d", r);
errno = EINVAL;
return 0;
}
/* sanity check: buffers should be 2-aligned
* (i.e. their start addresses should be even) */
for (i = 0; i < pcount; i++) {
if (pvec[i].vp_addr & 1) {
printf("setup_dma: user buffer is not aligned\n"); printf("setup_dma: user buffer is not aligned\n");
errno = EINVAL;
return 0; return 0;
} }
/* vector is not allowed to cross a 64K boundary */
if (user_phys/0x10000 != (user_phys+n-1)/0x10000)
n= ((user_phys/0x10000)+1)*0x10000 - user_phys;
/* vector is not allowed to be bigger than 64K, but we get that
* for free.
*/
if (j >= N_PRDTE)
{
/* Too many entries */
printf("setup_dma: user buffer has too many entries\n");
return 0;
} }
prdt[j].prdte_base= user_phys; /* Step 3: redistribute pvec ranges to PRD's */
prdt[j].prdte_count= n; i = 0;
prdt[j].prdte_reserved= 0; size = 0;
prdt[j].prdte_flags= 0; offset = 0;
j++; while (i < pcount) {
prdt[j].prdte_base = pvec[i].vp_addr + offset;
bytes = pvec[i].vp_size - offset;
offset += n; /* make sure PRD does not cross 64K boundary */
if (offset >= iov[i].iov_size) if (prdt[j].prdte_base / 0x10000 !=
{ (prdt[j].prdte_base + bytes - 1) / 0x10000) {
bytes = ((prdt[j].prdte_base / 0x10000) + 1) * 0x10000
- prdt[j].prdte_base;
}
/* PRD size must not exceed 64K, but we get that for free */
prdt[j].prdte_count = bytes;
prdt[j].prdte_reserved = 0;
prdt[j].prdte_flags = 0;
offset += bytes;
if (offset == pvec[i].vp_size) {
i++; i++;
offset= 0; offset = 0;
addr_offset= 0;
} }
size -= n; /* keeping track of how much data in vvec were processed */
} size += bytes;
if (j <= 0 || j > N_PRDTE) j++;
panic("bad prdt index: %d", j); if (j == N_PRDTE && i < pcount) {
prdt[j-1].prdte_flags |= PRDTE_FL_EOT; /* we are out of PRDT space and not yet done with
* all regions! Bail out gracefully. */
printf("setup_dma: user buffer has too many entries\n");
errno = EINVAL;
return 0;
}
}
prdt[j - 1].prdte_flags |= PRDTE_FL_EOT;
if (size < *sizep) {
/* sys_vumap didn't resolve everything in one go, therefore
* buffers were not physically contiguous.
*/
printf("setup_dma: user buffers are not physically contiguous\n");
errno = EINVAL;
return 0;
}
#if VERBOSE_DMA #if VERBOSE_DMA
printf("dma not bad\n"); printf("dma not bad\n");
for (i= 0; i<j; i++) { for (i = 0; i < j; i++) {
printf("prdt[%d]: base 0x%lx, size %d, flags 0x%x\n", printf("prdt[%d]: base 0x%lx, size %d, flags 0x%x\n",
i, prdt[i].prdte_base, prdt[i].prdte_count, i, prdt[i].prdte_base, prdt[i].prdte_count,
prdt[i].prdte_flags); prdt[i].prdte_flags);
@ -1422,18 +1447,18 @@ static int setup_dma(
#endif #endif
/* Verify that the bus master is not active */ /* Verify that the bus master is not active */
r= sys_inb(wn->base_dma + DMA_STATUS, &v); r = sys_inb(wn->base_dma + DMA_STATUS, &v);
if (r != 0) panic("setup_dma: sys_inb failed: %d", r); if (r != 0) panic("setup_dma: sys_inb failed: %d", r);
if (v & DMA_ST_BM_ACTIVE) if (v & DMA_ST_BM_ACTIVE)
panic("Bus master IDE active"); panic("Bus master IDE active");
if (prdt_phys & 3) if (prdt_phys & 3)
panic("prdt not aligned: 0x%lx", prdt_phys); panic("prdt not aligned: 0x%lx", prdt_phys);
r= sys_outl(wn->base_dma + DMA_PRDTP, prdt_phys); r = sys_outl(wn->base_dma + DMA_PRDTP, prdt_phys);
if (r != 0) panic("setup_dma: sys_outl failed: %d", r); if (r != 0) panic("setup_dma: sys_outl failed: %d", r);
/* Clear interrupt and error flags */ /* Clear interrupt and error flags */
r= sys_outb(wn->base_dma + DMA_STATUS, DMA_ST_INT | DMA_ST_ERROR); r = sys_outb(wn->base_dma + DMA_STATUS, DMA_ST_INT | DMA_ST_ERROR);
if (r != 0) panic("setup_dma: sys_outb failed: %d", r); if (r != 0) panic("setup_dma: sys_outb failed: %d", r);
return 1; return 1;
@ -1849,8 +1874,9 @@ static int atapi_transfer(
if(do_dma) { if(do_dma) {
stop_dma(wn); stop_dma(wn);
if (!setup_dma(&nbytes, proc_nr, iov, addr_offset, 0)) { r = setup_dma(&nbytes, proc_nr, iov, addr_offset, 0);
do_dma = 0; if (r == 0) {
return errno;
} else if(nbytes != nblocks * CD_SECTOR_SIZE) { } else if(nbytes != nblocks * CD_SECTOR_SIZE) {
stop_dma(wn); stop_dma(wn);
do_dma = 0; do_dma = 0;