OpenCloudOS-Kernel/drivers/media/pci/b2c2/flexcop-pci.c

441 lines
12 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* Linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-pci.c - covers the PCI part including DMA transfers
* see flexcop.c for copyright information
*/
#define FC_LOG_PREFIX "flexcop-pci"
#include "flexcop-common.h"
static int enable_pid_filtering = 1;
module_param(enable_pid_filtering, int, 0444);
MODULE_PARM_DESC(enable_pid_filtering,
"enable hardware pid filtering: supported values: 0 (fullts), 1");
static int irq_chk_intv = 100;
module_param(irq_chk_intv, int, 0644);
MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog.");
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level, args...) \
do { if ((debug & (level))) printk(args); } while (0)
#define DEBSTATUS ""
#else
#define dprintk(level, args...) no_printk(args)
#define DEBSTATUS " (debugging is not enabled)"
#endif
#define deb_info(args...) dprintk(0x01, args)
#define deb_reg(args...) dprintk(0x02, args)
#define deb_ts(args...) dprintk(0x04, args)
#define deb_irq(args...) dprintk(0x08, args)
#define deb_chk(args...) dprintk(0x10, args)
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))."
DEBSTATUS);
#define DRIVER_VERSION "0.1"
#define DRIVER_NAME "flexcop-pci"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de>"
struct flexcop_pci {
struct pci_dev *pdev;
#define FC_PCI_INIT 0x01
#define FC_PCI_DMA_INIT 0x02
int init_state;
void __iomem *io_mem;
u32 irq;
/* buffersize (at least for DMA1, need to be % 188 == 0,
* this logic is required */
#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188)
#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188)
struct flexcop_dma dma[2];
int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */
u32 last_dma1_cur_pos;
/* position of the pointer last time the timer/packet irq occurred */
int count;
int count_prev;
int stream_problem;
spinlock_t irq_lock;
unsigned long last_irq;
struct delayed_work irq_check_work;
struct flexcop_device *fc_dev;
};
static int lastwreg, lastwval, lastrreg, lastrval;
static flexcop_ibi_value flexcop_pci_read_ibi_reg(struct flexcop_device *fc,
flexcop_ibi_register r)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
flexcop_ibi_value v;
v.raw = readl(fc_pci->io_mem + r);
if (lastrreg != r || lastrval != v.raw) {
lastrreg = r; lastrval = v.raw;
deb_reg("new rd: %3x: %08x\n", r, v.raw);
}
return v;
}
static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc,
flexcop_ibi_register r, flexcop_ibi_value v)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
if (lastwreg != r || lastwval != v.raw) {
lastwreg = r; lastwval = v.raw;
deb_reg("new wr: %3x: %08x\n", r, v.raw);
}
writel(v.raw, fc_pci->io_mem + r);
return 0;
}
static void flexcop_pci_irq_check_work(struct work_struct *work)
{
struct flexcop_pci *fc_pci =
container_of(work, struct flexcop_pci, irq_check_work.work);
struct flexcop_device *fc = fc_pci->fc_dev;
if (fc->feedcount) {
if (fc_pci->count == fc_pci->count_prev) {
deb_chk("no IRQ since the last check\n");
if (fc_pci->stream_problem++ == 3) {
struct dvb_demux_feed *feed;
deb_info("flexcop-pci: stream problem, resetting pid filter\n");
spin_lock_irq(&fc->demux.lock);
list_for_each_entry(feed, &fc->demux.feed_list,
list_head) {
flexcop_pid_feed_control(fc, feed, 0);
}
list_for_each_entry(feed, &fc->demux.feed_list,
list_head) {
flexcop_pid_feed_control(fc, feed, 1);
}
spin_unlock_irq(&fc->demux.lock);
fc_pci->stream_problem = 0;
}
} else {
fc_pci->stream_problem = 0;
fc_pci->count_prev = fc_pci->count;
}
}
schedule_delayed_work(&fc_pci->irq_check_work,
msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv));
}
/* When PID filtering is turned on, we use the timer IRQ, because small amounts
* of data need to be passed to the user space instantly as well. When PID
* filtering is turned off, we use the page-change-IRQ */
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers Maintain a per-CPU global "struct pt_regs *" variable which can be used instead of passing regs around manually through all ~1800 interrupt handlers in the Linux kernel. The regs pointer is used in few places, but it potentially costs both stack space and code to pass it around. On the FRV arch, removing the regs parameter from all the genirq function results in a 20% speed up of the IRQ exit path (ie: from leaving timer_interrupt() to leaving do_IRQ()). Where appropriate, an arch may override the generic storage facility and do something different with the variable. On FRV, for instance, the address is maintained in GR28 at all times inside the kernel as part of general exception handling. Having looked over the code, it appears that the parameter may be handed down through up to twenty or so layers of functions. Consider a USB character device attached to a USB hub, attached to a USB controller that posts its interrupts through a cascaded auxiliary interrupt controller. A character device driver may want to pass regs to the sysrq handler through the input layer which adds another few layers of parameter passing. I've build this code with allyesconfig for x86_64 and i386. I've runtested the main part of the code on FRV and i386, though I can't test most of the drivers. I've also done partial conversion for powerpc and MIPS - these at least compile with minimal configurations. This will affect all archs. Mostly the changes should be relatively easy. Take do_IRQ(), store the regs pointer at the beginning, saving the old one: struct pt_regs *old_regs = set_irq_regs(regs); And put the old one back at the end: set_irq_regs(old_regs); Don't pass regs through to generic_handle_irq() or __do_IRQ(). In timer_interrupt(), this sort of change will be necessary: - update_process_times(user_mode(regs)); - profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(get_irq_regs())); + profile_tick(CPU_PROFILING); I'd like to move update_process_times()'s use of get_irq_regs() into itself, except that i386, alone of the archs, uses something other than user_mode(). Some notes on the interrupt handling in the drivers: (*) input_dev() is now gone entirely. The regs pointer is no longer stored in the input_dev struct. (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does something different depending on whether it's been supplied with a regs pointer or not. (*) Various IRQ handler function pointers have been moved to type irq_handler_t. Signed-Off-By: David Howells <dhowells@redhat.com> (cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)
{
struct flexcop_pci *fc_pci = dev_id;
struct flexcop_device *fc = fc_pci->fc_dev;
unsigned long flags;
flexcop_ibi_value v;
irqreturn_t ret = IRQ_HANDLED;
spin_lock_irqsave(&fc_pci->irq_lock, flags);
v = fc->read_ibi_reg(fc, irq_20c);
/* errors */
if (v.irq_20c.Data_receiver_error)
deb_chk("data receiver error\n");
if (v.irq_20c.Continuity_error_flag)
deb_chk("Continuity error flag is set\n");
if (v.irq_20c.LLC_SNAP_FLAG_set)
deb_chk("LLC_SNAP_FLAG_set is set\n");
if (v.irq_20c.Transport_Error)
deb_chk("Transport error\n");
if ((fc_pci->count % 1000) == 0)
deb_chk("%d valid irq took place so far\n", fc_pci->count);
if (v.irq_20c.DMA1_IRQ_Status == 1) {
if (fc_pci->active_dma1_addr == 0)
flexcop_pass_dmx_packets(fc_pci->fc_dev,
fc_pci->dma[0].cpu_addr0,
fc_pci->dma[0].size / 188);
else
flexcop_pass_dmx_packets(fc_pci->fc_dev,
fc_pci->dma[0].cpu_addr1,
fc_pci->dma[0].size / 188);
deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr);
fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr;
/* for the timer IRQ we only can use buffer dmx feeding, because we don't have
* complete TS packets when reading from the DMA memory */
} else if (v.irq_20c.DMA1_Timer_Status == 1) {
dma_addr_t cur_addr =
fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2;
u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0;
media: b2c2: Add missing check in flexcop_pci_isr: A out-of-bounds bug can be triggered by an interrupt, the reason for this bug is the lack of checking of register values. In flexcop_pci_isr, the driver reads value from a register and uses it as a dma address. Finally, this address will be passed to the count parameter of find_next_packet. If this value is larger than the size of dma, the index of buffer will be out-of-bounds. Fix this by adding a check after reading the value of the register. The following KASAN report reveals it: BUG: KASAN: slab-out-of-bounds in find_next_packet drivers/media/dvb-core/dvb_demux.c:528 [inline] BUG: KASAN: slab-out-of-bounds in _dvb_dmx_swfilter drivers/media/dvb-core/dvb_demux.c:572 [inline] BUG: KASAN: slab-out-of-bounds in dvb_dmx_swfilter+0x3fa/0x420 drivers/media/dvb-core/dvb_demux.c:603 Read of size 1 at addr ffff8880608c00a0 by task swapper/2/0 CPU: 2 PID: 0 Comm: swapper/2 Not tainted 4.19.177-gdba4159c14ef #25 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba5276e321-prebuilt.qemu.org 04/01/2014 Call Trace: <IRQ> __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xec/0x156 lib/dump_stack.c:118 print_address_description+0x78/0x290 mm/kasan/report.c:256 kasan_report_error mm/kasan/report.c:354 [inline] kasan_report+0x25b/0x380 mm/kasan/report.c:412 __asan_report_load1_noabort+0x19/0x20 mm/kasan/report.c:430 find_next_packet drivers/media/dvb-core/dvb_demux.c:528 [inline] _dvb_dmx_swfilter drivers/media/dvb-core/dvb_demux.c:572 [inline] dvb_dmx_swfilter+0x3fa/0x420 drivers/media/dvb-core/dvb_demux.c:603 flexcop_pass_dmx_data+0x2e/0x40 drivers/media/common/b2c2/flexcop.c:167 flexcop_pci_isr+0x3d1/0x5d0 drivers/media/pci/b2c2/flexcop-pci.c:212 __handle_irq_event_percpu+0xfb/0x770 kernel/irq/handle.c:149 handle_irq_event_percpu+0x79/0x150 kernel/irq/handle.c:189 handle_irq_event+0xac/0x140 kernel/irq/handle.c:206 handle_fasteoi_irq+0x232/0x5c0 kernel/irq/chip.c:725 generic_handle_irq_desc include/linux/irqdesc.h:155 [inline] handle_irq+0x230/0x3a0 arch/x86/kernel/irq_64.c:87 do_IRQ+0xa7/0x1e0 arch/x86/kernel/irq.c:247 common_interrupt+0xf/0xf arch/x86/entry/entry_64.S:670 </IRQ> RIP: 0010:native_safe_halt+0x28/0x30 arch/x86/include/asm/irqflags.h:61 Code: 00 00 55 be 04 00 00 00 48 c7 c7 00 62 2f 8c 48 89 e5 e8 fb 31 e8 f8 8b 05 75 4f 8e 03 85 c0 7e 07 0f 00 2d 8a 61 66 00 fb f4 <5d> c3 90 90 90 90 90 90 0f 1f 44 00 00 55 48 89 e5 41 57 41 56 41 RSP: 0018:ffff88806b71fcc8 EFLAGS: 00000246 ORIG_RAX: ffffffffffffffde RAX: 0000000000000000 RBX: ffffffff8bde44c8 RCX: ffffffff88a11285 RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffffffff8c2f6200 RBP: ffff88806b71fcc8 R08: fffffbfff185ec40 R09: fffffbfff185ec40 R10: 0000000000000001 R11: fffffbfff185ec40 R12: 0000000000000002 R13: ffffffff8be9d6e0 R14: 0000000000000000 R15: 0000000000000000 arch_safe_halt arch/x86/include/asm/paravirt.h:94 [inline] default_idle+0x6f/0x360 arch/x86/kernel/process.c:557 arch_cpu_idle+0xf/0x20 arch/x86/kernel/process.c:548 default_idle_call+0x3b/0x60 kernel/sched/idle.c:93 cpuidle_idle_call kernel/sched/idle.c:153 [inline] do_idle+0x2ab/0x3c0 kernel/sched/idle.c:263 cpu_startup_entry+0xcb/0xe0 kernel/sched/idle.c:369 start_secondary+0x3b8/0x4e0 arch/x86/kernel/smpboot.c:271 secondary_startup_64+0xa4/0xb0 arch/x86/kernel/head_64.S:243 Allocated by task 1: save_stack+0x43/0xd0 mm/kasan/kasan.c:448 set_track mm/kasan/kasan.c:460 [inline] kasan_kmalloc+0xad/0xe0 mm/kasan/kasan.c:553 kasan_slab_alloc+0x11/0x20 mm/kasan/kasan.c:490 slab_post_alloc_hook mm/slab.h:445 [inline] slab_alloc_node mm/slub.c:2741 [inline] slab_alloc mm/slub.c:2749 [inline] kmem_cache_alloc+0xeb/0x280 mm/slub.c:2754 kmem_cache_zalloc include/linux/slab.h:699 [inline] __kernfs_new_node+0xe2/0x6f0 fs/kernfs/dir.c:633 kernfs_new_node+0x9a/0x120 fs/kernfs/dir.c:693 __kernfs_create_file+0x5f/0x340 fs/kernfs/file.c:992 sysfs_add_file_mode_ns+0x22a/0x4e0 fs/sysfs/file.c:306 create_files fs/sysfs/group.c:63 [inline] internal_create_group+0x34e/0xc30 fs/sysfs/group.c:147 sysfs_create_group fs/sysfs/group.c:173 [inline] sysfs_create_groups+0x9c/0x140 fs/sysfs/group.c:200 driver_add_groups+0x3e/0x50 drivers/base/driver.c:129 bus_add_driver+0x3a5/0x790 drivers/base/bus.c:684 driver_register+0x1cd/0x410 drivers/base/driver.c:170 __pci_register_driver+0x197/0x200 drivers/pci/pci-driver.c:1411 cx88_audio_pci_driver_init+0x23/0x25 drivers/media/pci/cx88/cx88-alsa.c: 1017 do_one_initcall+0xe0/0x610 init/main.c:884 do_initcall_level init/main.c:952 [inline] do_initcalls init/main.c:960 [inline] do_basic_setup init/main.c:978 [inline] kernel_init_freeable+0x4d0/0x592 init/main.c:1145 kernel_init+0x18/0x190 init/main.c:1062 ret_from_fork+0x24/0x30 arch/x86/entry/entry_64.S:415 Freed by task 0: (stack is not available) The buggy address belongs to the object at ffff8880608c0000 which belongs to the cache kernfs_node_cache of size 160 The buggy address is located 0 bytes to the right of 160-byte region [ffff8880608c0000, ffff8880608c00a0) The buggy address belongs to the page: page:ffffea0001823000 count:1 mapcount:0 mapping:ffff88806bed1e00 index:0x0 compound_mapcount: 0 flags: 0x100000000008100(slab|head) raw: 0100000000008100 dead000000000100 dead000000000200 ffff88806bed1e00 raw: 0000000000000000 0000000000240024 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff8880608bff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff8880608c0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff8880608c0080: 00 00 00 00 fc fc fc fc fc fc fc fc 00 00 00 00 ^ ffff8880608c0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff8880608c0180: fc fc fc fc fc fc fc fc 00 00 00 00 00 00 00 00 ================================================================== Link: https://lore.kernel.org/linux-media/1620723603-30912-1-git-send-email-zheyuma97@gmail.com Reported-by: Zheyu Ma <zheyuma97@gmail.com> Signed-off-by: Zheyu Ma <zheyuma97@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-05-11 17:00:03 +08:00
if (cur_pos > fc_pci->dma[0].size * 2)
goto error;
deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, last_cur_pos: %08x ",
jiffies_to_usecs(jiffies - fc_pci->last_irq),
v.raw, (unsigned long long)cur_addr, cur_pos,
fc_pci->last_dma1_cur_pos);
fc_pci->last_irq = jiffies;
/* buffer end was reached, restarted from the beginning
* pass the data from last_cur_pos to the buffer end to the demux
*/
if (cur_pos < fc_pci->last_dma1_cur_pos) {
deb_irq(" end was reached: passing %d bytes ",
(fc_pci->dma[0].size*2 - 1) -
fc_pci->last_dma1_cur_pos);
flexcop_pass_dmx_data(fc_pci->fc_dev,
fc_pci->dma[0].cpu_addr0 +
fc_pci->last_dma1_cur_pos,
(fc_pci->dma[0].size*2) -
fc_pci->last_dma1_cur_pos);
fc_pci->last_dma1_cur_pos = 0;
}
if (cur_pos > fc_pci->last_dma1_cur_pos) {
deb_irq(" passing %d bytes ",
cur_pos - fc_pci->last_dma1_cur_pos);
flexcop_pass_dmx_data(fc_pci->fc_dev,
fc_pci->dma[0].cpu_addr0 +
fc_pci->last_dma1_cur_pos,
cur_pos - fc_pci->last_dma1_cur_pos);
}
deb_irq("\n");
fc_pci->last_dma1_cur_pos = cur_pos;
fc_pci->count++;
} else {
deb_irq("isr for flexcop called, apparently without reason (%08x)\n",
v.raw);
ret = IRQ_NONE;
}
media: b2c2: Add missing check in flexcop_pci_isr: A out-of-bounds bug can be triggered by an interrupt, the reason for this bug is the lack of checking of register values. In flexcop_pci_isr, the driver reads value from a register and uses it as a dma address. Finally, this address will be passed to the count parameter of find_next_packet. If this value is larger than the size of dma, the index of buffer will be out-of-bounds. Fix this by adding a check after reading the value of the register. The following KASAN report reveals it: BUG: KASAN: slab-out-of-bounds in find_next_packet drivers/media/dvb-core/dvb_demux.c:528 [inline] BUG: KASAN: slab-out-of-bounds in _dvb_dmx_swfilter drivers/media/dvb-core/dvb_demux.c:572 [inline] BUG: KASAN: slab-out-of-bounds in dvb_dmx_swfilter+0x3fa/0x420 drivers/media/dvb-core/dvb_demux.c:603 Read of size 1 at addr ffff8880608c00a0 by task swapper/2/0 CPU: 2 PID: 0 Comm: swapper/2 Not tainted 4.19.177-gdba4159c14ef #25 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba5276e321-prebuilt.qemu.org 04/01/2014 Call Trace: <IRQ> __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xec/0x156 lib/dump_stack.c:118 print_address_description+0x78/0x290 mm/kasan/report.c:256 kasan_report_error mm/kasan/report.c:354 [inline] kasan_report+0x25b/0x380 mm/kasan/report.c:412 __asan_report_load1_noabort+0x19/0x20 mm/kasan/report.c:430 find_next_packet drivers/media/dvb-core/dvb_demux.c:528 [inline] _dvb_dmx_swfilter drivers/media/dvb-core/dvb_demux.c:572 [inline] dvb_dmx_swfilter+0x3fa/0x420 drivers/media/dvb-core/dvb_demux.c:603 flexcop_pass_dmx_data+0x2e/0x40 drivers/media/common/b2c2/flexcop.c:167 flexcop_pci_isr+0x3d1/0x5d0 drivers/media/pci/b2c2/flexcop-pci.c:212 __handle_irq_event_percpu+0xfb/0x770 kernel/irq/handle.c:149 handle_irq_event_percpu+0x79/0x150 kernel/irq/handle.c:189 handle_irq_event+0xac/0x140 kernel/irq/handle.c:206 handle_fasteoi_irq+0x232/0x5c0 kernel/irq/chip.c:725 generic_handle_irq_desc include/linux/irqdesc.h:155 [inline] handle_irq+0x230/0x3a0 arch/x86/kernel/irq_64.c:87 do_IRQ+0xa7/0x1e0 arch/x86/kernel/irq.c:247 common_interrupt+0xf/0xf arch/x86/entry/entry_64.S:670 </IRQ> RIP: 0010:native_safe_halt+0x28/0x30 arch/x86/include/asm/irqflags.h:61 Code: 00 00 55 be 04 00 00 00 48 c7 c7 00 62 2f 8c 48 89 e5 e8 fb 31 e8 f8 8b 05 75 4f 8e 03 85 c0 7e 07 0f 00 2d 8a 61 66 00 fb f4 <5d> c3 90 90 90 90 90 90 0f 1f 44 00 00 55 48 89 e5 41 57 41 56 41 RSP: 0018:ffff88806b71fcc8 EFLAGS: 00000246 ORIG_RAX: ffffffffffffffde RAX: 0000000000000000 RBX: ffffffff8bde44c8 RCX: ffffffff88a11285 RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffffffff8c2f6200 RBP: ffff88806b71fcc8 R08: fffffbfff185ec40 R09: fffffbfff185ec40 R10: 0000000000000001 R11: fffffbfff185ec40 R12: 0000000000000002 R13: ffffffff8be9d6e0 R14: 0000000000000000 R15: 0000000000000000 arch_safe_halt arch/x86/include/asm/paravirt.h:94 [inline] default_idle+0x6f/0x360 arch/x86/kernel/process.c:557 arch_cpu_idle+0xf/0x20 arch/x86/kernel/process.c:548 default_idle_call+0x3b/0x60 kernel/sched/idle.c:93 cpuidle_idle_call kernel/sched/idle.c:153 [inline] do_idle+0x2ab/0x3c0 kernel/sched/idle.c:263 cpu_startup_entry+0xcb/0xe0 kernel/sched/idle.c:369 start_secondary+0x3b8/0x4e0 arch/x86/kernel/smpboot.c:271 secondary_startup_64+0xa4/0xb0 arch/x86/kernel/head_64.S:243 Allocated by task 1: save_stack+0x43/0xd0 mm/kasan/kasan.c:448 set_track mm/kasan/kasan.c:460 [inline] kasan_kmalloc+0xad/0xe0 mm/kasan/kasan.c:553 kasan_slab_alloc+0x11/0x20 mm/kasan/kasan.c:490 slab_post_alloc_hook mm/slab.h:445 [inline] slab_alloc_node mm/slub.c:2741 [inline] slab_alloc mm/slub.c:2749 [inline] kmem_cache_alloc+0xeb/0x280 mm/slub.c:2754 kmem_cache_zalloc include/linux/slab.h:699 [inline] __kernfs_new_node+0xe2/0x6f0 fs/kernfs/dir.c:633 kernfs_new_node+0x9a/0x120 fs/kernfs/dir.c:693 __kernfs_create_file+0x5f/0x340 fs/kernfs/file.c:992 sysfs_add_file_mode_ns+0x22a/0x4e0 fs/sysfs/file.c:306 create_files fs/sysfs/group.c:63 [inline] internal_create_group+0x34e/0xc30 fs/sysfs/group.c:147 sysfs_create_group fs/sysfs/group.c:173 [inline] sysfs_create_groups+0x9c/0x140 fs/sysfs/group.c:200 driver_add_groups+0x3e/0x50 drivers/base/driver.c:129 bus_add_driver+0x3a5/0x790 drivers/base/bus.c:684 driver_register+0x1cd/0x410 drivers/base/driver.c:170 __pci_register_driver+0x197/0x200 drivers/pci/pci-driver.c:1411 cx88_audio_pci_driver_init+0x23/0x25 drivers/media/pci/cx88/cx88-alsa.c: 1017 do_one_initcall+0xe0/0x610 init/main.c:884 do_initcall_level init/main.c:952 [inline] do_initcalls init/main.c:960 [inline] do_basic_setup init/main.c:978 [inline] kernel_init_freeable+0x4d0/0x592 init/main.c:1145 kernel_init+0x18/0x190 init/main.c:1062 ret_from_fork+0x24/0x30 arch/x86/entry/entry_64.S:415 Freed by task 0: (stack is not available) The buggy address belongs to the object at ffff8880608c0000 which belongs to the cache kernfs_node_cache of size 160 The buggy address is located 0 bytes to the right of 160-byte region [ffff8880608c0000, ffff8880608c00a0) The buggy address belongs to the page: page:ffffea0001823000 count:1 mapcount:0 mapping:ffff88806bed1e00 index:0x0 compound_mapcount: 0 flags: 0x100000000008100(slab|head) raw: 0100000000008100 dead000000000100 dead000000000200 ffff88806bed1e00 raw: 0000000000000000 0000000000240024 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff8880608bff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff8880608c0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff8880608c0080: 00 00 00 00 fc fc fc fc fc fc fc fc 00 00 00 00 ^ ffff8880608c0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff8880608c0180: fc fc fc fc fc fc fc fc 00 00 00 00 00 00 00 00 ================================================================== Link: https://lore.kernel.org/linux-media/1620723603-30912-1-git-send-email-zheyuma97@gmail.com Reported-by: Zheyu Ma <zheyuma97@gmail.com> Signed-off-by: Zheyu Ma <zheyuma97@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-05-11 17:00:03 +08:00
error:
spin_unlock_irqrestore(&fc_pci->irq_lock, flags);
return ret;
}
static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
if (onoff) {
flexcop_dma_config(fc, &fc_pci->dma[0], FC_DMA_1);
flexcop_dma_config(fc, &fc_pci->dma[1], FC_DMA_2);
flexcop_dma_config_timer(fc, FC_DMA_1, 0);
flexcop_dma_xfer_control(fc, FC_DMA_1,
FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 1);
deb_irq("DMA xfer enabled\n");
fc_pci->last_dma1_cur_pos = 0;
flexcop_dma_control_timer_irq(fc, FC_DMA_1, 1);
deb_irq("IRQ enabled\n");
fc_pci->count_prev = fc_pci->count;
} else {
flexcop_dma_control_timer_irq(fc, FC_DMA_1, 0);
deb_irq("IRQ disabled\n");
flexcop_dma_xfer_control(fc, FC_DMA_1,
FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 0);
deb_irq("DMA xfer disabled\n");
}
return 0;
}
static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci)
{
int ret;
ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[0],
FC_DEFAULT_DMA1_BUFSIZE);
if (ret != 0)
return ret;
ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[1],
FC_DEFAULT_DMA2_BUFSIZE);
if (ret != 0) {
flexcop_dma_free(&fc_pci->dma[0]);
return ret;
}
flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_MEDIA |
FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1);
flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_CAO |
FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2);
fc_pci->init_state |= FC_PCI_DMA_INIT;
return ret;
}
static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci)
{
if (fc_pci->init_state & FC_PCI_DMA_INIT) {
flexcop_dma_free(&fc_pci->dma[0]);
flexcop_dma_free(&fc_pci->dma[1]);
}
fc_pci->init_state &= ~FC_PCI_DMA_INIT;
}
static int flexcop_pci_init(struct flexcop_pci *fc_pci)
{
int ret;
info("card revision %x", fc_pci->pdev->revision);
if ((ret = pci_enable_device(fc_pci->pdev)) != 0)
return ret;
pci_set_master(fc_pci->pdev);
if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0)
goto err_pci_disable_device;
fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800);
if (!fc_pci->io_mem) {
err("cannot map io memory\n");
ret = -EIO;
goto err_pci_release_regions;
}
pci_set_drvdata(fc_pci->pdev, fc_pci);
spin_lock_init(&fc_pci->irq_lock);
if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr,
IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0)
goto err_pci_iounmap;
fc_pci->init_state |= FC_PCI_INIT;
return ret;
err_pci_iounmap:
pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
err_pci_release_regions:
pci_release_regions(fc_pci->pdev);
err_pci_disable_device:
pci_disable_device(fc_pci->pdev);
return ret;
}
static void flexcop_pci_exit(struct flexcop_pci *fc_pci)
{
if (fc_pci->init_state & FC_PCI_INIT) {
free_irq(fc_pci->pdev->irq, fc_pci);
pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
pci_release_regions(fc_pci->pdev);
pci_disable_device(fc_pci->pdev);
}
fc_pci->init_state &= ~FC_PCI_INIT;
}
static int flexcop_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct flexcop_device *fc;
struct flexcop_pci *fc_pci;
int ret = -ENOMEM;
if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) {
err("out of memory\n");
return -ENOMEM;
}
/* general flexcop init */
fc_pci = fc->bus_specific;
fc_pci->fc_dev = fc;
fc->read_ibi_reg = flexcop_pci_read_ibi_reg;
fc->write_ibi_reg = flexcop_pci_write_ibi_reg;
fc->i2c_request = flexcop_i2c_request;
fc->get_mac_addr = flexcop_eeprom_check_mac_addr;
fc->stream_control = flexcop_pci_stream_control;
if (enable_pid_filtering)
info("will use the HW PID filter.");
else
info("will pass the complete TS to the demuxer.");
fc->pid_filtering = enable_pid_filtering;
fc->bus_type = FC_PCI;
fc->dev = &pdev->dev;
fc->owner = THIS_MODULE;
/* bus specific part */
fc_pci->pdev = pdev;
if ((ret = flexcop_pci_init(fc_pci)) != 0)
goto err_kfree;
/* init flexcop */
if ((ret = flexcop_device_initialize(fc)) != 0)
goto err_pci_exit;
/* init dma */
if ((ret = flexcop_pci_dma_init(fc_pci)) != 0)
goto err_fc_exit;
INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work);
if (irq_chk_intv > 0)
schedule_delayed_work(&fc_pci->irq_check_work,
msecs_to_jiffies(irq_chk_intv < 100 ?
100 :
irq_chk_intv));
return ret;
err_fc_exit:
flexcop_device_exit(fc);
err_pci_exit:
flexcop_pci_exit(fc_pci);
err_kfree:
flexcop_device_kfree(fc);
return ret;
}
/* in theory every _exit function should be called exactly two times,
* here and in the bail-out-part of the _init-function
*/
static void flexcop_pci_remove(struct pci_dev *pdev)
{
struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);
if (irq_chk_intv > 0)
cancel_delayed_work(&fc_pci->irq_check_work);
flexcop_pci_dma_exit(fc_pci);
flexcop_device_exit(fc_pci->fc_dev);
flexcop_pci_exit(fc_pci);
flexcop_device_kfree(fc_pci->fc_dev);
}
static const struct pci_device_id flexcop_pci_tbl[] = {
{ PCI_DEVICE(0x13d0, 0x2103) },
{ },
};
MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl);
static struct pci_driver flexcop_pci_driver = {
.name = "b2c2_flexcop_pci",
.id_table = flexcop_pci_tbl,
.probe = flexcop_pci_probe,
.remove = flexcop_pci_remove,
};
module_pci_driver(flexcop_pci_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");