501 changes: 383 additions & 118 deletions src/boot.c

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/cdrom.c
Expand Up @@ -142,7 +142,7 @@ cdrom_boot(struct drive_s *drive)

int ret = scsi_is_ready(&dop);
if (ret)
dprintf(1, "scsi_is_ready returned %d\n", ret);
dprintf(5, "scsi_is_ready returned %d\n", ret);

// Read the Boot Record Volume Descriptor
u8 buffer[CDROM_SECTOR_SIZE];
Expand Down
2 changes: 2 additions & 0 deletions src/cp437.c
@@ -1,5 +1,7 @@
/*
* code page 437 to unicode map
*
* This file may be distributed under the terms of the GNU LGPLv3 license.
*/

#include "types.h"
Expand Down
95 changes: 95 additions & 0 deletions src/fmap.c
@@ -0,0 +1,95 @@
// Flashmap support.
//
// Copyright (C) 2019 3mdeb
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

#include "malloc.h" // malloc
#include "string.h" // memcpy
#include "output.h" // dprintf
#include "util.h" // find_cb_subtable
#include "fmap.h" // find_fmap_directory

static void* fmap_entry = NULL;
static void* rom_begin = NULL;

#define ROM_END ((void *)0xFFFFFFFF)

#define CB_TAG_BOOT_MEDIA_PARAMS 0x0030

struct cb_boot_media_params {
u32 tag;
u32 size;
/* offsets are relative to start of boot media */
u64 fmap_offset;
u64 cbfs_offset;
u64 cbfs_size;
u64 boot_media_size;
};

void find_fmap_directory(void)
{
struct cb_boot_media_params *cbbmp;

// Find coreboot table.
struct cb_header *cbh = find_cb_table();

if (!cbh) {
dprintf(1, "coreboot table not found\n");
return;
}

cbbmp = find_cb_subtable(cbh, CB_TAG_BOOT_MEDIA_PARAMS);

if (!cbbmp) {
dprintf(1, "Boot Media Params not found\n");
return;
}

if (cbbmp->fmap_offset != 0 && cbbmp->boot_media_size != 0) {
rom_begin = (void *)(ROM_END - cbbmp->boot_media_size + 1);
fmap_entry = (void *)((u32)rom_begin + (u32)cbbmp->fmap_offset);
dprintf(1, "FMAP found @ %p\n", fmap_entry);
} else {
dprintf(1, "FMAP not found\n");
}
}

int fmap_locate_area(const char *name, struct region *ar)
{
size_t offset;

if (!fmap_entry || !rom_begin)
return -1;

/* Start reading the areas just after fmap header. */
offset = sizeof(struct fmap);

struct fmap_area *area = malloc_tmp(sizeof(*area));
if (!area) {
warn_noalloc();
return -1;
}

while (1) {
iomemcpy(area, fmap_entry + offset, sizeof(*area));

if (strcmp((const char *)area->name, name)) {
offset += sizeof(struct fmap_area);
continue;
}

dprintf(1, "FMAP: area %s found @ %p (%d bytes)\n",
name, (void *) area->offset, area->size);

ar->offset = (u32)rom_begin + area->offset;
ar->size = area->size;
free(area);
return 0;
}

free(area);
dprintf(1, "FMAP: area %s not found\n", name);

return -1;
}
51 changes: 51 additions & 0 deletions src/fmap.h
@@ -0,0 +1,51 @@
// Flashmap support.
//
// Copyright (C) 2019 3mdeb
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

#ifndef _FMAP_H
#define _FMAP_H

#define FMAP_SIGNATURE "__FMAP__"
#define FMAP_VER_MAJOR 1 /* this header's FMAP minor version */
#define FMAP_VER_MINOR 1 /* this header's FMAP minor version */
#define FMAP_STRLEN 32 /* maximum length for strings, */
/* including null-terminator */

enum fmap_flags {
FMAP_AREA_STATIC = 1 << 0,
FMAP_AREA_COMPRESSED = 1 << 1,
FMAP_AREA_RO = 1 << 2,
FMAP_AREA_PRESERVE = 1 << 3,
};

/* Mapping of volatile and static regions in firmware binary */
struct fmap_area {
u32 offset; /* offset relative to base */
u32 size; /* size in bytes */
u8 name[FMAP_STRLEN]; /* descriptive name */
u16 flags; /* flags for this area */
} PACKED;

struct fmap {
u8 signature[8]; /* "__FMAP__" (0x5F5F464D41505F5F) */
u8 ver_major; /* major version */
u8 ver_minor; /* minor version */
u64 base; /* address of the firmware binary */
u32 size; /* size of firmware binary in bytes */
u8 name[FMAP_STRLEN]; /* name of this firmware binary */
u16 nareas; /* number of areas described by
fmap_areas[] below */
struct fmap_area areas[];
} PACKED;

struct region {
size_t offset;
size_t size;
};

void find_fmap_directory(void);
int fmap_locate_area(const char *name, struct region *ar);

#endif
55 changes: 41 additions & 14 deletions src/fw/biostables.c
Expand Up @@ -141,18 +141,38 @@ find_acpi_table(u32 signature)
if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE)
return NULL;
struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address;
struct xsdt_descriptor_rev2 *xsdt =
RsdpAddr->xsdt_physical_address >= 0x100000000
? NULL : (void*)(u32)(RsdpAddr->xsdt_physical_address);
dprintf(4, "rsdt=%p\n", rsdt);
if (!rsdt || rsdt->signature != RSDT_SIGNATURE)
return NULL;
void *end = (void*)rsdt + rsdt->length;
int i;
for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
struct acpi_table_header *tbl = (void*)rsdt->table_offset_entry[i];
if (!tbl || tbl->signature != signature)
continue;
dprintf(4, "table(%x)=%p\n", signature, tbl);
return tbl;
dprintf(4, "xsdt=%p\n", xsdt);

if (xsdt && xsdt->signature == XSDT_SIGNATURE) {
void *end = (void*)xsdt + xsdt->length;
int i;
for (i=0; (void*)&xsdt->table_offset_entry[i] < end; i++) {
if (xsdt->table_offset_entry[i] >= 0x100000000)
continue; /* above 4G */
struct acpi_table_header *tbl = (void*)(u32)xsdt->table_offset_entry[i];
if (!tbl || tbl->signature != signature)
continue;
dprintf(1, "table(%x)=%p (via xsdt)\n", signature, tbl);
return tbl;
}
}

if (rsdt && rsdt->signature == RSDT_SIGNATURE) {
void *end = (void*)rsdt + rsdt->length;
int i;
for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
struct acpi_table_header *tbl = (void*)rsdt->table_offset_entry[i];
if (!tbl || tbl->signature != signature)
continue;
dprintf(1, "table(%x)=%p (via rsdt)\n", signature, tbl);
return tbl;
}
}

dprintf(4, "no table %x found\n", signature);
return NULL;
}
Expand Down Expand Up @@ -236,6 +256,7 @@ find_acpi_features(void)
void *p = fadt;
acpi_set_reset_reg(p + 116, *(u8 *)(p + 128));
}
acpi_dsdt_parse();
}


Expand Down Expand Up @@ -441,10 +462,16 @@ smbios_romfile_setup(void)
/* common case: add our own type 0, with 3 strings and 4 '\0's */
u16 t0_len = sizeof(struct smbios_type_0) + strlen(BIOS_NAME) +
strlen(VERSION) + strlen(BIOS_DATE) + 4;
ep.structure_table_length += t0_len;
if (t0_len > ep.max_structure_size)
ep.max_structure_size = t0_len;
ep.number_of_structures++;
if (t0_len > (0xffff - ep.structure_table_length)) {
dprintf(1, "Insufficient space (%d bytes) to add SMBIOS type 0 table (%d bytes)\n",
0xffff - ep.structure_table_length, t0_len);
need_t0 = 0;
} else {
ep.structure_table_length += t0_len;
if (t0_len > ep.max_structure_size)
ep.max_structure_size = t0_len;
ep.number_of_structures++;
}
}

/* allocate final blob and record its address in the entry point */
Expand Down
47 changes: 47 additions & 0 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 @@ -162,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
37 changes: 32 additions & 5 deletions src/fw/csm.c
Expand Up @@ -258,11 +258,21 @@ handle_csm_0006(struct bregs *regs)
u16 region = regs->bx; // (1 for F000 seg, 2 for E000 seg, 0 for either)
void *chunk = NULL;

dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
size, align, region);

if (!region)
region = 3;

dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
size, align, region);
// DX = Required address alignment. Bit mapped.
// First non-zero bit from the right is the alignment.*/
if (align) {
align = 1 << __ffs(align);
if (align < MALLOC_MIN_ALIGN)
align = MALLOC_MIN_ALIGN;
} else {
align = MALLOC_MIN_ALIGN;
}

if (region & 2)
chunk = _malloc(&ZoneLow, size, align);
Expand Down Expand Up @@ -309,6 +319,23 @@ handle_csm(struct bregs *regs)
csm_return(regs);
}

static int csm_prio_to_seabios(u16 csm_prio)
{
switch (csm_prio) {
case BBS_DO_NOT_BOOT_FROM:
case BBS_IGNORE_ENTRY:
return -1;

case BBS_LOWEST_PRIORITY:
case BBS_UNPRIORITIZED_ENTRY:
default:
// SeaBIOS default priorities start at 1, with 0 being used for
// an item explicitly selected from interactive_bootmenu().
// As in find_prio(), add 1 to the value being returned.
return csm_prio + 1;
}
}

int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave)
{
if (!csm_boot_table)
Expand All @@ -317,7 +344,7 @@ int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave)
int index = 1 + (chanid * 2) + slave;
dprintf(3, "CSM bootprio for ATA%d,%d (index %d) is %d\n", chanid, slave,
index, bbs[index].BootPriority);
return bbs[index].BootPriority;
return csm_prio_to_seabios(bbs[index].BootPriority);
}

int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid)
Expand All @@ -326,7 +353,7 @@ int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid)
return -1;
BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
dprintf(3, "CSM bootprio for FDC is %d\n", bbs[0].BootPriority);
return bbs[0].BootPriority;
return csm_prio_to_seabios(bbs[0].BootPriority);
}

int csm_bootprio_pci(struct pci_device *pci)
Expand All @@ -340,7 +367,7 @@ int csm_bootprio_pci(struct pci_device *pci)
if (pci->bdf == pci_to_bdf(bbs[i].Bus, bbs[i].Device, bbs[i].Function)) {
dprintf(3, "CSM bootprio for PCI(%d,%d,%d) is %d\n", bbs[i].Bus,
bbs[i].Device, bbs[i].Function, bbs[i].BootPriority);
return bbs[i].BootPriority;
return csm_prio_to_seabios(bbs[i].BootPriority);
}
}
return -1;
Expand Down
673 changes: 673 additions & 0 deletions src/fw/dsdt_parser.c

Large diffs are not rendered by default.

110 changes: 110 additions & 0 deletions src/fw/lib_vpd.c
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#include "lib_vpd.h"
#include "output.h" // dprintf

/* Given an encoded string, this functions decodes the length field which varies
* from 1 byte to many bytes.
*
* The in points the actual byte going to be decoded. The *length returns
* the decoded length field. The number of consumed bytes will be stroed in
* decoded_len.
*
* Returns VPD_FAIL if more bit is 1, but actually reaches the end of string.
*/
int VPDdecodeLen(const s32 max_len,const u8 *in, s32 *length, s32 *decoded_len)
{
u8 more;
int i = 0;

if (!length)
return VPD_FAIL;

if (!decoded_len)
return VPD_FAIL;

*length = 0;
do {
if (i >= max_len)
return VPD_FAIL;

more = in[i] & 0x80;
*length <<= 7;
*length |= in[i] & 0x7f;
++i;
} while (more);

*decoded_len = i;

return VPD_OK;
}

