diff options
Diffstat (limited to 'drivers/s390/cio/vfio_ccw_cp.c')
| -rw-r--r-- | drivers/s390/cio/vfio_ccw_cp.c | 416 |
1 files changed, 226 insertions, 190 deletions
diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index c0a09fa8991a..5f6e10225627 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -42,8 +42,7 @@ struct ccwchain { /* * page_array_alloc() - alloc memory for page array * @pa: page_array on which to perform the operation - * @iova: target guest physical address - * @len: number of bytes that should be pinned from @iova + * @len: number of pages that should be pinned from @iova * * Attempt to allocate memory for page array. * @@ -56,31 +55,24 @@ struct ccwchain { * -EINVAL if pa->pa_nr is not initially zero, or pa->pa_iova is not NULL * -ENOMEM if alloc failed */ -static int page_array_alloc(struct page_array *pa, u64 iova, unsigned int len) +static int page_array_alloc(struct page_array *pa, unsigned int len) { - int i; - if (pa->pa_nr || pa->pa_iova) return -EINVAL; - pa->pa_nr = ((iova & ~PAGE_MASK) + len + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - if (!pa->pa_nr) + if (len == 0) return -EINVAL; - pa->pa_iova = kcalloc(pa->pa_nr, - sizeof(*pa->pa_iova) + sizeof(*pa->pa_page), - GFP_KERNEL); - if (unlikely(!pa->pa_iova)) { - pa->pa_nr = 0; + pa->pa_nr = len; + + pa->pa_iova = kcalloc(len, sizeof(*pa->pa_iova), GFP_KERNEL); + if (!pa->pa_iova) return -ENOMEM; - } - pa->pa_page = (struct page **)&pa->pa_iova[pa->pa_nr]; - pa->pa_iova[0] = iova; - pa->pa_page[0] = NULL; - for (i = 1; i < pa->pa_nr; i++) { - pa->pa_iova[i] = pa->pa_iova[i - 1] + PAGE_SIZE; - pa->pa_page[i] = NULL; + pa->pa_page = kcalloc(len, sizeof(*pa->pa_page), GFP_KERNEL); + if (!pa->pa_page) { + kfree(pa->pa_iova); + return -ENOMEM; } return 0; @@ -91,12 +83,13 @@ static int page_array_alloc(struct page_array *pa, u64 iova, unsigned int len) * @pa: page_array on which to perform the operation * @vdev: the vfio device to perform the operation * @pa_nr: number of user pages to unpin + * @unaligned: were pages unaligned on the pin request * * Only unpin if any pages were pinned to begin with, i.e. pa_nr > 0, * otherwise only clear pa->pa_nr */ static void page_array_unpin(struct page_array *pa, - struct vfio_device *vdev, int pa_nr) + struct vfio_device *vdev, int pa_nr, bool unaligned) { int unpinned = 0, npage = 1; @@ -105,7 +98,8 @@ static void page_array_unpin(struct page_array *pa, dma_addr_t *last = &first[npage]; if (unpinned + npage < pa_nr && - *first + npage * PAGE_SIZE == *last) { + *first + npage * PAGE_SIZE == *last && + !unaligned) { npage++; continue; } @@ -121,13 +115,20 @@ static void page_array_unpin(struct page_array *pa, /* * page_array_pin() - Pin user pages in memory * @pa: page_array on which to perform the operation - * @mdev: the mediated device to perform pin operations + * @vdev: the vfio device to perform pin operations + * @unaligned: are pages aligned to 4K boundary? * * Returns number of pages pinned upon success. * If the pin request partially succeeds, or fails completely, * all pages are left unpinned and a negative error value is returned. + * + * Requests to pin "aligned" pages can be coalesced into a single + * vfio_pin_pages request for the sake of efficiency, based on the + * expectation of 4K page requests. Unaligned requests are probably + * dealing with 2K "pages", and cannot be coalesced without + * reworking this logic to incorporate that math. */ -static int page_array_pin(struct page_array *pa, struct vfio_device *vdev) +static int page_array_pin(struct page_array *pa, struct vfio_device *vdev, bool unaligned) { int pinned = 0, npage = 1; int ret = 0; @@ -137,7 +138,8 @@ static int page_array_pin(struct page_array *pa, struct vfio_device *vdev) dma_addr_t *last = &first[npage]; if (pinned + npage < pa->pa_nr && - *first + npage * PAGE_SIZE == *last) { + *first + npage * PAGE_SIZE == *last && + !unaligned) { npage++; continue; } @@ -159,14 +161,15 @@ static int page_array_pin(struct page_array *pa, struct vfio_device *vdev) return ret; err_out: - page_array_unpin(pa, vdev, pinned); + page_array_unpin(pa, vdev, pinned, unaligned); return ret; } /* Unpin the pages before releasing the memory. */ -static void page_array_unpin_free(struct page_array *pa, struct vfio_device *vdev) +static void page_array_unpin_free(struct page_array *pa, struct vfio_device *vdev, bool unaligned) { - page_array_unpin(pa, vdev, pa->pa_nr); + page_array_unpin(pa, vdev, pa->pa_nr, unaligned); + kfree(pa->pa_page); kfree(pa->pa_iova); } @@ -187,7 +190,7 @@ static bool page_array_iova_pinned(struct page_array *pa, u64 iova, u64 length) } /* Create the list of IDAL words for a page_array. */ static inline void page_array_idal_create_words(struct page_array *pa, - unsigned long *idaws) + dma64_t *idaws) { int i; @@ -199,11 +202,12 @@ static inline void page_array_idal_create_words(struct page_array *pa, * idaw. */ - for (i = 0; i < pa->pa_nr; i++) - idaws[i] = page_to_phys(pa->pa_page[i]); + for (i = 0; i < pa->pa_nr; i++) { + idaws[i] = virt_to_dma64(page_to_virt(pa->pa_page[i])); - /* Adjust the first IDAW, since it may not start on a page boundary */ - idaws[0] += pa->pa_iova[0] & (PAGE_SIZE - 1); + /* Incorporate any offset from each starting address */ + idaws[i] = dma64_add(idaws[i], pa->pa_iova[i] & ~PAGE_MASK); + } } static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len) @@ -223,55 +227,12 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len) pccw1->flags = ccw0.flags; pccw1->count = ccw0.count; } - pccw1->cda = ccw0.cda; + pccw1->cda = u32_to_dma32(ccw0.cda); pccw1++; } } -/* - * Within the domain (@mdev), copy @n bytes from a guest physical - * address (@iova) to a host physical address (@to). - */ -static long copy_from_iova(struct vfio_device *vdev, void *to, u64 iova, - unsigned long n) -{ - struct page_array pa = {0}; - int i, ret; - unsigned long l, m; - - ret = page_array_alloc(&pa, iova, n); - if (ret < 0) - return ret; - - ret = page_array_pin(&pa, vdev); - if (ret < 0) { - page_array_unpin_free(&pa, vdev); - return ret; - } - - l = n; - for (i = 0; i < pa.pa_nr; i++) { - void *from = kmap_local_page(pa.pa_page[i]); - - m = PAGE_SIZE; - if (i == 0) { - from += iova & (PAGE_SIZE - 1); - m -= iova & (PAGE_SIZE - 1); - } - - m = min(l, m); - memcpy(to + (n - l), from, m); - kunmap_local(from); - - l -= m; - if (l == 0) - break; - } - - page_array_unpin_free(&pa, vdev); - - return l; -} +#define idal_is_2k(_cp) (!(_cp)->orb.cmd.c64 || (_cp)->orb.cmd.i2k) /* * Helpers to operate ccwchain. @@ -338,11 +299,12 @@ static inline int ccw_does_data_transfer(struct ccw1 *ccw) * * Returns 1 if yes, 0 if no. */ -static inline int is_cpa_within_range(u32 cpa, u32 head, int len) +static inline int is_cpa_within_range(dma32_t cpa, u32 head, int len) { u32 tail = head + (len - 1) * sizeof(struct ccw1); + u32 gcpa = dma32_to_u32(cpa); - return (head <= cpa && cpa <= tail); + return head <= gcpa && gcpa <= tail; } static inline int is_tic_within_range(struct ccw1 *ccw, u32 head, int len) @@ -356,45 +318,46 @@ static inline int is_tic_within_range(struct ccw1 *ccw, u32 head, int len) static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len) { struct ccwchain *chain; - void *data; - size_t size; - - /* Make ccw address aligned to 8. */ - size = ((sizeof(*chain) + 7L) & -8L) + - sizeof(*chain->ch_ccw) * len + - sizeof(*chain->ch_pa) * len; - chain = kzalloc(size, GFP_DMA | GFP_KERNEL); + + chain = kzalloc(sizeof(*chain), GFP_KERNEL); if (!chain) return NULL; - data = (u8 *)chain + ((sizeof(*chain) + 7L) & -8L); - chain->ch_ccw = (struct ccw1 *)data; + chain->ch_ccw = kcalloc(len, sizeof(*chain->ch_ccw), GFP_DMA | GFP_KERNEL); + if (!chain->ch_ccw) + goto out_err; - data = (u8 *)(chain->ch_ccw) + sizeof(*chain->ch_ccw) * len; - chain->ch_pa = (struct page_array *)data; - - chain->ch_len = len; + chain->ch_pa = kcalloc(len, sizeof(*chain->ch_pa), GFP_KERNEL); + if (!chain->ch_pa) + goto out_err; list_add_tail(&chain->next, &cp->ccwchain_list); return chain; + +out_err: + kfree(chain->ch_ccw); + kfree(chain); + return NULL; } static void ccwchain_free(struct ccwchain *chain) { list_del(&chain->next); + kfree(chain->ch_pa); + kfree(chain->ch_ccw); kfree(chain); } /* Free resource for a ccw that allocated memory for its cda. */ static void ccwchain_cda_free(struct ccwchain *chain, int idx) { - struct ccw1 *ccw = chain->ch_ccw + idx; + struct ccw1 *ccw = &chain->ch_ccw[idx]; if (ccw_is_tic(ccw)) return; - kfree(phys_to_virt(ccw->cda)); + kfree(dma32_to_virt(ccw->cda)); } /** @@ -419,14 +382,6 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp) cnt++; /* - * As we don't want to fail direct addressing even if the - * orb specified one of the unsupported formats, we defer - * checking for IDAWs in unsupported formats to here. - */ - if ((!cp->orb.cmd.c64 || cp->orb.cmd.i2k) && ccw_is_idal(ccw)) - return -EOPNOTSUPP; - - /* * We want to keep counting if the current CCW has the * command-chaining flag enabled, or if it is a TIC CCW * that loops back into the current chain. The latter @@ -463,25 +418,26 @@ static int tic_target_chain_exists(struct ccw1 *tic, struct channel_program *cp) static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp); -static int ccwchain_handle_ccw(u32 cda, struct channel_program *cp) +static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp) { struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; struct ccwchain *chain; int len, ret; + u32 gcda; + gcda = dma32_to_u32(cda); /* Copy 2K (the most we support today) of possible CCWs */ - len = copy_from_iova(vdev, cp->guest_cp, cda, - CCWCHAIN_LEN_MAX * sizeof(struct ccw1)); - if (len) - return len; + ret = vfio_dma_rw(vdev, gcda, cp->guest_cp, CCWCHAIN_LEN_MAX * sizeof(struct ccw1), false); + if (ret) + return ret; /* Convert any Format-0 CCWs to Format-1 */ if (!cp->orb.cmd.fmt) convert_ccw0_to_ccw1(cp->guest_cp, CCWCHAIN_LEN_MAX); /* Count the CCWs in the current chain */ - len = ccwchain_calc_length(cda, cp); + len = ccwchain_calc_length(gcda, cp); if (len < 0) return len; @@ -489,7 +445,9 @@ static int ccwchain_handle_ccw(u32 cda, struct channel_program *cp) chain = ccwchain_alloc(cp, len); if (!chain) return -ENOMEM; - chain->ch_iova = cda; + + chain->ch_len = len; + chain->ch_iova = gcda; /* Copy the actual CCWs into the new chain */ memcpy(chain->ch_ccw, cp->guest_cp, len * sizeof(struct ccw1)); @@ -510,7 +468,7 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp) int i, ret; for (i = 0; i < chain->ch_len; i++) { - tic = chain->ch_ccw + i; + tic = &chain->ch_ccw[i]; if (!ccw_is_tic(tic)) continue; @@ -528,19 +486,18 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp) return 0; } -static int ccwchain_fetch_tic(struct ccwchain *chain, - int idx, +static int ccwchain_fetch_tic(struct ccw1 *ccw, struct channel_program *cp) { - struct ccw1 *ccw = chain->ch_ccw + idx; struct ccwchain *iter; - u32 ccw_head; + u32 offset, ccw_head; list_for_each_entry(iter, &cp->ccwchain_list, next) { ccw_head = iter->ch_iova; if (is_cpa_within_range(ccw->cda, ccw_head, iter->ch_len)) { - ccw->cda = (__u32) (addr_t) (((char *)iter->ch_ccw) + - (ccw->cda - ccw_head)); + /* Calculate offset of TIC target */ + offset = dma32_to_u32(ccw->cda) - ccw_head; + ccw->cda = virt_to_dma32((void *)iter->ch_ccw + offset); return 0; } } @@ -548,43 +505,126 @@ static int ccwchain_fetch_tic(struct ccwchain *chain, return -EFAULT; } -static int ccwchain_fetch_direct(struct ccwchain *chain, - int idx, - struct channel_program *cp) +static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int idaw_nr) +{ + struct vfio_device *vdev = + &container_of(cp, struct vfio_ccw_private, cp)->vdev; + dma64_t *idaws; + dma32_t *idaws_f1; + int idal_len = idaw_nr * sizeof(*idaws); + int idaw_size = idal_is_2k(cp) ? PAGE_SIZE / 2 : PAGE_SIZE; + int idaw_mask = ~(idaw_size - 1); + int i, ret; + + idaws = kcalloc(idaw_nr, sizeof(*idaws), GFP_DMA | GFP_KERNEL); + if (!idaws) + return ERR_PTR(-ENOMEM); + + if (ccw_is_idal(ccw)) { + /* Copy IDAL from guest */ + ret = vfio_dma_rw(vdev, dma32_to_u32(ccw->cda), idaws, idal_len, false); + if (ret) { + kfree(idaws); + return ERR_PTR(ret); + } + } else { + /* Fabricate an IDAL based off CCW data address */ + if (cp->orb.cmd.c64) { + idaws[0] = u64_to_dma64(dma32_to_u32(ccw->cda)); + for (i = 1; i < idaw_nr; i++) { + idaws[i] = dma64_add(idaws[i - 1], idaw_size); + idaws[i] = dma64_and(idaws[i], idaw_mask); + } + } else { + idaws_f1 = (dma32_t *)idaws; + idaws_f1[0] = ccw->cda; + for (i = 1; i < idaw_nr; i++) { + idaws_f1[i] = dma32_add(idaws_f1[i - 1], idaw_size); + idaws_f1[i] = dma32_and(idaws_f1[i], idaw_mask); + } + } + } + + return idaws; +} + +/* + * ccw_count_idaws() - Calculate the number of IDAWs needed to transfer + * a specified amount of data + * + * @ccw: The Channel Command Word being translated + * @cp: Channel Program being processed + * + * The ORB is examined, since it specifies what IDAWs could actually be + * used by any CCW in the channel program, regardless of whether or not + * the CCW actually does. An ORB that does not specify Format-2-IDAW + * Control could still contain a CCW with an IDAL, which would be + * Format-1 and thus only move 2K with each IDAW. Thus all CCWs within + * the channel program must follow the same size requirements. + */ +static int ccw_count_idaws(struct ccw1 *ccw, + struct channel_program *cp) { struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; - struct ccw1 *ccw; - struct page_array *pa; u64 iova; - unsigned long *idaws; + int size = cp->orb.cmd.c64 ? sizeof(u64) : sizeof(u32); int ret; int bytes = 1; - int idaw_nr, idal_len; - int i; - - ccw = chain->ch_ccw + idx; if (ccw->count) bytes = ccw->count; - /* Calculate size of IDAL */ if (ccw_is_idal(ccw)) { - /* Read first IDAW to see if it's 4K-aligned or not. */ - /* All subsequent IDAws will be 4K-aligned. */ - ret = copy_from_iova(vdev, &iova, ccw->cda, sizeof(iova)); + /* Read first IDAW to check its starting address. */ + /* All subsequent IDAWs will be 2K- or 4K-aligned. */ + ret = vfio_dma_rw(vdev, dma32_to_u32(ccw->cda), &iova, size, false); if (ret) return ret; + + /* + * Format-1 IDAWs only occupy the first 32 bits, + * and bit 0 is always off. + */ + if (!cp->orb.cmd.c64) + iova = iova >> 32; } else { - iova = ccw->cda; + iova = dma32_to_u32(ccw->cda); } - idaw_nr = idal_nr_words((void *)iova, bytes); - idal_len = idaw_nr * sizeof(*idaws); + + /* Format-1 IDAWs operate on 2K each */ + if (!cp->orb.cmd.c64) + return idal_2k_nr_words((void *)iova, bytes); + + /* Using the 2K variant of Format-2 IDAWs? */ + if (cp->orb.cmd.i2k) + return idal_2k_nr_words((void *)iova, bytes); + + /* The 'usual' case is 4K Format-2 IDAWs */ + return idal_nr_words((void *)iova, bytes); +} + +static int ccwchain_fetch_ccw(struct ccw1 *ccw, + struct page_array *pa, + struct channel_program *cp) +{ + struct vfio_device *vdev = + &container_of(cp, struct vfio_ccw_private, cp)->vdev; + dma64_t *idaws; + dma32_t *idaws_f1; + int ret; + int idaw_nr; + int i; + + /* Calculate size of IDAL */ + idaw_nr = ccw_count_idaws(ccw, cp); + if (idaw_nr < 0) + return idaw_nr; /* Allocate an IDAL from host storage */ - idaws = kcalloc(idaw_nr, sizeof(*idaws), GFP_DMA | GFP_KERNEL); - if (!idaws) { - ret = -ENOMEM; + idaws = get_guest_idal(ccw, cp, idaw_nr); + if (IS_ERR(idaws)) { + ret = PTR_ERR(idaws); goto out_init; } @@ -594,40 +634,31 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, * required for the data transfer, since we only only support * 4K IDAWs today. */ - pa = chain->ch_pa + idx; - ret = page_array_alloc(pa, iova, bytes); + ret = page_array_alloc(pa, idaw_nr); if (ret < 0) goto out_free_idaws; - if (ccw_is_idal(ccw)) { - /* Copy guest IDAL into host IDAL */ - ret = copy_from_iova(vdev, idaws, ccw->cda, idal_len); - if (ret) - goto out_unpin; - - /* - * Copy guest IDAWs into page_array, in case the memory they - * occupy is not contiguous. - */ - for (i = 0; i < idaw_nr; i++) - pa->pa_iova[i] = idaws[i]; - } else { - /* - * No action is required here; the iova addresses in page_array - * were initialized sequentially in page_array_alloc() beginning - * with the contents of ccw->cda. - */ + /* + * Copy guest IDAWs into page_array, in case the memory they + * occupy is not contiguous. + */ + idaws_f1 = (dma32_t *)idaws; + for (i = 0; i < idaw_nr; i++) { + if (cp->orb.cmd.c64) + pa->pa_iova[i] = dma64_to_u64(idaws[i]); + else + pa->pa_iova[i] = dma32_to_u32(idaws_f1[i]); } if (ccw_does_data_transfer(ccw)) { - ret = page_array_pin(pa, vdev); + ret = page_array_pin(pa, vdev, idal_is_2k(cp)); if (ret < 0) goto out_unpin; } else { pa->pa_nr = 0; } - ccw->cda = (__u32) virt_to_phys(idaws); + ccw->cda = virt_to_dma32(idaws); ccw->flags |= CCW_FLAG_IDA; /* Populate the IDAL with pinned/translated addresses from page */ @@ -636,7 +667,7 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, return 0; out_unpin: - page_array_unpin_free(pa, vdev); + page_array_unpin_free(pa, vdev, idal_is_2k(cp)); out_free_idaws: kfree(idaws); out_init: @@ -647,25 +678,23 @@ out_init: /* * Fetch one ccw. * To reduce memory copy, we'll pin the cda page in memory, - * and to get rid of the cda 2G limitiaion of ccw1, we'll translate + * and to get rid of the cda 2G limitation of ccw1, we'll translate * direct ccws to idal ccws. */ -static int ccwchain_fetch_one(struct ccwchain *chain, - int idx, +static int ccwchain_fetch_one(struct ccw1 *ccw, + struct page_array *pa, struct channel_program *cp) -{ - struct ccw1 *ccw = chain->ch_ccw + idx; +{ if (ccw_is_tic(ccw)) - return ccwchain_fetch_tic(chain, idx, cp); + return ccwchain_fetch_tic(ccw, cp); - return ccwchain_fetch_direct(chain, idx, cp); + return ccwchain_fetch_ccw(ccw, pa, cp); } /** * cp_init() - allocate ccwchains for a channel program. * @cp: channel_program on which to perform the operation - * @mdev: the mediated device to perform pin/unpin operations * @orb: control block for the channel program from the guest * * This creates one or more ccwchain(s), and copies the raw data of @@ -708,15 +737,9 @@ int cp_init(struct channel_program *cp, union orb *orb) /* Build a ccwchain for the first CCW segment */ ret = ccwchain_handle_ccw(orb->cmd.cpa, cp); - if (!ret) { + if (!ret) cp->initialized = true; - /* It is safe to force: if it was not set but idals used - * ccwchain_calc_length would have returned an error. - */ - cp->orb.cmd.c64 = 1; - } - return ret; } @@ -742,7 +765,7 @@ void cp_free(struct channel_program *cp) cp->initialized = false; list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) { for (i = 0; i < chain->ch_len; i++) { - page_array_unpin_free(chain->ch_pa + i, vdev); + page_array_unpin_free(&chain->ch_pa[i], vdev, idal_is_2k(cp)); ccwchain_cda_free(chain, i); } ccwchain_free(chain); @@ -770,7 +793,7 @@ void cp_free(struct channel_program *cp) * program. * * These APIs will copy the ccws into kernel-space buffers, and update - * the guest phsical addresses with their corresponding host physical + * the guest physical addresses with their corresponding host physical * addresses. Then channel I/O device drivers could issue the * translated channel program to real devices to perform an I/O * operation. @@ -789,6 +812,8 @@ void cp_free(struct channel_program *cp) int cp_prefetch(struct channel_program *cp) { struct ccwchain *chain; + struct ccw1 *ccw; + struct page_array *pa; int len, idx, ret; /* this is an error in the caller */ @@ -798,7 +823,10 @@ int cp_prefetch(struct channel_program *cp) list_for_each_entry(chain, &cp->ccwchain_list, next) { len = chain->ch_len; for (idx = 0; idx < len; idx++) { - ret = ccwchain_fetch_one(chain, idx, cp); + ccw = &chain->ch_ccw[idx]; + pa = &chain->ch_pa[idx]; + + ret = ccwchain_fetch_one(ccw, pa, cp); if (ret) goto out_err; } @@ -817,14 +845,13 @@ out_err: /** * cp_get_orb() - get the orb of the channel program * @cp: channel_program on which to perform the operation - * @intparm: new intparm for the returned orb - * @lpm: candidate value of the logical-path mask for the returned orb + * @sch: subchannel the operation will be performed against * * This function returns the address of the updated orb of the channel * program. Channel I/O device drivers could use this orb to issue a * ssch. */ -union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm) +union orb *cp_get_orb(struct channel_program *cp, struct subchannel *sch) { union orb *orb; struct ccwchain *chain; @@ -836,16 +863,24 @@ union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm) orb = &cp->orb; - orb->cmd.intparm = intparm; + orb->cmd.intparm = (u32)virt_to_phys(sch); orb->cmd.fmt = 1; - orb->cmd.key = PAGE_DEFAULT_KEY >> 4; + + /* + * Everything built by vfio-ccw is a Format-2 IDAL. + * If the input was a Format-1 IDAL, indicate that + * 2K Format-2 IDAWs were created here. + */ + if (!orb->cmd.c64) + orb->cmd.i2k = 1; + orb->cmd.c64 = 1; if (orb->cmd.lpm == 0) - orb->cmd.lpm = lpm; + orb->cmd.lpm = sch->lpm; chain = list_first_entry(&cp->ccwchain_list, struct ccwchain, next); cpa = chain->ch_ccw; - orb->cmd.cpa = (__u32)virt_to_phys(cpa); + orb->cmd.cpa = virt_to_dma32(cpa); return orb; } @@ -867,7 +902,7 @@ union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm) void cp_update_scsw(struct channel_program *cp, union scsw *scsw) { struct ccwchain *chain; - u32 cpa = scsw->cmd.cpa; + dma32_t cpa = scsw->cmd.cpa; u32 ccw_head; if (!cp->initialized) @@ -880,7 +915,7 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw) * in the ioctl directly. Path status changes etc. */ list_for_each_entry(chain, &cp->ccwchain_list, next) { - ccw_head = (u32)(u64)chain->ch_ccw; + ccw_head = dma32_to_u32(virt_to_dma32(chain->ch_ccw)); /* * On successful execution, cpa points just beyond the end * of the chain. @@ -890,9 +925,10 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw) * (cpa - ccw_head) is the offset value of the host * physical ccw to its chain head. * Adding this value to the guest physical ccw chain - * head gets us the guest cpa. + * head gets us the guest cpa: + * cpa = chain->ch_iova + (cpa - ccw_head) */ - cpa = chain->ch_iova + (cpa - ccw_head); + cpa = dma32_add(cpa, chain->ch_iova - ccw_head); break; } } @@ -919,7 +955,7 @@ bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length) list_for_each_entry(chain, &cp->ccwchain_list, next) { for (i = 0; i < chain->ch_len; i++) - if (page_array_iova_pinned(chain->ch_pa + i, iova, length)) + if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) return true; } |
