/* * bebob_command.c - driver for BeBoB based devices * * Copyright (c) 2013-2014 Takashi Sakamoto * * Licensed under the terms of the GNU General Public License, version 2. */ #include "./bebob.h" int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id, unsigned int fb_id, unsigned int num) { u8 *buf; int err; buf = kzalloc(12, GFP_KERNEL); if (buf == NULL) return -ENOMEM; buf[0] = 0x00; /* AV/C CONTROL */ buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */ buf[2] = 0xb8; /* FUNCTION BLOCK */ buf[3] = 0x80; /* type is 'selector'*/ buf[4] = 0xff & fb_id; /* function block id */ buf[5] = 0x10; /* control attribute is CURRENT */ buf[6] = 0x02; /* selector length is 2 */ buf[7] = 0xff & num; /* input function block plug number */ buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */ err = fcp_avc_transaction(unit, buf, 12, buf, 12, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8)); if (err > 0 && err < 9) err = -EIO; else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ err = -ENOSYS; else if (buf[0] == 0x0a) /* REJECTED */ err = -EINVAL; else if (err > 0) err = 0; kfree(buf); return err; } int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id, unsigned int fb_id, unsigned int *num) { u8 *buf; int err; buf = kzalloc(12, GFP_KERNEL); if (buf == NULL) return -ENOMEM; buf[0] = 0x01; /* AV/C STATUS */ buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */ buf[2] = 0xb8; /* FUNCTION BLOCK */ buf[3] = 0x80; /* type is 'selector'*/ buf[4] = 0xff & fb_id; /* function block id */ buf[5] = 0x10; /* control attribute is CURRENT */ buf[6] = 0x02; /* selector length is 2 */ buf[7] = 0xff; /* input function block plug number */ buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */ err = fcp_avc_transaction(unit, buf, 12, buf, 12, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(8)); if (err > 0 && err < 9) err = -EIO; else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ err = -ENOSYS; else if (buf[0] == 0x0a) /* REJECTED */ err = -EINVAL; else if (buf[0] == 0x0b) /* IN TRANSITION */ err = -EAGAIN; if (err < 0) goto end; *num = buf[7]; err = 0; end: kfree(buf); return err; } static inline void avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr) { buf[1] = addr[0]; memcpy(buf + 4, addr + 1, 5); } static inline void avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr, unsigned int itype) { buf[0] = 0x01; /* AV/C STATUS */ buf[2] = 0x02; /* AV/C GENERAL PLUG INFO */ buf[3] = 0xc0; /* BridgeCo extension */ avc_bridgeco_fill_extension_addr(buf, addr); buf[9] = itype; /* info type */ } int avc_bridgeco_get_plug_type(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES], enum avc_bridgeco_plug_type *type) { u8 *buf; int err; buf = kzalloc(12, GFP_KERNEL); if (buf == NULL) return -ENOMEM; /* Info type is 'plug type'. */ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00); err = fcp_avc_transaction(unit, buf, 12, buf, 12, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9)); if ((err >= 0) && (err < 8)) err = -EIO; else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ err = -ENOSYS; else if (buf[0] == 0x0a) /* REJECTED */ err = -EINVAL; else if (buf[0] == 0x0b) /* IN TRANSITION */ err = -EAGAIN; if (err < 0) goto end; *type = buf[10]; err = 0; end: kfree(buf); return err; } int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf, unsigned int len) { int err; /* Info type is 'channel position'. */ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03); err = fcp_avc_transaction(unit, buf, 12, buf, 256, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9)); if ((err >= 0) && (err < 8)) err = -EIO; else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ err = -ENOSYS; else if (buf[0] == 0x0a) /* REJECTED */ err = -EINVAL; else if (buf[0] == 0x0b) /* IN TRANSITION */ err = -EAGAIN; if (err < 0) goto end; /* Pick up specific data. */ memmove(buf, buf + 10, err - 10); err = 0; end: return err; } int avc_bridgeco_get_plug_section_type(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES], unsigned int id, u8 *type) { u8 *buf; int err; /* section info includes charactors but this module don't need it */ buf = kzalloc(12, GFP_KERNEL); if (buf == NULL) return -ENOMEM; /* Info type is 'section info'. */ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07); buf[10] = 0xff & ++id; /* section id */ err = fcp_avc_transaction(unit, buf, 12, buf, 12, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9) | BIT(10)); if ((err >= 0) && (err < 8)) err = -EIO; else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ err = -ENOSYS; else if (buf[0] == 0x0a) /* REJECTED */ err = -EINVAL; else if (buf[0] == 0x0b) /* IN TRANSITION */ err = -EAGAIN; if (err < 0) goto end; *type = buf[11]; err = 0; end: kfree(buf); return err; } int avc_bridgeco_get_plug_input(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7]) { int err; u8 *buf; buf = kzalloc(18, GFP_KERNEL); if (buf == NULL) return -ENOMEM; /* Info type is 'plug input'. */ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05); err = fcp_avc_transaction(unit, buf, 16, buf, 16, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7)); if ((err >= 0) && (err < 8)) err = -EIO; else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ err = -ENOSYS; else if (buf[0] == 0x0a) /* REJECTED */ err = -EINVAL; else if (buf[0] == 0x0b) /* IN TRANSITION */ err = -EAGAIN; if (err < 0) goto end; memcpy(input, buf + 10, 5); err = 0; end: kfree(buf); return err; } int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf, unsigned int *len, unsigned int eid) { int err; /* check given buffer */ if ((buf == NULL) || (*len < 12)) { err = -EINVAL; goto end; } buf[0] = 0x01; /* AV/C STATUS */ buf[2] = 0x2f; /* AV/C STREAM FORMAT SUPPORT */ buf[3] = 0xc1; /* Bridgeco extension - List Request */ avc_bridgeco_fill_extension_addr(buf, addr); buf[10] = 0xff & eid; /* Entry ID */ err = fcp_avc_transaction(unit, buf, 12, buf, *len, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(10)); if ((err >= 0) && (err < 12)) err = -EIO; else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ err = -ENOSYS; else if (buf[0] == 0x0a) /* REJECTED */ err = -EINVAL; else if (buf[0] == 0x0b) /* IN TRANSITION */ err = -EAGAIN; else if (buf[10] != eid) err = -EIO; if (err < 0) goto end; /* Pick up 'stream format info'. */ memmove(buf, buf + 11, err - 11); *len = err - 11; err = 0; end: return err; } creation. The iommu_data pointer hence remains null. This causes a kernel oops when userspace tries to set the iommu type of a container associated with a mediated device to VFIO_SPAPR_TCE_v2_IOMMU. [ 82.585440] mtty mtty: MDEV: Registered [ 87.655522] iommu: Adding device 83b8f4f2-509f-382f-3c1e-e6bfe0fa1001 to group 10 [ 87.655527] vfio_mdev 83b8f4f2-509f-382f-3c1e-e6bfe0fa1001: MDEV: group_id = 10 [ 116.297184] Unable to handle kernel paging request for data at address 0x00000030 [ 116.297389] Faulting instruction address: 0xd000000007870524 [ 116.297465] Oops: Kernel access of bad area, sig: 11 [#1] [ 116.297611] SMP NR_CPUS=2048 [ 116.297611] NUMA [ 116.297627] PowerNV ... [ 116.297954] CPU: 33 PID: 7067 Comm: qemu-system-ppc Not tainted 4.10.0-rc5-mdev-test #8 [ 116.297993] task: c000000e7718b680 task.stack: c000000e77214000 [ 116.298025] NIP: d000000007870524 LR: d000000007870518 CTR: 0000000000000000 [ 116.298064] REGS: c000000e77217990 TRAP: 0300 Not tainted (4.10.0-rc5-mdev-test) [ 116.298103] MSR: 9000000000009033 <SF,HV,EE,ME,IR,DR,RI,LE> [ 116.298107] CR: 84004444 XER: 00000000 [ 116.298154] CFAR: c00000000000888c DAR: 0000000000000030 DSISR: 40000000 SOFTE: 1 GPR00: d000000007870518 c000000e77217c10 d00000000787b0ed c000000eed2103c0 GPR04: 0000000000000000 0000000000000000 c000000eed2103e0 0000000f24320000 GPR08: 0000000000000104 0000000000000001 0000000000000000 d0000000078729b0 GPR12: c00000000025b7e0 c00000000fe08400 0000000000000001 000001002d31d100 GPR16: 000001002c22c850 00003ffff315c750 0000000043145680 0000000043141bc0 GPR20: ffffffffffffffed fffffffffffff000 0000000020003b65 d000000007706018 GPR24: c000000f16cf0d98 d000000007706000 c000000003f42980 c000000003f42980 GPR28: c000000f1575ac00 c000000003f429c8 0000000000000000 c000000eed2103c0 [ 116.298504] NIP [d000000007870524] tce_iommu_attach_group+0x10c/0x360 [vfio_iommu_spapr_tce] [ 116.298555] LR [d000000007870518] tce_iommu_attach_group+0x100/0x360 [vfio_iommu_spapr_tce] [ 116.298601] Call Trace: [ 116.298610] [c000000e77217c10] [d000000007870518] tce_iommu_attach_group+0x100/0x360 [vfio_iommu_spapr_tce] (unreliable) [ 116.298671] [c000000e77217cb0] [d0000000077033a0] vfio_fops_unl_ioctl+0x278/0x3e0 [vfio] [ 116.298713] [c000000e77217d40] [c0000000002a3ebc] do_vfs_ioctl+0xcc/0x8b0 [ 116.298745] [c000000e77217de0] [c0000000002a4700] SyS_ioctl+0x60/0xc0 [ 116.298782] [c000000e77217e30] [c00000000000b220] system_call+0x38/0xfc [ 116.298812] Instruction dump: [ 116.298828] 7d3f4b78 409effc8 3d220000 e9298020 3c800140 38a00018 608480c0 e8690028 [ 116.298869] 4800249d e8410018 7c7f1b79 41820230 <e93e0030> 2fa90000 419e0114 e9090020 [ 116.298914] ---[ end trace 1e10b0ced08b9120 ]--- This patch fixes the oops. Reported-by: Vaibhav Jain <vaibhav@linux.vnet.ibm.com> Signed-off-by: Greg Kurz <groug@kaod.org> Signed-off-by: Alex Williamson <alex.williamson@redhat.com>