/* Given the encoded string, this function invokes callback with extracted
* (key, value). The *consumed will be plused the number of bytes consumed in
* this function.
*
* The input_buf points to the first byte of the input buffer.
*
* The *consumed starts from 0, which is actually the next byte to be decoded.
* It can be non-zero to be used in multiple calls.
*
* If one entry is successfully decoded, sends it to callback and returns the
* result.
*/
int decodeVpdString(const s32 max_len, const u8 *input_buf, s32 *consumed,
VpdDecodeCallback callback, void *callback_arg)
{
int type;
s32 key_len, value_len;
s32 decoded_len;
const u8 *key, *value;

/* type */
if (*consumed >= max_len)
return VPD_FAIL;

type = input_buf[*consumed];
switch (type) {
case VPD_TYPE_INFO:
case VPD_TYPE_STRING:
(*consumed)++;
/* key */
if (VPD_OK != VPDdecodeLen(max_len - *consumed,
&input_buf[*consumed], &key_len,
&decoded_len) ||
*consumed + decoded_len >= max_len) {
return VPD_FAIL;
}

*consumed += decoded_len;
key = &input_buf[*consumed];
*consumed += key_len;

/* value */
if (VPD_OK != VPDdecodeLen(max_len - *consumed,
&input_buf[*consumed],
&value_len, &decoded_len) ||
*consumed + decoded_len > max_len) {
return VPD_FAIL;
}
*consumed += decoded_len;
value = &input_buf[*consumed];
*consumed += value_len;

if (type == VPD_TYPE_STRING)
return callback(key, key_len, value, value_len,
callback_arg);

return VPD_OK;

default:
return VPD_FAIL;
break;
}

return VPD_OK;
}
199 changes: 199 additions & 0 deletions src/fw/lib_vpd.h
@@ -0,0 +1,199 @@
/*
* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Copyright (c) 2018 Libretrend LDA
*/

#ifndef __LIB_VPD__
#define __LIB_VPD__

#include "types.h"

enum {
VPD_OK = 0,
VPD_FAIL,
};

enum {
VPD_TYPE_TERMINATOR = 0,
VPD_TYPE_STRING,
VPD_TYPE_INFO = 0xfe,
VPD_TYPE_IMPLICIT_TERMINATOR = 0xff,
};

enum {
VPD_AS_LONG_AS = -1,
};

enum { /* export_type */
VPD_EXPORT_KEY_VALUE = 1,
VPD_EXPORT_VALUE,
VPD_EXPORT_AS_PARAMETER,
};

/* Callback for decodeVpdString to invoke. */
typedef int VpdDecodeCallback(const u8 *key, s32 key_len, const u8 *value,
s32 value_len, void *arg);

/* Container data types */
struct StringPair {
u8 *key;
u8 *value;
int pad_len;
int filter_out; /* TRUE means not exported. */
struct StringPair *next;
};

struct PairContainer {
struct StringPair *first;
};


/***********************************************************************
* Encode and decode VPD entries
***********************************************************************/

/* Encodes the len into multiple bytes with the following format.
*
* 7 6 ............ 0
* +----+------------------+
* |More| Length | ...
* +----+------------------+
*
* The encode_buf points to the actual position we are going to store.
* encoded_len will return the exact bytes we encoded in this function.
* Returns fail if the buffer is not long enough.
*/
int VPDencodeLen(const s32 len, u8 *encode_buf, const s32 max_len,
s32 *encoded_len);

/* Given an encoded string, this functions decodes the length field which varies
* from 1 byte to many bytes.
*
* The in points the actual byte going to be decoded. The *length returns
* the decoded length field. The number of consumed bytes will be stroed in
* decoded_len.
*
* Returns VPD_FAIL if more bit is 1, but actually reaches the end of string.
*/
int VPDdecodeLen(const s32 max_len, const u8 *in, s32 *length,
s32 *decoded_len);


/* Encodes the terminator.
* When calling, the output_buf should point to the start of buffer while
* *generated_len should contain how many bytes exist in buffer now.
* After return, *generated_len would be plused the number of bytes generated
* in this function.
*/
int encodeVpdTerminator(const int max_buffer_len, u8 *output_buf,
int *generated_len);

/* Encodes the given type/key/value pair into buffer.
*
* The pad_value_len is used to control the output value length.
* When pad_value_len > 0, the value is outputted into fixed length (pad \0
* if the value is shorter). Truncated if too long.
* pad_value_len == VPD_NO_LIMIT, output the value as long as possible.
* This is useful when we want to fix the structure layout at beginning.
*
* The encoded string will be stored in output_buf. If it is longer than
* max_buffer_len, this function returns fail. If the buffer is long enough,
* the generated_len will be updated.
*
* When calling, the output_buf should point to the start of buffer while
* *generated_len should contain how many bytes exist in buffer now.
* After return, *generated_len would be plused the number of bytes generated
* in this function.
*
* The initial value of *generated_len can be non-zero, so that this value
* can be used between multiple calls to encodeVpd2Pair().
*/
int encodeVpdString(const u8 *key, const u8 *value, const int pad_value_len,
const int max_buffer_len, u8 *output_buf,
int *generated_len);


/* Given the encoded string, this function invokes callback with extracted
* (key, value). The *consumed will be plused the number of bytes consumed in
* this function.
*
* The input_buf points to the first byte of the input buffer.
*
* The *consumed starts from 0, which is actually the next byte to be decoded.
* It can be non-zero to be used in multiple calls.
*
* If one entry is successfully decoded, sends it to callback and returns the
* result.
*/
int decodeVpdString(const s32 max_len, const u8 *input_buf, s32 *consumed,
VpdDecodeCallback callback, void *callback_arg);

/***********************************************************************
* Container helpers
***********************************************************************/
void VPDinitContainer(struct PairContainer *container);

struct StringPair *VPDfindString(struct PairContainer *container, const u8 *key,
struct StringPair ***prev_next);

/* If key is already existed in container, its value will be replaced.
* If not existed, creates new entry in container.
*/
void VPDsetString(struct PairContainer *container, const u8 *key,
const u8 *value, const int pad_len);

/* merge all entries in src into dst. If key is duplicate, overwrite it.
*/
void VPDmergeContainer(struct PairContainer *dst,
const struct PairContainer *src);

/* subtract src from dst.
*/
int VPDsubtractContainer(struct PairContainer *dst,
const struct PairContainer *src);

/* Given a container, encode its all entries into the buffer.
*/
int VPDencodeContainer(const struct PairContainer *container,
const int max_buf_len, u8 *buf, int *generated);

/* Given a VPD blob, decode its entries and push into container.
*/
int VPDdecodeToContainer(struct PairContainer *container, const s32 max_len,
const u8 *input_buf, s32 *consumed);

/* Set filter for exporting functions.
* If filter is NULL, resets the filter so that everything can be exported.
*/
int VPDsetContainerFilter(struct PairContainer *container, const u8 *filter);

/*
* Remove a key.
* Returns VPD_OK if deleted successfully. Otherwise, VPD_FAIL.
*/
int VPDdeleteKey(struct PairContainer *container, const u8 *key);

/*
* Returns number of pairs in container.
*/
int VPDlenOfContainer(const struct PairContainer *container);

/*
* Export the container content with human-readable text.
*
* The buf points to the first byte of buffer and *generated contains the number
* of bytes already existed in buffer.
*
* Afterward, the *generated will be plused on exact bytes this function has
* generated.
*/
int VPDexportContainer(const int export_type,
const struct PairContainer *container,
const int max_buf_len, u8 *buf, int *generated);

void VPDdestroyContainer(struct PairContainer *container);

#endif /* __LIB_VPD__ */
226 changes: 160 additions & 66 deletions src/fw/paravirt.c
Expand Up @@ -16,6 +16,7 @@
#include "hw/pci_regs.h" // PCI_DEVICE_ID
#include "hw/serialio.h" // PORT_SERIAL1
#include "hw/rtc.h" // CMOS_*
#include "hw/virtio-mmio.h" // virtio_mmio_acpi
#include "malloc.h" // malloc_tmp
#include "output.h" // dprintf
#include "paravirt.h" // qemu_cfg_preinit
Expand Down Expand Up @@ -67,9 +68,56 @@ static void kvm_detect(void)
if (strcmp(signature, "KVMKVMKVM") == 0) {
dprintf(1, "Running on KVM\n");
PlatformRunningOn |= PF_KVM;
if (eax >= KVM_CPUID_SIGNATURE + 0x10) {
cpuid(KVM_CPUID_SIGNATURE + 0x10, &eax, &ebx, &ecx, &edx);
dprintf(1, "kvm: have invtsc, freq %u kHz\n", eax);
tsctimer_setfreq(eax, "invtsc");
}
}
}

#define KVM_FEATURE_CLOCKSOURCE 0
#define KVM_FEATURE_CLOCKSOURCE2 3

#define MSR_KVM_SYSTEM_TIME 0x12
#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01

#define PVCLOCK_TSC_STABLE_BIT (1 << 0)

struct pvclock_vcpu_time_info *kvmclock;

static void kvmclock_init(void)
{
unsigned int eax, ebx, ecx, edx, msr;

if (!runningOnKVM())
return;

cpuid(KVM_CPUID_SIGNATURE + 0x01, &eax, &ebx, &ecx, &edx);
if (eax & (1 << KVM_FEATURE_CLOCKSOURCE2))
msr = MSR_KVM_SYSTEM_TIME_NEW;
else if (eax & (1 << KVM_FEATURE_CLOCKSOURCE))
msr = MSR_KVM_SYSTEM_TIME;
else
return;

kvmclock = memalign_low(sizeof(*kvmclock), 32);
memset(kvmclock, 0, sizeof(*kvmclock));
u32 value = (u32)(kvmclock);
dprintf(1, "kvmclock: at 0x%x (msr 0x%x)\n", value, msr);
wrmsr(msr, value | 0x01);

if (!(kvmclock->flags & PVCLOCK_TSC_STABLE_BIT))
return;
u32 MHz = (1000 << 16) / (kvmclock->tsc_to_system_mul >> 16);
if (kvmclock->tsc_shift < 0)
MHz <<= -kvmclock->tsc_shift;
else
MHz >>= kvmclock->tsc_shift;
dprintf(1, "kvmclock: stable tsc, %d MHz\n", MHz);
tsctimer_setfreq(MHz * 1000, "kvmclock");
}

static void qemu_detect(void)
{
if (!CONFIG_QEMU_HARDWARE)
Expand Down Expand Up @@ -102,13 +150,15 @@ static void qemu_detect(void)
dprintf(1, "Running on QEMU (unknown nb: %04x:%04x)\n", v, d);
break;
}
kvm_detect();
}

static int qemu_early_e820(void);

void
qemu_preinit(void)
{
qemu_detect();
kvm_detect();

if (!CONFIG_QEMU)
return;
Expand All @@ -118,28 +168,24 @@ qemu_preinit(void)
return;
}

if (!runningOnQEMU()) {
dprintf(1, "Warning: No QEMU Northbridge found (isapc?)\n");
PlatformRunningOn |= PF_QEMU;
kvm_detect();
// try read e820 table first
if (!qemu_early_e820()) {
// when it fails get memory size from nvram.
u32 rs = ((rtc_read(CMOS_MEM_EXTMEM2_LOW) << 16)
| (rtc_read(CMOS_MEM_EXTMEM2_HIGH) << 24));
if (rs)
rs += 16 * 1024 * 1024;
else
rs = (((rtc_read(CMOS_MEM_EXTMEM_LOW) << 10)
| (rtc_read(CMOS_MEM_EXTMEM_HIGH) << 18))
+ 1 * 1024 * 1024);
RamSize = rs;
e820_add(0, rs, E820_RAM);
dprintf(1, "RamSize: 0x%08x [cmos]\n", RamSize);
}

// On emulators, get memory size from nvram.
u32 rs = ((rtc_read(CMOS_MEM_EXTMEM2_LOW) << 16)
| (rtc_read(CMOS_MEM_EXTMEM2_HIGH) << 24));
if (rs)
rs += 16 * 1024 * 1024;
else
rs = (((rtc_read(CMOS_MEM_EXTMEM_LOW) << 10)
| (rtc_read(CMOS_MEM_EXTMEM_HIGH) << 18))
+ 1 * 1024 * 1024);
RamSize = rs;
e820_add(0, rs, E820_RAM);

/* reserve 256KB BIOS area at the end of 4 GB */
e820_add(0xfffc0000, 256*1024, E820_RESERVED);

dprintf(1, "RamSize: 0x%08x [cmos]\n", RamSize);
}

