49 changes: 49 additions & 0 deletions src/fw/coreboot.c
@@ -1,6 +1,7 @@
// Coreboot interface support.
//
// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2015-2016 Eltan B.V.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

Expand All @@ -17,6 +18,7 @@
#include "stacks.h" // yield
#include "string.h" // memset
#include "util.h" // coreboot_preinit
#include "coreboot.h" // cbfs_romfile overrides


/****************************************************************
Expand All @@ -34,6 +36,14 @@ 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 @@ -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
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
95 changes: 69 additions & 26 deletions src/fw/paravirt.c
Expand Up @@ -205,6 +205,7 @@ qemu_platform_setup(void)
#define QEMU_CFG_UUID 0x02
#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 +252,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 +294,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 +329,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 +376,26 @@ 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;
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
6 changes: 5 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,6 +44,7 @@ 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
Expand All @@ -52,7 +54,9 @@ 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
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");
}
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
5 changes: 5 additions & 0 deletions src/hw/ahci.c
Expand Up @@ -361,6 +361,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
1 change: 0 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
2 changes: 2 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
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
13 changes: 12 additions & 1 deletion src/hw/sdcard.c
@@ -1,6 +1,7 @@
// PCI SD Host Controller Interface
//
// Copyright (C) 2014 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2016 Eltan B.V.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

Expand Down Expand Up @@ -95,6 +96,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 @@ -458,9 +463,15 @@ 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;

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);

// Register drive
ret = sdcard_get_capacity(drive, csd);
if (ret)
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
2 changes: 2 additions & 0 deletions src/hw/serialio.h
Expand Up @@ -20,6 +20,8 @@
#define SEROFF_LSR 5
#define SEROFF_MSR 6

#define UART_THRE (1 << 5)

void serial_debug_preinit(void);
void serial_debug_putc(char c);
void serial_debug_flush(void);
Expand Down
3 changes: 3 additions & 0 deletions src/hw/usb-xhci.c
Expand Up @@ -347,6 +347,9 @@ xhci_hub_reset(struct usbhub_s *hub, u32 port)
// A USB2 port - perform device reset
xhci_print_port_state(3, __func__, port, portsc);
writel(&xhci->pr[port].portsc, portsc | XHCI_PORTSC_PR);
if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0)
return -1;
msleep(20); // Patch to make XHCI work on AMD Mullins
break;
default:
return -1;
Expand Down
2 changes: 1 addition & 1 deletion src/hw/usb.h
Expand Up @@ -77,7 +77,7 @@ struct usbhub_op_s {
****************************************************************/

// USB mandated timings (in ms)
#define USB_TIME_SIGATT 100
#define USB_TIME_SIGATT 500 // 100 // WIV changed to 500 to make ADATA CIO3 stick working ( 200 is sufficient for SanDisk Ultra USB 3.0)
#define USB_TIME_ATTDB 100
#define USB_TIME_DRST 10
#define USB_TIME_DRSTR 50
Expand Down
2 changes: 1 addition & 1 deletion src/kbd.c
Expand Up @@ -29,7 +29,7 @@ kbd_init(void)
, x + FIELD_SIZEOF(struct bios_data_area_s, kbd_buf));
}

static u8
u8
enqueue_key(u16 keycode)
{
u16 buffer_start = GET_BDA(kbd_buf_start_offset);
Expand Down
18 changes: 18 additions & 0 deletions src/misc.c
Expand Up @@ -11,6 +11,7 @@
#include "output.h" // debug_enter
#include "stacks.h" // call16_int
#include "string.h" // memset
#include "hw/serialio.h"

#define PORT_MATH_CLEAR 0x00f0

Expand Down Expand Up @@ -57,6 +58,23 @@ handle_10(struct bregs *regs)
{
debug_enter(regs, DEBUG_HDL_10);
// don't do anything, since the VGA BIOS handles int10h requests
#if CONFIG_SEABIOS_SERIAL_CONSOLE
switch (regs->ah) {
case 0x0A: // Write char at cursor
case 0x0E: // Write text in tele-type mode
{
#define UART_THRE (1 << 5)
int timeout = 100000;

while ((inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) & UART_THRE) != UART_THRE)
if (!timeout--) return;
}
break;
default:
dprintf(2, "unhandled INT10 ah=%x al=%x\n",regs->ah,regs->al);
break;
}
#endif
}

