MIPS patches queue

- Use PCI macros (Philippe Mathieu-Daudé)
 - Clean up VT82C686B south bridge (BALATON Zoltan)
 - Introduce clock_ticks_to_ns() (Peter Maydell)
 - Add Loongson-3 machine (Huacai Chen)
 - Make addresses used by bootloader unsigned (Jiaxun Yang)
 - Clean fuloong2e PROM environment (Jiaxun Yang)
 - Add integration test of fuloong2e booting Linux (Jiaxun Yang)
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAl/zmLwACgkQ4+MsLN6t
 wN49yA//cxwKReLwXZ16L2wtVukOREeTCA4IHCDqCQHVbURXFD6i+KK6Xyda2fNU
 IFpHWvkbRuRPxMrS6HbWt33FLFTdMSZkywEHCGh4x1PlTtfWHYD5veTrLLm1kyqX
 l9xN8WZ7NEAfG940v+XSsEx182dyk10cbbU5jIrlPUvJixV8GJeJScRoZyqNP/kZ
 UFmhyhGx7FGeuJ7rZV8VamdY/NuOl1sHNYUGH45V93mNl5geqRbUOiqOt/pdxoFQ
 A1Fj5LU8oZFojI/BIjcq8qUeRGBeRv9lBbsu7COBArZAp/ciqgiNxT5xBVfM2Xz4
 4nJjOhcp+p8DcXiVy12vL/y8anEkSntdnaLoXU0tmyYx6v8rP/5wVfuSnkWXdHoB
 2xchCt/OIKZlG7cNQNKug01/BOwg3J+x+rnpIzdg67mPs8O5PaFRKXH7JzADtLU0
 hF+f9DyXm67OjppqhIZZOQKnn/09jXxHg81xYvoSMkrjy2eOQWfJ6/Xcl/Qc43g6
 ezP7PoZhatQ98FqtMcEGUS+PtF3t+MK1UkHywvQ1t9VnAoTE1g3Yx/aFdQPRXBWU
 Qhlj66Hqji4IWzRAotmX2BA4aAAeu4RmE6/IGqmVMNLlAp3JLmpKeHS/f12oWWOE
 M7s7p2KISOhY4edtiLk6CshmU1GNJWebCnm5tjKn3lUIRHohWJI=
 =Mazq
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/philmd-gitlab/tags/mips-20210104' into staging

MIPS patches queue

- Use PCI macros (Philippe Mathieu-Daudé)
- Clean up VT82C686B south bridge (BALATON Zoltan)
- Introduce clock_ticks_to_ns() (Peter Maydell)
- Add Loongson-3 machine (Huacai Chen)
- Make addresses used by bootloader unsigned (Jiaxun Yang)
- Clean fuloong2e PROM environment (Jiaxun Yang)
- Add integration test of fuloong2e booting Linux (Jiaxun Yang)

# gpg: Signature made Mon 04 Jan 2021 22:37:48 GMT
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* remotes/philmd-gitlab/tags/mips-20210104: (35 commits)
  tests/acceptance: Test boot_linux_console for fuloong2e
  hw/mips/fuloong2e: Correct cpuclock in PROM environment
  hw/mips/fuloong2e: Remove unused env entry
  hw/mips/fuloong2e: Replace faulty documentation links
  hw/mips/fuloong2e: Remove define DEBUG_FULOONG2E_INIT
  hw/mips: Use address translation helper to handle ENVP_ADDR
  hw/mips/malta: Use address translation helper to calculate bootloader_run_addr
  hw/mips: Make bootloader addresses unsigned
  docs/system: Update MIPS machine documentation
  hw/mips: Add Loongson-3 machine support
  hw/mips: Add Loongson-3 boot parameter helpers
  hw/mips: Implement fw_cfg_arch_key_name()
  hw/intc: Rework Loongson LIOINTC
  clock: Define and use new clock_display_freq()
  clock: Remove clock_get_ns()
  target/mips: Don't use clock_get_ns() in clock period calculation
  clock: Introduce clock_ticks_to_ns()
  vt82c686: Rename superio config related parts
  vt82c686: Use shorter name for local variable holding object state
  vt82c686: Remove unneeded includes and defines
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-01-05 21:06:42 +00:00
commit 2e0b5bbe81
37 changed files with 1537 additions and 362 deletions

View File

@ -1159,6 +1159,9 @@ M: Huacai Chen <chenhuacai@kernel.org>
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
S: Maintained
F: hw/intc/loongson_liointc.c
F: hw/mips/loongson3_bootp.c
F: hw/mips/loongson3_bootp.h
F: hw/mips/loongson3_virt.c
Boston
M: Paul Burton <paulburton@kernel.org>

View File

@ -3,6 +3,7 @@
include mips-softmmu-common.mak
CONFIG_IDE_VIA=y
CONFIG_FULOONG=y
CONFIG_LOONGSON3V=y
CONFIG_ATI_VGA=y
CONFIG_RTL8139_PCI=y
CONFIG_JAZZ=y

View File

@ -238,8 +238,17 @@ object during device instance init. For example:
Fetching clock frequency/period
-------------------------------
To get the current state of a clock, use the functions ``clock_get()``,
``clock_get_ns()`` or ``clock_get_hz()``.
To get the current state of a clock, use the functions ``clock_get()``
or ``clock_get_hz()``.
``clock_get()`` returns the period of the clock in its fully precise
internal representation, as an unsigned 64-bit integer in units of
2^-32 nanoseconds. (For many purposes ``clock_ticks_to_ns()`` will
be more convenient; see the section below on expiry deadlines.)
``clock_get_hz()`` returns the frequency of the clock, rounded to the
next lowest integer. This implies some inaccuracy due to the rounding,
so be cautious about using it in calculations.
It is also possible to register a callback on clock frequency changes.
Here is an example:
@ -254,10 +263,44 @@ Here is an example:
*/
/* do something with the new period */
fprintf(stdout, "device new period is %" PRIu64 "ns\n",
clock_get_ns(dev->my_clk_input));
fprintf(stdout, "device new period is %" PRIu64 "* 2^-32 ns\n",
clock_get(dev->my_clk_input));
}
If you are only interested in the frequency for displaying it to
humans (for instance in debugging), use ``clock_display_freq()``,
which returns a prettified string-representation, e.g. "33.3 MHz".
The caller must free the string with g_free() after use.
Calculating expiry deadlines
----------------------------
A commonly required operation for a clock is to calculate how long
it will take for the clock to tick N times; this can then be used
to set a timer expiry deadline. Use the function ``clock_ticks_to_ns()``,
which takes an unsigned 64-bit count of ticks and returns the length
of time in nanoseconds required for the clock to tick that many times.
It is important not to try to calculate expiry deadlines using a
shortcut like multiplying a "period of clock in nanoseconds" value
by the tick count, because clocks can have periods which are not a
whole number of nanoseconds, and the accumulated error in the
multiplication can be significant.
For a clock with a very long period and a large number of ticks,
the result of this function could in theory be too large to fit in
a 64-bit value. To avoid overflow in this case, ``clock_ticks_to_ns()``
saturates the result to INT64_MAX (because this is the largest valid
input to the QEMUTimer APIs). Since INT64_MAX nanoseconds is almost
300 years, anything with an expiry later than that is in the "will
never happen" category. Callers of ``clock_ticks_to_ns()`` should
therefore generally not special-case the possibility of a saturated
result but just allow the timer to be set to that far-future value.
(If you are performing further calculations on the returned value
rather than simply passing it to a QEMUTimer function like
``timer_mod_ns()`` then you should be careful to avoid overflow
in those calculations, of course.)
Changing a clock period
-----------------------

View File

@ -84,6 +84,16 @@ The Fuloong 2E emulation supports:
- RTL8139D as a network card chipset
The Loongson-3 virtual platform emulation supports:
- Loongson 3A CPU
- LIOINTC as interrupt controller
- GPEX and virtio as peripheral devices
- Both KVM and TCG supported
The mipssim pseudo board emulation provides an environment similar to
what the proprietary MIPS emulator uses for running Linux. It supports:

View File

@ -1147,7 +1147,8 @@ static void create_pcie_irq_map(const VirtMachineState *vms,
full_irq_map, sizeof(full_irq_map));
qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupt-map-mask",
0x1800, 0, 0, /* devfn (PCI_SLOT(3)) */
cpu_to_be16(PCI_DEVFN(3, 0)), /* Slot 3 */
0, 0,
0x7 /* PCI irq */);
}

View File

@ -11,4 +11,5 @@ softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-ac97.c'))
softmmu_ss.add(when: 'CONFIG_PCSPK', if_true: files('pcspk.c'))
softmmu_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c'))
softmmu_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c'))
softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c'))
softmmu_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c'))

93
hw/audio/via-ac97.c Normal file
View File

@ -0,0 +1,93 @@
/*
* VIA south bridges sound support
*
* This work is licensed under the GNU GPL license version 2 or later.
*/
/*
* TODO: This is entirely boiler plate just registering empty PCI devices
* with the right ID guests expect, functionality should be added here.
*/
#include "qemu/osdep.h"
#include "hw/isa/vt82c686.h"
#include "hw/pci/pci.h"
static void via_ac97_realize(PCIDevice *pci_dev, Error **errp)
{
pci_set_word(pci_dev->config + PCI_COMMAND,
PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY);
pci_set_word(pci_dev->config + PCI_STATUS,
PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_MEDIUM);
pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
}
static void via_ac97_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->realize = via_ac97_realize;
k->vendor_id = PCI_VENDOR_ID_VIA;
k->device_id = PCI_DEVICE_ID_VIA_AC97;
k->revision = 0x50;
k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->desc = "VIA AC97";
/* Reason: Part of a south bridge chip */
dc->user_creatable = false;
}
static const TypeInfo via_ac97_info = {
.name = TYPE_VIA_AC97,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PCIDevice),
.class_init = via_ac97_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
};
static void via_mc97_realize(PCIDevice *pci_dev, Error **errp)
{
pci_set_word(pci_dev->config + PCI_COMMAND,
PCI_COMMAND_INVALIDATE | PCI_COMMAND_VGA_PALETTE);
pci_set_word(pci_dev->config + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
}
static void via_mc97_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->realize = via_mc97_realize;
k->vendor_id = PCI_VENDOR_ID_VIA;
k->device_id = PCI_DEVICE_ID_VIA_MC97;
k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
k->revision = 0x30;
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
dc->desc = "VIA MC97";
/* Reason: Part of a south bridge chip */
dc->user_creatable = false;
}
static const TypeInfo via_mc97_info = {
.name = TYPE_VIA_MC97,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PCIDevice),
.class_init = via_mc97_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
};
static void via_ac97_register_types(void)
{
type_register_static(&via_ac97_info);
type_register_static(&via_mc97_info);
}
type_init(via_ac97_register_types)

View File

