1 /* Copyright (c) 2013 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 * Delay/beep functions used in dev-mode kernel selection. 6 */ 7 8 #include "sysincludes.h" 9 10 #include "crc32.h" 11 #include "gbb_header.h" 12 #include "utility.h" 13 #include "vboot_api.h" 14 #include "vboot_audio.h" 15 #include "vboot_audio_private.h" 16 #include "vboot_common.h" 17 18 /* BIOS doesn't have /usr/include */ 19 #ifndef UINT_MAX 20 #define UINT_MAX 4294967295U /* 0xffffffff */ 21 #endif 22 23 /* 24 * Need one second of noise in the first 22 seconds. 25 * Total delay >= 30 seconds, <= 60 seconds. 26 */ 27 #define REQUIRED_NOISE_TIME 1000 28 #define REQUIRED_NOISE_WITHIN 22000 29 #define REQUIRED_TOTAL_DELAY 30000 30 #define MAX_CUSTOM_DELAY 60000 31 32 /* These are visible externally only to make testing easier */ 33 VbDevMusicNote default_notes_[] = { {20000, 0}, /* 20 seconds */ 34 {250, 400}, /* two beeps */ 35 {250, 0}, 36 {250, 400}, 37 {9250, 0} }; /* total 30 seconds */ 38 uint32_t default_count_ = sizeof(default_notes_) / sizeof(VbDevMusicNote); 39 40 VbDevMusicNote short_notes_[] = { {2000, 0} }; /* two seconds */ 41 uint32_t short_count_ = sizeof(short_notes_) / sizeof(VbDevMusicNote); 42 43 /* No need to dynamically allocate this, is there? */ 44 static VbAudioContext au; 45 46 /* Convert from msecs to VbExGetTimer() units. */ 47 static uint64_t ticks_per_msec = 0; /* Initialized by VbAudioOpen() */ 48 static uint64_t VbMsecToTicks(uint16_t msec) { 49 return ticks_per_msec * msec; 50 } 51 52 /** 53 * Find and return a valid set of note events. 54 * 55 * We'll use the user's struct if possible, but we will still enforce the 56 * 30-second timeout and require at least a second of audible noise within that 57 * period. We allocate storage for two reasons: the user's struct will be in 58 * flash, which is slow to read, and we may need one extra note at the end to 59 * pad out the user's notes to a full 30 seconds. The caller should free it 60 * when finished. 61 */ 62 static void VbGetDevMusicNotes(VbAudioContext *audio, int use_short) 63 { 64 VbDevMusicNote *notebuf = 0; 65 VbDevMusicNote *builtin = 0; 66 VbDevMusic *hdr = CUSTOM_MUSIC_NOTES; 67 uint32_t maxsize = CUSTOM_MUSIC_MAXSIZE; /* always <= flash size (8M) */ 68 uint32_t maxnotes, mysum, mylen, i; 69 uint32_t this_msecs, on_msecs, total_msecs; 70 uint32_t count; 71 72 VBDEBUG(("VbGetDevMusicNotes: use_short is %d, hdr is %p, " 73 "maxsize is %d\n", use_short, hdr, maxsize)); 74 75 if (use_short) { 76 builtin = short_notes_; 77 count = short_count_; 78 goto nope; 79 } 80 81 builtin = default_notes_; 82 count = default_count_; 83 84 /* If we can't beep in the background, don't allow customization. */ 85 if (!audio->background_beep) 86 goto nope; 87 88 if (!hdr || maxsize < sizeof(VbDevMusic)) 89 goto nope; 90 91 if (0 != Memcmp(hdr->sig, "$SND", sizeof(hdr->sig))) { 92 VBDEBUG(("VbGetDevMusicNotes: bad sig\n")); 93 goto nope; 94 } 95 96 /* 97 * How many notes will fit in the flash region? One more than you'd 98 * think, because there's one note in the header itself. 99 */ 100 maxnotes = 1 + (maxsize - sizeof(VbDevMusic)) / sizeof(VbDevMusicNote); 101 if (hdr->count == 0 || hdr->count > maxnotes) { 102 VBDEBUG(("VbGetDevMusicNotes: count=%d maxnotes=%d\n", 103 hdr->count, maxnotes)); 104 goto nope; 105 } 106 107 /* 108 * CUSTOM_MUSIC_MAXSIZE can't be larger than the size of the flash 109 * (around 8M or so) so this isn't really necessary, but let's be safe 110 * anyway. 111 */ 112 if ((sizeof(VbDevMusicNote) > UINT_MAX / hdr->count) || 113 (sizeof(hdr->count) > 114 UINT_MAX - hdr->count * sizeof(VbDevMusicNote))) { 115 VBDEBUG(("VbGetDevMusicNotes: count=%d, just isn't right\n", 116 hdr->count)); 117 goto nope; 118 } 119 120 /* Now we know this won't overflow */ 121 mylen = (uint32_t)(sizeof(hdr->count) + 122 hdr->count * sizeof(VbDevMusicNote)); 123 mysum = Crc32(&(hdr->count), mylen); 124 125 if (mysum != hdr->checksum) { 126 VBDEBUG(("VbGetDevMusicNotes: mysum=%08x, want=%08x\n", 127 mysum, hdr->checksum)); 128 goto nope; 129 } 130 131 VBDEBUG(("VbGetDevMusicNotes: custom notes struct at %p\n", hdr)); 132 133 /* 134 * Measure the audible sound up to the first 22 seconds, being careful 135 * to avoid rollover. The note time is 16 bits, and the note count is 136 * 32 bits. The product should fit in 64 bits. 137 */ 138 total_msecs = 0; 139 on_msecs = 0; 140 for (i=0; i < hdr->count; i++) { 141 this_msecs = hdr->notes[i].msec ; 142 if (this_msecs) { 143 total_msecs += this_msecs; 144 if (total_msecs <= REQUIRED_NOISE_WITHIN && 145 hdr->notes[i].frequency >= 100 && 146 hdr->notes[i].frequency <= 2000) 147 on_msecs += this_msecs; 148 } 149 } 150 151 /* We require at least one second of noise in the first 22 seconds */ 152 VBDEBUG(("VbGetDevMusicNotes: with %d msecs of sound to begin\n", 153 on_msecs)); 154 if (on_msecs < REQUIRED_NOISE_TIME) 155 goto nope; 156 157 /* 158 * We'll also require that the total time be less than a minute. No 159 * real reason, it just gives us less to worry about. 160 */ 161 VBDEBUG(("VbGetDevMusicNotes: lasting %d msecs\n", total_msecs)); 162 if (total_msecs > MAX_CUSTOM_DELAY) { 163 goto nope; 164 } 165 166 /* One more check, just to be paranoid. */ 167 if (hdr->count > (UINT_MAX / sizeof(VbDevMusicNote) - 1)) { 168 VBDEBUG(("VbGetDevMusicNotes: they're all out to get me!\n")); 169 goto nope; 170 } 171 172 /* Looks good. Allocate the space (plus one) and copy it over. */ 173 notebuf = VbExMalloc((hdr->count + 1) * sizeof(VbDevMusicNote)); 174 Memcpy(notebuf, hdr->notes, hdr->count * sizeof(VbDevMusicNote)); 175 count = hdr->count; 176 177 /* We also require at least 30 seconds of delay. */ 178 if (total_msecs < REQUIRED_TOTAL_DELAY) { 179 /* 180 * If the total time is less than 30 seconds, the needed 181 * difference will fit in 16 bits. 182 */ 183 this_msecs = (REQUIRED_TOTAL_DELAY - total_msecs) & 0xffff; 184 notebuf[hdr->count].msec = this_msecs; 185 notebuf[hdr->count].frequency = 0; 186 count++; 187 VBDEBUG(("VbGetDevMusicNotes: adding %d msecs of silence\n", 188 this_msecs)); 189 } 190 191 /* Done */ 192 audio->music_notes = notebuf; 193 audio->note_count = count; 194 audio->free_notes_when_done = 1; 195 return; 196 197 nope: 198 /* No custom notes, use the default. The count is already set. */ 199 VBDEBUG(("VbGetDevMusicNotes: using %d default notes\n", count)); 200 audio->music_notes = builtin; 201 audio->note_count = count; 202 audio->free_notes_when_done = 0; 203 } 204 205 206 /** 207 * Initialization function. Returns context for processing dev-mode delay. 208 */ 209 VbAudioContext *VbAudioOpen(VbCommonParams *cparams) 210 { 211 GoogleBinaryBlockHeader *gbb = cparams->gbb; 212 VbAudioContext *audio = &au; 213 int use_short = 0; 214 uint64_t a, b; 215 216 /* Note: may need to allocate things here in future */ 217 218 /* Calibrate audio delay */ 219 a = VbExGetTimer(); 220 VbExSleepMs(10); 221 b = VbExGetTimer(); 222 ticks_per_msec = (b - a) / 10ULL ; 223 VBDEBUG(("VbAudioOpen() - ticks_per_msec is %" PRIu64 "\n", 224 ticks_per_msec)); 225 226 /* Initialize */ 227 Memset(audio, 0, sizeof(*audio)); 228 audio->background_beep = 1; 229 audio->play_until = b; /* "zero" starts now */ 230 231 /* See if we have full background sound capability or not. */ 232 if (VBERROR_SUCCESS != VbExBeep(0,0)) { 233 VBDEBUG(("VbAudioOpen() - VbExBeep() is limited\n")); 234 audio->background_beep = 0; 235 } 236 237 /* 238 * Prepare to generate audio/delay event. Use a short developer screen 239 * delay if indicated by GBB flags. 240 */ 241 if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1 242 && (gbb->flags & GBB_FLAG_DEV_SCREEN_SHORT_DELAY)) { 243 VBDEBUG(("VbAudioOpen() - using short dev screen delay\n")); 244 use_short = 1; 245 } 246 247 VbGetDevMusicNotes(audio, use_short); 248 VBDEBUG(("VbAudioOpen() - note count %d\n", audio->note_count)); 249 250 return audio; 251 } 252 253 /** 254 * Caller should loop without extra delay until this returns false. 255 */ 256 int VbAudioLooping(VbAudioContext *audio) 257 { 258 uint64_t now; 259 uint16_t freq = audio->current_frequency; 260 uint16_t msec = 0; 261 int looping = 1; 262 263 now = VbExGetTimer(); 264 while (audio->next_note < audio->note_count && 265 now >= audio->play_until) { 266 freq = audio->music_notes[audio->next_note].frequency; 267 msec = audio->music_notes[audio->next_note].msec; 268 audio->play_until += VbMsecToTicks(msec); 269 audio->next_note++; 270 } 271 272 if (now >= audio->play_until) { 273 looping = 0; 274 freq = 0; 275 } 276 277 /* Do action here. */ 278 if (audio->background_beep) { 279 if (audio->current_frequency != freq) { 280 VbExBeep(0, freq); 281 audio->current_frequency = freq; 282 } 283 } else if (freq && msec) { 284 VbExBeep(msec, freq); 285 now = VbExGetTimer(); 286 } 287 288 audio->last_time = now; 289 return looping; 290 } 291 292 /** 293 * Caller should call this prior to booting. 294 */ 295 void VbAudioClose(VbAudioContext *audio) 296 { 297 VbExBeep(0,0); 298 if (audio->free_notes_when_done) 299 VbExFree(audio->music_notes); 300 } 301