1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/sched.h> |
3 | #include <linux/mm.h> |
4 | #include <linux/uaccess.h> |
5 | #include <linux/mmzone.h> |
6 | #include <linux/ioport.h> |
7 | #include <linux/seq_file.h> |
8 | #include <linux/console.h> |
9 | #include <linux/init.h> |
10 | #include <linux/edd.h> |
11 | #include <linux/dmi.h> |
12 | #include <linux/pfn.h> |
13 | #include <linux/pci.h> |
14 | #include <linux/export.h> |
15 | |
16 | #include <asm/probe_roms.h> |
17 | #include <asm/pci-direct.h> |
18 | #include <asm/e820/api.h> |
19 | #include <asm/mmzone.h> |
20 | #include <asm/setup.h> |
21 | #include <asm/sections.h> |
22 | #include <asm/io.h> |
23 | #include <asm/setup_arch.h> |
24 | #include <asm/sev.h> |
25 | |
26 | static struct resource system_rom_resource = { |
27 | .name = "System ROM" , |
28 | .start = 0xf0000, |
29 | .end = 0xfffff, |
30 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
31 | }; |
32 | |
33 | static struct resource extension_rom_resource = { |
34 | .name = "Extension ROM" , |
35 | .start = 0xe0000, |
36 | .end = 0xeffff, |
37 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
38 | }; |
39 | |
40 | static struct resource adapter_rom_resources[] = { { |
41 | .name = "Adapter ROM" , |
42 | .start = 0xc8000, |
43 | .end = 0, |
44 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
45 | }, { |
46 | .name = "Adapter ROM" , |
47 | .start = 0, |
48 | .end = 0, |
49 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
50 | }, { |
51 | .name = "Adapter ROM" , |
52 | .start = 0, |
53 | .end = 0, |
54 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
55 | }, { |
56 | .name = "Adapter ROM" , |
57 | .start = 0, |
58 | .end = 0, |
59 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
60 | }, { |
61 | .name = "Adapter ROM" , |
62 | .start = 0, |
63 | .end = 0, |
64 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
65 | }, { |
66 | .name = "Adapter ROM" , |
67 | .start = 0, |
68 | .end = 0, |
69 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
70 | } }; |
71 | |
72 | static struct resource video_rom_resource = { |
73 | .name = "Video ROM" , |
74 | .start = 0xc0000, |
75 | .end = 0xc7fff, |
76 | .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM |
77 | }; |
78 | |
79 | /* does this oprom support the given pci device, or any of the devices |
80 | * that the driver supports? |
81 | */ |
82 | static bool match_id(struct pci_dev *pdev, unsigned short vendor, unsigned short device) |
83 | { |
84 | struct pci_driver *drv = to_pci_driver(drv: pdev->dev.driver); |
85 | const struct pci_device_id *id; |
86 | |
87 | if (pdev->vendor == vendor && pdev->device == device) |
88 | return true; |
89 | |
90 | for (id = drv ? drv->id_table : NULL; id && id->vendor; id++) |
91 | if (id->vendor == vendor && id->device == device) |
92 | break; |
93 | |
94 | return id && id->vendor; |
95 | } |
96 | |
97 | static bool probe_list(struct pci_dev *pdev, unsigned short vendor, |
98 | const void *rom_list) |
99 | { |
100 | unsigned short device; |
101 | |
102 | do { |
103 | if (get_kernel_nofault(device, rom_list) != 0) |
104 | device = 0; |
105 | |
106 | if (device && match_id(pdev, vendor, device)) |
107 | break; |
108 | |
109 | rom_list += 2; |
110 | } while (device); |
111 | |
112 | return !!device; |
113 | } |
114 | |
115 | static struct resource *find_oprom(struct pci_dev *pdev) |
116 | { |
117 | struct resource *oprom = NULL; |
118 | int i; |
119 | |
120 | for (i = 0; i < ARRAY_SIZE(adapter_rom_resources); i++) { |
121 | struct resource *res = &adapter_rom_resources[i]; |
122 | unsigned short offset, vendor, device, list, rev; |
123 | const void *rom; |
124 | |
125 | if (res->end == 0) |
126 | break; |
127 | |
128 | rom = isa_bus_to_virt(address: res->start); |
129 | if (get_kernel_nofault(offset, rom + 0x18) != 0) |
130 | continue; |
131 | |
132 | if (get_kernel_nofault(vendor, rom + offset + 0x4) != 0) |
133 | continue; |
134 | |
135 | if (get_kernel_nofault(device, rom + offset + 0x6) != 0) |
136 | continue; |
137 | |
138 | if (match_id(pdev, vendor, device)) { |
139 | oprom = res; |
140 | break; |
141 | } |
142 | |
143 | if (get_kernel_nofault(list, rom + offset + 0x8) == 0 && |
144 | get_kernel_nofault(rev, rom + offset + 0xc) == 0 && |
145 | rev >= 3 && list && |
146 | probe_list(pdev, vendor, rom_list: rom + offset + list)) { |
147 | oprom = res; |
148 | break; |
149 | } |
150 | } |
151 | |
152 | return oprom; |
153 | } |
154 | |
155 | void __iomem *pci_map_biosrom(struct pci_dev *pdev) |
156 | { |
157 | struct resource *oprom = find_oprom(pdev); |
158 | |
159 | if (!oprom) |
160 | return NULL; |
161 | |
162 | return ioremap(offset: oprom->start, size: resource_size(res: oprom)); |
163 | } |
164 | EXPORT_SYMBOL(pci_map_biosrom); |
165 | |
166 | void pci_unmap_biosrom(void __iomem *image) |
167 | { |
168 | iounmap(addr: image); |
169 | } |
170 | EXPORT_SYMBOL(pci_unmap_biosrom); |
171 | |
172 | size_t pci_biosrom_size(struct pci_dev *pdev) |
173 | { |
174 | struct resource *oprom = find_oprom(pdev); |
175 | |
176 | return oprom ? resource_size(res: oprom) : 0; |
177 | } |
178 | EXPORT_SYMBOL(pci_biosrom_size); |
179 | |
180 | #define ROMSIGNATURE 0xaa55 |
181 | |
182 | static int __init romsignature(const unsigned char *rom) |
183 | { |
184 | const unsigned short * const ptr = (const unsigned short *)rom; |
185 | unsigned short sig; |
186 | |
187 | return get_kernel_nofault(sig, ptr) == 0 && sig == ROMSIGNATURE; |
188 | } |
189 | |
190 | static int __init romchecksum(const unsigned char *rom, unsigned long length) |
191 | { |
192 | unsigned char sum, c; |
193 | |
194 | for (sum = 0; length && get_kernel_nofault(c, rom++) == 0; length--) |
195 | sum += c; |
196 | return !length && !sum; |
197 | } |
198 | |
199 | void __init probe_roms(void) |
200 | { |
201 | unsigned long start, length, upper; |
202 | const unsigned char *rom; |
203 | unsigned char c; |
204 | int i; |
205 | |
206 | /* |
207 | * The ROM memory range is not part of the e820 table and is therefore not |
208 | * pre-validated by BIOS. The kernel page table maps the ROM region as encrypted |
209 | * memory, and SNP requires encrypted memory to be validated before access. |
210 | * Do that here. |
211 | */ |
212 | snp_prep_memory(paddr: video_rom_resource.start, |
213 | sz: ((system_rom_resource.end + 1) - video_rom_resource.start), |
214 | op: SNP_PAGE_STATE_PRIVATE); |
215 | |
216 | /* video rom */ |
217 | upper = adapter_rom_resources[0].start; |
218 | for (start = video_rom_resource.start; start < upper; start += 2048) { |
219 | rom = isa_bus_to_virt(address: start); |
220 | if (!romsignature(rom)) |
221 | continue; |
222 | |
223 | video_rom_resource.start = start; |
224 | |
225 | if (get_kernel_nofault(c, rom + 2) != 0) |
226 | continue; |
227 | |
228 | /* 0 < length <= 0x7f * 512, historically */ |
229 | length = c * 512; |
230 | |
231 | /* if checksum okay, trust length byte */ |
232 | if (length && romchecksum(rom, length)) |
233 | video_rom_resource.end = start + length - 1; |
234 | |
235 | request_resource(root: &iomem_resource, new: &video_rom_resource); |
236 | break; |
237 | } |
238 | |
239 | start = (video_rom_resource.end + 1 + 2047) & ~2047UL; |
240 | if (start < upper) |
241 | start = upper; |
242 | |
243 | /* system rom */ |
244 | request_resource(root: &iomem_resource, new: &system_rom_resource); |
245 | upper = system_rom_resource.start; |
246 | |
247 | /* check for extension rom (ignore length byte!) */ |
248 | rom = isa_bus_to_virt(address: extension_rom_resource.start); |
249 | if (romsignature(rom)) { |
250 | length = resource_size(res: &extension_rom_resource); |
251 | if (romchecksum(rom, length)) { |
252 | request_resource(root: &iomem_resource, new: &extension_rom_resource); |
253 | upper = extension_rom_resource.start; |
254 | } |
255 | } |
256 | |
257 | /* check for adapter roms on 2k boundaries */ |
258 | for (i = 0; i < ARRAY_SIZE(adapter_rom_resources) && start < upper; start += 2048) { |
259 | rom = isa_bus_to_virt(address: start); |
260 | if (!romsignature(rom)) |
261 | continue; |
262 | |
263 | if (get_kernel_nofault(c, rom + 2) != 0) |
264 | continue; |
265 | |
266 | /* 0 < length <= 0x7f * 512, historically */ |
267 | length = c * 512; |
268 | |
269 | /* but accept any length that fits if checksum okay */ |
270 | if (!length || start + length > upper || !romchecksum(rom, length)) |
271 | continue; |
272 | |
273 | adapter_rom_resources[i].start = start; |
274 | adapter_rom_resources[i].end = start + length - 1; |
275 | request_resource(root: &iomem_resource, new: &adapter_rom_resources[i]); |
276 | |
277 | start = adapter_rom_resources[i++].end & ~2047UL; |
278 | } |
279 | } |
280 | |
281 | |