Home | History | Annotate | Download | only in lib
      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