coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
smihandler.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <arch/io.h>
4 #include <console/console.h>
5 #include <cpu/x86/msr.h>
6 #include <cpu/x86/smm.h>
7 #include <ec/acpi/ec.h>
8 #include <soc/nvs.h>
9 
10 /*
11  * TODO: Perform RE of protocols in vendor firmware:
12  * - gEfiSmmSxDispatch2ProtocolGuid
13  * - gEfiSmmPowerButtonDispatch2ProtocolGuid
14  *
15  * However, note that first glance suggests that no handlers
16  * will be very interesting and that gEfiSmmGpiDispatch2ProtocolGuid
17  * was unused (as I recall).
18  *
19  * Also, consider gEfiSmmIoTrapDispatch2ProtocolGuid, but
20  * this is less likely.
21  */
22 
23 /* Keep in sync with dsdt.asl; could insert into SSDT at runtime */
24 #define APM_CNT_BOARD_SMI 0xDD
25 
26 /* Toggle TURBO_MODE_DISABLE bit in IA32_MISC_ENABLE MSR
27  when requested by EC. */
28 static void toggle_turbo_disable(uint8_t function_parameter_0)
29 {
30  if (function_parameter_0 == 1) {
31  printk(BIOS_DEBUG, "EC: Enabling Intel Turbo Mode\n");
32  msr_unset(IA32_MISC_ENABLE, 0x4000000000);
33  } else if (function_parameter_0 == 0) {
34  printk(BIOS_DEBUG, "EC: Disabling Intel Turbo Mode\n");
35  msr_set(IA32_MISC_ENABLE, 0x4000000000);
36  }
37 }
38 
39 /* Set WiFi and BT enable bits in EC RAM. */
40 static void enable_rf_by_capability(void)
41 {
42  /* FIXME: We're not tracking (driver) 'capabilities' at the moment (must we?),
43  so we just enable WiFi and BT here. If this was tracked, then
44  bits may be cleared here */
45  uint8_t rf_register = ec_read(0x71);
46  ec_write(0x71, rf_register | 0x03);
47 }
48 
49 /* Set OS capability bits in EC RAM. */
50 static void handle_acpi_osys(void)
51 {
52  uint8_t os_support;
53 
54  /* TODO: Add _OSI method support to coreboot and make this work */
55  printk(BIOS_DEBUG, "GNVS.OSYS = %d\n", gnvs->unused_was_osys);
56  switch (gnvs->unused_was_osys) {
57  /* Linux */
58  case 1000:
59  os_support = 64;
60  break;
61  /* Windows versions by year */
62  case 2009:
63  os_support = 3;
64  break;
65  case 2012:
66  os_support = 4;
67  break;
68  case 2013:
69  os_support = 5;
70  break;
71  case 2015:
72  os_support = 6;
73  break;
74  /* Operating system unknown */
75  default:
76  printk(BIOS_DEBUG, "GNVS.OSYS not supported!\n");
77  printk(BIOS_DEBUG, "No capabilities!\n");
78  os_support = 0;
79  break;
80  }
81 
82  ec_write(0x5C, os_support);
83 }
84 
85 /* Handles EC's _REG, _PTS and _WAK methods.
86  Partially involves setting EC RAM offsets based on GNVS.OSYS - OS capabilities? */
88  uint8_t function_parameter_0, uint8_t function_parameter_1)
89 {
90  switch (function_parameter_0) {
91  case 1:
92  printk(BIOS_DEBUG, "EC: Called for _REG method - OS initialise\n");
95  // NOTE: Not handling (driver) 'capabilities'
96  break;
97  case 2:
98  printk(BIOS_DEBUG, "EC: Called for _PTS method - Entering sleep\n");
99  // NOTE: Not saving (driver) 'capabilities'
100  // NOTE: Not saving and restoring EC RAM offset 0x4F
101  break;
102  case 3:
103  printk(BIOS_DEBUG, "EC: Called for _WAK method - Sleep resume\n");
106  // NOTE: Not saving and restoring EC RAM offset 0x4F
107  break;
108  default:
109  printk(BIOS_DEBUG, "function_parameter_0 is invalid!\n");
110  break;
111  }
112 }
113 
114 /* TODO: Reverse engineer 0x80 function and implement if necessary */
115 static void ec_smi_handler(uint8_t smif)
116 {
117  uint8_t smm_data_port;
118  uint8_t function_parameter_0;
119  uint8_t function_parameter_1;
120 
121  /* Parameters encoded onto SMI data port because PRMx NVS are not present
122  - Callers must only use 4 bits per argument
123  - _PTS and _WAK are required to call in spec-compliant way */
124  smm_data_port = inb(APM_STS);
125  function_parameter_0 = smm_data_port & ~0xF0;
126  function_parameter_1 = smm_data_port >> 4;
127 
128  printk(BIOS_DEBUG, "Function 0x%x(0x%x, 0x%x) called\n",
129  smif, function_parameter_0, function_parameter_1);
130  switch (smif) {
131  case 0x80:
132  printk(BIOS_WARNING, "Function 0x80 is unimplemented!\n");
133  printk(BIOS_DEBUG, "Function calls offset 0 in ACER_BOOT_DEVICE_SERVICE_PROTOCOL_GUID\n");
134  break;
135  case 0x81:
136  toggle_turbo_disable(function_parameter_0);
137  break;
138  case 0x82:
139  handle_acpi_wake_event(function_parameter_0, function_parameter_1);
140  break;
141  default:
142  /* Not handled */
143  printk(BIOS_DEBUG, "Requested function is unknown!\n");
144  return;
145  }
146 
147  /*
148  * gnvs->smif:
149  * - On success, the handler returns 0
150  * - On failure, the handler returns a value != 0
151  */
152  gnvs->smif = 0;
153 }
154 
156 {
157  /* TODO: Continue SmmKbcDriver RE of common service registration and confirm */
158  switch (data) {
159  case APM_CNT_BOARD_SMI:
160  if (gnvs) {
162  }
163  break;
164  case APM_CNT_ACPI_ENABLE: /* Events generate SCIs for OS */
165  /* use 0x68/0x6C to prevent races with userspace */
166  ec_set_ports(0x6C, 0x68);
167  /* discard all events */
169  /* Tests at runtime show this re-enables charging and battery reporting */
170  send_ec_command(0xE9); /* Vendor implements using ACPI "CMDB" register" */
171  send_ec_data(0x81);
172  /* TODO: Set touchpad GPP owner to ACPI? */
173  break;
174  case APM_CNT_ACPI_DISABLE: /* Events generate SMIs for SMM */
175  /* use 0x68/0x6C to prevent races with userspace */
176  ec_set_ports(0x6C, 0x68);
177  /* discard all events */
179  /* Tests at runtime show this disables charging and battery reporting */
180  send_ec_command(0xE9); /* Vendor implements using ACPI "CMDB" register" */
181  send_ec_data(0x80);
182  /* TODO: Set touchpad GPP owner to GPIO? */
183  break;
184  default:
185  break;
186  }
187  return 0;
188 }
#define printk(level,...)
Definition: stdlib.h:16
int __weak mainboard_smi_apmc(u8 data)
Definition: smihandler.c:209
u8 inb(u16 port)
u8 ec_read(u8 addr)
Definition: ec.c:107
int send_ec_command(u8 command)
Definition: ec.c:13
void ec_clear_out_queue(void)
Definition: ec.c:92
void ec_set_ports(u16 cmd_reg, u16 data_reg)
Definition: ec.c:143
int send_ec_data(u8 data)
Definition: ec.c:35
int ec_write(u8 addr, u8 data)
Definition: ec.c:115
static void msr_unset(unsigned int reg, uint64_t unset)
Helper for unsetting MSR bitmasks.
Definition: msr.h:392
#define IA32_MISC_ENABLE
Definition: msr.h:45
static void msr_set(unsigned int reg, uint64_t set)
Helper for setting MSR bitmasks.
Definition: msr.h:381
#define APM_STS
Definition: smm.h:30
#define APM_CNT_ACPI_DISABLE
Definition: smm.h:21
#define APM_CNT_ACPI_ENABLE
Definition: smm.h:22
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_WARNING
BIOS_WARNING - Bad configuration.
Definition: loglevel.h:86
#define APM_CNT_BOARD_SMI
Definition: smihandler.c:24
static void enable_rf_by_capability(void)
Definition: smihandler.c:40
static void handle_acpi_osys(void)
Definition: smihandler.c:50
static void toggle_turbo_disable(uint8_t function_parameter_0)
Definition: smihandler.c:28
static void handle_acpi_wake_event(uint8_t function_parameter_0, uint8_t function_parameter_1)
Definition: smihandler.c:87
static void ec_smi_handler(uint8_t smif)
Definition: smihandler.c:115
struct global_nvs * gnvs
uint8_t u8
Definition: stdint.h:45
unsigned char uint8_t
Definition: stdint.h:8
u16 unused_was_osys
Definition: nvs.h:10
u8 smif
Definition: nvs.h:11