#define MSR_IA32_FEATURE_CONTROL 0x0000003a
Expand All @@ -164,6 +210,8 @@ qemu_platform_setup(void)
return;
}

kvmclock_init();

// Initialize pci
pci_setup();
smm_device_setup();
Expand All @@ -190,9 +238,11 @@ qemu_platform_setup(void)

RsdpAddr = find_acpi_rsdp();

if (RsdpAddr)
if (RsdpAddr) {
acpi_dsdt_parse();
virtio_mmio_setup_acpi();
return;

}
/* If present, loader should have installed an RSDP.
* Not installed? We might still be able to continue
* using the builtin RSDP.
Expand Down Expand Up @@ -400,16 +450,23 @@ qemu_get_romfile_key(struct romfile_s *file)
return qfile->select;
}

static int rtc_present(void)
{
return rtc_read(CMOS_RTC_MONTH) != 0xff;
}

u16
qemu_get_present_cpus_count(void)
{
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;
if (rtc_present()) {
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 All @@ -433,47 +490,11 @@ struct qemu_smbios_header {
static void
qemu_cfg_e820(void)
{
struct e820_reservation *table;
int i, size;

if (!CONFIG_QEMU)
return;

// "etc/e820" has both ram and reservations
table = romfile_loadfile("etc/e820", &size);
if (table) {
for (i = 0; i < size / sizeof(struct e820_reservation); i++) {
switch (table[i].type) {
case E820_RAM:
dprintf(1, "RamBlock: addr 0x%016llx len 0x%016llx [e820]\n",
table[i].address, table[i].length);
if (table[i].address < RamSize)
// ignore, preinit got it from cmos already and
// adding this again would ruin any reservations
// done so far
continue;
if (table[i].address < 0x100000000LL) {
// below 4g -- adjust RamSize to mark highest lowram addr
if (RamSize < table[i].address + table[i].length)
RamSize = table[i].address + table[i].length;
} else {
// above 4g -- adjust RamSizeOver4G to mark highest ram addr
if (0x100000000LL + RamSizeOver4G < table[i].address + table[i].length)
RamSizeOver4G = table[i].address + table[i].length - 0x100000000LL;
}
/* fall through */
case E820_RESERVED:
e820_add(table[i].address, table[i].length, table[i].type);
break;
default:
/*
* Qemu 1.7 uses RAM + RESERVED only. Ignore
* everything else, so we have the option to
* extend this in the future without breakage.
*/
break;
}
}
if (romfile_find("etc/e820")) {
// qemu_early_e820() has handled everything
return;
}

Expand Down Expand Up @@ -571,18 +592,18 @@ struct QemuCfgFile {
char name[56];
};

void qemu_cfg_init(void)
static int qemu_cfg_detect(void)
{
if (!runningOnQEMU())
return;
if (cfg_enabled)
return 1;

// Detect fw_cfg interface.
qemu_cfg_select(QEMU_CFG_SIGNATURE);
char *sig = "QEMU";
int i;
for (i = 0; i < 4; i++)
if (inb(PORT_QEMU_CFG_DATA) != sig[i])
return;
return 0;

dprintf(1, "Found QEMU fw_cfg\n");
cfg_enabled = 1;
Expand All @@ -595,6 +616,16 @@ void qemu_cfg_init(void)
dprintf(1, "QEMU fw_cfg DMA interface supported\n");
cfg_dma_enabled = 1;
}
return 1;
}

void qemu_cfg_init(void)
{
if (!runningOnQEMU())
return;

if (!qemu_cfg_detect())
return;

// Populate romfiles for legacy fw_cfg entries
qemu_cfg_legacy();
Expand Down Expand Up @@ -625,3 +656,66 @@ void qemu_cfg_init(void)
&& !romfile_find("vgaroms/sgabios.bin"))
const_romfile_add_int("etc/sercon-port", PORT_SERIAL1);
}

/*
* This runs before malloc and romfile are ready, so we have to work
* with stack allocations and read from fw_cfg in chunks.
*/
static int qemu_early_e820(void)
{
struct e820_reservation table;
struct QemuCfgFile qfile;
u32 select = 0, size = 0;
u32 count, i;

if (!qemu_cfg_detect())
return 0;

// find e820 table
qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
count = be32_to_cpu(count);
for (i = 0; i < count; i++) {
qemu_cfg_read(&qfile, sizeof(qfile));
if (memcmp(qfile.name, "etc/e820", 9) != 0)
continue;
select = be16_to_cpu(qfile.select);
size = be32_to_cpu(qfile.size);
break;
}
if (select == 0) {
// may happen on old qemu
dprintf(1, "qemu/e820: fw_cfg file etc/e820 not found\n");
return 0;
}

// walk e820 table
qemu_cfg_select(select);
count = size/sizeof(table);
for (i = 0, select = 0; i < count; i++) {
qemu_cfg_read(&table, sizeof(table));
switch (table.type) {
case E820_RESERVED:
e820_add(table.address, table.length, table.type);
dprintf(3, "qemu/e820: addr 0x%016llx len 0x%016llx [reserved]\n",
table.address, table.length);
break;
case E820_RAM:
e820_add(table.address, table.length, table.type);
dprintf(1, "qemu/e820: addr 0x%016llx len 0x%016llx [RAM]\n",
table.address, table.length);
if (table.address < 0x100000000LL) {
// below 4g
if (RamSize < table.address + table.length)
RamSize = table.address + table.length;
} else {
// above 4g
if (RamSizeOver4G < table.address + table.length - 0x100000000LL)
RamSizeOver4G = table.address + table.length - 0x100000000LL;
}
}
}

dprintf(3, "qemu/e820: RamSize: 0x%08x\n", RamSize);
dprintf(3, "qemu/e820: RamSizeOver4G: 0x%016llx\n", RamSizeOver4G);
return 1;
}
12 changes: 12 additions & 0 deletions src/fw/paravirt.h
Expand Up @@ -5,6 +5,18 @@
#include "biosvar.h" // GET_GLOBAL
#include "romfile.h" // struct romfile_s

// kvmclock
struct pvclock_vcpu_time_info {
u32 version;
u32 pad0;
u64 tsc_timestamp;
u64 system_time;
u32 tsc_to_system_mul;
s8 tsc_shift;
u8 flags;
u8 pad[2];
} __attribute__((__packed__)); /* 32 bytes */

// Types of paravirtualized platforms.
#define PF_QEMU (1<<0)
#define PF_XEN (1<<1)
Expand Down
22 changes: 10 additions & 12 deletions src/fw/pciinit.c
Expand Up @@ -292,12 +292,10 @@ static void intel_igd_setup(struct pci_device *dev, void *arg)
{
struct romfile_s *opregion = romfile_find("etc/igd-opregion");
u64 bdsm_size = le64_to_cpu(romfile_loadint("etc/igd-bdsm-size", 0));
void *addr;
u16 bdf = dev->bdf;

/* Apply OpRegion to any Intel VGA device, more than one is undefined */
if (opregion && opregion->size) {
addr = memalign_high(PAGE_SIZE, opregion->size);
void *addr = memalign_high(PAGE_SIZE, opregion->size);
if (!addr) {
warn_noalloc();
return;
Expand All @@ -308,27 +306,26 @@ static void intel_igd_setup(struct pci_device *dev, void *arg)
return;
}

pci_config_writel(bdf, 0xFC, cpu_to_le32((u32)addr));
pci_config_writel(dev->bdf, 0xFC, cpu_to_le32((u32)addr));

dprintf(1, "Intel IGD OpRegion enabled at 0x%08x, size %dKB, dev "
"%02x:%02x.%x\n", (u32)addr, opregion->size >> 10,
pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf));
dprintf(1, "Intel IGD OpRegion enabled at 0x%08x, size %dKB, dev %pP\n"
, (u32)addr, opregion->size >> 10, dev);
}

/* Apply BDSM only to Intel VGA at 00:02.0 */
if (bdsm_size && (bdf == pci_to_bdf(0, 2, 0))) {
addr = memalign_tmphigh(1024 * 1024, bdsm_size);
if (bdsm_size && (dev->bdf == pci_to_bdf(0, 2, 0))) {
void *addr = memalign_tmphigh(1024 * 1024, bdsm_size);
if (!addr) {
warn_noalloc();
return;
}

e820_add((u32)addr, bdsm_size, E820_RESERVED);

pci_config_writel(bdf, 0x5C, cpu_to_le32((u32)addr));
pci_config_writel(dev->bdf, 0x5C, cpu_to_le32((u32)addr));

dprintf(1, "Intel IGD BDSM enabled at 0x%08x, size %lldMB, dev "
"00:02.0\n", (u32)addr, bdsm_size >> 20);
dprintf(1, "Intel IGD BDSM enabled at 0x%08x, size %lldMB, dev %pP\n"
, (u32)addr, bdsm_size >> 20, dev);
}
}

Expand Down Expand Up @@ -483,6 +480,7 @@ static void mch_mmconfig_setup(u16 bdf)
pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, 0);
pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR + 4, upper);
pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower);
pci_enable_mmconfig(Q35_HOST_BRIDGE_PCIEXBAR_ADDR, "q35");
}

static void mch_mem_addr_setup(struct pci_device *dev, void *arg)
Expand Down
4 changes: 4 additions & 0 deletions src/fw/smbios.c
Expand Up @@ -205,6 +205,10 @@ smbios_init_type_0(void *start)

*end = 0;
end++;
if (!str_index) {
*end = 0;
end++;
}

return end;
}
Expand Down
158 changes: 158 additions & 0 deletions src/fw/vpd.c
@@ -0,0 +1,158 @@
// VPD interface support.
//
// Copyright (C) 2018 Libretrend LDA
//
// This file may be distributed under the terms of the GNU LGPLv3 license.

#include "bregs.h" // bregs
#include "block.h" // MAXDESCSIZE
#include "byteorder.h" // be32_to_cpu
#include "config.h" // CONFIG_*
#include "malloc.h" // free
#include "output.h" // dprintf
#include "romfile.h" // romfile_findprefix
#include "stacks.h" // yield
#include "string.h" // memcpy
#include "util.h" // find_cb_table

#include "vpd.h"
#include "lib_vpd.h"
#include "vpd_tables.h" // vpd_gets

#define CB_TAG_VPD 0x002c
#define CROSVPD_CBMEM_MAGIC 0x43524f53
#define CROSVPD_CBMEM_VERSION 0x0001
#define MAX_INPUT 60
#define ENTER_KEY 28

struct cbmem_vpd *vpd;

struct cbmem_vpd {
u32 magic;
u32 version;
u32 ro_size;
u32 rw_size;
u8 blob[0];
/* The blob contains both RO and RW data. It starts with RO (0 ..
* ro_size) and then RW (ro_size .. ro_size+rw_size).
*/
};

struct cb_ref {
u32 tag;
u32 size;
u64 cbmem_addr;
};

struct vpd_gets_arg {
const u8 *key;
const u8 *value;
s32 key_len, value_len;
int matched;
};

