LCOV - code coverage report
Current view: top level - external/gard - gard.c (source / functions) Hit Total Coverage
Test: skiboot.info Lines: 329 464 70.9 %
Date: 2024-01-02 21:04:04 Functions: 23 24 95.8 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  * Manipulate GARD records in the GARD partition
       4                 :            :  *
       5                 :            :  * Copyright 2013-2019 IBM Corp.
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <fcntl.h>
       9                 :            : #include <stdio.h>
      10                 :            : #include <stdlib.h>
      11                 :            : #include <errno.h>
      12                 :            : #include <string.h>
      13                 :            : #include <unistd.h>
      14                 :            : #include <sys/mman.h>
      15                 :            : #include <sys/stat.h>
      16                 :            : #include <dirent.h>
      17                 :            : #include <limits.h>
      18                 :            : #include <inttypes.h>
      19                 :            : #include <ctype.h>
      20                 :            : 
      21                 :            : #include <ccan/array_size/array_size.h>
      22                 :            : 
      23                 :            : #include <mtd/mtd-abi.h>
      24                 :            : 
      25                 :            : #include <getopt.h>
      26                 :            : 
      27                 :            : #include <libflash/libflash.h>
      28                 :            : #include <libflash/libffs.h>
      29                 :            : #include <libflash/file.h>
      30                 :            : #include <libflash/blocklevel.h>
      31                 :            : #include <common/arch_flash.h>
      32                 :            : 
      33                 :            : #include "gard.h"
      34                 :            : 
      35                 :            : #define FDT_PATH "/proc/device-tree"
      36                 :            : #define FDT_FSP_NODE FDT_PATH"/fsps"
      37                 :            : #define FDT_ACTIVE_FLASH_PATH FDT_PATH"/chosen/ibm,system-flash"
      38                 :            : #define SYSFS_MTD_PATH "/sys/class/mtd/"
      39                 :            : #define FLASH_GARD_PART "GUARD"
      40                 :            : 
      41                 :            : #define VPNOR_GARD_DIR "/media/pnor-prsv"
      42                 :            : #define VPNOR_GARD_FILE VPNOR_GARD_DIR"/GUARD"
      43                 :            : 
      44                 :            : /* Full gard version number (possibly includes gitid). */
      45                 :            : extern const char version[];
      46                 :            : 
      47                 :            : 
      48                 :            : #define __unused __attribute__((unused))
      49                 :            : 
      50                 :            : struct gard_ctx {
      51                 :            :         uint32_t f_size;
      52                 :            :         uint32_t f_pos;
      53                 :            : 
      54                 :            :         uint32_t gard_part_idx;
      55                 :            :         uint32_t gard_data_pos;
      56                 :            :         uint32_t gard_data_len;
      57                 :            : 
      58                 :            :         struct blocklevel_device *bl;
      59                 :            :         struct ffs_handle *ffs;
      60                 :            : };
      61                 :            : 
      62                 :          0 : static void show_flash_err(int rc)
      63                 :            : {
      64                 :          0 :         switch (rc) {
      65                 :          0 :                 case FFS_ERR_BAD_MAGIC:
      66                 :          0 :                         fprintf(stderr, "libffs bad magic\n");
      67                 :            :                         break;
      68                 :          0 :                 case FFS_ERR_BAD_VERSION:
      69                 :          0 :                         fprintf(stderr, "libffs bad version\n");
      70                 :            :                         break;
      71                 :          0 :                 case FFS_ERR_BAD_CKSUM:
      72                 :          0 :                         fprintf(stderr, "libffs bad check sum\n");
      73                 :            :                         break;
      74                 :          0 :                 case FFS_ERR_PART_NOT_FOUND:
      75                 :          0 :                         fprintf(stderr, "libffs flash partition not found\n");
      76                 :            :                         break;
      77                 :            :                         /* ------- */
      78                 :          0 :                 case FLASH_ERR_MALLOC_FAILED:
      79                 :          0 :                         fprintf(stderr, "libflash malloc failed\n");
      80                 :            :                         break;
      81                 :          0 :                 case FLASH_ERR_CHIP_UNKNOWN:
      82                 :          0 :                         fprintf(stderr, "libflash unknown flash chip\n");
      83                 :            :                         break;
      84                 :          0 :                 case FLASH_ERR_PARM_ERROR:
      85                 :          0 :                         fprintf(stderr, "libflash parameter error\n");
      86                 :            :                         break;
      87                 :          0 :                 case FLASH_ERR_ERASE_BOUNDARY:
      88                 :          0 :                         fprintf(stderr, "libflash erase boundary error\n");
      89                 :            :                         break;
      90                 :          0 :                 case FLASH_ERR_WREN_TIMEOUT:
      91                 :          0 :                         fprintf(stderr, "libflash WREN timeout\n");
      92                 :            :                         break;
      93                 :          0 :                 case FLASH_ERR_WIP_TIMEOUT:
      94                 :          0 :                         fprintf(stderr, "libflash WIP timeout\n");
      95                 :            :                         break;
      96                 :          0 :                 case FLASH_ERR_VERIFY_FAILURE:
      97                 :          0 :                         fprintf(stderr, "libflash verification failure\n");
      98                 :            :                         break;
      99                 :          0 :                 case FLASH_ERR_4B_NOT_SUPPORTED:
     100                 :          0 :                         fprintf(stderr, "libflash 4byte mode not supported\n");
     101                 :            :                         break;
     102                 :          0 :                 case FLASH_ERR_CTRL_CONFIG_MISMATCH:
     103                 :          0 :                         fprintf(stderr, "libflash control config mismatch\n");
     104                 :            :                         break;
     105                 :          0 :                 case FLASH_ERR_CHIP_ER_NOT_SUPPORTED:
     106                 :          0 :                         fprintf(stderr, "libflash chip not supported\n");
     107                 :            :                         break;
     108                 :          0 :                 case FLASH_ERR_CTRL_CMD_UNSUPPORTED:
     109                 :          0 :                         fprintf(stderr, "libflash unsupported control command\n");
     110                 :            :                         break;
     111                 :          0 :                 case FLASH_ERR_CTRL_TIMEOUT:
     112                 :          0 :                         fprintf(stderr, "libflash control timeout\n");
     113                 :            :                         break;
     114                 :          0 :                 case FLASH_ERR_ECC_INVALID:
     115                 :          0 :                         fprintf(stderr, "libflash ecc invalid\n");
     116                 :            :                         break;
     117                 :          0 :                 default:
     118                 :          0 :                         fprintf(stderr, "A libflash/libffs error has occurred %d\n", rc);
     119                 :            :         }
     120                 :          0 : }
     121                 :            : 
     122                 :            : const struct chip_unit_desc *chip_units;
     123                 :            : int chip_unit_count;
     124                 :            : 
     125                 :         46 : static void set_chip_gen(const struct chip_unit_desc *c)
     126                 :            : {
     127                 :         46 :         chip_units = c;
     128                 :         46 :         chip_unit_count = 0;
     129                 :            : 
     130                 :       3258 :         while (strcmp("LAST_IN_RANGE", c->desc)) {
     131                 :       3212 :                 chip_unit_count++;
     132                 :       3212 :                 c++;
     133                 :            :         }
     134                 :         46 : }
     135                 :            : 
     136                 :            : #ifdef __powerpc64__
     137                 :            : static void guess_chip_gen(void)
     138                 :            : {
     139                 :            :         /*
     140                 :            :          * Guesstimate what chip generation based on the PVR if we're running
     141                 :            :          * on ppc64.
     142                 :            :          */
     143                 :            :         uint32_t pvr;
     144                 :            : 
     145                 :            :         /* grab the chip type from the PVR SPR */
     146                 :            :         asm ("mfspr  %0,0x11f" : "=r" (pvr));
     147                 :            : 
     148                 :            :         switch (pvr >> 16) {
     149                 :            :         case 0x004b: /* murano */
     150                 :            :         case 0x004c: /* naples */
     151                 :            :         case 0x004d: /* venice */
     152                 :            :                 set_chip_gen(p8_chip_units);
     153                 :            :                 return;
     154                 :            : 
     155                 :            :         case 0x004e: /* nimbus */
     156                 :            :         case 0x004f: /* axone */
     157                 :            :                 set_chip_gen(p9_chip_units);
     158                 :            :                 return;
     159                 :            : 
     160                 :            :         case 0x0080: /* power10 */
     161                 :            :                 set_chip_gen(p10_chip_units);
     162                 :            :                 return;
     163                 :            : 
     164                 :            :         default:
     165                 :            :                 fprintf(stderr, "Unsupported processor (pvr %#x)! Set the processor generation manually with -8, -9 or -0\n", pvr);
     166                 :            :                 exit(1);
     167                 :            :         }
     168                 :            : }
     169                 :            : #else
     170                 :         30 : static void guess_chip_gen(void)
     171                 :            : {
     172                 :            : #ifdef ASSUME_P8
     173                 :            :         set_chip_gen(p8_chip_units);
     174                 :            : #else
     175                 :         30 :         set_chip_gen(p9_chip_units);
     176                 :            : #endif
     177                 :         30 : }
     178                 :            : #endif
     179                 :            : 
     180                 :        194 : static const char *target_type_to_str(int type)
     181                 :            : {
     182                 :        194 :         int i;
     183                 :            : 
     184                 :       4658 :         for (i = 0; i < chip_unit_count; i++)
     185                 :       4658 :                 if (chip_units[i].type == type)
     186                 :        194 :                         return chip_units[i].desc;
     187                 :            : 
     188                 :            :         return "UNKNOWN";
     189                 :            : }
     190                 :            : 
     191                 :         72 : static int str_to_target_type(const char *path)
     192                 :            : {
     193                 :         72 :         int i, len;
     194                 :            : 
     195                 :       1826 :         for (i = 0; i < chip_unit_count; i++) {
     196                 :       1822 :                 len = strlen(chip_units[i].desc);
     197                 :            : 
     198                 :       1822 :                 if (!strncasecmp(chip_units[i].desc, path, len))
     199                 :         68 :                         return chip_units[i].type; /* match! */
     200                 :            :         }
     201                 :            : 
     202                 :            :         return -1;
     203                 :            : }
     204                 :            : 
     205                 :         18 : static const char *deconfig_reason_str(enum gard_reason reason)
     206                 :            : {
     207                 :         18 :         switch (reason) {
     208                 :            :         case GARD_NO_REASON:
     209                 :            :                 return "None";
     210                 :         10 :         case GARD_MANUAL:
     211                 :         10 :                 return "Manual";
     212                 :          0 :         case GARD_UNRECOVERABLE:
     213                 :          0 :                 return "Unrecoverable";
     214                 :          2 :         case GARD_FATAL:
     215                 :          2 :                 return "Fatal";
     216                 :          6 :         case GARD_PREDICTIVE:
     217                 :          6 :                 return "Predictive";
     218                 :          0 :         case GARD_POWER:
     219                 :          0 :                 return "Power"; // What does this even mean?
     220                 :          0 :         case GARD_HYP:
     221                 :          0 :                 return "Hypervisor";
     222                 :          0 :         case GARD_RECONFIG:
     223                 :          0 :                 return "Reconfig";
     224                 :          0 :         default:
     225                 :          0 :                 return "Unknown";
     226                 :            :         }
     227                 :            : };
     228                 :            : 
     229                 :          4 : static const char *path_type_to_str(enum path_type t)
     230                 :            : {
     231                 :          4 :         switch (t) {
     232                 :            :                 case PATH_NA:
     233                 :            :                         return "not applicable";
     234                 :          0 :                 case PATH_AFFINITY:
     235                 :          0 :                         return "affinity";
     236                 :          4 :                 case PATH_PHYSICAL:
     237                 :          4 :                         return "physical";
     238                 :          0 :                 case PATH_DEVICE:
     239                 :          0 :                         return "device";
     240                 :          0 :                 case PATH_POWER:
     241                 :          0 :                         return "power";
     242                 :            :         }
     243                 :          0 :         return "Unknown";
     244                 :            : }
     245                 :            : 
     246                 :            : /*
     247                 :            :  * NB: buffer is assumped to be MAX_PATH_SIZE
     248                 :            :  */
     249                 :         28 : static char *format_path(struct entity_path *path, char *buffer)
     250                 :            : {
     251                 :         28 :         int elements = path->type_size & PATH_ELEMENTS_MASK;
     252                 :         28 :         int i, offset = 0;
     253                 :            : 
     254                 :        140 :         for (i = 0; i < elements; i++) {
     255                 :        112 :                 const struct path_element *e = &path->path_elements[i];
     256                 :            : 
     257                 :        112 :                 offset += sprintf(buffer + offset, "/%s%d",
     258                 :        112 :                         target_type_to_str(e->target_type),
     259                 :        112 :                         e->instance);
     260                 :            :         }
     261                 :            : 
     262                 :         28 :         return buffer;
     263                 :            : }
     264                 :            : 
     265                 :            : /*
     266                 :            :  * parses a Path string into the entity_path structured provided.
     267                 :            :  *
     268                 :            :  * str    - In param, String to parse
     269                 :            :  * parsed - Out param, resultant entity_path
     270                 :            :  *
     271                 :            :  * e.g.
     272                 :            :  *
     273                 :            :  * "/Sys0/Node0/Proc1" -> {
     274                 :            :  *      type_size = 0x23,
     275                 :            :  *
     276                 :            :  *      path_element[0] = {0, 0}
     277                 :            :  *      path_element[1] = {1, 0}
     278                 :            :  *      path_element[2] = {2, 1}
     279                 :            :  * }
     280                 :            :  */
     281                 :         18 : static int parse_path(const char *str, struct entity_path *parsed)
     282                 :            : {
     283                 :         18 :         int unit_count = 0;
     284                 :            : 
     285                 :         18 :         memset(parsed, 0, sizeof(*parsed));
     286                 :            : 
     287                 :         82 :         while (*str != '\0') {
     288                 :         72 :                 int unit_id = str_to_target_type(++str); /* ++ skips the '/' */
     289                 :         72 :                 long instance;
     290                 :         72 :                 char *end;
     291                 :         72 :                 size_t len;
     292                 :            : 
     293                 :         72 :                 if (unit_count > MAX_PATH_ELEMENTS - 1) {
     294                 :          2 :                         fprintf(stderr, "Path has more than 10 components!\n");
     295                 :          8 :                         return -1;
     296                 :            :                 }
     297                 :            : 
     298                 :            :                 /* find the type Id of this component */
     299                 :         70 :                 if (unit_id < 0) { /* unknown unit, bail out */
     300                 :          4 :                         fprintf(stderr, "Unknown unit at: '%s'\n", str);
     301                 :          4 :                         return -1;
     302                 :            :                 }
     303                 :            : 
     304                 :         66 :                 parsed->path_elements[unit_count].target_type = unit_id;
     305                 :            : 
     306                 :            :                 /* now parse the instance # */
     307                 :         66 :                 len = strlen(target_type_to_str(unit_id));
     308                 :         66 :                 instance = strtol(str + len, &end, 10);
     309                 :            : 
     310                 :         66 :                 if (!isdigit(*(str + len))) {
     311                 :          0 :                         fprintf(stderr, "Missing instance number after '%s'\n",
     312                 :            :                                         str);
     313                 :          0 :                         return -1;
     314                 :            :                 }
     315                 :            : 
     316                 :         66 :                 if (*end != '\0' && *end != '/') {
     317                 :          0 :                         fprintf(stderr, "Unable to parse instance after '%s'\n",
     318                 :            :                                         str);
     319                 :          0 :                         return -1;
     320                 :            :                 }
     321                 :            : 
     322                 :         66 :                 if (instance > 255 || instance < 0) {
     323                 :          2 :                         fprintf(stderr,
     324                 :            :                                 "Instance %ld is invalid. Must be 0 to 255\n",
     325                 :            :                                 instance);
     326                 :          2 :                         return -1;
     327                 :            :                 }
     328                 :         64 :                 parsed->path_elements[unit_count].instance = instance;
     329                 :            : 
     330                 :         64 :                 str = end;
     331                 :         64 :                 unit_count++;
     332                 :            :         }
     333                 :            : 
     334                 :            :         /*
     335                 :            :          * We assume the path is a physical path because every gard record I've
     336                 :            :          * seen so far uses them. We might need to fix this later on, but lets
     337                 :            :          * cross the bridge when we have to.
     338                 :            :          */
     339                 :         10 :         parsed->type_size = (unit_count & 0xf) |
     340                 :            :                         (PATH_PHYSICAL << PATH_TYPE_SHIFT);
     341                 :            : 
     342                 :         10 :         return 0;
     343                 :            : }
     344                 :            : 
     345                 :            : static struct gard_record blank_record;
     346                 :            : 
     347                 :        112 : static bool is_valid_record(struct gard_record *g)
     348                 :            : {
     349                 :        112 :         return memcmp(&blank_record, g, sizeof(*g));
     350                 :            : }
     351                 :            : 
     352                 :         10 : static int do_iterate(struct gard_ctx *ctx,
     353                 :            :                 int (*func)(struct gard_ctx *ctx, int pos,
     354                 :            :                         struct gard_record *gard, void *priv),
     355                 :            :                 void *priv)
     356                 :            : {
     357                 :         10 :         int rc = 0;
     358                 :         10 :         unsigned int i;
     359                 :         10 :         struct gard_record gard, null_gard;
     360                 :            : 
     361                 :         10 :         memset(&null_gard, UINT_MAX, sizeof(gard));
     362                 :         22 :         for (i = 0; i * sizeof(gard) < ctx->gard_data_len && rc == 0; i++) {
     363                 :         22 :                 memset(&gard, 0, sizeof(gard));
     364                 :            : 
     365                 :         22 :                 rc = blocklevel_read(ctx->bl, ctx->gard_data_pos + (i * sizeof(gard)),
     366                 :            :                                         &gard, sizeof(gard));
     367                 :            :                 /* It isn't super clear what constitutes the end, this should do */
     368                 :         22 :                 if (rc || memcmp(&gard, &null_gard, sizeof(gard)) == 0)
     369                 :            :                         break;
     370                 :            : 
     371                 :         12 :                 rc = func(ctx, i, &gard, priv);
     372                 :            :         }
     373                 :            : 
     374                 :         10 :         return rc;
     375                 :            : }
     376                 :            : 
     377                 :            : /*
     378                 :            :  * read the next guard record into the supplied buffer (gard)
     379                 :            :  *
     380                 :            :  * returns the record id (nb: 1 based not zero)
     381                 :            :  *
     382                 :            :  */
     383                 :        104 : static int __gard_next(struct gard_ctx *ctx, int pos, struct gard_record *gard, int *rc)
     384                 :            : {
     385                 :        104 :         uint32_t offset = pos * sizeof(*gard);
     386                 :            : 
     387                 :        104 :         if (offset > ctx->gard_data_len) /* too big */
     388                 :            :                 return -1;
     389                 :            : 
     390                 :            :         /* you lose error handling information, *gruble* */
     391                 :        104 :         memset(gard, 0, sizeof(*gard));
     392                 :        104 :         *rc = blocklevel_read(ctx->bl, ctx->gard_data_pos + offset,
     393                 :            :                                 gard, sizeof(*gard));
     394                 :            : 
     395                 :        104 :         if (!is_valid_record(gard))
     396                 :            :                 return -1;
     397                 :            : 
     398                 :         56 :         if (*rc)
     399                 :          0 :                 return -1;
     400                 :            : 
     401                 :            :         return pos;
     402                 :            : }
     403                 :            : 
     404                 :            : #define for_each_gard(ctx, pos, gard, rc) \
     405                 :            :         for (pos = __gard_next(ctx, 0, gard, rc); \
     406                 :            :                 pos >= 0; pos = __gard_next(ctx, ++pos, gard, rc))
     407                 :            : 
     408                 :          6 : static int count_records(struct gard_ctx *ctx)
     409                 :            : {
     410                 :          6 :         struct gard_record record;
     411                 :          6 :         int rc, pos, count = 0;
     412                 :            : 
     413                 :         14 :         for_each_gard(ctx, pos, &record, &rc)
     414                 :          8 :                 count++;
     415                 :            : 
     416                 :          6 :         return rc ? rc : count;
     417                 :            : }
     418                 :            : 
     419                 :         14 : static int count_valid_records(struct gard_ctx *ctx)
     420                 :            : {
     421                 :         14 :         struct gard_record record;
     422                 :         14 :         int rc, pos, count = 0;
     423                 :            : 
     424                 :         28 :         for_each_gard(ctx, pos, &record, &rc)
     425                 :         14 :                 count++;
     426                 :            : 
     427                 :         14 :         return rc ? rc : count;
     428                 :            : }
     429                 :            : 
     430                 :         10 : static size_t find_longest_path(struct gard_ctx *ctx)
     431                 :            : {
     432                 :         10 :         char scratch[MAX_PATH_SIZE];
     433                 :         10 :         struct gard_record gard;
     434                 :         10 :         size_t len, longest = 0;
     435                 :         10 :         int rc, pos;
     436                 :            : 
     437                 :         24 :         for_each_gard(ctx, pos, &gard, &rc) {
     438                 :         14 :                 len = strlen(format_path(&gard.target_id, scratch));
     439                 :         14 :                 if (len > longest)
     440                 :            :                         longest = len;
     441                 :            :         }
     442                 :            : 
     443                 :         10 :         return longest;
     444                 :            : }
     445                 :            : 
     446                 :         20 : static void draw_ruler(char c, int size)
     447                 :            : {
     448                 :         20 :         int i;
     449                 :            : 
     450                 :       1580 :         for (i = 0; i < size; i++)
     451                 :       1560 :                 putchar(c);
     452                 :         20 :         putchar('\n');
     453                 :         20 : }
     454                 :            : 
     455                 :         14 : static int do_list(struct gard_ctx *ctx, int argc __attribute__((unused)),
     456                 :            :                    char **argv __attribute__((unused)))
     457                 :            : {
     458                 :            :         /* This header matches the line formatting above in do_list_i() */
     459                 :         14 :         const char *header = " ID       | Error    | Type       | Path";
     460                 :         14 :         size_t ruler_size;
     461                 :         14 :         char scratch[MAX_PATH_SIZE];
     462                 :         14 :         struct gard_record gard;
     463                 :         14 :         int rc = 0, pos;
     464                 :            : 
     465                 :            :         /* No entries */
     466                 :         14 :         if (count_valid_records(ctx) == 0) {
     467                 :          4 :                 printf("No GARD entries to display\n");
     468                 :          4 :                 return 0;
     469                 :            :         }
     470                 :            : 
     471                 :         10 :         puts(header);
     472                 :            : 
     473                 :         10 :         ruler_size = strlen(header) + find_longest_path(ctx);
     474                 :         10 :         draw_ruler('-', ruler_size);
     475                 :            : 
     476                 :         24 :         for_each_gard(ctx, pos, &gard, &rc) {
     477                 :         28 :                 printf(" %08x | %08x | %-10s | %s%s\n",
     478                 :            :                         be32toh(gard.record_id),
     479                 :            :                         be32toh(gard.errlog_eid),
     480                 :         14 :                         deconfig_reason_str(gard.error_type),
     481                 :            :                         format_path(&gard.target_id, scratch),
     482                 :         14 :                         gard.record_id == 0xffffffff ? " *CLEARED*" : "");
     483                 :            :         }
     484                 :            : 
     485                 :         10 :         draw_ruler('=', ruler_size);
     486                 :            : 
     487                 :         10 :         return rc;
     488                 :            : }
     489                 :            : 
     490                 :          6 : static int do_show_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
     491                 :            : {
     492                 :          6 :         uint32_t id;
     493                 :            : 
     494                 :          6 :         (void)ctx;
     495                 :          6 :         (void)pos;
     496                 :            : 
     497                 :          6 :         if (!priv || !gard)
     498                 :            :                 return -1;
     499                 :            : 
     500                 :          6 :         id = *(uint32_t *)priv;
     501                 :            : 
     502                 :          6 :         if (be32toh(gard->record_id) == id) {
     503                 :          4 :                 unsigned int count, i;
     504                 :            : 
     505                 :          4 :                 printf("Record ID:    0x%08x%s\n", id, id == 0xffffffff ? " *CLEARED*" : "");
     506                 :          4 :                 printf("========================\n");
     507                 :          4 :                 printf("Error ID:     0x%08x\n", be32toh(gard->errlog_eid));
     508                 :          8 :                 printf("Error Type:   %s (0x%02x)\n",
     509                 :            :                         deconfig_reason_str(gard->error_type),
     510                 :          4 :                         gard->error_type);
     511                 :          4 :                 printf("Path Type: %s\n", path_type_to_str(gard->target_id.type_size >> PATH_TYPE_SHIFT));
     512                 :          4 :                 count = gard->target_id.type_size & PATH_ELEMENTS_MASK;
     513                 :         20 :                 for (i = 0; i < count && i < MAX_PATH_ELEMENTS; i++)
     514                 :         16 :                         printf("%*c%s, Instance #%d\n", i + 1, '>', target_type_to_str(gard->target_id.path_elements[i].target_type),
     515                 :         16 :                                gard->target_id.path_elements[i].instance);
     516                 :            :         }
     517                 :            : 
     518                 :            :         return 0;
     519                 :            : }
     520                 :            : 
     521                 :          4 : static int do_show(struct gard_ctx *ctx, int argc, char **argv)
     522                 :            : {
     523                 :          4 :         uint32_t id;
     524                 :          4 :         int rc;
     525                 :            : 
     526                 :          4 :         if (argc != 2) {
     527                 :          0 :                 fprintf(stderr, "%s option requires a GARD record\n", argv[0]);
     528                 :          0 :                 return -1;
     529                 :            :         }
     530                 :            : 
     531                 :          4 :         id = strtoul(argv[1], NULL, 16);
     532                 :            : 
     533                 :          4 :         rc = do_iterate(ctx, &do_show_i, &id);
     534                 :            : 
     535                 :          4 :         return rc;
     536                 :            : }
     537                 :            : 
     538                 :          6 : static int do_clear_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
     539                 :            : {
     540                 :          6 :         int largest, rc = 0;
     541                 :          6 :         char *buf;
     542                 :          6 :         struct gard_record null_gard;
     543                 :            : 
     544                 :          6 :         if (!gard || !ctx || !priv)
     545                 :            :                 return -1;
     546                 :            : 
     547                 :            :         /* Not this one */
     548                 :          6 :         if (be32toh(gard->record_id) != *(uint32_t *)priv)
     549                 :            :                 return 0;
     550                 :            : 
     551                 :          6 :         memset(&null_gard, 0xFF, sizeof(null_gard));
     552                 :            : 
     553                 :          6 :         largest = count_records(ctx);
     554                 :            : 
     555                 :          6 :         printf("Clearing gard record 0x%08x...", be32toh(gard->record_id));
     556                 :            : 
     557                 :          6 :         if (largest < 0 || pos > largest) {
     558                 :            :                 /* Something went horribly wrong */
     559                 :          0 :                 fprintf(stderr, "largest index out of range %d\n", largest);
     560                 :          0 :                 return -1;
     561                 :            :         }
     562                 :            : 
     563                 :          6 :         if (pos < largest) {
     564                 :            :                 /* We're not clearing the last record, shift all the records up */
     565                 :          6 :                 int buf_len = ((largest - pos) * sizeof(struct gard_record));
     566                 :          6 :                 int buf_pos = ctx->gard_data_pos + ((pos + 1) * sizeof(struct gard_record));
     567                 :          6 :                 buf = malloc(buf_len);
     568                 :          6 :                 if (!buf)
     569                 :            :                         return -ENOMEM;
     570                 :            : 
     571                 :          6 :                 rc = blocklevel_read(ctx->bl, buf_pos, buf, buf_len);
     572                 :          6 :                 if (rc) {
     573                 :          0 :                         free(buf);
     574                 :          0 :                         fprintf(stderr, "Couldn't read from flash at 0x%08x for len 0x%08x\n", buf_pos, buf_len);
     575                 :          0 :                         return rc;
     576                 :            :                 }
     577                 :            : 
     578                 :          6 :                 rc = blocklevel_smart_write(ctx->bl, buf_pos - sizeof(*gard), buf, buf_len);
     579                 :          6 :                 free(buf);
     580                 :          6 :                 if (rc) {
     581                 :          0 :                         fprintf(stderr, "Couldn't write to flash at 0x%08x for len 0x%08x\n",
     582                 :            :                                         buf_pos - (int) sizeof(struct gard_record), buf_len);
     583                 :          0 :                         return rc;
     584                 :            :                 }
     585                 :            :         }
     586                 :            : 
     587                 :            :         /* Now wipe the last record */
     588                 :          6 :         rc = blocklevel_smart_write(ctx->bl, ctx->gard_data_pos + (largest * sizeof(null_gard)),
     589                 :            :                                     &null_gard, sizeof(null_gard));
     590                 :          6 :         printf("done\n");
     591                 :            : 
     592                 :          6 :         return rc;
     593                 :            : }
     594                 :            : 
     595                 :          4 : static int reset_partition(struct gard_ctx *ctx)
     596                 :            : {
     597                 :          4 :         int no_ecc_len = (ctx->gard_data_len / 9) * 8;
     598                 :          4 :         struct gard_record *gard;
     599                 :          4 :         int rc = 0;
     600                 :            : 
     601                 :          4 :         gard = malloc(ctx->gard_data_len);
     602                 :          4 :         if (!gard) {
     603                 :            :                 return FLASH_ERR_MALLOC_FAILED;
     604                 :            :         }
     605                 :          4 :         memset(gard, 0xFF, ctx->gard_data_len);
     606                 :            : 
     607                 :          4 :         rc = blocklevel_smart_erase(ctx->bl, ctx->gard_data_pos, ctx->gard_data_len);
     608                 :          4 :         if (rc) {
     609                 :          0 :                 fprintf(stderr, "Couldn't erase the gard partition. Bailing out\n");
     610                 :          0 :                 goto out;
     611                 :            :         }
     612                 :            : 
     613                 :          4 :         rc = blocklevel_write(ctx->bl, ctx->gard_data_pos, gard, no_ecc_len);
     614                 :          4 :         if (rc)
     615                 :          0 :                 fprintf(stderr, "Couldn't reset the entire gard partition. Bailing out\n");
     616                 :            : 
     617                 :          4 : out:
     618                 :          4 :         free(gard);
     619                 :          4 :         return rc;
     620                 :            : }
     621                 :            : 
     622                 :         10 : static int do_clear(struct gard_ctx *ctx, int argc, char **argv)
     623                 :            : {
     624                 :         10 :         int rc;
     625                 :         10 :         uint32_t id;
     626                 :            : 
     627                 :         10 :         if (argc != 2) {
     628                 :          0 :                 fprintf(stderr, "%s option requires a GARD record or 'all'\n", argv[0]);
     629                 :          0 :                 return -1;
     630                 :            :         }
     631                 :            : 
     632                 :         10 :         if (strncmp(argv[1], "all", strlen("all")) == 0) {
     633                 :          4 :                 printf("Clearing the entire gard partition...");
     634                 :          4 :                 fflush(stdout);
     635                 :          4 :                 rc = reset_partition(ctx);
     636                 :          4 :                 printf("done\n");
     637                 :            :         } else {
     638                 :          6 :                 id = strtoul(argv[1], NULL, 16);
     639                 :          6 :                 rc = do_iterate(ctx, do_clear_i, &id);
     640                 :            :         }
     641                 :            : 
     642                 :            :         return rc;
     643                 :            : }
     644                 :            : 
     645                 :         18 : static int do_create(struct gard_ctx *ctx, int argc, char **argv)
     646                 :            : {
     647                 :         18 :         int rc, pos, max_id = 0, last_pos = 0;
     648                 :         18 :         struct gard_record gard;
     649                 :         18 :         struct entity_path path;
     650                 :            : 
     651                 :         18 :         if (argc < 2) {
     652                 :          0 :                 fprintf(stderr, "create requires path to gard\n");
     653                 :          0 :                 fprintf(stderr, "e.g.\n");
     654                 :          0 :                 fprintf(stderr, "     /Sys0/Node0/Proc0\n");
     655                 :          0 :                 fprintf(stderr, "     /Sys0/Node0/DIMM15\n");
     656                 :          0 :                 return -1;
     657                 :            :         }
     658                 :            : 
     659                 :         18 :         if (parse_path(argv[1], &path)) {
     660                 :          8 :                 fprintf(stderr, "Unable to parse path\n");
     661                 :          8 :                 return -1;
     662                 :            :         }
     663                 :            : 
     664                 :            :         /* check if we already have a gard record applied to this path */
     665                 :         14 :         for_each_gard(ctx, pos, &gard, &rc) {
     666                 :          6 :                 if (!memcmp(&path, &gard.target_id, sizeof(path))) {
     667                 :          2 :                         fprintf(stderr,
     668                 :            :                                 "Unit %s is already GARDed by record %#08x\n",
     669                 :            :                                 argv[1], be32toh(gard.record_id));
     670                 :          2 :                         return -1;
     671                 :            :                 }
     672                 :            : 
     673                 :            :                 /*
     674                 :            :                  * Keep track of the largest record ID seen so far,
     675                 :            :                  * we'll give the new record the max + 1 to ensure
     676                 :            :                  * that it's unique
     677                 :            :                  */
     678                 :          4 :                 if (be32toh(gard.record_id) > max_id)
     679                 :          4 :                         max_id = be32toh(gard.record_id);
     680                 :            : 
     681                 :          4 :                 last_pos++;
     682                 :            :         }
     683                 :            : 
     684                 :            :         /* do we have an empty record to write into? */
     685                 :          8 :         if (!rc && !is_valid_record(&gard)) {
     686                 :          8 :                 int offset = last_pos * sizeof(gard);
     687                 :            : 
     688                 :          8 :                 memset(&gard, 0xff, sizeof(gard));
     689                 :            : 
     690                 :          8 :                 gard.record_id = be32toh(max_id + 1);
     691                 :          8 :                 gard.error_type = GARD_MANUAL;
     692                 :          8 :                 gard.target_id = path;
     693                 :          8 :                 gard.errlog_eid = 0x0;
     694                 :            : 
     695                 :          8 :                 if (offset > ctx->gard_data_len - sizeof(gard)) {
     696                 :          0 :                         fprintf(stderr, "No space in GUARD for a new record\n");
     697                 :          0 :                         return -1;
     698                 :            :                 }
     699                 :            : 
     700                 :          8 :                 rc = blocklevel_smart_write(ctx->bl,
     701                 :          8 :                         ctx->gard_data_pos + offset, &gard, sizeof(gard));
     702                 :            :         }
     703                 :            : 
     704                 :          8 :         return rc;
     705                 :            : }
     706                 :            : 
     707                 :         46 : static int check_gard_partition(struct gard_ctx *ctx)
     708                 :            : {
     709                 :         46 :         int rc;
     710                 :         46 :         struct gard_record gard;
     711                 :         46 :         char msg[2];
     712                 :            : 
     713                 :         46 :         if (ctx->gard_data_len == 0 || ctx->gard_data_len % sizeof(struct gard_record) != 0)
     714                 :            :                 /* Just warn for now */
     715                 :          0 :                 fprintf(stderr, "The %s partition doesn't appear to be an exact multiple of"
     716                 :            :                                 "gard records in size: %zd vs %u (or partition is zero in length)\n",
     717                 :            :                                 FLASH_GARD_PART, sizeof(struct gard_record), ctx->gard_data_len);
     718                 :            : 
     719                 :            :         /*
     720                 :            :          * Attempt to read the first record, nothing can really operate if the
     721                 :            :          * first record is dead. There (currently) isn't a way to validate more
     722                 :            :          * than ECC correctness.
     723                 :            :          */
     724                 :         46 :         rc = blocklevel_read(ctx->bl, ctx->gard_data_pos, &gard, sizeof(gard));
     725                 :         46 :         if (rc == FLASH_ERR_ECC_INVALID) {
     726                 :          0 :                 fprintf(stderr, "The data at the GUARD partition does not appear to be valid gard data\n");
     727                 :          0 :                 fprintf(stderr, "Clear the entire GUARD partition? [y/N]\n");
     728                 :          0 :                 if (fgets(msg, sizeof(msg), stdin) == NULL) {
     729                 :          0 :                         fprintf(stderr, "Couldn't read from standard input\n");
     730                 :          0 :                         return -1;
     731                 :            :                 }
     732                 :          0 :                 if (msg[0] == 'y') {
     733                 :          0 :                         rc = reset_partition(ctx);
     734                 :          0 :                         if (rc) {
     735                 :          0 :                                 fprintf(stderr, "Couldn't reset the GUARD partition. Bailing out\n");
     736                 :          0 :                                 return rc;
     737                 :            :                         }
     738                 :            :                 }
     739                 :            :                 /*
     740                 :            :                  * else leave rc as is so that the main bails out, not going to be
     741                 :            :                  * able to do sensible anyway
     742                 :            :                  */
     743                 :            :         }
     744                 :            :         return rc;
     745                 :            : }
     746                 :            : 
     747                 :            : __attribute__ ((unused))
     748                 :            : static int do_nop(struct gard_ctx *ctx, int argc, char **argv)
     749                 :            : {
     750                 :            :         (void)ctx;
     751                 :            :         (void)argc;
     752                 :            :         fprintf(stderr, "Unimplemented action '%s'\n", argv[0]);
     753                 :            :         return EXIT_SUCCESS;
     754                 :            : }
     755                 :            : 
     756                 :            : struct {
     757                 :            :         const char      *name;
     758                 :            :         const char      *desc;
     759                 :            :         int             (*fn)(struct gard_ctx *, int, char **);
     760                 :            : } actions[] = {
     761                 :            :         { "list", "List current GARD records", do_list },
     762                 :            :         { "show", "Show details of a GARD record", do_show },
     763                 :            :         { "clear", "Clear GARD records", do_clear },
     764                 :            :         { "create", "Create a GARD record", do_create },
     765                 :            : };
     766                 :            : 
     767                 :          2 : static void print_version(void)
     768                 :            : {
     769                 :          4 :         printf("Open-Power GARD tool %s\n", version);
     770                 :            : }
     771                 :            : 
     772                 :          2 : static void usage(const char *progname)
     773                 :            : {
     774                 :          2 :         unsigned int i;
     775                 :            : 
     776                 :          2 :         print_version();
     777                 :          2 :         fprintf(stderr, "Usage: %s [-a -e -f <file> -p] <command> [<args>]\n\n",
     778                 :            :                         progname);
     779                 :          2 :         fprintf(stderr, "-8 --p8\n");
     780                 :          2 :         fprintf(stderr, "-9 --p9\n");
     781                 :          2 :         fprintf(stderr, "-0 --p10\n\tSet the processor generation\n\n");
     782                 :          2 :         fprintf(stderr, "-e --ecc\n\tForce reading/writing with ECC bytes.\n\n");
     783                 :          2 :         fprintf(stderr, "-f --file <file>\n\tDon't search for MTD device,"
     784                 :            :                         " read from <file>.\n\n");
     785                 :          2 :         fprintf(stderr, "-p --part\n\tUsed in conjunction with -f to specify"
     786                 :            :                         " that just\n");
     787                 :          2 :         fprintf(stderr, "\tthe GUARD partition is in <file> and libffs\n");
     788                 :          2 :         fprintf(stderr, "\tshouldn't be used.\n\n");
     789                 :            : 
     790                 :            : 
     791                 :          2 :         fprintf(stderr, "Where <command> is one of:\n\n");
     792                 :            : 
     793                 :         12 :         for (i = 0; i < ARRAY_SIZE(actions); i++) {
     794                 :          8 :                 fprintf(stderr,  "\t%-7s\t%s\n",
     795                 :            :                                 actions[i].name, actions[i].desc);
     796                 :            :         }
     797                 :          2 : }
     798                 :            : 
     799                 :         48 : static bool is_fsp(void)
     800                 :            : {
     801                 :         48 :         return access(FDT_FSP_NODE, F_OK) == 0;
     802                 :            : }
     803                 :            : 
     804                 :            : static struct option global_options[] = {
     805                 :            :         { "file", required_argument, 0, 'f' },
     806                 :            :         { "part", no_argument, 0, 'p' },
     807                 :            :         { "ecc", no_argument, 0, 'e' },
     808                 :            :         { "p8", no_argument, 0, '8' },
     809                 :            :         { "p9", no_argument, 0, '9' },
     810                 :            :         { "p10", no_argument, 0, '0' },
     811                 :            :         { 0 },
     812                 :            : };
     813                 :            : static const char *global_optstring = "+ef:p890";
     814                 :            : 
     815                 :         48 : int main(int argc, char **argv)
     816                 :            : {
     817                 :         48 :         const char *action, *progname;
     818                 :         48 :         char *filename = NULL;
     819                 :         48 :         struct gard_ctx _ctx, *ctx;
     820                 :         48 :         uint64_t bl_size;
     821                 :         48 :         int rc, i = 0;
     822                 :         48 :         bool part = 0;
     823                 :         48 :         bool ecc = 0;
     824                 :            : 
     825                 :         48 :         progname = argv[0];
     826                 :            : 
     827                 :         48 :         ctx = &_ctx;
     828                 :         48 :         memset(ctx, 0, sizeof(*ctx));
     829                 :         48 :         memset(&blank_record, 0xff, sizeof(blank_record));
     830                 :            : 
     831                 :            :         /* process global options */
     832                 :        202 :         for (;;) {
     833                 :        202 :                 int c;
     834                 :            : 
     835                 :        202 :                 c = getopt_long(argc, argv, global_optstring, global_options,
     836                 :            :                                 NULL);
     837                 :        202 :                 if (c == -1)
     838                 :            :                         break;
     839                 :        154 :                 switch (c) {
     840                 :         46 :                 case 'e':
     841                 :         46 :                         ecc = true;
     842                 :         46 :                         break;
     843                 :         46 :                 case 'f':
     844                 :            :                         /* If they specify -f twice */
     845                 :         46 :                         free(filename);
     846                 :            : 
     847                 :         46 :                         filename = strdup(optarg);
     848                 :         46 :                         if (!filename) {
     849                 :          0 :                                 fprintf(stderr, "Out of memory\n");
     850                 :          0 :                                 return EXIT_FAILURE;
     851                 :            :                         }
     852                 :            :                         break;
     853                 :         46 :                 case 'p':
     854                 :         46 :                         part = true;
     855                 :         46 :                         break;
     856                 :         10 :                 case '8':
     857                 :         10 :                         set_chip_gen(p8_chip_units);
     858                 :         10 :                         break;
     859                 :          6 :                 case '9':
     860                 :          6 :                         set_chip_gen(p9_chip_units);
     861                 :          6 :                         break;
     862                 :          0 :                 case '0':
     863                 :          0 :                         set_chip_gen(p10_chip_units);
     864                 :          0 :                         break;
     865                 :          0 :                 case '?':
     866                 :          0 :                         usage(progname);
     867                 :          0 :                         rc = EXIT_FAILURE;
     868                 :          0 :                         goto out_free;
     869                 :            :                 }
     870                 :            :         }
     871                 :            : 
     872                 :            : 
     873                 :         48 :         if (is_fsp() && !filename) {
     874                 :          0 :                 fprintf(stderr, "This is the OpenPower gard tool which does "
     875                 :            :                                 "not support FSP systems\n");
     876                 :          0 :                 return EXIT_FAILURE;
     877                 :            :         }
     878                 :            : 
     879                 :            : 
     880                 :            :         /*
     881                 :            :          * It doesn't make sense to specify that we have the gard partition but
     882                 :            :          * read from flash
     883                 :            :          */
     884                 :         48 :         if (part && !filename) {
     885                 :          0 :                 usage(progname);
     886                 :          0 :                 fprintf(stderr, "-p only makes sense when used with -f!\n");
     887                 :          0 :                 return EXIT_FAILURE;
     888                 :            :         }
     889                 :            : 
     890                 :            :         /* do we have a command? */
     891                 :         48 :         if (optind == argc) {
     892                 :          2 :                 usage(progname);
     893                 :          2 :                 rc = EXIT_FAILURE;
     894                 :          2 :                 goto out_free;
     895                 :            :         }
     896                 :            : 
     897                 :         46 :         argc -= optind;
     898                 :         46 :         argv += optind;
     899                 :         46 :         action = argv[0];
     900                 :            : 
     901                 :            : #ifdef __arm__
     902                 :            :         /*
     903                 :            :          * HACK: Look for a vPNOR GUARD file if we haven't been given anything
     904                 :            :          * explitly. If it exists then we can safely assume that:
     905                 :            :          * a) The host is a P9
     906                 :            :          * b) The file is ECC protected
     907                 :            :          * c) The file is a bare partition.
     908                 :            :          *
     909                 :            :          * This is a stupid hack, but there's not other sane place for it.
     910                 :            :          * arch_init_flash() always looks for a FFS formatted PNOR when
     911                 :            :          * filename is NULL
     912                 :            :          */
     913                 :            :         if (!filename) {
     914                 :            :                 struct stat buf;
     915                 :            : 
     916                 :            :                 if (!stat(VPNOR_GARD_FILE, &buf)) {
     917                 :            :                         filename = strdup(VPNOR_GARD_FILE);
     918                 :            :                         /* BUG: This ignores the command line settings */
     919                 :            :                         part = true;
     920                 :            :                         ecc = true;
     921                 :            :                 } else if (!stat(VPNOR_GARD_DIR, &buf)) {
     922                 :            :                         printf(VPNOR_GARD_FILE" is missing. Nothing to do\n");
     923                 :            :                         return 0;
     924                 :            :                 }
     925                 :            :         }
     926                 :            : #endif
     927                 :            : 
     928                 :         46 :         if (!chip_units)
     929                 :         30 :                 guess_chip_gen();
     930                 :            : 
     931                 :            :         /*
     932                 :            :          * Force libflash to do flash accesses via the MTD. Direct mode is
     933                 :            :          * generally unsafe since it fiddles with the flash controller state
     934                 :            :          * underneath the kernel. Anyone who needs direct mode can use pflash
     935                 :            :          * instead.
     936                 :            :          */
     937                 :         46 :         arch_flash_access(ctx->bl, PNOR_MTD);
     938                 :            : 
     939                 :         46 :         if (arch_flash_init(&(ctx->bl), filename, true)) {
     940                 :            :                 /* Can fail for a few ways, most likely couldn't open MTD device */
     941                 :          0 :                 fprintf(stderr, "Can't open %s\n", filename ? filename : "MTD Device. Are you root?");
     942                 :          0 :                 rc = EXIT_FAILURE;
     943                 :          0 :                 goto out_free;
     944                 :            :         }
     945                 :            : 
     946                 :         46 :         rc = blocklevel_get_info(ctx->bl, NULL, &bl_size, NULL);
     947                 :         46 :         if (rc)
     948                 :          0 :                 goto out;
     949                 :            : 
     950                 :         46 :         if (bl_size > UINT_MAX) {
     951                 :          0 :                 fprintf(stderr, "MTD device bigger than %i: size: %" PRIu64 "\n",
     952                 :            :                         UINT_MAX, bl_size);
     953                 :          0 :                 rc = EXIT_FAILURE;
     954                 :          0 :                 goto out;
     955                 :            :         }
     956                 :         46 :         ctx->f_size = bl_size;
     957                 :            : 
     958                 :         46 :         if (!part) {
     959                 :          0 :                 rc = ffs_init(0, ctx->f_size, ctx->bl, &ctx->ffs, 1);
     960                 :          0 :                 if (rc)
     961                 :          0 :                         goto out;
     962                 :            : 
     963                 :          0 :                 rc = ffs_lookup_part(ctx->ffs, FLASH_GARD_PART, &ctx->gard_part_idx);
     964                 :          0 :                 if (rc)
     965                 :          0 :                         goto out;
     966                 :            : 
     967                 :          0 :                 rc = ffs_part_info(ctx->ffs, ctx->gard_part_idx, NULL, &(ctx->gard_data_pos),
     968                 :            :                                 &(ctx->gard_data_len), NULL, NULL);
     969                 :          0 :                 if (rc)
     970                 :          0 :                         goto out;
     971                 :            :         } else {
     972                 :         46 :                 if (ecc) {
     973                 :         46 :                         rc = blocklevel_ecc_protect(ctx->bl, 0, ctx->f_size);
     974                 :         46 :                         if (rc)
     975                 :          0 :                                 goto out;
     976                 :            :                 }
     977                 :            : 
     978                 :         46 :                 ctx->gard_data_pos = 0;
     979                 :         46 :                 ctx->gard_data_len = ctx->f_size;
     980                 :            :         }
     981                 :            : 
     982                 :         46 :         rc = check_gard_partition(ctx);
     983                 :         46 :         if (rc) {
     984                 :          0 :                 fprintf(stderr, "Does not appear to be sane gard data\n");
     985                 :          0 :                 goto out;
     986                 :            :         }
     987                 :            : 
     988                 :        124 :         for (i = 0; i < ARRAY_SIZE(actions); i++) {
     989                 :        124 :                 if (!strcmp(actions[i].name, action)) {
     990                 :         46 :                         rc = actions[i].fn(ctx, argc, argv);
     991                 :         46 :                         break;
     992                 :            :                 }
     993                 :            :         }
     994                 :            : 
     995                 :          0 : out:
     996                 :         46 :         if (ctx->ffs)
     997                 :          0 :                 ffs_close(ctx->ffs);
     998                 :            : 
     999                 :         46 :         file_exit_close(ctx->bl);
    1000                 :            : 
    1001                 :         46 :         if (i == ARRAY_SIZE(actions)) {
    1002                 :          0 :                 fprintf(stderr, "%s: '%s' isn't a valid command\n", progname, action);
    1003                 :          0 :                 usage(progname);
    1004                 :          0 :                 rc = EXIT_FAILURE;
    1005                 :          0 :                 goto out_free;
    1006                 :            :         }
    1007                 :            : 
    1008                 :         46 :         if (rc > 0) {
    1009                 :          0 :                 show_flash_err(rc);
    1010                 :          0 :                 if (filename && rc == FFS_ERR_BAD_MAGIC)
    1011                 :          0 :                         fprintf(stderr, "Maybe you didn't give a full flash image file?\nDid you mean '--part'?\n");
    1012                 :            :         }
    1013                 :            : 
    1014                 :         46 : out_free:
    1015                 :         48 :         free(filename);
    1016                 :         48 :         return rc;
    1017                 :            : }

Generated by: LCOV version 1.14