154 changes: 108 additions & 46 deletions src/boot.c
Expand Up @@ -103,6 +103,33 @@ find_prio(const char *glob)
return -1;
}

// search for 'ipxe enable' bit value
int find_pxen(void)
{
int i = 0;
for (i=0; i < BootorderCount; i++)
{
if (glob_prefix("pxen0", Bootorder[i]))
return 0;
if (glob_prefix("pxen1", Bootorder[i]))
return 1;
}
return -1;
}

// search for 'boot from usb' bit value
// if it doesn't exist - set to enabled
static int find_usben(void)
{
int i;
for (i=0; i < BootorderCount; i++)
{
if (glob_prefix("usben0", Bootorder[i]))
return 0;
}
return 1;
}

#define FW_PCI_DOMAIN "/pci@i0cf8"

static char *
Expand Down Expand Up @@ -207,14 +234,21 @@ int bootprio_find_named_rom(const char *name, int instance)
return find_prio(desc);
}

static int usb_portmap(struct usbdevice_s *usbdev)
{
if (usbdev->hub->op->portmap)
return usbdev->hub->op->portmap(usbdev->hub, usbdev->port);
return usbdev->port + 1;
}

static char *
build_usb_path(char *buf, int max, struct usbhub_s *hub)
{
if (!hub->usbdev)
// Root hub - nothing to add.
return buf;
char *p = build_usb_path(buf, max, hub->usbdev->hub);
p += snprintf(p, buf+max-p, "/hub@%x", hub->usbdev->port+1);
p += snprintf(p, buf+max-p, "/hub@%x", usb_portmap(hub->usbdev));
return p;
}

Expand All @@ -227,12 +261,12 @@ int bootprio_find_usb(struct usbdevice_s *usbdev, int lun)
p = build_pci_path(desc, sizeof(desc), "usb", usbdev->hub->cntl->pci);
p = build_usb_path(p, desc+sizeof(desc)-p, usbdev->hub);
snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%x"
, usbdev->port+1, lun);
, usb_portmap(usbdev), lun);
int ret = find_prio(desc);
if (ret >= 0)
return ret;
// Try usb-host/redir - for example: /pci@i0cf8/usb@1,2/usb-host@1
snprintf(p, desc+sizeof(desc)-p, "/usb-*@%x", usbdev->port+1);
snprintf(p, desc+sizeof(desc)-p, "/usb-*@%x", usb_portmap(usbdev));
return find_prio(desc);
}

Expand Down Expand Up @@ -372,24 +406,26 @@ boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio)
}

void
boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio)
boot_add_floppy(struct drive_s *drive, const char *desc, int prio)
{
bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio)
, (u32)drive_g, desc);
, (u32)drive, desc);
}

void
boot_add_hd(struct drive_s *drive_g, const char *desc, int prio)
boot_add_hd(struct drive_s *drive, const char *desc, int prio)
{
bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
, (u32)drive_g, desc);
int usben = find_usben();
if ((glob_prefix("USB*", desc) == NULL) || usben == 1)
bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
, (u32)drive, desc);
}

void
boot_add_cd(struct drive_s *drive_g, const char *desc, int prio)
boot_add_cd(struct drive_s *drive, const char *desc, int prio)
{
bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio)
, (u32)drive_g, desc);
, (u32)drive, desc);
}

// Add a CBFS payload entry
Expand Down Expand Up @@ -454,6 +490,9 @@ interactive_bootmenu(void)
{
// XXX - show available drives?

int n_key = 0;
int pxen = find_pxen();

if (! CONFIG_BOOTMENU || !romfile_loadint("etc/show-boot-menu", 1))
return;

Expand All @@ -462,58 +501,81 @@ interactive_bootmenu(void)

char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL);
int menukey = romfile_loadint("etc/boot-menu-key", 1);
printf("%s", bootmsg ?: "\nPress ESC for boot menu.\n\n");

printf("\n%s", bootmsg && pxen == 1 ? bootmsg : "Press F10 key now for boot menu\n\n");
free(bootmsg);

u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT);
enable_bootsplash();
int scan_code = get_keystroke(menutime);
disable_bootsplash();
if (scan_code != menukey)

// 0x31 for N or n key
if (scan_code == 0x31 && pxen == 1)
n_key = 1;

if (scan_code != menukey && n_key != 1)
return;

while (get_keystroke(0) >= 0)
;

printf("Select boot device:\n\n");
wait_threads();

