159 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * memconsole-x86-legacy.c4 *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 0xDEADBABE20#define BIOS_MEMCONSOLE_V2_MAGIC (('M')|('C'<<8)|('O'<<16)|('N'<<24))21 22struct 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 43static char *memconsole_baseaddr;44static size_t memconsole_length;45 46static 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 52static 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 65static 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, and80 * set the global variables to point to it. Return true if found.81 */82static 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 structure99 * note: signature is not necessarily dword-aligned100 */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 121static 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};130MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);131 132static 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 140static int __init memconsole_x86_init(void)141{142 if (!memconsole_find())143 return -ENODEV;144 145 return memconsole_sysfs_init();146}147 148static void __exit memconsole_x86_exit(void)149{150 memconsole_exit();151}152 153module_init(memconsole_x86_init);154module_exit(memconsole_x86_exit);155 156MODULE_AUTHOR("Google, Inc.");157MODULE_DESCRIPTION("EBDA specific parts of the memory based BIOS console.");158MODULE_LICENSE("GPL");159