linux/drivers/firmware/google/memconsole-x86-legacy.c
<<
>>
Prefs
   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
  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, and
  80 * 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 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
 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_LICENSE("GPL");
 158