// Show menu items
int maxmenu = 0;
// choice = 1 - boot 1st device from list by default
int maxmenu = 0, choice = 1;
char find_iPXE[5];
struct bootentry_s *pos;
hlist_for_each_entry(pos, &BootList, node) {
char desc[60];
maxmenu++;
printf("%d. %s\n", maxmenu
, strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
}
if (tpm_can_show_menu()) {
printf("\nt. TPM Configuration\n");

// If N key is pressed, do not print boot menu
// and boot directly from iPXE
if (n_key) {
hlist_for_each_entry(pos, &BootList, node) {
maxmenu++;
strtcpy(find_iPXE, pos->description, 5);
if (strcmp(find_iPXE, "iPXE") == 0)
choice = maxmenu;
}
}
// Show menu items if menu-key is pressed
else {
printf("Select boot device:\n\n");
wait_threads();

// Show menu items
hlist_for_each_entry(pos, &BootList, node) {
char desc[77];
maxmenu++;
printf("%d. %s\n", maxmenu
, strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
}
if (tpm_can_show_menu()) {
printf("\nt. TPM Configuration\n");
}

// Get key press. If the menu key is ESC, do not restart boot unless
// 1.5 seconds have passed. Otherwise users (trained by years of
// repeatedly hitting keys to enter the BIOS) will end up hitting ESC
// multiple times and immediately booting the primary boot device.
int esc_accepted_time = irqtimer_calc(menukey == 1 ? 1500 : 0);
for (;;) {
scan_code = get_keystroke(1000);
if (scan_code == 1 && !irqtimer_check(esc_accepted_time))
continue;
if (tpm_can_show_menu() && scan_code == 20 /* t */) {
printf("\n");
tpm_menu();
// Get key press. If the menu key is ESC, do not restart boot unless
// 1.5 seconds have passed. Otherwise users (trained by years of
// repeatedly hitting keys to enter the BIOS) will end up hitting ESC
// multiple times and immediately booting the primary boot device.
int esc_accepted_time = irqtimer_calc(menukey == 1 ? 1500 : 0);
for (;;) {
scan_code = get_keystroke(1000);
if (scan_code == 1 && !irqtimer_check(esc_accepted_time))
continue;
if (tpm_can_show_menu() && scan_code == 20 /* t */) {
printf("\n");
tpm_menu();
}
if (scan_code >= 1 && scan_code <= maxmenu+1)
break;
}
if (scan_code >= 1 && scan_code <= maxmenu+1)
break;
printf("\n");
if (scan_code == 0x01)
// ESC
return;

choice = scan_code - 1;
}
printf("\n");
if (scan_code == 0x01)
// ESC
return;

// Find entry and make top priority.
int choice = scan_code - 1;
hlist_for_each_entry(pos, &BootList, node) {
if (! --choice)
break;
Expand Down Expand Up @@ -648,13 +710,13 @@ boot_disk(u8 bootdrv, int checksig)

// Boot from a CD-ROM
static void
boot_cdrom(struct drive_s *drive_g)
boot_cdrom(struct drive_s *drive)
{
if (! CONFIG_CDROM_BOOT)
return;
printf("Booting from DVD/CD...\n");

int status = cdrom_boot(drive_g);
int status = cdrom_boot(drive);
if (status) {
printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
return;
Expand Down
6 changes: 3 additions & 3 deletions src/cdrom.c
Expand Up @@ -31,7 +31,7 @@ cdemu_read(struct disk_op_s *op)
{
struct drive_s *drive_gf = GET_LOW(emulated_drive_gf);
struct disk_op_s dop;
dop.drive_gf = drive_gf;
dop.drive_fl = drive_gf;
dop.command = op->command;
dop.lba = GET_LOW(CDEmu.ilba) + op->lba / 4;

Expand Down Expand Up @@ -136,8 +136,8 @@ cdrom_boot(struct drive_s *drive)
struct disk_op_s dop;
int cdid = getDriveId(EXTTYPE_CD, drive);
memset(&dop, 0, sizeof(dop));
dop.drive_gf = drive;
if (!dop.drive_gf || cdid < 0)
dop.drive_fl = drive;
if (!dop.drive_fl || cdid < 0)
return 1;

int ret = scsi_is_ready(&dop);
Expand Down
1 change: 1 addition & 0 deletions src/clock.c
Expand Up @@ -295,6 +295,7 @@ clock_update(void)
floppy_tick();
usb_check_event();
ps2_check_event();
sercon_check_event();
}

// INT 08h System Timer ISR Entry Point
Expand Down
275 changes: 275 additions & 0 deletions src/cp437.c
@@ -0,0 +1,275 @@
/*
* code page 437 to unicode map
*/

#include "types.h"
#include "biosvar.h"
#include "cp437.h"

static VAR16 u16 cp437_to_unicode_map[256] = {

/* https://en.wikipedia.org/wiki/Code_page_437 */
[ 0x00 ] = 0x0000,
[ 0x01 ] = 0x263A,
[ 0x02 ] = 0x263B,
[ 0x03 ] = 0x2665,
[ 0x04 ] = 0x2666,
[ 0x05 ] = 0x2663,
[ 0x06 ] = 0x2660,
[ 0x07 ] = 0x2022,
[ 0x08 ] = 0x25D8,
[ 0x09 ] = 0x25CB,
[ 0x0a ] = 0x25D9,
[ 0x0b ] = 0x2642,
[ 0x0c ] = 0x2640,
[ 0x0d ] = 0x266A,
[ 0x0e ] = 0x266B,
[ 0x0f ] = 0x263C,
[ 0x10 ] = 0x25BA,
[ 0x11 ] = 0x25C4,
[ 0x12 ] = 0x2195,
[ 0x13 ] = 0x203C,
[ 0x14 ] = 0x00B6,
[ 0x15 ] = 0x00A7,
[ 0x16 ] = 0x25AC,
[ 0x17 ] = 0x21A8,
[ 0x18 ] = 0x2191,
[ 0x19 ] = 0x2193,
[ 0x1a ] = 0x2192,
[ 0x1b ] = 0x2190,
[ 0x1c ] = 0x221F,
[ 0x1d ] = 0x2194,
[ 0x1e ] = 0x25B2,
[ 0x1f ] = 0x25BC,
[ 0x7f ] = 0x2302,

/* http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP437.TXT */
[ 0x20 ] = 0x0020, // SPACE
[ 0x21 ] = 0x0021, // EXCLAMATION MARK
[ 0x22 ] = 0x0022, // QUOTATION MARK
[ 0x23 ] = 0x0023, // NUMBER SIGN
[ 0x24 ] = 0x0024, // DOLLAR SIGN
[ 0x25 ] = 0x0025, // PERCENT SIGN
[ 0x26 ] = 0x0026, // AMPERSAND
[ 0x27 ] = 0x0027, // APOSTROPHE
[ 0x28 ] = 0x0028, // LEFT PARENTHESIS
[ 0x29 ] = 0x0029, // RIGHT PARENTHESIS
[ 0x2a ] = 0x002a, // ASTERISK
[ 0x2b ] = 0x002b, // PLUS SIGN
[ 0x2c ] = 0x002c, // COMMA
[ 0x2d ] = 0x002d, // HYPHEN-MINUS
[ 0x2e ] = 0x002e, // FULL STOP
[ 0x2f ] = 0x002f, // SOLIDUS
[ 0x30 ] = 0x0030, // DIGIT ZERO
[ 0x31 ] = 0x0031, // DIGIT ONE
[ 0x32 ] = 0x0032, // DIGIT TWO
[ 0x33 ] = 0x0033, // DIGIT THREE
[ 0x34 ] = 0x0034, // DIGIT FOUR
[ 0x35 ] = 0x0035, // DIGIT FIVE
[ 0x36 ] = 0x0036, // DIGIT SIX
[ 0x37 ] = 0x0037, // DIGIT SEVEN
[ 0x38 ] = 0x0038, // DIGIT EIGHT
[ 0x39 ] = 0x0039, // DIGIT NINE
[ 0x3a ] = 0x003a, // COLON
[ 0x3b ] = 0x003b, // SEMICOLON
[ 0x3c ] = 0x003c, // LESS-THAN SIGN
[ 0x3d ] = 0x003d, // EQUALS SIGN
[ 0x3e ] = 0x003e, // GREATER-THAN SIGN
[ 0x3f ] = 0x003f, // QUESTION MARK
[ 0x40 ] = 0x0040, // COMMERCIAL AT
[ 0x41 ] = 0x0041, // LATIN CAPITAL LETTER A
[ 0x42 ] = 0x0042, // LATIN CAPITAL LETTER B
[ 0x43 ] = 0x0043, // LATIN CAPITAL LETTER C
[ 0x44 ] = 0x0044, // LATIN CAPITAL LETTER D
[ 0x45 ] = 0x0045, // LATIN CAPITAL LETTER E
[ 0x46 ] = 0x0046, // LATIN CAPITAL LETTER F
[ 0x47 ] = 0x0047, // LATIN CAPITAL LETTER G
[ 0x48 ] = 0x0048, // LATIN CAPITAL LETTER H
[ 0x49 ] = 0x0049, // LATIN CAPITAL LETTER I
[ 0x4a ] = 0x004a, // LATIN CAPITAL LETTER J
[ 0x4b ] = 0x004b, // LATIN CAPITAL LETTER K
[ 0x4c ] = 0x004c, // LATIN CAPITAL LETTER L
[ 0x4d ] = 0x004d, // LATIN CAPITAL LETTER M
[ 0x4e ] = 0x004e, // LATIN CAPITAL LETTER N
[ 0x4f ] = 0x004f, // LATIN CAPITAL LETTER O
[ 0x50 ] = 0x0050, // LATIN CAPITAL LETTER P
[ 0x51 ] = 0x0051, // LATIN CAPITAL LETTER Q
[ 0x52 ] = 0x0052, // LATIN CAPITAL LETTER R
[ 0x53 ] = 0x0053, // LATIN CAPITAL LETTER S
[ 0x54 ] = 0x0054, // LATIN CAPITAL LETTER T
[ 0x55 ] = 0x0055, // LATIN CAPITAL LETTER U
[ 0x56 ] = 0x0056, // LATIN CAPITAL LETTER V
[ 0x57 ] = 0x0057, // LATIN CAPITAL LETTER W
[ 0x58 ] = 0x0058, // LATIN CAPITAL LETTER X
[ 0x59 ] = 0x0059, // LATIN CAPITAL LETTER Y
[ 0x5a ] = 0x005a, // LATIN CAPITAL LETTER Z
[ 0x5b ] = 0x005b, // LEFT SQUARE BRACKET
[ 0x5c ] = 0x005c, // REVERSE SOLIDUS
[ 0x5d ] = 0x005d, // RIGHT SQUARE BRACKET
[ 0x5e ] = 0x005e, // CIRCUMFLEX ACCENT
[ 0x5f ] = 0x005f, // LOW LINE
[ 0x60 ] = 0x0060, // GRAVE ACCENT
[ 0x61 ] = 0x0061, // LATIN SMALL LETTER A
[ 0x62 ] = 0x0062, // LATIN SMALL LETTER B
[ 0x63 ] = 0x0063, // LATIN SMALL LETTER C
[ 0x64 ] = 0x0064, // LATIN SMALL LETTER D
[ 0x65 ] = 0x0065, // LATIN SMALL LETTER E
[ 0x66 ] = 0x0066, // LATIN SMALL LETTER F
[ 0x67 ] = 0x0067, // LATIN SMALL LETTER G
[ 0x68 ] = 0x0068, // LATIN SMALL LETTER H
[ 0x69 ] = 0x0069, // LATIN SMALL LETTER I
[ 0x6a ] = 0x006a, // LATIN SMALL LETTER J
[ 0x6b ] = 0x006b, // LATIN SMALL LETTER K
[ 0x6c ] = 0x006c, // LATIN SMALL LETTER L
[ 0x6d ] = 0x006d, // LATIN SMALL LETTER M
[ 0x6e ] = 0x006e, // LATIN SMALL LETTER N
[ 0x6f ] = 0x006f, // LATIN SMALL LETTER O
[ 0x70 ] = 0x0070, // LATIN SMALL LETTER P
[ 0x71 ] = 0x0071, // LATIN SMALL LETTER Q
[ 0x72 ] = 0x0072, // LATIN SMALL LETTER R
[ 0x73 ] = 0x0073, // LATIN SMALL LETTER S
[ 0x74 ] = 0x0074, // LATIN SMALL LETTER T
[ 0x75 ] = 0x0075, // LATIN SMALL LETTER U
[ 0x76 ] = 0x0076, // LATIN SMALL LETTER V
[ 0x77 ] = 0x0077, // LATIN SMALL LETTER W
[ 0x78 ] = 0x0078, // LATIN SMALL LETTER X
[ 0x79 ] = 0x0079, // LATIN SMALL LETTER Y
[ 0x7a ] = 0x007a, // LATIN SMALL LETTER Z
[ 0x7b ] = 0x007b, // LEFT CURLY BRACKET
[ 0x7c ] = 0x007c, // VERTICAL LINE
[ 0x7d ] = 0x007d, // RIGHT CURLY BRACKET
[ 0x7e ] = 0x007e, // TILDE
[ 0x80 ] = 0x00c7, // LATIN CAPITAL LETTER C WITH CEDILLA
[ 0x81 ] = 0x00fc, // LATIN SMALL LETTER U WITH DIAERESIS
[ 0x82 ] = 0x00e9, // LATIN SMALL LETTER E WITH ACUTE
[ 0x83 ] = 0x00e2, // LATIN SMALL LETTER A WITH CIRCUMFLEX
[ 0x84 ] = 0x00e4, // LATIN SMALL LETTER A WITH DIAERESIS
[ 0x85 ] = 0x00e0, // LATIN SMALL LETTER A WITH GRAVE
[ 0x86 ] = 0x00e5, // LATIN SMALL LETTER A WITH RING ABOVE
[ 0x87 ] = 0x00e7, // LATIN SMALL LETTER C WITH CEDILLA
[ 0x88 ] = 0x00ea, // LATIN SMALL LETTER E WITH CIRCUMFLEX
[ 0x89 ] = 0x00eb, // LATIN SMALL LETTER E WITH DIAERESIS
[ 0x8a ] = 0x00e8, // LATIN SMALL LETTER E WITH GRAVE
[ 0x8b ] = 0x00ef, // LATIN SMALL LETTER I WITH DIAERESIS
[ 0x8c ] = 0x00ee, // LATIN SMALL LETTER I WITH CIRCUMFLEX
[ 0x8d ] = 0x00ec, // LATIN SMALL LETTER I WITH GRAVE
[ 0x8e ] = 0x00c4, // LATIN CAPITAL LETTER A WITH DIAERESIS
[ 0x8f ] = 0x00c5, // LATIN CAPITAL LETTER A WITH RING ABOVE
[ 0x90 ] = 0x00c9, // LATIN CAPITAL LETTER E WITH ACUTE
[ 0x91 ] = 0x00e6, // LATIN SMALL LIGATURE AE
[ 0x92 ] = 0x00c6, // LATIN CAPITAL LIGATURE AE
[ 0x93 ] = 0x00f4, // LATIN SMALL LETTER O WITH CIRCUMFLEX
[ 0x94 ] = 0x00f6, // LATIN SMALL LETTER O WITH DIAERESIS
[ 0x95 ] = 0x00f2, // LATIN SMALL LETTER O WITH GRAVE
[ 0x96 ] = 0x00fb, // LATIN SMALL LETTER U WITH CIRCUMFLEX
[ 0x97 ] = 0x00f9, // LATIN SMALL LETTER U WITH GRAVE
[ 0x98 ] = 0x00ff, // LATIN SMALL LETTER Y WITH DIAERESIS
[ 0x99 ] = 0x00d6, // LATIN CAPITAL LETTER O WITH DIAERESIS
[ 0x9a ] = 0x00dc, // LATIN CAPITAL LETTER U WITH DIAERESIS
[ 0x9b ] = 0x00a2, // CENT SIGN
[ 0x9c ] = 0x00a3, // POUND SIGN
[ 0x9d ] = 0x00a5, // YEN SIGN
[ 0x9e ] = 0x20a7, // PESETA SIGN
[ 0x9f ] = 0x0192, // LATIN SMALL LETTER F WITH HOOK
[ 0xa0 ] = 0x00e1, // LATIN SMALL LETTER A WITH ACUTE
[ 0xa1 ] = 0x00ed, // LATIN SMALL LETTER I WITH ACUTE
[ 0xa2 ] = 0x00f3, // LATIN SMALL LETTER O WITH ACUTE
[ 0xa3 ] = 0x00fa, // LATIN SMALL LETTER U WITH ACUTE
[ 0xa4 ] = 0x00f1, // LATIN SMALL LETTER N WITH TILDE
[ 0xa5 ] = 0x00d1, // LATIN CAPITAL LETTER N WITH TILDE
[ 0xa6 ] = 0x00aa, // FEMININE ORDINAL INDICATOR
[ 0xa7 ] = 0x00ba, // MASCULINE ORDINAL INDICATOR
[ 0xa8 ] = 0x00bf, // INVERTED QUESTION MARK
[ 0xa9 ] = 0x2310, // REVERSED NOT SIGN
[ 0xaa ] = 0x00ac, // NOT SIGN
[ 0xab ] = 0x00bd, // VULGAR FRACTION ONE HALF
[ 0xac ] = 0x00bc, // VULGAR FRACTION ONE QUARTER
[ 0xad ] = 0x00a1, // INVERTED EXCLAMATION MARK
[ 0xae ] = 0x00ab, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
[ 0xaf ] = 0x00bb, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
[ 0xb0 ] = 0x2591, // LIGHT SHADE
[ 0xb1 ] = 0x2592, // MEDIUM SHADE
[ 0xb2 ] = 0x2593, // DARK SHADE
[ 0xb3 ] = 0x2502, // BOX DRAWINGS LIGHT VERTICAL
[ 0xb4 ] = 0x2524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
[ 0xb5 ] = 0x2561, // BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
[ 0xb6 ] = 0x2562, // BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
[ 0xb7 ] = 0x2556, // BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
[ 0xb8 ] = 0x2555, // BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
[ 0xb9 ] = 0x2563, // BOX DRAWINGS DOUBLE VERTICAL AND LEFT
[ 0xba ] = 0x2551, // BOX DRAWINGS DOUBLE VERTICAL
[ 0xbb ] = 0x2557, // BOX DRAWINGS DOUBLE DOWN AND LEFT
[ 0xbc ] = 0x255d, // BOX DRAWINGS DOUBLE UP AND LEFT
[ 0xbd ] = 0x255c, // BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
[ 0xbe ] = 0x255b, // BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
[ 0xbf ] = 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT
[ 0xc0 ] = 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT
[ 0xc1 ] = 0x2534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
[ 0xc2 ] = 0x252c, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
[ 0xc3 ] = 0x251c, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
[ 0xc4 ] = 0x2500, // BOX DRAWINGS LIGHT HORIZONTAL
[ 0xc5 ] = 0x253c, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
[ 0xc6 ] = 0x255e, // BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
[ 0xc7 ] = 0x255f, // BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
[ 0xc8 ] = 0x255a, // BOX DRAWINGS DOUBLE UP AND RIGHT
[ 0xc9 ] = 0x2554, // BOX DRAWINGS DOUBLE DOWN AND RIGHT
[ 0xca ] = 0x2569, // BOX DRAWINGS DOUBLE UP AND HORIZONTAL
[ 0xcb ] = 0x2566, // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
[ 0xcc ] = 0x2560, // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
[ 0xcd ] = 0x2550, // BOX DRAWINGS DOUBLE HORIZONTAL
[ 0xce ] = 0x256c, // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
[ 0xcf ] = 0x2567, // BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
[ 0xd0 ] = 0x2568, // BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
[ 0xd1 ] = 0x2564, // BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
[ 0xd2 ] = 0x2565, // BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
[ 0xd3 ] = 0x2559, // BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
[ 0xd4 ] = 0x2558, // BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
[ 0xd5 ] = 0x2552, // BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
[ 0xd6 ] = 0x2553, // BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
[ 0xd7 ] = 0x256b, // BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
[ 0xd8 ] = 0x256a, // BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
[ 0xd9 ] = 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT
[ 0xda ] = 0x250c, // BOX DRAWINGS LIGHT DOWN AND RIGHT
[ 0xdb ] = 0x2588, // FULL BLOCK
[ 0xdc ] = 0x2584, // LOWER HALF BLOCK
[ 0xdd ] = 0x258c, // LEFT HALF BLOCK
[ 0xde ] = 0x2590, // RIGHT HALF BLOCK
[ 0xdf ] = 0x2580, // UPPER HALF BLOCK
[ 0xe0 ] = 0x03b1, // GREEK SMALL LETTER ALPHA
[ 0xe1 ] = 0x00df, // LATIN SMALL LETTER SHARP S
[ 0xe2 ] = 0x0393, // GREEK CAPITAL LETTER GAMMA
[ 0xe3 ] = 0x03c0, // GREEK SMALL LETTER PI
[ 0xe4 ] = 0x03a3, // GREEK CAPITAL LETTER SIGMA
[ 0xe5 ] = 0x03c3, // GREEK SMALL LETTER SIGMA
[ 0xe6 ] = 0x00b5, // MICRO SIGN
[ 0xe7 ] = 0x03c4, // GREEK SMALL LETTER TAU
[ 0xe8 ] = 0x03a6, // GREEK CAPITAL LETTER PHI
[ 0xe9 ] = 0x0398, // GREEK CAPITAL LETTER THETA
[ 0xea ] = 0x03a9, // GREEK CAPITAL LETTER OMEGA
[ 0xeb ] = 0x03b4, // GREEK SMALL LETTER DELTA
[ 0xec ] = 0x221e, // INFINITY
[ 0xed ] = 0x03c6, // GREEK SMALL LETTER PHI
[ 0xee ] = 0x03b5, // GREEK SMALL LETTER EPSILON
[ 0xef ] = 0x2229, // INTERSECTION
[ 0xf0 ] = 0x2261, // IDENTICAL TO
[ 0xf1 ] = 0x00b1, // PLUS-MINUS SIGN
[ 0xf2 ] = 0x2265, // GREATER-THAN OR EQUAL TO
[ 0xf3 ] = 0x2264, // LESS-THAN OR EQUAL TO
[ 0xf4 ] = 0x2320, // TOP HALF INTEGRAL
[ 0xf5 ] = 0x2321, // BOTTOM HALF INTEGRAL
[ 0xf6 ] = 0x00f7, // DIVISION SIGN
[ 0xf7 ] = 0x2248, // ALMOST EQUAL TO
[ 0xf8 ] = 0x00b0, // DEGREE SIGN
[ 0xf9 ] = 0x2219, // BULLET OPERATOR
[ 0xfa ] = 0x00b7, // MIDDLE DOT
[ 0xfb ] = 0x221a, // SQUARE ROOT
[ 0xfc ] = 0x207f, // SUPERSCRIPT LATIN SMALL LETTER N
[ 0xfd ] = 0x00b2, // SUPERSCRIPT TWO
[ 0xfe ] = 0x25a0, // BLACK SQUARE
[ 0xff ] = 0x00a0, // NO-BREAK SPACE
};

u16 cp437_to_unicode(u8 cp437)
{
return GET_GLOBAL(cp437_to_unicode_map[cp437]);
}
1 change: 1 addition & 0 deletions src/cp437.h
@@ -0,0 +1 @@
u16 cp437_to_unicode(u8 cp437);
218 changes: 109 additions & 109 deletions src/disk.c

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions src/fw/biostables.c
Expand Up @@ -134,8 +134,8 @@ void *find_acpi_rsdp(void)
return NULL;
}

static struct fadt_descriptor_rev1 *
find_fadt(void)
void *
find_acpi_table(u32 signature)
{
dprintf(4, "rsdp=%p\n", RsdpAddr);
if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE)
Expand All @@ -147,20 +147,20 @@ find_fadt(void)
void *end = (void*)rsdt + rsdt->length;
int i;
for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[i];
if (!fadt || fadt->signature != FACP_SIGNATURE)
struct acpi_table_header *tbl = (void*)rsdt->table_offset_entry[i];
if (!tbl || tbl->signature != signature)
continue;
dprintf(4, "fadt=%p\n", fadt);
return fadt;
dprintf(4, "table(%x)=%p\n", signature, tbl);
return tbl;
}
dprintf(4, "no fadt found\n");
dprintf(4, "no table %x found\n", signature);
return NULL;
}

u32
find_resume_vector(void)
{
struct fadt_descriptor_rev1 *fadt = find_fadt();
struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
if (!fadt)
return 0;
struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl;
Expand Down Expand Up @@ -218,7 +218,7 @@ acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val)
void
find_acpi_features(void)
{
struct fadt_descriptor_rev1 *fadt = find_fadt();
struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
if (!fadt)
return;
u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk);
Expand Down
68 changes: 62 additions & 6 deletions src/fw/coreboot.c
Expand Up @@ -17,6 +17,7 @@
#include "stacks.h" // yield
#include "string.h" // memset
#include "util.h" // coreboot_preinit
#include "coreboot.h" // cbfs_romfile overrides


/****************************************************************
Expand All @@ -34,6 +35,13 @@ struct cb_header {

#define CB_SIGNATURE 0x4f49424C // "LBIO"

// Generic cb record
//
struct cb_record {
u32 tag;
u32 size;
};

struct cb_memory_range {
u64 start;
u64 size;
Expand Down Expand Up @@ -80,10 +88,12 @@ struct cb_cbmem_ref {
#define CB_TAG_CBMEM_CONSOLE 0x17

struct cbmem_console {
u32 buffer_size;
u32 buffer_cursor;
u8 buffer_body[0];
u32 size;
u32 cursor;
u8 body[0];
} PACKED;
#define CBMC_CURSOR_MASK ((1 << 28) - 1)
#define CBMC_OVERFLOW (1 << 31)
static struct cbmem_console *cbcon = NULL;

static u16
Expand Down Expand Up @@ -160,6 +170,45 @@ find_cb_table(void)
static struct cb_memory *CBMemTable;
const char *CBvendor = "", *CBpart = "";

char *
get_cbmem_file(char * filename, int * size)
{
char *f = NULL;
struct cbfile_record *cbf = NULL;
struct file_container *cbmem_file_ptr =NULL;
unsigned long temp;

dprintf(3, "looking for file \"%s\" in cbmem\n", filename);

if ( size ) *size = 0;

// First we need to find the coreboot table
struct cb_header *cbh = find_cb_table();

if (cbh) {
// Now find the file entry
cbf = find_cb_subtable(cbh, CB_TAG_FILE);
}

cbmem_file_ptr = (struct file_container *) (u32) cbf->forward;

// If we found the FILE table we now need to walk through each entry.
while (cbmem_file_ptr->file_signature == CBMEM_ID_FILE) {

if (!strcmp( cbmem_file_ptr->file_name, filename ) ) {

f = (char *)cbmem_file_ptr->file_data;
if ( size ) *size = cbmem_file_ptr->file_size;
break;
}
dprintf(3, "found file \"%s\" in cbmem\n", cbmem_file_ptr->file_name);
temp = (u32) cbmem_file_ptr + sizeof(struct file_container) + cbmem_file_ptr->file_size;
temp = ALIGN(temp, 16);
cbmem_file_ptr = (struct file_container *) temp;
}
return f;
}

// Populate max ram and e820 map info by scanning for a coreboot table.
void
coreboot_preinit(void)
Expand Down Expand Up @@ -220,9 +269,16 @@ void coreboot_debug_putc(char c)
return;
if (!cbcon)
return;
u32 cursor = cbcon->buffer_cursor++;
if (cursor < cbcon->buffer_size)
cbcon->buffer_body[cursor] = c;
u32 cursor = cbcon->cursor & CBMC_CURSOR_MASK;
u32 flags = cbcon->cursor & ~CBMC_CURSOR_MASK;
if (cursor >= cbcon->size)
return; // Old coreboot version with legacy overflow mechanism.
cbcon->body[cursor++] = c;
if (cursor >= cbcon->size) {
cursor = 0;
flags |= CBMC_OVERFLOW;
}
cbcon->cursor = flags | cursor;
}

/****************************************************************
Expand Down
45 changes: 45 additions & 0 deletions src/fw/coreboot.h
@@ -0,0 +1,45 @@
//*****************************************************************************
//
// Copyright (c) 2015 Eltan B.V. All rights reserved.
// Software License Agreement
//
// THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// ELTAN SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
// OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
//
//*****************************************************************************

#ifndef __COREBOOT_H
#define __COREBOOT_H

struct cbmem_entry {
u32 magic;
u32 id;
u64 base;
u64 size;
};

//
// Intended for cbfs file overrides
//
struct file_container {
u32 file_signature; /* "FILE" */
u32 file_size; /* size of file_data[] */
char file_name[32];
char file_data[0]; /* Variable size */
};

#define CBMEM_ID_FILE 0x46494c45 //'FILE'
#define CB_TAG_FILE 0x25
#define CBMEM_MAGIC 0x434f5245

struct cbfile_record {
u32 tag;
u32 size;
u64 forward;
};
char * get_cbmem_file( char * filename, int * size );

#endif //__COREBOOT_H
52 changes: 52 additions & 0 deletions src/fw/dev-pci.h
@@ -0,0 +1,52 @@
#ifndef _PCI_CAP_H
#define _PCI_CAP_H

#include "types.h"

/*
*
* QEMU-specific vendor(Red Hat)-specific capability.
* It's intended to provide some hints for firmware to init PCI devices.
*
* Its structure is shown below:
*
* Header:
*
* u8 id; Standard PCI Capability Header field
* u8 next; Standard PCI Capability Header field
* u8 len; Standard PCI Capability Header field
* u8 type; Red Hat vendor-specific capability type
* Data:
*
* u32 bus_res; minimum bus number to reserve;
* this is necessary for PCI Express Root Ports
* to support PCI bridges hotplug
* u64 io; IO space to reserve
* u32 mem; non-prefetchable memory to reserve
*
* At most of the following two fields may be set to a value
* different from 0xFF...F:
* u32 prefetchable_mem_32; prefetchable memory to reserve (32-bit MMIO)
* u64 prefetchable_mem_64; prefetchable memory to reserve (64-bit MMIO)
*
* If any field value in Data section is 0xFF...F,
* it means that such kind of reservation is not needed and must be ignored.
*
*/

/* Offset of vendor-specific capability type field */
#define PCI_CAP_REDHAT_TYPE_OFFSET 3

/* List of valid Red Hat vendor-specific capability types */
#define REDHAT_CAP_RESOURCE_RESERVE 1


/* Offsets of RESOURCE_RESERVE capability fields */
#define RES_RESERVE_BUS_RES 4
#define RES_RESERVE_IO 8
#define RES_RESERVE_MEM 16
#define RES_RESERVE_PREF_MEM_32 20
#define RES_RESERVE_PREF_MEM_64 24
#define RES_RESERVE_CAP_SIZE 32

#endif /* _PCI_CAP_H */
113 changes: 87 additions & 26 deletions src/fw/paravirt.c
Expand Up @@ -14,6 +14,7 @@
#include "hw/pci.h" // pci_config_readw
#include "hw/pcidevice.h" // pci_probe_devices
#include "hw/pci_regs.h" // PCI_DEVICE_ID
#include "hw/serialio.h" // PORT_SERIAL1
#include "hw/rtc.h" // CMOS_*
#include "malloc.h" // malloc_tmp
#include "output.h" // dprintf
Expand All @@ -32,9 +33,16 @@ u32 RamSize;
u64 RamSizeOver4G;
// Type of emulator platform.
int PlatformRunningOn VARFSEG;
// cfg enabled
int cfg_enabled = 0;
// cfg_dma enabled
int cfg_dma_enabled = 0;

inline int qemu_cfg_enabled(void)
{
return cfg_enabled;
}

inline int qemu_cfg_dma_enabled(void)
{
return cfg_dma_enabled;
Expand Down Expand Up @@ -203,8 +211,10 @@ qemu_platform_setup(void)
#define QEMU_CFG_SIGNATURE 0x00
#define QEMU_CFG_ID 0x01
#define QEMU_CFG_UUID 0x02
#define QEMU_CFG_NOGRAPHIC 0x04
#define QEMU_CFG_NUMA 0x0d
#define QEMU_CFG_BOOT_MENU 0x0e
#define QEMU_CFG_NB_CPUS 0x05
#define QEMU_CFG_MAX_CPUS 0x0f
#define QEMU_CFG_FILE_DIR 0x19
#define QEMU_CFG_ARCH_LOCAL 0x8000
Expand Down Expand Up @@ -251,6 +261,20 @@ qemu_cfg_read(void *buf, int len)
}
}

static void
qemu_cfg_write(void *buf, int len)
{
if (len == 0) {
return;
}

if (qemu_cfg_dma_enabled()) {
qemu_cfg_dma_transfer(buf, len, QEMU_CFG_DMA_CTL_WRITE);
} else {
warn_internalerror();
}
}

static void
qemu_cfg_skip(int len)
{
Expand Down Expand Up @@ -279,6 +303,18 @@ qemu_cfg_read_entry(void *buf, int e, int len)
}
}

static void
qemu_cfg_write_entry(void *buf, int e, int len)
{
if (qemu_cfg_dma_enabled()) {
u32 control = (e << 16) | QEMU_CFG_DMA_CTL_SELECT
| QEMU_CFG_DMA_CTL_WRITE;
qemu_cfg_dma_transfer(buf, len, control);
} else {
warn_internalerror();
}
}

struct qemu_romfile_s {
struct romfile_s file;
int select, skip;
Expand All @@ -302,6 +338,36 @@ qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen)
return file->size;
}

// Bare-bones function for writing a file knowing only its unique
// identifying key (select)
int
qemu_cfg_write_file_simple(void *src, u16 key, u32 offset, u32 len)
{
if (offset == 0) {
/* Do it in one transfer */
qemu_cfg_write_entry(src, key, len);
} else {
qemu_cfg_select(key);
qemu_cfg_skip(offset);
qemu_cfg_write(src, len);
}
return len;
}

int
qemu_cfg_write_file(void *src, struct romfile_s *file, u32 offset, u32 len)
{
if ((offset + len) > file->size)
return -1;

if (!qemu_cfg_dma_enabled() || (file->copy != qemu_cfg_read_file)) {
warn_internalerror();
return -1;
}
return qemu_cfg_write_file_simple(src, qemu_get_romfile_key(file),
offset, len);
}

static void
qemu_romfile_add(char *name, int select, int skip, int size)
{
Expand All @@ -319,40 +385,28 @@ qemu_romfile_add(char *name, int select, int skip, int size)
romfile_add(&qfile->file);
}

static int
qemu_romfile_get_fwcfg_entry(char *name, int *select)
u16
qemu_get_romfile_key(struct romfile_s *file)
{
struct romfile_s *file = romfile_find(name);
if (!file)
return 0;
struct qemu_romfile_s *qfile;
if (file->copy != qemu_cfg_read_file) {
warn_internalerror();
return 0;
}
qfile = container_of(file, struct qemu_romfile_s, file);
if (select)
*select = qfile->select;
return file->size;
}

static int boot_cpus_sel;
static int boot_cpus_file_sz;

u16
qemu_init_present_cpus_count(void)
{
u16 smp_count = romfile_loadint("etc/boot-cpus",
rtc_read(CMOS_BIOS_SMP_COUNT) + 1);
boot_cpus_file_sz =
qemu_romfile_get_fwcfg_entry("etc/boot-cpus", &boot_cpus_sel);
return smp_count;
return qfile->select;
}

u16
qemu_get_present_cpus_count(void)
{
u16 smp_count;
if (!boot_cpus_file_sz) {
smp_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1;
} else {
qemu_cfg_read_entry(&smp_count, boot_cpus_sel, boot_cpus_file_sz);
u16 smp_count = 0;
if (qemu_cfg_enabled()) {
qemu_cfg_read_entry(&smp_count, QEMU_CFG_NB_CPUS, sizeof(smp_count));
}
u16 cmos_cpu_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1;
if (smp_count < cmos_cpu_count) {
smp_count = cmos_cpu_count;
}
return smp_count;
}
Expand Down Expand Up @@ -459,6 +513,12 @@ qemu_cfg_legacy(void)
qemu_romfile_add("etc/irq0-override", QEMU_CFG_IRQ0_OVERRIDE, 0, 1);
qemu_romfile_add("etc/max-cpus", QEMU_CFG_MAX_CPUS, 0, 2);

// serial console
u16 nogfx = 0;
qemu_cfg_read_entry(&nogfx, QEMU_CFG_NOGRAPHIC, sizeof(nogfx));
if (nogfx)
const_romfile_add_int("etc/sercon-port", PORT_SERIAL1);

// NUMA data
u64 numacount;
qemu_cfg_read_entry(&numacount, QEMU_CFG_NUMA, sizeof(numacount));
Expand Down Expand Up @@ -528,6 +588,7 @@ void qemu_cfg_init(void)
return;

dprintf(1, "Found QEMU fw_cfg\n");
cfg_enabled = 1;

// Detect DMA interface.
u32 id;
Expand Down
7 changes: 6 additions & 1 deletion src/fw/paravirt.h
Expand Up @@ -3,6 +3,7 @@

#include "config.h" // CONFIG_*
#include "biosvar.h" // GET_GLOBAL
#include "romfile.h" // struct romfile_s

// Types of paravirtualized platforms.
#define PF_QEMU (1<<0)
Expand Down Expand Up @@ -43,16 +44,20 @@ static inline int runningOnKVM(void) {
#define QEMU_CFG_DMA_CTL_READ 0x02
#define QEMU_CFG_DMA_CTL_SKIP 0x04
#define QEMU_CFG_DMA_CTL_SELECT 0x08
#define QEMU_CFG_DMA_CTL_WRITE 0x10

// QEMU_CFG_DMA ID bit
#define QEMU_CFG_VERSION_DMA 2

int qemu_cfg_enabled(void);
int qemu_cfg_dma_enabled(void);
void qemu_preinit(void);
void qemu_platform_setup(void);
void qemu_cfg_init(void);

u16 qemu_init_present_cpus_count(void);
u16 qemu_get_present_cpus_count(void);
int qemu_cfg_write_file(void *src, struct romfile_s *file, u32 offset, u32 len);
int qemu_cfg_write_file_simple(void *src, u16 key, u32 offset, u32 len);
u16 qemu_get_romfile_key(struct romfile_s *file);

#endif
108 changes: 103 additions & 5 deletions src/fw/pciinit.c
Expand Up @@ -15,6 +15,7 @@
#include "hw/pcidevice.h" // pci_probe_devices
#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL
#include "hw/pci_regs.h" // PCI_COMMAND
#include "fw/dev-pci.h" // REDHAT_CAP_RESOURCE_RESERVE
#include "list.h" // struct hlist_node
#include "malloc.h" // free
#include "output.h" // dprintf
Expand Down Expand Up @@ -522,6 +523,32 @@ static void pci_bios_init_platform(void)
}
}

static u8 pci_find_resource_reserve_capability(u16 bdf)
{
if (pci_config_readw(bdf, PCI_VENDOR_ID) == PCI_VENDOR_ID_REDHAT &&
pci_config_readw(bdf, PCI_DEVICE_ID) ==
PCI_DEVICE_ID_REDHAT_ROOT_PORT) {
u8 cap = 0;
do {
cap = pci_find_capability(bdf, PCI_CAP_ID_VNDR, cap);
} while (cap &&
pci_config_readb(bdf, cap + PCI_CAP_REDHAT_TYPE_OFFSET) !=
REDHAT_CAP_RESOURCE_RESERVE);
if (cap) {
u8 cap_len = pci_config_readb(bdf, cap + PCI_CAP_FLAGS);
if (cap_len < RES_RESERVE_CAP_SIZE) {
dprintf(1, "PCI: QEMU resource reserve cap length %d is invalid\n",
cap_len);
}
} else {
dprintf(1, "PCI: invalid QEMU resource reserve cap offset\n");
}
return cap;
} else {
dprintf(1, "PCI: QEMU resource reserve cap not found\n");
return 0;
}
}

/****************************************************************
* Bus initialization
Expand Down Expand Up @@ -578,9 +605,33 @@ pci_bios_init_bus_rec(int bus, u8 *pci_bus)
pci_bios_init_bus_rec(secbus, pci_bus);

if (subbus != *pci_bus) {
u8 res_bus = *pci_bus;
u8 cap = pci_find_resource_reserve_capability(bdf);

if (cap) {
u32 tmp_res_bus = pci_config_readl(bdf,
cap + RES_RESERVE_BUS_RES);
if (tmp_res_bus != (u32)-1) {
res_bus = tmp_res_bus & 0xFF;
if ((u8)(res_bus + secbus) < secbus ||
(u8)(res_bus + secbus) < res_bus) {
dprintf(1, "PCI: bus_reserve value %d is invalid\n",
res_bus);
res_bus = 0;
}
}
if (secbus + res_bus > *pci_bus) {
dprintf(1, "PCI: QEMU resource reserve cap: bus = %u\n",
res_bus);
res_bus = secbus + res_bus;
} else {
res_bus = *pci_bus;
}
}
dprintf(1, "PCI: subordinate bus = 0x%x -> 0x%x\n",
subbus, *pci_bus);
subbus = *pci_bus;
subbus, res_bus);
subbus = res_bus;
*pci_bus = res_bus;
} else {
dprintf(1, "PCI: subordinate bus = 0x%x\n", subbus);
}
Expand Down Expand Up @@ -762,7 +813,7 @@ static int pci_bus_hotplug_support(struct pci_bus *bus, u8 pcie_cap)
return downstream_port && slot_implemented;
}

shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC, 0);
shpc_cap = pci_find_capability(bus->bus_dev->bdf, PCI_CAP_ID_SHPC, 0);
return !!shpc_cap;
}

Expand Down Expand Up @@ -844,20 +895,67 @@ static int pci_bios_check_devices(struct pci_bus *busses)
*/
parent = &busses[0];
int type;
u8 pcie_cap = pci_find_capability(s->bus_dev, PCI_CAP_ID_EXP, 0);
u16 bdf = s->bus_dev->bdf;
u8 pcie_cap = pci_find_capability(bdf, PCI_CAP_ID_EXP, 0);
u8 qemu_cap = pci_find_resource_reserve_capability(bdf);

int hotplug_support = pci_bus_hotplug_support(s, pcie_cap);
for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) {
u64 align = (type == PCI_REGION_TYPE_IO) ?
PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN;
if (!pci_bridge_has_region(s->bus_dev, type))
continue;
u64 size = 0;
if (qemu_cap) {
u32 tmp_size;
u64 tmp_size_64;
switch(type) {
case PCI_REGION_TYPE_IO:
tmp_size_64 = (pci_config_readl(bdf, qemu_cap + RES_RESERVE_IO) |
(u64)pci_config_readl(bdf, qemu_cap + RES_RESERVE_IO + 4) << 32);
if (tmp_size_64 != (u64)-1) {
size = tmp_size_64;
}
break;
case PCI_REGION_TYPE_MEM:
tmp_size = pci_config_readl(bdf, qemu_cap + RES_RESERVE_MEM);
if (tmp_size != (u32)-1) {
size = tmp_size;
}
break;
case PCI_REGION_TYPE_PREFMEM:
tmp_size = pci_config_readl(bdf, qemu_cap + RES_RESERVE_PREF_MEM_32);
tmp_size_64 = (pci_config_readl(bdf, qemu_cap + RES_RESERVE_PREF_MEM_64) |
(u64)pci_config_readl(bdf, qemu_cap + RES_RESERVE_PREF_MEM_64 + 4) << 32);
if (tmp_size != (u32)-1 && tmp_size_64 == (u64)-1) {
size = tmp_size;
} else if (tmp_size == (u32)-1 && tmp_size_64 != (u64)-1) {
size = tmp_size_64;
} else if (tmp_size != (u32)-1 && tmp_size_64 != (u64)-1) {
dprintf(1, "PCI: resource reserve cap PREF32 and PREF64"
" conflict\n");
}
break;
default:
break;
}
}
if (pci_region_align(&s->r[type]) > align)
align = pci_region_align(&s->r[type]);
u64 sum = pci_region_sum(&s->r[type]);
int resource_optional = pcie_cap && (type == PCI_REGION_TYPE_IO);
if (!sum && hotplug_support && !resource_optional)
sum = align; /* reserve min size for hot-plug */
u64 size = ALIGN(sum, align);
if (size > sum) {
dprintf(1, "PCI: QEMU resource reserve cap: "
"size %08llx type %s\n",
size, region_type_name[type]);
if (type != PCI_REGION_TYPE_IO) {
size = ALIGN(size, align);
}
} else {
size = ALIGN(sum, align);
}
int is64 = pci_bios_bridge_region_is64(&s->r[type],
s->bus_dev, type);
// entry->bar is -1 if the entry represents a bridge region
Expand Down
116 changes: 99 additions & 17 deletions src/fw/romfile_loader.c
Expand Up @@ -4,7 +4,9 @@
#include "string.h" // strcmp
#include "romfile.h" // struct romfile_s
#include "malloc.h" // Zone*, _malloc
#include "list.h" // struct hlist_node
#include "output.h" // warn_*
#include "paravirt.h" // qemu_cfg_write_file

struct romfile_loader_file {
struct romfile_s *file;
Expand All @@ -15,6 +17,16 @@ struct romfile_loader_files {
struct romfile_loader_file files[];
};

// Data structures for storing "write pointer" entries for possible replay
struct romfile_wr_pointer_entry {
u64 pointer;
u32 offset;
u16 key;
u8 ptr_size;
struct hlist_node node;
};
static struct hlist_head romfile_pointer_list;

static struct romfile_loader_file *
romfile_loader_find(const char *name,
struct romfile_loader_files *files)
Expand All @@ -28,19 +40,32 @@ romfile_loader_find(const char *name,
return NULL;
}

// Replay "write pointer" entries back to QEMU
void romfile_fw_cfg_resume(void)
{
if (!CONFIG_QEMU)
return;

struct romfile_wr_pointer_entry *entry;
hlist_for_each_entry(entry, &romfile_pointer_list, node) {
qemu_cfg_write_file_simple(&entry->pointer, entry->key,
entry->offset, entry->ptr_size);
}
}

static void romfile_loader_allocate(struct romfile_loader_entry_s *entry,
struct romfile_loader_files *files)
{
struct zone_s *zone;
struct romfile_loader_file *file = &files->files[files->nfiles];
void *data;
int ret;
unsigned alloc_align = le32_to_cpu(entry->alloc_align);
unsigned alloc_align = le32_to_cpu(entry->alloc.align);

if (alloc_align & (alloc_align - 1))
goto err;

switch (entry->alloc_zone) {
switch (entry->alloc.zone) {
case ROMFILE_LOADER_ALLOC_ZONE_HIGH:
zone = &ZoneHigh;
break;
Expand All @@ -52,9 +77,9 @@ static void romfile_loader_allocate(struct romfile_loader_entry_s *entry,
}
if (alloc_align < MALLOC_MIN_ALIGN)
alloc_align = MALLOC_MIN_ALIGN;
if (entry->alloc_file[ROMFILE_LOADER_FILESZ - 1])
if (entry->alloc.file[ROMFILE_LOADER_FILESZ - 1])
goto err;
file->file = romfile_find(entry->alloc_file);
file->file = romfile_find(entry->alloc.file);
if (!file->file || !file->file->size)
return;
data = _malloc(zone, file->file->size, alloc_align);
Expand All @@ -80,24 +105,24 @@ static void romfile_loader_add_pointer(struct romfile_loader_entry_s *entry,
{
struct romfile_loader_file *dest_file;
struct romfile_loader_file *src_file;
unsigned offset = le32_to_cpu(entry->pointer_offset);
unsigned offset = le32_to_cpu(entry->pointer.offset);
u64 pointer = 0;

dest_file = romfile_loader_find(entry->pointer_dest_file, files);
src_file = romfile_loader_find(entry->pointer_src_file, files);
dest_file = romfile_loader_find(entry->pointer.dest_file, files);
src_file = romfile_loader_find(entry->pointer.src_file, files);

if (!dest_file || !src_file || !dest_file->data || !src_file->data ||
offset + entry->pointer_size < offset ||
offset + entry->pointer_size > dest_file->file->size ||
entry->pointer_size < 1 || entry->pointer_size > 8 ||
entry->pointer_size & (entry->pointer_size - 1))
offset + entry->pointer.size < offset ||
offset + entry->pointer.size > dest_file->file->size ||
entry->pointer.size < 1 || entry->pointer.size > 8 ||
entry->pointer.size & (entry->pointer.size - 1))
goto err;

memcpy(&pointer, dest_file->data + offset, entry->pointer_size);
memcpy(&pointer, dest_file->data + offset, entry->pointer.size);
pointer = le64_to_cpu(pointer);
pointer += (unsigned long)src_file->data;
pointer = cpu_to_le64(pointer);
memcpy(dest_file->data + offset, &pointer, entry->pointer_size);
memcpy(dest_file->data + offset, &pointer, entry->pointer.size);

return;
err:
Expand All @@ -108,12 +133,12 @@ static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry,
struct romfile_loader_files *files)
{
struct romfile_loader_file *file;
unsigned offset = le32_to_cpu(entry->cksum_offset);
unsigned start = le32_to_cpu(entry->cksum_start);
unsigned len = le32_to_cpu(entry->cksum_length);
unsigned offset = le32_to_cpu(entry->cksum.offset);
unsigned start = le32_to_cpu(entry->cksum.start);
unsigned len = le32_to_cpu(entry->cksum.length);
u8 *data;

file = romfile_loader_find(entry->cksum_file, files);
file = romfile_loader_find(entry->cksum.file, files);

if (!file || !file->data || offset >= file->file->size ||
start + len < start || start + len > file->file->size)
Expand All @@ -127,6 +152,59 @@ static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry,
warn_internalerror();
}

static void romfile_loader_write_pointer(struct romfile_loader_entry_s *entry,
struct romfile_loader_files *files)
{
struct romfile_s *dest_file;
struct romfile_loader_file *src_file;
unsigned dst_offset = le32_to_cpu(entry->wr_pointer.dst_offset);
unsigned src_offset = le32_to_cpu(entry->wr_pointer.src_offset);
u64 pointer = 0;

/* Writing back to a file that may not be loaded in RAM */
dest_file = romfile_find(entry->wr_pointer.dest_file);
src_file = romfile_loader_find(entry->wr_pointer.src_file, files);

if (!dest_file || !src_file || !src_file->data ||
dst_offset + entry->wr_pointer.size < dst_offset ||
dst_offset + entry->wr_pointer.size > dest_file->size ||
src_offset >= src_file->file->size ||
entry->wr_pointer.size < 1 || entry->wr_pointer.size > 8 ||
entry->wr_pointer.size & (entry->wr_pointer.size - 1)) {
goto err;
}

pointer = (unsigned long)src_file->data + src_offset;
/* Make sure the pointer fits within wr_pointer.size */
if ((entry->wr_pointer.size != sizeof(u64)) &&
((pointer >> (entry->wr_pointer.size * 8)) > 0)) {
goto err;
}
pointer = cpu_to_le64(pointer);

/* Only supported on QEMU */
if (qemu_cfg_write_file(&pointer, dest_file, dst_offset,
entry->wr_pointer.size) != entry->wr_pointer.size) {
goto err;
}

/* Store the info so it can replayed later if necessary */
struct romfile_wr_pointer_entry *store = malloc_high(sizeof(*store));
if (!store) {
warn_noalloc();
return;
}
store->pointer = pointer;
store->key = qemu_get_romfile_key(dest_file);
store->offset = dst_offset;
store->ptr_size = entry->wr_pointer.size;
hlist_add_head(&store->node, &romfile_pointer_list);

return;
err:
warn_internalerror();
}

int romfile_loader_execute(const char *name)
{
struct romfile_loader_entry_s *entry;
Expand Down Expand Up @@ -161,6 +239,10 @@ int romfile_loader_execute(const char *name)
break;
case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM:
romfile_loader_add_checksum(entry, files);
break;
case ROMFILE_LOADER_COMMAND_WRITE_POINTER:
romfile_loader_write_pointer(entry, files);
break;
default:
/* Skip commands that we don't recognize. */
break;
Expand Down
65 changes: 42 additions & 23 deletions src/fw/romfile_loader.h
Expand Up @@ -11,55 +11,72 @@ struct romfile_loader_entry_s {
u32 command;
union {
/*
* COMMAND_ALLOCATE - allocate a table from @alloc_file
* subject to @alloc_align alignment (must be power of 2)
* and @alloc_zone (can be HIGH or FSEG) requirements.
* COMMAND_ALLOCATE - allocate a table from @alloc.file
* subject to @alloc.align alignment (must be power of 2)
* and @alloc.zone (can be HIGH or FSEG) requirements.
*
* Must appear exactly once for each file, and before
* this file is referenced by any other command.
*/
struct {
char alloc_file[ROMFILE_LOADER_FILESZ];
u32 alloc_align;
u8 alloc_zone;
};
char file[ROMFILE_LOADER_FILESZ];
u32 align;
u8 zone;
} alloc;

/*
* COMMAND_ADD_POINTER - patch the table (originating from
* @dest_file) at @pointer_offset, by adding a pointer to the table
* @dest_file) at @pointer.offset, by adding a pointer to the table
* originating from @src_file. 1,2,4 or 8 byte unsigned
* addition is used depending on @pointer_size.
* addition is used depending on @pointer.size.
*/
struct {
char pointer_dest_file[ROMFILE_LOADER_FILESZ];
char pointer_src_file[ROMFILE_LOADER_FILESZ];
u32 pointer_offset;
u8 pointer_size;
};
char dest_file[ROMFILE_LOADER_FILESZ];
char src_file[ROMFILE_LOADER_FILESZ];
u32 offset;
u8 size;
} pointer;

/*
* COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by
* @cksum_start and @cksum_length fields,
* @cksum.start and @cksum.length fields,
* and then add the value at @cksum_offset.
* Checksum simply sums -X for each byte X in the range
* using 8-bit math.
*/
struct {
char cksum_file[ROMFILE_LOADER_FILESZ];
u32 cksum_offset;
u32 cksum_start;
u32 cksum_length;
};
char file[ROMFILE_LOADER_FILESZ];
u32 offset;
u32 start;
u32 length;
} cksum;

/*
* COMMAND_WRITE_POINTER - Write back to a host file via DMA,
* @wr_pointer.dest_file at offset @wr_pointer.dst_offset, a pointer
* to the table originating from @wr_pointer.src_file at offset
* @wr_pointer.src_offset.
* 1,2,4 or 8 byte unsigned addition is used depending on
* @wr_pointer.size.
*/
struct {
char dest_file[ROMFILE_LOADER_FILESZ];
char src_file[ROMFILE_LOADER_FILESZ];
u32 dst_offset;
u32 src_offset;
u8 size;
} wr_pointer;

/* padding */
char pad[124];
};
};

enum {
ROMFILE_LOADER_COMMAND_ALLOCATE = 0x1,
ROMFILE_LOADER_COMMAND_ADD_POINTER = 0x2,
ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
ROMFILE_LOADER_COMMAND_ALLOCATE = 0x1,
ROMFILE_LOADER_COMMAND_ADD_POINTER = 0x2,
ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
ROMFILE_LOADER_COMMAND_WRITE_POINTER = 0x4,
};

enum {
Expand All @@ -69,4 +86,6 @@ enum {

int romfile_loader_execute(const char *name);

void romfile_fw_cfg_resume(void);

#endif
14 changes: 13 additions & 1 deletion src/fw/shadow.c
Expand Up @@ -167,7 +167,7 @@ make_bios_readonly(void)
}

void
qemu_prep_reset(void)
qemu_reboot(void)
{
if (!CONFIG_QEMU || runningOnXen())
return;
Expand All @@ -187,4 +187,16 @@ qemu_prep_reset(void)
memcpy(hrp + 4, hrp + 4 + BIOS_SRC_OFFSET, cend - (hrp + 4));
barrier();
HaveRunPost = 0;
barrier();

// Request a QEMU system reset. Do the reset in this function as
// the BIOS code was overwritten above and not all BIOS
// functionality may be available.

// Attempt PCI style reset
outb(0x02, PORT_PCI_REBOOT);
outb(0x06, PORT_PCI_REBOOT);

// Next try triple faulting the CPU to force a reset
asm volatile("int3");
}
11 changes: 10 additions & 1 deletion src/fw/smm.c
Expand Up @@ -52,7 +52,8 @@ struct smm_state {
struct smm_layout {
struct smm_state backup1;
struct smm_state backup2;
u8 stack[0x7c00];
u32 backup_a20;
u8 stack[0x8000 - sizeof(struct smm_state)*2 - sizeof(u32)];
u64 codeentry;
u8 pad_8008[0x7df8];
struct smm_state cpu;
Expand Down Expand Up @@ -102,10 +103,14 @@ handle_smi(u16 cs)
memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu));
memcpy(&smm->cpu.i32.eax, regs, sizeof(regs));
smm->cpu.i32.eip = regs[3];
// Enable a20 and backup its previous state
smm->backup_a20 = set_a20(1);
} else if (smm->cpu.i32.ecx == CALL32SMM_RETURNID) {
dprintf(9, "smm cpu ret %x esp=%x\n", regs[3], regs[4]);
memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu));
memcpy(&smm->cpu.i32.eax, regs, sizeof(regs));
if (!smm->backup_a20)
set_a20(0);
smm->cpu.i32.eip = regs[3];
}
} else if (rev == SMM_REV_I64) {
Expand All @@ -116,9 +121,13 @@ handle_smi(u16 cs)
memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu));
memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs));
smm->cpu.i64.rip = (u32)regs[4];
// Enable a20 and backup its previous state
smm->backup_a20 = set_a20(1);
} else if ((u32)smm->cpu.i64.rcx == CALL32SMM_RETURNID) {
memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu));
memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs));
if (!smm->backup_a20)
set_a20(0);
smm->cpu.i64.rip = (u32)regs[4];
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/fw/smp.c
Expand Up @@ -176,7 +176,7 @@ smp_setup(void)
return;

MaxCountCPUs = romfile_loadint("etc/max-cpus", 0);
u16 smp_count = qemu_init_present_cpus_count();
u16 smp_count = qemu_get_present_cpus_count();
if (MaxCountCPUs < smp_count)
MaxCountCPUs = smp_count;

Expand Down
10 changes: 7 additions & 3 deletions src/hw/ahci.c
Expand Up @@ -130,7 +130,6 @@ static int ahci_command(struct ahci_port_s *port_gf, int iswrite, int isatapi,
intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
if (intbits)
ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1);
ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1);

u32 end = timer_calc(AHCI_REQUEST_TIMEOUT);
Expand Down Expand Up @@ -218,7 +217,7 @@ int ahci_atapi_process_op(struct disk_op_s *op)
return 0;

struct ahci_port_s *port_gf = container_of(
op->drive_gf, struct ahci_port_s, drive);
op->drive_fl, struct ahci_port_s, drive);
struct ahci_cmd_s *cmd = port_gf->cmd;

if (op->command == CMD_WRITE || op->command == CMD_FORMAT)
Expand All @@ -238,7 +237,7 @@ static int
ahci_disk_readwrite_aligned(struct disk_op_s *op, int iswrite)
{
struct ahci_port_s *port_gf = container_of(
op->drive_gf, struct ahci_port_s, drive);
op->drive_fl, struct ahci_port_s, drive);
struct ahci_cmd_s *cmd = port_gf->cmd;
int rc;

