1*426790bdSJeffrey Kardatzke // SPDX-License-Identifier: BSD-2-Clause 2*426790bdSJeffrey Kardatzke /* 3*426790bdSJeffrey Kardatzke * Copyright (c) 2023, Google Limited 4*426790bdSJeffrey Kardatzke */ 5*426790bdSJeffrey Kardatzke 6*426790bdSJeffrey Kardatzke #include <compiler.h> 7*426790bdSJeffrey Kardatzke #include <console.h> 8*426790bdSJeffrey Kardatzke #include <drivers/cbmem_console.h> 9*426790bdSJeffrey Kardatzke #include <drivers/serial.h> 10*426790bdSJeffrey Kardatzke #include <io.h> 11*426790bdSJeffrey Kardatzke #include <keep.h> 12*426790bdSJeffrey Kardatzke #include <kernel/dt.h> 13*426790bdSJeffrey Kardatzke #include <libfdt.h> 14*426790bdSJeffrey Kardatzke #include <mm/core_mmu.h> 15*426790bdSJeffrey Kardatzke #include <types_ext.h> 16*426790bdSJeffrey Kardatzke #include <util.h> 17*426790bdSJeffrey Kardatzke 18*426790bdSJeffrey Kardatzke #define CURSOR_MASK (BIT(28) - 1) 19*426790bdSJeffrey Kardatzke #define OVERFLOW BIT(31) 20*426790bdSJeffrey Kardatzke 21*426790bdSJeffrey Kardatzke struct cbmem_console { 22*426790bdSJeffrey Kardatzke uint32_t size; 23*426790bdSJeffrey Kardatzke uint32_t cursor; 24*426790bdSJeffrey Kardatzke uint8_t body[0]; 25*426790bdSJeffrey Kardatzke }; 26*426790bdSJeffrey Kardatzke 27*426790bdSJeffrey Kardatzke struct cbmem_console_data { 28*426790bdSJeffrey Kardatzke paddr_t base; 29*426790bdSJeffrey Kardatzke struct cbmem_console *console; 30*426790bdSJeffrey Kardatzke struct serial_chip chip; 31*426790bdSJeffrey Kardatzke uint32_t size; 32*426790bdSJeffrey Kardatzke }; 33*426790bdSJeffrey Kardatzke 34*426790bdSJeffrey Kardatzke /* 35*426790bdSJeffrey Kardatzke * Structures describing coreboot's in-memory descriptor tables. See 36*426790bdSJeffrey Kardatzke * https://github.com/coreboot/coreboot/blob/ea2a38be323173075db3b13729a4006ea1fef72d/src/commonlib/include/commonlib/coreboot_tables.h 37*426790bdSJeffrey Kardatzke * for canonical implementation. 38*426790bdSJeffrey Kardatzke */ 39*426790bdSJeffrey Kardatzke 40*426790bdSJeffrey Kardatzke struct cb_header { 41*426790bdSJeffrey Kardatzke uint8_t signature[4]; 42*426790bdSJeffrey Kardatzke uint32_t header_bytes; 43*426790bdSJeffrey Kardatzke uint32_t header_checksum; 44*426790bdSJeffrey Kardatzke uint32_t table_bytes; 45*426790bdSJeffrey Kardatzke uint32_t table_checksum; 46*426790bdSJeffrey Kardatzke uint32_t table_entries; 47*426790bdSJeffrey Kardatzke }; 48*426790bdSJeffrey Kardatzke 49*426790bdSJeffrey Kardatzke #define CB_TAG_CBMEM_CONSOLE 0x17 50*426790bdSJeffrey Kardatzke 51*426790bdSJeffrey Kardatzke struct cb_entry { 52*426790bdSJeffrey Kardatzke uint32_t tag; 53*426790bdSJeffrey Kardatzke uint32_t size; 54*426790bdSJeffrey Kardatzke uint64_t value; 55*426790bdSJeffrey Kardatzke }; 56*426790bdSJeffrey Kardatzke 57*426790bdSJeffrey Kardatzke static struct cbmem_console_data cbmem_console; 58*426790bdSJeffrey Kardatzke 59*426790bdSJeffrey Kardatzke static void cbmem_console_flush(struct serial_chip *chip __unused) 60*426790bdSJeffrey Kardatzke { 61*426790bdSJeffrey Kardatzke } 62*426790bdSJeffrey Kardatzke 63*426790bdSJeffrey Kardatzke static int cbmem_console_getchar(struct serial_chip *chip __unused) 64*426790bdSJeffrey Kardatzke { 65*426790bdSJeffrey Kardatzke return 0; 66*426790bdSJeffrey Kardatzke } 67*426790bdSJeffrey Kardatzke 68*426790bdSJeffrey Kardatzke static bool cbmem_console_have_rx_data(struct serial_chip *chip __unused) 69*426790bdSJeffrey Kardatzke { 70*426790bdSJeffrey Kardatzke return false; 71*426790bdSJeffrey Kardatzke } 72*426790bdSJeffrey Kardatzke 73*426790bdSJeffrey Kardatzke static void cbmem_console_putc(struct serial_chip *chip, int ch) 74*426790bdSJeffrey Kardatzke { 75*426790bdSJeffrey Kardatzke struct cbmem_console_data *pd = 76*426790bdSJeffrey Kardatzke container_of(chip, struct cbmem_console_data, chip); 77*426790bdSJeffrey Kardatzke struct cbmem_console *c = pd->console; 78*426790bdSJeffrey Kardatzke 79*426790bdSJeffrey Kardatzke if (!pd->size) 80*426790bdSJeffrey Kardatzke return; 81*426790bdSJeffrey Kardatzke 82*426790bdSJeffrey Kardatzke if ((c->cursor & CURSOR_MASK) + 1 >= pd->size) { 83*426790bdSJeffrey Kardatzke c->cursor &= ~CURSOR_MASK; 84*426790bdSJeffrey Kardatzke c->cursor |= OVERFLOW; 85*426790bdSJeffrey Kardatzke c->body[0] = (uint8_t)(ch & 0xFF); 86*426790bdSJeffrey Kardatzke } else { 87*426790bdSJeffrey Kardatzke c->body[c->cursor & CURSOR_MASK] = (uint8_t)(ch & 0xFF); 88*426790bdSJeffrey Kardatzke c->cursor++; 89*426790bdSJeffrey Kardatzke } 90*426790bdSJeffrey Kardatzke } 91*426790bdSJeffrey Kardatzke 92*426790bdSJeffrey Kardatzke static const struct serial_ops cbmem_console_ops = { 93*426790bdSJeffrey Kardatzke .flush = cbmem_console_flush, 94*426790bdSJeffrey Kardatzke .getchar = cbmem_console_getchar, 95*426790bdSJeffrey Kardatzke .have_rx_data = cbmem_console_have_rx_data, 96*426790bdSJeffrey Kardatzke .putc = cbmem_console_putc, 97*426790bdSJeffrey Kardatzke }; 98*426790bdSJeffrey Kardatzke DECLARE_KEEP_PAGER(cbmem_console_ops); 99*426790bdSJeffrey Kardatzke 100*426790bdSJeffrey Kardatzke static paddr_t get_cbmem_console_from_coreboot_table(paddr_t table_addr, 101*426790bdSJeffrey Kardatzke size_t table_size) 102*426790bdSJeffrey Kardatzke { 103*426790bdSJeffrey Kardatzke struct cb_header *header = NULL; 104*426790bdSJeffrey Kardatzke void *ptr = NULL; 105*426790bdSJeffrey Kardatzke uint32_t i = 0; 106*426790bdSJeffrey Kardatzke struct cb_entry *entry = NULL; 107*426790bdSJeffrey Kardatzke paddr_t cbmem_console_base = 0; 108*426790bdSJeffrey Kardatzke void *base = NULL; 109*426790bdSJeffrey Kardatzke 110*426790bdSJeffrey Kardatzke base = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, table_addr, table_size); 111*426790bdSJeffrey Kardatzke if (!base) 112*426790bdSJeffrey Kardatzke return 0; 113*426790bdSJeffrey Kardatzke 114*426790bdSJeffrey Kardatzke header = (struct cb_header *)base; 115*426790bdSJeffrey Kardatzke if (memcmp(header->signature, "LBIO", 4)) 116*426790bdSJeffrey Kardatzke goto done; 117*426790bdSJeffrey Kardatzke 118*426790bdSJeffrey Kardatzke if (header->header_bytes + header->table_bytes > table_size) 119*426790bdSJeffrey Kardatzke goto done; 120*426790bdSJeffrey Kardatzke 121*426790bdSJeffrey Kardatzke ptr = (uint8_t *)base + header->header_bytes; 122*426790bdSJeffrey Kardatzke for (i = 0; i < header->table_entries; ++i) { 123*426790bdSJeffrey Kardatzke entry = (struct cb_entry *)ptr; 124*426790bdSJeffrey Kardatzke if ((uint8_t *)ptr >= (uint8_t *)base + table_size - 125*426790bdSJeffrey Kardatzke sizeof(struct cb_entry)) { 126*426790bdSJeffrey Kardatzke goto done; 127*426790bdSJeffrey Kardatzke } 128*426790bdSJeffrey Kardatzke 129*426790bdSJeffrey Kardatzke switch (get_le32(&entry->tag)) { 130*426790bdSJeffrey Kardatzke case CB_TAG_CBMEM_CONSOLE: 131*426790bdSJeffrey Kardatzke cbmem_console_base = get_le64(&entry->value); 132*426790bdSJeffrey Kardatzke goto done; 133*426790bdSJeffrey Kardatzke default: 134*426790bdSJeffrey Kardatzke /* We skip all but one tag type. */ 135*426790bdSJeffrey Kardatzke break; 136*426790bdSJeffrey Kardatzke } 137*426790bdSJeffrey Kardatzke 138*426790bdSJeffrey Kardatzke ptr = (uint8_t *)ptr + get_le32(&entry->size); 139*426790bdSJeffrey Kardatzke } 140*426790bdSJeffrey Kardatzke 141*426790bdSJeffrey Kardatzke done: 142*426790bdSJeffrey Kardatzke core_mmu_remove_mapping(MEM_AREA_RAM_NSEC, base, table_size); 143*426790bdSJeffrey Kardatzke return cbmem_console_base; 144*426790bdSJeffrey Kardatzke } 145*426790bdSJeffrey Kardatzke 146*426790bdSJeffrey Kardatzke bool cbmem_console_init_from_dt(void *fdt) 147*426790bdSJeffrey Kardatzke { 148*426790bdSJeffrey Kardatzke int offset = 0; 149*426790bdSJeffrey Kardatzke paddr_t cb_addr = 0; 150*426790bdSJeffrey Kardatzke size_t cb_size = 0; 151*426790bdSJeffrey Kardatzke paddr_t cbmem_console_base = 0; 152*426790bdSJeffrey Kardatzke 153*426790bdSJeffrey Kardatzke if (!fdt) 154*426790bdSJeffrey Kardatzke return false; 155*426790bdSJeffrey Kardatzke 156*426790bdSJeffrey Kardatzke offset = fdt_path_offset(fdt, "/firmware/coreboot"); 157*426790bdSJeffrey Kardatzke if (offset < 0) 158*426790bdSJeffrey Kardatzke return false; 159*426790bdSJeffrey Kardatzke 160*426790bdSJeffrey Kardatzke cb_addr = fdt_reg_base_address(fdt, offset); 161*426790bdSJeffrey Kardatzke cb_size = fdt_reg_size(fdt, offset); 162*426790bdSJeffrey Kardatzke 163*426790bdSJeffrey Kardatzke cbmem_console_base = get_cbmem_console_from_coreboot_table(cb_addr, 164*426790bdSJeffrey Kardatzke cb_size); 165*426790bdSJeffrey Kardatzke if (!cbmem_console_base) 166*426790bdSJeffrey Kardatzke return false; 167*426790bdSJeffrey Kardatzke 168*426790bdSJeffrey Kardatzke cbmem_console.base = cbmem_console_base; 169*426790bdSJeffrey Kardatzke cbmem_console.console = (struct cbmem_console *) 170*426790bdSJeffrey Kardatzke core_mmu_add_mapping(MEM_AREA_RAM_NSEC, cbmem_console_base, 171*426790bdSJeffrey Kardatzke sizeof(struct cbmem_console)); 172*426790bdSJeffrey Kardatzke if (!cbmem_console.console) 173*426790bdSJeffrey Kardatzke return false; 174*426790bdSJeffrey Kardatzke 175*426790bdSJeffrey Kardatzke /* 176*426790bdSJeffrey Kardatzke * Copy the size now to prevent non-secure world from spoofing 177*426790bdSJeffrey Kardatzke * it later. 178*426790bdSJeffrey Kardatzke */ 179*426790bdSJeffrey Kardatzke cbmem_console.size = cbmem_console.console->size; 180*426790bdSJeffrey Kardatzke cbmem_console.chip.ops = &cbmem_console_ops; 181*426790bdSJeffrey Kardatzke 182*426790bdSJeffrey Kardatzke register_serial_console(&cbmem_console.chip); 183*426790bdSJeffrey Kardatzke return true; 184*426790bdSJeffrey Kardatzke } 185