1 /*
2  * memconsole-x86-legacy.c
3  *
4  * EBDA specific parts of the memory based BIOS console.
5  *
6  * Copyright 2017 Google Inc.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License v2.0 as published by
10  * the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17 
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/dmi.h>
21 #include <linux/mm.h>
22 #include <asm/bios_ebda.h>
23 #include <asm/e820.h>
24 #include <linux/acpi.h>
25 
26 #include "memconsole.h"
27 
28 #define BIOS_MEMCONSOLE_V1_MAGIC	0xDEADBABE
29 #define BIOS_MEMCONSOLE_V2_MAGIC	(('M')|('C'<<8)|('O'<<16)|('N'<<24))
30 
31 struct biosmemcon_ebda {
32 	u32 signature;
33 	union {
34 		struct {
35 			u8  enabled;
36 			u32 buffer_addr;
37 			u16 start;
38 			u16 end;
39 			u16 num_chars;
40 			u8  wrapped;
41 		} __packed v1;
42 		struct {
43 			u32 buffer_addr;
44 			/* Misdocumented as number of pages! */
45 			u16 num_bytes;
46 			u16 start;
47 			u16 end;
48 		} __packed v2;
49 	};
50 } __packed;
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_setup(phys_to_virt(hdr->v1.buffer_addr), hdr->v1.num_chars);
61 }
62 
63 static void found_v2_header(struct biosmemcon_ebda *hdr)
64 {
65 	pr_info("memconsole: BIOS console v2 EBDA structure found at %p\n",
66 		hdr);
67 	pr_info("memconsole: BIOS console buffer at 0x%.8x, start = %d, end = %d, num_bytes = %d\n",
68 		hdr->v2.buffer_addr, hdr->v2.start,
69 		hdr->v2.end, hdr->v2.num_bytes);
70 
71 	memconsole_setup(phys_to_virt(hdr->v2.buffer_addr + hdr->v2.start),
72 			 hdr->v2.end - hdr->v2.start);
73 }
74 
75 /*
76  * Search through the EBDA for the BIOS Memory Console, and
77  * set the global variables to point to it.  Return true if found.
78  */
79 static bool memconsole_ebda_init(void)
80 {
81 	unsigned int address;
82 	size_t length, cur;
83 
84 	address = get_bios_ebda();
85 	if (!address) {
86 		pr_info("memconsole: BIOS EBDA non-existent.\n");
87 		return false;
88 	}
89 
90 	/* EBDA length is byte 0 of EBDA (in KB) */
91 	length = *(u8 *)phys_to_virt(address);
92 	length <<= 10; /* convert to bytes */
93 
94 	/*
95 	 * Search through EBDA for BIOS memory console structure
96 	 * note: signature is not necessarily dword-aligned
97 	 */
98 	for (cur = 0; cur < length; cur++) {
99 		struct biosmemcon_ebda *hdr = phys_to_virt(address + cur);
100 
101 		/* memconsole v1 */
102 		if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) {
103 			found_v1_header(hdr);
104 			return true;
105 		}
106 
107 		/* memconsole v2 */
108 		if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) {
109 			found_v2_header(hdr);
110 			return true;
111 		}
112 	}
113 
114 	pr_info("memconsole: BIOS console EBDA structure not found!\n");
115 	return false;
116 }
117 
118 static struct dmi_system_id memconsole_dmi_table[] __initdata = {
119 	{
120 		.ident = "Google Board",
121 		.matches = {
122 			DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
123 		},
124 	},
125 	{}
126 };
127 MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
128 
129 static bool __init memconsole_find(void)
130 {
131 	if (!dmi_check_system(memconsole_dmi_table))
132 		return false;
133 
134 	return memconsole_ebda_init();
135 }
136 
137 static int __init memconsole_x86_init(void)
138 {
139 	if (!memconsole_find())
140 		return -ENODEV;
141 
142 	return memconsole_sysfs_init();
143 }
144 
145 static void __exit memconsole_x86_exit(void)
146 {
147 	memconsole_exit();
148 }
149 
150 module_init(memconsole_x86_init);
151 module_exit(memconsole_x86_exit);
152 
153 MODULE_AUTHOR("Google, Inc.");
154 MODULE_LICENSE("GPL");
155