Skip to content

Commit 535943c

Browse files
mikelrgregkh
authored andcommitted
PCI: Use ioremap(), not phys_to_virt() for platform ROM
[ Upstream commit 72e0ef0 ] On some EFI systems, the video BIOS is provided by the EFI firmware. The boot stub code stores the physical address of the ROM image in pdev->rom. Currently we attempt to access this pointer using phys_to_virt(), which doesn't work with CONFIG_HIGHMEM. On these systems, attempting to load the radeon module on a x86_32 kernel can result in the following: BUG: unable to handle page fault for address: 3e8ed03c #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page *pde = 00000000 Oops: 0000 [#1] PREEMPT SMP CPU: 0 PID: 317 Comm: systemd-udevd Not tainted 5.6.0-rc3-next-20200228 #2 Hardware name: Apple Computer, Inc. MacPro1,1/Mac-F4208DC8, BIOS MP11.88Z.005C.B08.0707021221 07/02/07 EIP: radeon_get_bios+0x5ed/0xe50 [radeon] Code: 00 00 84 c0 0f 85 12 fd ff ff c7 87 64 01 00 00 00 00 00 00 8b 47 08 8b 55 b0 e8 1e 83 e1 d6 85 c0 74 1a 8b 55 c0 85 d2 74 13 <80> 38 55 75 0e 80 78 01 aa 0f 84 a4 03 00 00 8d 74 26 00 68 dc 06 EAX: 3e8ed03c EBX: 00000000 ECX: 3e8ed03c EDX: 00010000 ESI: 00040000 EDI: eec04000 EBP: eef3fc60 ESP: eef3fbe0 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 EFLAGS: 00010206 CR0: 80050033 CR2: 3e8ed03c CR3: 2ec77000 CR4: 000006d0 Call Trace: r520_init+0x26/0x240 [radeon] radeon_device_init+0x533/0xa50 [radeon] radeon_driver_load_kms+0x80/0x220 [radeon] drm_dev_register+0xa7/0x180 [drm] radeon_pci_probe+0x10f/0x1a0 [radeon] pci_device_probe+0xd4/0x140 Fix the issue by updating all drivers which can access a platform provided ROM. Instead of calling the helper function pci_platform_rom() which uses phys_to_virt(), call ioremap() directly on the pdev->rom. radeon_read_platform_bios() previously directly accessed an __iomem pointer. Avoid this by calling memcpy_fromio() instead of kmemdup(). pci_platform_rom() now has no remaining callers, so remove it. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mikel Rychliski <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> Acked-by: Alex Deucher <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent aa325ff commit 535943c

File tree

5 files changed

+52
-44
lines changed

5 files changed

+52
-44
lines changed

drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -192,30 +192,35 @@ static bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev)
192192

193193
static bool amdgpu_read_platform_bios(struct amdgpu_device *adev)
194194
{
195-
uint8_t __iomem *bios;
196-
size_t size;
195+
phys_addr_t rom = adev->pdev->rom;
196+
size_t romlen = adev->pdev->romlen;
197+
void __iomem *bios;
197198

198199
adev->bios = NULL;
199200

200-
bios = pci_platform_rom(adev->pdev, &size);
201-
if (!bios) {
201+
if (!rom || romlen == 0)
202202
return false;
203-
}
204203

205-
adev->bios = kzalloc(size, GFP_KERNEL);
206-
if (adev->bios == NULL)
204+
adev->bios = kzalloc(romlen, GFP_KERNEL);
205+
if (!adev->bios)
207206
return false;
208207

209-
memcpy_fromio(adev->bios, bios, size);
208+
bios = ioremap(rom, romlen);
209+
if (!bios)
210+
goto free_bios;
210211

211-
if (!check_atom_bios(adev->bios, size)) {
212-
kfree(adev->bios);
213-
return false;
214-
}
212+
memcpy_fromio(adev->bios, bios, romlen);
213+
iounmap(bios);
215214

216-
adev->bios_size = size;
215+
if (!check_atom_bios(adev->bios, romlen))
216+
goto free_bios;
217+
218+
adev->bios_size = romlen;
217219

218220
return true;
221+
free_bios:
222+
kfree(adev->bios);
223+
return false;
219224
}
220225

