187 lines
3.6 KiB
C
187 lines
3.6 KiB
C
/*****************************************************************************
|
|
* Boot menu: Displays boot devices and waits for user to select one
|
|
*
|
|
* Copyright 2017 Red Hat, Inc.
|
|
*
|
|
* This program and the accompanying materials
|
|
* are made available under the terms of the BSD License
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.opensource.org/licenses/bsd-license.php
|
|
*
|
|
* Contributors:
|
|
* Thomas Huth, Red Hat Inc. - initial implementation
|
|
*****************************************************************************/
|
|
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <paflof.h>
|
|
#include <helpers.h>
|
|
#include "bootmenu.h"
|
|
|
|
#define MAX_DEVS 36 /* Enough for 10 digits + 26 letters */
|
|
#define MAX_ALIAS_LEN 8 /* Maximum length of alias names */
|
|
|
|
struct bootdev {
|
|
char alias[MAX_ALIAS_LEN];
|
|
char *path;
|
|
};
|
|
|
|
static int nr_devs;
|
|
static struct bootdev bootdevs[MAX_DEVS];
|
|
|
|
/**
|
|
* Look up an alias name.
|
|
* @return The NUL-terminated device tree path (should be released with free()
|
|
* when it's not required anymore), or NULL if it can't be found.
|
|
*/
|
|
static char *find_alias(char *alias)
|
|
{
|
|
char *path;
|
|
long len;
|
|
|
|
forth_push((unsigned long)alias);
|
|
forth_push(strlen(alias));
|
|
forth_eval("find-alias");
|
|
|
|
len = forth_pop();
|
|
if (!len)
|
|
return NULL;
|
|
|
|
path = malloc(len + 1);
|
|
if (!path) {
|
|
puts("Out of memory in find_alias");
|
|
return NULL;
|
|
}
|
|
memcpy(path, (void *)forth_pop(), len);
|
|
path[len] = '\0';
|
|
|
|
return path;
|
|
}
|
|
|
|
static void bootmenu_populate_devs_alias(const char *alias)
|
|
{
|
|
int idx;
|
|
|
|
for (idx = 0; idx <= 9 && nr_devs < MAX_DEVS; idx++, nr_devs++) {
|
|
char *cur_alias = bootdevs[nr_devs].alias;
|
|
if (idx == 0)
|
|
strcpy(cur_alias, alias);
|
|
else
|
|
sprintf(cur_alias, "%s%i", alias, idx);
|
|
bootdevs[nr_devs].path = find_alias(cur_alias);
|
|
if (!bootdevs[nr_devs].path)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void bootmenu_populate_devs(void)
|
|
{
|
|
bootmenu_populate_devs_alias("cdrom");
|
|
bootmenu_populate_devs_alias("disk");
|
|
bootmenu_populate_devs_alias("net");
|
|
}
|
|
|
|
static void bootmenu_free_devs(void)
|
|
{
|
|
while (nr_devs-- > 0) {
|
|
free(bootdevs[nr_devs].path);
|
|
bootdevs[nr_devs].path = NULL;
|
|
}
|
|
}
|
|
|
|
static void bootmenu_show_devs(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nr_devs; i++) {
|
|
printf("%c) %6s : %s\n", i < 9 ? '1' + i : 'a' + i - 9,
|
|
bootdevs[i].alias, bootdevs[i].path);
|
|
}
|
|
}
|
|
|
|
static bool has_key(void)
|
|
{
|
|
forth_eval("key?");
|
|
return forth_pop();
|
|
}
|
|
|
|
static char get_key(void)
|
|
{
|
|
forth_eval("key");
|
|
return forth_pop();
|
|
}
|
|
|
|
/* Flush pending key presses */
|
|
static void flush_keys(void)
|
|
{
|
|
uint32_t start;
|
|
|
|
start = SLOF_GetTimer();
|
|
while (SLOF_GetTimer() - start < 10) {
|
|
if (has_key()) {
|
|
get_key();
|
|
start = SLOF_GetTimer();
|
|
}
|
|
}
|
|
}
|
|
|
|
static int bootmenu_get_selection(void)
|
|
{
|
|
char key = 0;
|
|
int sel;
|
|
|
|
do {
|
|
sel = -1;
|
|
if (!has_key())
|
|
continue;
|
|
key = get_key();
|
|
switch (key) {
|
|
case '0':
|
|
return -1;
|
|
case '1' ... '9':
|
|
sel = key - '1';
|
|
break;
|
|
case 'a' ... 'z':
|
|
sel = key - 'a' + 9;
|
|
break;
|
|
case 'A' ... 'Z':
|
|
sel = key - 'A' + 9;
|
|
break;
|
|
default:
|
|
/* Might be another escape code (F12) ... skip it */
|
|
flush_keys();
|
|
break;
|
|
}
|
|
} while (sel < 0 || sel >= nr_devs);
|
|
|
|
return sel;
|
|
}
|
|
|
|
void bootmenu(void)
|
|
{
|
|
int sel;
|
|
|
|
bootmenu_populate_devs();
|
|
if (!nr_devs) {
|
|
puts("No available boot devices!");
|
|
return;
|
|
}
|
|
|
|
puts("\nSelect boot device (or press '0' to abort):");
|
|
bootmenu_show_devs();
|
|
|
|
if (has_key()) /* In case the user hammered on F12 */
|
|
flush_keys();
|
|
|
|
sel = bootmenu_get_selection();
|
|
if (sel < 0) {
|
|
forth_push(0);
|
|
} else {
|
|
forth_push((unsigned long)bootdevs[sel].alias);
|
|
forth_push(strlen(bootdevs[sel].alias));
|
|
}
|
|
|
|
bootmenu_free_devs();
|
|
}
|