@@ -162,6 +162,8 @@ int64_t opal_entry_check(struct stack_frame *eframe)
case OPAL_CEC_REBOOT:
case OPAL_CEC_REBOOT2:
case OPAL_SIGNAL_SYSTEM_RESET:
+ case OPAL_ADDR_TO_SYM:
+ case OPAL_SYM_TO_ADDR:
break;
default:
printf("CPU ATTEMPT TO RE-ENTER FIRMWARE! PIR=%04lx cpu @%p -> pir=%04x token=%llu\n",
@@ -52,40 +52,128 @@ char __attrconst tohex(uint8_t nibble)
return __tohex[nibble];
}
-static unsigned long get_symbol(unsigned long addr, char **sym, char **sym_end)
+static unsigned long addr_to_sym(unsigned long addr, char **sym, char **sym_end, unsigned long *size)
{
- unsigned long prev = 0, next;
+ unsigned long prev = 0, next = 0;
char *psym = NULL, *p = __sym_map_start;
*sym = *sym_end = NULL;
- while(p < __sym_map_end) {
+
+ while (p < __sym_map_end) {
next = strtoul(p, &p, 16) | SKIBOOT_BASE;
if (next > addr && prev <= addr) {
- p = psym + 3;;
+ if (!psym)
+ return 0;
+ p = psym + 3;
if (p >= __sym_map_end)
return 0;
*sym = p;
- while(p < __sym_map_end && *p != 10)
+ while (p < __sym_map_end && *p != '\n')
p++;
*sym_end = p;
+ *size = next - prev;
+
return prev;
}
+
prev = next;
psym = p;
- while(p < __sym_map_end && *p != 10)
+ while (p < __sym_map_end && *p != '\n')
+ p++;
+ p++;
+ }
+
+ return 0;
+}
+
+static unsigned long sym_to_addr(const char *name, unsigned long *size)
+{
+ size_t len = strlen(name);
+ unsigned long addr = 0;
+ char *sym;
+ char *p = __sym_map_start;
+
+ while (p < __sym_map_end) {
+ addr = strtoul(p, &p, 16) | SKIBOOT_BASE;
+ p += 3;
+ if (p >= __sym_map_end)
+ return 0;
+
+ if (*(p + len) == '\n' && !strncmp(name, p, len)) {
+ char *sym_end;
+
+ /* Now get the size */
+ if (addr_to_sym(addr, &sym, &sym_end, size) == 0) {
+ if (strcmp(name, "_end"))
+ printf("sym_to_addr: name=%s p=%p addr=%lx addr_to_sym failed\n", name, p, addr);
+ *size = 0;
+ }
+
+ /*
+ * May be more than one symbol at this address but
+ * symbol length calculation should still work in
+ * that case.
+ */
+
+ return addr;
+ }
+
+ while (p < __sym_map_end && *p != '\n')
p++;
p++;
}
return 0;
}
+static int64_t opal_addr_to_sym(uint64_t addr, __be64 *symaddr, __be64 *symsize, char *namebuf, uint64_t buflen)
+{
+ unsigned long saddr;
+ unsigned long ssize;
+ char *sym, *sym_end;
+ size_t l;
+
+ saddr = addr_to_sym(addr, &sym, &sym_end, &ssize);
+ if (!saddr)
+ return OPAL_RESOURCE;
+
+ if (buflen > sym_end - sym)
+ l = sym_end - sym;
+ else
+ l = buflen - 1;
+ memcpy(namebuf, sym, l);
+ namebuf[l] = '\0';
+
+ *symaddr = cpu_to_be64(saddr);
+ *symsize = cpu_to_be64(ssize);
+
+ return OPAL_SUCCESS;
+}
+opal_call(OPAL_ADDR_TO_SYM, opal_addr_to_sym, 5);
+
+static int64_t opal_sym_to_addr(const char *name, __be64 *symaddr, __be64 *symsize)
+{
+ unsigned long saddr;
+ unsigned long ssize;
+
+ saddr = sym_to_addr(name, &ssize);
+ if (!saddr)
+ return OPAL_RESOURCE;
+
+ *symaddr = cpu_to_be64(saddr);
+ *symsize = cpu_to_be64(ssize);
+
+ return OPAL_SUCCESS;
+}
+opal_call(OPAL_SYM_TO_ADDR, opal_sym_to_addr, 3);
+
size_t snprintf_symbol(char *buf, size_t len, uint64_t addr)
{
unsigned long saddr;
+ unsigned long ssize;
char *sym, *sym_end;
size_t l;
- saddr = get_symbol(addr, &sym, &sym_end);
+ saddr = addr_to_sym(addr, &sym, &sym_end, &ssize);
if (!saddr)
return 0;
new file mode 100644
@@ -0,0 +1,42 @@
+.. _OPAL_ADDR_TO_SYM:
+
+OPAL_ADDR_TO_SYM
+================
+
+.. code-block:: c
+
+ #define OPAL_ADDR_TO_SYM 181
+
+ static int64_t opal_addr_to_sym(uint64_t addr, __be64 *symaddr, __be64 *symsize, char *namebuf, uint64_t buflen);
+
+This OPAL call looks up a firmware code address for symbol information.
+
+Arguments
+---------
+
+ ``addr``
+ Contains address to be looked up.
+
+ ``symaddr``
+ Returns the start address of the symbol for the object which
+ contains addr or immediately precedes addr.
+
+ ``symsize``
+ Returns the size of the object, or the number of bytes until the
+ next symbol.
+
+ ``namebuf``
+ Contains a buffer for the symbol name to be copied into, as a NUL
+ terminated string.
+
+ ``buflen``
+ Contains the length of the bufer that may be used.
+
+
+Returns
+-------
+
+:ref:`OPAL_SUCCESS`
+ Found a symbol.
+:ref:`OPAL_RESOURCE`
+ Did not find a symbol.
new file mode 100644
@@ -0,0 +1,35 @@
+.. _OPAL_SYM_TO_ADDR:
+
+OPAL_SYM_TO_ADDR
+================
+
+.. code-block:: c
+
+ #define OPAL_SYM_TO_ADDR 182
+
+ static int64_t opal_sym_to_addr(const char *name, __be64 *symaddr, __be64 *symsize);
+
+This OPAL call looks up a firmware symbol name for its address.
+
+Arguments
+---------
+
+ ``name``
+ Contains a pointer to NUL terminated symbol name to be looked up.
+
+ ``symaddr``
+ Returns the start address of the symbol for the object which
+ contains addr or immediately precedes addr.
+
+ ``symsize``
+ Returns the size of the object, or the number of bytes until the
+ next symbol.
+
+
+Returns
+-------
+
+:ref:`OPAL_SUCCESS`
+ Found a symbol.
+:ref:`OPAL_RESOURCE`
+ Did not find a symbol.
@@ -227,7 +227,9 @@
#define OPAL_SECVAR_ENQUEUE_UPDATE 178
#define OPAL_PHB_SET_OPTION 179
#define OPAL_PHB_GET_OPTION 180
-#define OPAL_LAST 180
+#define OPAL_ADDR_TO_SYM 181
+#define OPAL_SYM_TO_ADDR 182
+#define OPAL_LAST 182
#define QUIESCE_HOLD 1 /* Spin all calls at entry */
#define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */
These calls can be used by Linux to annotate BUG addresses with symbols, look up symbol addresses in xmon, etc. This is preferable over having Linux parse the OPAL symbol map itself, because OPAL's parsing code already exists for its own symbol printing, and it can support other code regions than the skiboot symbols, e.g., the wake-up code in the HOMER (where CPUs have been seen to get stuck). Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- core/opal.c | 2 + core/utils.c | 102 ++++++++++++++++++++++-- doc/opal-api/opal-get-symbol-181.rst | 42 ++++++++++ doc/opal-api/opal-lookup-symbol-182.rst | 35 ++++++++ include/opal-api.h | 4 +- 5 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 doc/opal-api/opal-get-symbol-181.rst create mode 100644 doc/opal-api/opal-lookup-symbol-182.rst