Commit 2ae157ec authored by Eric Farman's avatar Eric Farman Committed by Heiko Carstens

s390/vfio_ccw: Fix target addresses of TIC CCWs

The processing of a Transfer-In-Channel (TIC) CCW requires locating
the target of the CCW in the channel program, and updating the
address to reflect what will actually be sent to hardware.

An error exists where the 64-bit virtual address is truncated to
32-bits (variable "cda") when performing this math. Since s390
addresses of that size are 31-bits, this leaves that additional
bit enabled such that the resulting I/O triggers a channel
program check. This shows up occasionally when booting a KVM
guest from a passthrough DASD device:

  ..snip...
  Interrupt Response Block Data:
  : 0x0000000000003990
      Function Ctrl : [Start]
      Activity Ctrl :
      Status Ctrl : [Alert] [Primary] [Secondary] [Status-Pending]
      Device Status :
      Channel Status : [Program-Check]
      cpa=: 0x00000000008d0018
      prev_ccw=: 0x0000000000000000
      this_ccw=: 0x0000000000000000
  ...snip...
  dasd-ipl: Failed to run IPL1 channel program

The channel program address of "0x008d0018" in the IRB doesn't
look wrong, but tracing the CCWs shows the offending bit enabled:

  ccw=0x0000012e808d0000 cda=00a0b030
  ccw=0x0000012e808d0008 cda=00a0b038
  ccw=0x0000012e808d0010 cda=808d0008
  ccw=0x0000012e808d0018 cda=00a0b040

Fix the calculation of the TIC CCW's data address such that it points
to a valid 31-bit address regardless of the input address.

Fixes: bd36cfbb ("s390/vfio_ccw_cp: use new address translation helpers")
Signed-off-by: default avatarEric Farman <farman@linux.ibm.com>
Reviewed-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Link: https://lore.kernel.org/r/20240628163738.3643513-1-farman@linux.ibm.comSigned-off-by: default avatarAlexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent feaa3344
...@@ -490,13 +490,14 @@ static int ccwchain_fetch_tic(struct ccw1 *ccw, ...@@ -490,13 +490,14 @@ static int ccwchain_fetch_tic(struct ccw1 *ccw,
struct channel_program *cp) struct channel_program *cp)
{ {
struct ccwchain *iter; struct ccwchain *iter;
u32 cda, ccw_head; u32 offset, ccw_head;
list_for_each_entry(iter, &cp->ccwchain_list, next) { list_for_each_entry(iter, &cp->ccwchain_list, next) {
ccw_head = iter->ch_iova; ccw_head = iter->ch_iova;
if (is_cpa_within_range(ccw->cda, ccw_head, iter->ch_len)) { if (is_cpa_within_range(ccw->cda, ccw_head, iter->ch_len)) {
cda = (u64)iter->ch_ccw + dma32_to_u32(ccw->cda) - ccw_head; /* Calculate offset of TIC target */
ccw->cda = u32_to_dma32(cda); offset = dma32_to_u32(ccw->cda) - ccw_head;
ccw->cda = virt_to_dma32((void *)iter->ch_ccw + offset);
return 0; return 0;
} }
} }
...@@ -914,7 +915,7 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw) ...@@ -914,7 +915,7 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw)
* in the ioctl directly. Path status changes etc. * in the ioctl directly. Path status changes etc.
*/ */
list_for_each_entry(chain, &cp->ccwchain_list, next) { 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 * On successful execution, cpa points just beyond the end
* of the chain. * of the chain.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment