coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
ssdt.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <superio/common/ssdt.h>
4 
5 #include <device/device.h>
6 #include <device/pnp.h>
7 #include <acpi/acpigen.h>
8 #include <acpi/acpi.h>
9 #include <console/console.h>
10 #include <types.h>
11 #include <string.h>
12 
13 struct superio_dev {
14  const char *acpi_hid;
16  u8 irq[2];
17 };
18 
19 static const struct superio_dev superio_devs[] = {
20  {ACPI_HID_FDC, {0x3f0, 0x3f2, 0x3f7}, {6, } },
21  {ACPI_HID_KEYBOARD, {60, 64, }, {1, } },
22  {ACPI_HID_MOUSE, {60, 64, }, {12, } },
23  {ACPI_HID_COM, {0x3f8, 0x2f8, 0x3e8, 0x2e8}, {4, 3} },
24  {ACPI_HID_LPT, {0x378, }, {7, } },
25 };
26 
28 static const u8 irq_idx[] = {PNP_IDX_IRQ0, PNP_IDX_IRQ1};
29 
30 static const struct superio_dev *superio_guess_function(const struct device *dev)
31 {
32  for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
33  struct resource *res = probe_resource(dev, io_idx[i]);
34  if (!res || !res->base)
35  continue;
36 
37  for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
38  for (size_t k = 0; k < 4; k++) {
39  if (!superio_devs[j].io_base[k])
40  continue;
41  if (superio_devs[j].io_base[k] == res->base)
42  return &superio_devs[j];
43  }
44  }
45  }
46  for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
47  struct resource *res = probe_resource(dev, irq_idx[i]);
48  if (!res || !res->size)
49  continue;
50  for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
51  for (size_t k = 0; k < 2; k++) {
52  if (!superio_devs[j].irq[k])
53  continue;
54  if (superio_devs[j].irq[k] == res->base)
55  return &superio_devs[j];
56  }
57  }
58  }
59  return NULL;
60 }
61 
62 /* Return true if there are resources to report */
63 static bool has_resources(const struct device *dev)
64 {
65  for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
66  struct resource *res = probe_resource(dev, io_idx[i]);
67  if (!res || !res->base || !res->size)
68  continue;
69  return 1;
70  }
71  for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
72  struct resource *res = probe_resource(dev, irq_idx[i]);
73  if (!res || !res->size || res->base > 16)
74  continue;
75  return 1;
76  }
77  return 0;
78 }
79 
80 /* Add IO and IRQ resources for _CRS or _PRS */
81 static void ldn_gen_resources(const struct device *dev)
82 {
83  uint16_t irq = 0;
84  for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
85  struct resource *res = probe_resource(dev, io_idx[i]);
86  if (!res || !res->base)
87  continue;
88  resource_t base = res->base;
89  resource_t size = res->size;
90  while (size > 0) {
91  resource_t sz = size > 255 ? 255 : size;
92  /* TODO: Needs test with regions >= 256 bytes */
93  acpigen_write_io16(base, base, 1, sz, 1);
94  size -= sz;
95  base += sz;
96  }
97  }
98  for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
99  struct resource *res = probe_resource(dev, irq_idx[i]);
100  if (!res || !res->size || res->base >= 16)
101  continue;
102  irq |= 1 << res->base;
103  }
104  if (irq)
105  acpigen_write_irq(irq);
106 
107 }
108 
109 /* Add resource base and size for additional SuperIO code */
110 static void ldn_gen_resources_use(const struct device *dev)
111 {
112  char name[5];
113  for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
114  struct resource *res = probe_resource(dev, io_idx[i]);
115  if (!res || !res->base || !res->size)
116  continue;
117 
118  snprintf(name, sizeof(name), "IO%zXB", i);
119  name[4] = '\0';
121 
122  snprintf(name, sizeof(name), "IO%zXS", i);
123  name[4] = '\0';
125  }
126 }
127 
128 const char *superio_common_ldn_acpi_name(const struct device *dev)
129 {
130  u8 ldn = dev->path.pnp.device & 0xff;
131  u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
132  static char name[5];
133 
134  snprintf(name, sizeof(name), "L%02X%01X", ldn, vldn);
135 
136  name[4] = '\0';
137 
138  return name;
139 }
140 
141 static const char *name_from_hid(const char *hid)
142 {
143  static const struct {
144  const char *hid;
145  const char *name;
146  } lookup[] = {
147  {ACPI_HID_FDC, "FDC" },
148  {ACPI_HID_KEYBOARD, "PS2 Keyboard" },
149  {ACPI_HID_MOUSE, "PS2 Mouse"},
150  {ACPI_HID_COM, "COM port" },
151  {ACPI_HID_LPT, "LPT" },
152  {ACPI_HID_PNP, "Generic PNP device" },
153  };
154 
155  for (size_t i = 0; hid && i < ARRAY_SIZE(lookup); i++) {
156  if (strcmp(hid, lookup[i].hid) == 0)
157  return lookup[i].name;
158  }
159  return "Generic device";
160 }
161 
163 {
164  if (!dev || !dev->bus || !dev->bus->dev) {
165  printk(BIOS_CRIT, "BUG: Invalid argument in %s!\n", __func__);
166  return;
167  }
168 
169  const char *scope = acpi_device_scope(dev);
170  const char *name = acpi_device_name(dev);
171  const u8 ldn = dev->path.pnp.device & 0xff;
172  const u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
173  const char *hid;
174 
175  /* Validate devicetree settings */
176  bool bug = false;
177  if (dev->bus->dev->path.type != DEVICE_PATH_PNP) {
178  bug = true;
179  printk(BIOS_CRIT, "BUG: Parent of device %s is not a PNP device\n",
180  dev_path(dev));
181  } else if (dev->bus->dev->path.pnp.port != dev->path.pnp.port) {
182  bug = true;
183  printk(BIOS_CRIT, "BUG: Parent of device %s has wrong I/O port\n",
184  dev_path(dev));
185  }
186  if (bug) {
187  printk(BIOS_CRIT, "BUG: Check your devicetree!\n");
188  return;
189  }
190 
191  if (!scope || !name) {
192  printk(BIOS_ERR, "%s: Missing ACPI path/scope\n", dev_path(dev));
193  return;
194  }
195  if (vldn) {
196  printk(BIOS_DEBUG, "%s: Ignoring virtual LDN\n", dev_path(dev));
197  return;
198  }
199 
200  printk(BIOS_DEBUG, "%s.%s: %s\n", scope, name, dev_path(dev));
201 
202  /* Scope */
203  acpigen_write_scope(scope);
204 
205  /* Device */
207 
209 
210  acpigen_write_name_byte("LDN", ldn);
211  acpigen_write_name_byte("VLDN", vldn);
212 
213  acpigen_write_method("_STA", 0);
214  {
216  acpigen_emit_namestring("^^QLDN");
219 
220  /* Multiply (Local0, 0xf, Local0) */
225 
228 
229  }
230  acpigen_pop_len(); /* Method */
231 
232  /*
233  * The ACPI6.2 spec Chapter 6.1.5 requires to set a _HID if no _ADR
234  * is present. Tests on Windows 10 showed that this is also true for
235  * disabled (_STA = 0) devices, otherwise it BSODs.
236  */
237 
238  hid = acpi_device_hid(dev);
239  if (!hid) {
240  printk(BIOS_ERR, "%s: SuperIO driver doesn't provide a _HID\n", dev_path(dev));
241  /* Try to guess it... */
242  const struct superio_dev *sdev = superio_guess_function(dev);
243  if (sdev && sdev->acpi_hid) {
244  hid = sdev->acpi_hid;
245  printk(BIOS_WARNING, "%s: Guessed _HID is '%s'\n", dev_path(dev), hid);
246  } else {
247  hid = ACPI_HID_PNP;
248  printk(BIOS_ERR, "%s: Failed to guessed _HID\n", dev_path(dev));
249  }
250  }
251 
252  acpigen_write_name_string("_HID", hid);
254 
255  acpigen_write_method("_DIS", 0);
256  {
257  acpigen_emit_namestring("^^DLDN");
259  }
260  acpigen_pop_len(); /* Method */
261 
262  if (dev->enabled && has_resources(dev)) {
263  /* Resources - _CRS */
264  acpigen_write_name("_CRS");
266  ldn_gen_resources(dev);
268 
269  /* Resources - _PRS */
270  acpigen_write_name("_PRS");
272  ldn_gen_resources(dev);
274 
275  /* Resources base and size for 3rd party ACPI code */
277  }
278 
279  acpigen_pop_len(); /* Device */
280  acpigen_pop_len(); /* Scope */
281 }
void acpi_device_write_uid(const struct device *dev)
Definition: device.c:203
const char * acpi_device_hid(const struct device *dev)
Definition: device.c:78
const char * acpi_device_name(const struct device *dev)
Definition: device.c:49
const char * acpi_device_scope(const struct device *dev)
Definition: device.c:158
void acpigen_emit_namestring(const char *namepath)
Definition: acpigen.c:275
void acpigen_write_store(void)
Definition: acpigen.c:1333
void acpigen_write_integer(uint64_t data)
Definition: acpigen.c:136
void acpigen_pop_len(void)
Definition: acpigen.c:37
void acpigen_write_name_byte(const char *name, uint8_t val)
Definition: acpigen.c:152
void acpigen_write_scope(const char *name)
Definition: acpigen.c:326
void acpigen_write_resourcetemplate_footer(void)
Definition: acpigen.c:1165
void acpigen_write_irq(u16 mask)
Definition: acpigen.c:1109
void acpigen_write_name_integer(const char *name, uint64_t val)
Definition: acpigen.c:170
void acpigen_emit_byte(unsigned char b)
Definition: acpigen.c:61
void acpigen_write_resourcetemplate_header(void)
Definition: acpigen.c:1147
void acpigen_write_device(const char *name)
Definition: acpigen.c:769
void acpigen_write_io16(u16 min, u16 max, u8 align, u8 len, u8 decode16)
Definition: acpigen.c:1123
void acpigen_write_method(const char *name, int nargs)
Definition: acpigen.c:758
void acpigen_write_name(const char *name)
Definition: acpigen.c:320
void acpigen_write_name_string(const char *name, const char *string)
Definition: acpigen.c:176
const char * name
Definition: mmu.c:92
#define ARRAY_SIZE(a)
Definition: helpers.h:12
#define printk(level,...)
Definition: stdlib.h:16
struct resource * probe_resource(const struct device *dev, unsigned int index)
See if a resource structure already exists for a given index.
Definition: device_util.c:323
const char * dev_path(const struct device *dev)
Definition: device_util.c:149
#define ACPI_HID_FDC
Definition: acpi.h:145
#define ACPI_HID_COM
Definition: acpi.h:148
#define ACPI_HID_PNP
Definition: acpi.h:150
#define ACPI_HID_LPT
Definition: acpi.h:149
#define ACPI_HID_MOUSE
Definition: acpi.h:147
#define ACPI_HID_KEYBOARD
Definition: acpi.h:146
@ LOCAL0_OP
Definition: acpigen.h:81
@ RETURN_OP
Definition: acpigen.h:146
@ MULTIPLY_OP
Definition: acpigen.h:103
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_CRIT
BIOS_CRIT - Recovery unlikely.
Definition: loglevel.h:56
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_WARNING
BIOS_WARNING - Bad configuration.
Definition: loglevel.h:86
@ DEVICE_PATH_PNP
Definition: path.h:10
#define PNP_IDX_IO2
Definition: pnp_def.h:7
#define PNP_IDX_IRQ1
Definition: pnp_def.h:11
#define PNP_IDX_IO3
Definition: pnp_def.h:8
#define PNP_IDX_IO0
Definition: pnp_def.h:5
#define PNP_IDX_IO1
Definition: pnp_def.h:6
#define PNP_IDX_IRQ0
Definition: pnp_def.h:10
u64 resource_t
Definition: resource.h:43
uintptr_t base
Definition: uart.c:17
#define NULL
Definition: stddef.h:19
unsigned short uint16_t
Definition: stdint.h:11
uint16_t u16
Definition: stdint.h:48
uint8_t u8
Definition: stdint.h:45
int strcmp(const char *s1, const char *s2)
Definition: string.c:103
DEVTREE_CONST struct device * dev
Definition: device.h:78
struct pnp_path pnp
Definition: path.h:117
enum device_path_type type
Definition: path.h:114
Definition: device.h:107
struct device_path path
Definition: device.h:115
DEVTREE_CONST struct bus * bus
Definition: device.h:108
unsigned int enabled
Definition: device.h:122
unsigned int port
Definition: path.h:58
unsigned int device
Definition: path.h:59
resource_t base
Definition: resource.h:45
resource_t size
Definition: resource.h:46
const char * acpi_hid
Definition: ssdt.c:14
u8 irq[2]
Definition: ssdt.c:16
u16 io_base[4]
Definition: ssdt.c:15
static const u8 irq_idx[]
Definition: ssdt.c:28
static void ldn_gen_resources_use(const struct device *dev)
Definition: ssdt.c:110
static const u8 io_idx[]
Definition: ssdt.c:27
const char * superio_common_ldn_acpi_name(const struct device *dev)
Definition: ssdt.c:128
void superio_common_fill_ssdt_generator(const struct device *dev)
Definition: ssdt.c:162
static void ldn_gen_resources(const struct device *dev)
Definition: ssdt.c:81
static bool has_resources(const struct device *dev)
Definition: ssdt.c:63
static const char * name_from_hid(const char *hid)
Definition: ssdt.c:141
static const struct superio_dev superio_devs[]
Definition: ssdt.c:19
static const struct superio_dev * superio_guess_function(const struct device *dev)
Definition: ssdt.c:30
int snprintf(char *buf, size_t size, const char *fmt,...)
Note: This file is only for POSIX compatibility, and is meant to be chain-included via string....
Definition: vsprintf.c:35