xref: /aosp_15_r20/external/coreboot/src/drivers/spi/flashconsole.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <commonlib/helpers.h>
4 #include <commonlib/region.h>
5 #include <fmap.h>
6 #include <console/console.h>
7 #include <console/flash.h>
8 #include <types.h>
9 
10 #define LINE_BUFFER_SIZE 128
11 #define READ_BUFFER_SIZE 0x100
12 
13 static const struct region_device *rdev_ptr;
14 static struct region_device rdev;
15 static uint8_t line_buffer[LINE_BUFFER_SIZE];
16 static size_t offset;
17 static size_t line_offset;
18 
flashconsole_init(void)19 void flashconsole_init(void)
20 {
21 	uint8_t buffer[READ_BUFFER_SIZE];
22 	size_t size;
23 	size_t initial_offset = 0;
24 	size_t len = READ_BUFFER_SIZE;
25 	size_t i;
26 
27 	if (fmap_locate_area_as_rdev_rw("CONSOLE", &rdev)) {
28 		printk(BIOS_INFO, "Can't find 'CONSOLE' area in FMAP\n");
29 		return;
30 	}
31 	size = region_device_sz(&rdev);
32 
33 	/*
34 	 * We need to check the region until we find a 0xff indicating
35 	 * the end of a previous log write.
36 	 * We can't erase the region because one stage would erase the
37 	 * data from the previous stage. Also, it looks like doing an
38 	 * erase could completely freeze the SPI controller and then
39 	 * we can't write anything anymore (apparently might happen if
40 	 * the sector is already erased, so we would need to read
41 	 * anyways to check if it's all 0xff).
42 	 */
43 	for (i = 0; i < len && initial_offset < size;) {
44 		// Fill the buffer on first iteration
45 		if (i == 0) {
46 			len = MIN(READ_BUFFER_SIZE, size - offset);
47 			if (rdev_readat(&rdev, buffer, initial_offset, len) != len)
48 				return;
49 		}
50 		if (buffer[i] == 0xff) {
51 			initial_offset += i;
52 			break;
53 		}
54 		// If we're done, repeat the process for the next sector
55 		if (++i == READ_BUFFER_SIZE) {
56 			initial_offset += len;
57 			i = 0;
58 		}
59 	}
60 	// Make sure there is still space left on the console
61 	if (initial_offset >= size) {
62 		printk(BIOS_INFO, "No space left on 'console' region in SPI flash\n");
63 		return;
64 	}
65 
66 	offset = initial_offset;
67 	rdev_ptr = &rdev;
68 }
69 
flashconsole_tx_byte(unsigned char c)70 void flashconsole_tx_byte(unsigned char c)
71 {
72 	if (!rdev_ptr)
73 		return;
74 
75 	size_t region_size = region_device_sz(rdev_ptr);
76 
77 	if (line_offset < LINE_BUFFER_SIZE)
78 		line_buffer[line_offset++] = c;
79 
80 	if (line_offset >= LINE_BUFFER_SIZE ||
81 	    offset + line_offset >= region_size || c == '\n') {
82 		flashconsole_tx_flush();
83 	}
84 }
85 
flashconsole_tx_flush(void)86 void flashconsole_tx_flush(void)
87 {
88 	size_t len = line_offset;
89 	size_t region_size;
90 	static int busy;
91 
92 	/* Prevent any recursive loops in case the spi flash driver
93 	 * calls printk (in case of transaction timeout or
94 	 * any other error while writing) */
95 	if (busy)
96 		return;
97 
98 	if (!rdev_ptr)
99 		return;
100 
101 	busy = 1;
102 	region_size = region_device_sz(rdev_ptr);
103 	if (offset + len >= region_size)
104 		len = region_size - offset;
105 
106 	if (rdev_writeat(&rdev, line_buffer, offset, len) != len)
107 		return;
108 
109 	// If the region is full, stop future write attempts
110 	if (offset + len >= region_size)
111 		return;
112 
113 	offset += len;
114 	line_offset = 0;
115 
116 	busy = 0;
117 }
118