]>
Commit | Line | Data |
---|---|---|
6854f87c SG |
1 | /* |
2 | * Copyright (C) 2014 Google, Inc | |
3 | * | |
4 | * From coreboot, originally based on the Linux kernel (drivers/pci/pci.c). | |
5 | * | |
6 | * Modifications are: | |
7 | * Copyright (C) 2003-2004 Linux Networx | |
8 | * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx) | |
9 | * Copyright (C) 2003-2006 Ronald G. Minnich <rminnich@gmail.com> | |
10 | * Copyright (C) 2004-2005 Li-Ta Lo <ollie@lanl.gov> | |
11 | * Copyright (C) 2005-2006 Tyan | |
12 | * (Written by Yinghai Lu <yhlu@tyan.com> for Tyan) | |
13 | * Copyright (C) 2005-2009 coresystems GmbH | |
14 | * (Written by Stefan Reinauer <stepan@coresystems.de> for coresystems GmbH) | |
15 | * | |
16 | * PCI Bus Services, see include/linux/pci.h for further explanation. | |
17 | * | |
18 | * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, | |
19 | * David Mosberger-Tang | |
20 | * | |
21 | * Copyright 1997 -- 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz> | |
22 | ||
23 | * SPDX-License-Identifier: GPL-2.0 | |
24 | */ | |
25 | ||
26 | #include <common.h> | |
27 | #include <bios_emul.h> | |
28 | #include <errno.h> | |
29 | #include <malloc.h> | |
30 | #include <pci.h> | |
31 | #include <pci_rom.h> | |
32 | #include <vbe.h> | |
33 | #include <video_fb.h> | |
34 | ||
35 | #ifdef CONFIG_HAVE_ACPI_RESUME | |
36 | #include <asm/acpi.h> | |
37 | #endif | |
38 | ||
39 | __weak bool board_should_run_oprom(pci_dev_t dev) | |
40 | { | |
41 | return true; | |
42 | } | |
43 | ||
44 | static bool should_load_oprom(pci_dev_t dev) | |
45 | { | |
46 | #ifdef CONFIG_HAVE_ACPI_RESUME | |
47 | if (acpi_get_slp_type() == 3) | |
48 | return false; | |
49 | #endif | |
50 | if (IS_ENABLED(CONFIG_ALWAYS_LOAD_OPROM)) | |
51 | return 1; | |
52 | if (board_should_run_oprom(dev)) | |
53 | return 1; | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
58 | __weak uint32_t board_map_oprom_vendev(uint32_t vendev) | |
59 | { | |
60 | return vendev; | |
61 | } | |
62 | ||
63 | static int pci_rom_probe(pci_dev_t dev, uint class, | |
64 | struct pci_rom_header **hdrp) | |
65 | { | |
66 | struct pci_rom_header *rom_header; | |
67 | struct pci_rom_data *rom_data; | |
68 | u16 vendor, device; | |
40305240 | 69 | u16 rom_vendor, rom_device; |
6854f87c SG |
70 | u32 vendev; |
71 | u32 mapped_vendev; | |
72 | u32 rom_address; | |
73 | ||
74 | pci_read_config_word(dev, PCI_VENDOR_ID, &vendor); | |
75 | pci_read_config_word(dev, PCI_DEVICE_ID, &device); | |
76 | vendev = vendor << 16 | device; | |
77 | mapped_vendev = board_map_oprom_vendev(vendev); | |
78 | if (vendev != mapped_vendev) | |
79 | debug("Device ID mapped to %#08x\n", mapped_vendev); | |
80 | ||
81 | #ifdef CONFIG_X86_OPTION_ROM_ADDR | |
82 | rom_address = CONFIG_X86_OPTION_ROM_ADDR; | |
83 | #else | |
4a2708a0 SG |
84 | |
85 | if (pciauto_setup_rom(pci_bus_to_hose(PCI_BUS(dev)), dev)) { | |
86 | debug("Cannot find option ROM\n"); | |
87 | return -ENOENT; | |
88 | } | |
89 | ||
6854f87c SG |
90 | pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_address); |
91 | if (rom_address == 0x00000000 || rom_address == 0xffffffff) { | |
92 | debug("%s: rom_address=%x\n", __func__, rom_address); | |
93 | return -ENOENT; | |
94 | } | |
95 | ||
96 | /* Enable expansion ROM address decoding. */ | |
97 | pci_write_config_dword(dev, PCI_ROM_ADDRESS, | |
98 | rom_address | PCI_ROM_ADDRESS_ENABLE); | |
99 | #endif | |
100 | debug("Option ROM address %x\n", rom_address); | |
ef2d17fe | 101 | rom_header = (struct pci_rom_header *)(unsigned long)rom_address; |
6854f87c SG |
102 | |
103 | debug("PCI expansion ROM, signature %#04x, INIT size %#04x, data ptr %#04x\n", | |
40305240 SG |
104 | le16_to_cpu(rom_header->signature), |
105 | rom_header->size * 512, le16_to_cpu(rom_header->data)); | |
6854f87c | 106 | |
40305240 | 107 | if (le16_to_cpu(rom_header->signature) != PCI_ROM_HDR) { |
6854f87c | 108 | printf("Incorrect expansion ROM header signature %04x\n", |
40305240 | 109 | le16_to_cpu(rom_header->signature)); |
6854f87c SG |
110 | return -EINVAL; |
111 | } | |
112 | ||
40305240 SG |
113 | rom_data = (((void *)rom_header) + le16_to_cpu(rom_header->data)); |
114 | rom_vendor = le16_to_cpu(rom_data->vendor); | |
115 | rom_device = le16_to_cpu(rom_data->device); | |
6854f87c SG |
116 | |
117 | debug("PCI ROM image, vendor ID %04x, device ID %04x,\n", | |
40305240 | 118 | rom_vendor, rom_device); |
6854f87c SG |
119 | |
120 | /* If the device id is mapped, a mismatch is expected */ | |
40305240 | 121 | if ((vendor != rom_vendor || device != rom_device) && |
6854f87c SG |
122 | (vendev == mapped_vendev)) { |
123 | printf("ID mismatch: vendor ID %04x, device ID %04x\n", | |
40305240 | 124 | rom_vendor, rom_device); |
c5caba03 | 125 | /* Continue anyway */ |
6854f87c SG |
126 | } |
127 | ||
128 | debug("PCI ROM image, Class Code %04x%02x, Code Type %02x\n", | |
129 | rom_data->class_hi, rom_data->class_lo, rom_data->type); | |
130 | ||
131 | if (class != ((rom_data->class_hi << 8) | rom_data->class_lo)) { | |
132 | debug("Class Code mismatch ROM %08x, dev %08x\n", | |
133 | (rom_data->class_hi << 8) | rom_data->class_lo, | |
134 | class); | |
135 | } | |
136 | *hdrp = rom_header; | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | int pci_rom_load(uint16_t class, struct pci_rom_header *rom_header, | |
142 | struct pci_rom_header **ram_headerp) | |
143 | { | |
144 | struct pci_rom_data *rom_data; | |
145 | unsigned int rom_size; | |
146 | unsigned int image_size = 0; | |
147 | void *target; | |
148 | ||
149 | do { | |
150 | /* Get next image, until we see an x86 version */ | |
151 | rom_header = (struct pci_rom_header *)((void *)rom_header + | |
152 | image_size); | |
153 | ||
154 | rom_data = (struct pci_rom_data *)((void *)rom_header + | |
40305240 | 155 | le16_to_cpu(rom_header->data)); |
6854f87c | 156 | |
40305240 SG |
157 | image_size = le16_to_cpu(rom_data->ilen) * 512; |
158 | } while ((rom_data->type != 0) && (rom_data->indicator == 0)); | |
6854f87c SG |
159 | |
160 | if (rom_data->type != 0) | |
161 | return -EACCES; | |
162 | ||
163 | rom_size = rom_header->size * 512; | |
164 | ||
bdc88d4e | 165 | #ifdef PCI_VGA_RAM_IMAGE_START |
6854f87c | 166 | target = (void *)PCI_VGA_RAM_IMAGE_START; |
bdc88d4e SG |
167 | #else |
168 | target = (void *)malloc(rom_size); | |
169 | if (!target) | |
170 | return -ENOMEM; | |
171 | #endif | |
6854f87c | 172 | if (target != rom_header) { |
fba7eac1 SG |
173 | ulong start = get_timer(0); |
174 | ||
6854f87c SG |
175 | debug("Copying VGA ROM Image from %p to %p, 0x%x bytes\n", |
176 | rom_header, target, rom_size); | |
177 | memcpy(target, rom_header, rom_size); | |
178 | if (memcmp(target, rom_header, rom_size)) { | |
179 | printf("VGA ROM copy failed\n"); | |
180 | return -EFAULT; | |
181 | } | |
fba7eac1 | 182 | debug("Copy took %lums\n", get_timer(start)); |
6854f87c SG |
183 | } |
184 | *ram_headerp = target; | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | static struct vbe_mode_info mode_info; | |
190 | ||
191 | int vbe_get_video_info(struct graphic_device *gdev) | |
192 | { | |
193 | #ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE | |
194 | struct vesa_mode_info *vesa = &mode_info.vesa; | |
195 | ||
196 | gdev->winSizeX = vesa->x_resolution; | |
197 | gdev->winSizeY = vesa->y_resolution; | |
198 | ||
199 | gdev->plnSizeX = vesa->x_resolution; | |
200 | gdev->plnSizeY = vesa->y_resolution; | |
201 | ||
202 | gdev->gdfBytesPP = vesa->bits_per_pixel / 8; | |
203 | ||
204 | switch (vesa->bits_per_pixel) { | |
205 | case 24: | |
206 | gdev->gdfIndex = GDF_32BIT_X888RGB; | |
207 | break; | |
208 | case 16: | |
209 | gdev->gdfIndex = GDF_16BIT_565RGB; | |
210 | break; | |
211 | default: | |
212 | gdev->gdfIndex = GDF__8BIT_INDEX; | |
213 | break; | |
214 | } | |
215 | ||
216 | gdev->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS; | |
217 | gdev->pciBase = vesa->phys_base_ptr; | |
218 | ||
219 | gdev->frameAdrs = vesa->phys_base_ptr; | |
220 | gdev->memSize = vesa->bytes_per_scanline * vesa->y_resolution; | |
221 | ||
222 | gdev->vprBase = vesa->phys_base_ptr; | |
223 | gdev->cprBase = vesa->phys_base_ptr; | |
224 | ||
23609c71 | 225 | return gdev->winSizeX ? 0 : -ENOSYS; |
6854f87c SG |
226 | #else |
227 | return -ENOSYS; | |
228 | #endif | |
229 | } | |
230 | ||
bc17d8f4 | 231 | int pci_run_vga_bios(pci_dev_t dev, int (*int15_handler)(void), int exec_method) |
6854f87c SG |
232 | { |
233 | struct pci_rom_header *rom, *ram; | |
234 | int vesa_mode = -1; | |
235 | uint16_t class; | |
bc17d8f4 | 236 | bool emulate; |
6854f87c SG |
237 | int ret; |
238 | ||
239 | /* Only execute VGA ROMs */ | |
240 | pci_read_config_word(dev, PCI_CLASS_DEVICE, &class); | |
241 | if ((class ^ PCI_CLASS_DISPLAY_VGA) & 0xff00) { | |
242 | debug("%s: Class %#x, should be %#x\n", __func__, class, | |
243 | PCI_CLASS_DISPLAY_VGA); | |
244 | return -ENODEV; | |
245 | } | |
246 | ||
247 | if (!should_load_oprom(dev)) | |
248 | return -ENXIO; | |
249 | ||
250 | ret = pci_rom_probe(dev, class, &rom); | |
251 | if (ret) | |
252 | return ret; | |
253 | ||
254 | ret = pci_rom_load(class, rom, &ram); | |
255 | if (ret) | |
256 | return ret; | |
257 | ||
258 | if (!board_should_run_oprom(dev)) | |
259 | return -ENXIO; | |
260 | ||
261 | #if defined(CONFIG_FRAMEBUFFER_SET_VESA_MODE) && \ | |
262 | defined(CONFIG_FRAMEBUFFER_VESA_MODE) | |
263 | vesa_mode = CONFIG_FRAMEBUFFER_VESA_MODE; | |
264 | #endif | |
9a99caf3 | 265 | debug("Selected vesa mode %#x\n", vesa_mode); |
bc17d8f4 SG |
266 | |
267 | if (exec_method & PCI_ROM_USE_NATIVE) { | |
268 | #ifdef CONFIG_X86 | |
269 | emulate = false; | |
270 | #else | |
271 | if (!(exec_method & PCI_ROM_ALLOW_FALLBACK)) { | |
272 | printf("BIOS native execution is only available on x86\n"); | |
273 | return -ENOSYS; | |
274 | } | |
275 | emulate = true; | |
276 | #endif | |
277 | } else { | |
278 | #ifdef CONFIG_BIOSEMU | |
279 | emulate = true; | |
280 | #else | |
281 | if (!(exec_method & PCI_ROM_ALLOW_FALLBACK)) { | |
282 | printf("BIOS emulation not available - see CONFIG_BIOSEMU\n"); | |
283 | return -ENOSYS; | |
284 | } | |
285 | emulate = false; | |
286 | #endif | |
287 | } | |
288 | ||
6854f87c SG |
289 | if (emulate) { |
290 | #ifdef CONFIG_BIOSEMU | |
291 | BE_VGAInfo *info; | |
292 | ||
293 | ret = biosemu_setup(dev, &info); | |
294 | if (ret) | |
295 | return ret; | |
296 | biosemu_set_interrupt_handler(0x15, int15_handler); | |
297 | ret = biosemu_run(dev, (uchar *)ram, 1 << 16, info, true, | |
298 | vesa_mode, &mode_info); | |
299 | if (ret) | |
300 | return ret; | |
6854f87c SG |
301 | #endif |
302 | } else { | |
303 | #ifdef CONFIG_X86 | |
304 | bios_set_interrupt_handler(0x15, int15_handler); | |
305 | ||
306 | bios_run_on_x86(dev, (unsigned long)ram, vesa_mode, | |
307 | &mode_info); | |
6854f87c SG |
308 | #endif |
309 | } | |
9a99caf3 | 310 | debug("Final vesa mode %#x\n", mode_info.video_mode); |
6854f87c SG |
311 | |
312 | return 0; | |
313 | } |