Expand Down Expand Up @@ -361,6 +360,11 @@ ahci_port_alloc(struct ahci_ctrl_s *ctrl, u32 pnr)

ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis);
if (ctrl->caps & HOST_CAP_64) {
ahci_port_writel(ctrl, pnr, PORT_LST_ADDR_HI, 0);
ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR_HI, 0);
}

return port;
}

Expand Down
26 changes: 13 additions & 13 deletions src/hw/ata.c
Expand Up @@ -271,15 +271,15 @@ ata_cmd_nondata(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd)
****************************************************************/

// Transfer 'op->count' blocks (of 'blocksize' bytes) to/from drive
// 'op->drive_gf'.
// 'op->drive_fl'.
static int
ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize)
{
dprintf(16, "ata_pio_transfer id=%p write=%d count=%d bs=%d buf=%p\n"
, op->drive_gf, iswrite, op->count, blocksize, op->buf_fl);
, op->drive_fl, iswrite, op->count, blocksize, op->buf_fl);

struct atadrive_s *adrive_gf = container_of(
op->drive_gf, struct atadrive_s, drive);
op->drive_fl, struct atadrive_s, drive);
struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Expand All @@ -289,14 +289,14 @@ ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize)
for (;;) {
if (iswrite) {
// Write data to controller
dprintf(16, "Write sector id=%p dest=%p\n", op->drive_gf, buf_fl);
dprintf(16, "Write sector id=%p dest=%p\n", op->drive_fl, buf_fl);
if (CONFIG_ATA_PIO32)
outsl_fl(iobase1, buf_fl, blocksize / 4);
else
outsw_fl(iobase1, buf_fl, blocksize / 2);
} else {
// Read data from controller
dprintf(16, "Read sector id=%p dest=%p\n", op->drive_gf, buf_fl);
dprintf(16, "Read sector id=%p dest=%p\n", op->drive_fl, buf_fl);
if (CONFIG_ATA_PIO32)
insl_fl(iobase1, buf_fl, blocksize / 4);
else
Expand Down Expand Up @@ -366,7 +366,7 @@ ata_try_dma(struct disk_op_s *op, int iswrite, int blocksize)
// Need minimum alignment of 1.
return -1;
struct atadrive_s *adrive_gf = container_of(
op->drive_gf, struct atadrive_s, drive);
op->drive_fl, struct atadrive_s, drive);
struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster);
if (! iomaster)
Expand Down Expand Up @@ -413,10 +413,10 @@ ata_dma_transfer(struct disk_op_s *op)
{
if (! CONFIG_ATA_DMA)
return -1;
dprintf(16, "ata_dma_transfer id=%p buf=%p\n", op->drive_gf, op->buf_fl);
dprintf(16, "ata_dma_transfer id=%p buf=%p\n", op->drive_fl, op->buf_fl);

struct atadrive_s *adrive_gf = container_of(
op->drive_gf, struct atadrive_s, drive);
op->drive_fl, struct atadrive_s, drive);
struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster);