221226
#ifdef CONFIG_ACPI

drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowpci.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,21 +101,34 @@ platform_init(struct nvkm_bios *bios, const char *name)
101101
else
102102
return ERR_PTR(-ENODEV);
103103

104+
if (!pdev->rom || pdev->romlen == 0)
105+
return ERR_PTR(-ENODEV);
106+
104107
if ((priv = kmalloc(sizeof(*priv), GFP_KERNEL))) {
108+
priv->size = pdev->romlen;
105109
if (ret = -ENODEV,
106-
(priv->rom = pci_platform_rom(pdev, &priv->size)))
110+
(priv->rom = ioremap(pdev->rom, pdev->romlen)))
107111
return priv;
108112
kfree(priv);
109113
}
110114

111115
return ERR_PTR(ret);
112116
}
113117

118+
static void
119+
platform_fini(void *data)
120+
{
121+
struct priv *priv = data;
122+
123+
iounmap(priv->rom);
124+
kfree(priv);
125+
}
126+
114127
const struct nvbios_source
115128
nvbios_platform = {
116129
.name = "PLATFORM",
117130
.init = platform_init,
118-
.fini = (void(*)(void *))kfree,
131+
.fini = platform_fini,
119132
.read = pcirom_read,
120133
.rw = true,
121134
};

drivers/gpu/drm/radeon/radeon_bios.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,25 +108,33 @@ static bool radeon_read_bios(struct radeon_device *rdev)
108108

109109
static bool radeon_read_platform_bios(struct radeon_device *rdev)
110110
{
111-
uint8_t __iomem *bios;
112-
size_t size;
111+
phys_addr_t rom = rdev->pdev->rom;
112+
size_t romlen = rdev->pdev->romlen;
113+
void __iomem *bios;
113114

114115
rdev->bios = NULL;
115116

116-
bios = pci_platform_rom(rdev->pdev, &size);
117-
if (!bios) {
117+
if (!rom || romlen == 0)
118118
return false;
119-
}
120119

121-
if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) {
120+
rdev->bios = kzalloc(romlen, GFP_KERNEL);
121+
if (!rdev->bios)
122122
return false;
123-
}
124-
rdev->bios = kmemdup(bios, size, GFP_KERNEL);
125-
if (rdev->bios == NULL) {
126-
return false;
127-
}
123+
124+
bios = ioremap(rom, romlen);
125+
if (!bios)
126+
goto free_bios;
127+
128+
memcpy_fromio(rdev->bios, bios, romlen);
129+
iounmap(bios);
130+
131+
if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa)
132+
goto free_bios;
128133

129134
return true;
135+
free_bios:
136+
kfree(rdev->bios);
137+
return false;
130138
}
131139

132140
#ifdef CONFIG_ACPI

drivers/pci/rom.c

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -195,20 +195,3 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
195195
pci_disable_rom(pdev);
196196
}
197197
EXPORT_SYMBOL(pci_unmap_rom);
198-
199-
/**
200-
* pci_platform_rom - provides a pointer to any ROM image provided by the
201-
* platform
202-
* @pdev: pointer to pci device struct
203-
* @size: pointer to receive size of pci window over ROM
204-
*/
205-
void __iomem *pci_platform_rom(struct pci_dev *pdev, size_t *size)
206-
{
207-
if (pdev->rom && pdev->romlen) {
208-
*size = pdev->romlen;
209-
return phys_to_virt((phys_addr_t)pdev->rom);
210-
}
211-
212-
return NULL;
213-
}
214-
EXPORT_SYMBOL(pci_platform_rom);

include/linux/pci.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,6 @@ int pci_enable_rom(struct pci_dev *pdev);
12161216
void pci_disable_rom(struct pci_dev *pdev);
12171217
void __iomem __must_check *pci_map_rom(struct pci_dev *pdev, size_t *size);
12181218
void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom);
1219-
void __iomem __must_check *pci_platform_rom(struct pci_dev *pdev, size_t *size);
12201219

12211220
/* Power management related routines */
12221221
int pci_save_state(struct pci_dev *dev);

0 commit comments

Comments
 (0)