static struct cbmem_vpd* find_vpd(void)
{
struct cb_header *cbh = find_cb_table();
if (!cbh) {
dprintf(1, "Unable to find coreboot table\n");
return NULL;
}

struct cb_ref *cbref = find_cb_subtable(cbh, CB_TAG_VPD);
if (!cbref) {
dprintf(1, "Unable to find VPD table\n");
return NULL;
}
struct cbmem_vpd *cb_vpd = (void*)(u32)cbref->cbmem_addr;

dprintf(1, "VPD found @ %llx\n", cbref->cbmem_addr);
dprintf(1, "magic: %x version: %x ro_size: %x rw_size: %x\n",
cb_vpd->magic, cb_vpd->version, cb_vpd->ro_size, cb_vpd->rw_size);


if ((cb_vpd->magic != CROSVPD_CBMEM_MAGIC) &&
(cb_vpd->version != CROSVPD_CBMEM_VERSION)) {
dprintf(1, "Wrong VPD CBMEM magic\n");
return NULL;
}

if ((cb_vpd->ro_size == 0) && (cb_vpd->rw_size == 0)) {
dprintf(1, "VPD uninitialized or empty\n");
return NULL;
}
vpd = cb_vpd;

return cb_vpd;
}

static int vpd_gets_callback(const u8 *key, s32 key_len, const u8 *value,
s32 value_len, void *arg)
{
struct vpd_gets_arg *result = (struct vpd_gets_arg *)arg;

if (key_len != result->key_len ||
memcmp(key, result->key, key_len) != 0)
/* Returns VPD_OK to continue parsing. */
return VPD_OK;

result->matched = 1;
result->value = value;
result->value_len = value_len;
/* Returns VPD_FAIL to stop parsing. */
return VPD_FAIL;
}

const void *vpd_find_key(const char *key, int *size, enum vpd_region region)
{
struct vpd_gets_arg arg = {0};
int consumed = 0;

if (!vpd) {
if (!find_vpd())
return NULL;
}

arg.key = (const u8 *)key;
arg.key_len = strlen(key);

if (region == VPD_ANY || region == VPD_RO)
while (VPD_OK == decodeVpdString(vpd->ro_size, vpd->blob,
&consumed, vpd_gets_callback, &arg)) {
/* Iterate until found or no more entries. */
}

if (!arg.matched && region != VPD_RO)
while (VPD_OK == decodeVpdString(vpd->rw_size,
vpd->blob + vpd->ro_size, &consumed,
vpd_gets_callback, &arg)) {
/* Iterate until found or no more entries. */
}

if (!arg.matched)
return NULL;

*size = arg.value_len;
return arg.value;
}

char *vpd_gets(const char *key, char *buffer, int size, enum vpd_region region)
{
const void *string_address;
int string_size;

string_address = vpd_find_key(key, &string_size, region);

if (!string_address)
return NULL;

if (size > (string_size + 1)) {
memcpy(buffer, string_address, string_size);
buffer[string_size] = '\0';
} else {
memcpy(buffer, string_address, size - 1);
buffer[size - 1] = '\0';
}
return buffer;
}

42 changes: 42 additions & 0 deletions src/fw/vpd.h
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#ifndef __VPD_H__
#define __VPD_H__

enum vpd_region {
VPD_ANY = 0,
VPD_RO = 1,
VPD_RW = 2
};
/*
* Reads VPD string value by key.
*
* Reads in at most one less than size characters from VPD and stores them
* into buffer. A terminating null byte ('\0') is stored after the last
* character in the buffer.
*
* Returns NULL if key is not found, otherwise buffer.
*/
char *vpd_gets(const char *key, char *buffer, int size, enum vpd_region region);

/*
* Find VPD value by key.
*
* Searches for a VPD entry in the VPD cache. If found, places the size of the
* entry into '*size' and returns the pointer to the entry data.
*
* This function presumes that VPD is cached in DRAM (which is the case in the
* current implementation) and as such returns the pointer into the cache. The
* user is not supposed to modify the data, and does not have to free the
* memory.
*
* Returns NULL if key is not found.
*/

const void *vpd_find_key(const char *key, int *size, enum vpd_region region);

#endif /* __VPD_H__ */
114 changes: 114 additions & 0 deletions src/fw/vpd_tables.h
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Ported from mosys project (http://code.google.com/p/mosys/).
*/

#ifndef __LIB_VPD_TABLES_H__
#define __LIB_VPD_TABLES_H__

#include "types.h"

#define VPD_ENTRY_MAGIC "_SM_"
#define VPD_INFO_MAGIC \
"\xfe" /* type: VPD header */ \
"\x09" /* key length, 9 = 1 + 8 */ \
"\x01" /* info version, 1 */ \
"gVpdInfo" /* signature, 8 bytes */ \
"\x04" /* value length */

/* Google specific VPD info */
struct google_vpd_info {
union {
struct {
u8 type;
u8 key_len;
u8 info_ver;
u8 signature[8];
u8 value_len;
} tlv;
u8 magic[12];
} header;
u32 size;
} PACKED;

/* Entry */
struct vpd_entry {
u8 anchor_string[4];
u8 entry_cksum;
u8 entry_length;
u8 major_ver;
u8 minor_ver;
u16 max_size;
u8 entry_rev;
u8 format_area[5];
u8 inter_anchor_string[5];
u8 inter_anchor_cksum;
u16 table_length;
u32 table_address;
u16 table_entry_count;
u8 bcd_revision;
} PACKED;

/* Header */
struct vpd_header {
u8 type;
u8 length;
u16 handle;
} PACKED;

/* Type 0 - firmware information */
struct vpd_table_firmware {
u8 vendor;
u8 version;
u16 start_address;
u8 release_date;
u8 rom_size_64k_blocks;
u32 characteristics;
u8 extension[2]; /* v2.4+ */
u8 major_ver; /* v2.4+ */
u8 minor_ver; /* v2.4+ */
u8 ec_major_ver; /* v2.4+ */
u8 ec_minor_ver; /* v2.4+ */
} PACKED;

/* Type 1 - system information */
struct vpd_table_system {
u8 manufacturer;
u8 name;
u8 version;
u8 serial_number;
u8 uuid[16];
u8 wakeup_type;
u8 sku_number; /* v2.4+ */
u8 family; /* v2.4+ */
} PACKED;

/* Type 127 - end of table */
struct vpd_table_eot {
struct vpd_header header;
} PACKED;

/* Type 241 - binary blob pointer */
struct vpd_table_binary_blob_pointer {
u8 struct_major_version;
u8 struct_minor_version;
u8 vendor;
u8 description;
u8 major_version;
u8 minor_version;
u8 variant;
u8 reserved[5];
u8 uuid[16];
u32 offset;
u32 size;
} PACKED;

/* The length and number of strings defined here is not a limitation of VPD.
* These numbers were deemed good enough during development. */
#define VPD_MAX_STRINGS 10
#define VPD_MAX_STRING_LENGTH 64

#endif /* __LIB_VPD_TABLES_H__ */
2 changes: 2 additions & 0 deletions src/hw/ahci.c
Expand Up @@ -345,6 +345,7 @@ ahci_port_alloc(struct ahci_ctrl_s *ctrl, u32 pnr)
warn_noalloc();
return NULL;
}
memset(port, 0, sizeof(*port));
port->pnr = pnr;
port->ctrl = ctrl;
port->list = memalign_tmp(1024, 1024);
Expand Down Expand Up @@ -593,6 +594,7 @@ static int ahci_port_setup(struct ahci_port_s *port)
, ata_extract_version(buffer));
port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0);
}
boot_lchs_find_ata_device(ctrl->pci_tmp, pnr, 0, &(port->drive.lchs));
return 0;
}

Expand Down
8 changes: 8 additions & 0 deletions src/hw/ata.c
Expand Up @@ -755,6 +755,10 @@ init_drive_atapi(struct atadrive_s *dummy, u16 *buffer)
int prio = bootprio_find_ata_device(adrive->chan_gf->pci_tmp,
adrive->chan_gf->chanid,
adrive->slave);
boot_lchs_find_ata_device(adrive->chan_gf->pci_tmp,
adrive->chan_gf->chanid,
adrive->slave,
&(adrive->drive.lchs));
boot_add_cd(&adrive->drive, desc, prio);
}

Expand Down Expand Up @@ -805,6 +809,10 @@ init_drive_ata(struct atadrive_s *dummy, u16 *buffer)
int prio = bootprio_find_ata_device(adrive->chan_gf->pci_tmp,
adrive->chan_gf->chanid,
adrive->slave);
boot_lchs_find_ata_device(adrive->chan_gf->pci_tmp,
adrive->chan_gf->chanid,
adrive->slave,
&(adrive->drive.lchs));
// Register with bcv system.
boot_add_hd(&adrive->drive, desc, prio);

Expand Down
10 changes: 7 additions & 3 deletions src/hw/blockcmd.c
Expand Up @@ -144,8 +144,9 @@ scsi_is_ready(struct disk_op_s *op)
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",
* reported by the device 3 times. If the device reports "IN PROGRESS",
* 30 seconds is added. */
int tries = 3;
int in_progress = 0;
u32 end = timer_calc(5000);
for (;;) {
Expand All @@ -167,8 +168,11 @@ scsi_is_ready(struct disk_op_s *op)

// Sense succeeded.
if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */
dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
return -1;
tries--;
dprintf(1, "Device reports MEDIUM NOT PRESENT - %d tries left\n",
tries);
if (!tries)
return -1;
}

if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) {
Expand Down
2 changes: 2 additions & 0 deletions src/hw/esp-scsi.c
Expand Up @@ -181,6 +181,8 @@ esp_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv)

char *name = znprintf(MAXDESCSIZE, "esp %pP %d:%d",
llun->pci, llun->target, llun->lun);
boot_lchs_find_scsi_device(llun->pci, llun->target, llun->lun,
&(llun->drive.lchs));
int prio = bootprio_find_scsi_device(llun->pci, llun->target, llun->lun);
int ret = scsi_drive_setup(&llun->drive, name, prio);
free(name);
Expand Down
2 changes: 2 additions & 0 deletions src/hw/lsi-scsi.c
Expand Up @@ -158,6 +158,8 @@ lsi_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv)
lsi_scsi_init_lun(llun, tmpl_llun->pci, tmpl_llun->iobase,
tmpl_llun->target, lun);

boot_lchs_find_scsi_device(llun->pci, llun->target, llun->lun,
&(llun->drive.lchs));
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);
Expand Down
1 change: 1 addition & 0 deletions src/hw/megasas.c
Expand Up @@ -225,6 +225,7 @@ megasas_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
free(mlun);
return -1;
}
boot_lchs_find_scsi_device(pci, target, lun, &(mlun->drive.lchs));
name = znprintf(MAXDESCSIZE, "MegaRAID SAS (PCI %pP) LD %d:%d"
, pci, target, lun);
prio = bootprio_find_scsi_device(pci, target, lun);
Expand Down
2 changes: 2 additions & 0 deletions src/hw/mpt-scsi.c
Expand Up @@ -221,6 +221,8 @@ mpt_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv)
mpt_scsi_init_lun(llun, tmpl_llun->pci, tmpl_llun->iobase,
tmpl_llun->target, lun);

boot_lchs_find_scsi_device(llun->pci, llun->target, llun->lun,
&(llun->drive.lchs));
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);
Expand Down
16 changes: 15 additions & 1 deletion src/hw/nvme-int.h
Expand Up @@ -10,6 +10,8 @@
#include "types.h" // u32
#include "pcidevice.h" // struct pci_device

#define NVME_MAX_PRPL_ENTRIES 15 /* Allows requests up to 64kb */

/* Data structures */

/* The register file of a NVMe host controller. This struct follows the naming
Expand Down Expand Up @@ -117,9 +119,15 @@ struct nvme_namespace {

u32 block_size;
u32 metadata_size;
u32 max_req_size;

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

/* Page List */
u32 prpl_len;
void *prp1;
u64 prpl[NVME_MAX_PRPL_ENTRIES];
};

