From: Keith Busch keith.busch@intel.com
mainline inclusion from mainline-v4.20-rc1 commit e51cd9ce5dd3b10f9e67a30a4dc00fc1fa80c673 category: bugfix bugzilla: NA CVE: NA
-------------------------------------------------
Move the bus ops fallback into separate functions. No functional change here.
[wangxiongfeng]
We came across the following Oops.
[ 7365.854051] Internal error: Oops: 96000004 [#1] SMP [ 7365.858907] Process bash (pid: 110038, stack limit = 0x00000000c3909a30) [ 7365.865580] CPU: 42 PID: 110038 Comm: bash Tainted: G O 4.19.36-vhulk1907.1.0.h628.eulerosv2r8.aarch64 #1 [ 7365.876398] Hardware name: Huawei TaiShan 2280 V2/BC82AMDD, BIOS 1.05 09/18/2019 [ 7365.883758] pstate: 60400089 (nZCv daIf +PAN -UAO) [ 7365.888529] pc : aer_inj_read_config+0xc4/0x1a0 [aer_inject] [ 7365.894163] lr : (null) [ 7365.897462] sp : ffff000036ad37c0 [ 7365.900761] x29: ffff000036ad37c0 x28: 0000000000000001 [ 7365.906049] x27: 0000000000000001 x26: 0000000000000004 [ 7365.911338] x25: ffff000000a29000 x24: ffff000036ad3864 [ 7365.916625] x23: 0000000000000000 x22: 0000000000000000 [ 7365.921912] x21: 0000000000000080 x20: ffffa02fd7c34800 [ 7365.927201] x19: ffff000000a29020 x18: ffffffffffffffff [ 7365.932488] x17: 0000000000000000 x16: 0000000000000000 [ 7365.937775] x15: ffff0000091d9708 x14: 6963702f302e3030 [ 7365.943064] x13: 0000000000000040 x12: 0000000000000228 [ 7365.948352] x11: 0000000000000000 x10: 0000000000000000 [ 7365.953639] x9 : 0000000000000000 x8 : ffff802fd8998000 [ 7365.958926] x7 : ffff8027df40f200 x6 : 0000000000000001 [ 7365.964213] x5 : ffff000000a26128 x4 : ffff000036ad3864 [ 7365.969501] x3 : 0000000000000004 x2 : 0000000000000000 [ 7365.974788] x1 : 0000000000000000 x0 : 0000000000000000 [ 7365.980076] Call trace: [ 7365.982514] aer_inj_read_config+0xc4/0x1a0 [aer_inject] [ 7365.987805] pci_bus_read_config_dword+0xb0/0x120 [ 7365.992488] pci_bus_generic_read_dev_vendor_id+0xcc/0x2e0 [ 7365.997949] pci_bus_read_dev_vendor_id+0x58/0x98 [ 7366.002631] pci_scan_single_device+0x8c/0x110 [ 7366.007054] pci_scan_slot+0x40/0x110 [ 7366.010700] pci_scan_child_bus_extend+0x60/0x358 [ 7366.015383] pci_scan_child_bus+0x24/0x30 [ 7366.019375] pci_scan_bridge_extend+0x278/0x518 [ 7366.023886] pci_scan_child_bus_extend+0x158/0x358 [ 7366.028654] pci_scan_child_bus+0x24/0x30 [ 7366.032646] pci_scan_bridge_extend+0x278/0x518 [ 7366.037155] pci_scan_child_bus_extend+0x158/0x358 [ 7366.041923] pci_rescan_bus+0x24/0x48 [ 7366.045568] bus_rescan_store+0x8c/0xb0 [ 7366.049389] bus_attr_store+0x40/0x58 [ 7366.053036] sysfs_kf_write+0x58/0x80 [ 7366.056682] kernfs_fop_write+0xe8/0x1f0 [ 7366.060588] __vfs_write+0x60/0x190 [ 7366.064061] vfs_write+0xac/0x1c0 [ 7366.067361] ksys_write+0x6c/0xd8 [ 7366.070661] __arm64_sys_write+0x24/0x30 [ 7366.074568] el0_svc_common+0x78/0x130 [ 7366.078301] el0_svc_handler+0x38/0x78 [ 7366.082033] el0_svc+0x8/0xc [ 7366.084902] Code: f9007280 2a1603e1 aa1803e4 2a1a03e3 (f9400c05)
When we use 'aer-inject' to inject AER error into a PCI device. The 'pci_ops' of the 'pci_bus' of the device is changed to 'aer_inj_pci_ops'. When we remove the device and rescan the PCI tree, the 'pci_ops' of the newly allocced 'pci_bus' is also assign as 'aer_inj_pci_ops'. When we access the configuration space under the newly allocced 'pci_bus' and the register we want to access is not an AER register, kernel will try to find the original 'pci_ops',rather than 'aer_inj_pci_ops, and call 'pci_ops->write/read'. For the newly allocced 'pci_bus', we can not find the original 'pci_ops', so an Oops happened. This patch add a check for 'pci_ops' before we access its member.
Signed-off-by: Keith Busch keith.busch@intel.com Signed-off-by: Bjorn Helgaas bhelgaas@google.com Signed-off-by: Xiongfeng Wang wangxiongfeng2@huawei.com Reviewed-by: Hanjun Guo guohanjun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/pci/pcie/aer_inject.c | 66 +++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 28 deletions(-)
diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c index 0eb2434..48024a8 100644 --- a/drivers/pci/pcie/aer_inject.c +++ b/drivers/pci/pcie/aer_inject.c @@ -175,14 +175,48 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where, return target; }
+static int aer_inj_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *val) +{ + struct pci_ops *ops, *my_ops; + int rv; + + ops = __find_pci_bus_ops(bus); + if (!ops) + return -1; + + my_ops = bus->ops; + bus->ops = ops; + rv = ops->read(bus, devfn, where, size, val); + bus->ops = my_ops; + + return rv; +} + +static int aer_inj_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 val) +{ + struct pci_ops *ops, *my_ops; + int rv; + + ops = __find_pci_bus_ops(bus); + if (!ops) + return -1; + + my_ops = bus->ops; + bus->ops = ops; + rv = ops->write(bus, devfn, where, size, val); + bus->ops = my_ops; + + return rv; +} + static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { u32 *sim; struct aer_error *err; unsigned long flags; - struct pci_ops *ops; - struct pci_ops *my_ops; int domain; int rv;
@@ -203,18 +237,7 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, return 0; } out: - ops = __find_pci_bus_ops(bus); - /* - * pci_lock must already be held, so we can directly - * manipulate bus->ops. Many config access functions, - * including pci_generic_config_read() require the original - * bus->ops be installed to function, so temporarily put them - * back. - */ - my_ops = bus->ops; - bus->ops = ops; - rv = ops->read(bus, devfn, where, size, val); - bus->ops = my_ops; + rv = aer_inj_read(bus, devfn, where, size, val); spin_unlock_irqrestore(&inject_lock, flags); return rv; } @@ -226,8 +249,6 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, struct aer_error *err; unsigned long flags; int rw1cs; - struct pci_ops *ops; - struct pci_ops *my_ops; int domain; int rv;
@@ -251,18 +272,7 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, return 0; } out: - ops = __find_pci_bus_ops(bus); - /* - * pci_lock must already be held, so we can directly - * manipulate bus->ops. Many config access functions, - * including pci_generic_config_write() require the original - * bus->ops be installed to function, so temporarily put them - * back. - */ - my_ops = bus->ops; - bus->ops = ops; - rv = ops->write(bus, devfn, where, size, val); - bus->ops = my_ops; + rv = aer_inj_write(bus, devfn, where, size, val); spin_unlock_irqrestore(&inject_lock, flags); return rv; }