From 1cf4e576d67fa4a92ad72da1d06ab19a684ee74c Mon Sep 17 00:00:00 2001 From: Rodrigo Arias Mallo Date: Wed, 2 Oct 2024 14:50:33 +0200 Subject: [PATCH] Add more operations to plictool --- tools/Makefile | 2 +- tools/plictool.c | 426 ++++++++++++++++++++++++++++------------------- 2 files changed, 258 insertions(+), 170 deletions(-) diff --git a/tools/Makefile b/tools/Makefile index c57de84..fd445cb 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,4 +1,4 @@ -CFLAGS=-static +CFLAGS=-Wall -static PREFIX?=/usr/local bin=plictool csrtool memtool unalign diff --git a/tools/plictool.c b/tools/plictool.c index 3b51124..e1caa1b 100644 --- a/tools/plictool.c +++ b/tools/plictool.c @@ -2,102 +2,58 @@ * SPDX-License-Identifier: MIT * Author: Rodrigo Arias Mallo */ -/* Small utility to dump the state of the PLIC. */ +/* Small utility to manage the PLIC. */ /* Changelog: * v0.0.1 (2024-09-03): Initial version. * v0.0.2 (2024-09-04): Print contexts in another line and masked information. * v0.0.3 (2024-09-04): Make output format more clear and add manual. * v0.0.4 (2024-09-30): Implement support for claiming an interrupt. + * v0.0.5 (2024-10-02): Support other read/write operations. */ -#define VERSION "v0.0.4" +#define VERSION "v0.0.5" #include #include #include +#include #include #include #include #include #include -enum op { LIST, CLAIM } operation = LIST; +int operation; const char *plic_address_str = "0x40800000"; long ncontexts = 2L; long maxsources = 1024L; long nsources = 1024L; -long claim_ctx = 0; -uint32_t claim_value = 0; -uint32_t *claim_pvalue = NULL; + +long context = -1; +long source = -1; +long value = -1; +bool value_set = NULL; + +struct ctx { + uint32_t threshold; +}; struct source_ctx { - unsigned enabled; - unsigned threshold; - unsigned masked; + bool enabled; + bool masked; + uint32_t threshold; + const char *state; }; struct source { + bool pending; + bool show; + uint32_t priority; + struct source_ctx *ctx; + long ncontexts; }; -static void -usage(void) -{ - printf( -"NAME\n" -" plictool - dump PLIC information\n" -"\n" -"SYNOPSIS\n" -" plictool [-a addr] [-L] [-s nsources] [-c ncontexts]\n" -" plictool [-a addr] [-C] [-c context] [-w value]\n" -"\n" -"DESCRIPTION\n" -" Dumps information about the RISC-V PLIC interrupt controller.\n" -" Optional arguments:\n" -"\n" -" -L Show PLIC information (default operation).\n" -"\n" -" -C context Claim an interrupt on the given context.\n" -"\n" -" -a addr The starting address of the PLIC.\n" -" The default is 0x40800000.\n" -"\n" -" -s nsources Limit the number of sources to print.\n" -" The default is 1024.\n" -"\n" -" -c ncontexts Limit the number of contexts to print.\n" -" The default is 15872.\n" -"\n" -" -C ctx Claim the interrupt on the given context.\n" -"\n" -" -w value Use this value to claim an interrupt.\n" -"\n" -"OUTPUT FORMAT\n" -" Information is printed for each source that has some bit set\n" -" in the pending or priority registers, or it has some context\n" -" enabled. The the following attributes are printed:\n" -"\n" -" src source number\n" -" pend pending bit\n" -" prio priority of the source\n" -"\n" -" Additionally, for each enabled context of the source the\n" -" following attributes are printed for that context:\n" -"\n" -" ctx context number\n" -" thre threshold value\n" -" masked the source is masked (prio <= thre)\n" -"\n" -"AUTHOR\n" -" Rodrigo Arias Mallo \n" -"\n" -"SEE ALSO\n" -" See https://github.com/riscv/riscv-plic-spec/\n" -"\n" -); - exit(1); -} - static uint32_t read_reg(void *base, size_t offset) { @@ -112,14 +68,89 @@ write_reg(void *base, size_t offset, uint32_t value) *p = value; } -static void -claim_interrupt(void *base, uint32_t ctx, uint32_t *value) +uint32_t +claim_get(void *base, uint32_t ctx) { - size_t offset = 0x200004L + ctx * 0x1000; - uint32_t r = read_reg(base, offset); - uint32_t w = value ? *value : r; - write_reg(base, offset, w); - printf("claim ctx=%u r=%u w=%u\n", ctx, r, w); + return read_reg(base, 0x200004L + ctx * 0x1000); +} + +static void +claim_set(void *base, uint32_t ctx, uint32_t value) +{ + write_reg(base, 0x200004L + ctx * 0x1000, value); +} + +uint32_t +thre_get(void *base, uint32_t ctx) +{ + return read_reg(base, 0x200000L + (ctx * 0x1000L)); +} + +static void +thre_set(void *base, uint32_t ctx, uint32_t value) +{ + write_reg(base, 0x200000L + (ctx * 0x1000L), value); +} + +uint32_t +prio_get(void *base, uint32_t s) +{ + return read_reg(base, s * 4L); +} + +static void +prio_set(void *base, uint32_t s, uint32_t value) +{ + write_reg(base, s * 4L, value); +} + +uint32_t +pending_get(void *base, uint32_t s) +{ + uint32_t offset = 0x1000L + (s / 32L) * 4L; + uint32_t pending = read_reg(base, offset); + long shift = s % 32L; + + return (pending >> shift) & 1; +} + +static void +pending_set(void *base, uint32_t s, uint32_t value) +{ + uint32_t offset = 0x1000L + (s / 32L) * 4L; + uint32_t pending = read_reg(base, offset); + long shift = s % 32L; + + if (value) + pending |= (1L << shift); + else + pending &= ~(1L << shift); + + write_reg(base, offset, pending); +} + +uint32_t +enable_get(void *base, uint32_t c, uint32_t s) +{ + size_t off_en = 0x2000L + 0x80L * c + (s / 32L) * 4L; + uint32_t enabled_reg = read_reg(base, off_en); + long shift = s % 32L; + return (enabled_reg >> shift) & 1; +} + +static void +enable_set(void *base, uint32_t c, uint32_t s, uint32_t value) +{ + size_t off_en = 0x2000L + 0x80L * c + (s / 32L) * 4L; + uint32_t enabled_reg = read_reg(base, off_en); + long shift = s % 32L; + + if (value) + enabled_reg |= (1L << shift); + else + enabled_reg &= ~(1L << shift); + + write_reg(base, off_en, enabled_reg); } static void @@ -127,6 +158,7 @@ source_init(struct source *src, long ncontexts) { memset(src, 0, sizeof(struct source)); src->ctx = calloc(ncontexts, sizeof(struct source_ctx)); + src->ncontexts = ncontexts; if (src->ctx == NULL) { perror("calloc failed"); exit(1); @@ -134,95 +166,98 @@ source_init(struct source *src, long ncontexts) } static void -source_reset(struct source *src, long ncontexts) +source_reset(struct source *src) { - memset(src, 0, sizeof(struct source)); - memset(src->ctx, 0, ncontexts * sizeof(struct source_ctx)); + src->pending = false; + src->show = false; + src->priority = 0; + + memset(src->ctx, 0, src->ncontexts * sizeof(struct source_ctx)); } static void -source_free(struct source *src, long ncontexts) +source_free(struct source *src) { free(src->ctx); } static void -source_read(struct source *src, void *base, long s, long ncontexts) +source_read(struct source *src, void *base, long s) { - uint32_t pending_reg = read_reg(base, 0x1000L + (s / 32L) * 4L) + uint32_t pending_reg = read_reg(base, 0x1000L + (s / 32L) * 4L); long shift = s % 32L; src->pending = (pending_reg >> shift) & 1; src->priority = read_reg(base, 0x0000L + (s * 4L)); - for (long c = 0; c < ncontexts; c++) { + bool ctx_show = 0; + for (long c = 0; c < src->ncontexts; c++) { struct source_ctx *ctx = &src->ctx[c]; size_t off_en = 0x2000L + 0x80L * c + (s / 32L) * 4L; - uint32_t enabled_reg = reg_read(base, off_en); - ctx->enabled = (enable_reg >> shift) & 1; + uint32_t enabled_reg = read_reg(base, off_en); + ctx->enabled = (enabled_reg >> shift) & 1; ctx->threshold = read_reg(base, 0x200000L + (c * 0x1000L)); - ctx->masked = priority <= threshold; + ctx->masked = src->priority <= ctx->threshold; + ctx_show = ctx_show || ctx->enabled; + + if (!ctx->enabled) + ctx->state = "-"; + else if (ctx->masked) + ctx->state = "masked"; + else + ctx->state = "firing"; } + + /* Show the source if it has some bit to non-zero */ + src->show = src->pending || src->priority || ctx_show; } static void list_sources(void *base) { - struct source src; - - source_init(&src, ncontexts); - for (long s = 0; s < nsources; s++) { - source_read(&src, base, s, ncontexts); - int printed_source = 0; - - uint32_t *pending_reg = base + 0x1000L + (s / 32L) * 4L; - long shift = s % 32L; - uint32_t pending = ((*pending_reg) >> shift) & 1; - uint32_t *priority_reg = base + 0x0000L + (s * 4L); - uint32_t priority = *priority_reg; - - const char *fmt = - "src=%ld pend=%u prio=%u"; - - if (pending || priority) { - printf(fmt, s, pending, priority); - printed_source = 1; - } - - int first_context = 1; - int enabled_contexts = 0; - int unmasked = 0; - for (long c = 0; c < ncontexts; c++) { - uint32_t *enable_reg = base + 0x2000L + 0x80L * c + (s / 32L) * 4L; - uint32_t enabled = ((*enable_reg) >> shift) & 1; - uint32_t *threshold_reg = base + 0x200000L + (c * 0x1000L); - uint32_t threshold = *threshold_reg; - - if (!enabled) - continue; - - if (!printed_source) { - printf(fmt, s, pending, priority); - printed_source = 1; - } - - - int is_masked = priority <= threshold; - - if (!is_masked) - unmasked++; - - const char *masked = is_masked ? "masked" : "unmasked"; - - printf("\n ctx=%ld thre=%u %s", - c, threshold, masked); - enabled_contexts++; - } - - if (printed_source) - printf("\n"); + printf("Source\tPend\tPrio"); + for (long i = 0; i < ncontexts; i++) { + uint32_t threshold = read_reg(base, 0x200000L + (i * 0x1000L)); + printf("\tC%ld(%u)", i, threshold); } + printf("\n"); + + struct source s; + source_init(&s, ncontexts); + for (long i = 0; i < nsources; i++) { + source_reset(&s); + source_read(&s, base, i); + + if (!s.show) + continue; + + printf("%ld\t%s\t%u", i, s.pending ? "yes" : "-", s.priority); + + for (long j = 0; j < ncontexts; j++) + printf("\t%s", s.ctx[j].state); + + printf("\n"); + } + + source_free(&s); +} + +static void usage(void) +{ + printf("plictool "VERSION" -- Rodrigo Arias Mallo \n"); + + fprintf(stderr, +"Usage:\n" +" plictool [-a addr] [-L] [-n nsrc] [-x nctx] # List (default)\n" +" plictool [-a addr] -C ctx [-w value] # Claim\n" +" plictool [-a addr] -T ctx [-w value] # Threshold\n" +" plictool [-a addr] -I src [-w value] # Priority\n" +" plictool [-a addr] -P src [-w value] # Pending\n" +" plictool [-a addr] -E src -c ctx [-w value] # Enabled\n" +" plictool -v # Version\n" +); + exit(1); } int main(int argc, char *argv[]) @@ -230,41 +265,70 @@ int main(int argc, char *argv[]) const char *memfile = "/dev/mem"; int opt; - while ((opt = getopt(argc, argv, "f:a:hLs:c:C:w:")) != -1) { + while ((opt = getopt(argc, argv, "f:a:LC:T:P:I:E:n:x:c:w:vh")) != -1) { switch (opt) { - case 'f': - memfile = optarg; - break; - case 'a': - plic_address_str = optarg; - break; - case 'L': /* default operation */ - operation = LIST; - break; - case 's': - nsources = atol(optarg); - break; - case 'c': - ncontexts = atol(optarg); - break; - case 'C': - operation = CLAIM; - claim_ctx = atol(optarg); - break; - case 'w': - claim_value = atol(optarg); - claim_pvalue = &claim_value; - break; - case 'h': - default: /* '?' */ - usage(); + /* Common flags */ + case 'f': + memfile = optarg; + break; + case 'a': + plic_address_str = optarg; + break; + case 'n': + nsources = atol(optarg); + break; + case 'x': + ncontexts = atol(optarg); + break; + case 'C': /* claim */ + case 'T': /* threshold */ + operation = opt; + context = atol(optarg); + break; + case 'P': /* pending */ + case 'I': /* priority */ + case 'E': /* enable */ + operation = opt; + source = atol(optarg); + break; + case 'L': /* list */ + operation = opt; + break; + case 'c': + context = atol(optarg); + break; + case 'w': + value = atol(optarg); + value_set = true; + break; + case 'v': + printf("plictool "VERSION"\n"); + exit(0); + case 'h': + default: /* '?' */ + usage(); + break; + } + } + + if (operation == 'P' || operation == 'I' || operation == 'E') { + if (source < 0) { + fprintf(stderr, "missing source\n"); + exit(1); + } + } + + if (operation == 'C' || operation == 'T' || operation == 'E') { + if (context < 0) { + fprintf(stderr, "missing context\n"); + exit(1); } } unsigned long long plic_address = strtoull(plic_address_str, NULL, 16); - printf("plictool "VERSION" addr=0x%08llx nsrc=%ld nctx=%ld\n", - plic_address, nsources, ncontexts); + //printf("plictool "VERSION" addr=0x%08llx nsrc=%ld nctx=%ld\n", + // plic_address, nsources, ncontexts); int fd = open(memfile, O_RDWR | O_SYNC); @@ -287,10 +351,34 @@ int main(int argc, char *argv[]) exit(1); } - if (operation == CLAIM) - claim_interrupt(map_base, claim_ctx, claim_pvalue); - else if (operation == LIST) + if (operation == 'C') { /* claim */ + if (value_set) + claim_set(map_base, context, value); + else + printf("%u\n", claim_get(map_base, context)); + } else if (operation == 'T') { /* threshold */ + if (value_set) + thre_set(map_base, context, value); + else + printf("%u\n", thre_get(map_base, context)); + } else if (operation == 'I') { /* priority */ + if (value_set) + prio_set(map_base, source, value); + else + printf("%u\n", prio_get(map_base, source)); + } else if (operation == 'P') { /* pending */ + if (value_set) + pending_set(map_base, source, value); + else + printf("%u\n", pending_get(map_base, source)); + } else if (operation == 'E') { /* enable */ + if (value_set) + enable_set(map_base, context, source, value); + else + printf("%u\n", enable_get(map_base, context, source)); + } else /* list */ { list_sources(map_base); + } munmap(map_base, map_size);