MIPS patches 2017-02-22
Changes: * Add MIPS Boston board support -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.5 (GNU/Linux) iQIVAwUAWKzWYCI464bV95fCAQLAvA//bCOMN1dcTA9CZHPtHVR0zm7Mm83ree6c QglgHh3S0mGUbC3MqzO44hsd851QWigAt0zjI7xn4/rtSPS82Bn3+EXDXt+l3+Ll A58rx3Yjkz9XlLrhTSXlt0Pfltc6G7f1Migmq6scQsJflf5YLXQVyXVuhAwhiFSm 4U0UYXSMtRhU6PtgcjNu+qGn+1qn3DlaiwZcKMUh1X2f5PeCbzIJ47ju4hmFoQUi qARYU1Ia5dnHUi/8Df1dL+XJo3tafVdmIjfYP0SOuyRVxCs6GIAylV5h92HHgF8w rTmTlnD4XU2Ef+x4oBvejNLwL0mvW/pYo+VMWuV96kXUxRU5KeDjaQk1tULpxZcu xcKAqN6xcWllO2v1YomHSkzudby1FPECDPNsLkbvaGBG6mIOPgoAUyHZQT7MxWXN dhXa3cV770FYck6X4QU2LD9kSJ+8L0ZXGQj0EheQwU6ofJ66DW0//pzpVgAdXjJ4 BssXKdEnHlkpWJw/PWCxt2fUPKzmgRQ9Q6kR5PuzWHNXXBMzoc+ztOR5NfrcrUUT EpAEHLjoEjzT2wIwUmZccszZiLVT1fe5dGwCI+OgIUTkeq0tQMRL5nVuNufOiFgc 9KpfujUA4LlvgdEEA9HhEfhTQLjM4O0xI82mXN+R68w3mvsJ3ygKl6rd+7XNmmwx nRvHWith+EI= =sAzw -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/yongbok/tags/mips-20170222' into staging MIPS patches 2017-02-22 Changes: * Add MIPS Boston board support # gpg: Signature made Wed 22 Feb 2017 00:08:00 GMT # gpg: using RSA key 0x2238EB86D5F797C2 # gpg: Good signature from "Yongbok Kim <yongbok.kim@imgtec.com>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 8600 4CF5 3415 A5D9 4CFA 2B5C 2238 EB86 D5F7 97C2 * remotes/yongbok/tags/mips-20170222: hw/mips: MIPS Boston board support hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller loader: Support Flattened Image Trees (FIT images) dtc: Update requirement to v1.4.2 target-mips: Provide function to test if a CPU supports an ISA hw/mips_gic: Update pin state on mask changes hw/mips_gictimer: provide API for retrieving frequency hw/mips_cmgcr: allow GCR base to be moved Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
10f25e4844
8
configure
vendored
8
configure
vendored
|
@ -3378,7 +3378,7 @@ fi
|
||||||
fdt_required=no
|
fdt_required=no
|
||||||
for target in $target_list; do
|
for target in $target_list; do
|
||||||
case $target in
|
case $target in
|
||||||
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu)
|
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
|
||||||
fdt_required=yes
|
fdt_required=yes
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -3396,11 +3396,11 @@ fi
|
||||||
if test "$fdt" != "no" ; then
|
if test "$fdt" != "no" ; then
|
||||||
fdt_libs="-lfdt"
|
fdt_libs="-lfdt"
|
||||||
# explicitly check for libfdt_env.h as it is missing in some stable installs
|
# explicitly check for libfdt_env.h as it is missing in some stable installs
|
||||||
# and test for required functions to make sure we are on a version >= 1.4.0
|
# and test for required functions to make sure we are on a version >= 1.4.2
|
||||||
cat > $TMPC << EOF
|
cat > $TMPC << EOF
|
||||||
#include <libfdt.h>
|
#include <libfdt.h>
|
||||||
#include <libfdt_env.h>
|
#include <libfdt_env.h>
|
||||||
int main(void) { fdt_get_property_by_offset(0, 0, 0); return 0; }
|
int main(void) { fdt_first_subnode(0, 0); return 0; }
|
||||||
EOF
|
EOF
|
||||||
if compile_prog "" "$fdt_libs" ; then
|
if compile_prog "" "$fdt_libs" ; then
|
||||||
# system DTC is good - use it
|
# system DTC is good - use it
|
||||||
|
@ -3418,7 +3418,7 @@ EOF
|
||||||
fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs"
|
fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs"
|
||||||
elif test "$fdt" = "yes" ; then
|
elif test "$fdt" = "yes" ; then
|
||||||
# have neither and want - prompt for system/submodule install
|
# have neither and want - prompt for system/submodule install
|
||||||
error_exit "DTC (libfdt) version >= 1.4.0 not present. Your options:" \
|
error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" \
|
||||||
" (1) Preferred: Install the DTC (libfdt) devel package" \
|
" (1) Preferred: Install the DTC (libfdt) devel package" \
|
||||||
" (2) Fetch the DTC submodule, using:" \
|
" (2) Fetch the DTC submodule, using:" \
|
||||||
" git submodule update --init dtc"
|
" git submodule update --init dtc"
|
||||||
|
|
|
@ -10,3 +10,6 @@ CONFIG_JAZZ=y
|
||||||
CONFIG_G364FB=y
|
CONFIG_G364FB=y
|
||||||
CONFIG_JAZZ_LED=y
|
CONFIG_JAZZ_LED=y
|
||||||
CONFIG_VT82C686=y
|
CONFIG_VT82C686=y
|
||||||
|
CONFIG_MIPS_BOSTON=y
|
||||||
|
CONFIG_FITLOADER=y
|
||||||
|
CONFIG_PCI_XILINX=y
|
||||||
|
|
2
dtc
2
dtc
|
@ -1 +1 @@
|
||||||
Subproject commit 65cc4d2748a2c2e6f27f1cf39e07a5dbabd80ebf
|
Subproject commit ec02b34c05be04f249ffaaca4b666f5246877dea
|
|
@ -13,6 +13,7 @@ common-obj-$(CONFIG_PTIMER) += ptimer.o
|
||||||
common-obj-$(CONFIG_SOFTMMU) += sysbus.o
|
common-obj-$(CONFIG_SOFTMMU) += sysbus.o
|
||||||
common-obj-$(CONFIG_SOFTMMU) += machine.o
|
common-obj-$(CONFIG_SOFTMMU) += machine.o
|
||||||
common-obj-$(CONFIG_SOFTMMU) += loader.o
|
common-obj-$(CONFIG_SOFTMMU) += loader.o
|
||||||
|
common-obj-$(CONFIG_FITLOADER) += loader-fit.o
|
||||||
common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
|
common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
|
||||||
common-obj-$(CONFIG_SOFTMMU) += register.o
|
common-obj-$(CONFIG_SOFTMMU) += register.o
|
||||||
common-obj-$(CONFIG_SOFTMMU) += or-irq.o
|
common-obj-$(CONFIG_SOFTMMU) += or-irq.o
|
||||||
|
|
325
hw/core/loader-fit.c
Normal file
325
hw/core/loader-fit.c
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
/*
|
||||||
|
* Flattened Image Tree loader.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Imagination Technologies
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "exec/memory.h"
|
||||||
|
#include "hw/loader.h"
|
||||||
|
#include "hw/loader-fit.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "sysemu/device_tree.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
|
#include <libfdt.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#define FIT_LOADER_MAX_PATH (128)
|
||||||
|
|
||||||
|
static const void *fit_load_image_alloc(const void *itb, const char *name,
|
||||||
|
int *poff, size_t *psz)
|
||||||
|
{
|
||||||
|
const void *data;
|
||||||
|
const char *comp;
|
||||||
|
void *uncomp_data;
|
||||||
|
char path[FIT_LOADER_MAX_PATH];
|
||||||
|
int off, sz;
|
||||||
|
ssize_t uncomp_len;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/images/%s", name);
|
||||||
|
|
||||||
|
off = fdt_path_offset(itb, path);
|
||||||
|
if (off < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (poff) {
|
||||||
|
*poff = off;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = fdt_getprop(itb, off, "data", &sz);
|
||||||
|
if (!data) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
comp = fdt_getprop(itb, off, "compression", NULL);
|
||||||
|
if (!comp || !strcmp(comp, "none")) {
|
||||||
|
if (psz) {
|
||||||
|
*psz = sz;
|
||||||
|
}
|
||||||
|
uncomp_data = g_malloc(sz);
|
||||||
|
memmove(uncomp_data, data, sz);
|
||||||
|
return uncomp_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(comp, "gzip")) {
|
||||||
|
uncomp_len = UBOOT_MAX_GUNZIP_BYTES;
|
||||||
|
uncomp_data = g_malloc(uncomp_len);
|
||||||
|
|
||||||
|
uncomp_len = gunzip(uncomp_data, uncomp_len, (void *) data, sz);
|
||||||
|
if (uncomp_len < 0) {
|
||||||
|
error_printf("unable to decompress %s image\n", name);
|
||||||
|
g_free(uncomp_data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = g_realloc(uncomp_data, uncomp_len);
|
||||||
|
if (psz) {
|
||||||
|
*psz = uncomp_len;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_printf("unknown compression '%s'\n", comp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fit_image_addr(const void *itb, int img, const char *name,
|
||||||
|
hwaddr *addr)
|
||||||
|
{
|
||||||
|
const void *prop;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
prop = fdt_getprop(itb, img, name, &len);
|
||||||
|
if (!prop) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (len) {
|
||||||
|
case 4:
|
||||||
|
*addr = fdt32_to_cpu(*(fdt32_t *)prop);
|
||||||
|
return 0;
|
||||||
|
case 8:
|
||||||
|
*addr = fdt64_to_cpu(*(fdt64_t *)prop);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
error_printf("invalid %s address length %d\n", name, len);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fit_load_kernel(const struct fit_loader *ldr, const void *itb,
|
||||||
|
int cfg, void *opaque, hwaddr *pend)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
const void *data;
|
||||||
|
const void *load_data;
|
||||||
|
hwaddr load_addr, entry_addr;
|
||||||
|
int img_off, err;
|
||||||
|
size_t sz;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
name = fdt_getprop(itb, cfg, "kernel", NULL);
|
||||||
|
if (!name) {
|
||||||
|
error_printf("no kernel specified by FIT configuration\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz);
|
||||||
|
if (!data) {
|
||||||
|
error_printf("unable to load kernel image from FIT\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fit_image_addr(itb, img_off, "load", &load_addr);
|
||||||
|
if (err) {
|
||||||
|
error_printf("unable to read kernel load address from FIT\n");
|
||||||
|
ret = err;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fit_image_addr(itb, img_off, "entry", &entry_addr);
|
||||||
|
if (err) {
|
||||||
|
error_printf("unable to read kernel entry address from FIT\n");
|
||||||
|
ret = err;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ldr->kernel_filter) {
|
||||||
|
load_data = ldr->kernel_filter(opaque, data, &load_addr, &entry_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pend) {
|
||||||
|
*pend = load_addr + sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_addr = ldr->addr_to_phys(opaque, load_addr);
|
||||||
|
rom_add_blob_fixed(name, load_data, sz, load_addr);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
g_free((void *) data);
|
||||||
|
if (data != load_data) {
|
||||||
|
g_free((void *) load_data);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fit_load_fdt(const struct fit_loader *ldr, const void *itb,
|
||||||
|
int cfg, void *opaque, const void *match_data,
|
||||||
|
hwaddr kernel_end)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
const void *data;
|
||||||
|
const void *load_data;
|
||||||
|
hwaddr load_addr;
|
||||||
|
int img_off, err;
|
||||||
|
size_t sz;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
name = fdt_getprop(itb, cfg, "fdt", NULL);
|
||||||
|
if (!name) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz);
|
||||||
|
if (!data) {
|
||||||
|
error_printf("unable to load FDT image from FIT\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fit_image_addr(itb, img_off, "load", &load_addr);
|
||||||
|
if (err == -ENOENT) {
|
||||||
|
load_addr = ROUND_UP(kernel_end, 64 * K_BYTE) + (10 * M_BYTE);
|
||||||
|
} else if (err) {
|
||||||
|
ret = err;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ldr->fdt_filter) {
|
||||||
|
load_data = ldr->fdt_filter(opaque, data, match_data, &load_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
load_addr = ldr->addr_to_phys(opaque, load_addr);
|
||||||
|
sz = fdt_totalsize(load_data);
|
||||||
|
rom_add_blob_fixed(name, load_data, sz, load_addr);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
g_free((void *) data);
|
||||||
|
if (data != load_data) {
|
||||||
|
g_free((void *) load_data);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fit_cfg_compatible(const void *itb, int cfg, const char *compat)
|
||||||
|
{
|
||||||
|
const void *fdt;
|
||||||
|
const char *fdt_name;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
fdt_name = fdt_getprop(itb, cfg, "fdt", NULL);
|
||||||
|
if (!fdt_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdt = fit_load_image_alloc(itb, fdt_name, NULL, NULL);
|
||||||
|
if (!fdt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fdt_check_header(fdt)) {
|
||||||
|
ret = false;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fdt_node_check_compatible(fdt, 0, compat)) {
|
||||||
|
ret = false;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
out:
|
||||||
|
g_free((void *) fdt);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque)
|
||||||
|
{
|
||||||
|
const struct fit_loader_match *match;
|
||||||
|
const void *itb, *match_data = NULL;
|
||||||
|
const char *def_cfg_name;
|
||||||
|
char path[FIT_LOADER_MAX_PATH];
|
||||||
|
int itb_size, configs, cfg_off, off, err;
|
||||||
|
hwaddr kernel_end;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
itb = load_device_tree(filename, &itb_size);
|
||||||
|
if (!itb) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
configs = fdt_path_offset(itb, "/configurations");
|
||||||
|
if (configs < 0) {
|
||||||
|
ret = configs;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_off = -FDT_ERR_NOTFOUND;
|
||||||
|
|
||||||
|
if (ldr->matches) {
|
||||||
|
for (match = ldr->matches; match->compatible; match++) {
|
||||||
|
off = fdt_first_subnode(itb, configs);
|
||||||
|
while (off >= 0) {
|
||||||
|
if (fit_cfg_compatible(itb, off, match->compatible)) {
|
||||||
|
cfg_off = off;
|
||||||
|
match_data = match->data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
off = fdt_next_subnode(itb, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg_off >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg_off < 0) {
|
||||||
|
def_cfg_name = fdt_getprop(itb, configs, "default", NULL);
|
||||||
|
if (def_cfg_name) {
|
||||||
|
snprintf(path, sizeof(path), "/configurations/%s", def_cfg_name);
|
||||||
|
cfg_off = fdt_path_offset(itb, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg_off < 0) {
|
||||||
|
/* couldn't find a configuration to use */
|
||||||
|
ret = cfg_off;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fit_load_kernel(ldr, itb, cfg_off, opaque, &kernel_end);
|
||||||
|
if (err) {
|
||||||
|
ret = err;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end);
|
||||||
|
if (err) {
|
||||||
|
ret = err;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
g_free((void *) itb);
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -527,12 +527,7 @@ static void zfree(void *x, void *addr)
|
||||||
|
|
||||||
#define DEFLATED 8
|
#define DEFLATED 8
|
||||||
|
|
||||||
/* This is the usual maximum in uboot, so if a uImage overflows this, it would
|
ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen)
|
||||||
* overflow on real hardware too. */
|
|
||||||
#define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
|
|
||||||
|
|
||||||
static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
|
|
||||||
size_t srclen)
|
|
||||||
{
|
{
|
||||||
z_stream s;
|
z_stream s;
|
||||||
ssize_t dstbytes;
|
ssize_t dstbytes;
|
||||||
|
|
|
@ -20,31 +20,29 @@
|
||||||
#include "kvm_mips.h"
|
#include "kvm_mips.h"
|
||||||
#include "hw/intc/mips_gic.h"
|
#include "hw/intc/mips_gic.h"
|
||||||
|
|
||||||
static void mips_gic_set_vp_irq(MIPSGICState *gic, int vp, int pin, int level)
|
static void mips_gic_set_vp_irq(MIPSGICState *gic, int vp, int pin)
|
||||||
{
|
{
|
||||||
int ored_level = level;
|
int ored_level = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* ORing pending registers sharing same pin */
|
/* ORing pending registers sharing same pin */
|
||||||
if (!ored_level) {
|
for (i = 0; i < gic->num_irq; i++) {
|
||||||
for (i = 0; i < gic->num_irq; i++) {
|
if ((gic->irq_state[i].map_pin & GIC_MAP_MSK) == pin &&
|
||||||
if ((gic->irq_state[i].map_pin & GIC_MAP_MSK) == pin &&
|
gic->irq_state[i].map_vp == vp &&
|
||||||
gic->irq_state[i].map_vp == vp &&
|
gic->irq_state[i].enabled) {
|
||||||
gic->irq_state[i].enabled) {
|
ored_level |= gic->irq_state[i].pending;
|
||||||
ored_level |= gic->irq_state[i].pending;
|
|
||||||
}
|
|
||||||
if (ored_level) {
|
|
||||||
/* no need to iterate all interrupts */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (((gic->vps[vp].compare_map & GIC_MAP_MSK) == pin) &&
|
if (ored_level) {
|
||||||
(gic->vps[vp].mask & GIC_VP_MASK_CMP_MSK)) {
|
/* no need to iterate all interrupts */
|
||||||
/* ORing with local pending register (count/compare) */
|
break;
|
||||||
ored_level |= (gic->vps[vp].pend & GIC_VP_MASK_CMP_MSK) >>
|
|
||||||
GIC_VP_MASK_CMP_SHF;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (((gic->vps[vp].compare_map & GIC_MAP_MSK) == pin) &&
|
||||||
|
(gic->vps[vp].mask & GIC_VP_MASK_CMP_MSK)) {
|
||||||
|
/* ORing with local pending register (count/compare) */
|
||||||
|
ored_level |= (gic->vps[vp].pend & GIC_VP_MASK_CMP_MSK) >>
|
||||||
|
GIC_VP_MASK_CMP_SHF;
|
||||||
|
}
|
||||||
if (kvm_enabled()) {
|
if (kvm_enabled()) {
|
||||||
kvm_mips_set_ipi_interrupt(mips_env_get_cpu(gic->vps[vp].env),
|
kvm_mips_set_ipi_interrupt(mips_env_get_cpu(gic->vps[vp].env),
|
||||||
pin + GIC_CPU_PIN_OFFSET,
|
pin + GIC_CPU_PIN_OFFSET,
|
||||||
|
@ -55,21 +53,27 @@ static void mips_gic_set_vp_irq(MIPSGICState *gic, int vp, int pin, int level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gic_update_pin_for_irq(MIPSGICState *gic, int n_IRQ)
|
||||||
|
{
|
||||||
|
int vp = gic->irq_state[n_IRQ].map_vp;
|
||||||
|
int pin = gic->irq_state[n_IRQ].map_pin & GIC_MAP_MSK;
|
||||||
|
|
||||||
|
if (vp < 0 || vp >= gic->num_vps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mips_gic_set_vp_irq(gic, vp, pin);
|
||||||
|
}
|
||||||
|
|
||||||
static void gic_set_irq(void *opaque, int n_IRQ, int level)
|
static void gic_set_irq(void *opaque, int n_IRQ, int level)
|
||||||
{
|
{
|
||||||
MIPSGICState *gic = (MIPSGICState *) opaque;
|
MIPSGICState *gic = (MIPSGICState *) opaque;
|
||||||
int vp = gic->irq_state[n_IRQ].map_vp;
|
|
||||||
int pin = gic->irq_state[n_IRQ].map_pin & GIC_MAP_MSK;
|
|
||||||
|
|
||||||
gic->irq_state[n_IRQ].pending = (uint8_t) level;
|
gic->irq_state[n_IRQ].pending = (uint8_t) level;
|
||||||
if (!gic->irq_state[n_IRQ].enabled) {
|
if (!gic->irq_state[n_IRQ].enabled) {
|
||||||
/* GIC interrupt source disabled */
|
/* GIC interrupt source disabled */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (vp < 0 || vp >= gic->num_vps) {
|
gic_update_pin_for_irq(gic, n_IRQ);
|
||||||
return;
|
|
||||||
}
|
|
||||||
mips_gic_set_vp_irq(gic, vp, pin, level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define OFFSET_CHECK(c) \
|
#define OFFSET_CHECK(c) \
|
||||||
|
@ -209,7 +213,7 @@ static void gic_timer_store_vp_compare(MIPSGICState *gic, uint32_t vp_index,
|
||||||
gic->vps[vp_index].pend &= ~(1 << GIC_LOCAL_INT_COMPARE);
|
gic->vps[vp_index].pend &= ~(1 << GIC_LOCAL_INT_COMPARE);
|
||||||
if (gic->vps[vp_index].compare_map & GIC_MAP_TO_PIN_MSK) {
|
if (gic->vps[vp_index].compare_map & GIC_MAP_TO_PIN_MSK) {
|
||||||
uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK);
|
uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK);
|
||||||
mips_gic_set_vp_irq(gic, vp_index, pin, 0);
|
mips_gic_set_vp_irq(gic, vp_index, pin);
|
||||||
}
|
}
|
||||||
mips_gictimer_store_vp_compare(gic->gic_timer, vp_index, compare);
|
mips_gictimer_store_vp_compare(gic->gic_timer, vp_index, compare);
|
||||||
}
|
}
|
||||||
|
@ -286,6 +290,7 @@ static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||||
OFFSET_CHECK((base + size * 8) <= gic->num_irq);
|
OFFSET_CHECK((base + size * 8) <= gic->num_irq);
|
||||||
for (i = 0; i < size * 8; i++) {
|
for (i = 0; i < size * 8; i++) {
|
||||||
gic->irq_state[base + i].enabled &= !((data >> i) & 1);
|
gic->irq_state[base + i].enabled &= !((data >> i) & 1);
|
||||||
|
gic_update_pin_for_irq(gic, base + i);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GIC_SH_WEDGE_OFS:
|
case GIC_SH_WEDGE_OFS:
|
||||||
|
@ -305,6 +310,7 @@ static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||||
OFFSET_CHECK((base + size * 8) <= gic->num_irq);
|
OFFSET_CHECK((base + size * 8) <= gic->num_irq);
|
||||||
for (i = 0; i < size * 8; i++) {
|
for (i = 0; i < size * 8; i++) {
|
||||||
gic->irq_state[base + i].enabled |= (data >> i) & 1;
|
gic->irq_state[base + i].enabled |= (data >> i) & 1;
|
||||||
|
gic_update_pin_for_irq(gic, base + i);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GIC_SH_MAP0_PIN_OFS ... GIC_SH_MAP255_PIN_OFS:
|
case GIC_SH_MAP0_PIN_OFS ... GIC_SH_MAP255_PIN_OFS:
|
||||||
|
|
|
@ -4,3 +4,4 @@ obj-$(CONFIG_JAZZ) += mips_jazz.o
|
||||||
obj-$(CONFIG_FULONG) += mips_fulong2e.o
|
obj-$(CONFIG_FULONG) += mips_fulong2e.o
|
||||||
obj-y += gt64xxx_pci.o
|
obj-y += gt64xxx_pci.o
|
||||||
obj-$(CONFIG_MIPS_CPS) += cps.o
|
obj-$(CONFIG_MIPS_CPS) += cps.o
|
||||||
|
obj-$(CONFIG_MIPS_BOSTON) += boston.o
|
||||||
|
|
577
hw/mips/boston.c
Normal file
577
hw/mips/boston.c
Normal file
|
@ -0,0 +1,577 @@
|
||||||
|
/*
|
||||||
|
* MIPS Boston development board emulation.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Imagination Technologies
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
|
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "hw/boards.h"
|
||||||
|
#include "hw/char/serial.h"
|
||||||
|
#include "hw/hw.h"
|
||||||
|
#include "hw/ide/pci.h"
|
||||||
|
#include "hw/ide/ahci.h"
|
||||||
|
#include "hw/loader.h"
|
||||||
|
#include "hw/loader-fit.h"
|
||||||
|
#include "hw/mips/cps.h"
|
||||||
|
#include "hw/mips/cpudevs.h"
|
||||||
|
#include "hw/pci-host/xilinx-pcie.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "sysemu/char.h"
|
||||||
|
#include "sysemu/device_tree.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "sysemu/qtest.h"
|
||||||
|
|
||||||
|
#include <libfdt.h>
|
||||||
|
|
||||||
|
#define TYPE_MIPS_BOSTON "mips-boston"
|
||||||
|
#define BOSTON(obj) OBJECT_CHECK(BostonState, (obj), TYPE_MIPS_BOSTON)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
|
MachineState *mach;
|
||||||
|
MIPSCPSState *cps;
|
||||||
|
SerialState *uart;
|
||||||
|
|
||||||
|
CharBackend lcd_display;
|
||||||
|
char lcd_content[8];
|
||||||
|
bool lcd_inited;
|
||||||
|
|
||||||
|
hwaddr kernel_entry;
|
||||||
|
hwaddr fdt_base;
|
||||||
|
} BostonState;
|
||||||
|
|
||||||
|
enum boston_plat_reg {
|
||||||
|
PLAT_FPGA_BUILD = 0x00,
|
||||||
|
PLAT_CORE_CL = 0x04,
|
||||||
|
PLAT_WRAPPER_CL = 0x08,
|
||||||
|
PLAT_SYSCLK_STATUS = 0x0c,
|
||||||
|
PLAT_SOFTRST_CTL = 0x10,
|
||||||
|
#define PLAT_SOFTRST_CTL_SYSRESET (1 << 4)
|
||||||
|
PLAT_DDR3_STATUS = 0x14,
|
||||||
|
#define PLAT_DDR3_STATUS_LOCKED (1 << 0)
|
||||||
|
#define PLAT_DDR3_STATUS_CALIBRATED (1 << 2)
|
||||||
|
PLAT_PCIE_STATUS = 0x18,
|
||||||
|
#define PLAT_PCIE_STATUS_PCIE0_LOCKED (1 << 0)
|
||||||
|
#define PLAT_PCIE_STATUS_PCIE1_LOCKED (1 << 8)
|
||||||
|
#define PLAT_PCIE_STATUS_PCIE2_LOCKED (1 << 16)
|
||||||
|
PLAT_FLASH_CTL = 0x1c,
|
||||||
|
PLAT_SPARE0 = 0x20,
|
||||||
|
PLAT_SPARE1 = 0x24,
|
||||||
|
PLAT_SPARE2 = 0x28,
|
||||||
|
PLAT_SPARE3 = 0x2c,
|
||||||
|
PLAT_MMCM_DIV = 0x30,
|
||||||
|
#define PLAT_MMCM_DIV_CLK0DIV_SHIFT 0
|
||||||
|
#define PLAT_MMCM_DIV_INPUT_SHIFT 8
|
||||||
|
#define PLAT_MMCM_DIV_MUL_SHIFT 16
|
||||||
|
#define PLAT_MMCM_DIV_CLK1DIV_SHIFT 24
|
||||||
|
PLAT_BUILD_CFG = 0x34,
|
||||||
|
#define PLAT_BUILD_CFG_IOCU_EN (1 << 0)
|
||||||
|
#define PLAT_BUILD_CFG_PCIE0_EN (1 << 1)
|
||||||
|
#define PLAT_BUILD_CFG_PCIE1_EN (1 << 2)
|
||||||
|
#define PLAT_BUILD_CFG_PCIE2_EN (1 << 3)
|
||||||
|
PLAT_DDR_CFG = 0x38,
|
||||||
|
#define PLAT_DDR_CFG_SIZE (0xf << 0)
|
||||||
|
#define PLAT_DDR_CFG_MHZ (0xfff << 4)
|
||||||
|
PLAT_NOC_PCIE0_ADDR = 0x3c,
|
||||||
|
PLAT_NOC_PCIE1_ADDR = 0x40,
|
||||||
|
PLAT_NOC_PCIE2_ADDR = 0x44,
|
||||||
|
PLAT_SYS_CTL = 0x48,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void boston_lcd_event(void *opaque, int event)
|
||||||
|
{
|
||||||
|
BostonState *s = opaque;
|
||||||
|
if (event == CHR_EVENT_OPENED && !s->lcd_inited) {
|
||||||
|
qemu_chr_fe_printf(&s->lcd_display, " ");
|
||||||
|
s->lcd_inited = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t boston_lcd_read(void *opaque, hwaddr addr,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
BostonState *s = opaque;
|
||||||
|
uint64_t val = 0;
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 8:
|
||||||
|
val |= (uint64_t)s->lcd_content[(addr + 7) & 0x7] << 56;
|
||||||
|
val |= (uint64_t)s->lcd_content[(addr + 6) & 0x7] << 48;
|
||||||
|
val |= (uint64_t)s->lcd_content[(addr + 5) & 0x7] << 40;
|
||||||
|
val |= (uint64_t)s->lcd_content[(addr + 4) & 0x7] << 32;
|
||||||
|
/* fall through */
|
||||||
|
case 4:
|
||||||
|
val |= (uint64_t)s->lcd_content[(addr + 3) & 0x7] << 24;
|
||||||
|
val |= (uint64_t)s->lcd_content[(addr + 2) & 0x7] << 16;
|
||||||
|
/* fall through */
|
||||||
|
case 2:
|
||||||
|
val |= (uint64_t)s->lcd_content[(addr + 1) & 0x7] << 8;
|
||||||
|
/* fall through */
|
||||||
|
case 1:
|
||||||
|
val |= (uint64_t)s->lcd_content[(addr + 0) & 0x7];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void boston_lcd_write(void *opaque, hwaddr addr,
|
||||||
|
uint64_t val, unsigned size)
|
||||||
|
{
|
||||||
|
BostonState *s = opaque;
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 8:
|
||||||
|
s->lcd_content[(addr + 7) & 0x7] = val >> 56;
|
||||||
|
s->lcd_content[(addr + 6) & 0x7] = val >> 48;
|
||||||
|
s->lcd_content[(addr + 5) & 0x7] = val >> 40;
|
||||||
|
s->lcd_content[(addr + 4) & 0x7] = val >> 32;
|
||||||
|
/* fall through */
|
||||||
|
case 4:
|
||||||
|
s->lcd_content[(addr + 3) & 0x7] = val >> 24;
|
||||||
|
s->lcd_content[(addr + 2) & 0x7] = val >> 16;
|
||||||
|
/* fall through */
|
||||||
|
case 2:
|
||||||
|
s->lcd_content[(addr + 1) & 0x7] = val >> 8;
|
||||||
|
/* fall through */
|
||||||
|
case 1:
|
||||||
|
s->lcd_content[(addr + 0) & 0x7] = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_chr_fe_printf(&s->lcd_display,
|
||||||
|
"\r%-8.8s", s->lcd_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps boston_lcd_ops = {
|
||||||
|
.read = boston_lcd_read,
|
||||||
|
.write = boston_lcd_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint64_t boston_platreg_read(void *opaque, hwaddr addr,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
BostonState *s = opaque;
|
||||||
|
uint32_t gic_freq, val;
|
||||||
|
|
||||||
|
if (size != 4) {
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%uB platform register read", size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (addr & 0xffff) {
|
||||||
|
case PLAT_FPGA_BUILD:
|
||||||
|
case PLAT_CORE_CL:
|
||||||
|
case PLAT_WRAPPER_CL:
|
||||||
|
return 0;
|
||||||
|
case PLAT_DDR3_STATUS:
|
||||||
|
return PLAT_DDR3_STATUS_LOCKED | PLAT_DDR3_STATUS_CALIBRATED;
|
||||||
|
case PLAT_MMCM_DIV:
|
||||||
|
gic_freq = mips_gictimer_get_freq(s->cps->gic.gic_timer) / 1000000;
|
||||||
|
val = gic_freq << PLAT_MMCM_DIV_INPUT_SHIFT;
|
||||||
|
val |= 1 << PLAT_MMCM_DIV_MUL_SHIFT;
|
||||||
|
val |= 1 << PLAT_MMCM_DIV_CLK0DIV_SHIFT;
|
||||||
|
val |= 1 << PLAT_MMCM_DIV_CLK1DIV_SHIFT;
|
||||||
|
return val;
|
||||||
|
case PLAT_BUILD_CFG:
|
||||||
|
val = PLAT_BUILD_CFG_PCIE0_EN;
|
||||||
|
val |= PLAT_BUILD_CFG_PCIE1_EN;
|
||||||
|
val |= PLAT_BUILD_CFG_PCIE2_EN;
|
||||||
|
return val;
|
||||||
|
case PLAT_DDR_CFG:
|
||||||
|
val = s->mach->ram_size / G_BYTE;
|
||||||
|
assert(!(val & ~PLAT_DDR_CFG_SIZE));
|
||||||
|
val |= PLAT_DDR_CFG_MHZ;
|
||||||
|
return val;
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx,
|
||||||
|
addr & 0xffff);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void boston_platreg_write(void *opaque, hwaddr addr,
|
||||||
|
uint64_t val, unsigned size)
|
||||||
|
{
|
||||||
|
if (size != 4) {
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%uB platform register write", size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (addr & 0xffff) {
|
||||||
|
case PLAT_FPGA_BUILD:
|
||||||
|
case PLAT_CORE_CL:
|
||||||
|
case PLAT_WRAPPER_CL:
|
||||||
|
case PLAT_DDR3_STATUS:
|
||||||
|
case PLAT_PCIE_STATUS:
|
||||||
|
case PLAT_MMCM_DIV:
|
||||||
|
case PLAT_BUILD_CFG:
|
||||||
|
case PLAT_DDR_CFG:
|
||||||
|
/* read only */
|
||||||
|
break;
|
||||||
|
case PLAT_SOFTRST_CTL:
|
||||||
|
if (val & PLAT_SOFTRST_CTL_SYSRESET) {
|
||||||
|
qemu_system_reset_request();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" HWADDR_PRIx
|
||||||
|
" = 0x%" PRIx64, addr & 0xffff, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps boston_platreg_ops = {
|
||||||
|
.read = boston_platreg_read,
|
||||||
|
.write = boston_platreg_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void boston_flash_write(void *opaque, hwaddr addr,
|
||||||
|
uint64_t val, unsigned size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps boston_flash_ops = {
|
||||||
|
.write = boston_flash_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeInfo boston_device = {
|
||||||
|
.name = TYPE_MIPS_BOSTON,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(BostonState),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void boston_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&boston_device);
|
||||||
|
}
|
||||||
|
type_init(boston_register_types)
|
||||||
|
|
||||||
|
static void gen_firmware(uint32_t *p, hwaddr kernel_entry, hwaddr fdt_addr,
|
||||||
|
bool is_64b)
|
||||||
|
{
|
||||||
|
const uint32_t cm_base = 0x16100000;
|
||||||
|
const uint32_t gic_base = 0x16120000;
|
||||||
|
const uint32_t cpc_base = 0x16200000;
|
||||||
|
|
||||||
|
/* Move CM GCRs */
|
||||||
|
if (is_64b) {
|
||||||
|
stl_p(p++, 0x40287803); /* dmfc0 $8, CMGCRBase */
|
||||||
|
stl_p(p++, 0x00084138); /* dsll $8, $8, 4 */
|
||||||
|
} else {
|
||||||
|
stl_p(p++, 0x40087803); /* mfc0 $8, CMGCRBase */
|
||||||
|
stl_p(p++, 0x00084100); /* sll $8, $8, 4 */
|
||||||
|
}
|
||||||
|
stl_p(p++, 0x3c09a000); /* lui $9, 0xa000 */
|
||||||
|
stl_p(p++, 0x01094025); /* or $8, $9 */
|
||||||
|
stl_p(p++, 0x3c0a0000 | (cm_base >> 16)); /* lui $10, cm_base >> 16 */
|
||||||
|
if (is_64b) {
|
||||||
|
stl_p(p++, 0xfd0a0008); /* sd $10, 0x8($8) */
|
||||||
|
} else {
|
||||||
|
stl_p(p++, 0xad0a0008); /* sw $10, 0x8($8) */
|
||||||
|
}
|
||||||
|
stl_p(p++, 0x012a4025); /* or $8, $10 */
|
||||||
|
|
||||||
|
/* Move & enable GIC GCRs */
|
||||||
|
stl_p(p++, 0x3c090000 | (gic_base >> 16)); /* lui $9, gic_base >> 16 */
|
||||||
|
stl_p(p++, 0x35290001); /* ori $9, 0x1 */
|
||||||
|
if (is_64b) {
|
||||||
|
stl_p(p++, 0xfd090080); /* sd $9, 0x80($8) */
|
||||||
|
} else {
|
||||||
|
stl_p(p++, 0xad090080); /* sw $9, 0x80($8) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move & enable CPC GCRs */
|
||||||
|
stl_p(p++, 0x3c090000 | (cpc_base >> 16)); /* lui $9, cpc_base >> 16 */
|
||||||
|
stl_p(p++, 0x35290001); /* ori $9, 0x1 */
|
||||||
|
if (is_64b) {
|
||||||
|
stl_p(p++, 0xfd090088); /* sd $9, 0x88($8) */
|
||||||
|
} else {
|
||||||
|
stl_p(p++, 0xad090088); /* sw $9, 0x88($8) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup argument registers to follow the UHI boot protocol:
|
||||||
|
*
|
||||||
|
* a0/$4 = -2
|
||||||
|
* a1/$5 = virtual address of FDT
|
||||||
|
* a2/$6 = 0
|
||||||
|
* a3/$7 = 0
|
||||||
|
*/
|
||||||
|
stl_p(p++, 0x2404fffe); /* li $4, -2 */
|
||||||
|
/* lui $5, hi(fdt_addr) */
|
||||||
|
stl_p(p++, 0x3c050000 | ((fdt_addr >> 16) & 0xffff));
|
||||||
|
if (fdt_addr & 0xffff) { /* ori $5, lo(fdt_addr) */
|
||||||
|
stl_p(p++, 0x34a50000 | (fdt_addr & 0xffff));
|
||||||
|
}
|
||||||
|
stl_p(p++, 0x34060000); /* li $6, 0 */
|
||||||
|
stl_p(p++, 0x34070000); /* li $7, 0 */
|
||||||
|
|
||||||
|
/* Load kernel entry address & jump to it */
|
||||||
|
/* lui $25, hi(kernel_entry) */
|
||||||
|
stl_p(p++, 0x3c190000 | ((kernel_entry >> 16) & 0xffff));
|
||||||
|
/* ori $25, lo(kernel_entry) */
|
||||||
|
stl_p(p++, 0x37390000 | (kernel_entry & 0xffff));
|
||||||
|
stl_p(p++, 0x03200009); /* jr $25 */
|
||||||
|
}
|
||||||
|
|
||||||
|
static const void *boston_fdt_filter(void *opaque, const void *fdt_orig,
|
||||||
|
const void *match_data, hwaddr *load_addr)
|
||||||
|
{
|
||||||
|
BostonState *s = BOSTON(opaque);
|
||||||
|
MachineState *machine = s->mach;
|
||||||
|
const char *cmdline;
|
||||||
|
int err;
|
||||||
|
void *fdt;
|
||||||
|
size_t fdt_sz, ram_low_sz, ram_high_sz;
|
||||||
|
|
||||||
|
fdt_sz = fdt_totalsize(fdt_orig) * 2;
|
||||||
|
fdt = g_malloc0(fdt_sz);
|
||||||
|
|
||||||
|
err = fdt_open_into(fdt_orig, fdt, fdt_sz);
|
||||||
|
if (err) {
|
||||||
|
fprintf(stderr, "unable to open FDT\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdline = (machine->kernel_cmdline && machine->kernel_cmdline[0])
|
||||||
|
? machine->kernel_cmdline : " ";
|
||||||
|
err = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
|
||||||
|
if (err < 0) {
|
||||||
|
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ram_low_sz = MIN(256 * M_BYTE, machine->ram_size);
|
||||||
|
ram_high_sz = machine->ram_size - ram_low_sz;
|
||||||
|
qemu_fdt_setprop_sized_cells(fdt, "/memory@0", "reg",
|
||||||
|
1, 0x00000000, 1, ram_low_sz,
|
||||||
|
1, 0x90000000, 1, ram_high_sz);
|
||||||
|
|
||||||
|
fdt = g_realloc(fdt, fdt_totalsize(fdt));
|
||||||
|
qemu_fdt_dumpdtb(fdt, fdt_sz);
|
||||||
|
|
||||||
|
s->fdt_base = *load_addr;
|
||||||
|
|
||||||
|
return fdt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const void *boston_kernel_filter(void *opaque, const void *kernel,
|
||||||
|
hwaddr *load_addr, hwaddr *entry_addr)
|
||||||
|
{
|
||||||
|
BostonState *s = BOSTON(opaque);
|
||||||
|
|
||||||
|
s->kernel_entry = *entry_addr;
|
||||||
|
|
||||||
|
return kernel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fit_loader_match boston_matches[] = {
|
||||||
|
{ "img,boston" },
|
||||||
|
{ NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fit_loader boston_fit_loader = {
|
||||||
|
.matches = boston_matches,
|
||||||
|
.addr_to_phys = cpu_mips_kseg0_to_phys,
|
||||||
|
.fdt_filter = boston_fdt_filter,
|
||||||
|
.kernel_filter = boston_kernel_filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline XilinxPCIEHost *
|
||||||
|
xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr,
|
||||||
|
hwaddr cfg_base, uint64_t cfg_size,
|
||||||
|
hwaddr mmio_base, uint64_t mmio_size,
|
||||||
|
qemu_irq irq, bool link_up)
|
||||||
|
{
|
||||||
|
DeviceState *dev;
|
||||||
|
MemoryRegion *cfg, *mmio;
|
||||||
|
|
||||||
|
dev = qdev_create(NULL, TYPE_XILINX_PCIE_HOST);
|
||||||
|
|
||||||
|
qdev_prop_set_uint32(dev, "bus_nr", bus_nr);
|
||||||
|
qdev_prop_set_uint64(dev, "cfg_base", cfg_base);
|
||||||
|
qdev_prop_set_uint64(dev, "cfg_size", cfg_size);
|
||||||
|
qdev_prop_set_uint64(dev, "mmio_base", mmio_base);
|
||||||
|
qdev_prop_set_uint64(dev, "mmio_size", mmio_size);
|
||||||
|
qdev_prop_set_bit(dev, "link_up", link_up);
|
||||||
|
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
|
cfg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
|
||||||
|
memory_region_add_subregion_overlap(sys_mem, cfg_base, cfg, 0);
|
||||||
|
|
||||||
|
mmio = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
|
||||||
|
memory_region_add_subregion_overlap(sys_mem, 0, mmio, 0);
|
||||||
|
|
||||||
|
qdev_connect_gpio_out_named(dev, "interrupt_out", 0, irq);
|
||||||
|
|
||||||
|
return XILINX_PCIE_HOST(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void boston_mach_init(MachineState *machine)
|
||||||
|
{
|
||||||
|
DeviceState *dev;
|
||||||
|
BostonState *s;
|
||||||
|
Error *err = NULL;
|
||||||
|
const char *cpu_model;
|
||||||
|
MemoryRegion *flash, *ddr, *ddr_low_alias, *lcd, *platreg;
|
||||||
|
MemoryRegion *sys_mem = get_system_memory();
|
||||||
|
XilinxPCIEHost *pcie2;
|
||||||
|
PCIDevice *ahci;
|
||||||
|
DriveInfo *hd[6];
|
||||||
|
Chardev *chr;
|
||||||
|
int fw_size, fit_err;
|
||||||
|
bool is_64b;
|
||||||
|
|
||||||
|
if ((machine->ram_size % G_BYTE) ||
|
||||||
|
(machine->ram_size > (2 * G_BYTE))) {
|
||||||
|
error_report("Memory size must be 1GB or 2GB");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_model = machine->cpu_model ?: "I6400";
|
||||||
|
|
||||||
|
dev = qdev_create(NULL, TYPE_MIPS_BOSTON);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
|
s = BOSTON(dev);
|
||||||
|
s->mach = machine;
|
||||||
|
s->cps = g_new0(MIPSCPSState, 1);
|
||||||
|
|
||||||
|
if (!cpu_supports_cps_smp(cpu_model)) {
|
||||||
|
error_report("Boston requires CPUs which support CPS");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_64b = cpu_supports_isa(cpu_model, ISA_MIPS64);
|
||||||
|
|
||||||
|
object_initialize(s->cps, sizeof(MIPSCPSState), TYPE_MIPS_CPS);
|
||||||
|
qdev_set_parent_bus(DEVICE(s->cps), sysbus_get_default());
|
||||||
|
|
||||||
|
object_property_set_str(OBJECT(s->cps), cpu_model, "cpu-model", &err);
|
||||||
|
object_property_set_int(OBJECT(s->cps), smp_cpus, "num-vp", &err);
|
||||||
|
object_property_set_bool(OBJECT(s->cps), true, "realized", &err);
|
||||||
|
|
||||||
|
if (err != NULL) {
|
||||||
|
error_report("%s", error_get_pretty(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1);
|
||||||
|
|
||||||
|
flash = g_new(MemoryRegion, 1);
|
||||||
|
memory_region_init_rom_device(flash, NULL, &boston_flash_ops, s,
|
||||||
|
"boston.flash", 128 * M_BYTE, &err);
|
||||||
|
memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0);
|
||||||
|
|
||||||
|
ddr = g_new(MemoryRegion, 1);
|
||||||
|
memory_region_allocate_system_memory(ddr, NULL, "boston.ddr",
|
||||||
|
machine->ram_size);
|
||||||
|
memory_region_add_subregion_overlap(sys_mem, 0x80000000, ddr, 0);
|
||||||
|
|
||||||
|
ddr_low_alias = g_new(MemoryRegion, 1);
|
||||||
|
memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr",
|
||||||
|
ddr, 0, MIN(machine->ram_size, (256 * M_BYTE)));
|
||||||
|
memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0);
|
||||||
|
|
||||||
|
xilinx_pcie_init(sys_mem, 0,
|
||||||
|
0x10000000, 32 * M_BYTE,
|
||||||
|
0x40000000, 1 * G_BYTE,
|
||||||
|
get_cps_irq(s->cps, 2), false);
|
||||||
|
|
||||||
|
xilinx_pcie_init(sys_mem, 1,
|
||||||
|
0x12000000, 32 * M_BYTE,
|
||||||
|
0x20000000, 512 * M_BYTE,
|
||||||
|
get_cps_irq(s->cps, 1), false);
|
||||||
|
|
||||||
|
pcie2 = xilinx_pcie_init(sys_mem, 2,
|
||||||
|
0x14000000, 32 * M_BYTE,
|
||||||
|
0x16000000, 1 * M_BYTE,
|
||||||
|
get_cps_irq(s->cps, 0), true);
|
||||||
|
|
||||||
|
platreg = g_new(MemoryRegion, 1);
|
||||||
|
memory_region_init_io(platreg, NULL, &boston_platreg_ops, s,
|
||||||
|
"boston-platregs", 0x1000);
|
||||||
|
memory_region_add_subregion_overlap(sys_mem, 0x17ffd000, platreg, 0);
|
||||||
|
|
||||||
|
if (!serial_hds[0]) {
|
||||||
|
serial_hds[0] = qemu_chr_new("serial0", "null");
|
||||||
|
}
|
||||||
|
|
||||||
|
s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2,
|
||||||
|
get_cps_irq(s->cps, 3), 10000000,
|
||||||
|
serial_hds[0], DEVICE_NATIVE_ENDIAN);
|
||||||
|
|
||||||
|
lcd = g_new(MemoryRegion, 1);
|
||||||
|
memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8);
|
||||||
|
memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0);
|
||||||
|
|
||||||
|
chr = qemu_chr_new("lcd", "vc:320x240");
|
||||||
|
qemu_chr_fe_init(&s->lcd_display, chr, NULL);
|
||||||
|
qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL,
|
||||||
|
boston_lcd_event, s, NULL, true);
|
||||||
|
|
||||||
|
ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus,
|
||||||
|
PCI_DEVFN(0, 0),
|
||||||
|
true, TYPE_ICH9_AHCI);
|
||||||
|
g_assert(ARRAY_SIZE(hd) == ICH_AHCI(ahci)->ahci.ports);
|
||||||
|
ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports);
|
||||||
|
ahci_ide_create_devs(ahci, hd);
|
||||||
|
|
||||||
|
if (machine->firmware) {
|
||||||
|
fw_size = load_image_targphys(machine->firmware,
|
||||||
|
0x1fc00000, 4 * M_BYTE);
|
||||||
|
if (fw_size == -1) {
|
||||||
|
error_printf("unable to load firmware image '%s'\n",
|
||||||
|
machine->firmware);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (machine->kernel_filename) {
|
||||||
|
fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s);
|
||||||
|
if (fit_err) {
|
||||||
|
error_printf("unable to load FIT image\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000,
|
||||||
|
s->kernel_entry, s->fdt_base, is_64b);
|
||||||
|
} else if (!qtest_enabled()) {
|
||||||
|
error_printf("Please provide either a -kernel or -bios argument\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void boston_mach_class_init(MachineClass *mc)
|
||||||
|
{
|
||||||
|
mc->desc = "MIPS Boston";
|
||||||
|
mc->init = boston_mach_init;
|
||||||
|
mc->block_default_type = IF_IDE;
|
||||||
|
mc->default_ram_size = 2 * G_BYTE;
|
||||||
|
mc->max_cpus = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_MACHINE("boston", boston_mach_class_init)
|
|
@ -29,6 +29,20 @@ static inline bool is_gic_connected(MIPSGCRState *s)
|
||||||
return s->gic_mr != NULL;
|
return s->gic_mr != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void update_gcr_base(MIPSGCRState *gcr, uint64_t val)
|
||||||
|
{
|
||||||
|
CPUState *cpu;
|
||||||
|
MIPSCPU *mips_cpu;
|
||||||
|
|
||||||
|
gcr->gcr_base = val & GCR_BASE_GCRBASE_MSK;
|
||||||
|
memory_region_set_address(&gcr->iomem, gcr->gcr_base);
|
||||||
|
|
||||||
|
CPU_FOREACH(cpu) {
|
||||||
|
mips_cpu = MIPS_CPU(cpu);
|
||||||
|
mips_cpu->env.CP0_CMGCRBase = gcr->gcr_base >> 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val)
|
static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val)
|
||||||
{
|
{
|
||||||
if (is_cpc_connected(gcr)) {
|
if (is_cpc_connected(gcr)) {
|
||||||
|
@ -117,6 +131,9 @@ static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||||
MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
|
MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
|
||||||
|
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
|
case GCR_BASE_OFS:
|
||||||
|
update_gcr_base(gcr, data);
|
||||||
|
break;
|
||||||
case GCR_GIC_BASE_OFS:
|
case GCR_GIC_BASE_OFS:
|
||||||
update_gic_base(gcr, data);
|
update_gic_base(gcr, data);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -16,3 +16,4 @@ common-obj-$(CONFIG_FULONG) += bonito.o
|
||||||
common-obj-$(CONFIG_PCI_PIIX) += piix.o
|
common-obj-$(CONFIG_PCI_PIIX) += piix.o
|
||||||
common-obj-$(CONFIG_PCI_Q35) += q35.o
|
common-obj-$(CONFIG_PCI_Q35) += q35.o
|
||||||
common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
|
common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
|
||||||
|
common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o
|
||||||
|
|
328
hw/pci-host/xilinx-pcie.c
Normal file
328
hw/pci-host/xilinx-pcie.c
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
/*
|
||||||
|
* Xilinx PCIe host controller emulation.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Imagination Technologies
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hw/pci/pci_bridge.h"
|
||||||
|
#include "hw/pci-host/xilinx-pcie.h"
|
||||||
|
|
||||||
|
enum root_cfg_reg {
|
||||||
|
/* Interrupt Decode Register */
|
||||||
|
ROOTCFG_INTDEC = 0x138,
|
||||||
|
|
||||||
|
/* Interrupt Mask Register */
|
||||||
|
ROOTCFG_INTMASK = 0x13c,
|
||||||
|
/* INTx Interrupt Received */
|
||||||
|
#define ROOTCFG_INTMASK_INTX (1 << 16)
|
||||||
|
/* MSI Interrupt Received */
|
||||||
|
#define ROOTCFG_INTMASK_MSI (1 << 17)
|
||||||
|
|
||||||
|
/* PHY Status/Control Register */
|
||||||
|
ROOTCFG_PSCR = 0x144,
|
||||||
|
/* Link Up */
|
||||||
|
#define ROOTCFG_PSCR_LINK_UP (1 << 11)
|
||||||
|
|
||||||
|
/* Root Port Status/Control Register */
|
||||||
|
ROOTCFG_RPSCR = 0x148,
|
||||||
|
/* Bridge Enable */
|
||||||
|
#define ROOTCFG_RPSCR_BRIDGEEN (1 << 0)
|
||||||
|
/* Interrupt FIFO Not Empty */
|
||||||
|
#define ROOTCFG_RPSCR_INTNEMPTY (1 << 18)
|
||||||
|
/* Interrupt FIFO Overflow */
|
||||||
|
#define ROOTCFG_RPSCR_INTOVF (1 << 19)
|
||||||
|
|
||||||
|
/* Root Port Interrupt FIFO Read Register 1 */
|
||||||
|
ROOTCFG_RPIFR1 = 0x158,
|
||||||
|
#define ROOTCFG_RPIFR1_INT_LANE_SHIFT 27
|
||||||
|
#define ROOTCFG_RPIFR1_INT_ASSERT_SHIFT 29
|
||||||
|
#define ROOTCFG_RPIFR1_INT_VALID_SHIFT 31
|
||||||
|
/* Root Port Interrupt FIFO Read Register 2 */
|
||||||
|
ROOTCFG_RPIFR2 = 0x15c,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void xilinx_pcie_update_intr(XilinxPCIEHost *s,
|
||||||
|
uint32_t set, uint32_t clear)
|
||||||
|
{
|
||||||
|
int level;
|
||||||
|
|
||||||
|
s->intr |= set;
|
||||||
|
s->intr &= ~clear;
|
||||||
|
|
||||||
|
if (s->intr_fifo_r != s->intr_fifo_w) {
|
||||||
|
s->intr |= ROOTCFG_INTMASK_INTX;
|
||||||
|
}
|
||||||
|
|
||||||
|
level = !!(s->intr & s->intr_mask);
|
||||||
|
qemu_set_irq(s->irq, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xilinx_pcie_queue_intr(XilinxPCIEHost *s,
|
||||||
|
uint32_t fifo_reg1, uint32_t fifo_reg2)
|
||||||
|
{
|
||||||
|
XilinxPCIEInt *intr;
|
||||||
|
unsigned int new_w;
|
||||||
|
|
||||||
|
new_w = (s->intr_fifo_w + 1) % ARRAY_SIZE(s->intr_fifo);
|
||||||
|
if (new_w == s->intr_fifo_r) {
|
||||||
|
s->rpscr |= ROOTCFG_RPSCR_INTOVF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
intr = &s->intr_fifo[s->intr_fifo_w];
|
||||||
|
s->intr_fifo_w = new_w;
|
||||||
|
|
||||||
|
intr->fifo_reg1 = fifo_reg1;
|
||||||
|
intr->fifo_reg2 = fifo_reg2;
|
||||||
|
|
||||||
|
xilinx_pcie_update_intr(s, ROOTCFG_INTMASK_INTX, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xilinx_pcie_set_irq(void *opaque, int irq_num, int level)
|
||||||
|
{
|
||||||
|
XilinxPCIEHost *s = XILINX_PCIE_HOST(opaque);
|
||||||
|
|
||||||
|
xilinx_pcie_queue_intr(s,
|
||||||
|
(irq_num << ROOTCFG_RPIFR1_INT_LANE_SHIFT) |
|
||||||
|
(level << ROOTCFG_RPIFR1_INT_ASSERT_SHIFT) |
|
||||||
|
(1 << ROOTCFG_RPIFR1_INT_VALID_SHIFT),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xilinx_pcie_host_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||||
|
XilinxPCIEHost *s = XILINX_PCIE_HOST(dev);
|
||||||
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
|
PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
|
||||||
|
|
||||||
|
snprintf(s->name, sizeof(s->name), "pcie%u", s->bus_nr);
|
||||||
|
|
||||||
|
/* PCI configuration space */
|
||||||
|
pcie_host_mmcfg_init(pex, s->cfg_size);
|
||||||
|
|
||||||
|
/* MMIO region */
|
||||||
|
memory_region_init(&s->mmio, OBJECT(s), "mmio", UINT64_MAX);
|
||||||
|
memory_region_set_enabled(&s->mmio, false);
|
||||||
|
|
||||||
|
/* dummy I/O region */
|
||||||
|
memory_region_init_ram(&s->io, OBJECT(s), "io", 16, NULL);
|
||||||
|
memory_region_set_enabled(&s->io, false);
|
||||||
|
|
||||||
|
/* interrupt out */
|
||||||
|
qdev_init_gpio_out_named(dev, &s->irq, "interrupt_out", 1);
|
||||||
|
|
||||||
|
sysbus_init_mmio(sbd, &pex->mmio);
|
||||||
|
sysbus_init_mmio(sbd, &s->mmio);
|
||||||
|
|
||||||
|
pci->bus = pci_register_bus(dev, s->name, xilinx_pcie_set_irq,
|
||||||
|
pci_swizzle_map_irq_fn, s, &s->mmio,
|
||||||
|
&s->io, 0, 4, TYPE_PCIE_BUS);
|
||||||
|
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
|
||||||
|
qdev_init_nofail(DEVICE(&s->root));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *xilinx_pcie_host_root_bus_path(PCIHostState *host_bridge,
|
||||||
|
PCIBus *rootbus)
|
||||||
|
{
|
||||||
|
return "0000:00";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xilinx_pcie_host_init(Object *obj)
|
||||||
|
{
|
||||||
|
XilinxPCIEHost *s = XILINX_PCIE_HOST(obj);
|
||||||
|
XilinxPCIERoot *root = &s->root;
|
||||||
|
|
||||||
|
object_initialize(root, sizeof(*root), TYPE_XILINX_PCIE_ROOT);
|
||||||
|
object_property_add_child(obj, "root", OBJECT(root), NULL);
|
||||||
|
qdev_prop_set_uint32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
|
||||||
|
qdev_prop_set_bit(DEVICE(root), "multifunction", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property xilinx_pcie_host_props[] = {
|
||||||
|
DEFINE_PROP_UINT32("bus_nr", XilinxPCIEHost, bus_nr, 0),
|
||||||
|
DEFINE_PROP_SIZE("cfg_base", XilinxPCIEHost, cfg_base, 0),
|
||||||
|
DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 << 20),
|
||||||
|
DEFINE_PROP_SIZE("mmio_base", XilinxPCIEHost, mmio_base, 0),
|
||||||
|
DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 << 20),
|
||||||
|
DEFINE_PROP_BOOL("link_up", XilinxPCIEHost, link_up, true),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void xilinx_pcie_host_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||||
|
|
||||||
|
hc->root_bus_path = xilinx_pcie_host_root_bus_path;
|
||||||
|
dc->realize = xilinx_pcie_host_realize;
|
||||||
|
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||||
|
dc->fw_name = "pci";
|
||||||
|
dc->props = xilinx_pcie_host_props;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo xilinx_pcie_host_info = {
|
||||||
|
.name = TYPE_XILINX_PCIE_HOST,
|
||||||
|
.parent = TYPE_PCIE_HOST_BRIDGE,
|
||||||
|
.instance_size = sizeof(XilinxPCIEHost),
|
||||||
|
.instance_init = xilinx_pcie_host_init,
|
||||||
|
.class_init = xilinx_pcie_host_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t xilinx_pcie_root_config_read(PCIDevice *d,
|
||||||
|
uint32_t address, int len)
|
||||||
|
{
|
||||||
|
XilinxPCIEHost *s = XILINX_PCIE_HOST(OBJECT(d)->parent);
|
||||||
|
uint32_t val;
|
||||||
|
|
||||||
|
switch (address) {
|
||||||
|
case ROOTCFG_INTDEC:
|
||||||
|
val = s->intr;
|
||||||
|
break;
|
||||||
|
case ROOTCFG_INTMASK:
|
||||||
|
val = s->intr_mask;
|
||||||
|
break;
|
||||||
|
case ROOTCFG_PSCR:
|
||||||
|
val = s->link_up ? ROOTCFG_PSCR_LINK_UP : 0;
|
||||||
|
break;
|
||||||
|
case ROOTCFG_RPSCR:
|
||||||
|
if (s->intr_fifo_r != s->intr_fifo_w) {
|
||||||
|
s->rpscr &= ~ROOTCFG_RPSCR_INTNEMPTY;
|
||||||
|
} else {
|
||||||
|
s->rpscr |= ROOTCFG_RPSCR_INTNEMPTY;
|
||||||
|
}
|
||||||
|
val = s->rpscr;
|
||||||
|
break;
|
||||||
|
case ROOTCFG_RPIFR1:
|
||||||
|
if (s->intr_fifo_w == s->intr_fifo_r) {
|
||||||
|
/* FIFO empty */
|
||||||
|
val = 0;
|
||||||
|
} else {
|
||||||
|
val = s->intr_fifo[s->intr_fifo_r].fifo_reg1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ROOTCFG_RPIFR2:
|
||||||
|
if (s->intr_fifo_w == s->intr_fifo_r) {
|
||||||
|
/* FIFO empty */
|
||||||
|
val = 0;
|
||||||
|
} else {
|
||||||
|
val = s->intr_fifo[s->intr_fifo_r].fifo_reg2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
val = pci_default_read_config(d, address, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xilinx_pcie_root_config_write(PCIDevice *d, uint32_t address,
|
||||||
|
uint32_t val, int len)
|
||||||
|
{
|
||||||
|
XilinxPCIEHost *s = XILINX_PCIE_HOST(OBJECT(d)->parent);
|
||||||
|
switch (address) {
|
||||||
|
case ROOTCFG_INTDEC:
|
||||||
|
xilinx_pcie_update_intr(s, 0, val);
|
||||||
|
break;
|
||||||
|
case ROOTCFG_INTMASK:
|
||||||
|
s->intr_mask = val;
|
||||||
|
xilinx_pcie_update_intr(s, 0, 0);
|
||||||
|
break;
|
||||||
|
case ROOTCFG_RPSCR:
|
||||||
|
s->rpscr &= ~ROOTCFG_RPSCR_BRIDGEEN;
|
||||||
|
s->rpscr |= val & ROOTCFG_RPSCR_BRIDGEEN;
|
||||||
|
memory_region_set_enabled(&s->mmio, val & ROOTCFG_RPSCR_BRIDGEEN);
|
||||||
|
|
||||||
|
if (val & ROOTCFG_INTMASK_INTX) {
|
||||||
|
s->rpscr &= ~ROOTCFG_INTMASK_INTX;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ROOTCFG_RPIFR1:
|
||||||
|
case ROOTCFG_RPIFR2:
|
||||||
|
if (s->intr_fifo_w == s->intr_fifo_r) {
|
||||||
|
/* FIFO empty */
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
s->intr_fifo_r = (s->intr_fifo_r + 1) % ARRAY_SIZE(s->intr_fifo);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pci_default_write_config(d, address, val, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xilinx_pcie_root_init(PCIDevice *dev)
|
||||||
|
{
|
||||||
|
BusState *bus = qdev_get_parent_bus(DEVICE(dev));
|
||||||
|
XilinxPCIEHost *s = XILINX_PCIE_HOST(bus->parent);
|
||||||
|
|
||||||
|
pci_set_word(dev->config + PCI_COMMAND,
|
||||||
|
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||||
|
pci_set_word(dev->config + PCI_MEMORY_BASE, s->mmio_base >> 16);
|
||||||
|
pci_set_word(dev->config + PCI_MEMORY_LIMIT,
|
||||||
|
((s->mmio_base + s->mmio_size - 1) >> 16) & 0xfff0);
|
||||||
|
|
||||||
|
pci_bridge_initfn(dev, TYPE_PCI_BUS);
|
||||||
|
|
||||||
|
if (pcie_endpoint_cap_v1_init(dev, 0x80) < 0) {
|
||||||
|
hw_error("Failed to initialize PCIe capability");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xilinx_pcie_root_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||||
|
dc->desc = "Xilinx AXI-PCIe Host Bridge";
|
||||||
|
k->vendor_id = PCI_VENDOR_ID_XILINX;
|
||||||
|
k->device_id = 0x7021;
|
||||||
|
k->revision = 0;
|
||||||
|
k->class_id = PCI_CLASS_BRIDGE_HOST;
|
||||||
|
k->is_express = true;
|
||||||
|
k->is_bridge = true;
|
||||||
|
k->init = xilinx_pcie_root_init;
|
||||||
|
k->exit = pci_bridge_exitfn;
|
||||||
|
dc->reset = pci_bridge_reset;
|
||||||
|
k->config_read = xilinx_pcie_root_config_read;
|
||||||
|
k->config_write = xilinx_pcie_root_config_write;
|
||||||
|
/*
|
||||||
|
* PCI-facing part of the host bridge, not usable without the
|
||||||
|
* host-facing part, which can't be device_add'ed, yet.
|
||||||
|
*/
|
||||||
|
dc->cannot_instantiate_with_device_add_yet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo xilinx_pcie_root_info = {
|
||||||
|
.name = TYPE_XILINX_PCIE_ROOT,
|
||||||
|
.parent = TYPE_PCI_BRIDGE,
|
||||||
|
.instance_size = sizeof(XilinxPCIERoot),
|
||||||
|
.class_init = xilinx_pcie_root_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void xilinx_pcie_register(void)
|
||||||
|
{
|
||||||
|
type_register_static(&xilinx_pcie_root_info);
|
||||||
|
type_register_static(&xilinx_pcie_host_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(xilinx_pcie_register)
|
|
@ -14,6 +14,11 @@
|
||||||
|
|
||||||
#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
|
#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
|
||||||
|
|
||||||
|
uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic)
|
||||||
|
{
|
||||||
|
return NANOSECONDS_PER_SECOND / TIMER_PERIOD;
|
||||||
|
}
|
||||||
|
|
||||||
static void gic_vptimer_update(MIPSGICTimerState *gictimer,
|
static void gic_vptimer_update(MIPSGICTimerState *gictimer,
|
||||||
uint32_t vp_index, uint64_t now)
|
uint32_t vp_index, uint64_t now)
|
||||||
{
|
{
|
||||||
|
|
41
include/hw/loader-fit.h
Normal file
41
include/hw/loader-fit.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Flattened Image Tree loader.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Imagination Technologies
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HW_LOADER_FIT_H
|
||||||
|
#define HW_LOADER_FIT_H
|
||||||
|
|
||||||
|
#include <exec/hwaddr.h>
|
||||||
|
|
||||||
|
struct fit_loader_match {
|
||||||
|
const char *compatible;
|
||||||
|
const void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fit_loader {
|
||||||
|
const struct fit_loader_match *matches;
|
||||||
|
hwaddr (*addr_to_phys)(void *opaque, uint64_t addr);
|
||||||
|
const void *(*fdt_filter)(void *opaque, const void *fdt,
|
||||||
|
const void *match_data, hwaddr *load_addr);
|
||||||
|
const void *(*kernel_filter)(void *opaque, const void *kernel,
|
||||||
|
hwaddr *load_addr, hwaddr *entry_addr);
|
||||||
|
};
|
||||||
|
|
||||||
|
int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque);
|
||||||
|
|
||||||
|
#endif /* HW_LOADER_FIT_H */
|
|
@ -164,6 +164,8 @@ int load_uimage(const char *filename, hwaddr *ep,
|
||||||
*/
|
*/
|
||||||
int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz);
|
int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz);
|
||||||
|
|
||||||
|
ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen);
|
||||||
|
|
||||||
ssize_t read_targphys(const char *name,
|
ssize_t read_targphys(const char *name,
|
||||||
int fd, hwaddr dst_addr, size_t nbytes);
|
int fd, hwaddr dst_addr, size_t nbytes);
|
||||||
void pstrcpy_targphys(const char *name,
|
void pstrcpy_targphys(const char *name,
|
||||||
|
@ -214,4 +216,8 @@ void hmp_info_roms(Monitor *mon, const QDict *qdict);
|
||||||
int rom_add_vga(const char *file);
|
int rom_add_vga(const char *file);
|
||||||
int rom_add_option(const char *file, int32_t bootindex);
|
int rom_add_option(const char *file, int32_t bootindex);
|
||||||
|
|
||||||
|
/* This is the usual maximum in uboot, so if a uImage overflows this, it would
|
||||||
|
* overflow on real hardware too. */
|
||||||
|
#define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -41,6 +41,9 @@
|
||||||
#define GCR_L2_CONFIG_BYPASS_SHF 20
|
#define GCR_L2_CONFIG_BYPASS_SHF 20
|
||||||
#define GCR_L2_CONFIG_BYPASS_MSK ((0x1ULL) << GCR_L2_CONFIG_BYPASS_SHF)
|
#define GCR_L2_CONFIG_BYPASS_MSK ((0x1ULL) << GCR_L2_CONFIG_BYPASS_SHF)
|
||||||
|
|
||||||
|
/* GCR_BASE register fields */
|
||||||
|
#define GCR_BASE_GCRBASE_MSK 0xffffffff8000ULL
|
||||||
|
|
||||||
/* GCR_GIC_BASE register fields */
|
/* GCR_GIC_BASE register fields */
|
||||||
#define GCR_GIC_BASE_GICEN_MSK 1
|
#define GCR_GIC_BASE_GICEN_MSK 1
|
||||||
#define GCR_GIC_BASE_GICBASE_MSK 0xFFFFFFFE0000ULL
|
#define GCR_GIC_BASE_GICBASE_MSK 0xFFFFFFFE0000ULL
|
||||||
|
|
68
include/hw/pci-host/xilinx-pcie.h
Normal file
68
include/hw/pci-host/xilinx-pcie.h
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Xilinx PCIe host controller emulation.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Imagination Technologies
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HW_XILINX_PCIE_H
|
||||||
|
#define HW_XILINX_PCIE_H
|
||||||
|
|
||||||
|
#include "hw/hw.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
|
#include "hw/pci/pci_bus.h"
|
||||||
|
#include "hw/pci/pcie_host.h"
|
||||||
|
|
||||||
|
#define TYPE_XILINX_PCIE_HOST "xilinx-pcie-host"
|
||||||
|
#define XILINX_PCIE_HOST(obj) \
|
||||||
|
OBJECT_CHECK(XilinxPCIEHost, (obj), TYPE_XILINX_PCIE_HOST)
|
||||||
|
|
||||||
|
#define TYPE_XILINX_PCIE_ROOT "xilinx-pcie-root"
|
||||||
|
#define XILINX_PCIE_ROOT(obj) \
|
||||||
|
OBJECT_CHECK(XilinxPCIERoot, (obj), TYPE_XILINX_PCIE_ROOT)
|
||||||
|
|
||||||
|
typedef struct XilinxPCIERoot {
|
||||||
|
PCIBridge parent_obj;
|
||||||
|
} XilinxPCIERoot;
|
||||||
|
|
||||||
|
typedef struct XilinxPCIEInt {
|
||||||
|
uint32_t fifo_reg1;
|
||||||
|
uint32_t fifo_reg2;
|
||||||
|
} XilinxPCIEInt;
|
||||||
|
|
||||||
|
typedef struct XilinxPCIEHost {
|
||||||
|
PCIExpressHost parent_obj;
|
||||||
|
|
||||||
|
char name[16];
|
||||||
|
|
||||||
|
uint32_t bus_nr;
|
||||||
|
uint64_t cfg_base, cfg_size;
|
||||||
|
uint64_t mmio_base, mmio_size;
|
||||||
|
bool link_up;
|
||||||
|
qemu_irq irq;
|
||||||
|
|
||||||
|
MemoryRegion mmio, io;
|
||||||
|
|
||||||
|
XilinxPCIERoot root;
|
||||||
|
|
||||||
|
uint32_t intr;
|
||||||
|
uint32_t intr_mask;
|
||||||
|
XilinxPCIEInt intr_fifo[16];
|
||||||
|
unsigned int intr_fifo_r, intr_fifo_w;
|
||||||
|
uint32_t rpscr;
|
||||||
|
} XilinxPCIEHost;
|
||||||
|
|
||||||
|
#endif /* HW_XILINX_PCIE_H */
|
|
@ -31,6 +31,7 @@ struct MIPSGICTimerState {
|
||||||
MIPSGICTimerCB *cb;
|
MIPSGICTimerCB *cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic);
|
||||||
uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gic);
|
uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gic);
|
||||||
void mips_gictimer_store_sh_count(MIPSGICTimerState *gic, uint64_t count);
|
void mips_gictimer_store_sh_count(MIPSGICTimerState *gic, uint64_t count);
|
||||||
uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
|
uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
|
||||||
|
|
|
@ -815,6 +815,7 @@ int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc);
|
||||||
|
|
||||||
#define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model))
|
#define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model))
|
||||||
bool cpu_supports_cps_smp(const char *cpu_model);
|
bool cpu_supports_cps_smp(const char *cpu_model);
|
||||||
|
bool cpu_supports_isa(const char *cpu_model, unsigned int isa);
|
||||||
void cpu_set_exception_base(int vp_index, target_ulong address);
|
void cpu_set_exception_base(int vp_index, target_ulong address);
|
||||||
|
|
||||||
/* TODO QOM'ify CPU reset and remove */
|
/* TODO QOM'ify CPU reset and remove */
|
||||||
|
|
|
@ -20233,6 +20233,16 @@ bool cpu_supports_cps_smp(const char *cpu_model)
|
||||||
return (def->CP0_Config3 & (1 << CP0C3_CMGCR)) != 0;
|
return (def->CP0_Config3 & (1 << CP0C3_CMGCR)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cpu_supports_isa(const char *cpu_model, unsigned int isa)
|
||||||
|
{
|
||||||
|
const mips_def_t *def = cpu_mips_find_by_name(cpu_model);
|
||||||
|
if (!def) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (def->insn_flags & isa) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
void cpu_set_exception_base(int vp_index, target_ulong address)
|
void cpu_set_exception_base(int vp_index, target_ulong address)
|
||||||
{
|
{
|
||||||
MIPSCPU *vp = MIPS_CPU(qemu_get_cpu(vp_index));
|
MIPSCPU *vp = MIPS_CPU(qemu_get_cpu(vp_index));
|
||||||
|
|
Loading…
Reference in a new issue