@ -12,6 +12,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "hw/clock.h"
#include "trace.h"
@ -111,6 +112,11 @@ static void clock_disconnect(Clock *clk)
QLIST_REMOVE(clk, sibling);
}
char *clock_display_freq(Clock *clk)
{
return freq_to_str(clock_get_hz(clk));
}
static void clock_initfn(Object *obj)
{
Clock *clk = CLOCK(obj);

View File

@ -496,7 +496,7 @@ static void dino_set_irq(void *opaque, int irq, int level)
static int dino_pci_map_irq(PCIDevice *d, int irq_num)
{
int slot = d->devfn >> 3;
int slot = PCI_SLOT(d->devfn);
assert(irq_num >= 0 && irq_num <= 3);

View File

@ -140,7 +140,7 @@ typedef struct XenIOState {
int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
{
return irq_num + ((pci_dev->devfn >> 3) << 2);
return irq_num + (PCI_SLOT(pci_dev->devfn) << 2);
}
void xen_piix3_set_irq(void *opaque, int irq_num, int level)

View File

@ -1,6 +1,7 @@
/*
* QEMU Loongson Local I/O interrupt controler.
*
* Copyright (c) 2020 Huacai Chen <chenhc@lemote.com>
* Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
*
* This program is free software: you can redistribute it and/or modify
@ -19,13 +20,11 @@
*/
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/module.h"
#include "qemu/log.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "qom/object.h"
#define D(x)
#include "hw/intc/loongson_liointc.h"
#define NUM_IRQS 32
@ -40,13 +39,10 @@
#define R_IEN 0x24
#define R_IEN_SET 0x28
#define R_IEN_CLR 0x2c
#define R_PERCORE_ISR(x) (0x40 + 0x8 * x)
#define R_ISR_SIZE 0x8
#define R_START 0x40
#define R_END 0x64
#define TYPE_LOONGSON_LIOINTC "loongson.liointc"
DECLARE_INSTANCE_CHECKER(struct loongson_liointc, LOONGSON_LIOINTC,
TYPE_LOONGSON_LIOINTC)
struct loongson_liointc {
SysBusDevice parent_obj;
@ -123,14 +119,13 @@ liointc_read(void *opaque, hwaddr addr, unsigned int size)
goto out;
}
/* Rest is 4 byte */
/* Rest are 4 bytes */
if (size != 4 || (addr % 4)) {
goto out;
}
if (addr >= R_PERCORE_ISR(0) &&
addr < R_PERCORE_ISR(NUM_CORES)) {
int core = (addr - R_PERCORE_ISR(0)) / 8;
if (addr >= R_START && addr < R_END) {
int core = (addr - R_START) / R_ISR_SIZE;
r = p->per_core_isr[core];
goto out;
}
@ -147,7 +142,8 @@ liointc_read(void *opaque, hwaddr addr, unsigned int size)
}
out:
D(qemu_log("%s: size=%d addr=%lx val=%x\n", __func__, size, addr, r));
qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
__func__, size, addr, r);
return r;
}
@ -158,7 +154,8 @@ liointc_write(void *opaque, hwaddr addr,
struct loongson_liointc *p = opaque;
uint32_t value = val64;
D(qemu_log("%s: size=%d, addr=%lx val=%x\n", __func__, size, addr, value));
qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
__func__, size, addr, value);
/* Mapper is 1 byte */
if (size == 1 && addr < R_MAPPER_END) {
@ -166,14 +163,13 @@ liointc_write(void *opaque, hwaddr addr,
goto out;
}
/* Rest is 4 byte */
/* Rest are 4 bytes */
if (size != 4 || (addr % 4)) {
goto out;
}
if (addr >= R_PERCORE_ISR(0) &&
addr < R_PERCORE_ISR(NUM_CORES)) {
int core = (addr - R_PERCORE_ISR(0)) / 8;
if (addr >= R_START && addr < R_END) {
int core = (addr - R_START) / R_ISR_SIZE;
p->per_core_isr[core] = value;
goto out;
}
@ -224,7 +220,7 @@ static void loongson_liointc_init(Object *obj)
}
memory_region_init_io(&p->mmio, obj, &pic_ops, p,
"loongson.liointc", R_END);
TYPE_LOONGSON_LIOINTC, R_END);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
}

View File

@ -361,7 +361,7 @@ type_init(piix3_register_types)
static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx)
{
int slot_addend;
slot_addend = (pci_dev->devfn >> 3) - 1;
slot_addend = PCI_SLOT(pci_dev->devfn) - 1;
return (pci_intx + slot_addend) & 3;
}

View File

@ -13,3 +13,9 @@ pc87312_io_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x"
# apm.c
apm_io_read(uint8_t addr, uint8_t val) "read addr=0x%x val=0x%02x"
apm_io_write(uint8_t addr, uint8_t val) "write addr=0x%x val=0x%02x"
# vt82c686.c
via_isa_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
via_pm_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
via_superio_read(uint8_t addr, uint8_t val) "addr 0x%x val 0x%x"
via_superio_write(uint8_t addr, uint32_t val) "addr 0x%x val 0x%x"

View File