Expand Down Expand Up @@ -466,7 +466,7 @@ static int
ata_pio_cmd_data(struct disk_op_s *op, int iswrite, struct ata_pio_command *cmd)
{
struct atadrive_s *adrive_gf = container_of(
op->drive_gf, struct atadrive_s, drive);
op->drive_fl, struct atadrive_s, drive);
struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Expand Down Expand Up @@ -495,7 +495,7 @@ ata_dma_cmd_data(struct disk_op_s *op, struct ata_pio_command *cmd)
if (! CONFIG_ATA_DMA)
return -1;
struct atadrive_s *adrive_gf = container_of(
op->drive_gf, struct atadrive_s, drive);
op->drive_fl, struct atadrive_s, drive);
int ret = send_cmd(adrive_gf, cmd);
if (ret)
return ret;
Expand Down Expand Up @@ -559,7 +559,7 @@ ata_process_op(struct disk_op_s *op)
return 0;

struct atadrive_s *adrive_gf = container_of(
op->drive_gf, struct atadrive_s, drive);
op->drive_fl, struct atadrive_s, drive);
switch (op->command) {
case CMD_READ:
return ata_readwrite(op, 0);
Expand Down Expand Up @@ -597,7 +597,7 @@ ata_atapi_process_op(struct disk_op_s *op)
return default_process_op(op);

struct atadrive_s *adrive_gf = container_of(
op->drive_gf, struct atadrive_s, drive);
op->drive_fl, struct atadrive_s, drive);
struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
Expand Down Expand Up @@ -667,7 +667,7 @@ send_ata_identity(struct atadrive_s *adrive, u16 *buffer, int command)