/* Data structures for NVMe admin identify commands */
Expand All @@ -131,7 +139,12 @@ struct nvme_identify_ctrl {
char mn[40];
char fr[8];

char _boring[516 - 72];
u8 rab;
u8 ieee[3];
u8 cmic;
u8 mdts;

char _boring[516 - 78];

u32 nn; /* number of namespaces */
};
Expand Down Expand Up @@ -189,6 +202,7 @@ union nvme_identify {
#define NVME_CQE_DW3_P (1U << 16)

#define NVME_PAGE_SIZE 4096
#define NVME_PAGE_MASK ~(NVME_PAGE_SIZE - 1)

/* Length for the queue entries. */
#define NVME_SQE_SIZE_LOG 6
Expand Down
142 changes: 114 additions & 28 deletions src/hw/nvme.c
Expand Up @@ -152,7 +152,7 @@ nvme_wait(struct nvme_sq *sq)
/* Returns the next submission queue entry (or NULL if the queue is full). It
also fills out Command Dword 0 and clears the rest. */
static struct nvme_sqe *
nvme_get_next_sqe(struct nvme_sq *sq, u8 opc, void *metadata, void *data)
nvme_get_next_sqe(struct nvme_sq *sq, u8 opc, void *metadata, void *data, void *data2)
{
if (((sq->head + 1) & sq->common.mask) == sq->tail) {
dprintf(3, "submission queue is full");
Expand All @@ -166,11 +166,7 @@ nvme_get_next_sqe(struct nvme_sq *sq, u8 opc, void *metadata, void *data)
sqe->cdw0 = opc | (sq->tail << 16 /* CID */);
sqe->mptr = (u32)metadata;
sqe->dptr_prp1 = (u32)data;

if (sqe->dptr_prp1 & (NVME_PAGE_SIZE - 1)) {
/* Data buffer not page aligned. */
warn_internalerror();
}
sqe->dptr_prp2 = (u32)data2;

return sqe;
}
Expand Down Expand Up @@ -200,7 +196,7 @@ nvme_admin_identify(struct nvme_ctrl *ctrl, u8 cns, u32 nsid)
struct nvme_sqe *cmd_identify;
cmd_identify = nvme_get_next_sqe(&ctrl->admin_sq,
NVME_SQE_OPC_ADMIN_IDENTIFY, NULL,
identify_buf);
identify_buf, NULL);

if (!cmd_identify) {
warn_internalerror();
Expand Down Expand Up @@ -238,7 +234,8 @@ nvme_admin_identify_ns(struct nvme_ctrl *ctrl, u32 ns_id)
}

static void
nvme_probe_ns(struct nvme_ctrl *ctrl, struct nvme_namespace *ns, u32 ns_id)
nvme_probe_ns(struct nvme_ctrl *ctrl, struct nvme_namespace *ns, u32 ns_id,
u8 mdts)
{
ns->ctrl = ctrl;
ns->ns_id = ns_id;
Expand Down Expand Up @@ -281,6 +278,14 @@ nvme_probe_ns(struct nvme_ctrl *ctrl, struct nvme_namespace *ns, u32 ns_id)
ns->drive.blksize = ns->block_size;
ns->drive.sectors = ns->lba_count;

if (mdts) {
ns->max_req_size = ((1U << mdts) * NVME_PAGE_SIZE) / ns->block_size;
dprintf(3, "NVME NS %u max request size: %d sectors\n",
ns_id, ns->max_req_size);
} else {
ns->max_req_size = -1U;
}

ns->dma_buffer = zalloc_page_aligned(&ZoneHigh, NVME_PAGE_SIZE);

char *desc = znprintf(MAXDESCSIZE, "NVMe NS %u: %llu MiB (%llu %u-byte "
Expand Down Expand Up @@ -329,7 +334,7 @@ nvme_create_io_cq(struct nvme_ctrl *ctrl, struct nvme_cq *cq, u16 q_idx)

cmd_create_cq = nvme_get_next_sqe(&ctrl->admin_sq,
NVME_SQE_OPC_ADMIN_CREATE_IO_CQ, NULL,
cq->cqe);
cq->cqe, NULL);
if (!cmd_create_cq) {
goto err_destroy_cq;
}
Expand Down Expand Up @@ -373,7 +378,7 @@ nvme_create_io_sq(struct nvme_ctrl *ctrl, struct nvme_sq *sq, u16 q_idx, struct

cmd_create_sq = nvme_get_next_sqe(&ctrl->admin_sq,
NVME_SQE_OPC_ADMIN_CREATE_IO_SQ, NULL,
sq->sqe);
sq->sqe, NULL);
if (!cmd_create_sq) {
goto err_destroy_sq;
}
Expand Down Expand Up @@ -408,19 +413,29 @@ nvme_io_readwrite(struct nvme_namespace *ns, u64 lba, char *buf, u16 count,
int write)
{
u32 buf_addr = (u32)buf;
void *prp2;

if ((buf_addr & 0x3) ||
((buf_addr & ~(NVME_PAGE_SIZE - 1)) !=
((buf_addr + ns->block_size * count - 1) & ~(NVME_PAGE_SIZE - 1)))) {
/* Buffer is misaligned or crosses page boundary */
if (buf_addr & 0x3) {
/* Buffer is misaligned */
warn_internalerror();
return DISK_RET_EBADTRACK;
}

if ((ns->block_size * count) > (NVME_PAGE_SIZE * 2)) {
/* We need to describe more than 2 pages, rely on PRP List */
prp2 = ns->prpl;
} else if ((ns->block_size * count) > NVME_PAGE_SIZE) {
/* Directly embed the 2nd page if we only need 2 pages */
prp2 = (void *)(long)ns->prpl[0];
} else {
/* One page is enough, don't expose anything else */
prp2 = NULL;
}

struct nvme_sqe *io_read = nvme_get_next_sqe(&ns->ctrl->io_sq,
write ? NVME_SQE_OPC_IO_WRITE
: NVME_SQE_OPC_IO_READ,
NULL, buf);
NULL, buf, prp2);
io_read->nsid = ns->ns_id;
io_read->dword[10] = (u32)lba;
io_read->dword[11] = (u32)(lba >> 32);
Expand All @@ -440,6 +455,61 @@ nvme_io_readwrite(struct nvme_namespace *ns, u64 lba, char *buf, u16 count,
return DISK_RET_SUCCESS;
}

static void nvme_reset_prpl(struct nvme_namespace *ns)
{
ns->prpl_len = 0;
}

static int nvme_add_prpl(struct nvme_namespace *ns, u64 base)
{
if (ns->prpl_len >= NVME_MAX_PRPL_ENTRIES)
return -1;

ns->prpl[ns->prpl_len++] = base;

return 0;
}

static int nvme_build_prpl(struct nvme_namespace *ns, void *op_buf, u16 count)
{
int first_page = 1;
u32 base = (long)op_buf;
s32 size;

if (count > ns->max_req_size)
count = ns->max_req_size;

nvme_reset_prpl(ns);

size = count * ns->block_size;
/* Special case for transfers that fit into PRP1, but are unaligned */
if (((size + (base & ~NVME_PAGE_MASK)) <= NVME_PAGE_SIZE)) {
ns->prp1 = op_buf;
return count;
}

/* Every request has to be page aligned */
if (base & ~NVME_PAGE_MASK)
return 0;

/* Make sure a full block fits into the last chunk */
if (size & (ns->block_size - 1ULL))
return 0;

for (; size > 0; base += NVME_PAGE_SIZE, size -= NVME_PAGE_SIZE) {
if (first_page) {
/* First page is special */
ns->prp1 = (void*)base;
first_page = 0;
continue;
}
if (nvme_add_prpl(ns, base))
return 0;
}

return count;
}

static int
nvme_create_io_queues(struct nvme_ctrl *ctrl)
{
Expand Down Expand Up @@ -567,7 +637,7 @@ nvme_controller_enable(struct nvme_ctrl *ctrl)
/* Populate namespace IDs */
int ns_idx;
for (ns_idx = 0; ns_idx < ctrl->ns_count; ns_idx++) {
nvme_probe_ns(ctrl, &ctrl->ns[ns_idx], ns_idx + 1);
nvme_probe_ns(ctrl, &ctrl->ns[ns_idx], ns_idx + 1, identify->mdts);
}

dprintf(3, "NVMe initialization complete!\n");
Expand All @@ -586,8 +656,15 @@ nvme_controller_enable(struct nvme_ctrl *ctrl)
static void
nvme_controller_setup(void *opaque)
{
u8 skip_nonbootable = is_bootprio_strict();
struct pci_device *pci = opaque;

if (skip_nonbootable && bootprio_find_pci_device(pci) < 0) {
dprintf(1, "skipping init of a non-bootable NVMe at %pP\n",
pci);
goto err;
}

struct nvme_reg volatile *reg = pci_enable_membar(pci, PCI_BASE_ADDRESS_0);
if (!reg)
return;
Expand Down Expand Up @@ -649,25 +726,34 @@ nvme_cmd_readwrite(struct nvme_namespace *ns, struct disk_op_s *op, int write)
{
int res = DISK_RET_SUCCESS;
u16 const max_blocks = NVME_PAGE_SIZE / ns->block_size;
u16 i;
u16 i, blocks;

for (i = 0; i < op->count && res == DISK_RET_SUCCESS;) {
u16 blocks_remaining = op->count - i;
u16 blocks = blocks_remaining < max_blocks ? blocks_remaining
: max_blocks;
char *op_buf = op->buf_fl + i * ns->block_size;

if (write) {
memcpy(ns->dma_buffer, op_buf, blocks * ns->block_size);
}
blocks = nvme_build_prpl(ns, op_buf, blocks_remaining);
if (blocks) {
res = nvme_io_readwrite(ns, op->lba + i, ns->prp1, blocks, write);
dprintf(5, "ns %u %s lba %llu+%u: %d\n", ns->ns_id, write ? "write"
: "read",
op->lba, blocks, res);
} else {
blocks = blocks_remaining < max_blocks ? blocks_remaining
: max_blocks;

if (write) {
memcpy(ns->dma_buffer, op_buf, blocks * ns->block_size);
}

res = nvme_io_readwrite(ns, op->lba + i, ns->dma_buffer, blocks, write);
dprintf(3, "ns %u %s lba %llu+%u: %d\n", ns->ns_id, write ? "write"
: "read",
op->lba + i, blocks, res);
res = nvme_io_readwrite(ns, op->lba + i, ns->dma_buffer, blocks, write);
dprintf(5, "ns %u %s lba %llu+%u: %d\n", ns->ns_id, write ? "write"
: "read",
op->lba + i, blocks, res);

if (!write && res == DISK_RET_SUCCESS) {
memcpy(op_buf, ns->dma_buffer, blocks * ns->block_size);
if (!write && res == DISK_RET_SUCCESS) {
memcpy(op_buf, ns->dma_buffer, blocks * ns->block_size);
}
}

i += blocks;
Expand Down
69 changes: 57 additions & 12 deletions src/hw/pci.c
Expand Up @@ -14,40 +14,76 @@
#define PORT_PCI_CMD 0x0cf8
#define PORT_PCI_DATA 0x0cfc

static u32 mmconfig;

static void *mmconfig_addr(u16 bdf, u32 addr)
{
return (void*)(mmconfig + ((u32)bdf << 12) + addr);
}

static u32 ioconfig_cmd(u16 bdf, u32 addr)
{
return 0x80000000 | (bdf << 8) | (addr & 0xfc);
}

void pci_config_writel(u16 bdf, u32 addr, u32 val)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
outl(val, PORT_PCI_DATA);
if (!MODESEGMENT && mmconfig) {
writel(mmconfig_addr(bdf, addr), val);
} else {
outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
outl(val, PORT_PCI_DATA);
}
}

void pci_config_writew(u16 bdf, u32 addr, u16 val)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
outw(val, PORT_PCI_DATA + (addr & 2));
if (!MODESEGMENT && mmconfig) {
writew(mmconfig_addr(bdf, addr), val);
} else {
outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
outw(val, PORT_PCI_DATA + (addr & 2));
}
}

void pci_config_writeb(u16 bdf, u32 addr, u8 val)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
outb(val, PORT_PCI_DATA + (addr & 3));
if (!MODESEGMENT && mmconfig) {
writeb(mmconfig_addr(bdf, addr), val);
} else {
outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
outb(val, PORT_PCI_DATA + (addr & 3));
}
}

u32 pci_config_readl(u16 bdf, u32 addr)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
return inl(PORT_PCI_DATA);
if (!MODESEGMENT && mmconfig) {
return readl(mmconfig_addr(bdf, addr));
} else {
outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
return inl(PORT_PCI_DATA);
}
}

u16 pci_config_readw(u16 bdf, u32 addr)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
return inw(PORT_PCI_DATA + (addr & 2));
if (!MODESEGMENT && mmconfig) {
return readw(mmconfig_addr(bdf, addr));
} else {
outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
return inw(PORT_PCI_DATA + (addr & 2));
}
}

u8 pci_config_readb(u16 bdf, u32 addr)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
return inb(PORT_PCI_DATA + (addr & 3));
if (!MODESEGMENT && mmconfig) {
return readb(mmconfig_addr(bdf, addr));
} else {
outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
return inb(PORT_PCI_DATA + (addr & 3));
}
}

void
Expand All @@ -58,6 +94,15 @@ pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
pci_config_writew(bdf, addr, val);
}

void
pci_enable_mmconfig(u64 addr, const char *name)
{
if (addr >= 0x100000000ll)
return;
dprintf(1, "PCIe: using %s mmconfig at 0x%llx\n", name, addr);
mmconfig = addr;
}

u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap)
{
int i;
Expand Down
1 change: 1 addition & 0 deletions src/hw/pci.h
Expand Up @@ -39,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);
void pci_enable_mmconfig(u64 addr, const char *name);
u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap);
int pci_next(int bdf, int bus);
int pci_probe_host(void);
Expand Down
4 changes: 4 additions & 0 deletions src/hw/ps2port.c
Expand Up @@ -534,6 +534,10 @@ ps2port_setup(void)
ASSERT32FLAT();
if (! CONFIG_PS2PORT)
return;
if (acpi_dsdt_present_eisaid(0x0303) == 0) {
dprintf(1, "ACPI: no PS/2 keyboard present\n");
return;
}
dprintf(3, "init ps2port\n");

