Enable (and test) custom dev-mode sounds

BUG=none
TEST=manual

  cd src/platform/vboot_reference
  make && make runtests

Change-Id: I7f7d50d7c9c5541e0b99031245f882996a6b88ec
Reviewed-on: http://gerrit.chromium.org/gerrit/8731
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Tested-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
Bill Richardson
2011-10-04 08:36:09 -07:00
parent 253a58e383
commit 94d7034590
8 changed files with 462 additions and 63 deletions

View File

@@ -2,7 +2,7 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Display functions used in kernel selection.
* Delay/beep functions used in dev-mode kernel selection.
*/
#ifndef VBOOT_REFERENCE_VBOOT_AUDIO_H_

View File

@@ -0,0 +1,47 @@
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Private declarations for vboot_audio.c. Defined here for easier testing.
*/
#ifndef VBOOT_REFERENCE_VBOOT_AUDIO_PRIVATE_H_
#define VBOOT_REFERENCE_VBOOT_AUDIO_PRIVATE_H_
#include "vboot_api.h"
#include "vboot_audio.h"
typedef struct VbDevMusicNote {
uint16_t msec;
uint16_t frequency;
} __attribute__((packed)) VbDevMusicNote;
typedef struct VbDevMusic {
uint8_t sig[4]; /* "$SND" */
uint32_t checksum; /* crc32 over count & all notes */
uint32_t count; /* number of notes */
VbDevMusicNote notes[1]; /* gcc allows [0], MSVC doesn't */
/* more VbDevMusicNotes follow immediately */
} __attribute__((packed)) VbDevMusic;
struct VbAudioContext {
uint32_t note_count;
VbDevMusicNote* music_notes;
int free_notes_when_done;
uint32_t current_note;
uint32_t current_note_loops;
int background_beep;
};
#ifdef CUSTOM_MUSIC
void *VbExGetMusicPtr(void);
uint32_t VbExMaxMusicSize(void);
#define CUSTOM_MUSIC_NOTES VbExGetMusicPtr()
#define CUSTOM_MUSIC_MAXSIZE VbExMaxMusicSize()
#else
#define CUSTOM_MUSIC_NOTES 0
#define CUSTOM_MUSIC_MAXSIZE 0
#endif
#endif /* VBOOT_REFERENCE_VBOOT_AUDIO_PRIVATE_H_ */

View File