struct disk_op_s dop;
memset(&dop, 0, sizeof(dop));
dop.drive_gf = &adrive->drive;
dop.drive_fl = &adrive->drive;
dop.count = 1;
dop.lba = 1;
dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer);
Expand Down
107 changes: 103 additions & 4 deletions src/hw/blockcmd.c
Expand Up @@ -5,14 +5,15 @@
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

#include "biosvar.h" // GET_GLOBALFLAT
#include "block.h" // struct disk_op_s
#include "blockcmd.h" // struct cdb_request_sense
#include "byteorder.h" // be32_to_cpu
#include "farptr.h" // GET_FLATPTR
#include "output.h" // dprintf
#include "std/disk.h" // DISK_RET_EPARAM
#include "string.h" // memset
#include "util.h" // timer_calc
#include "malloc.h"


/****************************************************************
Expand Down Expand Up @@ -116,7 +117,7 @@ scsi_fill_cmd(struct disk_op_s *op, void *cdbcmd, int maxcdb)
: CDB_CMD_WRITE_10);
cmd->lba = cpu_to_be32(op->lba);
cmd->count = cpu_to_be16(op->count);
return GET_GLOBALFLAT(op->drive_gf->blksize);
return GET_FLATPTR(op->drive_fl->blksize);
case CMD_SCSI:
if (MODESEGMENT)
return -1;
Expand All @@ -140,7 +141,7 @@ int
scsi_is_ready(struct disk_op_s *op)
{
ASSERT32FLAT();
dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_gf);
dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_fl);

/* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is
* reported by the device. If the device reports "IN PROGRESS",
Expand Down Expand Up @@ -181,14 +182,109 @@ scsi_is_ready(struct disk_op_s *op)
return 0;
}

#define CDB_CMD_REPORT_LUNS 0xA0

struct cdb_report_luns {
u8 command;
u8 reserved_01[5];
u32 length;
u8 pad[6];
} PACKED;

struct scsi_lun {
u16 lun[4];
};

struct cdbres_report_luns {
u32 length;
u32 reserved;
struct scsi_lun luns[];
};

static u64 scsilun2u64(struct scsi_lun *scsi_lun)
{
int i;
u64 ret = 0;
for (i = 0; i < ARRAY_SIZE(scsi_lun->lun); i++)
ret |= be16_to_cpu(scsi_lun->lun[i]) << (16 * i);
return ret;
}

// Issue REPORT LUNS on a temporary drive and iterate reported luns calling
// @add_lun for each
int scsi_rep_luns_scan(struct drive_s *tmp_drive, scsi_add_lun add_lun)
{
int ret = -1;
/* start with the smallest possible buffer, otherwise some devices in QEMU
* may (incorrectly) error out on returning less data than fits in it */
u32 maxluns = 1;
u32 nluns, i;
struct cdb_report_luns cdb = {
.command = CDB_CMD_REPORT_LUNS,
};
struct disk_op_s op = {
.drive_fl = tmp_drive,
.command = CMD_SCSI,
.count = 1,
.cdbcmd = &cdb,
};
struct cdbres_report_luns *resp;

ASSERT32FLAT();

while (1) {
op.blocksize = sizeof(struct cdbres_report_luns) +
maxluns * sizeof(struct scsi_lun);
op.buf_fl = malloc_tmp(op.blocksize);
if (!op.buf_fl) {
warn_noalloc();
return -1;
}

cdb.length = cpu_to_be32(op.blocksize);
if (process_op(&op) != DISK_RET_SUCCESS)
goto out;

resp = op.buf_fl;
nluns = be32_to_cpu(resp->length) / sizeof(struct scsi_lun);
if (nluns <= maxluns)
break;

free(op.buf_fl);
maxluns = nluns;
}

for (i = 0, ret = 0; i < nluns; i++) {
u64 lun = scsilun2u64(&resp->luns[i]);
if (lun >> 32)
continue;
ret += !add_lun((u32)lun, tmp_drive);
}
out:
free(op.buf_fl);
return ret;
}

// Iterate LUNs on the target and call @add_lun for each
int scsi_sequential_scan(struct drive_s *tmp_drive, u32 maxluns,
scsi_add_lun add_lun)
{
int ret;
u32 lun;

for (lun = 0, ret = 0; lun < maxluns; lun++)
ret += !add_lun(lun, tmp_drive);
return ret;
}

// Validate drive, find block size / sector count, and register drive.
int
scsi_drive_setup(struct drive_s *drive, const char *s, int prio)
{
ASSERT32FLAT();
struct disk_op_s dop;
memset(&dop, 0, sizeof(dop));
dop.drive_gf = drive;
dop.drive_fl = drive;
struct cdbres_inquiry data;
int ret = cdb_get_inquiry(&dop, &data);
if (ret)
Expand Down Expand Up @@ -217,6 +313,9 @@ scsi_drive_setup(struct drive_s *drive, const char *s, int prio)
return 0;
}

if (pdt != SCSI_TYPE_DISK)
return -1;

ret = scsi_is_ready(&dop);
if (ret) {
dprintf(1, "scsi_is_ready returned %d\n", ret);
Expand Down
4 changes: 4 additions & 0 deletions src/hw/blockcmd.h
Expand Up @@ -106,5 +106,9 @@ int scsi_is_read(struct disk_op_s *op);
int scsi_is_ready(struct disk_op_s *op);
struct drive_s;
int scsi_drive_setup(struct drive_s *drive, const char *s, int prio);
typedef int (*scsi_add_lun)(u32 lun, struct drive_s *tmpl_drv);
int scsi_rep_luns_scan(struct drive_s *tmp_drive, scsi_add_lun add_lun);
int scsi_sequential_scan(struct drive_s *tmp_drive, u32 maxluns,
scsi_add_lun add_lun);

#endif // blockcmd.h
37 changes: 26 additions & 11 deletions src/hw/esp-scsi.c
Expand Up @@ -83,7 +83,7 @@ esp_scsi_process_op(struct disk_op_s *op)
if (!CONFIG_ESP_SCSI)
return DISK_RET_EBADTRACK;
struct esp_lun_s *llun_gf =
container_of(op->drive_gf, struct esp_lun_s, drive);
container_of(op->drive_fl, struct esp_lun_s, drive);
u16 target = GET_GLOBALFLAT(llun_gf->target);
u16 lun = GET_GLOBALFLAT(llun_gf->lun);
u8 cdbcmd[16];
Expand Down Expand Up @@ -153,24 +153,35 @@ esp_scsi_process_op(struct disk_op_s *op)
return DISK_RET_EBADTRACK;
}

static int
esp_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
static void
esp_scsi_init_lun(struct esp_lun_s *llun, struct pci_device *pci, u32 iobase,
u8 target, u8 lun)
{
struct esp_lun_s *llun = malloc_fseg(sizeof(*llun));
if (!llun) {
warn_noalloc();
return -1;
}
memset(llun, 0, sizeof(*llun));
llun->drive.type = DTYPE_ESP_SCSI;
llun->drive.cntl_id = pci->bdf;
llun->pci = pci;
llun->target = target;
llun->lun = lun;
llun->iobase = iobase;
}

static int
esp_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv)
{
struct esp_lun_s *tmpl_llun =
container_of(tmpl_drv, struct esp_lun_s, drive);
struct esp_lun_s *llun = malloc_fseg(sizeof(*llun));
if (!llun) {
warn_noalloc();
return -1;
}
esp_scsi_init_lun(llun, tmpl_llun->pci, tmpl_llun->iobase,
tmpl_llun->target, lun);

char *name = znprintf(MAXDESCSIZE, "esp %pP %d:%d", pci, target, lun);
int prio = bootprio_find_scsi_device(pci, target, lun);
char *name = znprintf(MAXDESCSIZE, "esp %pP %d:%d",
llun->pci, llun->target, llun->lun);
int prio = bootprio_find_scsi_device(llun->pci, llun->target, llun->lun);
int ret = scsi_drive_setup(&llun->drive, name, prio);
free(name);
if (ret)
Expand All @@ -185,7 +196,11 @@ esp_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
static void
esp_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target)
{
esp_scsi_add_lun(pci, iobase, target, 0);
struct esp_lun_s llun0;

esp_scsi_init_lun(&llun0, pci, iobase, target, 0);

scsi_rep_luns_scan(&llun0.drive, esp_scsi_add_lun);
}

static void
Expand Down
20 changes: 10 additions & 10 deletions src/hw/floppy.c
Expand Up @@ -473,7 +473,7 @@ floppy_dma_cmd(struct disk_op_s *op, int count, int command, u8 *param)
return DISK_RET_EBOUNDARY;

// Invoke floppy controller
u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
u8 floppyid = GET_GLOBALFLAT(op->drive_fl->cntl_id);
ret = floppy_drive_pio(floppyid, command, param);
if (ret)
return ret;
Expand Down Expand Up @@ -506,11 +506,11 @@ lba2chs(struct disk_op_s *op)
struct chs_s res = { };

u32 tmp = op->lba;
u16 nls = GET_GLOBALFLAT(op->drive_gf->lchs.sector);
u16 nls = GET_GLOBALFLAT(op->drive_fl->lchs.sector);
res.sector = (tmp % nls) + 1;

tmp /= nls;
u16 nlh = GET_GLOBALFLAT(op->drive_gf->lchs.head);
u16 nlh = GET_GLOBALFLAT(op->drive_fl->lchs.head);
res.head = tmp % nlh;

tmp /= nlh;
Expand Down Expand Up @@ -538,12 +538,12 @@ static int
floppy_read(struct disk_op_s *op)
{
struct chs_s chs = lba2chs(op);
int ret = floppy_prep(op->drive_gf, chs.cylinder);
int ret = floppy_prep(op->drive_fl, chs.cylinder);
if (ret)
return ret;

// send read-normal-data command to controller
u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
u8 floppyid = GET_GLOBALFLAT(op->drive_fl->cntl_id);
u8 param[8];
param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2
param[1] = chs.cylinder;
Expand All @@ -561,12 +561,12 @@ static int
floppy_write(struct disk_op_s *op)
{
struct chs_s chs = lba2chs(op);
int ret = floppy_prep(op->drive_gf, chs.cylinder);
int ret = floppy_prep(op->drive_fl, chs.cylinder);
if (ret)
return ret;

// send write-normal-data command to controller
u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
u8 floppyid = GET_GLOBALFLAT(op->drive_fl->cntl_id);
u8 param[8];
param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2
param[1] = chs.cylinder;
Expand All @@ -584,7 +584,7 @@ static int
floppy_verify(struct disk_op_s *op)
{
struct chs_s chs = lba2chs(op);
int ret = floppy_prep(op->drive_gf, chs.cylinder);
int ret = floppy_prep(op->drive_fl, chs.cylinder);
if (ret)
return ret;

Expand All @@ -597,12 +597,12 @@ static int
floppy_format(struct disk_op_s *op)
{
struct chs_s chs = lba2chs(op);
int ret = floppy_prep(op->drive_gf, chs.cylinder);
int ret = floppy_prep(op->drive_fl, chs.cylinder);
if (ret)
return ret;

// send format-track command to controller
u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
u8 floppyid = GET_GLOBALFLAT(op->drive_fl->cntl_id);
u8 param[7];
param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2
param[1] = FLOPPY_SIZE_CODE;
Expand Down
39 changes: 27 additions & 12 deletions src/hw/lsi-scsi.c
Expand Up @@ -57,7 +57,7 @@ lsi_scsi_process_op(struct disk_op_s *op)
if (!CONFIG_LSI_SCSI)
return DISK_RET_EBADTRACK;
struct lsi_lun_s *llun_gf =
container_of(op->drive_gf, struct lsi_lun_s, drive);
container_of(op->drive_fl, struct lsi_lun_s, drive);
u16 target = GET_GLOBALFLAT(llun_gf->target);
u16 lun = GET_GLOBALFLAT(llun_gf->lun);
u8 cdbcmd[16];
Expand Down Expand Up @@ -132,24 +132,35 @@ lsi_scsi_process_op(struct disk_op_s *op)
return DISK_RET_EBADTRACK;
}

static int
lsi_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
static void
lsi_scsi_init_lun(struct lsi_lun_s *llun, struct pci_device *pci, u32 iobase,
u8 target, u8 lun)
{
struct lsi_lun_s *llun = malloc_fseg(sizeof(*llun));
if (!llun) {
warn_noalloc();
return -1;
}
memset(llun, 0, sizeof(*llun));
llun->drive.type = DTYPE_LSI_SCSI;
llun->drive.cntl_id = pci->bdf;
llun->pci = pci;
llun->target = target;
llun->lun = lun;
llun->iobase = iobase;
}

static int
lsi_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv)
{
struct lsi_lun_s *tmpl_llun =
container_of(tmpl_drv, struct lsi_lun_s, drive);
struct lsi_lun_s *llun = malloc_fseg(sizeof(*llun));
if (!llun) {
warn_noalloc();
return -1;
}
lsi_scsi_init_lun(llun, tmpl_llun->pci, tmpl_llun->iobase,
tmpl_llun->target, lun);

char *name = znprintf(MAXDESCSIZE, "lsi %pP %d:%d", pci, target, lun);
int prio = bootprio_find_scsi_device(pci, target, lun);
char *name = znprintf(MAXDESCSIZE, "lsi %pP %d:%d",
llun->pci, llun->target, llun->lun);
int prio = bootprio_find_scsi_device(llun->pci, llun->target, llun->lun);
int ret = scsi_drive_setup(&llun->drive, name, prio);
free(name);
if (ret)
Expand All @@ -164,8 +175,12 @@ lsi_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
static void
lsi_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target)
{
/* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */
lsi_scsi_add_lun(pci, iobase, target, 0);
struct lsi_lun_s llun0;

lsi_scsi_init_lun(&llun0, pci, iobase, target, 0);

if (scsi_rep_luns_scan(&llun0.drive, lsi_scsi_add_lun) < 0)
scsi_sequential_scan(&llun0.drive, 8, lsi_scsi_add_lun);
}

static void
Expand Down
2 changes: 1 addition & 1 deletion src/hw/megasas.c
Expand Up @@ -167,7 +167,7 @@ megasas_process_op(struct disk_op_s *op)
if (blocksize < 0)
return default_process_op(op);
struct megasas_lun_s *mlun_gf =
container_of(op->drive_gf, struct megasas_lun_s, drive);
container_of(op->drive_fl, struct megasas_lun_s, drive);
struct megasas_cmd_frame *frame = GET_GLOBALFLAT(mlun_gf->frame);
u16 pci_id = GET_GLOBALFLAT(mlun_gf->pci_id);
int i;
Expand Down
50 changes: 31 additions & 19 deletions src/hw/mpt-scsi.c
Expand Up @@ -118,9 +118,6 @@ static int
mpt_scsi_cmd(u32 iobase, struct disk_op_s *op,
u8 *cdb, u16 target, u16 lun, u16 blocksize)
{
if (lun != 0)
return DISK_RET_ENOTREADY;

u32 end = timer_calc(MPT_POLL_TIMEOUT);

u8 sense_buf[18];
Expand Down Expand Up @@ -191,31 +188,42 @@ mpt_scsi_process_op(struct disk_op_s *op)
return default_process_op(op);

struct mpt_lun_s *llun_gf =
container_of(op->drive_gf, struct mpt_lun_s, drive);
container_of(op->drive_fl, struct mpt_lun_s, drive);
u16 target = GET_GLOBALFLAT(llun_gf->target);
u16 lun = GET_GLOBALFLAT(llun_gf->lun);
u32 iobase = GET_GLOBALFLAT(llun_gf->iobase);
return mpt_scsi_cmd(iobase, op, cdbcmd, target, lun, blocksize);
}

static int
mpt_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
static void
mpt_scsi_init_lun(struct mpt_lun_s *llun, struct pci_device *pci,
u32 iobase, u8 target, u8 lun)
{
struct mpt_lun_s *llun = malloc_fseg(sizeof(*llun));
if (!llun) {
warn_noalloc();
return -1;
}
memset(llun, 0, sizeof(*llun));
llun->drive.type = DTYPE_MPT_SCSI;
llun->drive.cntl_id = pci->bdf;
llun->pci = pci;
llun->target = target;
llun->lun = lun;
llun->iobase = iobase;
}

char *name = znprintf(MAXDESCSIZE, "mpt %pP %d:%d", pci, target, lun);
int prio = bootprio_find_scsi_device(pci, target, lun);
static int
mpt_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv)
{
struct mpt_lun_s *tmpl_llun =
container_of(tmpl_drv, struct mpt_lun_s, drive);
struct mpt_lun_s *llun = malloc_fseg(sizeof(*llun));
if (!llun) {
warn_noalloc();
return -1;
}
mpt_scsi_init_lun(llun, tmpl_llun->pci, tmpl_llun->iobase,
tmpl_llun->target, lun);

char *name = znprintf(MAXDESCSIZE, "mpt %pP %d:%d",
llun->pci, llun->target, llun->lun);
int prio = bootprio_find_scsi_device(llun->pci, llun->target, llun->lun);
int ret = scsi_drive_setup(&llun->drive, name, prio);
free(name);
if (ret) {
Expand All @@ -231,8 +239,12 @@ mpt_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
static void
mpt_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target)
{
/* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */
mpt_scsi_add_lun(pci, iobase, target, 0);
struct mpt_lun_s llun0;

mpt_scsi_init_lun(&llun0, pci, iobase, target, 0);

if (scsi_rep_luns_scan(&llun0.drive, mpt_scsi_add_lun) < 0)
scsi_sequential_scan(&llun0.drive, 8, mpt_scsi_add_lun);
}

static inline void
Expand Down Expand Up @@ -262,12 +274,12 @@ init_mpt_scsi(void *data)

// send IOC Init message through the doorbell
mpt_out_doorbell(MPT_DOORBELL_HANDSHAKE,
sizeof(MptIOCInitRequest)/sizeof(u32),
iobase);
sizeof(MptIOCInitRequest)/sizeof(u32),
iobase);

outsl(iobase + MPT_REG_DOORBELL,
(u32 *)&MptIOCInitRequest,
sizeof(MptIOCInitRequest)/sizeof(u32));
(u32 *)&MptIOCInitRequest,
sizeof(MptIOCInitRequest)/sizeof(u32));

