Home | History | Annotate | Download | only in lib
      1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #include <stddef.h>
      7 #include <stdio.h>
      8 #include <string.h>
      9 #include <sys/types.h>
     10 #include <sys/stat.h>
     11 #include <unistd.h>
     12 #include <ctype.h>
     13 
     14 #include "host_common.h"
     15 
     16 #include "crossystem.h"
     17 #include "crossystem_arch.h"
     18 #include "utility.h"
     19 #include "vboot_common.h"
     20 #include "vboot_nvstorage.h"
     21 #include "vboot_struct.h"
     22 
     23 /* Filename for kernel command line */
     24 #define KERNEL_CMDLINE_PATH "/proc/cmdline"
     25 
     26 /* Fields that GetVdatString() can get */
     27 typedef enum VdatStringField {
     28   VDAT_STRING_TIMERS = 0,           /* Timer values */
     29   VDAT_STRING_LOAD_FIRMWARE_DEBUG,  /* LoadFirmware() debug information */
     30   VDAT_STRING_LOAD_KERNEL_DEBUG,    /* LoadKernel() debug information */
     31   VDAT_STRING_MAINFW_ACT            /* Active main firmware */
     32 } VdatStringField;
     33 
     34 
     35 /* Fields that GetVdatInt() can get */
     36 typedef enum VdatIntField {
     37   VDAT_INT_FLAGS = 0,                /* Flags */
     38   VDAT_INT_HEADER_VERSION,           /* Header version for VbSharedData */
     39   VDAT_INT_DEVSW_BOOT,               /* Dev switch position at boot */
     40   VDAT_INT_DEVSW_VIRTUAL,            /* Dev switch is virtual */
     41   VDAT_INT_RECSW_BOOT,               /* Recovery switch position at boot */
     42   VDAT_INT_HW_WPSW_BOOT,             /* Hardware WP switch position at boot */
     43   VDAT_INT_SW_WPSW_BOOT,             /* Flash chip's WP setting at boot */
     44 
     45   VDAT_INT_FW_VERSION_TPM,           /* Current firmware version in TPM */
     46   VDAT_INT_KERNEL_VERSION_TPM,       /* Current kernel version in TPM */
     47   VDAT_INT_TRIED_FIRMWARE_B,         /* Tried firmware B due to fwb_tries */
     48   VDAT_INT_KERNEL_KEY_VERIFIED,      /* Kernel key verified using
     49                                       * signature, not just hash */
     50   VDAT_INT_RECOVERY_REASON,          /* Recovery reason for current boot */
     51   VDAT_INT_FW_BOOT2                  /* Firmware selection by vboot2 */
     52 } VdatIntField;
     53 
     54 
     55 /* Description of build options that may be specified on the
     56  * kernel command line. */
     57 typedef enum VbBuildOption {
     58   VB_BUILD_OPTION_UNKNOWN,
     59   VB_BUILD_OPTION_DEBUG,
     60   VB_BUILD_OPTION_NODEBUG
     61 } VbBuildOption;
     62 
     63 static const char *fw_results[] = {"unknown", "trying", "success", "failure"};
     64 
     65 /* Masks for kern_nv usage by kernel. */
     66 #define KERN_NV_FWUPDATE_TRIES_MASK 0x0000000F
     67 #define KERN_NV_BLOCK_DEVMODE_FLAG  0x00000010
     68 /* If you want to use the remaining currently-unused bits in kern_nv
     69  * for something kernel-y, define a new field (the way we did for
     70  * fwupdate_tries).  Don't just modify kern_nv directly, because that
     71  * makes it too easy to accidentally corrupt other sub-fields. */
     72 #define KERN_NV_CURRENTLY_UNUSED    0xFFFFFFE0
     73 
     74 /* Return true if the FWID starts with the specified string. */
     75 int FwidStartsWith(const char *start) {
     76   char fwid[VB_MAX_STRING_PROPERTY];
     77   if (!VbGetSystemPropertyString("fwid", fwid, sizeof(fwid)))
     78     return 0;
     79 
     80   return 0 == strncmp(fwid, start, strlen(start));
     81 }
     82 
     83 static int vnc_read;
     84 
     85 int VbGetNvStorage(VbNvParam param) {
     86   uint32_t value;
     87   int retval;
     88   static VbNvContext cached_vnc;
     89 
     90   /* TODO: locking around NV access */
     91   if (!vnc_read) {
     92     if (0 != VbReadNvStorage(&cached_vnc))
     93       return -1;
     94     vnc_read = 1;
     95   }
     96 
     97   if (0 != VbNvSetup(&cached_vnc))
     98     return -1;
     99   retval = VbNvGet(&cached_vnc, param, &value);
    100   if (0 != VbNvTeardown(&cached_vnc))
    101     return -1;
    102   if (0 != retval)
    103     return -1;
    104 
    105   /* TODO: If vnc.raw_changed, attempt to reopen NVRAM for write and
    106    * save the new defaults.  If we're able to, log. */
    107   /* TODO: release lock */
    108 
    109   return (int)value;
    110 }
    111 
    112 
    113 int VbSetNvStorage(VbNvParam param, int value) {
    114   VbNvContext vnc;
    115   int retval = -1;
    116   int i;
    117 
    118   if (0 != VbReadNvStorage(&vnc))
    119     return -1;
    120 
    121   if (0 != VbNvSetup(&vnc))
    122     goto VbSetNvCleanup;
    123   i = VbNvSet(&vnc, param, (uint32_t)value);
    124   if (0 != VbNvTeardown(&vnc))
    125     goto VbSetNvCleanup;
    126   if (0 != i)
    127     goto VbSetNvCleanup;
    128 
    129   if (vnc.raw_changed) {
    130     vnc_read = 0;
    131     if (0 != VbWriteNvStorage(&vnc))
    132       goto VbSetNvCleanup;
    133   }
    134 
    135   /* Success */
    136   retval = 0;
    137 
    138 VbSetNvCleanup:
    139   /* TODO: release lock */
    140   return retval;
    141 }
    142 
    143 /*
    144  * Set a param value, and try to flag it for persistent backup.
    145  * It's okay if backup isn't supported. It's best-effort only.
    146  */
    147 static int VbSetNvStorage_WithBackup(VbNvParam param, int value)
    148 {
    149   int retval;
    150   retval = VbSetNvStorage(param, value);
    151   if (!retval)
    152     VbSetNvStorage(VBNV_BACKUP_NVRAM_REQUEST, 1);
    153   return retval;
    154 }
    155 
    156 /* Find what build/debug status is specified on the kernel command
    157  * line, if any. */
    158 static VbBuildOption VbScanBuildOption(void) {
    159   FILE* f = NULL;
    160   char buf[4096] = "";
    161   char *t, *saveptr;
    162   const char *delimiters = " \r\n";
    163 
    164   f = fopen(KERNEL_CMDLINE_PATH, "r");
    165   if (NULL != f) {
    166     if (NULL == fgets(buf, sizeof(buf), f))
    167       buf[0] = 0;
    168     fclose(f);
    169   }
    170   for (t = strtok_r(buf, delimiters, &saveptr); t;
    171        t = strtok_r(NULL, delimiters, &saveptr)) {
    172     if (0 == strcmp(t, "cros_debug"))
    173       return VB_BUILD_OPTION_DEBUG;
    174     else if (0 == strcmp(t, "cros_nodebug"))
    175       return VB_BUILD_OPTION_NODEBUG;
    176   }
    177 
    178   return VB_BUILD_OPTION_UNKNOWN;
    179 }
    180 
    181 
    182 /* Determine whether the running OS image was built for debugging.
    183  * Returns 1 if yes, 0 if no or indeterminate. */
    184 int VbGetDebugBuild(void) {
    185   return VB_BUILD_OPTION_DEBUG == VbScanBuildOption();
    186 }
    187 
    188 
    189 /* Determine whether OS-level debugging should be allowed.
    190  * Returns 1 if yes, 0 if no or indeterminate. */
    191 int VbGetCrosDebug(void) {
    192   /* If the currently running system specifies its debug status, use
    193    * that in preference to other indicators. */
    194   VbBuildOption option = VbScanBuildOption();
    195   if (VB_BUILD_OPTION_DEBUG == option) {
    196       return 1;
    197   } else if (VB_BUILD_OPTION_NODEBUG == option) {
    198       return 0;
    199   }
    200 
    201   /* Command line is silent; allow debug if the dev switch is on. */
    202   if (1 == VbGetSystemPropertyInt("devsw_boot"))
    203     return 1;
    204 
    205   /* All other cases disallow debug. */
    206   return 0;
    207 }
    208 
    209 
    210 char* GetVdatLoadFirmwareDebug(char* dest, int size,
    211                                const VbSharedDataHeader* sh) {
    212   snprintf(dest, size,
    213            "Check A result=%d\n"
    214            "Check B result=%d\n"
    215            "Firmware index booted=0x%02x\n"
    216            "TPM combined version at start=0x%08x\n"
    217            "Lowest combined version from firmware=0x%08x\n",
    218            sh->check_fw_a_result,
    219            sh->check_fw_b_result,
    220            sh->firmware_index,
    221            sh->fw_version_tpm_start,
    222            sh->fw_version_lowest);
    223   return dest;
    224 }
    225 
    226 
    227 #define TRUNCATED "\n(truncated)\n"
    228 
    229 char* GetVdatLoadKernelDebug(char* dest, int size,
    230                              const VbSharedDataHeader* sh) {
    231   int used = 0;
    232   int first_call_tracked = 0;
    233   int call;
    234 
    235   /* Make sure we have space for truncation warning */
    236   if (size < strlen(TRUNCATED) + 1)
    237     return NULL;
    238   size -= strlen(TRUNCATED) + 1;
    239 
    240   used += snprintf(
    241       dest + used, size - used,
    242       "Calls to LoadKernel()=%d\n",
    243       sh->lk_call_count);
    244   if (used > size)
    245     goto LoadKernelDebugExit;
    246 
    247   /* Report on the last calls */
    248   if (sh->lk_call_count > VBSD_MAX_KERNEL_CALLS)
    249     first_call_tracked = sh->lk_call_count - VBSD_MAX_KERNEL_CALLS;
    250   for (call = first_call_tracked; call < sh->lk_call_count; call++) {
    251     const VbSharedDataKernelCall* shc =
    252         sh->lk_calls + (call & (VBSD_MAX_KERNEL_CALLS - 1));
    253     int first_part_tracked = 0;
    254     int part;
    255 
    256     used += snprintf(
    257         dest + used, size - used,
    258         "Call %d:\n"
    259         "  Boot flags=0x%02x\n"
    260         "  Boot mode=%d\n"
    261         "  Test error=%d\n"
    262         "  Return code=%d\n"
    263         "  Debug flags=0x%02x\n"
    264         "  Drive sectors=%" PRIu64 "\n"
    265         "  Sector size=%d\n"
    266         "  Check result=%d\n"
    267         "  Kernel partitions found=%d\n",
    268         call + 1,
    269         shc->boot_flags,
    270         shc->boot_mode,
    271         shc->test_error_num,
    272         shc->return_code,
    273         shc->flags,
    274         shc->sector_count,
    275         shc->sector_size,
    276         shc->check_result,
    277         shc->kernel_parts_found);
    278     if (used > size)
    279       goto LoadKernelDebugExit;
    280 
    281     /* If we found too many partitions, only prints ones where the
    282      * structure has info. */
    283     if (shc->kernel_parts_found > VBSD_MAX_KERNEL_PARTS)
    284       first_part_tracked = shc->kernel_parts_found - VBSD_MAX_KERNEL_PARTS;
    285 
    286     /* Report on the partitions checked */
    287     for (part = first_part_tracked; part < shc->kernel_parts_found; part++) {
    288       const VbSharedDataKernelPart* shp =
    289           shc->parts + (part & (VBSD_MAX_KERNEL_PARTS - 1));
    290 
    291       used += snprintf(
    292           dest + used, size - used,
    293           "  Kernel %d:\n"
    294           "    GPT index=%d\n"
    295           "    Start sector=%" PRIu64 "\n"
    296           "    Sector count=%" PRIu64 "\n"
    297           "    Combined version=0x%08x\n"
    298           "    Check result=%d\n"
    299           "    Debug flags=0x%02x\n",
    300           part + 1,
    301           shp->gpt_index,
    302           shp->sector_start,
    303           shp->sector_count,
    304           shp->combined_version,
    305           shp->check_result,
    306           shp->flags);
    307       if (used > size)
    308         goto LoadKernelDebugExit;
    309     }
    310   }
    311 
    312 LoadKernelDebugExit:
    313 
    314   /* Warn if data was truncated; we left space for this above. */
    315   if (used > size)
    316     strcat(dest, TRUNCATED);
    317 
    318   return dest;
    319 }
    320 
    321 
    322 char* GetVdatString(char* dest, int size, VdatStringField field)
    323 {
    324   VbSharedDataHeader* sh = VbSharedDataRead();
    325   char* value = dest;
    326 
    327   if (!sh)
    328     return NULL;
    329 
    330   switch (field) {
    331     case VDAT_STRING_TIMERS:
    332       snprintf(dest, size,
    333                "LFS=%" PRIu64 ",%" PRIu64
    334                " LF=%" PRIu64 ",%" PRIu64
    335                " LK=%" PRIu64 ",%" PRIu64,
    336                sh->timer_vb_init_enter,
    337                sh->timer_vb_init_exit,
    338                sh->timer_vb_select_firmware_enter,
    339                sh->timer_vb_select_firmware_exit,
    340                sh->timer_vb_select_and_load_kernel_enter,
    341                sh->timer_vb_select_and_load_kernel_exit);
    342       break;
    343 
    344     case VDAT_STRING_LOAD_FIRMWARE_DEBUG:
    345       value = GetVdatLoadFirmwareDebug(dest, size, sh);
    346       break;
    347 
    348     case VDAT_STRING_LOAD_KERNEL_DEBUG:
    349       value = GetVdatLoadKernelDebug(dest, size, sh);
    350       break;
    351 
    352     case VDAT_STRING_MAINFW_ACT:
    353       switch(sh->firmware_index) {
    354         case 0:
    355           StrCopy(dest, "A", size);
    356           break;
    357         case 1:
    358           StrCopy(dest, "B", size);
    359           break;
    360         case 0xFF:
    361           StrCopy(dest, "recovery", size);
    362           break;
    363         default:
    364           value = NULL;
    365       }
    366       break;
    367 
    368     default:
    369       value = NULL;
    370       break;
    371   }
    372 
    373   free(sh);
    374   return value;
    375 }
    376 
    377 
    378 int GetVdatInt(VdatIntField field) {
    379   VbSharedDataHeader* sh = VbSharedDataRead();
    380   int value = -1;
    381 
    382   if (!sh)
    383     return -1;
    384 
    385   /* Fields supported in version 1 */
    386   switch (field) {
    387     case VDAT_INT_FLAGS:
    388       value = (int)sh->flags;
    389       break;
    390     case VDAT_INT_HEADER_VERSION:
    391       value = sh->struct_version;
    392       break;
    393     case VDAT_INT_TRIED_FIRMWARE_B:
    394       value = (sh->flags & VBSD_FWB_TRIED ? 1 : 0);
    395       break;
    396     case VDAT_INT_KERNEL_KEY_VERIFIED:
    397       value = (sh->flags & VBSD_KERNEL_KEY_VERIFIED ? 1 : 0);
    398       break;
    399     case VDAT_INT_FW_VERSION_TPM:
    400       value = (int)sh->fw_version_tpm;
    401       break;
    402     case VDAT_INT_KERNEL_VERSION_TPM:
    403       value = (int)sh->kernel_version_tpm;
    404       break;
    405     case VDAT_INT_FW_BOOT2:
    406       value = (sh->flags & VBSD_BOOT_FIRMWARE_VBOOT2 ? 1 : 0);
    407     default:
    408       break;
    409   }
    410 
    411   /* Fields added in struct version 2 */
    412   if (sh->struct_version >= 2) {
    413     switch(field) {
    414       case VDAT_INT_DEVSW_BOOT:
    415         value = (sh->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0);
    416         break;
    417       case VDAT_INT_DEVSW_VIRTUAL:
    418         value = (sh->flags & VBSD_HONOR_VIRT_DEV_SWITCH ? 1 : 0);
    419         break;
    420       case VDAT_INT_RECSW_BOOT:
    421         value = (sh->flags & VBSD_BOOT_REC_SWITCH_ON ? 1 : 0);
    422         break;
    423       case VDAT_INT_HW_WPSW_BOOT:
    424         value = (sh->flags & VBSD_BOOT_FIRMWARE_WP_ENABLED ? 1 : 0);
    425         break;
    426       case VDAT_INT_SW_WPSW_BOOT:
    427         value = (sh->flags & VBSD_BOOT_FIRMWARE_SW_WP_ENABLED ? 1 : 0);
    428         break;
    429       case VDAT_INT_RECOVERY_REASON:
    430         value = sh->recovery_reason;
    431         break;
    432       default:
    433         break;
    434     }
    435   }
    436 
    437   free(sh);
    438   return value;
    439 }
    440 
    441 /* Return version of VbSharedData struct or -1 if not found. */
    442 int VbSharedDataVersion(void) {
    443   return GetVdatInt(VDAT_INT_HEADER_VERSION);
    444 }
    445 
    446 int VbGetSystemPropertyInt(const char* name) {
    447   int value = -1;
    448 
    449   /* Check architecture-dependent properties first */
    450   value = VbGetArchPropertyInt(name);
    451   if (-1 != value)
    452     return value;
    453 
    454   /* NV storage values */
    455   else if (!strcasecmp(name,"kern_nv")) {
    456     value = VbGetNvStorage(VBNV_KERNEL_FIELD);
    457   } else if (!strcasecmp(name,"nvram_cleared")) {
    458     value = VbGetNvStorage(VBNV_KERNEL_SETTINGS_RESET);
    459   } else if (!strcasecmp(name,"recovery_request")) {
    460     value = VbGetNvStorage(VBNV_RECOVERY_REQUEST);
    461   } else if (!strcasecmp(name,"dbg_reset")) {
    462     value = VbGetNvStorage(VBNV_DEBUG_RESET_MODE);
    463   } else if (!strcasecmp(name,"disable_dev_request")) {
    464     value = VbGetNvStorage(VBNV_DISABLE_DEV_REQUEST);
    465   } else if (!strcasecmp(name,"clear_tpm_owner_request")) {
    466     value = VbGetNvStorage(VBNV_CLEAR_TPM_OWNER_REQUEST);
    467   } else if (!strcasecmp(name,"clear_tpm_owner_done")) {
    468     value = VbGetNvStorage(VBNV_CLEAR_TPM_OWNER_DONE);
    469   } else if (!strcasecmp(name,"fwb_tries")) {
    470     value = VbGetNvStorage(VBNV_TRY_B_COUNT);
    471   } else if (!strcasecmp(name,"fw_vboot2")) {
    472     value = GetVdatInt(VDAT_INT_FW_BOOT2);
    473   } else if (!strcasecmp(name,"fw_try_count")) {
    474     value = VbGetNvStorage(VBNV_FW_TRY_COUNT);
    475   } else if (!strcasecmp(name,"fwupdate_tries")) {
    476     value = VbGetNvStorage(VBNV_KERNEL_FIELD);
    477     if (value != -1)
    478       value &= KERN_NV_FWUPDATE_TRIES_MASK;
    479   } else if (!strcasecmp(name,"block_devmode")) {
    480     value = VbGetNvStorage(VBNV_KERNEL_FIELD);
    481     if (value != -1) {
    482       value &= KERN_NV_BLOCK_DEVMODE_FLAG;
    483       value = !!value;
    484     }
    485   } else if (!strcasecmp(name,"loc_idx")) {
    486     value = VbGetNvStorage(VBNV_LOCALIZATION_INDEX);
    487   } else if (!strcasecmp(name,"backup_nvram_request")) {
    488     value = VbGetNvStorage(VBNV_BACKUP_NVRAM_REQUEST);
    489   } else if (!strcasecmp(name,"dev_boot_usb")) {
    490     value = VbGetNvStorage(VBNV_DEV_BOOT_USB);
    491   } else if (!strcasecmp(name,"dev_boot_legacy")) {
    492     value = VbGetNvStorage(VBNV_DEV_BOOT_LEGACY);
    493   } else if (!strcasecmp(name,"dev_boot_signed_only")) {
    494     value = VbGetNvStorage(VBNV_DEV_BOOT_SIGNED_ONLY);
    495   } else if (!strcasecmp(name,"oprom_needed")) {
    496     value = VbGetNvStorage(VBNV_OPROM_NEEDED);
    497   } else if (!strcasecmp(name,"recovery_subcode")) {
    498     value = VbGetNvStorage(VBNV_RECOVERY_SUBCODE);
    499   }
    500   /* Other parameters */
    501   else if (!strcasecmp(name,"cros_debug")) {
    502     value = VbGetCrosDebug();
    503   } else if (!strcasecmp(name,"debug_build")) {
    504     value = VbGetDebugBuild();
    505   } else if (!strcasecmp(name,"devsw_boot")) {
    506     value = GetVdatInt(VDAT_INT_DEVSW_BOOT);
    507   } else if (!strcasecmp(name,"devsw_virtual")) {
    508     value = GetVdatInt(VDAT_INT_DEVSW_VIRTUAL);
    509   } else if (!strcasecmp(name, "recoverysw_boot")) {
    510     value = GetVdatInt(VDAT_INT_RECSW_BOOT);
    511   } else if (!strcasecmp(name, "wpsw_boot")) {
    512     value = GetVdatInt(VDAT_INT_HW_WPSW_BOOT);
    513   } else if (!strcasecmp(name, "sw_wpsw_boot")) {
    514     value = GetVdatInt(VDAT_INT_SW_WPSW_BOOT);
    515   } else if (!strcasecmp(name,"vdat_flags")) {
    516     value = GetVdatInt(VDAT_INT_FLAGS);
    517   } else if (!strcasecmp(name,"tpm_fwver")) {
    518     value = GetVdatInt(VDAT_INT_FW_VERSION_TPM);
    519   } else if (!strcasecmp(name,"tpm_kernver")) {
    520     value = GetVdatInt(VDAT_INT_KERNEL_VERSION_TPM);
    521   } else if (!strcasecmp(name,"tried_fwb")) {
    522     value = GetVdatInt(VDAT_INT_TRIED_FIRMWARE_B);
    523   } else if (!strcasecmp(name,"recovery_reason")) {
    524     value = GetVdatInt(VDAT_INT_RECOVERY_REASON);
    525   }
    526 
    527   return value;
    528 }
    529 
    530 
    531 const char* VbGetSystemPropertyString(const char* name, char* dest,
    532                                       size_t size) {
    533   static const char unknown_string[] = "unknown";
    534 
    535   /* Check architecture-dependent properties first */
    536   if (VbGetArchPropertyString(name, dest, size))
    537     return dest;
    538 
    539   if (!strcasecmp(name,"kernkey_vfy")) {
    540     switch(GetVdatInt(VDAT_INT_KERNEL_KEY_VERIFIED)) {
    541       case 0:
    542         return "hash";
    543       case 1:
    544         return "sig";
    545       default:
    546         return NULL;
    547     }
    548   } else if (!strcasecmp(name, "mainfw_act")) {
    549     return GetVdatString(dest, size, VDAT_STRING_MAINFW_ACT);
    550   } else if (!strcasecmp(name, "vdat_timers")) {
    551     return GetVdatString(dest, size, VDAT_STRING_TIMERS);
    552   } else if (!strcasecmp(name, "vdat_lfdebug")) {
    553     return GetVdatString(dest, size, VDAT_STRING_LOAD_FIRMWARE_DEBUG);
    554   } else if (!strcasecmp(name, "vdat_lkdebug")) {
    555     return GetVdatString(dest, size, VDAT_STRING_LOAD_KERNEL_DEBUG);
    556   } else if (!strcasecmp(name, "ddr_type")) {
    557     return unknown_string;
    558   } else if (!strcasecmp(name, "fw_try_next")) {
    559     return VbGetNvStorage(VBNV_FW_TRY_NEXT) ? "B" : "A";
    560   } else if (!strcasecmp(name, "fw_tried")) {
    561     return VbGetNvStorage(VBNV_FW_TRIED) ? "B" : "A";
    562   } else if (!strcasecmp(name, "fw_result")) {
    563     int v = VbGetNvStorage(VBNV_FW_RESULT);
    564     if (v < ARRAY_SIZE(fw_results))
    565       return fw_results[v];
    566     else
    567       return "unknown";
    568   } else if (!strcasecmp(name, "fw_prev_tried")) {
    569     return VbGetNvStorage(VBNV_FW_PREV_TRIED) ? "B" : "A";
    570   } else if (!strcasecmp(name, "fw_prev_result")) {
    571     int v = VbGetNvStorage(VBNV_FW_PREV_RESULT);
    572     if (v < ARRAY_SIZE(fw_results))
    573       return fw_results[v];
    574     else
    575       return "unknown";
    576   }
    577 
    578   return NULL;
    579 }
    580 
    581 
    582 int VbSetSystemPropertyInt(const char* name, int value) {
    583   /* Check architecture-dependent properties first */
    584 
    585   if (0 == VbSetArchPropertyInt(name, value))
    586     return 0;
    587 
    588   /* NV storage values */
    589   if (!strcasecmp(name,"nvram_cleared")) {
    590     /* Can only clear this flag; it's set inside the NV storage library. */
    591     return VbSetNvStorage(VBNV_KERNEL_SETTINGS_RESET, 0);
    592   } else if (!strcasecmp(name,"recovery_request")) {
    593     return VbSetNvStorage(VBNV_RECOVERY_REQUEST, value);
    594   } else if (!strcasecmp(name,"recovery_subcode")) {
    595     return VbSetNvStorage(VBNV_RECOVERY_SUBCODE, value);
    596   } else if (!strcasecmp(name,"dbg_reset")) {
    597     return VbSetNvStorage(VBNV_DEBUG_RESET_MODE, value);
    598   } else if (!strcasecmp(name,"disable_dev_request")) {
    599     return VbSetNvStorage(VBNV_DISABLE_DEV_REQUEST, value);
    600   } else if (!strcasecmp(name,"clear_tpm_owner_request")) {
    601     return VbSetNvStorage(VBNV_CLEAR_TPM_OWNER_REQUEST, value);
    602   } else if (!strcasecmp(name,"clear_tpm_owner_done")) {
    603     /* Can only clear this flag; it's set by firmware. */
    604     return VbSetNvStorage(VBNV_CLEAR_TPM_OWNER_DONE, 0);
    605   } else if (!strcasecmp(name,"fwb_tries")) {
    606     return VbSetNvStorage(VBNV_TRY_B_COUNT, value);
    607   } else if (!strcasecmp(name,"fw_try_count")) {
    608     return VbSetNvStorage(VBNV_FW_TRY_COUNT, value);
    609   } else if (!strcasecmp(name,"oprom_needed")) {
    610     return VbSetNvStorage(VBNV_OPROM_NEEDED, value);
    611   } else if (!strcasecmp(name,"backup_nvram_request")) {
    612       /* Best-effort only, since it requires firmware and TPM support. */
    613     return VbSetNvStorage(VBNV_BACKUP_NVRAM_REQUEST, value);
    614   } else if (!strcasecmp(name,"fwupdate_tries")) {
    615     int kern_nv = VbGetNvStorage(VBNV_KERNEL_FIELD);
    616     if (kern_nv == -1)
    617       return -1;
    618     kern_nv &= ~KERN_NV_FWUPDATE_TRIES_MASK;
    619     kern_nv |= (value & KERN_NV_FWUPDATE_TRIES_MASK);
    620     return VbSetNvStorage_WithBackup(VBNV_KERNEL_FIELD, kern_nv);
    621   } else if (!strcasecmp(name,"block_devmode")) {
    622     int kern_nv = VbGetNvStorage(VBNV_KERNEL_FIELD);
    623     if (kern_nv == -1)
    624       return -1;
    625     kern_nv &= ~KERN_NV_BLOCK_DEVMODE_FLAG;
    626     if (value)
    627 	kern_nv |= KERN_NV_BLOCK_DEVMODE_FLAG;
    628     return VbSetNvStorage_WithBackup(VBNV_KERNEL_FIELD, kern_nv);
    629   } else if (!strcasecmp(name,"loc_idx")) {
    630     return VbSetNvStorage_WithBackup(VBNV_LOCALIZATION_INDEX, value);
    631   } else if (!strcasecmp(name,"dev_boot_usb")) {
    632     return VbSetNvStorage_WithBackup(VBNV_DEV_BOOT_USB, value);
    633   } else if (!strcasecmp(name,"dev_boot_legacy")) {
    634     return VbSetNvStorage_WithBackup(VBNV_DEV_BOOT_LEGACY, value);
    635   } else if (!strcasecmp(name,"dev_boot_signed_only")) {
    636     return VbSetNvStorage_WithBackup(VBNV_DEV_BOOT_SIGNED_ONLY, value);
    637   }
    638 
    639   return -1;
    640 }
    641 
    642 
    643 int VbSetSystemPropertyString(const char* name, const char* value) {
    644   /* Chain to architecture-dependent properties */
    645   if (0 == VbSetArchPropertyString(name, value))
    646     return 0;
    647 
    648   if (!strcasecmp(name, "fw_try_next")) {
    649     if (!strcasecmp(value, "A"))
    650       return VbSetNvStorage(VBNV_FW_TRY_NEXT, 0);
    651     else if (!strcasecmp(value, "B"))
    652       return VbSetNvStorage(VBNV_FW_TRY_NEXT, 1);
    653     else
    654       return -1;
    655 
    656   } else if (!strcasecmp(name, "fw_result")) {
    657     int i;
    658 
    659     for (i = 0; i < ARRAY_SIZE(fw_results); i++) {
    660       if (!strcasecmp(value, fw_results[i]))
    661 	return VbSetNvStorage(VBNV_FW_RESULT, i);
    662     }
    663     return -1;
    664   }
    665 
    666   return -1;
    667 }
    668