coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
opregion.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <acpi/acpi.h>
4 #include <types.h>
5 #include <string.h>
6 #include <cbfs.h>
7 #include <device/device.h>
8 #include <device/pci.h>
9 #include <device/pci_ids.h>
10 #include <device/pci_ops.h>
11 #include <console/console.h>
12 #include <cbmem.h>
13 #include "intel_bios.h"
14 #include "opregion.h"
15 
16 __weak
17 const char *mainboard_vbt_filename(void)
18 {
19  return "vbt.bin";
20 }
21 
22 static char vbt_data[CONFIG_VBT_DATA_SIZE_KB * KiB];
23 static size_t vbt_data_sz;
24 
25 void *locate_vbt(size_t *vbt_size)
26 {
27  uint32_t vbtsig = 0;
28 
29  if (vbt_data_sz != 0) {
30  if (vbt_size)
31  *vbt_size = vbt_data_sz;
32  return (void *)vbt_data;
33  }
34 
35  const char *filename = mainboard_vbt_filename();
36 
37  size_t file_size = cbfs_load(filename, vbt_data, sizeof(vbt_data));
38 
39  if (file_size == 0)
40  return NULL;
41 
42  if (vbt_size)
43  *vbt_size = file_size;
44 
45  memcpy(&vbtsig, vbt_data, sizeof(vbtsig));
46  if (vbtsig != VBT_SIGNATURE) {
47  printk(BIOS_ERR, "Missing/invalid signature in VBT data file!\n");
48  return NULL;
49  }
50 
51  printk(BIOS_INFO, "Found a VBT of %zu bytes after decompression\n",
52  file_size);
53  vbt_data_sz = file_size;
54 
55  return (void *)vbt_data;
56 }
57 
58 /* Write ASLS PCI register and prepare SWSCI register. */
60 {
61  struct device *igd;
62  u16 reg16;
63  u16 sci_reg;
64 
65  igd = pcidev_on_root(0x2, 0);
66  if (!igd || !igd->enabled)
67  return;
68 
69  /*
70  * Intel BIOS Specification
71  * Chapter 5.3.7 "Initialize Hardware State"
72  */
74 
75  /*
76  * Atom-based platforms use a combined SMI/SCI register,
77  * whereas non-Atom platforms use a separate SCI register.
78  */
79  if (CONFIG(INTEL_GMA_SWSMISCI))
80  sci_reg = SWSMISCI;
81  else
82  sci_reg = SWSCI;
83 
84  /*
85  * Intel's Windows driver relies on this:
86  * Intel BIOS Specification
87  * Chapter 5.4 "ASL Software SCI Handler"
88  */
89  reg16 = pci_read_config16(igd, sci_reg);
90  reg16 &= ~GSSCIE;
91  reg16 |= SMISCISEL;
92  pci_write_config16(igd, sci_reg, reg16);
93 }
94 
95 /* Restore ASLS register on S3 resume and prepare SWSCI. */
96 static enum cb_err intel_gma_restore_opregion(void)
97 {
98  const igd_opregion_t *const opregion = cbmem_find(CBMEM_ID_IGD_OPREGION);
99  if (!opregion) {
100  printk(BIOS_ERR, "GMA: Failed to find IGD OpRegion.\n");
101  return CB_ERR;
102  }
103  /* Write ASLS PCI register and prepare SWSCI register. */
105  return CB_SUCCESS;
106 }
107 
108 static enum cb_err vbt_validate(struct region_device *rdev)
109 {
110  uint32_t sig;
111 
112  if (rdev_readat(rdev, &sig, 0, sizeof(sig)) != sizeof(sig))
113  return CB_ERR;
114 
115  if (sig != VBT_SIGNATURE)
116  return CB_ERR;
117 
118  return CB_SUCCESS;
119 }
120 
121 static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
122 {
123  const optionrom_header_t *oprom;
124  const optionrom_pcir_t *pcir;
125  struct region_device rd;
126  enum cb_err ret;
127  u8 opromsize;
128  size_t offset;
129 
130  // FIXME: caller should supply a region_device instead of vbios pointer
131  if (rdev_chain_mem(&rd, vbios, sizeof(*oprom)))
132  return CB_ERR;
133 
134  if (rdev_readat(&rd, &opromsize, offsetof(optionrom_header_t, size),
135  sizeof(opromsize)) != sizeof(opromsize) || !opromsize)
136  return CB_ERR;
137 
138  if (rdev_chain_mem(&rd, vbios, opromsize * 512))
139  return CB_ERR;
140 
141  oprom = rdev_mmap(&rd, 0, sizeof(*oprom));
142  if (!oprom)
143  return CB_ERR;
144 
145  if (!oprom->pcir_offset || !oprom->vbt_offset) {
146  rdev_munmap(&rd, (void *)oprom);
147  return CB_ERR;
148  }
149 
150  pcir = rdev_mmap(&rd, oprom->pcir_offset, sizeof(*pcir));
151  if (pcir == NULL) {
152  rdev_munmap(&rd, (void *)oprom);
153  return CB_ERR;
154  }
155 
156  printk(BIOS_DEBUG, "GMA: %s: %x %x %x %x %x\n", __func__,
157  oprom->signature, pcir->vendor, pcir->classcode[0],
158  pcir->classcode[1], pcir->classcode[2]);
159 
160  /* Make sure we got an Intel VGA option rom */
161  if ((oprom->signature != OPROM_SIGNATURE) ||
162  (pcir->vendor != PCI_VID_INTEL) ||
163  (pcir->signature != 0x52494350) ||
164  (pcir->classcode[0] != 0x00) ||
165  (pcir->classcode[1] != 0x00) ||
166  (pcir->classcode[2] != 0x03)) {
167  rdev_munmap(&rd, (void *)oprom);
168  rdev_munmap(&rd, (void *)pcir);
169  return CB_ERR;
170  }
171 
172  rdev_munmap(&rd, (void *)pcir);
173 
174  /* Search for $VBT as some VBIOS are broken... */
175  offset = oprom->vbt_offset;
176  do {
177  ret = rdev_chain(rdev, &rd, offset,
178  (opromsize * 512) - offset);
179  offset++;
180  } while (ret == CB_SUCCESS && vbt_validate(rdev) != CB_SUCCESS);
181 
182  offset--;
183 
184  if (ret == CB_SUCCESS && offset != oprom->vbt_offset)
185  printk(BIOS_WARNING, "GMA: Buggy VBIOS found\n");
186  else if (ret != CB_SUCCESS)
187  printk(BIOS_ERR, "GMA: Broken VBIOS found\n");
188 
189  rdev_munmap(&rd, (void *)oprom);
190  return ret;
191 }
192 
193 static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
194 {
195  size_t vbt_data_size;
196  void *vbt = locate_vbt(&vbt_data_size);
197 
198  if (vbt == NULL)
199  return CB_ERR;
200 
201  if (rdev_chain_mem(rdev, vbt, vbt_data_size))
202  return CB_ERR;
203 
204  printk(BIOS_INFO, "GMA: Found VBT in CBFS\n");
205 
206  return CB_SUCCESS;
207 }
208 
209 static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
210 {
211  const u8 *oprom =
212  (const u8 *)pci_rom_probe(pcidev_on_root(0x2, 0));
213  if (oprom == NULL)
214  return CB_ERR;
215 
216  printk(BIOS_INFO, "GMA: Found VBIOS in CBFS\n");
217 
218  return locate_vbt_vbios(oprom, rdev);
219 }
220 
221 /*
222  * Try to locate VBT in possible locations and return if found.
223  * VBT can be possibly in one of 3 regions:
224  * 1. Stitched directly into CBFS region as VBT
225  * 2. Part of pci8086 option ROM within CBFS
226  * 3. part of VBIOS at location 0xC0000.
227  */
228 static enum cb_err find_vbt_location(struct region_device *rdev)
229 {
230  /* Search for vbt.bin in CBFS. */
231  if (locate_vbt_cbfs(rdev) == CB_SUCCESS &&
233  printk(BIOS_INFO, "GMA: Found valid VBT in CBFS\n");
234  return CB_SUCCESS;
235  }
236  /* Search for pci8086,XXXX.rom in CBFS. */
237  else if (locate_vbt_vbios_cbfs(rdev) == CB_SUCCESS &&
239  printk(BIOS_INFO, "GMA: Found valid VBT in VBIOS\n");
240  return CB_SUCCESS;
241  }
242  /*
243  * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
244  * Native Graphics Init as fake Option ROM or when coreboot did run the
245  * VBIOS on legacy platforms.
246  * TODO: Place generated fake VBT in CBMEM and get rid of this.
247  */
248  else if (locate_vbt_vbios((u8 *)0xc0000, rdev) == CB_SUCCESS &&
250  printk(BIOS_INFO, "GMA: Found valid VBT in legacy area\n");
251  return CB_SUCCESS;
252  }
253 
254  printk(BIOS_ERR, "GMA: VBT couldn't be found\n");
255  return CB_ERR;
256 }
257 
258 /* Function to get the IGD Opregion version */
259 static struct opregion_version opregion_get_version(void)
260 {
261  if (CONFIG(INTEL_GMA_OPREGION_2_1))
262  return (struct opregion_version) { .major = 2, .minor = 1 };
263 
264  return (struct opregion_version) { .major = 2, .minor = 0 };
265 }
266 
267 /*
268  * Function to determine if we need to use extended VBT region to pass
269  * VBT pointer. If VBT size > 6 KiB then we need to use extended VBT
270  * region.
271  */
272 static inline bool is_ext_vbt_required(igd_opregion_t *opregion, optionrom_vbt_t *vbt)
273 {
274  return (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1));
275 }
276 
277 /* Function to determine if the VBT uses a relative address */
278 static inline bool uses_relative_vbt_addr(opregion_header_t *header)
279 {
280  if (header->opver.major > 2)
281  return true;
282 
283  return header->opver.major >= 2 && header->opver.minor >= 1;
284 }
285 
286 /*
287  * Copy extended VBT at the end of opregion and fill rvda and rvds
288  * values correctly for the opregion.
289  */
290 static void opregion_add_ext_vbt(igd_opregion_t *opregion, uint8_t *ext_vbt,
291  optionrom_vbt_t *vbt)
292 {
293 
294  opregion_header_t *header = &opregion->header;
295  /* Copy VBT into extended VBT region (at offset 8 KiB) */
296  memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
297 
298  /* Fill RVDA value with relative address of the opregion buffer in case of
299  IGD Opregion version 2.1+ and physical address otherwise */
300 
302  opregion->mailbox3.rvda = sizeof(*opregion);
303  else
304  opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
305 
306  opregion->mailbox3.rvds = vbt->hdr_vbt_size;
307 }
308 
309 /* Initialize IGD OpRegion, called from ACPI code and OS drivers */
311 {
312  igd_opregion_t *opregion;
313  struct region_device rdev;
314  optionrom_vbt_t *vbt = NULL;
315  size_t opregion_size = sizeof(igd_opregion_t);
316 
317  if (acpi_is_wakeup_s3())
319 
321  return CB_ERR;
322 
323  vbt = rdev_mmap_full(&rdev);
324  if (!vbt) {
325  printk(BIOS_ERR, "GMA: Error mapping VBT\n");
326  return CB_ERR;
327  }
328 
329  if (vbt->hdr_vbt_size > region_device_sz(&rdev)) {
330  printk(BIOS_ERR, "GMA: Error mapped only a partial VBT\n");
331  rdev_munmap(&rdev, vbt);
332  return CB_ERR;
333  }
334 
335  if (is_ext_vbt_required(opregion, vbt))
336  opregion_size += vbt->hdr_vbt_size;
337 
338  opregion = cbmem_add(CBMEM_ID_IGD_OPREGION, opregion_size);
339  if (!opregion) {
340  printk(BIOS_ERR, "GMA: Failed to add IGD OpRegion to CBMEM.\n");
341  return CB_ERR;
342  }
343 
344  memset(opregion, 0, opregion_size);
345 
346  memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
347  sizeof(opregion->header.signature));
348  memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
349  ARRAY_SIZE(vbt->coreblock_biosbuild));
350 
351  /* Get the opregion version information */
352  opregion->header.opver = opregion_get_version();
353 
354  /* Extended VBT support */
355  if (is_ext_vbt_required(opregion, vbt)) {
356  /* Place extended VBT just after opregion */
357  uint8_t *ext_vbt = (uint8_t *)opregion + sizeof(*opregion);
358  opregion_add_ext_vbt(opregion, ext_vbt, vbt);
359  } else {
360  /* Raw VBT size which can fit in gvd1 */
361  memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
362  }
363 
364  rdev_munmap(&rdev, vbt);
365 
366  /* 8kb */
367  opregion->header.size = sizeof(igd_opregion_t) / 1024;
368 
369  // FIXME We just assume we're mobile for now
370  opregion->header.mailboxes = MAILBOXES_MOBILE;
371 
372  // TODO Initialize Mailbox 1
373  opregion->mailbox1.clid = 1;
374 
375  // TODO Initialize Mailbox 3
376  opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
377  opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
378  opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e
380  opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
381  opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
382  opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
383  opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
384  opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
385  opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
386  opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
387  opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
388  opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
389  opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
390  opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
391 
392  /* Write ASLS PCI register and prepare SWSCI register. */
394 
395  return CB_SUCCESS;
396 }
struct arm64_kernel_header header
Definition: fit_payload.c:30
static int acpi_is_wakeup_s3(void)
Definition: acpi.h:9
void * memcpy(void *dest, const void *src, size_t n)
Definition: memcpy.c:7
void * memset(void *dstpp, int c, size_t len)
Definition: memset.c:12
#define ARRAY_SIZE(a)
Definition: helpers.h:12
#define offsetof(TYPE, MEMBER)
Definition: helpers.h:84
#define KiB
Definition: helpers.h:75
cb_err
coreboot error codes
Definition: cb_err.h:15
@ CB_ERR
Generic error code.
Definition: cb_err.h:17
@ CB_SUCCESS
Call completed successfully.
Definition: cb_err.h:16
static size_t cbfs_load(const char *name, void *buf, size_t size)
Definition: cbfs.h:282
void * cbmem_add(u32 id, u64 size)
Definition: imd_cbmem.c:144
void * cbmem_find(u32 id)
Definition: imd_cbmem.c:166
#define CBMEM_ID_IGD_OPREGION
Definition: cbmem_id.h:29
#define printk(level,...)
Definition: stdlib.h:16
DEVTREE_CONST struct device * pcidev_on_root(uint8_t dev, uint8_t fn)
Definition: device_const.c:260
@ CONFIG
Definition: dsi_common.h:201
static struct region_device rdev
Definition: flashconsole.c:14
static size_t offset
Definition: flashconsole.c:16
static __always_inline void pci_write_config32(const struct device *dev, u16 reg, u32 val)
Definition: pci_ops.h:76
static __always_inline u16 pci_read_config16(const struct device *dev, u16 reg)
Definition: pci_ops.h:52
static __always_inline void pci_write_config16(const struct device *dev, u16 reg, u16 val)
Definition: pci_ops.h:70
#define VBT_SIGNATURE
Definition: intel_bios.h:712
#define BIOS_INFO
BIOS_INFO - Expected events.
Definition: loglevel.h:113
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_WARNING
BIOS_WARNING - Bad configuration.
Definition: loglevel.h:86
static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
Definition: opregion.c:193
__weak const char * mainboard_vbt_filename(void)
Definition: opregion.c:17
static enum cb_err intel_gma_restore_opregion(void)
Definition: opregion.c:96
static void intel_gma_opregion_register(uintptr_t opregion)
Definition: opregion.c:59
static struct opregion_version opregion_get_version(void)
Definition: opregion.c:259
static void opregion_add_ext_vbt(igd_opregion_t *opregion, uint8_t *ext_vbt, optionrom_vbt_t *vbt)
Definition: opregion.c:290
static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
Definition: opregion.c:121
static enum cb_err find_vbt_location(struct region_device *rdev)
Definition: opregion.c:228
void * locate_vbt(size_t *vbt_size)
Definition: opregion.c:25
static bool is_ext_vbt_required(igd_opregion_t *opregion, optionrom_vbt_t *vbt)
Definition: opregion.c:272
static size_t vbt_data_sz
Definition: opregion.c:23
static char vbt_data[CONFIG_VBT_DATA_SIZE_KB *KiB]
Definition: opregion.c:22
enum cb_err intel_gma_init_igd_opregion(void)
Definition: opregion.c:310
static enum cb_err vbt_validate(struct region_device *rdev)
Definition: opregion.c:108
static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
Definition: opregion.c:209
static bool uses_relative_vbt_addr(opregion_header_t *header)
Definition: opregion.c:278
#define IGD_PFIT_STRETCH
Definition: opregion.h:164
#define IGD_FIELD_VALID
Definition: opregion.h:162
#define SWSCI
Definition: opregion.h:11
#define IGD_WORD_FIELD_VALID
Definition: opregion.h:163
#define ASLS
Definition: opregion.h:10
#define OPROM_SIGNATURE
Definition: opregion.h:198
#define IGD_BACKLIGHT_BRIGHTNESS
Definition: opregion.h:159
#define IGD_OPREGION_SIGNATURE
Definition: opregion.h:36
#define SWSMISCI
Definition: opregion.h:12
#define IGD_INITIAL_BRIGHTNESS
Definition: opregion.h:160
#define MAILBOXES_MOBILE
Definition: opregion.h:44
#define SMISCISEL
Definition: opregion.h:14
#define GSSCIE
Definition: opregion.h:13
#define PCI_VID_INTEL
Definition: pci_ids.h:2157
struct rom_header * pci_rom_probe(const struct device *dev)
Definition: pci_rom.c:52
int rdev_munmap(const struct region_device *rd, void *mapping)
Definition: region.c:65
static size_t region_device_sz(const struct region_device *rdev)
Definition: region.h:132
int rdev_chain_mem(struct region_device *child, const void *base, size_t size)
Definition: region.c:293
static void * rdev_mmap_full(const struct region_device *rd)
Definition: region.h:148
void * rdev_mmap(const struct region_device *rd, size_t offset, size_t size)
Definition: region.c:46
int rdev_chain(struct region_device *child, const struct region_device *parent, size_t offset, size_t size)
Definition: region.c:136
ssize_t rdev_readat(const struct region_device *rd, void *b, size_t offset, size_t size)
Definition: region.c:77
const struct smm_save_state_ops *legacy_ops __weak
Definition: save_state.c:8
#define NULL
Definition: stddef.h:19
unsigned int uint32_t
Definition: stdint.h:14
unsigned long uintptr_t
Definition: stdint.h:21
uint16_t u16
Definition: stdint.h:48
uint8_t u8
Definition: stdint.h:45
unsigned char uint8_t
Definition: stdint.h:8
Definition: device.h:107
unsigned int enabled
Definition: device.h:122
typedef void(X86APIP X86EMU_intrFuncs)(int num)