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