enable_hwirq(1, FUNC16(entry_09));
Expand Down
1 change: 1 addition & 0 deletions src/hw/pvscsi.c
Expand Up @@ -273,6 +273,7 @@ pvscsi_add_lun(struct pci_device *pci, void *iobase,
plun->iobase = iobase;
plun->ring_dsc = ring_dsc;

boot_lchs_find_scsi_device(pci, target, lun, &(plun->drive.lchs));
char *name = znprintf(MAXDESCSIZE, "pvscsi %pP %d:%d", pci, target, lun);
int prio = bootprio_find_scsi_device(pci, target, lun);
int ret = scsi_drive_setup(&plun->drive, name, prio);
Expand Down
4 changes: 4 additions & 0 deletions src/hw/serialio.c
Expand Up @@ -106,6 +106,10 @@ u16 DebugOutputPort VARFSEG = 0x402;
void
qemu_debug_preinit(void)
{
/* Xen doesn't support checking if debug output is active. */
if (runningOnXen())
return;

/* Check if the QEMU debug output port is active */
if (CONFIG_DEBUG_IO &&
inb(GET_GLOBAL(DebugOutputPort)) != QEMU_DEBUGCON_READBACK)
Expand Down
28 changes: 26 additions & 2 deletions src/hw/timer.c
Expand Up @@ -101,8 +101,10 @@ tsctimer_setup(void)
void
timer_setup(void)
{
if (!CONFIG_TSC_TIMER || (CONFIG_PMTIMER && TimerPort != PORT_PIT_COUNTER0))
if (!CONFIG_TSC_TIMER)
return;
if (TimerPort != PORT_PIT_COUNTER0)
return; // have timer already

// Check if CPU has a timestamp counter
u32 eax, ebx, ecx, edx, cpuid_features = 0;
Expand All @@ -113,11 +115,33 @@ timer_setup(void)
tsctimer_setup();
}

void
tsctimer_setfreq(u32 khz, const char *src)
{
if (!CONFIG_TSC_TIMER)
return;
if (TimerPort != PORT_PIT_COUNTER0)
return; // have timer already

TimerKHz = khz;
ShiftTSC = 0;
while (TimerKHz >= 6000) {
ShiftTSC++;
TimerKHz = (TimerKHz + 1) >> 1;
}
TimerPort = 0;

dprintf(1, "CPU Mhz=%u (%s)\n", (TimerKHz << ShiftTSC) / 1000, src);
}

void
pmtimer_setup(u16 ioport)
{
if (!CONFIG_PMTIMER)
return;
if (TimerPort != PORT_PIT_COUNTER0)
return; // have timer already

dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport);
TimerPort = ioport;
TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000);
Expand Down Expand Up @@ -156,7 +180,7 @@ timer_read(void)
// Read from PIT.
outb(PM_SEL_READBACK | PM_READ_VALUE | PM_READ_COUNTER0, PORT_PIT_MODE);
u16 v = inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8);
return timer_adjust_bits(v, 0xffff);
return timer_adjust_bits(-v, 0xffff);
}

// Return the TSC value that is 'msecs' time in the future.
Expand Down
3 changes: 2 additions & 1 deletion src/hw/tpm_drivers.c
Expand Up @@ -620,7 +620,8 @@ tpmhw_transmit(u8 locty, struct tpm_req_header *req,
return -1;

irc = td->readresp(respbuffer, respbufferlen);
if (irc != 0)
if (irc != 0 ||
*respbufferlen < sizeof(struct tpm_rsp_header))
return -1;

td->ready();
Expand Down
57 changes: 34 additions & 23 deletions src/hw/usb-hid.c
Expand Up @@ -49,6 +49,15 @@ set_idle(struct usb_pipe *pipe, int ms)
#define KEYREPEATWAITMS 500
#define KEYREPEATMS 33

// Format of USB keyboard event data
struct keyevent {
u8 modifiers;
u8 reserved;
u8 keys[6];
};

#define MAX_KBD_EVENT 16

static int
usb_kbd_setup(struct usbdevice_s *usbdev
, struct usb_endpoint_descriptor *epdesc)
Expand All @@ -59,8 +68,12 @@ usb_kbd_setup(struct usbdevice_s *usbdev
// XXX - this enables the first found keyboard (could be random)
return -1;

if (epdesc->wMaxPacketSize != 8)
if (epdesc->wMaxPacketSize < sizeof(struct keyevent)
|| epdesc->wMaxPacketSize > MAX_KBD_EVENT) {
dprintf(1, "USB keyboard wMaxPacketSize=%d; aborting\n"
, epdesc->wMaxPacketSize);
return -1;
}

// Enable "boot" protocol.
int ret = set_protocol(usbdev->defpipe, 0);
Expand All @@ -69,7 +82,7 @@ usb_kbd_setup(struct usbdevice_s *usbdev
// Periodically send reports to enable key repeat.
ret = set_idle(usbdev->defpipe, KEYREPEATMS);
if (ret)
return -1;
dprintf(3, "Warning: Failed to set key repeat rate\n");

keyboard_pipe = usb_alloc_pipe(usbdev, epdesc);
if (!keyboard_pipe)
Expand All @@ -79,6 +92,14 @@ usb_kbd_setup(struct usbdevice_s *usbdev
return 0;
}

// Format of USB mouse event data
struct mouseevent {
u8 buttons;
u8 x, y;
};

#define MAX_MOUSE_EVENT 8

static int
usb_mouse_setup(struct usbdevice_s *usbdev
, struct usb_endpoint_descriptor *epdesc)
Expand All @@ -89,8 +110,12 @@ usb_mouse_setup(struct usbdevice_s *usbdev
// XXX - this enables the first found mouse (could be random)
return -1;

if (epdesc->wMaxPacketSize < 3 || epdesc->wMaxPacketSize > 8)
if (epdesc->wMaxPacketSize < sizeof(struct mouseevent)
|| epdesc->wMaxPacketSize > MAX_MOUSE_EVENT) {
dprintf(1, "USB mouse wMaxPacketSize=%d; aborting\n"
, epdesc->wMaxPacketSize);
return -1;
}

// Enable "boot" protocol.
int ret = set_protocol(usbdev->defpipe, 0);
Expand Down Expand Up @@ -163,13 +188,6 @@ static u16 ModifierToScanCode[] VAR16 = {

#define RELEASEBIT 0x80

// Format of USB keyboard event data
struct keyevent {
u8 modifiers;
u8 reserved;
u8 keys[6];
};

// Translate data from KeyToScanCode[] to calls to process_key().
static void
prockeys(u16 scancode, u8 key_release, u8 mods)
Expand Down Expand Up @@ -309,11 +327,11 @@ usb_check_key(void)
return;

for (;;) {
struct keyevent data;
int ret = usb_poll_intr(pipe, &data);
u8 data[MAX_KBD_EVENT];
int ret = usb_poll_intr(pipe, data);
if (ret)
break;
handle_key(&data);
handle_key((void*)data);
}
}

Expand Down Expand Up @@ -349,13 +367,6 @@ usb_kbd_command(int command, u8 *param)
* Mouse events
****************************************************************/

// Format of USB mouse event data
struct mouseevent {
u8 buttons;
u8 x, y;
u8 reserved[5];
};

// Process USB mouse data.
static void
handle_mouse(struct mouseevent *data)
Expand All @@ -381,11 +392,11 @@ usb_check_mouse(void)
return;

for (;;) {
struct mouseevent data;
int ret = usb_poll_intr(pipe, &data);
u8 data[MAX_MOUSE_EVENT];
int ret = usb_poll_intr(pipe, data);
if (ret)
break;
handle_mouse(&data);
handle_mouse((void*)data);
}
}

Expand Down
73 changes: 60 additions & 13 deletions src/hw/usb-xhci.c
Expand Up @@ -356,6 +356,8 @@ xhci_hub_reset(struct usbhub_s *hub, u32 port)
dprintf(3, "XHCI: USB2 port - performing device reset\n");
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, 1500) != 0)
return -1;
break;
default:
return -1;
Expand Down Expand Up @@ -539,17 +541,13 @@ configure_xhci(void *data)
free(xhci);
}

