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:
commit
2e0b5bbe81
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
-----------------------
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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 */);
|
||||
}
|
||||
|
||||
|
@ -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
93
hw/audio/via-ac97.c
Normal 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)
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
35
hw/mips/fw_cfg.c
Normal 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
19
hw/mips/fw_cfg.h
Normal 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
|
@ -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
151
hw/mips/loongson3_bootp.c
Normal 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
241
hw/mips/loongson3_bootp.h
Normal 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
638
hw/mips/loongson3_virt.c
Normal 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)
|
@ -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. */
|
||||
|
@ -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'))
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 ? ... */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
22
include/hw/intc/loongson_liointc.h
Normal file
22
include/hw/intc/loongson_liointc.h
Normal 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 */
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user