@ -12,14 +12,11 @@
#include "qemu/osdep.h"
#include "hw/isa/vt82c686.h"
#include "hw/i2c/i2c.h"
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h"
#include "hw/isa/isa.h"
#include "hw/isa/superio.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "hw/mips/mips.h"
#include "hw/isa/apm.h"
#include "hw/acpi/acpi.h"
#include "hw/i2c/pm_smbus.h"
@ -27,43 +24,34 @@
#include "qemu/module.h"
#include "qemu/timer.h"
#include "exec/address-spaces.h"
#include "qom/object.h"
/* #define DEBUG_VT82C686B */
#ifdef DEBUG_VT82C686B
#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __func__, ##__VA_ARGS__)
#else
#define DPRINTF(fmt, ...)
#endif
#include "trace.h"
typedef struct SuperIOConfig {
uint8_t config[0x100];
uint8_t regs[0x100];
uint8_t index;
uint8_t data;
} SuperIOConfig;
struct VT82C686BState {
struct VT82C686BISAState {
PCIDevice dev;
MemoryRegion superio;
SuperIOConfig superio_conf;
SuperIOConfig superio_cfg;
};
#define TYPE_VT82C686B_DEVICE "VT82C686B"
OBJECT_DECLARE_SIMPLE_TYPE(VT82C686BState, VT82C686B_DEVICE)
OBJECT_DECLARE_SIMPLE_TYPE(VT82C686BISAState, VT82C686B_ISA)
static void superio_ioport_writeb(void *opaque, hwaddr addr, uint64_t data,
unsigned size)
static void superio_cfg_write(void *opaque, hwaddr addr, uint64_t data,
unsigned size)
{
SuperIOConfig *superio_conf = opaque;
SuperIOConfig *sc = opaque;
DPRINTF("superio_ioport_writeb address 0x%x val 0x%x\n", addr, data);
if (addr == 0x3f0) {
superio_conf->index = data & 0xff;
if (addr == 0x3f0) { /* config index register */
sc->index = data & 0xff;
} else {
bool can_write = true;
/* 0x3f1 */
switch (superio_conf->index) {
/* 0x3f1, config data register */
trace_via_superio_write(sc->index, data & 0xff);
switch (sc->index) {
case 0x00 ... 0xdf:
case 0xe4:
case 0xe5:
@ -75,39 +63,29 @@ static void superio_ioport_writeb(void *opaque, hwaddr addr, uint64_t data,
case 0xfd ... 0xff:
can_write = false;
break;
case 0xe7:
if ((data & 0xff) != 0xfe) {
DPRINTF("change uart 1 base. unsupported yet\n");
can_write = false;
}
break;
case 0xe8:
if ((data & 0xff) != 0xbe) {
DPRINTF("change uart 2 base. unsupported yet\n");
can_write = false;
}
break;
/* case 0xe6 ... 0xe8: Should set base port of parallel and serial */
default:
break;
}
if (can_write) {
superio_conf->config[superio_conf->index] = data & 0xff;
sc->regs[sc->index] = data & 0xff;
}
}
}
static uint64_t superio_ioport_readb(void *opaque, hwaddr addr, unsigned size)
static uint64_t superio_cfg_read(void *opaque, hwaddr addr, unsigned size)
{
SuperIOConfig *superio_conf = opaque;
SuperIOConfig *sc = opaque;
uint8_t val = sc->regs[sc->index];
DPRINTF("superio_ioport_readb address 0x%x\n", addr);
return superio_conf->config[superio_conf->index];
trace_via_superio_read(sc->index, val);
return val;
}
static const MemoryRegionOps superio_ops = {
.read = superio_ioport_readb,
.write = superio_ioport_writeb,
static const MemoryRegionOps superio_cfg_ops = {
.read = superio_cfg_read,
.write = superio_cfg_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
.min_access_size = 1,
@ -117,8 +95,8 @@ static const MemoryRegionOps superio_ops = {
static void vt82c686b_isa_reset(DeviceState *dev)
{
VT82C686BState *vt82c = VT82C686B_DEVICE(dev);
uint8_t *pci_conf = vt82c->dev.config;
VT82C686BISAState *s = VT82C686B_ISA(dev);
uint8_t *pci_conf = s->dev.config;
pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
@ -134,31 +112,27 @@ static void vt82c686b_isa_reset(DeviceState *dev)
pci_conf[0x5f] = 0x04;
pci_conf[0x77] = 0x10; /* GPIO Control 1/2/3/4 */
vt82c->superio_conf.config[0xe0] = 0x3c;
vt82c->superio_conf.config[0xe2] = 0x03;
vt82c->superio_conf.config[0xe3] = 0xfc;
vt82c->superio_conf.config[0xe6] = 0xde;
vt82c->superio_conf.config[0xe7] = 0xfe;
vt82c->superio_conf.config[0xe8] = 0xbe;
s->superio_cfg.regs[0xe0] = 0x3c; /* Device ID */
s->superio_cfg.regs[0xe2] = 0x03; /* Function select */
s->superio_cfg.regs[0xe3] = 0xfc; /* Floppy ctrl base addr */
s->superio_cfg.regs[0xe6] = 0xde; /* Parallel port base addr */
s->superio_cfg.regs[0xe7] = 0xfe; /* Serial port 1 base addr */
s->superio_cfg.regs[0xe8] = 0xbe; /* Serial port 2 base addr */
}
/* write config pci function0 registers. PCI-ISA bridge */
static void vt82c686b_write_config(PCIDevice *d, uint32_t address,
static void vt82c686b_write_config(PCIDevice *d, uint32_t addr,
uint32_t val, int len)
{
VT82C686BState *vt686 = VT82C686B_DEVICE(d);
VT82C686BISAState *s = VT82C686B_ISA(d);
DPRINTF("vt82c686b_write_config address 0x%x val 0x%x len 0x%x\n",
address, val, len);
pci_default_write_config(d, address, val, len);
if (address == 0x85) { /* enable or disable super IO configure */
memory_region_set_enabled(&vt686->superio, val & 0x2);
trace_via_isa_write(addr, val, len);
pci_default_write_config(d, addr, val, len);
if (addr == 0x85) { /* enable or disable super IO configure */
memory_region_set_enabled(&s->superio, val & 0x2);
}
}
#define ACPI_DBG_IO_ADDR 0xb044
struct VT686PMState {
PCIDevice dev;
MemoryRegion io;
@ -168,22 +142,7 @@ struct VT686PMState {
uint32_t smb_io_base;
};
struct VT686AC97State {
PCIDevice dev;
};
struct VT686MC97State {
PCIDevice dev;
};
#define TYPE_VT82C686B_PM_DEVICE "VT82C686B_PM"
OBJECT_DECLARE_SIMPLE_TYPE(VT686PMState, VT82C686B_PM_DEVICE)
#define TYPE_VT82C686B_MC97_DEVICE "VT82C686B_MC97"
OBJECT_DECLARE_SIMPLE_TYPE(VT686MC97State, VT82C686B_MC97_DEVICE)
#define TYPE_VT82C686B_AC97_DEVICE "VT82C686B_AC97"
OBJECT_DECLARE_SIMPLE_TYPE(VT686AC97State, VT82C686B_AC97_DEVICE)
OBJECT_DECLARE_SIMPLE_TYPE(VT686PMState, VT82C686B_PM)
static void pm_update_sci(VT686PMState *s)
{
@ -220,12 +179,10 @@ static void pm_io_space_update(VT686PMState *s)
memory_region_transaction_commit();
}
static void pm_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len)
static void pm_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len)
{
DPRINTF("pm_write_config address 0x%x val 0x%x len 0x%x\n",
address, val, len);
pci_default_write_config(d, address, val, len);
trace_via_pm_write(addr, val, len);
pci_default_write_config(d, addr, val, len);
}
static int vmstate_acpi_post_load(void *opaque, int version_id)
@ -253,104 +210,10 @@ static const VMStateDescription vmstate_acpi = {
}
};
/*
* TODO: vt82c686b_ac97_init() and vt82c686b_mc97_init()
* just register a PCI device now, functionalities will be implemented later.
*/
static void vt82c686b_ac97_realize(PCIDevice *dev, Error **errp)
{
VT686AC97State *s = VT82C686B_AC97_DEVICE(dev);
uint8_t *pci_conf = s->dev.config;
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
PCI_COMMAND_PARITY);
pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST |
PCI_STATUS_DEVSEL_MEDIUM);
pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03);
}
void vt82c686b_ac97_init(PCIBus *bus, int devfn)
{
PCIDevice *dev;
dev = pci_new(devfn, TYPE_VT82C686B_AC97_DEVICE);
pci_realize_and_unref(dev, bus, &error_fatal);
}
static void via_ac97_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->realize = vt82c686b_ac97_realize;
k->vendor_id = PCI_VENDOR_ID_VIA;
k->device_id = PCI_DEVICE_ID_VIA_AC97;
k->revision = 0x50;
k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->desc = "AC97";
}
static const TypeInfo via_ac97_info = {
.name = TYPE_VT82C686B_AC97_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT686AC97State),
.class_init = via_ac97_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
};
static void vt82c686b_mc97_realize(PCIDevice *dev, Error **errp)
{
VT686MC97State *s = VT82C686B_MC97_DEVICE(dev);
uint8_t *pci_conf = s->dev.config;
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
PCI_COMMAND_VGA_PALETTE);
pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03);
}
void vt82c686b_mc97_init(PCIBus *bus, int devfn)
{
PCIDevice *dev;
dev = pci_new(devfn, TYPE_VT82C686B_MC97_DEVICE);
pci_realize_and_unref(dev, bus, &error_fatal);
}
static void via_mc97_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->realize = vt82c686b_mc97_realize;
k->vendor_id = PCI_VENDOR_ID_VIA;
k->device_id = PCI_DEVICE_ID_VIA_MC97;
k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
k->revision = 0x30;
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
dc->desc = "MC97";
}
static const TypeInfo via_mc97_info = {
.name = TYPE_VT82C686B_MC97_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT686MC97State),
.class_init = via_mc97_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
};
/* vt82c686 pm init */
static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp)
{
VT686PMState *s = VT82C686B_PM_DEVICE(dev);
VT686PMState *s = VT82C686B_PM(dev);
uint8_t *pci_conf;
pci_conf = s->dev.config;
@ -380,22 +243,6 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp)
acpi_pm1_cnt_init(&s->ar, &s->io, false, false, 2);
}
I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
qemu_irq sci_irq)
{
PCIDevice *dev;
VT686PMState *s;
dev = pci_new(devfn, TYPE_VT82C686B_PM_DEVICE);
qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base);
s = VT82C686B_PM_DEVICE(dev);
pci_realize_and_unref(dev, bus, &error_fatal);
return s->smb.smbus;
}
static Property via_pm_properties[] = {
DEFINE_PROP_UINT32("smb_io_base", VT686PMState, smb_io_base, 0),
DEFINE_PROP_END_OF_LIST(),
@ -419,7 +266,7 @@ static void via_pm_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo via_pm_info = {
.name = TYPE_VT82C686B_PM_DEVICE,
.name = TYPE_VT82C686B_PM,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT686PMState),
.class_init = via_pm_class_init,
@ -434,7 +281,7 @@ static const VMStateDescription vmstate_via = {
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, VT82C686BState),
VMSTATE_PCI_DEVICE(dev, VT82C686BISAState),
VMSTATE_END_OF_LIST()
}
};
@ -442,7 +289,7 @@ static const VMStateDescription vmstate_via = {
/* init the PCI-to-ISA bridge */
static void vt82c686b_realize(PCIDevice *d, Error **errp)
{
VT82C686BState *vt82c = VT82C686B_DEVICE(d);
VT82C686BISAState *s = VT82C686B_ISA(d);
uint8_t *pci_conf;
ISABus *isa_bus;
uint8_t *wmask;
@ -464,25 +311,15 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp)
}
}
memory_region_init_io(&vt82c->superio, OBJECT(d), &superio_ops,
&vt82c->superio_conf, "superio", 2);
memory_region_set_enabled(&vt82c->superio, false);
memory_region_init_io(&s->superio, OBJECT(d), &superio_cfg_ops,
&s->superio_cfg, "superio", 2);
memory_region_set_enabled(&s->superio, false);
/*
* The floppy also uses 0x3f0 and 0x3f1.
* But we do not emulate a floppy, so just set it here.
*/
memory_region_add_subregion(isa_bus->address_space_io, 0x3f0,
&vt82c->superio);
}
ISABus *vt82c686b_isa_init(PCIBus *bus, int devfn)
{
PCIDevice *d;
d = pci_create_simple_multifunction(bus, devfn, true,
TYPE_VT82C686B_DEVICE);
return ISA_BUS(qdev_get_child_bus(DEVICE(d), "isa.0"));
&s->superio);
}
static void via_class_init(ObjectClass *klass, void *data)
@ -507,9 +344,9 @@ static void via_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo via_info = {
.name = TYPE_VT82C686B_DEVICE,
.name = TYPE_VT82C686B_ISA,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT82C686BState),
.instance_size = sizeof(VT82C686BISAState),
.class_init = via_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
@ -537,8 +374,6 @@ static const TypeInfo via_superio_info = {
static void vt82c686b_register_types(void)
{
type_register_static(&via_ac97_info);
type_register_static(&via_mc97_info);
type_register_static(&via_pm_info);
type_register_static(&via_superio_info);
type_register_static(&via_info);

View File

@ -32,9 +32,24 @@ config FULOONG
bool
select PCI_BONITO
config LOONGSON3V
bool
imply VIRTIO_VGA
imply QXL if SPICE
select SERIAL
select GOLDFISH_RTC
select LOONGSON_LIOINTC
select PCI_DEVICES
select PCI_EXPRESS_GENERIC_BRIDGE
select MSI_NONBROKEN
select FW_CFG_MIPS
config MIPS_CPS
bool
select PTIMER
config MIPS_BOSTON
bool
config FW_CFG_MIPS
bool

View File

@ -14,8 +14,8 @@
* Fuloong 2e mini pc is based on ICT/ST Loongson 2e CPU (MIPS III like, 800MHz)
* https://www.linux-mips.org/wiki/Fuloong_2E
*
* Loongson 2e user manual:
* http://www.loongsondeveloper.com/doc/Loongson2EUserGuide.pdf
* Loongson 2e manuals:
* https://github.com/loongson-community/docs/tree/master/2E
*/
#include "qemu/osdep.h"
@ -47,9 +47,8 @@
#include "sysemu/reset.h"
#include "qemu/error-report.h"
#define DEBUG_FULOONG2E_INIT
#define ENVP_ADDR 0x80002000l
#define ENVP_PADDR 0x2000
#define ENVP_VADDR cpu_mips_phys_to_kseg0(NULL, ENVP_PADDR)
#define ENVP_NB_ENTRIES 16
#define ENVP_ENTRY_SIZE 256
@ -61,14 +60,7 @@
* PMON is not part of qemu and released with BSD license, anyone
* who want to build a pmon binary please first git-clone the source
* from the git repository at:
* http://www.loongson.cn/support/git/pmon
* Then follow the "Compile Guide" available at:
* http://dev.lemote.com/code/pmon
*
* Notes:
* 1, don't use the source at http://dev.lemote.com/http_git/pmon.git
* 2, use "Bonito2edev" to replace "dir_corresponding_to_your_target_hardware"
* in the "Compile Guide".
* https://github.com/loongson-community/pmon
*/
#define FULOONG_BIOSNAME "pmon_2e.bin"
@ -100,16 +92,16 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index,
}
table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
prom_buf[index] = tswap32(ENVP_ADDR + table_addr);
prom_buf[index] = tswap32(ENVP_VADDR + table_addr);
va_start(ap, string);
vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
va_end(ap);
}
static int64_t load_kernel(CPUMIPSState *env)
static uint64_t load_kernel(MIPSCPU *cpu)
{
int64_t kernel_entry, kernel_high, initrd_size;
uint64_t kernel_entry, kernel_high, initrd_size;
int index = 0;
long kernel_size;
ram_addr_t initrd_offset;
@ -118,8 +110,8 @@ static int64_t load_kernel(CPUMIPSState *env)
kernel_size = load_elf(loaderparams.kernel_filename, NULL,
cpu_mips_kseg0_to_phys, NULL,
(uint64_t *)&kernel_entry, NULL,
(uint64_t *)&kernel_high, NULL,
&kernel_entry, NULL,
&kernel_high, NULL,
0, EM_MIPS, 1, 0);
if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",
@ -167,20 +159,18 @@ static int64_t load_kernel(CPUMIPSState *env)
/* Setup minimum environment variables */
prom_set(prom_buf, index++, "busclock=33000000");
prom_set(prom_buf, index++, "cpuclock=100000000");
prom_set(prom_buf, index++, "cpuclock=%u", clock_get_hz(cpu->clock));
prom_set(prom_buf, index++, "memsize=%"PRIi64, loaderparams.ram_size / MiB);
prom_set(prom_buf, index++, "modetty0=38400n8r");
prom_set(prom_buf, index++, NULL);
rom_add_blob_fixed("prom", prom_buf, prom_size,
cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
rom_add_blob_fixed("prom", prom_buf, prom_size, ENVP_PADDR);
g_free(prom_buf);
return kernel_entry;
}
static void write_bootloader(CPUMIPSState *env, uint8_t *base,
int64_t kernel_addr)
uint64_t kernel_addr)
{
uint32_t *p;
@ -199,14 +189,14 @@ static void write_bootloader(CPUMIPSState *env, uint8_t *base,
stl_p(p++, 0x3c040000);
/* ori a0, a0, 2 */
stl_p(p++, 0x34840002);
/* lui a1, high(ENVP_ADDR) */
stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff));
/* ori a1, a0, low(ENVP_ADDR) */
stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff));
/* lui a2, high(ENVP_ADDR + 8) */
stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff));
/* ori a2, a2, low(ENVP_ADDR + 8) */
stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff));
/* lui a1, high(ENVP_VADDR) */
stl_p(p++, 0x3c050000 | ((ENVP_VADDR >> 16) & 0xffff));
/* ori a1, a0, low(ENVP_VADDR) */
stl_p(p++, 0x34a50000 | (ENVP_VADDR & 0xffff));
/* lui a2, high(ENVP_VADDR + 8) */
stl_p(p++, 0x3c060000 | (((ENVP_VADDR + 8) >> 16) & 0xffff));
/* ori a2, a2, low(ENVP_VADDR + 8) */
stl_p(p++, 0x34c60000 | ((ENVP_VADDR + 8) & 0xffff));
/* lui a3, high(env->ram_size) */
stl_p(p++, 0x3c070000 | (loaderparams.ram_size >> 16));
/* ori a3, a3, low(env->ram_size) */
@ -240,7 +230,9 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc,
ISABus *isa_bus;
PCIDevice *dev;
isa_bus = vt82c686b_isa_init(pci_bus, PCI_DEVFN(slot, 0));
dev = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(slot, 0), true,
TYPE_VT82C686B_ISA);
isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(dev), "isa.0"));
assert(isa_bus);
*p_isa_bus = isa_bus;
/* Interrupt controller */
@ -259,11 +251,14 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc,
pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci");
pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci");
*i2c_bus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(slot, 4), 0xeee1, NULL);
dev = pci_new(PCI_DEVFN(slot, 4), TYPE_VT82C686B_PM);
qdev_prop_set_uint32(DEVICE(dev), "smb_io_base", 0xeee1);
pci_realize_and_unref(dev, pci_bus, &error_fatal);
*i2c_bus = I2C_BUS(qdev_get_child_bus(DEVICE(dev), "i2c"));
/* Audio support */
vt82c686b_ac97_init(pci_bus, PCI_DEVFN(slot, 5));
vt82c686b_mc97_init(pci_bus, PCI_DEVFN(slot, 6));
pci_create_simple(pci_bus, PCI_DEVFN(slot, 5), TYPE_VIA_AC97);
pci_create_simple(pci_bus, PCI_DEVFN(slot, 6), TYPE_VIA_MC97);
}
/* Network support */
@ -294,7 +289,7 @@ static void mips_fuloong2e_init(MachineState *machine)
MemoryRegion *bios = g_new(MemoryRegion, 1);
long bios_size;
uint8_t *spd_data;
int64_t kernel_entry;
uint64_t kernel_entry;
PCIDevice *pci_dev;
PCIBus *pci_bus;
ISABus *isa_bus;
@ -335,7 +330,7 @@ static void mips_fuloong2e_init(MachineState *machine)
loaderparams.kernel_filename = kernel_filename;
loaderparams.kernel_cmdline = kernel_cmdline;
loaderparams.initrd_filename = initrd_filename;
kernel_entry = load_kernel(env);
kernel_entry = load_kernel(cpu);
write_bootloader(env, memory_region_get_ram_ptr(bios), kernel_entry);
} else {
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,

35
hw/mips/fw_cfg.c Normal file
View File

@ -0,0 +1,35 @@
/*
* QEMU fw_cfg helpers (MIPS specific)
*
* Copyright (c) 2020 Lemote, Inc.
*
* Author:
* Huacai Chen (chenhc@lemote.com)
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/mips/fw_cfg.h"
#include "hw/nvram/fw_cfg.h"
const char *fw_cfg_arch_key_name(uint16_t key)
{
static const struct {
uint16_t key;
const char *name;
} fw_cfg_arch_wellknown_keys[] = {
{FW_CFG_MACHINE_VERSION, "machine_version"},
{FW_CFG_CPU_FREQ, "cpu_frequency"},
};
for (size_t i = 0; i < ARRAY_SIZE(fw_cfg_arch_wellknown_keys); i++) {
if (fw_cfg_arch_wellknown_keys[i].key == key) {
return fw_cfg_arch_wellknown_keys[i].name;
}
}
return NULL;
}

19
hw/mips/fw_cfg.h Normal file
View File

@ -0,0 +1,19 @@
/*
* QEMU fw_cfg helpers (MIPS specific)
*
* Copyright (c) 2020 Huacai Chen
*
* SPDX-License-Identifier: MIT
*/
#ifndef HW_MIPS_FW_CFG_H
#define HW_MIPS_FW_CFG_H
#include "hw/boards.h"
#include "hw/nvram/fw_cfg.h"
/* Data for BIOS to identify machine */
#define FW_CFG_MACHINE_VERSION (FW_CFG_ARCH_LOCAL + 0)
#define FW_CFG_CPU_FREQ (FW_CFG_ARCH_LOCAL + 1)
#endif

View File

@ -982,7 +982,7 @@ static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num)
{
int slot;
slot = (pci_dev->devfn >> 3);
slot = PCI_SLOT(pci_dev->devfn);
switch (slot) {
/* PIIX4 USB */

151
hw/mips/loongson3_bootp.c Normal file
View File

@ -0,0 +1,151 @@
/*
* LEFI (a UEFI-like interface for BIOS-Kernel boot parameters) helpers
*
* Copyright (c) 2018-2020 Huacai Chen (chenhc@lemote.com)
* Copyright (c) 2018-2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/cutils.h"
#include "cpu.h"
#include "hw/boards.h"
#include "hw/mips/loongson3_bootp.h"
#define LOONGSON3_CORE_PER_NODE 4
static void init_cpu_info(void *g_cpuinfo, uint64_t cpu_freq)
{
struct efi_cpuinfo_loongson *c = g_cpuinfo;
c->cputype = cpu_to_le32(Loongson_3A);
c->processor_id = cpu_to_le32(MIPS_CPU(first_cpu)->env.CP0_PRid);
if (cpu_freq > UINT_MAX) {
c->cpu_clock_freq = cpu_to_le32(UINT_MAX);
} else {
c->cpu_clock_freq = cpu_to_le32(cpu_freq);
}
c->cpu_startup_core_id = cpu_to_le16(0);
c->nr_cpus = cpu_to_le32(current_machine->smp.cpus);
c->total_node = cpu_to_le32(DIV_ROUND_UP(current_machine->smp.cpus,
LOONGSON3_CORE_PER_NODE));
}
static void init_memory_map(void *g_map, uint64_t ram_size)
{
struct efi_memory_map_loongson *emap = g_map;
emap->nr_map = cpu_to_le32(2);
emap->mem_freq = cpu_to_le32(300000000);
emap->map[0].node_id = cpu_to_le32(0);
emap->map[0].mem_type = cpu_to_le32(1);
emap->map[0].mem_start = cpu_to_le64(0x0);
emap->map[0].mem_size = cpu_to_le32(240);
emap->map[1].node_id = cpu_to_le32(0);
emap->map[1].mem_type = cpu_to_le32(2);
emap->map[1].mem_start = cpu_to_le64(0x90000000);
emap->map[1].mem_size = cpu_to_le32((ram_size / MiB) - 256);
}
static void init_system_loongson(void *g_system)
{
struct system_loongson *s = g_system;
s->ccnuma_smp = cpu_to_le32(0);
s->sing_double_channel = cpu_to_le32(1);
s->nr_uarts = cpu_to_le32(1);
s->uarts[0].iotype = cpu_to_le32(2);
s->uarts[0].int_offset = cpu_to_le32(2);
s->uarts[0].uartclk = cpu_to_le32(25000000); /* Random value */
s->uarts[0].uart_base = cpu_to_le64(virt_memmap[VIRT_UART].base);
}
static void init_irq_source(void *g_irq_source)
{
struct irq_source_routing_table *irq_info = g_irq_source;
irq_info->node_id = cpu_to_le32(0);
irq_info->PIC_type = cpu_to_le32(0);
irq_info->dma_mask_bits = cpu_to_le16(64);
irq_info->pci_mem_start_addr = cpu_to_le64(virt_memmap[VIRT_PCIE_MMIO].base);
irq_info->pci_mem_end_addr = cpu_to_le64(virt_memmap[VIRT_PCIE_MMIO].base +
virt_memmap[VIRT_PCIE_MMIO].size - 1);
irq_info->pci_io_start_addr = cpu_to_le64(virt_memmap[VIRT_PCIE_PIO].base);
}
static void init_interface_info(void *g_interface)
{
struct interface_info *interface = g_interface;
interface->vers = cpu_to_le16(0x01);
strpadcpy(interface->description, 64, "UEFI_Version_v1.0", '\0');
}
static void board_devices_info(void *g_board)
{
struct board_devices *bd = g_board;
strpadcpy(bd->name, 64, "Loongson-3A-VIRT-1w-V1.00-demo", '\0');
}
static void init_special_info(void *g_special)
{
struct loongson_special_attribute *special = g_special;
strpadcpy(special->special_name, 64, "2018-05-01", '\0');
}
void init_loongson_params(struct loongson_params *lp, void *p,
uint64_t cpu_freq, uint64_t ram_size)
{
init_cpu_info(p, cpu_freq);
lp->cpu_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
p += ROUND_UP(sizeof(struct efi_cpuinfo_loongson), 64);
init_memory_map(p, ram_size);
lp->memory_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
p += ROUND_UP(sizeof(struct efi_memory_map_loongson), 64);
init_system_loongson(p);
lp->system_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
p += ROUND_UP(sizeof(struct system_loongson), 64);
init_irq_source(p);
lp->irq_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
p += ROUND_UP(sizeof(struct irq_source_routing_table), 64);
init_interface_info(p);
lp->interface_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
p += ROUND_UP(sizeof(struct interface_info), 64);
board_devices_info(p);
lp->boarddev_table_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
p += ROUND_UP(sizeof(struct board_devices), 64);
init_special_info(p);
lp->special_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
p += ROUND_UP(sizeof(struct loongson_special_attribute), 64);
}
void init_reset_system(struct efi_reset_system_t *reset)
{
reset->Shutdown = cpu_to_le64(0xffffffffbfc000a8);
reset->ResetCold = cpu_to_le64(0xffffffffbfc00080);
reset->ResetWarm = cpu_to_le64(0xffffffffbfc00080);
}

241
hw/mips/loongson3_bootp.h Normal file
View File

@ -0,0 +1,241 @@
/*
* LEFI (a UEFI-like interface for BIOS-Kernel boot parameters) data structures
* defined at arch/mips/include/asm/mach-loongson64/boot_param.h in Linux kernel
*
* Copyright (c) 2017-2020 Huacai Chen (chenhc@lemote.com)
* Copyright (c) 2017-2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef HW_MIPS_LOONGSON3_BOOTP_H
#define HW_MIPS_LOONGSON3_BOOTP_H
struct efi_memory_map_loongson {
uint16_t vers; /* version of efi_memory_map */
uint32_t nr_map; /* number of memory_maps */
uint32_t mem_freq; /* memory frequence */
struct mem_map {
uint32_t node_id; /* node_id which memory attached to */
uint32_t mem_type; /* system memory, pci memory, pci io, etc. */
uint64_t mem_start; /* memory map start address */
uint32_t mem_size; /* each memory_map size, not the total size */
} map[128];
} QEMU_PACKED;
enum loongson_cpu_type {
Legacy_2E = 0x0,
Legacy_2F = 0x1,
Legacy_3A = 0x2,
Legacy_3B = 0x3,
Legacy_1A = 0x4,
Legacy_1B = 0x5,
Legacy_2G = 0x6,
Legacy_2H = 0x7,
Loongson_1A = 0x100,
Loongson_1B = 0x101,
Loongson_2E = 0x200,
Loongson_2F = 0x201,
Loongson_2G = 0x202,
Loongson_2H = 0x203,
Loongson_3A = 0x300,
Loongson_3B = 0x301
};
/*
* Capability and feature descriptor structure for MIPS CPU
*/
struct efi_cpuinfo_loongson {
uint16_t vers; /* version of efi_cpuinfo_loongson */
uint32_t processor_id; /* PRID, e.g. 6305, 6306 */
uint32_t cputype; /* Loongson_3A/3B, etc. */
uint32_t total_node; /* num of total numa nodes */
uint16_t cpu_startup_core_id; /* Boot core id */
uint16_t reserved_cores_mask;
uint32_t cpu_clock_freq; /* cpu_clock */
uint32_t nr_cpus;
char cpuname[64];
} QEMU_PACKED;
#define MAX_UARTS 64
struct uart_device {
uint32_t iotype;
uint32_t uartclk;
uint32_t int_offset;
uint64_t uart_base;
} QEMU_PACKED;
#define MAX_SENSORS 64
#define SENSOR_TEMPER 0x00000001
#define SENSOR_VOLTAGE 0x00000002
#define SENSOR_FAN 0x00000004
struct sensor_device {
char name[32]; /* a formal name */
char label[64]; /* a flexible description */
uint32_t type; /* SENSOR_* */
uint32_t id; /* instance id of a sensor-class */
uint32_t fan_policy; /* step speed or constant speed */
uint32_t fan_percent;/* only for constant speed policy */
uint64_t base_addr; /* base address of device registers */
} QEMU_PACKED;
struct system_loongson {
uint16_t vers; /* version of system_loongson */
uint32_t ccnuma_smp; /* 0: no numa; 1: has numa */
uint32_t sing_double_channel;/* 1: single; 2: double */
uint32_t nr_uarts;
struct uart_device uarts[MAX_UARTS];
uint32_t nr_sensors;
struct sensor_device sensors[MAX_SENSORS];
char has_ec;
char ec_name[32];
uint64_t ec_base_addr;
char has_tcm;
char tcm_name[32];
uint64_t tcm_base_addr;
uint64_t workarounds;
uint64_t of_dtb_addr; /* NULL if not support */
} QEMU_PACKED;
struct irq_source_routing_table {
uint16_t vers;
uint16_t size;
uint16_t rtr_bus;
uint16_t rtr_devfn;
uint32_t vendor;
uint32_t device;
uint32_t PIC_type; /* conform use HT or PCI to route to CPU-PIC */
uint64_t ht_int_bit; /* 3A: 1<<24; 3B: 1<<16 */
uint64_t ht_enable; /* irqs used in this PIC */
uint32_t node_id; /* node id: 0x0-0; 0x1-1; 0x10-2; 0x11-3 */
uint64_t pci_mem_start_addr;
uint64_t pci_mem_end_addr;
uint64_t pci_io_start_addr;
uint64_t pci_io_end_addr;
uint64_t pci_config_addr;
uint16_t dma_mask_bits;
uint16_t dma_noncoherent;
} QEMU_PACKED;
struct interface_info {
uint16_t vers; /* version of the specificition */
uint16_t size;
uint8_t flag;
char description[64];
} QEMU_PACKED;
#define MAX_RESOURCE_NUMBER 128
struct resource_loongson {
uint64_t start; /* resource start address */
uint64_t end; /* resource end address */
char name[64];
uint32_t flags;
};
struct archdev_data {}; /* arch specific additions */
struct board_devices {
char name[64]; /* hold the device name */
uint32_t num_resources; /* number of device_resource */
/* for each device's resource */
struct resource_loongson resource[MAX_RESOURCE_NUMBER];
/* arch specific additions */
struct archdev_data archdata;
};
struct loongson_special_attribute {
uint16_t vers; /* version of this special */
char special_name[64]; /* special_atribute_name */
uint32_t loongson_special_type; /* type of special device */
/* for each device's resource */
struct resource_loongson resource[MAX_RESOURCE_NUMBER];
};
struct loongson_params {
uint64_t memory_offset; /* efi_memory_map_loongson struct offset */
uint64_t cpu_offset; /* efi_cpuinfo_loongson struct offset */
uint64_t system_offset; /* system_loongson struct offset */
uint64_t irq_offset; /* irq_source_routing_table struct offset */
uint64_t interface_offset; /* interface_info struct offset */
uint64_t special_offset; /* loongson_special_attribute struct offset */
uint64_t boarddev_table_offset; /* board_devices offset */
};
struct smbios_tables {
uint16_t vers; /* version of smbios */
uint64_t vga_bios; /* vga_bios address */
struct loongson_params lp;
};
struct efi_reset_system_t {
uint64_t ResetCold;
uint64_t ResetWarm;
uint64_t ResetType;
uint64_t Shutdown;
uint64_t DoSuspend; /* NULL if not support */
};
struct efi_loongson {
uint64_t mps; /* MPS table */
uint64_t acpi; /* ACPI table (IA64 ext 0.71) */
uint64_t acpi20; /* ACPI table (ACPI 2.0) */
struct smbios_tables smbios; /* SM BIOS table */
uint64_t sal_systab; /* SAL system table */
uint64_t boot_info; /* boot info table */
};
struct boot_params {
struct efi_loongson efi;
struct efi_reset_system_t reset_system;
};
/* Overall MMIO & Memory layout */
enum {
VIRT_LOWMEM,
VIRT_PM,
VIRT_FW_CFG,
VIRT_RTC,
VIRT_PCIE_PIO,
VIRT_PCIE_ECAM,
VIRT_BIOS_ROM,
VIRT_UART,
VIRT_LIOINTC,
VIRT_PCIE_MMIO,
VIRT_HIGHMEM
};
/* Low MEM layout for QEMU kernel loader */
enum {
LOADER_KERNEL,
LOADER_INITRD,
LOADER_CMDLINE
};
/* BIOS ROM layout for QEMU kernel loader */
enum {
LOADER_BOOTROM,
LOADER_PARAM,
};
struct MemmapEntry {
hwaddr base;
hwaddr size;
};
extern const struct MemmapEntry virt_memmap[];
void init_loongson_params(struct loongson_params *lp, void *p,
uint64_t cpu_freq, uint64_t ram_size);
void init_reset_system(struct efi_reset_system_t *reset);
#endif

