1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved 4 * Copyright 2010 Intel Corporation; author: H. Peter Anvin 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330, 9 * Boston MA 02111-1307, USA; either version 2 of the License, or 10 * (at your option) any later version; incorporated herein by reference. 11 * 12 * ----------------------------------------------------------------------- */ 13 14 /* 15 * setadv.c 16 * 17 * (Over)write a data item in the auxilliary data vector. To 18 * delete an item, set its length to zero. 19 * 20 * Return 0 on success, -1 on error, and set errno. 21 * 22 */ 23 #define _GNU_SOURCE 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <stddef.h> 28 #include <stdint.h> 29 #include <string.h> 30 #include <errno.h> 31 #include "syslxint.h" 32 #include "syslxcom.h" 33 #include "syslxfs.h" 34 35 unsigned char syslinux_adv[2 * ADV_SIZE]; 36 37 #define ADV_MAGIC1 0x5a2d2fa5 /* Head signature */ 38 #define ADV_MAGIC2 0xa3041767 /* Total checksum */ 39 #define ADV_MAGIC3 0xdd28bf64 /* Tail signature */ 40 41 static void cleanup_adv(unsigned char *advbuf) 42 { 43 int i; 44 uint32_t csum; 45 46 /* Make sure both copies agree, and update the checksum */ 47 set_32((uint32_t *) advbuf, ADV_MAGIC1); 48 49 csum = ADV_MAGIC2; 50 for (i = 8; i < ADV_SIZE - 4; i += 4) 51 csum -= get_32((uint32_t *) (advbuf + i)); 52 53 set_32((uint32_t *) (advbuf + 4), csum); 54 set_32((uint32_t *) (advbuf + ADV_SIZE - 4), ADV_MAGIC3); 55 56 memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE); 57 } 58 59 int syslinux_setadv(int tag, size_t size, const void *data) 60 { 61 uint8_t *p; 62 size_t left; 63 uint8_t advtmp[ADV_SIZE]; 64 65 if ((unsigned)tag - 1 > 254) { 66 errno = EINVAL; 67 return -1; /* Impossible tag value */ 68 } 69 70 if (size > 255) { 71 errno = ENOSPC; /* Max 255 bytes for a data item */ 72 return -1; 73 } 74 75 left = ADV_LEN; 76 p = advtmp; 77 memcpy(p, syslinux_adv + 2 * 4, left); /* Make working copy */ 78 79 while (left >= 2) { 80 uint8_t ptag = p[0]; 81 size_t plen = p[1] + 2; 82 83 if (ptag == ADV_END) 84 break; 85 86 if (ptag == tag) { 87 /* Found our tag. Delete it. */ 88 89 if (plen >= left) { 90 /* Entire remainder is our tag */ 91 break; 92 } 93 memmove(p, p + plen, left - plen); 94 } else { 95 /* Not our tag */ 96 if (plen > left) 97 break; /* Corrupt tag (overrun) - overwrite it */ 98 99 left -= plen; 100 p += plen; 101 } 102 } 103 104 /* Now (p, left) reflects the position to write in and how much space 105 we have for our data. */ 106 107 if (size) { 108 if (left < size + 2) { 109 errno = ENOSPC; /* Not enough space for data */ 110 return -1; 111 } 112 113 *p++ = tag; 114 *p++ = size; 115 memcpy(p, data, size); 116 p += size; 117 left -= size + 2; 118 } 119 120 memset(p, 0, left); 121 122 /* If we got here, everything went OK, commit the write */ 123 memcpy(syslinux_adv + 2 * 4, advtmp, ADV_LEN); 124 cleanup_adv(syslinux_adv); 125 126 return 0; 127 } 128 129 void syslinux_reset_adv(unsigned char *advbuf) 130 { 131 /* Create an all-zero ADV */ 132 memset(advbuf + 2 * 4, 0, ADV_LEN); 133 cleanup_adv(advbuf); 134 } 135 136 static int adv_consistent(const unsigned char *p) 137 { 138 int i; 139 uint32_t csum; 140 141 if (get_32((uint32_t *) p) != ADV_MAGIC1 || 142 get_32((uint32_t *) (p + ADV_SIZE - 4)) != ADV_MAGIC3) 143 return 0; 144 145 csum = 0; 146 for (i = 4; i < ADV_SIZE - 4; i += 4) 147 csum += get_32((uint32_t *) (p + i)); 148 149 return csum == ADV_MAGIC2; 150 } 151 152 /* 153 * Verify that an in-memory ADV is consistent, making the copies consistent. 154 * If neither copy is OK, return -1 and call syslinux_reset_adv(). 155 */ 156 int syslinux_validate_adv(unsigned char *advbuf) 157 { 158 if (adv_consistent(advbuf + 0 * ADV_SIZE)) { 159 memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE); 160 return 0; 161 } else if (adv_consistent(advbuf + 1 * ADV_SIZE)) { 162 memcpy(advbuf, advbuf + ADV_SIZE, ADV_SIZE); 163 return 0; 164 } else { 165 syslinux_reset_adv(advbuf); 166 return -1; 167 } 168 } 169