2019-05-29 07:18:01 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2017-03-28 18:11:27 +02:00
/*
* memconsole - coreboot . c
*
* Memory based BIOS console accessed through coreboot table .
*
* Copyright 2017 Google Inc .
*/
2018-01-24 19:41:17 -06:00
# include <linux/device.h>
2019-05-10 11:01:51 -07:00
# include <linux/io.h>
2017-03-28 18:11:27 +02:00
# include <linux/kernel.h>
# include <linux/module.h>
# include "memconsole.h"
# include "coreboot_table.h"
# define CB_TAG_CBMEM_CONSOLE 0x17
/* CBMEM firmware console log descriptor. */
struct cbmem_cons {
2017-05-23 16:48:17 -07:00
u32 size_dont_access_after_boot ;
2017-05-02 15:16:30 -07:00
u32 cursor ;
2020-02-21 09:30:21 -06:00
u8 body [ ] ;
2017-03-28 18:11:27 +02:00
} __packed ;
2017-05-02 15:16:30 -07:00
# define CURSOR_MASK ((1 << 28) - 1)
# define OVERFLOW (1 << 31)
2019-05-10 11:01:49 -07:00
static struct cbmem_cons * cbmem_console ;
2017-05-23 16:48:17 -07:00
static u32 cbmem_console_size ;
2017-03-28 18:11:27 +02:00
2017-05-02 15:16:30 -07:00
/*
* The cbmem_console structure is read again on every access because it may
* change at any time if runtime firmware logs new messages . This may rarely
* lead to race conditions where the firmware overwrites the beginning of the
* ring buffer with more lines after we have already read | cursor | . It should be
* rare and harmless enough that we don ' t spend extra effort working around it .
*/
2017-05-02 15:16:29 -07:00
static ssize_t memconsole_coreboot_read ( char * buf , loff_t pos , size_t count )
{
2017-05-02 15:16:30 -07:00
u32 cursor = cbmem_console - > cursor & CURSOR_MASK ;
u32 flags = cbmem_console - > cursor & ~ CURSOR_MASK ;
2017-05-23 16:48:17 -07:00
u32 size = cbmem_console_size ;
2017-05-02 15:16:30 -07:00
struct seg { /* describes ring buffer segments in logical order */
u32 phys ; /* physical offset from start of mem buffer */
u32 len ; /* length of segment */
} seg [ 2 ] = { { 0 } , { 0 } } ;
size_t done = 0 ;
int i ;
if ( flags & OVERFLOW ) {
if ( cursor > size ) /* Shouldn't really happen, but... */
cursor = 0 ;
seg [ 0 ] = ( struct seg ) { . phys = cursor , . len = size - cursor } ;
seg [ 1 ] = ( struct seg ) { . phys = 0 , . len = cursor } ;
} else {
seg [ 0 ] = ( struct seg ) { . phys = 0 , . len = min ( cursor , size ) } ;
}
for ( i = 0 ; i < ARRAY_SIZE ( seg ) & & count > done ; i + + ) {
done + = memory_read_from_buffer ( buf + done , count - done , & pos ,
cbmem_console - > body + seg [ i ] . phys , seg [ i ] . len ) ;
pos - = seg [ i ] . len ;
}
return done ;
2017-05-02 15:16:29 -07:00
}
2018-01-24 19:41:17 -06:00
static int memconsole_probe ( struct coreboot_device * dev )
2017-03-28 18:11:27 +02:00
{
2019-05-10 11:01:49 -07:00
struct cbmem_cons * tmp_cbmc ;
2017-03-28 18:11:27 +02:00
2018-01-24 19:41:17 -06:00
tmp_cbmc = memremap ( dev - > cbmem_ref . cbmem_addr ,
sizeof ( * tmp_cbmc ) , MEMREMAP_WB ) ;
2017-03-28 18:11:27 +02:00
if ( ! tmp_cbmc )
return - ENOMEM ;
2017-05-23 16:48:17 -07:00
/* Read size only once to prevent overrun attack through /dev/mem. */
cbmem_console_size = tmp_cbmc - > size_dont_access_after_boot ;
2019-05-10 14:24:53 -07:00
cbmem_console = devm_memremap ( & dev - > dev , dev - > cbmem_ref . cbmem_addr ,
2017-05-23 16:48:17 -07:00
cbmem_console_size + sizeof ( * cbmem_console ) ,
2017-03-28 18:11:27 +02:00
MEMREMAP_WB ) ;
memunmap ( tmp_cbmc ) ;
2019-05-10 14:24:53 -07:00
if ( IS_ERR ( cbmem_console ) )
return PTR_ERR ( cbmem_console ) ;
2017-03-28 18:11:27 +02:00
2017-05-02 15:16:29 -07:00
memconsole_setup ( memconsole_coreboot_read ) ;
2017-03-28 18:11:27 +02:00
return memconsole_sysfs_init ( ) ;
}
2021-01-26 22:53:39 +01:00
static void memconsole_remove ( struct coreboot_device * dev )
2017-03-28 18:11:27 +02:00
{
memconsole_exit ( ) ;
}
2018-01-24 19:41:17 -06:00
static struct coreboot_driver memconsole_driver = {
2017-03-28 18:11:27 +02:00
. probe = memconsole_probe ,
. remove = memconsole_remove ,
2018-01-24 19:41:17 -06:00
. drv = {
2017-03-28 18:11:27 +02:00
. name = " memconsole " ,
} ,
2018-01-24 19:41:17 -06:00
. tag = CB_TAG_CBMEM_CONSOLE ,
2017-03-28 18:11:27 +02:00
} ;
2019-05-10 11:01:47 -07:00
module_coreboot_driver ( memconsole_driver ) ;
2017-03-28 18:11:27 +02:00
MODULE_AUTHOR ( " Google, Inc. " ) ;
MODULE_LICENSE ( " GPL " ) ;