1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * memconsole-x86-legacy.c 4 * 5 * EBDA specific parts of the memory based BIOS console. 6 * 7 * Copyright 2017 Google Inc. 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/dmi.h> 13 #include <linux/mm.h> 14 #include <asm/bios_ebda.h> 15 #include <linux/acpi.h> 16 17 #include "memconsole.h" 18 19 #define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE 20 #define BIOS_MEMCONSOLE_V2_MAGIC (('M')|('C'<<8)|('O'<<16)|('N'<<24)) 21 22 struct biosmemcon_ebda { 23 u32 signature; 24 union { 25 struct { 26 u8 enabled; 27 u32 buffer_addr; 28 u16 start; 29 u16 end; 30 u16 num_chars; 31 u8 wrapped; 32 } __packed v1; 33 struct { 34 u32 buffer_addr; 35 /* Misdocumented as number of pages! */ 36 u16 num_bytes; 37 u16 start; 38 u16 end; 39 } __packed v2; 40 }; 41 } __packed; 42 43 static char *memconsole_baseaddr; 44 static size_t memconsole_length; 45 46 static ssize_t memconsole_read(char *buf, loff_t pos, size_t count) 47 { 48 return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr, 49 memconsole_length); 50 } 51 52 static void found_v1_header(struct biosmemcon_ebda *hdr) 53 { 54 pr_info("memconsole: BIOS console v1 EBDA structure found at %p\n", 55 hdr); 56 pr_info("memconsole: BIOS console buffer at 0x%.8x, start = %d, end = %d, num = %d\n", 57 hdr->v1.buffer_addr, hdr->v1.start, 58 hdr->v1.end, hdr->v1.num_chars); 59 60 memconsole_baseaddr = phys_to_virt(hdr->v1.buffer_addr); 61 memconsole_length = hdr->v1.num_chars; 62 memconsole_setup(memconsole_read); 63 } 64 65 static void found_v2_header(struct biosmemcon_ebda *hdr) 66 { 67 pr_info("memconsole: BIOS console v2 EBDA structure found at %p\n", 68 hdr); 69 pr_info("memconsole: BIOS console buffer at 0x%.8x, start = %d, end = %d, num_bytes = %d\n", 70 hdr->v2.buffer_addr, hdr->v2.start, 71 hdr->v2.end, hdr->v2.num_bytes); 72 73 memconsole_baseaddr = phys_to_virt(hdr->v2.buffer_addr + hdr->v2.start); 74 memconsole_length = hdr->v2.end - hdr->v2.start; 75 memconsole_setup(memconsole_read); 76 } 77 78 /* 79 * Search through the EBDA for the BIOS Memory Console, and 80 * set the global variables to point to it. Return true if found. 81 */ 82 static bool memconsole_ebda_init(void) 83 { 84 unsigned int address; 85 size_t length, cur; 86 87 address = get_bios_ebda(); 88 if (!address) { 89 pr_info("memconsole: BIOS EBDA non-existent.\n"); 90 return false; 91 } 92 93 /* EBDA length is byte 0 of EBDA (in KB) */ 94 length = *(u8 *)phys_to_virt(address); 95 length <<= 10; /* convert to bytes */ 96 97 /* 98 * Search through EBDA for BIOS memory console structure 99 * note: signature is not necessarily dword-aligned 100 */ 101 for (cur = 0; cur < length; cur++) { 102 struct biosmemcon_ebda *hdr = phys_to_virt(address + cur); 103 104 /* memconsole v1 */ 105 if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) { 106 found_v1_header(hdr); 107 return true; 108 } 109 110 /* memconsole v2 */ 111 if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) { 112 found_v2_header(hdr); 113 return true; 114 } 115 } 116 117 pr_info("memconsole: BIOS console EBDA structure not found!\n"); 118 return false; 119 } 120 121 static const struct dmi_system_id memconsole_dmi_table[] __initconst = { 122 { 123 .ident = "Google Board", 124 .matches = { 125 DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."), 126 }, 127 }, 128 {} 129 }; 130 MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table); 131 132 static bool __init memconsole_find(void) 133 { 134 if (!dmi_check_system(memconsole_dmi_table)) 135 return false; 136 137 return memconsole_ebda_init(); 138 } 139 140 static int __init memconsole_x86_init(void) 141 { 142 if (!memconsole_find()) 143 return -ENODEV; 144 145 return memconsole_sysfs_init(); 146 } 147 148 static void __exit memconsole_x86_exit(void) 149 { 150 memconsole_exit(); 151 } 152 153 module_init(memconsole_x86_init); 154 module_exit(memconsole_x86_exit); 155 156 MODULE_AUTHOR("Google, Inc."); 157 MODULE_LICENSE("GPL"); 158