static void
xhci_controller_setup(struct pci_device *pci)
static struct usb_xhci_s*
xhci_controller_setup(void *baseaddr)
{
void *baseaddr = pci_enable_membar(pci, PCI_BASE_ADDRESS_0);
if (!baseaddr)
return;

struct usb_xhci_s *xhci = malloc_high(sizeof(*xhci));
if (!xhci) {
warn_noalloc();
return;
return NULL;
}
memset(xhci, 0, sizeof(*xhci));
xhci->caps = baseaddr;
Expand All @@ -564,13 +562,11 @@ xhci_controller_setup(struct pci_device *pci)
xhci->slots = hcs1 & 0xff;
xhci->xcap = ((hcc >> 16) & 0xffff) << 2;
xhci->context64 = (hcc & 0x04) ? 1 : 0;

xhci->usb.pci = pci;
xhci->usb.type = USB_TYPE_XHCI;

dprintf(1, "XHCI init on dev %pP: regs @ %p, %d ports, %d slots"
dprintf(1, "XHCI init: regs @ %p, %d ports, %d slots"
", %d byte contexts\n"
, pci, xhci->caps, xhci->ports, xhci->slots
, xhci->caps, xhci->ports, xhci->slots
, xhci->context64 ? 64 : 32);

if (xhci->xcap) {
Expand Down Expand Up @@ -621,11 +617,53 @@ xhci_controller_setup(struct pci_device *pci)
dprintf(1, "XHCI driver does not support page size code %d\n"
, pagesize<<12);
free(xhci);
return;
return NULL;
}

return xhci;
}

static void
xhci_controller_setup_pci(struct pci_device *pci)
{
struct usb_xhci_s *xhci;
void *baseaddr;

baseaddr = pci_enable_membar(pci, PCI_BASE_ADDRESS_0);
if (!baseaddr)
return;

dprintf(1, "PCI: XHCI at %pP (mmio %p)\n", pci, baseaddr);
pci_enable_busmaster(pci);

xhci = xhci_controller_setup(baseaddr);
if (!xhci)
return;

xhci->usb.pci = pci;
run_thread(configure_xhci, xhci);
}

static void
xhci_controller_setup_acpi(struct acpi_device *dev)
{
struct usb_xhci_s *xhci;
u64 mem, unused;
void *baseaddr;

if (acpi_dsdt_find_mem(dev, &mem, &unused) < 0)
return;
if (mem >= 0x100000000ll)
return;

baseaddr = (void*)(u32)mem;
dprintf(1, "ACPI: XHCI at mmio %p\n", baseaddr);

xhci = xhci_controller_setup(baseaddr);
if (!xhci)
return;

xhci->usb.mmio = baseaddr;
run_thread(configure_xhci, xhci);
}

Expand All @@ -634,10 +672,19 @@ xhci_setup(void)
{
if (! CONFIG_USB_XHCI)
return;

struct pci_device *pci;
foreachpci(pci) {
if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI)
xhci_controller_setup(pci);
xhci_controller_setup_pci(pci);
}

u16 xhci_eisaid = 0x0d10;
struct acpi_device *dev;
for (dev = acpi_dsdt_find_eisaid(NULL, xhci_eisaid);
dev != NULL;
dev = acpi_dsdt_find_eisaid(dev, xhci_eisaid)) {
xhci_controller_setup_acpi(dev);
}
}

Expand Down
27 changes: 19 additions & 8 deletions src/hw/usb.c
Expand Up @@ -248,14 +248,14 @@ get_device_config(struct usb_pipe *pipe)
if (ret)
return NULL;

void *config = malloc_tmphigh(cfg.wTotalLength);
struct usb_config_descriptor *config = malloc_tmphigh(cfg.wTotalLength);
if (!config) {
warn_noalloc();
return NULL;
}
req.wLength = cfg.wTotalLength;
ret = usb_send_default_control(pipe, &req, config);
if (ret) {
if (ret || config->wTotalLength != cfg.wTotalLength) {
free(config);
return NULL;
}
Expand Down Expand Up @@ -367,13 +367,24 @@ configure_usb_device(struct usbdevice_s *usbdev)
return 0;

// Determine if a driver exists for this device - only look at the
// first interface of the first configuration.
// interfaces of the first configuration.
int num_iface = config->bNumInterfaces;
void *config_end = (void*)config + config->wTotalLength;
struct usb_interface_descriptor *iface = (void*)(&config[1]);
if (iface->bInterfaceClass != USB_CLASS_HID
&& iface->bInterfaceClass != USB_CLASS_MASS_STORAGE
&& iface->bInterfaceClass != USB_CLASS_HUB)
// Not a supported device.
goto fail;
for (;;) {
if (!num_iface-- || (void*)iface + iface->bLength > config_end)
// Not a supported device.
goto fail;
if (iface->bDescriptorType == USB_DT_INTERFACE
&& (iface->bInterfaceClass == USB_CLASS_HUB
|| (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE
&& (iface->bInterfaceProtocol == US_PR_BULK
|| iface->bInterfaceProtocol == US_PR_UAS))
|| (iface->bInterfaceClass == USB_CLASS_HID
&& iface->bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT)))
break;
iface = (void*)iface + iface->bLength;
}

// Set the configuration.
ret = set_configuration(usbdev->defpipe, config->bConfigurationValue);
Expand Down
1 change: 1 addition & 0 deletions src/hw/usb.h
Expand Up @@ -35,6 +35,7 @@ struct usb_s {
struct usb_pipe *freelist;
struct mutex_s resetlock;
struct pci_device *pci;
void *mmio;
u8 type;
u8 maxaddr;
};
Expand Down
84 changes: 83 additions & 1 deletion src/hw/virtio-blk.c
Expand Up @@ -18,8 +18,9 @@
#include "stacks.h" // run_thread
#include "std/disk.h" // DISK_RET_SUCCESS
#include "string.h" // memset
#include "util.h" // usleep
#include "util.h" // usleep, bootprio_find_pci_device, is_bootprio_strict
#include "virtio-pci.h"
#include "virtio-mmio.h"
#include "virtio-ring.h"
#include "virtio-blk.h"

Expand Down Expand Up @@ -181,6 +182,78 @@ init_virtio_blk(void *data)
char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%pP", pci);
boot_add_hd(&vdrive->drive, desc, bootprio_find_pci_device(pci));

status |= VIRTIO_CONFIG_S_DRIVER_OK;
vp_set_status(&vdrive->vp, status);

boot_lchs_find_pci_device(pci, &vdrive->drive.lchs);
return;

fail:
vp_reset(&vdrive->vp);
free(vdrive->vq);
free(vdrive);
}

