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