// NMI handler
Expand Down
21 changes: 16 additions & 5 deletions src/optionroms.c
Expand Up @@ -188,15 +188,22 @@ deploy_romfile(struct romfile_s *file)
static void
run_file_roms(const char *prefix, int isvga, u64 *sources)
{
int pxen = find_pxen();
int sgaen = find_sgaen();
struct romfile_s *file = NULL;
for (;;) {
file = romfile_findprefix(prefix, file);
if (!file)
break;
struct rom_header *rom = deploy_romfile(file);
if (rom) {
setRomSource(sources, rom, (u32)file);
init_optionrom(rom, 0, isvga);
if (((strcmp(file->name, "genroms/pxe.rom") == 0) && (pxen == 1)) ||
((strcmp(file->name, "vgaroms/sgabios.bin") == 0) && (sgaen == 1))
)
{
struct rom_header *rom = deploy_romfile(file);
if (rom) {
setRomSource(sources, rom, (u32)file);
init_optionrom(rom, 0, isvga);
}
}
}
}
Expand Down Expand Up @@ -413,7 +420,11 @@ vgarom_setup(void)
EnforceChecksum = romfile_loadint("etc/optionroms-checksum", 1);
S3ResumeVga = romfile_loadint("etc/s3-resume-vga-init", CONFIG_QEMU);
RunPCIroms = romfile_loadint("etc/pci-optionrom-exec", 2);
ScreenAndDebug = romfile_loadint("etc/screen-and-debug", 1);
if (find_sgaen()) {
ScreenAndDebug = 0;
} else {
ScreenAndDebug = romfile_loadint("etc/screen-and-debug", 1);
}

// Clear option rom memory
memset((void*)BUILD_ROM_START, 0, rom_get_max() - BUILD_ROM_START);
Expand Down
8 changes: 6 additions & 2 deletions src/resume.c
Expand Up @@ -17,6 +17,7 @@
#include "string.h" // memset
#include "util.h" // dma_setup
#include "tcgbios.h" // tpm_s3_resume
#include "fw/romfile_loader.h" // romfile_fw_cfg_resume

// Handler for post calls that look like a resume.
void VISIBLE16
Expand Down Expand Up @@ -105,6 +106,9 @@ s3_resume(void)
tpm_s3_resume();
s3_resume_vga();

/* Replay any fw_cfg entries that go back to the host */
romfile_fw_cfg_resume();

make_bios_readonly();

// Invoke the resume vector.
Expand All @@ -121,8 +125,8 @@ tryReboot(void)
{
dprintf(1, "Attempting a hard reboot\n");

// Setup for reset on qemu.
qemu_prep_reset();
// Use a QEMU specific reboot on QEMU
qemu_reboot();

// Reboot using ACPI RESET_REG
acpi_reboot();
Expand Down
48 changes: 45 additions & 3 deletions src/romfile.c
@@ -1,6 +1,7 @@
// Access to pseudo "file" interface for configuration information.
//
// Copyright (C) 2012 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2015 Eltan B.V.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

Expand All @@ -9,6 +10,7 @@
#include "output.h" // dprintf
#include "romfile.h" // struct romfile_s
#include "string.h" // memcmp
#include "fw/coreboot.h" // cbfs_romfile overrides

static struct romfile_s *RomfileRoot VARVERIFY32INIT;

Expand All @@ -28,8 +30,16 @@ __romfile_findprefix(const char *prefix, int prefixlen, struct romfile_s *prev)
if (prev)
cur = prev->next;
while (cur) {
if (memcmp(prefix, cur->name, prefixlen) == 0)
return cur;
if (memcmp(prefix, cur->name, prefixlen) == 0) {

int dont_hide;
// Check if we should hide this
char *data = get_cbmem_file( (char *) cur->name, &dont_hide );
if (!data) // no override for this file
return cur;
if (dont_hide) // We know it and shouldn't hide
return cur;
}
cur = cur->next;
}
return NULL;
Expand All @@ -52,6 +62,17 @@ romfile_find(const char *name)
void *
romfile_loadfile(const char *name, int *psize)
{
// First try to read a file override from cbmem
char *data = get_cbmem_file( (char *) name, psize );

if ( data ) {
if ( !*psize ) {
dprintf(3, "Hiding romfile '%s'\n", name);
return NULL;
}
return data;
}

struct romfile_s *file = romfile_find(name);
if (!file)
return NULL;
Expand All @@ -60,7 +81,7 @@ romfile_loadfile(const char *name, int *psize)
if (!filesize)
return NULL;

char *data = malloc_tmphigh(filesize+1);
data = malloc_tmphigh(filesize+1);
if (!data) {
warn_noalloc();
return NULL;
Expand All @@ -83,6 +104,27 @@ romfile_loadfile(const char *name, int *psize)
u64
romfile_loadint(const char *name, u64 defval)
{
int size;

// First try to read a file override from cbmem
char *data = get_cbmem_file( (char *) name, &size );

if ( data ) {
u64 val = 0;

switch (size){
case 1:
case 2:
case 4:
case 8:
memcpy(&val, data, size);
break;
default:
return defval;
}
return val;
}

struct romfile_s *file = romfile_find(name);
if (!file)
return defval;
Expand Down
10 changes: 10 additions & 0 deletions src/serial.c
Expand Up @@ -76,6 +76,16 @@ handle_1400(struct bregs *regs)
u16 addr = getComAddr(regs);
if (!addr)
return;

if (CONFIG_SEABIOS_SERIAL_CONSOLE) {
/* Don't let the OS change the baud rate of the */
/* console. Just indicate success and return */
if (addr == CONFIG_DEBUG_SERIAL_PORT) {
set_success(regs);
return;
}
}

outb(inb(addr+SEROFF_LCR) | 0x80, addr+SEROFF_LCR);
if ((regs->al & 0xE0) == 0) {
outb(0x17, addr+SEROFF_DLL);
Expand Down
156 changes: 156 additions & 0 deletions src/serialconsole.c
@@ -0,0 +1,156 @@
// serial keyboard handler
//
// Copyright (C) 2014 Eltan B.V.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

#include "util.h" // kbd.c/enqueue_key

#if CONFIG_SEABIOS_SERIAL_CONSOLE
static u8 UartToScanCode[] VAR16 = {
// x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf
// =====================================================================================================
0x0f, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, // 0x
0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, // 1x
0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, 0x0a, 0x0b, 0x09, 0x0d, 0x33, 0x0c, 0x34, 0x35, // 2x
0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x27, 0x27, 0x33, 0x0d, 0x34, 0x35, // 3x
0x03, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, // 4x
0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x1a, 0x2b, 0x1b, 0x07, 0x0c, // 5x
0x29, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, // 6x
0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x1a, 0x2b, 0x1b, 0x29, 0x00, // 7x
};

void
uart_keyboard_handler(void)
{
u8 rx_buf[5], rx_bytes = 0, ascii_code = 0, scan_code = 0;

// check to see if there is a active serial port
if (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) == 0xFF)
return;

while (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) & 0x01) {
if (rx_bytes > sizeof(rx_buf)) {
dprintf(1, "uart_check_keystrokes: error too many bytes are available\n");
while (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) & 0x01)
inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_DATA);
return;
}
else
rx_buf[rx_bytes++] = inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_DATA);
}

if (!rx_bytes)
return;

if (rx_bytes == 1) {
ascii_code = rx_buf[0];
if (ascii_code >= ARRAY_SIZE(UartToScanCode)) {
dprintf(3, "uart_check_keystrokes: error UartToScanCode out of bounds index\n");
return;
}
scan_code = GET_GLOBAL(UartToScanCode[ascii_code]);
enqueue_key((u16) scan_code << 8 | ascii_code);
}
else if (rx_bytes == 2) { // assume it's actually 2 single-byte keystrokes
ascii_code = rx_buf[0];
if (ascii_code >= ARRAY_SIZE(UartToScanCode)) {
dprintf(3, "uart_check_keystrokes: error in 1/2 byte UartToScanCode out of bounds index\n");
return;
}
scan_code = GET_GLOBAL(UartToScanCode[ascii_code]);
enqueue_key((u16) scan_code << 8 | ascii_code);

ascii_code = rx_buf[1];
if (ascii_code >= ARRAY_SIZE(UartToScanCode)) {
dprintf(3, "uart_check_keystrokes: error in 2/2 UartToScanCode out of bounds index\n");
return;
}
scan_code = GET_GLOBAL(UartToScanCode[ascii_code]);
enqueue_key((u16) scan_code << 8 | ascii_code);
}
else if (rx_bytes == 3) {
if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x4f)) { // F1-F12
ascii_code = 0;
if ((rx_buf[2] >= 0x50) && (rx_buf[2] <= 0x59))
scan_code = (rx_buf[2] - 0x50) +0x3b;
else if ((rx_buf[2] >= 0x5a) && (rx_buf[2] <= 0x5b))
scan_code = (rx_buf[2] - 0x5a) + 0x85;
else {
dprintf(3, "uart_check_keystrokes: error in Fkey handling %x\n",rx_buf[2]);
return;
}
}
else if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x5b)) { // cursor keys
ascii_code = 0xe0;
if (rx_buf[2] == 0x41) scan_code = 0x48; // UP
else if (rx_buf[2] == 0x42) scan_code = 0x50; // DOWN
else if (rx_buf[2] == 0x43) scan_code = 0x4d; // LEFT
else if (rx_buf[2] == 0x44) scan_code = 0x4b; // RIGHT
else {
dprintf(3, "uart_check_keystrokes: error in cursor handling %x\n",rx_buf[2]);
return;
}
}
else {
dprintf(3, "uart_check_keystrokes: error in 3 byte key sequence\n");
return;
}
enqueue_key((u16) scan_code << 8 | ascii_code);
}
else if (rx_bytes == 4) {
if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x5b) && (rx_buf[2] == 0x33) && (rx_buf[3] == 0x7e)) { // DEL
ascii_code = 0xe0;
scan_code = 0x53;
enqueue_key((u16) scan_code << 8 | ascii_code);
}
else {
dprintf(3, "uart_check_keystrokes: unhandled 4 byte keystroke ");
dprintf(3, "%x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3]);
return;
}
}
/* these 5 byte scan codes are used by some terminal emulators */
else if (rx_bytes == 5) {
if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x5b) && (rx_buf[2] == 0x32) && (rx_buf[4] == 0x7e)) { // F9-F12
ascii_code = 0x00;
if (rx_buf[3] == 0x30) scan_code = 0x43; // F9
else if (rx_buf[3] == 0x31) scan_code = 0x44; // F10
else if (rx_buf[3] == 0x33) scan_code = 0x85; // F11
else if (rx_buf[3] == 0x34) scan_code = 0x86; // F12
else {
dprintf(3, "uart_check_keystrokes: unhandled 5 byte keystroke F9-F12 ");
dprintf(3, "%x %x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]);
return;
}
}
else if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x5b) && (rx_buf[2] == 0x31) && (rx_buf[4] == 0x7e)) { // F1-F8
ascii_code = 0x00;
if (rx_buf[3] == 0x31) scan_code = 0x3B; // F1
else if (rx_buf[3] == 0x32) scan_code = 0x3C; // F2
else if (rx_buf[3] == 0x33) scan_code = 0x3D; // F3
else if (rx_buf[3] == 0x34) scan_code = 0x3E; // F4
else if (rx_buf[3] == 0x35) scan_code = 0x3F; // F5
else if (rx_buf[3] == 0x37) scan_code = 0x40; // F6
else if (rx_buf[3] == 0x38) scan_code = 0x41; // F7
else if (rx_buf[3] == 0x39) scan_code = 0x42; // F8
else {
dprintf(3, "uart_check_keystrokes: unhandled 5 byte keystroke F1-F8 ");
dprintf(3, "%x %x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]);
return;
}
}
else {
dprintf(3, "uart_check_keystrokes: unhandled 5 byte keystroke ");
dprintf(3, "%x %x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]);
return;
}
enqueue_key((u16) scan_code << 8 | ascii_code);
}
else {
dprintf(3, "uart_check_keystrokes: unhandled rx_bytes = %x\n",rx_bytes);
dprintf(3, "%x %x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]);
return;
}
}
#endif /* CONFIG_SEABIOS_SERIAL_CONSOLE */
8 changes: 7 additions & 1 deletion src/util.h
Expand Up @@ -37,6 +37,8 @@ int bootprio_find_named_rom(const char *name, int instance);
struct usbdevice_s;
int bootprio_find_usb(struct usbdevice_s *usbdev, int lun);
int get_keystroke(int msec);
int find_pxen(void);
int find_sgaen(void);

// bootsplash.c
void enable_vga_console(void);
Expand Down Expand Up @@ -122,7 +124,7 @@ void pirtable_setup(void);
// fw/shadow.c
void make_bios_writable(void);
void make_bios_readonly(void);
void qemu_prep_reset(void);
void qemu_reboot(void);

// fw/smbios.c
void smbios_legacy_setup(void);
Expand Down Expand Up @@ -185,6 +187,7 @@ int jpeg_show(struct jpeg_decdata *jpeg, unsigned char *pic, int width
void kbd_init(void);
void handle_15c2(struct bregs *regs);
void process_key(u8 key);
u8 enqueue_key(u16 keycode);

// misc.c
extern int HaveRunPost;
Expand Down Expand Up @@ -232,6 +235,9 @@ void code_mutable_preinit(void);
void serial_setup(void);
void lpt_setup(void);

// serialconsole.c
void uart_keyboard_handler(void);

// version.c
extern const char VERSION[], BUILDINFO[];

Expand Down
2 changes: 1 addition & 1 deletion vgasrc/vgabios.h
Expand Up @@ -73,7 +73,7 @@ static inline int vga_emulate_text(void) {

// Debug settings
#define DEBUG_VGA_POST 1
#define DEBUG_VGA_10 3
#define DEBUG_VGA_10 9

// vgabios.c
int vga_bpp(struct vgamode_s *vmode_g);
Expand Down