// SPDX-License-Identifier: GPL-2.0 // // Microchip MCP251xFD Family CAN controller debug tool // // Copyright (c) 2020, 2021 Pengutronix, // Marc Kleine-Budde // #include #include #include #include #include #include #include #include #include #include "lib.h" #include "mcp251xfd.h" #include "mcp251xfd-dump-userspace.h" struct mcp251xfd_dump_iter { const void *start; const struct mcp251xfd_dump_object_header *hdr; const void *object_start; const void *object_end; }; const char * get_object_type_str(enum mcp251xfd_dump_object_type object_type) { switch (object_type) { case MCP251XFD_DUMP_OBJECT_TYPE_REG: return "REG"; case MCP251XFD_DUMP_OBJECT_TYPE_TEF: return "TEF"; case MCP251XFD_DUMP_OBJECT_TYPE_RX: return "RX"; case MCP251XFD_DUMP_OBJECT_TYPE_TX: return "TX"; case MCP251XFD_DUMP_OBJECT_TYPE_END: return "END"; default: return ""; } } static __attribute__((__unused__)) const char * get_ring_key_str(enum mcp251xfd_dump_object_ring_key key) { switch (key) { case MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD: return "head"; case MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL: return "tail"; case MCP251XFD_DUMP_OBJECT_RING_KEY_BASE: return "base"; case MCP251XFD_DUMP_OBJECT_RING_KEY_NR: return "nr"; case MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR: return "fifo-nr"; case MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM: return "obj-num"; case MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE: return "obj-size"; default: return ""; } } static int do_dev_coredump_read_reg(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_iter *iter, struct mcp251xfd_mem *mem) { const struct mcp251xfd_dump_object_reg *object; for (object = iter->object_start; (void *)(object + 1) <= iter->object_end; object++) { uint32_t reg, val; reg = le32toh(object->reg); val = le32toh(object->val); pr_debug("%s: offset=0x%04zx reg=0x%04x - val=0x%08x\n", __func__, (void *)object - iter->start, reg, val); if (reg > ARRAY_SIZE(mem->buf)) return -EINVAL; *(uint32_t *)(mem->buf + reg) = val; } return 0; } static int do_dev_coredump_read_ring(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_iter *iter, struct mcp251xfd_ring *ring) { const struct mcp251xfd_dump_object_reg *object; for (object = iter->object_start; (void *)(object + 1) <= iter->object_end; object++) { enum mcp251xfd_dump_object_ring_key key; uint32_t val; key = le32toh(object->reg); val = le32toh(object->val); pr_debug("%s: offset=0x%04zx key=0x%02x: %8s - val=0x%08x\n", __func__, (void *)object - iter->start, key, get_ring_key_str(key), val); switch (key) { case MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD: ring->head = val; break; case MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL: ring->tail = val; break; case MCP251XFD_DUMP_OBJECT_RING_KEY_BASE: ring->base = val; break; case MCP251XFD_DUMP_OBJECT_RING_KEY_NR: ring->nr = val; break; case MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR: ring->fifo_nr = val; break; case MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM: ring->obj_num = val; break; case MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE: ring->obj_size = val; break; default: continue; } } return 0; } static int do_dev_coredump_read(struct mcp251xfd_priv *priv, struct mcp251xfd_mem *mem, const void *dump, size_t dump_len) { struct mcp251xfd_dump_iter iter[] = { { .start = dump, .hdr = dump, }, }; while ((void *)(iter->hdr + 1) <= iter->start + dump_len && le32toh(iter->hdr->magic) == MCP251XFD_DUMP_MAGIC) { const struct mcp251xfd_dump_object_header *hdr = iter->hdr; enum mcp251xfd_dump_object_type object_type; struct mcp251xfd_ring ring; size_t object_offset, object_len; int err; object_type = le32toh(hdr->type); object_offset = le32toh(hdr->offset); object_len = le32toh(hdr->len); if (object_offset + object_len > dump_len) return -EFAULT; iter->object_start = iter->start + object_offset; iter->object_end = iter->object_start + object_len; mcp251xfd_dump_ring_init(&ring); pr_debug("%s: hdr=0x%04zx type=0x%08x: %8s - offset=0x%04zx len=0x%04zx end=0x%04zx\n", __func__, (void *)iter->hdr - iter->start, object_type, get_object_type_str(object_type), object_offset, object_len, object_offset + object_len); switch (object_type) { case MCP251XFD_DUMP_OBJECT_TYPE_REG: err = do_dev_coredump_read_reg(priv, iter, mem); break; case MCP251XFD_DUMP_OBJECT_TYPE_TEF: case MCP251XFD_DUMP_OBJECT_TYPE_RX: case MCP251XFD_DUMP_OBJECT_TYPE_TX: err = do_dev_coredump_read_ring(priv, iter, &ring); if (err) return err; if (ring.fifo_nr >= ARRAY_SIZE(priv->ring)) return -EINVAL; priv->ring[ring.fifo_nr] = ring; break; case MCP251XFD_DUMP_OBJECT_TYPE_END: return 0; default: return -EINVAL; } if (err) return err; iter->hdr++; } return -EINVAL; } int mcp251xfd_dev_coredump_read(struct mcp251xfd_priv *priv, struct mcp251xfd_mem *mem, const char *dump_path) { struct stat statbuf; size_t dump_len; void *dump; int fd, err; fd = open(dump_path, O_RDONLY); if (fd < 0) return -errno; err = fstat(fd, &statbuf); if (err < 0) { err = -errno; goto out_close; } dump_len = statbuf.st_size; dump = mmap(NULL, dump_len, PROT_READ, MAP_SHARED, fd, 0x0); if (dump == MAP_FAILED) { err = -errno; goto out_close; } err = do_dev_coredump_read(priv, mem, dump, dump_len); munmap(dump, dump_len); out_close: close(fd); return err; }