// Read the reply 16 bits at a time. Cannot use insl
// because the port is 32 bits wide.
Expand Down
199 changes: 199 additions & 0 deletions src/hw/nvme-int.h
@@ -0,0 +1,199 @@
// NVMe datastructures and constants
//
// Copyright 2017 Amazon.com, Inc. or its affiliates.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

#ifndef __NVME_INT_H
#define __NVME_INT_H

#include "types.h" // u32
#include "pcidevice.h" // struct pci_device

/* Data structures */

/* The register file of a NVMe host controller. This struct follows the naming
scheme in the NVMe specification. */
struct nvme_reg {
u64 cap; /* controller capabilities */
u32 vs; /* version */
u32 intms; /* interrupt mask set */
u32 intmc; /* interrupt mask clear */
u32 cc; /* controller configuration */
u32 _res0;
u32 csts; /* controller status */
u32 _res1;
u32 aqa; /* admin queue attributes */
u64 asq; /* admin submission queue base address */
u64 acq; /* admin completion queue base address */
};

/* Submission queue entry */
struct nvme_sqe {
union {
u32 dword[16];
struct {
u32 cdw0; /* Command DWORD 0 */
u32 nsid; /* Namespace ID */
u64 _res0;
u64 mptr; /* metadata ptr */

u64 dptr_prp1;
u64 dptr_prp2;
};
};
};

/* Completion queue entry */
struct nvme_cqe {
union {
u32 dword[4];
struct {
u32 cdw0;
u32 _res0;
u16 sq_head;
u16 sq_id;
u16 cid;
u16 status;
};
};
};

/* The common part of every submission or completion queue. */
struct nvme_queue {
u32 *dbl; /* doorbell */
u16 mask; /* length - 1 */
};

struct nvme_cq {
struct nvme_queue common;
struct nvme_cqe *cqe;

/* We have read upto (but not including) this entry in the queue. */
u16 head;

/* The current phase bit the controller uses to indicate that it has written
a new entry. This is inverted after each wrap. */
unsigned phase : 1;
};

struct nvme_sq {
struct nvme_queue common;
struct nvme_sqe *sqe;

/* Corresponding completion queue. We only support a single SQ per CQ. */
struct nvme_cq *cq;

/* The last entry the controller has fetched. */
u16 head;

/* The last value we have written to the tail doorbell. */
u16 tail;
};

struct nvme_ctrl {
struct pci_device *pci;
struct nvme_reg volatile *reg;

u32 doorbell_stride; /* in bytes */

struct nvme_sq admin_sq;
struct nvme_cq admin_cq;

u32 ns_count;
struct nvme_namespace *ns;

struct nvme_sq io_sq;
struct nvme_cq io_cq;
};

struct nvme_namespace {
struct drive_s drive;
struct nvme_ctrl *ctrl;

u32 ns_id;

u64 lba_count; /* The total amount of sectors. */

u32 block_size;
u32 metadata_size;

/* Page aligned buffer of size NVME_PAGE_SIZE. */
char *dma_buffer;
};

/* Data structures for NVMe admin identify commands */

struct nvme_identify_ctrl {
u16 vid;
u16 ssvid;
char sn[20];
char mn[40];
char fr[8];

char _boring[516 - 72];

u32 nn; /* number of namespaces */
};

struct nvme_identify_ns_list {
u32 ns_id[1024];
};

struct nvme_lba_format {
u16 ms;
u8 lbads;
u8 rp;
u8 res;
};

struct nvme_identify_ns {
u64 nsze;
u64 ncap;
u64 nuse;
u8 nsfeat;
u8 nlbaf;
u8 flbas;

char _boring[128 - 27];

struct nvme_lba_format lbaf[16];
};

union nvme_identify {
struct nvme_identify_ns ns;
struct nvme_identify_ctrl ctrl;
struct nvme_identify_ns_list ns_list;
};

/* NVMe constants */

#define NVME_CAP_CSS_NVME (1ULL << 37)

#define NVME_CSTS_FATAL (1U << 1)
#define NVME_CSTS_RDY (1U << 0)

#define NVME_CC_EN (1U << 0)

#define NVME_SQE_OPC_ADMIN_CREATE_IO_SQ 1U
#define NVME_SQE_OPC_ADMIN_CREATE_IO_CQ 5U
#define NVME_SQE_OPC_ADMIN_IDENTIFY 6U

#define NVME_SQE_OPC_IO_WRITE 1U
#define NVME_SQE_OPC_IO_READ 2U

#define NVME_ADMIN_IDENTIFY_CNS_ID_NS 0U
#define NVME_ADMIN_IDENTIFY_CNS_ID_CTRL 1U
#define NVME_ADMIN_IDENTIFY_CNS_GET_NS_LIST 2U

#define NVME_CQE_DW3_P (1U << 16)

#define NVME_PAGE_SIZE 4096

/* Length for the queue entries. */
#define NVME_SQE_SIZE_LOG 6
#define NVME_CQE_SIZE_LOG 4

#endif

/* EOF */
708 changes: 708 additions & 0 deletions src/hw/nvme.c

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions src/hw/nvme.h
@@ -0,0 +1,17 @@
// External interfaces for low level NVMe support
//
// Copyright 2017 Amazon.com, Inc. or its affiliates.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

#ifndef __NVME_H
#define __NVME_H

#include "block.h" // struct disk_op_s

void nvme_setup(void);
int nvme_process_op(struct disk_op_s *op);

#endif

/* EOF */
25 changes: 24 additions & 1 deletion src/hw/pci.c
Expand Up @@ -12,7 +12,6 @@
#include "x86.h" // outl

#define PORT_PCI_CMD 0x0cf8
#define PORT_PCI_REBOOT 0x0cf9
#define PORT_PCI_DATA 0x0cfc

void pci_config_writel(u16 bdf, u32 addr, u32 val)
Expand Down Expand Up @@ -59,6 +58,30 @@ pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
pci_config_writew(bdf, addr, val);
}

u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap)
{
int i;
u16 status = pci_config_readw(bdf, PCI_STATUS);

if (!(status & PCI_STATUS_CAP_LIST))
return 0;

if (cap == 0) {
/* find first */
cap = pci_config_readb(bdf, PCI_CAPABILITY_LIST);
} else {
/* find next */
cap = pci_config_readb(bdf, cap + PCI_CAP_LIST_NEXT);
}
for (i = 0; cap && i <= 0xff; i++) {
if (pci_config_readb(bdf, cap + PCI_CAP_LIST_ID) == cap_id)
return cap;
cap = pci_config_readb(bdf, cap + PCI_CAP_LIST_NEXT);
}

return 0;
}

// Helper function for foreachbdf() macro - return next device
int
pci_next(int bdf, int bus)
Expand Down
3 changes: 3 additions & 0 deletions src/hw/pci.h
Expand Up @@ -3,6 +3,8 @@

#include "types.h" // u32

#define PORT_PCI_REBOOT 0x0cf9

static inline u8 pci_bdf_to_bus(u16 bdf) {
return bdf >> 8;
}
Expand Down Expand Up @@ -37,6 +39,7 @@ u32 pci_config_readl(u16 bdf, u32 addr);
u16 pci_config_readw(u16 bdf, u32 addr);
u8 pci_config_readb(u16 bdf, u32 addr);
void pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on);
u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap);
int pci_next(int bdf, int bus);
int pci_probe_host(void);
void pci_reboot(void);
Expand Down
4 changes: 4 additions & 0 deletions src/hw/pci_ids.h
Expand Up @@ -18,6 +18,7 @@
#define PCI_CLASS_STORAGE_SATA 0x0106
#define PCI_CLASS_STORAGE_SATA_AHCI 0x010601
#define PCI_CLASS_STORAGE_SAS 0x0107
#define PCI_CLASS_STORAGE_NVME 0x0108
#define PCI_CLASS_STORAGE_OTHER 0x0180

#define PCI_BASE_CLASS_NETWORK 0x02
Expand Down Expand Up @@ -2262,6 +2263,9 @@
#define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600
#define PCI_DEVICE_ID_KORENIX_JETCARDF1 0x16ff

#define PCI_VENDOR_ID_REDHAT 0x1b36
#define PCI_DEVICE_ID_REDHAT_ROOT_PORT 0x000C

#define PCI_VENDOR_ID_TEKRAM 0x1de1
#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29

Expand Down
24 changes: 0 additions & 24 deletions src/hw/pcidevice.c
Expand Up @@ -134,30 +134,6 @@ pci_find_init_device(const struct pci_device_id *ids, void *arg)
return NULL;
}

u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap)
{
int i;
u16 status = pci_config_readw(pci->bdf, PCI_STATUS);

if (!(status & PCI_STATUS_CAP_LIST))
return 0;

if (cap == 0) {
/* find first */
cap = pci_config_readb(pci->bdf, PCI_CAPABILITY_LIST);
} else {
/* find next */
cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT);
}
for (i = 0; cap && i <= 0xff; i++) {
if (pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_ID) == cap_id)
return cap;
cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT);
}

return 0;
}

