// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2003 * Tait Electronics Limited, Christchurch, New Zealand */ /* * This file provides a shell like 'test' function to return * true/false from an integer or string compare of two memory * locations or a location and a scalar/literal. * A few parts were lifted from bash 'test' command */ #include <common.h> #include <config.h> #include <command.h> #include <mapmem.h> #include <asm/io.h> #define EQ 0 #define NE 1 #define LT 2 #define GT 3 #define LE 4 #define GE 5 struct op_tbl_s { char *op; /* operator string */ int opcode; /* internal representation of opcode */ }; typedef struct op_tbl_s op_tbl_t; static const op_tbl_t op_table [] = { { "-lt", LT }, { "<" , LT }, { "-gt", GT }, { ">" , GT }, { "-eq", EQ }, { "==" , EQ }, { "-ne", NE }, { "!=" , NE }, { "<>" , NE }, { "-ge", GE }, { ">=" , GE }, { "-le", LE }, { "<=" , LE }, }; static long evalexp(char *s, int w) { long l = 0; unsigned long addr; void *buf; /* if the parameter starts with a * then assume is a pointer to the value we want */ if (s[0] == '*') { addr = simple_strtoul(&s[1], NULL, 16); buf = map_physmem(addr, w, MAP_WRBACK); if (!buf && addr) { puts("Failed to map physical memory\n"); return 0; } switch (w) { case 1: l = (long)(*(u8 *)buf); break; case 2: l = (long)(*(u16 *)buf); break; case 4: l = (long)(*(u32 *)buf); break; } unmap_physmem(buf, w); return l; } else { l = simple_strtoul(s, NULL, 16); } /* avoid overflow on mask calculus */ return (w >= sizeof(long)) ? l : (l & ((1UL << (w * 8)) - 1)); } static char * evalstr(char *s) { /* if the parameter starts with a * then assume a string pointer else its a literal */ if (s[0] == '*') { return (char *)simple_strtoul(&s[1], NULL, 16); } else if (s[0] == '$') { int i = 2; if (s[1] != '{') return NULL; while (s[i] != '}') { if (s[i] == 0) return NULL; i++; } s[i] = 0; return env_get((const char *)&s[2]); } else { return s; } } static int stringcomp(char *s, char *t, int op) { int p; char *l, *r; l = evalstr(s); r = evalstr(t); p = strcmp(l, r); switch (op) { case EQ: return (p == 0); case NE: return (p != 0); case LT: return (p < 0); case GT: return (p > 0); case LE: return (p <= 0); case GE: return (p >= 0); } return (0); } static int arithcomp (char *s, char *t, int op, int w) { long l, r; l = evalexp (s, w); r = evalexp (t, w); switch (op) { case EQ: return (l == r); case NE: return (l != r); case LT: return (l < r); case GT: return (l > r); case LE: return (l <= r); case GE: return (l >= r); } return (0); } static int binary_test(char *op, char *arg1, char *arg2, int w) { int len, i; const op_tbl_t *optp; len = strlen(op); for (optp = (op_tbl_t *)&op_table, i = 0; i < ARRAY_SIZE(op_table); optp++, i++) { if ((strncmp (op, optp->op, len) == 0) && (len == strlen (optp->op))) { if (w == 0) { return (stringcomp(arg1, arg2, optp->opcode)); } else { return (arithcomp (arg1, arg2, optp->opcode, w)); } } } printf("Unknown operator '%s'\n", op); return 0; /* op code not found */ } /* command line interface to the shell test */ static int do_itest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int value, w; /* Validate arguments */ if ((argc != 4)) return CMD_RET_USAGE; /* Check for a data width specification. * Defaults to long (4) if no specification. * Uses -2 as 'width' for .s (string) so as not to upset existing code */ switch (w = cmd_get_data_size(argv[0], 4)) { case 1: case 2: case 4: value = binary_test (argv[2], argv[1], argv[3], w); break; case -2: value = binary_test (argv[2], argv[1], argv[3], 0); break; case -1: default: puts("Invalid data width specifier\n"); value = 0; break; } return !value; } U_BOOT_CMD( itest, 4, 0, do_itest, "return true/false on integer compare", "[.b, .w, .l, .s] [*]value1 <op> [*]value2" );