Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

IBM Real-Time "SMI Free" mode driver -v7

After a period of RFC for this driver, I think it is ready
for inclusion in the platform-driver-x86 tree, hopefully to
be staged in the next merge window into Linus's tree.

--Vernon

------------------------------------------------------------

IBM Real-Time "SMI Free" mode driver

This driver supports the Real-Time Linux (RTL) BIOS feature.
The RTL feature allows non-fatal System Management Interrupts
(SMIs) to be disabled on supported IBM platforms and is
intended to be coupled with a user-space daemon to monitor
the hardware in a way that can be prioritized and scheduled
to better suit the requirements for the system.

The Device is presented as a special "_RTL_" table to the OS
in the Extended BIOS Data Area. There is a simple protocol
for entering and exiting the mode at runtime. This driver
creates a simple sysfs interface to allow a simple entry and
exit from RTL mode in the UFI/BIOS.

Since the driver is specific to IBM SystemX hardware (x86-
based servers) it only builds on x86 builds. To reduce the
risk of loading on the wrong hardware, the module uses DMI
information and checks a list of servers that are known to
work.

Signed-off-by: Vernon Mauery <vernux@us.ibm.com>
Signed-off-by: Matthew Garrett <mjg@redhat.com>

authored by

Vernon Mauery and committed by
Matthew Garrett
35f0ce03 260586d2