void
init_virtio_blk_mmio(void *mmio)
{
u8 status = VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER;
dprintf(1, "found virtio-blk-mmio at %p\n", mmio);
struct virtiodrive_s *vdrive = malloc_low(sizeof(*vdrive));
if (!vdrive) {
warn_noalloc();
return;
}
memset(vdrive, 0, sizeof(*vdrive));
vdrive->drive.type = DTYPE_VIRTIO_BLK;
vdrive->drive.cntl_id = (u32)mmio;

vp_init_mmio(&vdrive->vp, mmio);
if (vp_find_vq(&vdrive->vp, 0, &vdrive->vq) < 0 ) {
dprintf(1, "fail to find vq for virtio-blk-mmio %p\n", mmio);
goto fail;
}

struct vp_device *vp = &vdrive->vp;
u64 features = vp_get_features(vp);
u64 version1 = 1ull << VIRTIO_F_VERSION_1;
u64 blk_size = 1ull << VIRTIO_BLK_F_BLK_SIZE;

features = features & (version1 | blk_size);
vp_set_features(vp, features);
status |= VIRTIO_CONFIG_S_FEATURES_OK;
vp_set_status(vp, status);
if (!(vp_get_status(vp) & VIRTIO_CONFIG_S_FEATURES_OK)) {
dprintf(1, "device didn't accept features: %p\n", mmio);
goto fail;
}

vdrive->drive.sectors =
vp_read(&vp->device, struct virtio_blk_config, capacity);
if (features & blk_size) {
vdrive->drive.blksize =
vp_read(&vp->device, struct virtio_blk_config, blk_size);
} else {
vdrive->drive.blksize = DISK_SECTOR_SIZE;
}
if (vdrive->drive.blksize != DISK_SECTOR_SIZE) {
dprintf(1, "virtio-blk-mmio %p block size %d is unsupported\n",
mmio, vdrive->drive.blksize);
goto fail;
}
dprintf(1, "virtio-blk-mmio %p blksize=%d sectors=%u\n",
mmio, vdrive->drive.blksize, (u32)vdrive->drive.sectors);

vdrive->drive.pchs.cylinder =
vp_read(&vp->device, struct virtio_blk_config, cylinders);
vdrive->drive.pchs.head =
vp_read(&vp->device, struct virtio_blk_config, heads);
vdrive->drive.pchs.sector =
vp_read(&vp->device, struct virtio_blk_config, sectors);

char *desc = znprintf(MAXDESCSIZE, "Virtio disk mmio:%p", mmio);
boot_add_hd(&vdrive->drive, desc, bootprio_find_mmio_device(mmio));

status |= VIRTIO_CONFIG_S_DRIVER_OK;
vp_set_status(&vdrive->vp, status);
return;
Expand All @@ -194,6 +267,8 @@ init_virtio_blk(void *data)
void
virtio_blk_setup(void)
{
u8 skip_nonbootable = is_bootprio_strict();

ASSERT32FLAT();
if (! CONFIG_VIRTIO_BLK)
return;
Expand All @@ -206,6 +281,13 @@ virtio_blk_setup(void)
(pci->device != PCI_DEVICE_ID_VIRTIO_BLK_09 &&
pci->device != PCI_DEVICE_ID_VIRTIO_BLK_10))
continue;

if (skip_nonbootable && bootprio_find_pci_device(pci) < 0) {
dprintf(1, "skipping init of a non-bootable virtio-blk at %pP\n",
pci);
continue;
}

run_thread(init_virtio_blk, pci);
}
}
1 change: 1 addition & 0 deletions src/hw/virtio-blk.h
Expand Up @@ -39,5 +39,6 @@ struct virtio_blk_outhdr {
struct disk_op_s;
int virtio_blk_process_op(struct disk_op_s *op);
void virtio_blk_setup(void);
void init_virtio_blk_mmio(void *mmio);

#endif /* _VIRTIO_BLK_H */
97 changes: 97 additions & 0 deletions src/hw/virtio-mmio.c
@@ -0,0 +1,97 @@
#include "config.h" // CONFIG_DEBUG_LEVEL
#include "malloc.h" // free
#include "output.h" // dprintf
#include "stacks.h" // run_thread
#include "string.h" // memset
#include "util.h" // acpi_dsdt_*
#include "virtio-pci.h"
#include "virtio-blk.h"
#include "virtio-scsi.h"
#include "virtio-ring.h"
#include "virtio-mmio.h"

void virtio_mmio_setup_acpi(void)
{
static const char *virtio_hid = "LNRO0005";
struct acpi_device *dev;
u64 mem, irq, unused;

for (dev = acpi_dsdt_find_string(NULL, virtio_hid);
dev != NULL;
dev = acpi_dsdt_find_string(dev, virtio_hid)) {
if (acpi_dsdt_find_mem(dev, &mem, &unused) < 0)
continue;
if (acpi_dsdt_find_irq(dev, &irq) < 0)
continue;
dprintf(1, "ACPI: virtio-mmio device %s at 0x%llx, irq %lld\n",
acpi_dsdt_name(dev), mem, irq);
virtio_mmio_setup_one(mem);
}
}

void virtio_mmio_setup_one(u64 addr)
{
static const char *names[] = {
[ 1 ] = "net",
[ 2 ] = "blk",
[ 3 ] = "console",
[ 4 ] = "rng",
[ 8 ] = "scsi",
[ 9 ] = "9p",
[ 16 ] = "gpu",
[ 19 ] = "vsock",
[ 18 ] = "input",
[ 26 ] = "fs",
};
const char *name;
u32 magic, version, devid;
void *mmio;

if (addr >= 0x100000000) {
dprintf(1, "virtio-mmio: %llx: above 4G\n", addr);
return;
}

mmio = (void*)(u32)(addr);
magic = readl(mmio);
if (magic != 0x74726976) {
dprintf(1, "virtio-mmio: %llx: magic mismatch\n", addr);
return;
}
version = readl(mmio+4);
if (version != 1 /* legacy */ &&
version != 2 /* 1.0 */) {
dprintf(1, "virtio-mmio: %llx: unknown version %d\n", addr, version);
return;
}
devid = readl(mmio+8);

name = (devid < ARRAY_SIZE(names) && names[devid] != NULL)
? names[devid] : "unknown";
dprintf(1, "virtio-mmio: %llx: device id %x (%s%s)\n",
addr, devid, name, version == 1 ? ", legacy" : "");

switch (devid) {
case 2: /* blk */
run_thread(init_virtio_blk_mmio, mmio);
break;
case 8: /* scsi */
run_thread(init_virtio_scsi_mmio, mmio);
break;
default:
break;
}
}

void vp_init_mmio(struct vp_device *vp, void *mmio)
{
memset(vp, 0, sizeof(*vp));
vp->use_mmio = 1;
vp->common.mode = VP_ACCESS_MMIO;
vp->common.memaddr = mmio;
vp->device.mode = VP_ACCESS_MMIO;
vp->device.memaddr = mmio + 0x100;
vp_reset(vp);
vp_set_status(vp, VIRTIO_CONFIG_S_ACKNOWLEDGE |
VIRTIO_CONFIG_S_DRIVER);
}
77 changes: 77 additions & 0 deletions src/hw/virtio-mmio.h
@@ -0,0 +1,77 @@
#ifndef _VIRTIO_MMIO_H
#define _VIRTIO_MMIO_H

struct vp_device;

typedef struct virtio_mmio_cfg {
u32 magic;
u32 version;
u32 device_id;
u32 vendor_id;

u32 device_feature;
u32 device_feature_select;
u32 res_18;
u32 res_1c;

u32 guest_feature;
u32 guest_feature_select;
u32 legacy_guest_page_size;
u32 res_2c;

u32 queue_select;
u32 queue_num_max;
u32 queue_num;
u32 legacy_queue_align;

u32 legacy_queue_pfn;
u32 queue_ready;
u32 res_48;
u32 res_4c;

u32 queue_notify;
u32 res_54;
u32 res_58;
u32 res_5c;

u32 irq_status;
u32 irq_ack;
u32 res_68;
u32 res_6c;

u32 device_status;
u32 res_74;
u32 res_78;
u32 res_7c;

u32 queue_desc_lo;
u32 queue_desc_hi;
u32 res_88;
u32 res_8c;

u32 queue_driver_lo;
u32 queue_driver_hi;
u32 res_98;
u32 res_9c;

u32 queue_device_lo;
u32 queue_device_hi;
u32 res_a8;
u32 shm_sel;

u32 shmem_len_lo;
u32 shmem_len_hi;
u32 shmem_base_lo;
u32 shmem_base_hi;

u32 res_c0_f7[14];

u32 res_f8;
u32 config_generation;
} virtio_mmio_cfg;

void virtio_mmio_setup_acpi(void);
void virtio_mmio_setup_one(u64 mmio);
void vp_init_mmio(struct vp_device *vp, void *mmio);

#endif /* _VIRTIO_MMIO_H */
78 changes: 61 additions & 17 deletions src/hw/virtio-pci.c
Expand Up @@ -23,6 +23,7 @@
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
#include "string.h" // memset
#include "virtio-pci.h"
#include "virtio-mmio.h"
#include "virtio-ring.h"

u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size)
Expand Down Expand Up @@ -189,7 +190,11 @@ u64 vp_get_features(struct vp_device *vp)
{
u32 f0, f1;

if (vp->use_modern) {
if (vp->use_mmio) {
vp_write(&vp->common, virtio_mmio_cfg, device_feature_select, 0);
f0 = vp_read(&vp->common, virtio_mmio_cfg, device_feature);
f1 = 0;
} else if (vp->use_modern) {
vp_write(&vp->common, virtio_pci_common_cfg, device_feature_select, 0);
f0 = vp_read(&vp->common, virtio_pci_common_cfg, device_feature);
vp_write(&vp->common, virtio_pci_common_cfg, device_feature_select, 1);
Expand All @@ -208,7 +213,10 @@ void vp_set_features(struct vp_device *vp, u64 features)
f0 = features;
f1 = features >> 32;

if (vp->use_modern) {
if (vp->use_mmio) {
vp_write(&vp->common, virtio_mmio_cfg, guest_feature_select, f0);
vp_write(&vp->common, virtio_mmio_cfg, guest_feature, f0);
} else if (vp->use_modern) {
vp_write(&vp->common, virtio_pci_common_cfg, guest_feature_select, 0);
vp_write(&vp->common, virtio_pci_common_cfg, guest_feature, f0);
vp_write(&vp->common, virtio_pci_common_cfg, guest_feature_select, 1);
Expand All @@ -220,7 +228,9 @@ void vp_set_features(struct vp_device *vp, u64 features)

u8 vp_get_status(struct vp_device *vp)
{
if (vp->use_modern) {
if (vp->use_mmio) {
return vp_read(&vp->common, virtio_mmio_cfg, device_status);
} else if (vp->use_modern) {
return vp_read(&vp->common, virtio_pci_common_cfg, device_status);
} else {
return vp_read(&vp->legacy, virtio_pci_legacy, status);
Expand All @@ -231,7 +241,9 @@ void vp_set_status(struct vp_device *vp, u8 status)
{
if (status == 0) /* reset */
return;
if (vp->use_modern) {
if (vp->use_mmio) {
vp_write(&vp->common, virtio_mmio_cfg, device_status, status);
} else if (vp->use_modern) {
vp_write(&vp->common, virtio_pci_common_cfg, device_status, status);
} else {
vp_write(&vp->legacy, virtio_pci_legacy, status, status);
Expand All @@ -240,7 +252,9 @@ void vp_set_status(struct vp_device *vp, u8 status)

u8 vp_get_isr(struct vp_device *vp)
{
if (vp->use_modern) {
if (vp->use_mmio) {
return vp_read(&vp->common, virtio_mmio_cfg, irq_status);
} else if (vp->use_modern) {
return vp_read(&vp->isr, virtio_pci_isr, isr);
} else {
return vp_read(&vp->legacy, virtio_pci_legacy, isr);
Expand All @@ -249,7 +263,10 @@ u8 vp_get_isr(struct vp_device *vp)

void vp_reset(struct vp_device *vp)
{
if (vp->use_modern) {
if (vp->use_mmio) {
vp_write(&vp->common, virtio_mmio_cfg, device_status, 0);
vp_read(&vp->common, virtio_mmio_cfg, irq_status);
} else if (vp->use_modern) {
vp_write(&vp->common, virtio_pci_common_cfg, device_status, 0);
vp_read(&vp->isr, virtio_pci_isr, isr);
} else {
Expand All @@ -260,7 +277,9 @@ void vp_reset(struct vp_device *vp)

void vp_notify(struct vp_device *vp, struct vring_virtqueue *vq)
{
if (vp->use_modern) {
if (vp->use_mmio) {
vp_write(&vp->common, virtio_mmio_cfg, queue_notify, vq->queue_index);
} else if (vp->use_modern) {
u32 offset = vq->queue_notify_off * vp->notify_off_multiplier;
switch (vp->notify.mode) {
case VP_ACCESS_IO:
Expand Down Expand Up @@ -305,14 +324,21 @@ int vp_find_vq(struct vp_device *vp, int queue_index,


/* select the queue */
if (vp->use_modern) {
if (vp->use_mmio) {
vp_write(&vp->common, virtio_mmio_cfg, queue_select, queue_index);
} else if (vp->use_modern) {
vp_write(&vp->common, virtio_pci_common_cfg, queue_select, queue_index);
} else {
vp_write(&vp->legacy, virtio_pci_legacy, queue_sel, queue_index);
}

/* check if the queue is available */
if (vp->use_modern) {
if (vp->use_mmio) {
num = vp_read(&vp->common, virtio_mmio_cfg, queue_num_max);
if (num > MAX_QUEUE_NUM)
num = MAX_QUEUE_NUM;
vp_write(&vp->common, virtio_mmio_cfg, queue_num, num);
} else if (vp->use_modern) {
num = vp_read(&vp->common, virtio_pci_common_cfg, queue_size);
if (num > MAX_QUEUE_NUM) {
vp_write(&vp->common, virtio_pci_common_cfg, queue_size,
Expand All @@ -332,7 +358,9 @@ int vp_find_vq(struct vp_device *vp, int queue_index,
}

/* check if the queue is already active */
if (vp->use_modern) {
if (vp->use_mmio) {
/* TODO */;
} else if (vp->use_modern) {
if (vp_read(&vp->common, virtio_pci_common_cfg, queue_enable)) {
dprintf(1, "ERROR: queue already active\n");
goto fail;
Expand All @@ -354,7 +382,25 @@ int vp_find_vq(struct vp_device *vp, int queue_index,
* NOTE: vr->desc is initialized by vring_init()
*/

if (vp->use_modern) {
if (vp->use_mmio) {
if (vp_read(&vp->common, virtio_mmio_cfg, version) == 2) {
vp_write(&vp->common, virtio_mmio_cfg, queue_desc_lo,
(unsigned long)virt_to_phys(vr->desc));
vp_write(&vp->common, virtio_mmio_cfg, queue_desc_hi, 0);
vp_write(&vp->common, virtio_mmio_cfg, queue_driver_lo,
(unsigned long)virt_to_phys(vr->avail));
vp_write(&vp->common, virtio_mmio_cfg, queue_driver_hi, 0);
vp_write(&vp->common, virtio_mmio_cfg, queue_device_lo,
(unsigned long)virt_to_phys(vr->used));
vp_write(&vp->common, virtio_mmio_cfg, queue_device_hi, 0);
vp_write(&vp->common, virtio_mmio_cfg, queue_ready, 1);
} else {
vp_write(&vp->common, virtio_mmio_cfg, legacy_guest_page_size,
(unsigned long)1 << PAGE_SHIFT);
vp_write(&vp->common, virtio_mmio_cfg, legacy_queue_pfn,
(unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT);
}
} else if (vp->use_modern) {
vp_write(&vp->common, virtio_pci_common_cfg, queue_desc_lo,
(unsigned long)virt_to_phys(vr->desc));
vp_write(&vp->common, virtio_pci_common_cfg, queue_desc_hi, 0);
Expand Down Expand Up @@ -417,9 +463,8 @@ void vp_init_simple(struct vp_device *vp, struct pci_device *pci)
vp->device.cfg = cap;
vp->device.bdf = pci->bdf;
vp_cap = NULL;
dprintf(1, "pci dev %x:%x virtio cap at 0x%x type %d [pci cfg access]\n",
pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
cap, type);
dprintf(1, "pci dev %pP virtio cap at 0x%x type %d"
" [pci cfg access]\n", pci, cap, type);
break;
default:
vp_cap = NULL;
Expand Down Expand Up @@ -473,10 +518,9 @@ void vp_init_simple(struct vp_device *vp, struct pci_device *pci)
mode = "Huh?";
break;
}
dprintf(1, "pci dev %x:%x virtio cap at 0x%x type %d "
dprintf(1, "pci dev %pP virtio cap at 0x%x type %d "
"bar %d at 0x%08llx off +0x%04x [%s]\n",
pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
vp_cap->cap, type, vp_cap->bar, addr, offset, mode);
pci, vp_cap->cap, type, vp_cap->bar, addr, offset, mode);
}

cap = pci_find_capability(pci->bdf, PCI_CAP_ID_VNDR, cap);
Expand Down
1 change: 1 addition & 0 deletions src/hw/virtio-pci.h
Expand Up @@ -111,6 +111,7 @@ struct vp_device {
struct vp_cap common, notify, isr, device, legacy;
u32 notify_off_multiplier;
u8 use_modern;
u8 use_mmio;
};

u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size);
Expand Down
2 changes: 1 addition & 1 deletion src/hw/virtio-ring.h
Expand Up @@ -20,7 +20,7 @@
#define VIRTIO_F_VERSION_1 32
#define VIRTIO_F_IOMMU_PLATFORM 33

#define MAX_QUEUE_NUM (128)
#define MAX_QUEUE_NUM (256)

#define VRING_DESC_F_NEXT 1
#define VRING_DESC_F_WRITE 2
Expand Down