638
hw/mips/loongson3_virt.c Normal file
View File

@ -0,0 +1,638 @@
/*
* Generic Loongson-3 Platform support
*
* Copyright (c) 2018-2020 Huacai Chen (chenhc@lemote.com)
* Copyright (c) 2018-2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* Generic virtualized PC Platform based on Loongson-3 CPU (MIPS64R2 with
* extensions, 800~2000MHz)
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/units.h"
#include "qemu/cutils.h"
#include "qemu/datadir.h"
#include "qapi/error.h"
#include "cpu.h"
#include "elf.h"
#include "kvm_mips.h"
#include "hw/boards.h"
#include "hw/char/serial.h"
#include "hw/intc/loongson_liointc.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "hw/mips/fw_cfg.h"
#include "hw/mips/loongson3_bootp.h"
#include "hw/misc/unimp.h"
#include "hw/intc/i8259.h"
#include "hw/loader.h"
#include "hw/isa/superio.h"
#include "hw/pci/msi.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
#include "hw/pci-host/gpex.h"
#include "hw/usb.h"
#include "net/net.h"
#include "exec/address-spaces.h"
#include "sysemu/kvm.h"
#include "sysemu/qtest.h"
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
#define PM_CNTL_MODE 0x10
#define LOONGSON_MAX_VCPUS 16
/*
* Loongson-3's virtual machine BIOS can be obtained here:
* 1, https://github.com/loongson-community/firmware-nonfree
* 2, http://dev.lemote.com:8000/files/firmware/UEFI/KVM/bios_loongson3.bin
*/
#define LOONGSON3_BIOSNAME "bios_loongson3.bin"
#define UART_IRQ 0
#define RTC_IRQ 1
#define PCIE_IRQ_BASE 2
const struct MemmapEntry virt_memmap[] = {
[VIRT_LOWMEM] = { 0x00000000, 0x10000000 },
[VIRT_PM] = { 0x10080000, 0x100 },
[VIRT_FW_CFG] = { 0x10080100, 0x100 },
[VIRT_RTC] = { 0x10081000, 0x1000 },
[VIRT_PCIE_PIO] = { 0x18000000, 0x80000 },
[VIRT_PCIE_ECAM] = { 0x1a000000, 0x2000000 },
[VIRT_BIOS_ROM] = { 0x1fc00000, 0x200000 },
[VIRT_UART] = { 0x1fe001e0, 0x8 },
[VIRT_LIOINTC] = { 0x3ff01400, 0x64 },
[VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 },
[VIRT_HIGHMEM] = { 0x80000000, 0x0 }, /* Variable */
};
static const struct MemmapEntry loader_memmap[] = {
[LOADER_KERNEL] = { 0x00000000, 0x4000000 },
[LOADER_INITRD] = { 0x04000000, 0x0 }, /* Variable */
[LOADER_CMDLINE] = { 0x0ff00000, 0x100000 },
};
static const struct MemmapEntry loader_rommap[] = {
[LOADER_BOOTROM] = { 0x1fc00000, 0x1000 },
[LOADER_PARAM] = { 0x1fc01000, 0x10000 },
};
struct LoongsonMachineState {
MachineState parent_obj;
MemoryRegion *pio_alias;
MemoryRegion *mmio_alias;
MemoryRegion *ecam_alias;
};
typedef struct LoongsonMachineState LoongsonMachineState;
#define TYPE_LOONGSON_MACHINE MACHINE_TYPE_NAME("loongson3-virt")
DECLARE_INSTANCE_CHECKER(LoongsonMachineState, LOONGSON_MACHINE, TYPE_LOONGSON_MACHINE)
static struct _loaderparams {
uint64_t cpu_freq;
uint64_t ram_size;
const char *kernel_cmdline;
const char *kernel_filename;
const char *initrd_filename;
uint64_t kernel_entry;
uint64_t a0, a1, a2;
} loaderparams;
static uint64_t loongson3_pm_read(void *opaque, hwaddr addr, unsigned size)
{
return 0;
}
static void loongson3_pm_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
if (addr != PM_CNTL_MODE) {
return;
}
switch (val) {
case 0x00:
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
return;
case 0xff:
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
return;
default:
return;
}
}
static const MemoryRegionOps loongson3_pm_ops = {
.read = loongson3_pm_read,
.write = loongson3_pm_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1
}
};
#define DEF_LOONGSON3_FREQ (800 * 1000 * 1000)
static uint64_t get_cpu_freq_hz(void)
{
#ifdef CONFIG_KVM
int ret;
uint64_t freq;
struct kvm_one_reg freq_reg = {
.id = KVM_REG_MIPS_COUNT_HZ,
.addr = (uintptr_t)(&freq)
};
if (kvm_enabled()) {
ret = kvm_vcpu_ioctl(first_cpu, KVM_GET_ONE_REG, &freq_reg);
if (ret >= 0) {
return freq * 2;
}
}
#endif
return DEF_LOONGSON3_FREQ;
}
static void init_boot_param(void)
{
static void *p;
struct boot_params *bp;
p = g_malloc0(loader_rommap[LOADER_PARAM].size);
bp = p;
bp->efi.smbios.vers = cpu_to_le16(1);
init_reset_system(&(bp->reset_system));
p += ROUND_UP(sizeof(struct boot_params), 64);
init_loongson_params(&(bp->efi.smbios.lp), p,
loaderparams.cpu_freq, loaderparams.ram_size);
rom_add_blob_fixed("params_rom", bp,
loader_rommap[LOADER_PARAM].size,
loader_rommap[LOADER_PARAM].base);
g_free(bp);
loaderparams.a2 = cpu_mips_phys_to_kseg0(NULL,
loader_rommap[LOADER_PARAM].base);
}
static void init_boot_rom(void)
{
const unsigned int boot_code[] = {
0x40086000, /* mfc0 t0, CP0_STATUS */
0x240900E4, /* li t1, 0xe4 #set kx, sx, ux, erl */
0x01094025, /* or t0, t0, t1 */
0x3C090040, /* lui t1, 0x40 #set bev */
0x01094025, /* or t0, t0, t1 */
0x40886000, /* mtc0 t0, CP0_STATUS */
0x00000000,
0x40806800, /* mtc0 zero, CP0_CAUSE */
0x00000000,
0x400A7801, /* mfc0 t2, $15, 1 */
0x314A00FF, /* andi t2, 0x0ff */
0x3C089000, /* dli t0, 0x900000003ff01000 */
0x00084438,
0x35083FF0,
0x00084438,
0x35081000,
0x314B0003, /* andi t3, t2, 0x3 #local cpuid */
0x000B5A00, /* sll t3, 8 */
0x010B4025, /* or t0, t0, t3 */
0x314C000C, /* andi t4, t2, 0xc #node id */
0x000C62BC, /* dsll t4, 42 */
0x010C4025, /* or t0, t0, t4 */
/* WaitForInit: */
0xDD020020, /* ld v0, FN_OFF(t0) #FN_OFF 0x020 */
0x1040FFFE, /* beqz v0, WaitForInit */
0x00000000, /* nop */
0xDD1D0028, /* ld sp, SP_OFF(t0) #FN_OFF 0x028 */
0xDD1C0030, /* ld gp, GP_OFF(t0) #FN_OFF 0x030 */
0xDD050038, /* ld a1, A1_OFF(t0) #FN_OFF 0x038 */
0x00400008, /* jr v0 #byebye */
0x00000000, /* nop */
0x1000FFFF, /* 1: b 1b */
0x00000000, /* nop */
/* Reset */
0x3C0C9000, /* dli t0, 0x9000000010080010 */
0x358C0000,
0x000C6438,
0x358C1008,
0x000C6438,
0x358C0010,
0x240D0000, /* li t1, 0x00 */
0xA18D0000, /* sb t1, (t0) */
0x1000FFFF, /* 1: b 1b */
0x00000000, /* nop */
/* Shutdown */
0x3C0C9000, /* dli t0, 0x9000000010080010 */
0x358C0000,
0x000C6438,
0x358C1008,
0x000C6438,
0x358C0010,
0x240D00FF, /* li t1, 0xff */
0xA18D0000, /* sb t1, (t0) */
0x1000FFFF, /* 1: b 1b */
0x00000000 /* nop */
};
rom_add_blob_fixed("boot_rom", boot_code, sizeof(boot_code),
loader_rommap[LOADER_BOOTROM].base);
}
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static void fw_conf_init(unsigned long ram_size)
{
FWCfgState *fw_cfg;
hwaddr cfg_addr = virt_memmap[VIRT_FW_CFG].base;
fw_cfg = fw_cfg_init_mem_wide(cfg_addr, cfg_addr + 8, 8, 0, NULL);
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)current_machine->smp.cpus);
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)current_machine->smp.max_cpus);
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
fw_cfg_add_i32(fw_cfg, FW_CFG_MACHINE_VERSION, 1);
fw_cfg_add_i64(fw_cfg, FW_CFG_CPU_FREQ, get_cpu_freq_hz());
qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
}
static int set_prom_cmdline(ram_addr_t initrd_offset, long initrd_size)
{
int ret = 0;
void *cmdline_buf;
hwaddr cmdline_vaddr;
unsigned int *parg_env;
/* Allocate cmdline_buf for command line. */
cmdline_buf = g_malloc0(loader_memmap[LOADER_CMDLINE].size);
cmdline_vaddr = cpu_mips_phys_to_kseg0(NULL,
loader_memmap[LOADER_CMDLINE].base);
/*
* Layout of cmdline_buf looks like this:
* argv[0], argv[1], 0, env[0], env[1], ... env[i], 0,
* argv[0]'s data, argv[1]'s data, env[0]'data, ..., env[i]'s data, 0
*/
parg_env = (void *)cmdline_buf;
ret = (3 + 1) * 4;
*parg_env++ = cmdline_vaddr + ret;
ret += (1 + snprintf(cmdline_buf + ret, 256 - ret, "g"));
/* argv1 */
*parg_env++ = cmdline_vaddr + ret;
if (initrd_size > 0)
ret += (1 + snprintf(cmdline_buf + ret, 256 - ret,
"rd_start=0x" TARGET_FMT_lx " rd_size=%li %s",
cpu_mips_phys_to_kseg0(NULL, initrd_offset),
initrd_size, loaderparams.kernel_cmdline));
else
ret += (1 + snprintf(cmdline_buf + ret, 256 - ret, "%s",
loaderparams.kernel_cmdline));
/* argv2 */
*parg_env++ = cmdline_vaddr + 4 * ret;
rom_add_blob_fixed("cmdline", cmdline_buf,
loader_memmap[LOADER_CMDLINE].size,
loader_memmap[LOADER_CMDLINE].base);
g_free(cmdline_buf);
loaderparams.a0 = 2;
loaderparams.a1 = cmdline_vaddr;
return 0;
}
static uint64_t load_kernel(CPUMIPSState *env)
{
long kernel_size;
ram_addr_t initrd_offset;
uint64_t kernel_entry, kernel_low, kernel_high, initrd_size;
kernel_size = load_elf(loaderparams.kernel_filename, NULL,
cpu_mips_kseg0_to_phys, NULL,
(uint64_t *)&kernel_entry,
(uint64_t *)&kernel_low, (uint64_t *)&kernel_high,
NULL, 0, EM_MIPS, 1, 0);
if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",
loaderparams.kernel_filename,
load_elf_strerror(kernel_size));
exit(1);
}
/* load initrd */
initrd_size = 0;
initrd_offset = 0;
if (loaderparams.initrd_filename) {
initrd_size = get_image_size(loaderparams.initrd_filename);
if (initrd_size > 0) {
initrd_offset = MAX(loader_memmap[LOADER_INITRD].base,
ROUND_UP(kernel_high, INITRD_PAGE_SIZE));
if (initrd_offset + initrd_size > loaderparams.ram_size) {
error_report("memory too small for initial ram disk '%s'",
loaderparams.initrd_filename);
exit(1);
}
initrd_size = load_image_targphys(loaderparams.initrd_filename,
initrd_offset,
loaderparams.ram_size - initrd_offset);
}
if (initrd_size == (target_ulong) -1) {
error_report("could not load initial ram disk '%s'",
loaderparams.initrd_filename);
exit(1);
}
}
/* Setup prom cmdline. */
set_prom_cmdline(initrd_offset, initrd_size);
return kernel_entry;
}
static void main_cpu_reset(void *opaque)
{
MIPSCPU *cpu = opaque;
CPUMIPSState *env = &cpu->env;
cpu_reset(CPU(cpu));
/* Loongson-3 reset stuff */
if (loaderparams.kernel_filename) {
if (cpu == MIPS_CPU(first_cpu)) {
env->active_tc.gpr[4] = loaderparams.a0;
env->active_tc.gpr[5] = loaderparams.a1;
env->active_tc.gpr[6] = loaderparams.a2;
env->active_tc.PC = loaderparams.kernel_entry;
}
env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL));
}
}
static inline void loongson3_virt_devices_init(MachineState *machine,
DeviceState *pic)
{
int i;
qemu_irq irq;
PCIBus *pci_bus;
DeviceState *dev;
MemoryRegion *mmio_reg, *ecam_reg;
LoongsonMachineState *s = LOONGSON_MACHINE(machine);
dev = qdev_new(TYPE_GPEX_HOST);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
pci_bus = PCI_HOST_BRIDGE(dev)->bus;
s->ecam_alias = g_new0(MemoryRegion, 1);
ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
memory_region_init_alias(s->ecam_alias, OBJECT(dev), "pcie-ecam",
ecam_reg, 0, virt_memmap[VIRT_PCIE_ECAM].size);
memory_region_add_subregion(get_system_memory(),
virt_memmap[VIRT_PCIE_ECAM].base,
s->ecam_alias);
s->mmio_alias = g_new0(MemoryRegion, 1);
mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
memory_region_init_alias(s->mmio_alias, OBJECT(dev), "pcie-mmio",
mmio_reg, virt_memmap[VIRT_PCIE_MMIO].base,
virt_memmap[VIRT_PCIE_MMIO].size);
memory_region_add_subregion(get_system_memory(),
virt_memmap[VIRT_PCIE_MMIO].base,
s->mmio_alias);
s->pio_alias = g_new0(MemoryRegion, 1);
memory_region_init_alias(s->pio_alias, OBJECT(dev), "pcie-pio",
get_system_io(), 0,
virt_memmap[VIRT_PCIE_PIO].size);
memory_region_add_subregion(get_system_memory(),
virt_memmap[VIRT_PCIE_PIO].base, s->pio_alias);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, virt_memmap[VIRT_PCIE_PIO].base);
for (i = 0; i < GPEX_NUM_IRQS; i++) {
irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i);
}
msi_nonbroken = true;
pci_vga_init(pci_bus);
if (defaults_enabled()) {
pci_create_simple(pci_bus, -1, "pci-ohci");
usb_create_simple(usb_bus_find(-1), "usb-kbd");
usb_create_simple(usb_bus_find(-1), "usb-tablet");
}
for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
if (!nd->model) {
nd->model = g_strdup("virtio");
}
pci_nic_init_nofail(nd, pci_bus, nd->model, NULL);
}
}
static void mips_loongson3_virt_init(MachineState *machine)
{
int i;
long bios_size;
MIPSCPU *cpu;
Clock *cpuclk;
CPUMIPSState *env;
DeviceState *liointc;
char *filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *kernel_filename = machine->kernel_filename;
const char *initrd_filename = machine->initrd_filename;
ram_addr_t ram_size = machine->ram_size;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *bios = g_new(MemoryRegion, 1);
MemoryRegion *iomem = g_new(MemoryRegion, 1);
/* TODO: TCG will support all CPU types */
if (!kvm_enabled()) {
if (!machine->cpu_type) {
machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A1000");
}
if (!strstr(machine->cpu_type, "Loongson-3A1000")) {
error_report("Loongson-3/TCG needs cpu type Loongson-3A1000");
exit(1);
}
} else {
if (!machine->cpu_type) {
machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A4000");
}
if (!strstr(machine->cpu_type, "Loongson-3A4000")) {
error_report("Loongson-3/KVM needs cpu type Loongson-3A4000");
exit(1);
}
}
if (ram_size < 512 * MiB) {
error_report("Loongson-3 machine needs at least 512MB memory");
exit(1);
}
/*
* The whole MMIO range among configure registers doesn't generate
* exception when accessing invalid memory. Create some unimplememted
* devices to emulate this feature.
*/
create_unimplemented_device("mmio fallback 0", 0x10000000, 256 * MiB);
create_unimplemented_device("mmio fallback 1", 0x30000000, 256 * MiB);
liointc = qdev_new("loongson.liointc");
sysbus_realize_and_unref(SYS_BUS_DEVICE(liointc), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(liointc), 0, virt_memmap[VIRT_LIOINTC].base);
serial_mm_init(address_space_mem, virt_memmap[VIRT_UART].base, 0,
qdev_get_gpio_in(liointc, UART_IRQ), 115200, serial_hd(0),
DEVICE_NATIVE_ENDIAN);
sysbus_create_simple("goldfish_rtc", virt_memmap[VIRT_RTC].base,
qdev_get_gpio_in(liointc, RTC_IRQ));
cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
clock_set_hz(cpuclk, DEF_LOONGSON3_FREQ);
for (i = 0; i < machine->smp.cpus; i++) {
int ip;
/* init CPUs */
cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
/* Init internal devices */
cpu_mips_irq_init_cpu(cpu);
cpu_mips_clock_init(cpu);
qemu_register_reset(main_cpu_reset, cpu);
if (i >= 4) {
continue; /* Only node-0 can be connected to LIOINTC */
}
for (ip = 0; ip < 4 ; ip++) {
int pin = i * 4 + ip;
sysbus_connect_irq(SYS_BUS_DEVICE(liointc),
pin, cpu->env.irq[ip + 2]);
}
}
env = &MIPS_CPU(first_cpu)->env;
/* Allocate RAM/BIOS, 0x00000000~0x10000000 is alias of 0x80000000~0x90000000 */
memory_region_init_rom(bios, NULL, "loongson3.bios",
virt_memmap[VIRT_BIOS_ROM].size, &error_fatal);
memory_region_init_alias(ram, NULL, "loongson3.lowmem",
machine->ram, 0, virt_memmap[VIRT_LOWMEM].size);
memory_region_init_io(iomem, NULL, &loongson3_pm_ops,
NULL, "loongson3_pm", virt_memmap[VIRT_PM].size);
memory_region_add_subregion(address_space_mem,
virt_memmap[VIRT_LOWMEM].base, ram);
memory_region_add_subregion(address_space_mem,
virt_memmap[VIRT_BIOS_ROM].base, bios);
memory_region_add_subregion(address_space_mem,
virt_memmap[VIRT_HIGHMEM].base, machine->ram);
memory_region_add_subregion(address_space_mem,
virt_memmap[VIRT_PM].base, iomem);
/*
* We do not support flash operation, just loading bios.bin as raw BIOS.
* Please use -L to set the BIOS path and -bios to set bios name.
*/
if (kernel_filename) {
loaderparams.cpu_freq = get_cpu_freq_hz();
loaderparams.ram_size = ram_size;
loaderparams.kernel_filename = kernel_filename;
loaderparams.kernel_cmdline = kernel_cmdline;
loaderparams.initrd_filename = initrd_filename;
loaderparams.kernel_entry = load_kernel(env);
init_boot_rom();
init_boot_param();
} else {
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
machine->firmware ?: LOONGSON3_BIOSNAME);
if (filename) {
bios_size = load_image_targphys(filename,
virt_memmap[VIRT_BIOS_ROM].base,
virt_memmap[VIRT_BIOS_ROM].size);
g_free(filename);
} else {
bios_size = -1;
}
if ((bios_size < 0 || bios_size > virt_memmap[VIRT_BIOS_ROM].size) &&
!kernel_filename && !qtest_enabled()) {
error_report("Could not load MIPS bios '%s'", machine->firmware);
exit(1);
}
fw_conf_init(ram_size);
}
loongson3_virt_devices_init(machine, liointc);
}
static void loongson3v_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "Loongson-3 Virtualization Platform";
mc->init = mips_loongson3_virt_init;
mc->block_default_type = IF_IDE;
mc->max_cpus = LOONGSON_MAX_VCPUS;
mc->default_ram_id = "loongson3.highram";
mc->default_ram_size = 1600 * MiB;
mc->kvm_type = mips_kvm_type;
mc->minimum_page_bits = 14;
}
static const TypeInfo loongson3_machine_types[] = {
{
.name = TYPE_LOONGSON_MACHINE,
.parent = TYPE_MACHINE,
.instance_size = sizeof(LoongsonMachineState),
.class_init = loongson3v_machine_class_init,
}
};
DEFINE_TYPES(loongson3_machine_types)

