@@ -17,6 +17,12 @@
static char *con_buf = (char *)INMEM_CON_START;
static size_t con_in;
+
+/*
+ * Skiboot is both the producer and consumer of the memcons. On the consumer
+ * side we need to keep track of how much of the log buffer has been written
+ * out to the console which is what con_out is for.
+ */
static size_t con_out;
static bool con_wrapped;
@@ -29,7 +35,7 @@ static struct opal_con_ops *opal_con_driver = &dummy_opal_con;
static struct lock con_lock = LOCK_UNLOCKED;
/* This is mapped via TCEs so we keep it alone in a page */
-struct memcons memcons __section(".data.memcons") = {
+struct memcons_desc memcons_desc __section(".data.memcons") = {
.magic = CPU_TO_BE64(MEMCONS_MAGIC),
.obuf_phys = CPU_TO_BE64(INMEM_CON_START),
.ibuf_phys = CPU_TO_BE64(INMEM_CON_START + INMEM_CON_OUT_LEN),
@@ -37,6 +43,15 @@ struct memcons memcons __section(".data.memcons") = {
.ibuf_size = CPU_TO_BE32(INMEM_CON_IN_LEN),
};
+struct memcons memcons = {
+ .desc = &memcons_desc,
+ .obuf = (char *) INMEM_CON_START,
+ .obuf_size = INMEM_CON_OUT_LEN,
+
+ .ibuf = (char *) (INMEM_CON_START + INMEM_CON_OUT_LEN),
+ .ibuf_size = INMEM_CON_IN_LEN,
+};
+
static bool dummy_console_enabled(void)
{
#ifdef FORCE_DUMMY_CONSOLE
@@ -187,7 +202,7 @@ static void inmem_write(char c)
}
/*
- * We must always re-generate memcons.out_pos because
+ * We must always re-generate memcons.desc->out_pos because
* under some circumstances, the console script will
* use a broken putmemproc that does RMW on the full
* 8 bytes containing out_pos and in_prod, thus corrupting
@@ -197,7 +212,7 @@ static void inmem_write(char c)
if (con_wrapped)
opos |= MEMCONS_OUT_POS_WRAP;
lwsync();
- memcons.out_pos = cpu_to_be32(opos);
+ memcons.desc->out_pos = cpu_to_be32(opos);
}
static void write_char(char c)
@@ -205,8 +220,8 @@ static void write_char(char c)
inmem_write(c);
/* If head reaches tail, push tail around & drop chars */
- if (con_out == memcons.out_pos)
- con_out = (memcons.out_pos + 1) % memcons.obuf_size;
+ if (con_out == memcons.desc->out_pos)
+ con_out = (memcons.desc->out_pos + 1) % memcons.desc->obuf_size;
}
ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count)
@@ -240,12 +255,12 @@ ssize_t write(int fd __unused, const void *buf, size_t count)
static size_t inmem_read(char *buf, size_t req)
{
size_t read = 0;
- char *ibuf = (char *)be64_to_cpu(memcons.ibuf_phys);
+ char *ibuf = (char *)be64_to_cpu(memcons.desc->ibuf_phys);
- while (req && be32_to_cpu(memcons.in_prod) != be32_to_cpu(memcons.in_cons)) {
- *(buf++) = ibuf[be32_to_cpu(memcons.in_cons)];
+ while (req && be32_to_cpu(memcons.desc->in_prod) != be32_to_cpu(memcons.desc->in_cons)) {
+ *(buf++) = ibuf[be32_to_cpu(memcons.desc->in_cons)];
lwsync();
- memcons.in_cons = cpu_to_be32((be32_to_cpu(memcons.in_cons) + 1) % INMEM_CON_IN_LEN);
+ memcons.desc->in_cons = cpu_to_be32((be32_to_cpu(memcons.desc->in_cons) + 1) % INMEM_CON_IN_LEN);
req--;
read++;
}
@@ -335,7 +350,7 @@ void init_opal_console(void)
void memcons_add_properties(void)
{
- dt_add_property_u64(opal_node, "ibm,opal-memcons", (u64) &memcons);
+ dt_add_property_u64(opal_node, "ibm,opal-memcons", (u64) &memcons_desc);
}
/*
@@ -407,7 +422,7 @@ static void dummy_console_poll(void *data __unused)
bool has_data = false;
lock(&con_lock);
- if (memcons.in_prod != memcons.in_cons)
+ if (memcons.desc->in_prod != memcons.desc->in_cons)
has_data = true;
if (has_data)
opal_update_pending_evt(OPAL_EVENT_CONSOLE_INPUT,
@@ -421,7 +436,7 @@ void dummy_console_add_nodes(void)
{
struct dt_property *p;
- add_opal_console_node(0, "raw", be32_to_cpu(memcons.obuf_size));
+ add_opal_console_node(0, "raw", be32_to_cpu(memcons.desc->obuf_size));
/* Mambo might have left a crap one, clear it */
p = __dt_find_property(dt_chosen, "linux,stdout-path");
@@ -12,21 +12,41 @@
* facility or FSP.
*
* (This is v3 of the format, the previous one sucked)
+ *
+ * NB: This might be writable from the BMC! Don't blindly trust the contents.
*/
-struct memcons {
+struct memcons_desc {
__be64 magic;
#define MEMCONS_MAGIC 0x6630696567726173LL
__be64 obuf_phys;
__be64 ibuf_phys;
__be32 obuf_size;
__be32 ibuf_size;
+
+ /* Output ring buffer write head. OPAL is the producer */
__be32 out_pos;
#define MEMCONS_OUT_POS_WRAP 0x80000000u
#define MEMCONS_OUT_POS_MASK 0x00ffffffu
+
+ /* Input ring buffer offsets. OPAL is the consumer */
__be32 in_prod;
__be32 in_cons;
};
+/* internal version of the above, trustworthy, native endian, etc */
+struct memcons {
+ struct memcons_desc *desc;
+
+ char *obuf;
+ uint32_t obuf_size;
+
+ char *ibuf;
+ uint32_t ibuf_size;
+
+ uint32_t out_pos;
+ bool has_wrapped;
+};
+
extern struct memcons memcons;
#define INMEM_CON_IN_LEN 16
@@ -21,13 +21,13 @@ static void map_debug_areas(void)
/* Our memcons is in a section of its own and already
* aligned to 4K. The buffers are mapped as a whole
*/
- fsp_tce_map(PSI_DMA_MEMCONS, &memcons, 0x1000);
+ fsp_tce_map(PSI_DMA_MEMCONS, memcons.desc, 0x1000);
fsp_tce_map(PSI_DMA_LOG_BUF, (void*)INMEM_CON_START, INMEM_CON_LEN);
debug_descriptor.memcons_tce = cpu_to_be32(PSI_DMA_MEMCONS);
- t = be64_to_cpu(memcons.obuf_phys) - INMEM_CON_START + PSI_DMA_LOG_BUF;
+ t = be64_to_cpu(memcons.desc->obuf_phys) - INMEM_CON_START + PSI_DMA_LOG_BUF;
debug_descriptor.memcons_obuf_tce = cpu_to_be32(t);
- t = be64_to_cpu(memcons.ibuf_phys) - INMEM_CON_START + PSI_DMA_LOG_BUF;
+ t = be64_to_cpu(memcons.desc->ibuf_phys) - INMEM_CON_START + PSI_DMA_LOG_BUF;
debug_descriptor.memcons_ibuf_tce = cpu_to_be32(t);
t = PSI_DMA_TRACE_BASE;
@@ -177,7 +177,7 @@ static bool hbrt_con_wrapped;
#define HBRT_CON_IN_LEN 0
#define HBRT_CON_OUT_LEN (HBRT_CON_LEN - HBRT_CON_IN_LEN)
-static struct memcons hbrt_memcons __section(".data.memcons") = {
+static struct memcons_desc hbrt_memcons __section(".data.memcons") = {
.magic = CPU_TO_BE64(MEMCONS_MAGIC),
.obuf_phys = CPU_TO_BE64(HBRT_CON_START),
.ibuf_phys = CPU_TO_BE64(HBRT_CON_START + HBRT_CON_OUT_LEN),
This patch renames the existing memcons structure to memcons_desc and adds a new memcons structure that is used internally by skiboot. There's two main reasons for doing this: a) The memcons_desc may be DMA mapped as writable so the FSP/BMC can write to it to implement the "input" portion of the in-memory console. As a result we should not be blindling trusting the buffer pointers from the descriptor and we should validate the offsets are correct. b) Skiboot can be running little endian so we to handle the endian conversions which can get a bit unwieldly. Seperating the external desctriptor (which is ABI) and the internal console state helps to address both issues. We can rely on the internal state since it can never be tampered with externally, and we can use the internal state to regenerate the external descriptor as-needed. Signed-off-by: Oliver O'Halloran <oohall@gmail.com> --- core/console.c | 39 ++++++++++++++++++++++---------- include/console.h | 22 +++++++++++++++++- platforms/ibm-fsp/common.c | 6 ++--- platforms/ibm-fsp/hostservices.c | 2 +- 4 files changed, 52 insertions(+), 17 deletions(-)