+380 -1
+22
Documentation/ABI/testing/sysfs-devices-system-ibm-rtl
··· 1 + What: state 2 + Date: Sep 2010 3 + KernelVersion: 2.6.37 4 + Contact: Vernon Mauery <vernux@us.ibm.com> 5 + Description: The state file allows a means by which to change in and 6 + out of Premium Real-Time Mode (PRTM), as well as the 7 + ability to query the current state. 8 + 0 => PRTM off 9 + 1 => PRTM enabled 10 + Users: The ibm-prtm userspace daemon uses this interface. 11 + 12 + 13 + What: version 14 + Date: Sep 2010 15 + KernelVersion: 2.6.37 16 + Contact: Vernon Mauery <vernux@us.ibm.com> 17 + Description: The version file provides a means by which to query 18 + the RTL table version that lives in the Extended 19 + BIOS Data Area (EBDA). 20 + Users: The ibm-prtm userspace daemon uses this interface. 21 + 22 +
+16
drivers/platform/x86/Kconfig
··· 615 615 functionality. If in doubt, say Y here; it will only load on 616 616 supported platforms. 617 617 618 + config IBM_RTL 619 + tristate "Device driver to enable PRTL support" 620 + depends on X86 && PCI 621 + ---help--- 622 + Enable support for IBM Premium Real Time Mode (PRTM). 623 + This module will allow you the enter and exit PRTM in the BIOS via 624 + sysfs on platforms that support this feature. System in PRTM will 625 + not receive CPU-generated SMIs for recoverable errors. Use of this 626 + feature without proper support may void your hardware warranty. 627 + 628 + If the proper BIOS support is found the driver will load and create 629 + /sys/devices/system/ibm_rtl/. The "state" variable will indicate 630 + whether or not the BIOS is in PRTM. 631 + state = 0 (BIOS SMIs on) 632 + state = 1 (BIOS SMIs off) 633 + 618 634 config XO1_RFKILL 619 635 tristate "OLPC XO-1 software RF kill switch" 620 636 depends on OLPC
+1 -1
drivers/platform/x86/Makefile
··· 32 32 obj-$(CONFIG_INTEL_IPS) += intel_ips.o 33 33 obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o 34 34 obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o 35 - 35 + obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
+341
drivers/platform/x86/ibm_rtl.c
··· 1 + /* 2 + * IBM Real-Time Linux driver 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, write to the Free Software 16 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 + * 18 + * Copyright (C) IBM Corporation, 2010 19 + * 20 + * Author: Keith Mannthey <kmannth@us.ibm.com> 21 + * Vernon Mauery <vernux@us.ibm.com> 22 + * 23 + */ 24 + 25 + #include <linux/kernel.h> 26 + #include <linux/delay.h> 27 + #include <linux/module.h> 28 + #include <linux/io.h> 29 + #include <linux/sysdev.h> 30 + #include <linux/dmi.h> 31 + #include <linux/mutex.h> 32 + #include <asm/bios_ebda.h> 33 + 34 + static bool force; 35 + module_param(force, bool, 0); 36 + MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); 37 + 38 + static bool debug; 39 + module_param(debug, bool, 0644); 40 + MODULE_PARM_DESC(debug, "Show debug output"); 41 + 42 + MODULE_LICENSE("GPL"); 43 + MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>"); 44 + MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>"); 45 + 46 + #define RTL_ADDR_TYPE_IO 1 47 + #define RTL_ADDR_TYPE_MMIO 2 48 + 49 + #define RTL_CMD_ENTER_PRTM 1 50 + #define RTL_CMD_EXIT_PRTM 2 51 + 52 + /* The RTL table as presented by the EBDA: */ 53 + struct ibm_rtl_table { 54 + char signature[5]; /* signature should be "_RTL_" */ 55 + u8 version; 56 + u8 rt_status; 57 + u8 command; 58 + u8 command_status; 59 + u8 cmd_address_type; 60 + u8 cmd_granularity; 61 + u8 cmd_offset; 62 + u16 reserve1; 63 + u32 cmd_port_address; /* platform dependent address */ 64 + u32 cmd_port_value; /* platform dependent value */ 65 + } __attribute__((packed)); 66 + 67 + /* to locate "_RTL_" signature do a masked 5-byte integer compare */ 68 + #define RTL_SIGNATURE 0x0000005f4c54525fULL 69 + #define RTL_MASK 0x000000ffffffffffULL 70 + 71 + #define RTL_DEBUG(A, ...) do { \ 72 + if (debug) \ 73 + pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \ 74 + } while (0) 75 + 76 + static DEFINE_MUTEX(rtl_lock); 77 + static struct ibm_rtl_table __iomem *rtl_table; 78 + static void __iomem *ebda_map; 79 + static void __iomem *rtl_cmd_addr; 80 + static u8 rtl_cmd_type; 81 + static u8 rtl_cmd_width; 82 + 83 + static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len) 84 + { 85 + if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO) 86 + return ioremap(addr, len); 87 + return ioport_map(addr, len); 88 + } 89 + 90 + static void rtl_port_unmap(void __iomem *addr) 91 + { 92 + if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO) 93 + iounmap(addr); 94 + else 95 + ioport_unmap(addr); 96 + } 97 + 98 + static int ibm_rtl_write(u8 value) 99 + { 100 + int ret = 0, count = 0; 101 + static u32 cmd_port_val; 102 + 103 + RTL_DEBUG("%s(%d)\n", __FUNCTION__, value); 104 + 105 + value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM; 106 + 107 + mutex_lock(&rtl_lock); 108 + 109 + if (ioread8(&rtl_table->rt_status) != value) { 110 + iowrite8(value, &rtl_table->command); 111 + 112 + switch (rtl_cmd_width) { 113 + case 8: 114 + cmd_port_val = ioread8(&rtl_table->cmd_port_value); 115 + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); 116 + iowrite8((u8)cmd_port_val, rtl_cmd_addr); 117 + break; 118 + case 16: 119 + cmd_port_val = ioread16(&rtl_table->cmd_port_value); 120 + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); 121 + iowrite16((u16)cmd_port_val, rtl_cmd_addr); 122 + break; 123 + case 32: 124 + cmd_port_val = ioread32(&rtl_table->cmd_port_value); 125 + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); 126 + iowrite32(cmd_port_val, rtl_cmd_addr); 127 + break; 128 + } 129 + 130 + while (ioread8(&rtl_table->command)) { 131 + msleep(10); 132 + if (count++ > 500) { 133 + pr_err("ibm-rtl: Hardware not responding to " 134 + "mode switch request\n"); 135 + ret = -EIO; 136 + break; 137 + } 138 + 139 + } 140 + 141 + if (ioread8(&rtl_table->command_status)) { 142 + RTL_DEBUG("command_status reports failed command\n"); 143 + ret = -EIO; 144 + } 145 + } 146 + 147 + mutex_unlock(&rtl_lock); 148 + return ret; 149 + } 150 + 151 + static ssize_t rtl_show_version(struct sysdev_class * dev, 152 + struct sysdev_class_attribute *attr, 153 + char *buf) 154 + { 155 + return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version)); 156 + } 157 + 158 + static ssize_t rtl_show_state(struct sysdev_class *dev, 159 + struct sysdev_class_attribute *attr, 160 + char *buf) 161 + { 162 + return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status)); 163 + } 164 + 165 + static ssize_t rtl_set_state(struct sysdev_class *dev, 166 + struct sysdev_class_attribute *attr, 167 + const char *buf, 168 + size_t count) 169 + { 170 + ssize_t ret; 171 + 172 + if (count < 1 || count > 2) 173 + return -EINVAL; 174 + 175 + switch (buf[0]) { 176 + case '0': 177 + ret = ibm_rtl_write(0); 178 + break; 179 + case '1': 180 + ret = ibm_rtl_write(1); 181 + break; 182 + default: 183 + ret = -EINVAL; 184 + } 185 + if (ret >= 0) 186 + ret = count; 187 + 188 + return ret; 189 + } 190 + 191 + static struct sysdev_class class_rtl = { 192 + .name = "ibm_rtl", 193 + }; 194 + 195 + static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL); 196 + static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state); 197 + 198 + static struct sysdev_class_attribute *rtl_attributes[] = { 199 + &attr_version, 200 + &attr_state, 201 + NULL 202 + }; 203 + 204 + 205 + static int rtl_setup_sysfs(void) { 206 + int ret, i; 207 + ret = sysdev_class_register(&class_rtl); 208 + 209 + if (!ret) { 210 + for (i = 0; rtl_attributes[i]; i ++) 211 + sysdev_class_create_file(&class_rtl, rtl_attributes[i]); 212 + } 213 + return ret; 214 + } 215 + 216 + static void rtl_teardown_sysfs(void) { 217 + int i; 218 + for (i = 0; rtl_attributes[i]; i ++) 219 + sysdev_class_remove_file(&class_rtl, rtl_attributes[i]); 220 + sysdev_class_unregister(&class_rtl); 221 + } 222 + 223 + static int dmi_check_cb(const struct dmi_system_id *id) 224 + { 225 + RTL_DEBUG("found IBM server '%s'\n", id->ident); 226 + return 0; 227 + } 228 + 229 + #define ibm_dmi_entry(NAME, TYPE) \ 230 + { \ 231 + .ident = NAME, \ 232 + .matches = { \ 233 + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \ 234 + DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \ 235 + }, \ 236 + .callback = dmi_check_cb \ 237 + } 238 + 239 + static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = { 240 + ibm_dmi_entry("BladeCenter LS21", "7971"), 241 + ibm_dmi_entry("BladeCenter LS22", "7901"), 242 + ibm_dmi_entry("BladeCenter HS21 XM", "7995"), 243 + ibm_dmi_entry("BladeCenter HS22", "7870"), 244 + ibm_dmi_entry("BladeCenter HS22V", "7871"), 245 + ibm_dmi_entry("System x3550 M2", "7946"), 246 + ibm_dmi_entry("System x3650 M2", "7947"), 247 + ibm_dmi_entry("System x3550 M3", "7944"), 248 + ibm_dmi_entry("System x3650 M3", "7945"), 249 + { } 250 + }; 251 + 252 + static int __init ibm_rtl_init(void) { 253 + unsigned long ebda_addr, ebda_size; 254 + unsigned int ebda_kb; 255 + int ret = -ENODEV, i; 256 + 257 + if (force) 258 + pr_warning("ibm-rtl: module loaded by force\n"); 259 + /* first ensure that we are running on IBM HW */ 260 + else if (!dmi_check_system(ibm_rtl_dmi_table)) 261 + return -ENODEV; 262 + 263 + /* Get the address for the Extended BIOS Data Area */ 264 + ebda_addr = get_bios_ebda(); 265 + if (!ebda_addr) { 266 + RTL_DEBUG("no BIOS EBDA found\n"); 267 + return -ENODEV; 268 + } 269 + 270 + ebda_map = ioremap(ebda_addr, 4); 271 + if (!ebda_map) 272 + return -ENOMEM; 273 + 274 + /* First word in the EDBA is the Size in KB */ 275 + ebda_kb = ioread16(ebda_map); 276 + RTL_DEBUG("EBDA is %d kB\n", ebda_kb); 277 + 278 + if (ebda_kb == 0) 279 + goto out; 280 + 281 + iounmap(ebda_map); 282 + ebda_size = ebda_kb*1024; 283 + 284 + /* Remap the whole table */ 285 + ebda_map = ioremap(ebda_addr, ebda_size); 286 + if (!ebda_map) 287 + return -ENOMEM; 288 + 289 + /* search for the _RTL_ signature at the start of the table */ 290 + for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) { 291 + struct ibm_rtl_table __iomem * tmp; 292 + tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i); 293 + if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) { 294 + phys_addr_t addr; 295 + unsigned int plen; 296 + RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp); 297 + rtl_table = tmp; 298 + /* The address, value, width and offset are platform 299 + * dependent and found in the ibm_rtl_table */ 300 + rtl_cmd_width = ioread8(&rtl_table->cmd_granularity); 301 + rtl_cmd_type = ioread8(&rtl_table->cmd_address_type); 302 + RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n", 303 + rtl_cmd_width, rtl_cmd_type); 304 + addr = ioread32(&rtl_table->cmd_port_address); 305 + RTL_DEBUG("addr = %#llx\n", addr); 306 + plen = rtl_cmd_width/sizeof(char); 307 + rtl_cmd_addr = rtl_port_map(addr, plen); 308 + RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr); 309 + if (!rtl_cmd_addr) { 310 + ret = -ENOMEM; 311 + break; 312 + } 313 + ret = rtl_setup_sysfs(); 314 + break; 315 + } 316 + } 317 + 318 + out: 319 + if (ret) { 320 + iounmap(ebda_map); 321 + rtl_port_unmap(rtl_cmd_addr); 322 + } 323 + 324 + return ret; 325 + } 326 + 327 + static void __exit ibm_rtl_exit(void) 328 + { 329 + if (rtl_table) { 330 + RTL_DEBUG("cleaning up"); 331 + /* do not leave the machine in SMI-free mode */ 332 + ibm_rtl_write(0); 333 + /* unmap, unlink and remove all traces */ 334 + rtl_teardown_sysfs(); 335 + iounmap(ebda_map); 336 + rtl_port_unmap(rtl_cmd_addr); 337 + } 338 + } 339 + 340 + module_init(ibm_rtl_init); 341 + module_exit(ibm_rtl_exit);