View File

@ -62,7 +62,8 @@
#include "hw/mips/cps.h"
#include "hw/qdev-clock.h"
#define ENVP_ADDR 0x80002000l
#define ENVP_PADDR 0x2000
#define ENVP_VADDR cpu_mips_phys_to_kseg0(NULL, ENVP_PADDR)
#define ENVP_NB_ENTRIES 16
#define ENVP_ENTRY_SIZE 256
@ -616,8 +617,8 @@ static void network_init(PCIBus *pci_bus)
}
}
static void write_bootloader_nanomips(uint8_t *base, int64_t run_addr,
int64_t kernel_entry)
static void write_bootloader_nanomips(uint8_t *base, uint64_t run_addr,
uint64_t kernel_entry)
{
uint16_t *p;
@ -656,29 +657,29 @@ static void write_bootloader_nanomips(uint8_t *base, int64_t run_addr,
/* li a0,2 */
}
stw_p(p++, 0xe3a0 | NM_HI1(ENVP_ADDR - 64));
stw_p(p++, 0xe3a0 | NM_HI1(ENVP_VADDR - 64));
stw_p(p++, NM_HI2(ENVP_ADDR - 64));
/* lui sp,%hi(ENVP_ADDR - 64) */
stw_p(p++, NM_HI2(ENVP_VADDR - 64));
/* lui sp,%hi(ENVP_VADDR - 64) */
stw_p(p++, 0x83bd); stw_p(p++, NM_LO(ENVP_ADDR - 64));
/* ori sp,sp,%lo(ENVP_ADDR - 64) */
stw_p(p++, 0x83bd); stw_p(p++, NM_LO(ENVP_VADDR - 64));
/* ori sp,sp,%lo(ENVP_VADDR - 64) */
stw_p(p++, 0xe0a0 | NM_HI1(ENVP_ADDR));
stw_p(p++, 0xe0a0 | NM_HI1(ENVP_VADDR));
stw_p(p++, NM_HI2(ENVP_ADDR));
/* lui a1,%hi(ENVP_ADDR) */
stw_p(p++, NM_HI2(ENVP_VADDR));
/* lui a1,%hi(ENVP_VADDR) */
stw_p(p++, 0x80a5); stw_p(p++, NM_LO(ENVP_ADDR));
/* ori a1,a1,%lo(ENVP_ADDR) */
stw_p(p++, 0x80a5); stw_p(p++, NM_LO(ENVP_VADDR));
/* ori a1,a1,%lo(ENVP_VADDR) */
stw_p(p++, 0xe0c0 | NM_HI1(ENVP_ADDR + 8));
stw_p(p++, 0xe0c0 | NM_HI1(ENVP_VADDR + 8));
stw_p(p++, NM_HI2(ENVP_ADDR + 8));
/* lui a2,%hi(ENVP_ADDR + 8) */
stw_p(p++, NM_HI2(ENVP_VADDR + 8));
/* lui a2,%hi(ENVP_VADDR + 8) */
stw_p(p++, 0x80c6); stw_p(p++, NM_LO(ENVP_ADDR + 8));
/* ori a2,a2,%lo(ENVP_ADDR + 8) */
stw_p(p++, 0x80c6); stw_p(p++, NM_LO(ENVP_VADDR + 8));
/* ori a2,a2,%lo(ENVP_VADDR + 8) */
stw_p(p++, 0xe0e0 | NM_HI1(loaderparams.ram_low_size));
@ -840,8 +841,8 @@ static void write_bootloader_nanomips(uint8_t *base, int64_t run_addr,
* a2 - 32-bit address of the environment variables table
* a3 - RAM size in bytes
*/
static void write_bootloader(uint8_t *base, int64_t run_addr,
int64_t kernel_entry)
static void write_bootloader(uint8_t *base, uint64_t run_addr,
uint64_t kernel_entry)
{
uint32_t *p;
@ -878,18 +879,18 @@ static void write_bootloader(uint8_t *base, int64_t run_addr,
stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */
}
/* lui sp, high(ENVP_ADDR) */
stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff));
/* ori sp, sp, low(ENVP_ADDR) */
stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff));
/* lui a1, high(ENVP_ADDR) */
stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff));
/* ori a1, a1, low(ENVP_ADDR) */
stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff));
/* lui a2, high(ENVP_ADDR + 8) */
stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff));
/* ori a2, a2, low(ENVP_ADDR + 8) */
stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff));
/* lui sp, high(ENVP_VADDR) */
stl_p(p++, 0x3c1d0000 | (((ENVP_VADDR - 64) >> 16) & 0xffff));
/* ori sp, sp, low(ENVP_VADDR) */
stl_p(p++, 0x37bd0000 | ((ENVP_VADDR - 64) & 0xffff));
/* lui a1, high(ENVP_VADDR) */
stl_p(p++, 0x3c050000 | ((ENVP_VADDR >> 16) & 0xffff));
/* ori a1, a1, low(ENVP_VADDR) */
stl_p(p++, 0x34a50000 | (ENVP_VADDR & 0xffff));
/* lui a2, high(ENVP_VADDR + 8) */
stl_p(p++, 0x3c060000 | (((ENVP_VADDR + 8) >> 16) & 0xffff));
/* ori a2, a2, low(ENVP_VADDR + 8) */
stl_p(p++, 0x34c60000 | ((ENVP_VADDR + 8) & 0xffff));
/* lui a3, high(ram_low_size) */
stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16));
/* ori a3, a3, low(ram_low_size) */
@ -1003,7 +1004,7 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index,
const char *string, ...)
{
va_list ap;
int32_t table_addr;
uint32_t table_addr;
if (index >= ENVP_NB_ENTRIES) {
return;
@ -1014,8 +1015,8 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index,
return;
}
table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
prom_buf[index] = tswap32(ENVP_ADDR + table_addr);
table_addr = sizeof(uint32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
prom_buf[index] = tswap32(ENVP_VADDR + table_addr);
va_start(ap, string);
vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
@ -1023,9 +1024,9 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index,
}
/* Kernel */
static int64_t load_kernel(void)
static uint64_t load_kernel(void)
{
int64_t kernel_entry, kernel_high, initrd_size;
uint64_t kernel_entry, kernel_high, initrd_size;
long kernel_size;
ram_addr_t initrd_offset;
int big_endian;
@ -1042,8 +1043,8 @@ static int64_t load_kernel(void)
kernel_size = load_elf(loaderparams.kernel_filename, NULL,
cpu_mips_kseg0_to_phys, NULL,
(uint64_t *)&kernel_entry, NULL,
(uint64_t *)&kernel_high, NULL, big_endian, EM_MIPS,
&kernel_entry, NULL,
&kernel_high, NULL, big_endian, EM_MIPS,
1, 0);
if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",
@ -1122,8 +1123,7 @@ static int64_t load_kernel(void)
prom_set(prom_buf, prom_index++, "38400n8r");
prom_set(prom_buf, prom_index++, NULL);
rom_add_blob_fixed("prom", prom_buf, prom_size,
cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
rom_add_blob_fixed("prom", prom_buf, prom_size, ENVP_PADDR);
g_free(prom_buf);
return kernel_entry;
@ -1234,7 +1234,7 @@ void mips_malta_init(MachineState *machine)
MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1);
const size_t smbus_eeprom_size = 8 * 256;
uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size);
int64_t kernel_entry, bootloader_run_addr;
uint64_t kernel_entry, bootloader_run_addr;
PCIBus *pci_bus;
ISABus *isa_bus;
qemu_irq cbus_irq, i8259_irq;
@ -1302,9 +1302,9 @@ void mips_malta_init(MachineState *machine)
/* For KVM we reserve 1MB of RAM for running bootloader */
if (kvm_enabled()) {
ram_low_size -= 0x100000;
bootloader_run_addr = 0x40000000 + ram_low_size;
bootloader_run_addr = cpu_mips_kvm_um_phys_to_kseg0(NULL, ram_low_size);
} else {
bootloader_run_addr = 0xbfc00000;
bootloader_run_addr = cpu_mips_phys_to_kseg0(NULL, RESET_ADDRESS);
}
/* Write a small bootloader to the flash location. */

View File

@ -1,6 +1,8 @@
mips_ss = ss.source_set()
mips_ss.add(files('mips_int.c'))
mips_ss.add(when: 'CONFIG_FW_CFG_MIPS', if_true: files('fw_cfg.c'))
mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c'))
mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c', 'loongson3_virt.c'))
mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c'))
mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c'))
mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c'))

View File

@ -60,9 +60,9 @@ typedef struct ResetData {
uint64_t vector;
} ResetData;
static int64_t load_kernel(void)
static uint64_t load_kernel(void)
{
int64_t entry, kernel_high, initrd_size;
uint64_t entry, kernel_high, initrd_size;
long kernel_size;
ram_addr_t initrd_offset;
int big_endian;
@ -75,8 +75,8 @@ static int64_t load_kernel(void)
kernel_size = load_elf(loaderparams.kernel_filename, NULL,
cpu_mips_kseg0_to_phys, NULL,
(uint64_t *)&entry, NULL,
(uint64_t *)&kernel_high, NULL, big_endian,
&entry, NULL,
&kernel_high, NULL, big_endian,
EM_MIPS, 1, 0);
if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",

View File

@ -196,8 +196,7 @@ FIELD(BONGENCFG, PCIQUEUE, 12, 1)
#define PCI_IDSEL_VIA686B (1 << PCI_IDSEL_VIA686B_BIT)
#define PCI_ADDR(busno , devno , funno , regno) \
((((busno) << 16) & 0xff0000) + (((devno) << 11) & 0xf800) + \
(((funno) << 8) & 0x700) + (regno))
((PCI_BUILD_BDF(busno, PCI_DEVFN(devno , funno)) << 8) + (regno))
typedef struct BonitoState BonitoState;
@ -469,8 +468,8 @@ static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr)
regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET;
if (idsel == 0) {
error_report("error in bonito pci config address " TARGET_FMT_plx
",pcimap_cfg=%x", addr, s->regs[BONITO_PCIMAP_CFG]);
error_report("error in bonito pci config address 0x" TARGET_FMT_plx
",pcimap_cfg=0x%x", addr, s->regs[BONITO_PCIMAP_CFG]);
exit(1);
}
pciaddr = PCI_ADDR(pci_bus_num(phb->bus), devno, funno, regno);
@ -571,7 +570,7 @@ static int pci_bonito_map_irq(PCIDevice *pci_dev, int irq_num)
{
int slot;
slot = (pci_dev->devfn >> 3);
slot = PCI_SLOT(pci_dev->devfn);
switch (slot) {
case 5: /* FULOONG2E_VIA_SLOT, SouthBridge, IDE, USB, ACPI, AC97, MC97 */
@ -632,7 +631,7 @@ static void bonito_pcihost_realize(DeviceState *dev, Error **errp)
phb->bus = pci_register_root_bus(dev, "pci",
pci_bonito_set_irq, pci_bonito_map_irq,
dev, &bs->pci_mem, get_system_io(),
0x28, 32, TYPE_PCI_BUS);
PCI_DEVFN(5, 0), 32, TYPE_PCI_BUS);
for (size_t i = 0; i < 3; i++) {
char *name = g_strdup_printf("pci.lomem%zu", i);
@ -729,7 +728,8 @@ static void bonito_realize(PCIDevice *dev, Error **errp)
pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x0000);
pci_set_byte(dev->config + PCI_INTERRUPT_LINE, 0x00);
pci_set_byte(dev->config + PCI_INTERRUPT_PIN, 0x01);
pci_config_set_interrupt_pin(dev->config, 0x01); /* interrupt pin A */
pci_set_byte(dev->config + PCI_MIN_GNT, 0x3c);
pci_set_byte(dev->config + PCI_MAX_LAT, 0x00);

View File

@ -889,7 +889,7 @@ static bool pnv_phb4_resolve_pe(PnvPhb4DMASpace *ds)
/* Read RTE */
bus_num = pci_bus_num(ds->bus);
addr = rtt & PHB_RTT_BASE_ADDRESS_MASK;
addr += 2 * ((bus_num << 8) | ds->devfn);
addr += 2 * PCI_BUILD_BDF(bus_num, ds->devfn);
if (dma_memory_read(&address_space_memory, addr, &rte, sizeof(rte))) {
phb_error(ds->phb, "Failed to read RTT entry at 0x%"PRIx64, addr);
/* Set error bits ? fence ? ... */

View File

@ -342,7 +342,7 @@ static const MemoryRegionOps e500_pci_reg_ops = {
static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int pin)
{
int devno = pci_dev->devfn >> 3;
int devno = PCI_SLOT(pci_dev->devfn);
int ret;
ret = ppce500_pci_map_irq_slot(devno, pin);

View File

@ -63,15 +63,13 @@ static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
if (slot == 32) {
slot = -1; /* XXX: should this be 0? */
}
func = (reg >> 8) & 7;
func = PCI_FUNC(reg >> 8);
/* ... and then convert them to x86 format */
/* config pointer */
retval = (reg & (0xff - 7)) | (addr & 7);
/* slot */
retval |= slot << 11;
/* fn */
retval |= func << 8;
/* slot, fn */
retval |= PCI_DEVFN(slot, func) << 8;
}
trace_unin_get_config_reg(reg, addr, retval);

View File

@ -243,7 +243,7 @@ static void ppc4xx_pci_reset(void *opaque)
* may need further refactoring for other boards. */
static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
{
int slot = pci_dev->devfn >> 3;
int slot = PCI_SLOT(pci_dev->devfn);
trace_ppc4xx_pci_map_irq(pci_dev->devfn, irq_num, slot);

View File

@ -109,7 +109,7 @@ static const MemoryRegionOps sh_pci_reg_ops = {
static int sh_pci_map_irq(PCIDevice *d, int irq_num)
{
return (d->devfn >> 3);
return PCI_SLOT(d->devfn);
}
static void sh_pci_set_irq(void *opaque, int irq_num, int level)

View File

@ -16,6 +16,8 @@
#include "qom/object.h"
#include "qemu/queue.h"
#include "qemu/host-utils.h"
#include "qemu/bitops.h"
#define TYPE_CLOCK "clock"
OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
@ -38,7 +40,6 @@ typedef void ClockCallback(void *opaque);
* macro helpers to convert to hertz / nanosecond
*/
#define CLOCK_PERIOD_FROM_NS(ns) ((ns) * (CLOCK_PERIOD_1SEC / 1000000000llu))
#define CLOCK_PERIOD_TO_NS(per) ((per) / (CLOCK_PERIOD_1SEC / 1000000000llu))
#define CLOCK_PERIOD_FROM_HZ(hz) (((hz) != 0) ? CLOCK_PERIOD_1SEC / (hz) : 0u)
#define CLOCK_PERIOD_TO_HZ(per) (((per) != 0) ? CLOCK_PERIOD_1SEC / (per) : 0u)
@ -213,9 +214,43 @@ static inline unsigned clock_get_hz(Clock *clk)
return CLOCK_PERIOD_TO_HZ(clock_get(clk));
}
static inline unsigned clock_get_ns(Clock *clk)
/**
* clock_ticks_to_ns:
* @clk: the clock to query
* @ticks: number of ticks
*
* Returns the length of time in nanoseconds for this clock
* to tick @ticks times. Because a clock can have a period
* which is not a whole number of nanoseconds, it is important
* to use this function when calculating things like timer
* expiry deadlines, rather than attempting to obtain a "period
* in nanoseconds" value and then multiplying that by a number
* of ticks.
*
* The result could in theory be too large to fit in a 64-bit
* value if the number of ticks and the clock period are both
* large; to avoid overflow the result will be saturated to INT64_MAX
* (because this is the largest valid input to the QEMUTimer APIs).
* Since INT64_MAX nanoseconds is almost 300 years, anything with
* an expiry later than that is in the "will never happen" category
* and callers can reasonably not special-case the saturated result.
*/
static inline uint64_t clock_ticks_to_ns(const Clock *clk, uint64_t ticks)
{
return CLOCK_PERIOD_TO_NS(clock_get(clk));
uint64_t ns_low, ns_high;
/*
* clk->period is the period in units of 2^-32 ns, so
* (clk->period * ticks) is the required length of time in those
* units, and we can convert to nanoseconds by multiplying by
* 2^32, which is the same as shifting the 128-bit multiplication
* result right by 32.
*/
mulu64(&ns_low, &ns_high, clk->period, ticks);
if (ns_high & MAKE_64BIT_MASK(31, 33)) {
return INT64_MAX;
}
return ns_low >> 32 | ns_high << 32;
}
/**
@ -229,4 +264,16 @@ static inline bool clock_is_enabled(const Clock *clk)
return clock_get(clk) != 0;
}
/**
* clock_display_freq: return human-readable representation of clock frequency
* @clk: clock
*
* Return a string which has a human-readable representation of the
* clock's frequency, e.g. "33.3 MHz". This is intended for debug
* and display purposes.
*
* The caller is responsible for freeing the string with g_free().
*/
char *clock_display_freq(Clock *clk);
#endif /* QEMU_HW_CLOCK_H */

View File

@ -0,0 +1,22 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 2020 Huacai Chen <chenhc@lemote.com>
* Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
*
*/
#ifndef LOONGSON_LIOINTC_H
#define LOONGSON_LIOINTC_H
#include "qemu/units.h"
#include "hw/sysbus.h"
#include "qom/object.h"
#define TYPE_LOONGSON_LIOINTC "loongson.liointc"
DECLARE_INSTANCE_CHECKER(struct loongson_liointc, LOONGSON_LIOINTC,
TYPE_LOONGSON_LIOINTC)
#endif /* LOONGSON_LIOINTC_H */

View File

@ -1,14 +1,10 @@
#ifndef HW_VT82C686_H
#define HW_VT82C686_H
#define TYPE_VT82C686B_ISA "vt82c686b-isa"
#define TYPE_VT82C686B_SUPERIO "vt82c686b-superio"
/* vt82c686.c */
ISABus *vt82c686b_isa_init(PCIBus * bus, int devfn);
void vt82c686b_ac97_init(PCIBus *bus, int devfn);
void vt82c686b_mc97_init(PCIBus *bus, int devfn);
I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
qemu_irq sci_irq);
#define TYPE_VT82C686B_PM "vt82c686b-pm"
#define TYPE_VIA_AC97 "via-ac97"
#define TYPE_VIA_MC97 "via-mc97"
#endif

View File

@ -736,11 +736,11 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
}
}
QLIST_FOREACH(ncl, &dev->clocks, node) {
qdev_printf("clock-%s%s \"%s\" freq_hz=%e\n",
g_autofree char *freq_str = clock_display_freq(ncl->clock);
qdev_printf("clock-%s%s \"%s\" freq_hz=%s\n",
ncl->output ? "out" : "in",
ncl->alias ? " (alias)" : "",
ncl->name,
CLOCK_PERIOD_TO_HZ(1.0 * clock_get(ncl->clock)));
ncl->name, freq_str);
}
class = object_get_class(OBJECT(dev));
do {

View File

@ -380,8 +380,8 @@ static void mips_cp0_period_set(MIPSCPU *cpu)
{
CPUMIPSState *env = &cpu->env;
env->cp0_count_ns = cpu->cp0_count_rate
* clock_get_ns(MIPS_CPU(cpu)->clock);
env->cp0_count_ns = clock_ticks_to_ns(MIPS_CPU(cpu)->clock,
cpu->cp0_count_rate);
assert(env->cp0_count_ns);
}

View File

@ -170,6 +170,27 @@ class BootLinuxConsole(LinuxKernelTest):
console_pattern = 'Kernel command line: %s' % kernel_command_line
self.wait_for_console_pattern(console_pattern)
def test_mips64el_fuloong2e(self):
"""
:avocado: tags=arch:mips64el
:avocado: tags=machine:fuloong2e
:avocado: tags=endian:little
"""
deb_url = ('http://archive.debian.org/debian/pool/main/l/linux/'
'linux-image-3.16.0-6-loongson-2e_3.16.56-1+deb8u1_mipsel.deb')
deb_hash = 'd04d446045deecf7b755ef576551de0c4184dd44'
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
kernel_path = self.extract_from_deb(deb_path,
'/boot/vmlinux-3.16.0-6-loongson-2e')
self.vm.set_console()
kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
self.vm.add_args('-kernel', kernel_path,
'-append', kernel_command_line)
self.vm.launch()
console_pattern = 'Kernel command line: %s' % kernel_command_line
self.wait_for_console_pattern(console_pattern)
def test_mips_malta_cpio(self):
"""
:avocado: tags=arch:mips