// Enable PCI bus-mastering (ie, DMA) support on a pci device
void
pci_enable_busmaster(struct pci_device *pci)
Expand Down
1 change: 0 additions & 1 deletion src/hw/pcidevice.h
Expand Up @@ -69,7 +69,6 @@ int pci_init_device(const struct pci_device_id *ids
, struct pci_device *pci, void *arg);
struct pci_device *pci_find_init_device(const struct pci_device_id *ids
, void *arg);
u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap);
void pci_enable_busmaster(struct pci_device *pci);
u16 pci_enable_iobar(struct pci_device *pci, u32 addr);
void *pci_enable_membar(struct pci_device *pci, u32 addr);
Expand Down
13 changes: 12 additions & 1 deletion src/hw/ps2port.c
Expand Up @@ -449,11 +449,22 @@ ps2_check_event(void)
static void
ps2_keyboard_setup(void *data)
{
/* flush incoming keys */
// flush incoming keys (also verifies port is likely present)
int ret = i8042_flush();
if (ret)
return;

// Disable keyboard / mouse and drain any input they may have sent
ret = i8042_command(I8042_CMD_KBD_DISABLE, NULL);
if (ret)
return;
ret = i8042_command(I8042_CMD_AUX_DISABLE, NULL);
if (ret)
return;
ret = i8042_flush();
if (ret)
return;

// Controller self-test.
u8 param[2];
ret = i8042_command(I8042_CMD_CTL_TEST, param);
Expand Down
4 changes: 2 additions & 2 deletions src/hw/pvscsi.c
Expand Up @@ -213,7 +213,7 @@ pvscsi_process_op(struct disk_op_s *op)
if (!CONFIG_PVSCSI)
return DISK_RET_EBADTRACK;
struct pvscsi_lun_s *plun =
container_of(op->drive_gf, struct pvscsi_lun_s, drive);
container_of(op->drive_fl, struct pvscsi_lun_s, drive);
struct pvscsi_ring_dsc_s *ring_dsc = plun->ring_dsc;
struct PVSCSIRingsState *s = ring_dsc->ring_state;
u32 req_entries = s->reqNumEntriesLog2;
Expand Down Expand Up @@ -290,7 +290,7 @@ static void
pvscsi_scan_target(struct pci_device *pci, void *iobase,
struct pvscsi_ring_dsc_s *ring_dsc, u8 target)
{
/* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */
/* pvscsi has no more than a single lun per target */
pvscsi_add_lun(pci, iobase, ring_dsc, target, 0);
}

Expand Down
2 changes: 1 addition & 1 deletion src/hw/ramdisk.c
Expand Up @@ -62,7 +62,7 @@ ramdisk_setup(void)
static int
ramdisk_copy(struct disk_op_s *op, int iswrite)
{
u32 offset = GET_GLOBALFLAT(op->drive_gf->cntl_id);
u32 offset = GET_GLOBALFLAT(op->drive_fl->cntl_id);
offset += (u32)op->lba * DISK_SECTOR_SIZE;
u64 opd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)op->buf_fl);
u64 ramd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE(offset);
Expand Down
14 changes: 12 additions & 2 deletions src/hw/sdcard.c
Expand Up @@ -95,6 +95,10 @@ struct sdhci_s {
#define ST_READ (1<<4)
#define ST_MULTIPLE (1<<5)

// SDHCI SDHC_CTRL1 Flags
#define CTRL1_HIGH_SPEED_EN (1<<2)
#define CTRL1_DAT_TX_WIDTH (1<<1) // 4 BIT IF SET 0 BIT IF CLEAR (DEFAULT)

// SDHCI capabilities flags
#define SD_CAPLO_V33 (1<<24)
#define SD_CAPLO_V30 (1<<25)
Expand Down Expand Up @@ -266,7 +270,7 @@ static int
sdcard_readwrite(struct disk_op_s *op, int iswrite)
{
struct sddrive_s *drive = container_of(
op->drive_gf, struct sddrive_s, drive);
op->drive_fl, struct sddrive_s, drive);
int cmd = iswrite ? SC_WRITE_SINGLE : SC_READ_SINGLE;
if (op->count > 1)
cmd = iswrite ? SC_WRITE_MULTIPLE : SC_READ_MULTIPLE;
Expand Down Expand Up @@ -458,13 +462,19 @@ sdcard_card_setup(struct sddrive_s *drive, int volt, int prio)
if (ret)
return ret;
// Set controller to data transfer clock rate
ret = sdcard_set_frequency(regs, 25000);
ret = sdcard_set_frequency(regs, 200000);
if (ret)
return ret;
// Register drive
ret = sdcard_get_capacity(drive, csd);
if (ret)
return ret;

u32 ctrl1 = readl(&regs->host_control);
ctrl1 |= CTRL1_HIGH_SPEED_EN;
writel(&regs->host_control, ctrl1);
dprintf(3, "host_control contains 0x%08x\n", ctrl1);

char pnm[7] = {};
int i;
for (i=0; i < (drive->card_type & SF_MMC ? 6 : 5); i++)
Expand Down
44 changes: 34 additions & 10 deletions src/hw/serialio.c
Expand Up @@ -17,20 +17,44 @@

#define DEBUG_TIMEOUT 100000

// Write to a serial port register
static void
serial_debug_write(u8 offset, u8 val)
{
if (CONFIG_DEBUG_SERIAL) {
outb(val, CONFIG_DEBUG_SERIAL_PORT + offset);
} else if (CONFIG_DEBUG_SERIAL_MMIO) {
ASSERT32FLAT();
writeb((void*)CONFIG_DEBUG_SERIAL_MEM_ADDRESS + 4*offset, val);
}
}

// Read from a serial port register
static u8
serial_debug_read(u8 offset)
{
if (CONFIG_DEBUG_SERIAL)
return inb(CONFIG_DEBUG_SERIAL_PORT + offset);
if (CONFIG_DEBUG_SERIAL_MMIO) {
ASSERT32FLAT();
return readb((void*)CONFIG_DEBUG_SERIAL_MEM_ADDRESS + 4*offset);
}
}

// Setup the debug serial port for output.
void
serial_debug_preinit(void)
{
if (!CONFIG_DEBUG_SERIAL)
if (!CONFIG_DEBUG_SERIAL && (!CONFIG_DEBUG_SERIAL_MMIO || MODESEGMENT))
return;
// setup for serial logging: 8N1
u8 oldparam, newparam = 0x03;
oldparam = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR);
outb(newparam, CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR);
oldparam = serial_debug_read(SEROFF_LCR);
serial_debug_write(SEROFF_LCR, newparam);
// Disable irqs
u8 oldier, newier = 0;
oldier = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER);
outb(newier, CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER);
oldier = serial_debug_read(SEROFF_IER);
serial_debug_write(SEROFF_IER, newier);

if (oldparam != newparam || oldier != newier)
dprintf(1, "Changing serial settings was %x/%x now %x/%x\n"
Expand All @@ -41,14 +65,14 @@ serial_debug_preinit(void)
static void
serial_debug(char c)
{
if (!CONFIG_DEBUG_SERIAL)
if (!CONFIG_DEBUG_SERIAL && (!CONFIG_DEBUG_SERIAL_MMIO || MODESEGMENT))
return;
int timeout = DEBUG_TIMEOUT;
while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x20) != 0x20)
while ((serial_debug_read(SEROFF_LSR) & 0x20) != 0x20)
if (!timeout--)
// Ran out of time.
return;
outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA);
serial_debug_write(SEROFF_DATA, c);
}

void
Expand All @@ -63,10 +87,10 @@ serial_debug_putc(char c)
void
serial_debug_flush(void)
{
if (!CONFIG_DEBUG_SERIAL)
if (!CONFIG_DEBUG_SERIAL && (!CONFIG_DEBUG_SERIAL_MMIO || MODESEGMENT))
return;
int timeout = DEBUG_TIMEOUT;
while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x60) != 0x60)
while ((serial_debug_read(SEROFF_LSR) & 0x60) != 0x60)
if (!timeout--)
// Ran out of time.
return;
Expand Down
55 changes: 31 additions & 24 deletions src/hw/timer.c
Expand Up @@ -159,6 +159,29 @@ timer_read(void)
return timer_adjust_bits(v, 0xffff);
}

// Return the TSC value that is 'msecs' time in the future.
u32
timer_calc(u32 msecs)
{
return timer_read() + (GET_GLOBAL(TimerKHz) * msecs);
}
u32
timer_calc_usec(u32 usecs)
{
u32 cur = timer_read(), khz = GET_GLOBAL(TimerKHz);
if (usecs > 500000)
return cur + DIV_ROUND_UP(usecs, 1000) * khz;
return cur + DIV_ROUND_UP(usecs * khz, 1000);
}
static u32
timer_calc_nsec(u32 nsecs)
{
u32 cur = timer_read(), khz = GET_GLOBAL(TimerKHz);
if (nsecs > 500000)
return cur + DIV_ROUND_UP(nsecs, 1000000) * khz;
return cur + DIV_ROUND_UP(nsecs * khz, 1000000);
}

// Check if the current time is past a previously calculated end time.
int
timer_check(u32 end)
Expand All @@ -167,53 +190,37 @@ timer_check(u32 end)
}

static void
timer_delay(u32 diff)
timer_delay(u32 end)
{
u32 start = timer_read();
u32 end = start + diff;
while (!timer_check(end))
cpu_relax();
}

static void
timer_sleep(u32 diff)
timer_sleep(u32 end)
{
u32 start = timer_read();
u32 end = start + diff;
while (!timer_check(end))
yield();
}

void ndelay(u32 count) {
timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000));
timer_delay(timer_calc_nsec(count));
}
void udelay(u32 count) {
timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000));
timer_delay(timer_calc_usec(count));
}
void mdelay(u32 count) {
timer_delay(count * GET_GLOBAL(TimerKHz));
timer_delay(timer_calc(count));
}

void nsleep(u32 count) {
timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000));
timer_sleep(timer_calc_nsec(count));
}
void usleep(u32 count) {
timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000));
timer_sleep(timer_calc_usec(count));
}
void msleep(u32 count) {
timer_sleep(count * GET_GLOBAL(TimerKHz));
}

// Return the TSC value that is 'msecs' time in the future.
u32
timer_calc(u32 msecs)
{
return timer_read() + (GET_GLOBAL(TimerKHz) * msecs);
}
u32
timer_calc_usec(u32 usecs)
{
return timer_read() + DIV_ROUND_UP(GET_GLOBAL(TimerKHz) * usecs, 1000);
timer_sleep(timer_calc(count));
}


Expand Down
4 changes: 2 additions & 2 deletions src/hw/usb-msc.c
Expand Up @@ -69,9 +69,9 @@ usb_process_op(struct disk_op_s *op)
return 0;

dprintf(16, "usb_cmd_data id=%p write=%d count=%d buf=%p\n"
, op->drive_gf, 0, op->count, op->buf_fl);
, op->drive_fl, 0, op->count, op->buf_fl);
struct usbdrive_s *udrive_gf = container_of(
op->drive_gf, struct usbdrive_s, drive);
op->drive_fl, struct usbdrive_s, drive);

// Setup command block wrapper.
struct cbw_s cbw;
Expand Down
47 changes: 30 additions & 17 deletions src/hw/usb-uas.c
Expand Up @@ -86,8 +86,9 @@ typedef struct {

struct uasdrive_s {
struct drive_s drive;
struct usbdevice_s *usbdev;
struct usb_pipe *command, *status, *data_in, *data_out;
int lun;
u32 lun;
};

int
Expand All @@ -97,7 +98,7 @@ uas_process_op(struct disk_op_s *op)
return DISK_RET_EBADTRACK;

struct uasdrive_s *drive_gf = container_of(
op->drive_gf, struct uasdrive_s, drive);
op->drive_fl, struct uasdrive_s, drive);

uas_ui ui;
memset(&ui, 0, sizeof(ui));
Expand Down Expand Up @@ -168,30 +169,41 @@ uas_process_op(struct disk_op_s *op)
return DISK_RET_EBADTRACK;
}

static int
uas_lun_setup(struct usbdevice_s *usbdev,
struct usb_pipe *command, struct usb_pipe *status,
struct usb_pipe *data_in, struct usb_pipe *data_out,
int lun)
static void
uas_init_lun(struct uasdrive_s *drive, struct usbdevice_s *usbdev,
struct usb_pipe *command, struct usb_pipe *status,
struct usb_pipe *data_in, struct usb_pipe *data_out,
u32 lun)
{
// Allocate drive structure.
struct uasdrive_s *drive = malloc_fseg(sizeof(*drive));
if (!drive) {
warn_noalloc();
return -1;
}
memset(drive, 0, sizeof(*drive));
if (usb_32bit_pipe(data_in))
drive->drive.type = DTYPE_UAS_32;
else
drive->drive.type = DTYPE_UAS;
drive->usbdev = usbdev;
drive->command = command;
drive->status = status;
drive->data_in = data_in;
drive->data_out = data_out;
drive->lun = lun;
}

static int
uas_add_lun(u32 lun, struct drive_s *tmpl_drv)
{
struct uasdrive_s *tmpl_lun =
container_of(tmpl_drv, struct uasdrive_s, drive);
struct uasdrive_s *drive = malloc_fseg(sizeof(*drive));
if (!drive) {
warn_noalloc();
return -1;
}
uas_init_lun(drive, tmpl_lun->usbdev,
tmpl_lun->command, tmpl_lun->status,
tmpl_lun->data_in, tmpl_lun->data_out,
lun);

int prio = bootprio_find_usb(usbdev, lun);
int prio = bootprio_find_usb(drive->usbdev, drive->lun);
int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio);
if (ret) {
free(drive);
Expand Down Expand Up @@ -258,9 +270,10 @@ usb_uas_setup(struct usbdevice_s *usbdev)
if (!command || !status || !data_in || !data_out)
goto fail;

/* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */
int ret = uas_lun_setup(usbdev, command, status, data_in, data_out, 0);
if (ret < 0) {
struct uasdrive_s lun0;
uas_init_lun(&lun0, usbdev, command, status, data_in, data_out, 0);
int ret = scsi_rep_luns_scan(&lun0.drive, uas_add_lun);
if (ret <= 0) {
dprintf(1, "Unable to configure UAS drive.\n");
goto fail;
}
Expand Down