@@ -2,63 +2,163 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Audio functions used in dev-mode kernel selection.
* Delay/beep functions used in dev-mode kernel selection.
*/
#include "crc32.h"
#include "gbb_header.h"
#include "utility.h"
#include "vboot_api.h"
#include "vboot_audio.h"
#include "vboot_audio_private.h"
#include "vboot_common.h"
typedef struct VbDevMusicNote {
uint16_t msec;
uint16_t frequency;
} __attribute__((packed)) VbDevMusicNote;
typedef struct VbDevMusic {
uint8_t sig[4]; /* "$SND" */
uint32_t checksum; /* crc32 over count & all notes */
uint32_t count; /* number of notes */
VbDevMusicNote notes[1]; /* gcc allows [0], MSVC doesn't */
/* more VbDevMusicNotes follow immediately */
} __attribute__((packed)) VbDevMusic;
static struct VbAudioContext {
uint32_t note_count;
VbDevMusicNote* music_notes;
uint32_t current_note;
uint32_t current_note_loops;
int background_beep;
} au;
#define DEV_LOOP_TIME 10 /* Minimum note granularity in msecs */
static uint16_t VbMsecToLoops(uint16_t msec) {
return (DEV_LOOP_TIME / 2 + msec) / DEV_LOOP_TIME;
}
static VbDevMusicNote default_notes[] = { {20000, 0}, /* 20 seconds */
/* These are visible externally only to make testing easier */
VbDevMusicNote default_notes_[] = { {20000, 0}, /* 20 seconds */
{250, 400}, /* two beeps */
{250, 0},
{250, 400},
{9250, 0} }; /* total 30 seconds */
uint32_t default_count_ = sizeof(default_notes_) / sizeof(VbDevMusicNote);
static VbDevMusicNote short_notes[] = { {2000, 0} }; /* two seconds */
VbDevMusicNote short_notes_[] = { {2000, 0} }; /* two seconds */
uint32_t short_count_ = sizeof(short_notes_) / sizeof(VbDevMusicNote);
/* Return a valid set of note events. */
static VbDevMusicNote* VbGetDevMusicNotes(uint32_t* count, int use_short) {
/* No need to dynamically allocate this, is there? */
static VbAudioContext au;
if (use_short) {
*count = sizeof(short_notes) / sizeof(short_notes[0]);
return short_notes;
/* Arg is 16-bit, but use 32-bit to avoid rollover */
static uint32_t VbMsecToLoops(uint32_t msec) {
return (DEV_LOOP_TIME / 2 + msec) / DEV_LOOP_TIME;
}
*count = sizeof(default_notes) / sizeof(default_notes[0]);
return default_notes;
/* Find and return a valid set of note events. We'll use the user's struct
* if possible, but we will still enforce the 30-second timeout and require at
* least a second of audible noise within that period. We allocate storage for
* two reasons: the user's struct will be in flash, which is slow to read, and
* we may need one extra note at the end to pad out the user's notes to a full
* 30 seconds. The caller should free it when finished.
*/
static void VbGetDevMusicNotes(VbAudioContext *audio, int use_short) {
VbDevMusicNote *notebuf = 0;
VbDevMusicNote *builtin = 0;
VbDevMusic *hdr = CUSTOM_MUSIC_NOTES;
uint32_t maxsize = CUSTOM_MUSIC_MAXSIZE;
uint32_t maxnotes, mysum, mylen, i;
uint64_t on_loops, total_loops, min_loops;
uint32_t this_loops;
uint32_t count;
VBDEBUG(("VbGetDevMusicNotes: use_short is %d, hdr is %lx, maxsize is %d\n",
use_short, hdr, maxsize));
if (use_short) {
builtin = short_notes_;
count = short_count_;
goto nope;
}
builtin = default_notes_;
count = default_count_;
/* If we can't beep in the background, don't allow customization. */
if (!audio->background_beep)
goto nope;
if (!hdr || maxsize < sizeof(VbDevMusic))
goto nope;
if (0 != Memcmp(hdr->sig, "$SND", sizeof(hdr->sig))) {
VBDEBUG(("VbGetDevMusicNotes: bad sig\n"));
goto nope;
}
maxnotes = 1 + (maxsize - sizeof(VbDevMusic)) / sizeof(VbDevMusicNote);
if (hdr->count == 0 || hdr->count > maxnotes) {
VBDEBUG(("VbGetDevMusicNotes: count=%d maxnotes=%d\n",
hdr->count, maxnotes));
goto nope;
}
mylen = (uint32_t)(sizeof(hdr->count) + hdr->count * sizeof(VbDevMusicNote));
mysum = Crc32(&(hdr->count), mylen);
if (mysum != hdr->checksum) {
VBDEBUG(("VbGetDevMusicNotes: mysum=%08x, want=%08x\n",
mysum, hdr->checksum));
goto nope;
}
VBDEBUG(("VbGetDevMusicNotes: custom notes struct found at %lx\n", hdr));
/* Measure the audible sound up to the first 22 seconds, being careful to
* avoid rollover. The note time is 16 bits, and the note count is 32 bits.
* The product should fit in 64 bits.
*/
total_loops = 0;
on_loops = 0;
min_loops = VbMsecToLoops(22000);
for (i=0; i < hdr->count; i++) {
this_loops = VbMsecToLoops(hdr->notes[i].msec);
if (this_loops) {
total_loops += this_loops;
if (total_loops <= min_loops &&
hdr->notes[i].frequency >= 100 && hdr->notes[i].frequency <= 2000)
on_loops += this_loops;
}
}
/* We require at least one second of noise in the first 22 seconds */
VBDEBUG(("VbGetDevMusicNotes: with %ld msecs of sound to begin\n",
on_loops * DEV_LOOP_TIME));
if (on_loops < VbMsecToLoops(1000)) {
goto nope;
}
/* We'll also require that the total time be less than a minute. No real
* reason, it just gives us less to worry about.
*/
VBDEBUG(("VbGetDevMusicNotes: lasting %ld msecs\n",
total_loops * DEV_LOOP_TIME));
if (total_loops > VbMsecToLoops(60000)) {
goto nope;
}
/* Okay, it looks good. Allocate the space (plus one) and copy it over */
notebuf = VbExMalloc((hdr->count + 1) * sizeof(VbDevMusicNote));
Memcpy(notebuf, hdr->notes, hdr->count * sizeof(VbDevMusicNote));
count = hdr->count;
/* We also require at least 30 seconds of delay. */
min_loops = VbMsecToLoops(30000);
if (total_loops < min_loops) {
/* If the total time is less than 30 seconds, the needed difference will
* fit in 16 bits.
*/
this_loops = (min_loops - total_loops) & 0xffff;
notebuf[hdr->count].msec = (uint16_t)(this_loops * DEV_LOOP_TIME);
notebuf[hdr->count].frequency = 0;
count++;
VBDEBUG(("VbGetDevMusicNotes: adding %ld msecs of silence\n",
this_loops * DEV_LOOP_TIME));
}
/* done */
audio->music_notes = notebuf;
audio->note_count = count;
audio->free_notes_when_done = 1;
return;
nope:
/* No custom notes, use the default. The count is already set. */
VBDEBUG(("VbGetDevMusicNotes: using %d default notes\n", count));
audio->music_notes = builtin;
audio->note_count = count;
audio->free_notes_when_done = 0;
}
@@ -66,6 +166,7 @@ static VbDevMusicNote* VbGetDevMusicNotes(uint32_t* count, int use_short) {
VbAudioContext* VbAudioOpen(VbCommonParams* cparams) {
GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data;
VbAudioContext* audio = &au;
int use_short = 0;
/* Note: may need to allocate things here in future */
@@ -76,7 +177,6 @@ VbAudioContext* VbAudioOpen(VbCommonParams* cparams) {
audio->current_note_loops = 0;
audio->background_beep = 1;
/* See if we have full background sound capability or not. */
if (VBERROR_SUCCESS != VbExBeep(0,0)) {
VBDEBUG(("VbAudioOpen() - VbExBeep() is limited\n"));
@@ -89,11 +189,10 @@ VbAudioContext* VbAudioOpen(VbCommonParams* cparams) {
if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1
&& (gbb->flags & GBB_FLAG_DEV_SCREEN_SHORT_DELAY)) {
VBDEBUG(("VbAudioOpen() - using short developer screen delay\n"));
audio->music_notes = VbGetDevMusicNotes(&(audio->note_count), 1);
} else {
audio->music_notes = VbGetDevMusicNotes(&(audio->note_count), 0);
use_short = 1;
}
VbGetDevMusicNotes(audio, use_short);
VBDEBUG(("VbAudioOpen() - note count %d\n", audio->note_count));
return audio;
@@ -144,8 +243,7 @@ int VbAudioLooping(VbAudioContext* audio) {
/* Caller should call this prior to booting */
void VbAudioClose(VbAudioContext* audio) {
VbExBeep(0,0);
/* Note: Free any allocated structs here */
if (audio->free_notes_when_done)
VbExFree(audio->music_notes);
}

View File

@@ -28,7 +28,8 @@ TEST_NAMES = cgptlib_test \
vboot_common3_tests \
vboot_firmware_tests \
vboot_nvstorage_test \
vboot_api_devmode_tests
vboot_api_devmode_tests \
vboot_audio_tests
TEST_BINS = $(addprefix ${BUILD_ROOT}/,$(TEST_NAMES))
@@ -53,14 +54,13 @@ ${TEST_LIB}: ${TEST_LIB_OBJS}
rm -f $@
ar qc $@ $^
${BUILD_ROOT}/vboot_api_kernel_for_test.o : $(FWDIR)/lib/vboot_api_kernel.c
$(CC) $(CFLAGS) $(INCLUDES) \
${BUILD_ROOT}/vboot_audio_for_test.o : $(FWDIR)/lib/vboot_audio.c
$(CC) $(CFLAGS) -DCUSTOM_MUSIC $(INCLUDES) \
-MMD -MF $@.d -c -o $@ $<
${BUILD_ROOT}/vboot_api_devmode_tests: vboot_api_devmode_tests.c \
${BUILD_ROOT}/vboot_api_kernel_for_test.o ${LIBS}
$(CC) $(CFLAGS) $(INCLUDES) $< \
${BUILD_ROOT}/vboot_api_kernel_for_test.o \
${BUILD_ROOT}/vboot_audio_tests: vboot_audio_tests.c \
${BUILD_ROOT}/vboot_audio_for_test.o ${LIBS}
$(CC) $(CFLAGS) $(INCLUDES) $< ${BUILD_ROOT}/vboot_audio_for_test.o \
${LIBS} -o $@ -lcrypto -lrt
${BUILD_ROOT}/rollback_index_test.o : rollback_index_test.c
@@ -142,6 +142,7 @@ runbmptests:
runsoundtests:
${BUILD_ROOT}/vboot_api_devmode_tests
${BUILD_ROOT}/vboot_audio_tests
ALLTESTS=runcgpttests runcryptotests runmisctests runfuzztests \
runbmptests runsoundtests

View File

@@ -89,3 +89,25 @@ int TEST_STR_EQ(const char* result, const char* expected_result,
}
}
int TEST_TRUE(int result, const char* testname) {
if (result) {
fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname);
} else {
fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname);
fprintf(stderr, " Expected TRUE, got 0\n");
gTestSuccess = 0;
}
return result;
}
int TEST_FALSE(int result, const char* testname) {
if (!result) {
fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname);
} else {
fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname);
fprintf(stderr, " Expected FALSE, got: 0x%lx\n", (long)result);
gTestSuccess = 0;
}
return !result;
}

View File

@@ -34,6 +34,14 @@ int TEST_PTR_NEQ(const void* result, const void* expected_result,
int TEST_STR_EQ(const char* result, const char* expected_result,
const char* testname);
/* Return 1 if the result is true, else return 0.
* Also update the global gTestSuccess flag if test fails. */
int TEST_TRUE(int result, const char* testname);
/* Return 1 if the result is false, else return 0.
* Also update the global gTestSuccess flag if test fails. */
int TEST_FALSE(int result, const char* testname);
/* ANSI Color coding sequences.
*
* Don't use \e as MSC does not recognize it as a valid escape sequence.

View File

@@ -5,20 +5,20 @@
* Tests for vboot_api_firmware
*/
#include <stdio.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "crc32.h"
#include "gbb_header.h"
#include "host_common.h"
#include "load_kernel_fw.h"
#include "rollback_index.h"
#include "test_common.h"
#include "vboot_common.h"
#include "vboot_display.h"
#include "vboot_nvstorage.h"
#include "vboot_struct.h"
#include "load_kernel_fw.h"
#include "gbb_header.h"
#include "vboot_display.h"
/* Expected results */
@@ -128,12 +128,8 @@ test_case_t test[] = {
{0, 0, 30360}, // returns at 30 seconds + 360ms
}},
};
/* Mock data */
static VbCommonParams cparams;
static LoadKernelParams lkparams;
@@ -150,7 +146,6 @@ static uint32_t kbd_fire_key;
static VbError_t beep_return;
static note_event_t *expected_event;
/* Reset mock data (for use before each test) */
static void ResetMocks(void) {
@@ -282,7 +277,6 @@ VbError_t VbExDisplayScreen(uint32_t screen_type) {
return VBERROR_SUCCESS;
}
/****************************************************************************/
VbError_t VbBootDeveloper(VbCommonParams* cparams, LoadKernelParams* p);
@@ -304,8 +298,8 @@ static void VbBootDeveloperSoundTest(void) {
(void) VbBootDeveloper(&cparams, &lkparams);
VBDEBUG(("INFO: matched %d total %d expected %d\n",
matched_events, current_event, test[i].num_events));
TEST_EQ(matched_events, test[i].num_events, test[i].name);
TEST_EQ(current_event, test[i].num_events, test[i].name);
TEST_TRUE(matched_events == test[i].num_events &&
current_event == test[i].num_events, test[i].name);
}
}

229
tests/vboot_audio_tests.c Normal file
View File

@@ -0,0 +1,229 @@
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Tests for vboot_audio
*/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "crc32.h"
#include "gbb_header.h"
#include "host_common.h"
#include "load_kernel_fw.h"
#include "rollback_index.h"
#include "test_common.h"
#include "vboot_audio.h"
#include "vboot_audio_private.h"
#include "vboot_common.h"
#include "vboot_display.h"
#include "vboot_nvstorage.h"
#include "vboot_struct.h"
/* Builtin notes */
extern VbDevMusicNote default_notes_[], short_notes_[];
extern uint32_t default_count_, short_count_;
/* Mock data */
static VbCommonParams cparams;
static GoogleBinaryBlockHeader gbb;
static VbDevMusicNote good_notes[] = { {100, 100},
{100, 0},
{200, 200},
{100, 0},
{300, 300},
{100, 0},
{400, 400},
{30000, 0} };
static VbDevMusic good_header =
{ .sig = { '$', 'S', 'N', 'D' },
.count = sizeof(good_notes) / sizeof(VbDevMusicNote),
};
static uint8_t notebuf[sizeof(good_header) +
sizeof(good_notes) - sizeof(VbDevMusicNote)];
static VbDevMusic *use_hdr;
static VbDevMusicNote *use_notes;
static uint32_t use_size;
/* Set correct checksum for custom notes */
void FixChecksum(VbDevMusic *hdr) {
hdr->checksum = Crc32(&(hdr->count), sizeof(hdr->count) +
hdr->count * sizeof(hdr->notes[0]));
}
/* Reset mock data (for use before each test) */
static void ResetMocks(void) {
VBDEBUG(("ResetMocks()\n"));
Memset(&cparams, 0, sizeof(cparams));
cparams.gbb_data = &gbb;
Memset(&gbb, 0, sizeof(gbb));
gbb.major_version = GBB_MAJOR_VER;
gbb.minor_version = GBB_MINOR_VER;
gbb.flags = 0;
use_hdr = (VbDevMusic *)notebuf;
use_notes = use_hdr->notes;
Memcpy(use_hdr, &good_header, sizeof(good_header));
Memcpy(use_notes, good_notes, sizeof(good_notes));
FixChecksum(use_hdr);
use_size = sizeof(notebuf);
}
/* Compare two sets of notes */
static int NotesMatch(VbDevMusicNote *a, VbDevMusicNote *b, uint32_t count) {
int i;
if (!a || !b)
return 0;
for ( i=0; i<count; i++) {
if ( a[i].msec != b[i].msec || a[i].frequency != b[i].frequency)
return 0;
}
return count;
}
/****************************************************************************/
/* Mocked verification functions */
void *VbExGetMusicPtr(void) {
return use_hdr;
}
uint32_t VbExMaxMusicSize(void) {
return use_size;
}
/****************************************************************************/
static void VbAudioTest(void) {
VbAudioContext* a = 0;
/* default is okay */
ResetMocks();
use_hdr = 0;
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == default_notes_ &&
a->note_count == default_count_,
"VbAudioTest( default )");
VbAudioClose(a);
/* short is okay */
ResetMocks();
use_hdr = 0;
gbb.flags = 0x00000001;
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == short_notes_ &&
a->note_count == short_count_,
"VbAudioTest( short )");
VbAudioClose(a);
/* good custom is okay */
ResetMocks();
a = VbAudioOpen(&cparams);
TEST_TRUE(NotesMatch(a->music_notes, good_notes, good_header.count) &&
a->note_count == good_header.count,
"VbAudioTest( custom good )");
VbAudioClose(a);
/* good custom is rejected when short flag is set */
ResetMocks();
gbb.flags = 0x00000001;
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == short_notes_ &&
a->note_count == short_count_,
"VbAudioTest( short has priority )");
VbAudioClose(a);
/* too short gets extended */
ResetMocks();
use_hdr->count--;
FixChecksum(use_hdr);
a = VbAudioOpen(&cparams);
TEST_TRUE(NotesMatch(a->music_notes, use_notes, use_hdr->count) &&
a->note_count == use_hdr->count + 1 &&
a->music_notes[use_hdr->count].msec == 28700 &&
a->music_notes[use_hdr->count].frequency == 0,
"VbAudioTest( too short )");
VbAudioClose(a);
/* too quiet is rejected */
ResetMocks();
use_notes[6].msec = 10;
FixChecksum(use_hdr);
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == default_notes_ &&
a->note_count == default_count_,
"VbAudioTest( too quiet )");
VbAudioClose(a);
/* inaudible is rejected */
ResetMocks();
use_notes[0].frequency = 99;
use_notes[2].frequency = 2001;
FixChecksum(use_hdr);
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == default_notes_ &&
a->note_count == default_count_,
"VbAudioTest( inaudible )");
VbAudioClose(a);
/* bad signature is rejected */
ResetMocks();
use_hdr->sig[0] = 'C';
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == default_notes_ &&
a->note_count == default_count_,
"VbAudioTest( bad signature )");
VbAudioClose(a);
/* count == 0 is rejected */
ResetMocks();
use_hdr->count = 0;
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == default_notes_ &&
a->note_count == default_count_,
"VbAudioTest( count == 0 )");
VbAudioClose(a);
/* too big is rejected */
ResetMocks();
use_hdr->count = 999;
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == default_notes_ &&
a->note_count == default_count_,
"VbAudioTest( count too big )");
VbAudioClose(a);
/* bad checksum is rejected */
ResetMocks();
use_hdr->checksum++;
a = VbAudioOpen(&cparams);
TEST_TRUE(a->music_notes == default_notes_ &&
a->note_count == default_count_,
"VbAudioTest( count too big )");
VbAudioClose(a);
}
/* disable MSVC warnings on unused arguments */
__pragma(warning (disable: 4100))
int main(int argc, char* argv[]) {
int error_code = 0;
VbAudioTest();
if (!gTestSuccess)
error_code = 255;
return error_code;
}