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 */
33VbDevMusicNote default_notes_[] = { {20000, 0}, /* 20 seconds */
34                                    {250, 400}, /* two beeps */
35                                    {250, 0},
36                                    {250, 400},
37                                    {9250, 0} }; /* total 30 seconds */
38uint32_t default_count_ = sizeof(default_notes_) / sizeof(VbDevMusicNote);
39
40VbDevMusicNote short_notes_[] = { {2000, 0} }; /* two seconds */
41uint32_t short_count_ = sizeof(short_notes_) / sizeof(VbDevMusicNote);
42
43/* No need to dynamically allocate this, is there? */
44static VbAudioContext au;
45
46/* Convert from msecs to VbExGetTimer() units. */
47static uint64_t ticks_per_msec = 0;     /* Initialized by VbAudioOpen() */
48static 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 */
62static 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 */
209VbAudioContext *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 */
256int 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 */
295void VbAudioClose(VbAudioContext *audio)
296{
297	VbExBeep(0,0);
298	if (audio->free_notes_when_done)
299		VbExFree(audio->music_notes);
300}
301