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