mirror of https://github.com/xemu-project/xemu.git
Fix nios2-linux-user syscalls.
Fix nios2-linux-user sigreturn. Enable tests for nios2-linux-user. Remove special handling of SIGSEGV. Check supervisor for eret, bret. Split special registers out of env->regs[]. Clean up interrupt processing. Raise unaligned data and destination exceptions. Set TLBMISC fields correctly on exceptions. Prevent writes to read-only or reserved control fields. Use tcg_constant_tl(). Implement shadow register sets. Implement external interrupt controller interface. Implement vectored interrupt controller. Enable semihosting tests for nios2-softmmu. -----BEGIN PGP SIGNATURE----- iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmJoNuQdHHJpY2hhcmQu aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV+a0ggAhawc3tod4OTHRlRq rvZrJK740bNMo8rtidDnh71+IGjBiz8pXahqkE78cADtMzNmQoScwWbjht3cuMN2 TMV0sbNDeA2OB98QzX6JTbCRtEfQAB7pyjpFvg6oXhYYSfwwhWbTR9QsYTHjq157 ZKOprafoSlmDlgWJhlAikLdvJb07/5jgmvsLbBzu8/G/HiJ4HhHyjZxL1wNz1t/+ 0KTAbnn3SWGDAhLGS/P6BMZKeU1EAExAwo7CtZeUbs+9QCfeM3cBAurG3WB1Vw14 ERPoGPPrARtoNPtgQFMHu0am3HH5HtneuzJfWaLT96rrwNyTrYY0EYti1NtFDW8O CCz42Q== =MHar -----END PGP SIGNATURE----- Merge tag 'pull-nios2-20220426' of https://gitlab.com/rth7680/qemu into staging Fix nios2-linux-user syscalls. Fix nios2-linux-user sigreturn. Enable tests for nios2-linux-user. Remove special handling of SIGSEGV. Check supervisor for eret, bret. Split special registers out of env->regs[]. Clean up interrupt processing. Raise unaligned data and destination exceptions. Set TLBMISC fields correctly on exceptions. Prevent writes to read-only or reserved control fields. Use tcg_constant_tl(). Implement shadow register sets. Implement external interrupt controller interface. Implement vectored interrupt controller. Enable semihosting tests for nios2-softmmu. # -----BEGIN PGP SIGNATURE----- # # iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmJoNuQdHHJpY2hhcmQu # aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV+a0ggAhawc3tod4OTHRlRq # rvZrJK740bNMo8rtidDnh71+IGjBiz8pXahqkE78cADtMzNmQoScwWbjht3cuMN2 # TMV0sbNDeA2OB98QzX6JTbCRtEfQAB7pyjpFvg6oXhYYSfwwhWbTR9QsYTHjq157 # ZKOprafoSlmDlgWJhlAikLdvJb07/5jgmvsLbBzu8/G/HiJ4HhHyjZxL1wNz1t/+ # 0KTAbnn3SWGDAhLGS/P6BMZKeU1EAExAwo7CtZeUbs+9QCfeM3cBAurG3WB1Vw14 # ERPoGPPrARtoNPtgQFMHu0am3HH5HtneuzJfWaLT96rrwNyTrYY0EYti1NtFDW8O # CCz42Q== # =MHar # -----END PGP SIGNATURE----- # gpg: Signature made Tue 26 Apr 2022 11:16:04 AM PDT # gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F # gpg: issuer "richard.henderson@linaro.org" # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [ultimate] * tag 'pull-nios2-20220426' of https://gitlab.com/rth7680/qemu: (68 commits) tests/tcg/nios2: Add test-shadow-1 tests/tcg/nios2: Add semihosting multiarch tests hw/nios2: Machine with a Vectored Interrupt Controller hw/nios2: Move memory regions into Nios2Machine hw/nios2: Introduce Nios2MachineState hw/intc: Vectored Interrupt Controller (VIC) linux-user/nios2: Handle various SIGILL exceptions target/nios2: Advance pc when raising exceptions target/nios2: Implement EIC interrupt processing target/nios2: Update helper_eret for shadow registers target/nios2: Implement rdprs, wrprs target/nios2: Introduce shadow register sets target/nios2: Implement Misaligned destination exception target/nios2: Use tcg_gen_lookup_and_goto_ptr target/nios2: Use gen_goto_tb for DISAS_TOO_MANY target/nios2: Hoist set of is_jmp into gen_goto_tb target/nios2: Create gen_jumpr target/nios2: Enable unaligned traps for system mode target/nios2: Drop CR_STATUS_EH from tb->flags target/nios2: Introduce dest_gpr ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
88d5814e6b
|
@ -1 +1,2 @@
|
|||
TARGET_ARCH=nios2
|
||||
TARGET_ALIGNED_ONLY=y
|
||||
|
|
|
@ -84,3 +84,6 @@ config GOLDFISH_PIC
|
|||
|
||||
config M68K_IRQC
|
||||
bool
|
||||
|
||||
config NIOS2_VIC
|
||||
bool
|
||||
|
|
|
@ -62,3 +62,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
|
|||
if_true: files('spapr_xive_kvm.c'))
|
||||
specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
|
||||
specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
|
||||
specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c'))
|
||||
|
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* Vectored Interrupt Controller for nios2 processor
|
||||
*
|
||||
* Copyright (c) 2022 Neuroblade
|
||||
*
|
||||
* Interface:
|
||||
* QOM property "cpu": link to the Nios2 CPU (must be set)
|
||||
* Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines
|
||||
* IRQ should be connected to nios2 IRQ0.
|
||||
*
|
||||
* Reference: "Embedded Peripherals IP User Guide
|
||||
* for Intel® Quartus® Prime Design Suite: 21.4"
|
||||
* Chapter 38 "Vectored Interrupt Controller Core"
|
||||
* See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/intc/nios2_vic.h"
|
||||
#include "cpu.h"
|
||||
|
||||
|
||||
enum {
|
||||
INT_CONFIG0 = 0,
|
||||
INT_CONFIG31 = 31,
|
||||
INT_ENABLE = 32,
|
||||
INT_ENABLE_SET = 33,
|
||||
INT_ENABLE_CLR = 34,
|
||||
INT_PENDING = 35,
|
||||
INT_RAW_STATUS = 36,
|
||||
SW_INTERRUPT = 37,
|
||||
SW_INTERRUPT_SET = 38,
|
||||
SW_INTERRUPT_CLR = 39,
|
||||
VIC_CONFIG = 40,
|
||||
VIC_STATUS = 41,
|
||||
VEC_TBL_BASE = 42,
|
||||
VEC_TBL_ADDR = 43,
|
||||
CSR_COUNT /* Last! */
|
||||
};
|
||||
|
||||
/* Requested interrupt level (INT_CONFIG[0:5]) */
|
||||
static inline uint32_t vic_int_config_ril(const Nios2VIC *vic, int irq_num)
|
||||
{
|
||||
return extract32(vic->int_config[irq_num], 0, 6);
|
||||
}
|
||||
|
||||
/* Requested NMI (INT_CONFIG[6]) */
|
||||
static inline uint32_t vic_int_config_rnmi(const Nios2VIC *vic, int irq_num)
|
||||
{
|
||||
return extract32(vic->int_config[irq_num], 6, 1);
|
||||
}
|
||||
|
||||
/* Requested register set (INT_CONFIG[7:12]) */
|
||||
static inline uint32_t vic_int_config_rrs(const Nios2VIC *vic, int irq_num)
|
||||
{
|
||||
return extract32(vic->int_config[irq_num], 7, 6);
|
||||
}
|
||||
|
||||
static inline uint32_t vic_config_vec_size(const Nios2VIC *vic)
|
||||
{
|
||||
return 1 << (2 + extract32(vic->vic_config, 0, 3));
|
||||
}
|
||||
|
||||
static inline uint32_t vic_int_pending(const Nios2VIC *vic)
|
||||
{
|
||||
return (vic->int_raw_status | vic->sw_int) & vic->int_enable;
|
||||
}
|
||||
|
||||
static void vic_update_irq(Nios2VIC *vic)
|
||||
{
|
||||
Nios2CPU *cpu = NIOS2_CPU(vic->cpu);
|
||||
uint32_t pending = vic_int_pending(vic);
|
||||
int irq = -1;
|
||||
int max_ril = 0;
|
||||
/* Note that if RIL is 0 for an interrupt it is effectively disabled */
|
||||
|
||||
vic->vec_tbl_addr = 0;
|
||||
vic->vic_status = 0;
|
||||
|
||||
if (pending == 0) {
|
||||
qemu_irq_lower(vic->output_int);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NIOS2_VIC_MAX_IRQ; i++) {
|
||||
if (pending & BIT(i)) {
|
||||
int ril = vic_int_config_ril(vic, i);
|
||||
if (ril > max_ril) {
|
||||
irq = i;
|
||||
max_ril = ril;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (irq < 0) {
|
||||
qemu_irq_lower(vic->output_int);
|
||||
return;
|
||||
}
|
||||
|
||||
vic->vec_tbl_addr = irq * vic_config_vec_size(vic) + vic->vec_tbl_base;
|
||||
vic->vic_status = irq | BIT(31);
|
||||
|
||||
/*
|
||||
* In hardware, the interface between the VIC and the CPU is via the
|
||||
* External Interrupt Controller interface, where the interrupt controller
|
||||
* presents the CPU with a packet of data containing:
|
||||
* - Requested Handler Address (RHA): 32 bits
|
||||
* - Requested Register Set (RRS) : 6 bits
|
||||
* - Requested Interrupt Level (RIL) : 6 bits
|
||||
* - Requested NMI flag (RNMI) : 1 bit
|
||||
* In our emulation, we implement this by writing the data directly to
|
||||
* fields in the CPU object and then raising the IRQ line to tell
|
||||
* the CPU that we've done so.
|
||||
*/
|
||||
|
||||
cpu->rha = vic->vec_tbl_addr;
|
||||
cpu->ril = max_ril;
|
||||
cpu->rrs = vic_int_config_rrs(vic, irq);
|
||||
cpu->rnmi = vic_int_config_rnmi(vic, irq);
|
||||
|
||||
qemu_irq_raise(vic->output_int);
|
||||
}
|
||||
|
||||
static void vic_set_irq(void *opaque, int irq_num, int level)
|
||||
{
|
||||
Nios2VIC *vic = opaque;
|
||||
|
||||
vic->int_raw_status = deposit32(vic->int_raw_status, irq_num, 1, !!level);
|
||||
vic_update_irq(vic);
|
||||
}
|
||||
|
||||
static void nios2_vic_reset(DeviceState *dev)
|
||||
{
|
||||
Nios2VIC *vic = NIOS2_VIC(dev);
|
||||
|
||||
memset(&vic->int_config, 0, sizeof(vic->int_config));
|
||||
vic->vic_config = 0;
|
||||
vic->int_raw_status = 0;
|
||||
vic->int_enable = 0;
|
||||
vic->sw_int = 0;
|
||||
vic->vic_status = 0;
|
||||
vic->vec_tbl_base = 0;
|
||||
vic->vec_tbl_addr = 0;
|
||||
}
|
||||
|
||||
static uint64_t nios2_vic_csr_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
Nios2VIC *vic = opaque;
|
||||
int index = offset / 4;
|
||||
|
||||
switch (index) {
|
||||
case INT_CONFIG0 ... INT_CONFIG31:
|
||||
return vic->int_config[index - INT_CONFIG0];
|
||||
case INT_ENABLE:
|
||||
return vic->int_enable;
|
||||
case INT_PENDING:
|
||||
return vic_int_pending(vic);
|
||||
case INT_RAW_STATUS:
|
||||
return vic->int_raw_status;
|
||||
case SW_INTERRUPT:
|
||||
return vic->sw_int;
|
||||
case VIC_CONFIG:
|
||||
return vic->vic_config;
|
||||
case VIC_STATUS:
|
||||
return vic->vic_status;
|
||||
case VEC_TBL_BASE:
|
||||
return vic->vec_tbl_base;
|
||||
case VEC_TBL_ADDR:
|
||||
return vic->vec_tbl_addr;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void nios2_vic_csr_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
Nios2VIC *vic = opaque;
|
||||
int index = offset / 4;
|
||||
|
||||
switch (index) {
|
||||
case INT_CONFIG0 ... INT_CONFIG31:
|
||||
vic->int_config[index - INT_CONFIG0] = value;
|
||||
break;
|
||||
case INT_ENABLE:
|
||||
vic->int_enable = value;
|
||||
break;
|
||||
case INT_ENABLE_SET:
|
||||
vic->int_enable |= value;
|
||||
break;
|
||||
case INT_ENABLE_CLR:
|
||||
vic->int_enable &= ~value;
|
||||
break;
|
||||
case SW_INTERRUPT:
|
||||
vic->sw_int = value;
|
||||
break;
|
||||
case SW_INTERRUPT_SET:
|
||||
vic->sw_int |= value;
|
||||
break;
|
||||
case SW_INTERRUPT_CLR:
|
||||
vic->sw_int &= ~value;
|
||||
break;
|
||||
case VIC_CONFIG:
|
||||
vic->vic_config = value;
|
||||
break;
|
||||
case VEC_TBL_BASE:
|
||||
vic->vec_tbl_base = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"nios2-vic: write to invalid CSR address %#"
|
||||
HWADDR_PRIx "\n", offset);
|
||||
}
|
||||
|
||||
vic_update_irq(vic);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps nios2_vic_csr_ops = {
|
||||
.read = nios2_vic_csr_read,
|
||||
.write = nios2_vic_csr_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = { .min_access_size = 4, .max_access_size = 4 }
|
||||
};
|
||||
|
||||
static void nios2_vic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Nios2VIC *vic = NIOS2_VIC(dev);
|
||||
|
||||
if (!vic->cpu) {
|
||||
/* This is a programming error in the code using this device */
|
||||
error_setg(errp, "nios2-vic 'cpu' link property was not set");
|
||||
return;
|
||||
}
|
||||
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &vic->output_int);
|
||||
qdev_init_gpio_in(dev, vic_set_irq, NIOS2_VIC_MAX_IRQ);
|
||||
|
||||
memory_region_init_io(&vic->csr, OBJECT(dev), &nios2_vic_csr_ops, vic,
|
||||
"nios2.vic.csr", CSR_COUNT * sizeof(uint32_t));
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &vic->csr);
|
||||
}
|
||||
|
||||
static Property nios2_vic_properties[] = {
|
||||
DEFINE_PROP_LINK("cpu", Nios2VIC, cpu, TYPE_CPU, CPUState *),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static const VMStateDescription nios2_vic_vmstate = {
|
||||
.name = "nios2-vic",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]){
|
||||
VMSTATE_UINT32_ARRAY(int_config, Nios2VIC, 32),
|
||||
VMSTATE_UINT32(vic_config, Nios2VIC),
|
||||
VMSTATE_UINT32(int_raw_status, Nios2VIC),
|
||||
VMSTATE_UINT32(int_enable, Nios2VIC),
|
||||
VMSTATE_UINT32(sw_int, Nios2VIC),
|
||||
VMSTATE_UINT32(vic_status, Nios2VIC),
|
||||
VMSTATE_UINT32(vec_tbl_base, Nios2VIC),
|
||||
VMSTATE_UINT32(vec_tbl_addr, Nios2VIC),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static void nios2_vic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = nios2_vic_reset;
|
||||
dc->realize = nios2_vic_realize;
|
||||
dc->vmsd = &nios2_vic_vmstate;
|
||||
device_class_set_props(dc, nios2_vic_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo nios2_vic_info = {
|
||||
.name = TYPE_NIOS2_VIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(Nios2VIC),
|
||||
.class_init = nios2_vic_class_init,
|
||||
};
|
||||
|
||||
static void nios2_vic_register_types(void)
|
||||
{
|
||||
type_register_static(&nios2_vic_info);
|
||||
}
|
||||
|
||||
type_init(nios2_vic_register_types);
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/intc/nios2_vic.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/boards.h"
|
||||
|
@ -36,17 +37,28 @@
|
|||
|
||||
#include "boot.h"
|
||||
|
||||
struct Nios2MachineState {
|
||||
MachineState parent_obj;
|
||||
|
||||
MemoryRegion phys_tcm;
|
||||
MemoryRegion phys_tcm_alias;
|
||||
MemoryRegion phys_ram;
|
||||
MemoryRegion phys_ram_alias;
|
||||
|
||||
bool vic;
|
||||
};
|
||||
|
||||
#define TYPE_NIOS2_MACHINE MACHINE_TYPE_NAME("10m50-ghrd")
|
||||
OBJECT_DECLARE_TYPE(Nios2MachineState, MachineClass, NIOS2_MACHINE)
|
||||
|
||||
#define BINARY_DEVICE_TREE_FILE "10m50-devboard.dtb"
|
||||
|
||||
static void nios2_10m50_ghrd_init(MachineState *machine)
|
||||
{
|
||||
Nios2MachineState *nms = NIOS2_MACHINE(machine);
|
||||
Nios2CPU *cpu;
|
||||
DeviceState *dev;
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
MemoryRegion *phys_tcm = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *phys_tcm_alias = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *phys_ram_alias = g_new(MemoryRegion, 1);
|
||||
ram_addr_t tcm_base = 0x0;
|
||||
ram_addr_t tcm_size = 0x1000; /* 1 kiB, but QEMU limit is 4 kiB */
|
||||
ram_addr_t ram_base = 0x08000000;
|
||||
|
@ -55,27 +67,56 @@ static void nios2_10m50_ghrd_init(MachineState *machine)
|
|||
int i;
|
||||
|
||||
/* Physical TCM (tb_ram_1k) with alias at 0xc0000000 */
|
||||
memory_region_init_ram(phys_tcm, NULL, "nios2.tcm", tcm_size,
|
||||
memory_region_init_ram(&nms->phys_tcm, NULL, "nios2.tcm", tcm_size,
|
||||
&error_abort);
|
||||
memory_region_init_alias(phys_tcm_alias, NULL, "nios2.tcm.alias",
|
||||
phys_tcm, 0, tcm_size);
|
||||
memory_region_add_subregion(address_space_mem, tcm_base, phys_tcm);
|
||||
memory_region_init_alias(&nms->phys_tcm_alias, NULL, "nios2.tcm.alias",
|
||||
&nms->phys_tcm, 0, tcm_size);
|
||||
memory_region_add_subregion(address_space_mem, tcm_base, &nms->phys_tcm);
|
||||
memory_region_add_subregion(address_space_mem, 0xc0000000 + tcm_base,
|
||||
phys_tcm_alias);
|
||||
&nms->phys_tcm_alias);
|
||||
|
||||
/* Physical DRAM with alias at 0xc0000000 */
|
||||
memory_region_init_ram(phys_ram, NULL, "nios2.ram", ram_size,
|
||||
memory_region_init_ram(&nms->phys_ram, NULL, "nios2.ram", ram_size,
|
||||
&error_abort);
|
||||
memory_region_init_alias(phys_ram_alias, NULL, "nios2.ram.alias",
|
||||
phys_ram, 0, ram_size);
|
||||
memory_region_add_subregion(address_space_mem, ram_base, phys_ram);
|
||||
memory_region_init_alias(&nms->phys_ram_alias, NULL, "nios2.ram.alias",
|
||||
&nms->phys_ram, 0, ram_size);
|
||||
memory_region_add_subregion(address_space_mem, ram_base, &nms->phys_ram);
|
||||
memory_region_add_subregion(address_space_mem, 0xc0000000 + ram_base,
|
||||
phys_ram_alias);
|
||||
&nms->phys_ram_alias);
|
||||
|
||||
/* Create CPU -- FIXME */
|
||||
cpu = NIOS2_CPU(cpu_create(TYPE_NIOS2_CPU));
|
||||
for (i = 0; i < 32; i++) {
|
||||
irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i);
|
||||
/* Create CPU. We need to set eic_present between init and realize. */
|
||||
cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
|
||||
|
||||
/* Enable the External Interrupt Controller within the CPU. */
|
||||
cpu->eic_present = nms->vic;
|
||||
|
||||
/* Configure new exception vectors. */
|
||||
cpu->reset_addr = 0xd4000000;
|
||||
cpu->exception_addr = 0xc8000120;
|
||||
cpu->fast_tlb_miss_addr = 0xc0000100;
|
||||
|
||||
qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal);
|
||||
|
||||
if (nms->vic) {
|
||||
DeviceState *dev = qdev_new(TYPE_NIOS2_VIC);
|
||||
MemoryRegion *dev_mr;
|
||||
qemu_irq cpu_irq;
|
||||
|
||||
object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_fatal);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
cpu_irq = qdev_get_gpio_in_named(DEVICE(cpu), "EIC", 0);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
irq[i] = qdev_get_gpio_in(dev, i);
|
||||
}
|
||||
|
||||
dev_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
|
||||
memory_region_add_subregion(address_space_mem, 0x18002000, dev_mr);
|
||||
} else {
|
||||
for (i = 0; i < 32; i++) {
|
||||
irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Register: Altera 16550 UART */
|
||||
|
@ -96,20 +137,44 @@ static void nios2_10m50_ghrd_init(MachineState *machine)
|
|||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xe0000880);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[5]);
|
||||
|
||||
/* Configure new exception vectors and reset CPU for it to take effect. */
|
||||
cpu->reset_addr = 0xd4000000;
|
||||
cpu->exception_addr = 0xc8000120;
|
||||
cpu->fast_tlb_miss_addr = 0xc0000100;
|
||||
|
||||
nios2_load_kernel(cpu, ram_base, ram_size, machine->initrd_filename,
|
||||
BINARY_DEVICE_TREE_FILE, NULL);
|
||||
}
|
||||
|
||||
static void nios2_10m50_ghrd_machine_init(struct MachineClass *mc)
|
||||
static bool get_vic(Object *obj, Error **errp)
|
||||
{
|
||||
Nios2MachineState *nms = NIOS2_MACHINE(obj);
|
||||
return nms->vic;
|
||||
}
|
||||
|
||||
static void set_vic(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
Nios2MachineState *nms = NIOS2_MACHINE(obj);
|
||||
nms->vic = value;
|
||||
}
|
||||
|
||||
static void nios2_10m50_ghrd_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->desc = "Altera 10M50 GHRD Nios II design";
|
||||
mc->init = nios2_10m50_ghrd_init;
|
||||
mc->is_default = true;
|
||||
|
||||
object_class_property_add_bool(oc, "vic", get_vic, set_vic);
|
||||
object_class_property_set_description(oc, "vic",
|
||||
"Set on/off to enable/disable the Vectored Interrupt Controller");
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("10m50-ghrd", nios2_10m50_ghrd_machine_init);
|
||||
static const TypeInfo nios2_10m50_ghrd_type_info = {
|
||||
.name = TYPE_NIOS2_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(Nios2MachineState),
|
||||
.class_init = nios2_10m50_ghrd_class_init,
|
||||
};
|
||||
|
||||
static void nios2_10m50_ghrd_type_init(void)
|
||||
{
|
||||
type_register_static(&nios2_10m50_ghrd_type_info);
|
||||
}
|
||||
type_init(nios2_10m50_ghrd_type_init);
|
||||
|
|
|
@ -3,6 +3,7 @@ config NIOS2_10M50
|
|||
select NIOS2
|
||||
select SERIAL
|
||||
select ALTERA_TIMER
|
||||
select NIOS2_VIC
|
||||
|
||||
config NIOS2_GENERIC_NOMMU
|
||||
bool
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Vectored Interrupt Controller for nios2 processor
|
||||
*
|
||||
* Copyright (c) 2022 Neuroblade
|
||||
*
|
||||
* Interface:
|
||||
* QOM property "cpu": link to the Nios2 CPU (must be set)
|
||||
* Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines
|
||||
* IRQ should be connected to nios2 IRQ0.
|
||||
*
|
||||
* Reference: "Embedded Peripherals IP User Guide
|
||||
* for Intel® Quartus® Prime Design Suite: 21.4"
|
||||
* Chapter 38 "Vectored Interrupt Controller Core"
|
||||
* See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef HW_INTC_NIOS2_VIC
|
||||
#define HW_INTC_NIOS2_VIC
|
||||
|
||||
#define TYPE_NIOS2_VIC "nios2-vic"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Nios2VIC, NIOS2_VIC)
|
||||
|
||||
#define NIOS2_VIC_MAX_IRQ 32
|
||||
|
||||
struct Nios2VIC {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
qemu_irq output_int;
|
||||
|
||||
/* properties */
|
||||
CPUState *cpu;
|
||||
MemoryRegion csr;
|
||||
|
||||
uint32_t int_config[NIOS2_VIC_MAX_IRQ];
|
||||
uint32_t vic_config;
|
||||
uint32_t int_raw_status;
|
||||
uint32_t int_enable;
|
||||
uint32_t sw_int;
|
||||
uint32_t vic_status;
|
||||
uint32_t vec_tbl_base;
|
||||
uint32_t vec_tbl_addr;
|
||||
};
|
||||
|
||||
#endif /* HW_INTC_NIOS2_VIC */
|
|
@ -1096,7 +1096,6 @@ static void init_thread(struct target_pt_regs *regs, struct image_info *infop)
|
|||
{
|
||||
regs->ea = infop->entry;
|
||||
regs->sp = infop->start_stack;
|
||||
regs->estatus = 0x3;
|
||||
}
|
||||
|
||||
#define LO_COMMPAGE TARGET_PAGE_SIZE
|
||||
|
@ -1170,7 +1169,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs,
|
|||
(*regs)[30] = -1; /* R_SSTATUS */
|
||||
(*regs)[31] = tswapreg(env->regs[R_RA]);
|
||||
|
||||
(*regs)[32] = tswapreg(env->regs[R_PC]);
|
||||
(*regs)[32] = tswapreg(env->pc);
|
||||
|
||||
(*regs)[33] = -1; /* R_STATUS */
|
||||
(*regs)[34] = tswapreg(env->regs[CR_ESTATUS]);
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
void cpu_loop(CPUNios2State *env)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
target_siginfo_t info;
|
||||
int trapnr, ret;
|
||||
|
||||
for (;;) {
|
||||
|
@ -39,6 +38,30 @@ void cpu_loop(CPUNios2State *env)
|
|||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
|
||||
case EXCP_DIV:
|
||||
/* Match kernel's handle_diverror_c(). */
|
||||
env->pc -= 4;
|
||||
force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc);
|
||||
break;
|
||||
|
||||
case EXCP_UNALIGN:
|
||||
case EXCP_UNALIGND:
|
||||
force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN,
|
||||
env->ctrl[CR_BADADDR]);
|
||||
break;
|
||||
|
||||
case EXCP_ILLEGAL:
|
||||
case EXCP_UNIMPL:
|
||||
/* Match kernel's handle_illegal_c(). */
|
||||
env->pc -= 4;
|
||||
force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc);
|
||||
break;
|
||||
case EXCP_SUPERI:
|
||||
/* Match kernel's handle_supervisor_instr(). */
|
||||
env->pc -= 4;
|
||||
force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc);
|
||||
break;
|
||||
|
||||
case EXCP_TRAP:
|
||||
switch (env->error_code) {
|
||||
case 0:
|
||||
|
@ -49,32 +72,41 @@ void cpu_loop(CPUNios2State *env)
|
|||
env->regs[7], env->regs[8], env->regs[9],
|
||||
0, 0);
|
||||
|
||||
if (env->regs[2] == 0) { /* FIXME: syscall 0 workaround */
|
||||
ret = 0;
|
||||
if (ret == -QEMU_ESIGRETURN) {
|
||||
/* rt_sigreturn has set all state. */
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == -QEMU_ERESTARTSYS) {
|
||||
env->pc -= 4;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* See the code after translate_rc_and_ret: all negative
|
||||
* values are errors (aided by userspace restricted to 2G),
|
||||
* errno is returned positive in r2, and error indication
|
||||
* is a boolean in r7.
|
||||
*/
|
||||
env->regs[2] = abs(ret);
|
||||
/* Return value is 0..4096 */
|
||||
env->regs[7] = ret > 0xfffff000u;
|
||||
env->regs[R_PC] += 4;
|
||||
env->regs[7] = ret < 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
qemu_log_mask(CPU_LOG_INT, "\nTrap 1\n");
|
||||
force_sig_fault(TARGET_SIGUSR1, 0, env->regs[R_PC]);
|
||||
force_sig_fault(TARGET_SIGUSR1, 0, env->pc);
|
||||
break;
|
||||
case 2:
|
||||
qemu_log_mask(CPU_LOG_INT, "\nTrap 2\n");
|
||||
force_sig_fault(TARGET_SIGUSR2, 0, env->regs[R_PC]);
|
||||
force_sig_fault(TARGET_SIGUSR2, 0, env->pc);
|
||||
break;
|
||||
case 31:
|
||||
qemu_log_mask(CPU_LOG_INT, "\nTrap 31\n");
|
||||
force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->regs[R_PC]);
|
||||
/* Match kernel's breakpoint_c(). */
|
||||
env->pc -= 4;
|
||||
force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(CPU_LOG_INT, "\nTrap %d\n", env->error_code);
|
||||
force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP,
|
||||
env->regs[R_PC]);
|
||||
force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP, env->pc);
|
||||
break;
|
||||
|
||||
case 16: /* QEMU specific, for __kuser_cmpxchg */
|
||||
|
@ -99,27 +131,13 @@ void cpu_loop(CPUNios2State *env)
|
|||
o = env->regs[5];
|
||||
n = env->regs[6];
|
||||
env->regs[2] = qatomic_cmpxchg(h, o, n) - o;
|
||||
env->regs[R_PC] += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXCP_DEBUG:
|
||||
info.si_signo = TARGET_SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_TRAP_BRKPT;
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
break;
|
||||
case 0xaa:
|
||||
{
|
||||
info.si_signo = TARGET_SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
/* TODO: check env->error_code */
|
||||
info.si_code = TARGET_SEGV_MAPERR;
|
||||
info._sifields._sigfault._addr = env->regs[R_PC];
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
}
|
||||
force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc);
|
||||
break;
|
||||
default:
|
||||
EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n",
|
||||
|
@ -133,28 +151,6 @@ void cpu_loop(CPUNios2State *env)
|
|||
|
||||
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
||||
{
|
||||
env->regs[0] = 0;
|
||||
env->regs[1] = regs->r1;
|
||||
env->regs[2] = regs->r2;
|
||||
env->regs[3] = regs->r3;
|
||||
env->regs[4] = regs->r4;
|
||||
env->regs[5] = regs->r5;
|
||||
env->regs[6] = regs->r6;
|
||||
env->regs[7] = regs->r7;
|
||||
env->regs[8] = regs->r8;
|
||||
env->regs[9] = regs->r9;
|
||||
env->regs[10] = regs->r10;
|
||||
env->regs[11] = regs->r11;
|
||||
env->regs[12] = regs->r12;
|
||||
env->regs[13] = regs->r13;
|
||||
env->regs[14] = regs->r14;
|
||||
env->regs[15] = regs->r15;
|
||||
/* TODO: unsigned long orig_r2; */
|
||||
env->regs[R_RA] = regs->ra;
|
||||
env->regs[R_FP] = regs->fp;
|
||||
env->regs[R_SP] = regs->sp;
|
||||
env->regs[R_GP] = regs->gp;
|
||||
env->regs[CR_ESTATUS] = regs->estatus;
|
||||
env->regs[R_PC] = regs->ea;
|
||||
/* TODO: unsigned long orig_r7; */
|
||||
env->pc = regs->ea;
|
||||
}
|
||||
|
|
|
@ -73,12 +73,11 @@ static void rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env)
|
|||
__put_user(env->regs[R_RA], &gregs[23]);
|
||||
__put_user(env->regs[R_FP], &gregs[24]);
|
||||
__put_user(env->regs[R_GP], &gregs[25]);
|
||||
__put_user(env->regs[R_PC], &gregs[27]);
|
||||
__put_user(env->pc, &gregs[27]);
|
||||
__put_user(env->regs[R_SP], &gregs[28]);
|
||||
}
|
||||
|
||||
static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc,
|
||||
int *pr2)
|
||||
static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc)
|
||||
{
|
||||
int temp;
|
||||
unsigned long *gregs = uc->tuc_mcontext.gregs;
|
||||
|
@ -122,14 +121,12 @@ static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc,
|
|||
__get_user(env->regs[R_GP], &gregs[25]);
|
||||
/* Not really necessary no user settable bits */
|
||||
__get_user(temp, &gregs[26]);
|
||||
__get_user(env->regs[R_PC], &gregs[27]);
|
||||
__get_user(env->pc, &gregs[27]);
|
||||
|
||||
__get_user(env->regs[R_RA], &gregs[23]);
|
||||
__get_user(env->regs[R_SP], &gregs[28]);
|
||||
|
||||
target_restore_altstack(&uc->tuc_stack, env);
|
||||
|
||||
*pr2 = env->regs[2];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -180,25 +177,17 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
|
|||
env->regs[4] = sig;
|
||||
env->regs[5] = frame_addr + offsetof(struct target_rt_sigframe, info);
|
||||
env->regs[6] = frame_addr + offsetof(struct target_rt_sigframe, uc);
|
||||
env->regs[R_PC] = ka->_sa_handler;
|
||||
env->pc = ka->_sa_handler;
|
||||
|
||||
unlock_user_struct(frame, frame_addr, 1);
|
||||
}
|
||||
|
||||
long do_sigreturn(CPUNios2State *env)
|
||||
{
|
||||
trace_user_do_sigreturn(env, 0);
|
||||
qemu_log_mask(LOG_UNIMP, "do_sigreturn: not implemented\n");
|
||||
return -TARGET_ENOSYS;
|
||||
}
|
||||
|
||||
long do_rt_sigreturn(CPUNios2State *env)
|
||||
{
|
||||
/* Verify, can we follow the stack back */
|
||||
abi_ulong frame_addr = env->regs[R_SP];
|
||||
struct target_rt_sigframe *frame;
|
||||
sigset_t set;
|
||||
int rval;
|
||||
|
||||
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
|
||||
goto badframe;
|
||||
|
@ -207,15 +196,15 @@ long do_rt_sigreturn(CPUNios2State *env)
|
|||
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
|
||||
set_sigmask(&set);
|
||||
|
||||
if (rt_restore_ucontext(env, &frame->uc, &rval)) {
|
||||
if (rt_restore_ucontext(env, &frame->uc)) {
|
||||
goto badframe;
|
||||
}
|
||||
|
||||
unlock_user_struct(frame, frame_addr, 0);
|
||||
return rval;
|
||||
return -QEMU_ESIGRETURN;
|
||||
|
||||
badframe:
|
||||
unlock_user_struct(frame, frame_addr, 0);
|
||||
force_sig(TARGET_SIGSEGV);
|
||||
return 0;
|
||||
return -QEMU_ESIGRETURN;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ static inline void cpu_clone_regs_child(CPUNios2State *env, target_ulong newsp,
|
|||
env->regs[R_SP] = newsp;
|
||||
}
|
||||
env->regs[R_RET0] = 0;
|
||||
env->regs[7] = 0;
|
||||
}
|
||||
|
||||
static inline void cpu_clone_regs_parent(CPUNios2State *env, unsigned flags)
|
||||
|
|
|
@ -31,12 +31,12 @@ static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
|
|||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
CPUNios2State *env = &cpu->env;
|
||||
|
||||
env->regs[R_PC] = value;
|
||||
env->pc = value;
|
||||
}
|
||||
|
||||
static bool nios2_cpu_has_work(CPUState *cs)
|
||||
{
|
||||
return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
|
||||
return cs->interrupt_request & CPU_INTERRUPT_HARD;
|
||||
}
|
||||
|
||||
static void nios2_cpu_reset(DeviceState *dev)
|
||||
|
@ -48,27 +48,42 @@ static void nios2_cpu_reset(DeviceState *dev)
|
|||
|
||||
ncc->parent_reset(dev);
|
||||
|
||||
memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
|
||||
env->regs[R_PC] = cpu->reset_addr;
|
||||
memset(env->ctrl, 0, sizeof(env->ctrl));
|
||||
env->pc = cpu->reset_addr;
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
/* Start in user mode with interrupts enabled. */
|
||||
env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
|
||||
env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE;
|
||||
memset(env->regs, 0, sizeof(env->regs));
|
||||
#else
|
||||
env->regs[CR_STATUS] = 0;
|
||||
env->ctrl[CR_STATUS] = CR_STATUS_RSIE;
|
||||
nios2_update_crs(env);
|
||||
memset(env->shadow_regs, 0, sizeof(env->shadow_regs));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static void nios2_cpu_set_irq(void *opaque, int irq, int level)
|
||||
static void eic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
Nios2CPU *cpu = opaque;
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
if (level) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
static void iic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
Nios2CPU *cpu = opaque;
|
||||
CPUNios2State *env = &cpu->env;
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
env->regs[CR_IPENDING] = deposit32(env->regs[CR_IPENDING], irq, 1, !!level);
|
||||
env->ctrl[CR_IPENDING] = deposit32(env->ctrl[CR_IPENDING], irq, 1, !!level);
|
||||
|
||||
if (env->regs[CR_IPENDING]) {
|
||||
if (env->ctrl[CR_IPENDING]) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
|
@ -84,15 +99,6 @@ static void nios2_cpu_initfn(Object *obj)
|
|||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
mmu_init(&cpu->env);
|
||||
|
||||
/*
|
||||
* These interrupt lines model the IIC (internal interrupt
|
||||
* controller). QEMU does not currently support the EIC
|
||||
* (external interrupt controller) -- if we did it would be
|
||||
* a separate device in hw/intc with a custom interface to
|
||||
* the CPU, and boards using it would not wire up these IRQ lines.
|
||||
*/
|
||||
qdev_init_gpio_in_named(DEVICE(cpu), nios2_cpu_set_irq, "IRQ", 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -101,36 +107,147 @@ static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model)
|
|||
return object_class_by_name(TYPE_NIOS2_CPU);
|
||||
}
|
||||
|
||||
static void realize_cr_status(CPUState *cs)
|
||||
{
|
||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
|
||||
/* Begin with all fields of all registers are reserved. */
|
||||
memset(cpu->cr_state, 0, sizeof(cpu->cr_state));
|
||||
|
||||
/*
|
||||
* The combination of writable and readonly is the set of all
|
||||
* non-reserved fields. We apply writable as a mask to bits,
|
||||
* and merge in existing readonly bits, before storing.
|
||||
*/
|
||||
#define WR_REG(C) cpu->cr_state[C].writable = -1
|
||||
#define RO_REG(C) cpu->cr_state[C].readonly = -1
|
||||
#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK
|
||||
#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK
|
||||
|
||||
WR_FIELD(CR_STATUS, PIE);
|
||||
WR_REG(CR_ESTATUS);
|
||||
WR_REG(CR_BSTATUS);
|
||||
RO_REG(CR_CPUID);
|
||||
RO_REG(CR_EXCEPTION);
|
||||
WR_REG(CR_BADADDR);
|
||||
|
||||
if (cpu->eic_present) {
|
||||
WR_FIELD(CR_STATUS, RSIE);
|
||||
RO_FIELD(CR_STATUS, NMI);
|
||||
WR_FIELD(CR_STATUS, PRS);
|
||||
RO_FIELD(CR_STATUS, CRS);
|
||||
WR_FIELD(CR_STATUS, IL);
|
||||
WR_FIELD(CR_STATUS, IH);
|
||||
} else {
|
||||
RO_FIELD(CR_STATUS, RSIE);
|
||||
WR_REG(CR_IENABLE);
|
||||
RO_REG(CR_IPENDING);
|
||||
}
|
||||
|
||||
if (cpu->mmu_present) {
|
||||
WR_FIELD(CR_STATUS, U);
|
||||
WR_FIELD(CR_STATUS, EH);
|
||||
|
||||
WR_FIELD(CR_PTEADDR, VPN);
|
||||
WR_FIELD(CR_PTEADDR, PTBASE);
|
||||
|
||||
RO_FIELD(CR_TLBMISC, D);
|
||||
RO_FIELD(CR_TLBMISC, PERM);
|
||||
RO_FIELD(CR_TLBMISC, BAD);
|
||||
RO_FIELD(CR_TLBMISC, DBL);
|
||||
WR_FIELD(CR_TLBMISC, PID);
|
||||
WR_FIELD(CR_TLBMISC, WE);
|
||||
WR_FIELD(CR_TLBMISC, RD);
|
||||
WR_FIELD(CR_TLBMISC, WAY);
|
||||
|
||||
WR_REG(CR_TLBACC);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are
|
||||
* unimplemented, so their corresponding control regs remain reserved.
|
||||
*/
|
||||
|
||||
#undef WR_REG
|
||||
#undef RO_REG
|
||||
#undef WR_FIELD
|
||||
#undef RO_FIELD
|
||||
}
|
||||
|
||||
static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (cpu->eic_present) {
|
||||
qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1);
|
||||
} else {
|
||||
qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32);
|
||||
}
|
||||
#endif
|
||||
|
||||
cpu_exec_realizefn(cs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
realize_cr_status(cs);
|
||||
qemu_init_vcpu(cs);
|
||||
cpu_reset(cs);
|
||||
|
||||
/* We have reserved storage for cpuid; might as well use it. */
|
||||
cpu->env.ctrl[CR_CPUID] = cs->cpu_index;
|
||||
|
||||
ncc->parent_realize(dev, errp);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static bool eic_take_interrupt(Nios2CPU *cpu)
|
||||
{
|
||||
CPUNios2State *env = &cpu->env;
|
||||
const uint32_t status = env->ctrl[CR_STATUS];
|
||||
|
||||
if (cpu->rnmi) {
|
||||
return !(status & CR_STATUS_NMI);
|
||||
}
|
||||
if (!(status & CR_STATUS_PIE)) {
|
||||
return false;
|
||||
}
|
||||
if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) {
|
||||
return false;
|
||||
}
|
||||
if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) {
|
||||
return true;
|
||||
}
|
||||
return status & CR_STATUS_RSIE;
|
||||
}
|
||||
|
||||
static bool iic_take_interrupt(Nios2CPU *cpu)
|
||||
{
|
||||
CPUNios2State *env = &cpu->env;
|
||||
|
||||
if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) {
|
||||
return false;
|
||||
}
|
||||
return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE];
|
||||
}
|
||||
|
||||
static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
CPUNios2State *env = &cpu->env;
|
||||
|
||||
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||
(env->regs[CR_STATUS] & CR_STATUS_PIE) &&
|
||||
(env->regs[CR_IPENDING] & env->regs[CR_IENABLE])) {
|
||||
cs->exception_index = EXCP_IRQ;
|
||||
nios2_cpu_do_interrupt(cs);
|
||||
return true;
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
if (cpu->eic_present
|
||||
? eic_take_interrupt(cpu)
|
||||
: iic_take_interrupt(cpu)) {
|
||||
cs->exception_index = EXCP_IRQ;
|
||||
nios2_cpu_do_interrupt(cs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -146,23 +263,26 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
|
|||
static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
|
||||
{
|
||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
CPUClass *cc = CPU_GET_CLASS(cs);
|
||||
CPUNios2State *env = &cpu->env;
|
||||
uint32_t val;
|
||||
|
||||
if (n > cc->gdb_num_core_regs) {
|
||||
if (n < 32) { /* GP regs */
|
||||
val = env->regs[n];
|
||||
} else if (n == 32) { /* PC */
|
||||
val = env->pc;
|
||||
} else if (n < 49) { /* Status regs */
|
||||
unsigned cr = n - 33;
|
||||
if (nios2_cr_reserved(&cpu->cr_state[cr])) {
|
||||
val = 0;
|
||||
} else {
|
||||
val = env->ctrl[n - 33];
|
||||
}
|
||||
} else {
|
||||
/* Invalid regs */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n < 32) { /* GP regs */
|
||||
return gdb_get_reg32(mem_buf, env->regs[n]);
|
||||
} else if (n == 32) { /* PC */
|
||||
return gdb_get_reg32(mem_buf, env->regs[R_PC]);
|
||||
} else if (n < 49) { /* Status regs */
|
||||
return gdb_get_reg32(mem_buf, env->regs[n - 1]);
|
||||
}
|
||||
|
||||
/* Invalid regs */
|
||||
return 0;
|
||||
return gdb_get_reg32(mem_buf, val);
|
||||
}
|
||||
|
||||
static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
|
@ -170,23 +290,32 @@ static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
|||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
CPUClass *cc = CPU_GET_CLASS(cs);
|
||||
CPUNios2State *env = &cpu->env;
|
||||
uint32_t val;
|
||||
|
||||
if (n > cc->gdb_num_core_regs) {
|
||||
return 0;
|
||||
}
|
||||
val = ldl_p(mem_buf);
|
||||
|
||||
if (n < 32) { /* GP regs */
|
||||
env->regs[n] = ldl_p(mem_buf);
|
||||
env->regs[n] = val;
|
||||
} else if (n == 32) { /* PC */
|
||||
env->regs[R_PC] = ldl_p(mem_buf);
|
||||
env->pc = val;
|
||||
} else if (n < 49) { /* Status regs */
|
||||
env->regs[n - 1] = ldl_p(mem_buf);
|
||||
unsigned cr = n - 33;
|
||||
/* ??? Maybe allow the debugger to write to readonly fields. */
|
||||
val &= cpu->cr_state[cr].writable;
|
||||
val |= cpu->cr_state[cr].readonly & env->ctrl[cr];
|
||||
env->ctrl[cr] = val;
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static Property nios2_properties[] = {
|
||||
DEFINE_PROP_BOOL("diverr_present", Nios2CPU, diverr_present, true),
|
||||
DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true),
|
||||
/* ALTR,pid-num-bits */
|
||||
DEFINE_PROP_UINT32("mmu_pid_num_bits", Nios2CPU, pid_num_bits, 8),
|
||||
|
@ -210,9 +339,7 @@ static const struct SysemuCPUOps nios2_sysemu_ops = {
|
|||
static const struct TCGCPUOps nios2_tcg_ops = {
|
||||
.initialize = nios2_tcg_init,
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
.record_sigsegv = nios2_cpu_record_sigsegv,
|
||||
#else
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
.tlb_fill = nios2_cpu_tlb_fill,
|
||||
.cpu_exec_interrupt = nios2_cpu_exec_interrupt,
|
||||
.do_interrupt = nios2_cpu_do_interrupt,
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "exec/cpu-defs.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
typedef struct CPUArchState CPUNios2State;
|
||||
|
@ -56,83 +57,114 @@ struct Nios2CPUClass {
|
|||
#define EXCEPTION_ADDRESS 0x00000004
|
||||
#define FAST_TLB_MISS_ADDRESS 0x00000008
|
||||
|
||||
#define NUM_GP_REGS 32
|
||||
#define NUM_CR_REGS 32
|
||||
|
||||
/* GP regs + CR regs + PC */
|
||||
#define NUM_CORE_REGS (32 + 32 + 1)
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* 63 shadow register sets; index 0 is the primary register set. */
|
||||
#define NUM_REG_SETS 64
|
||||
#endif
|
||||
|
||||
/* General purpose register aliases */
|
||||
#define R_ZERO 0
|
||||
#define R_AT 1
|
||||
#define R_RET0 2
|
||||
#define R_RET1 3
|
||||
#define R_ARG0 4
|
||||
#define R_ARG1 5
|
||||
#define R_ARG2 6
|
||||
#define R_ARG3 7
|
||||
#define R_ET 24
|
||||
#define R_BT 25
|
||||
#define R_GP 26
|
||||
#define R_SP 27
|
||||
#define R_FP 28
|
||||
#define R_EA 29
|
||||
#define R_BA 30
|
||||
#define R_RA 31
|
||||
enum {
|
||||
R_ZERO = 0,
|
||||
R_AT = 1,
|
||||
R_RET0 = 2,
|
||||
R_RET1 = 3,
|
||||
R_ARG0 = 4,
|
||||
R_ARG1 = 5,
|
||||
R_ARG2 = 6,
|
||||
R_ARG3 = 7,
|
||||
R_ET = 24,
|
||||
R_BT = 25,
|
||||
R_GP = 26,
|
||||
R_SP = 27,
|
||||
R_FP = 28,
|
||||
R_EA = 29,
|
||||
R_BA = 30,
|
||||
R_SSTATUS = 30,
|
||||
R_RA = 31,
|
||||
};
|
||||
|
||||
/* Control register aliases */
|
||||
#define CR_BASE 32
|
||||
#define CR_STATUS (CR_BASE + 0)
|
||||
#define CR_STATUS_PIE (1 << 0)
|
||||
#define CR_STATUS_U (1 << 1)
|
||||
#define CR_STATUS_EH (1 << 2)
|
||||
#define CR_STATUS_IH (1 << 3)
|
||||
#define CR_STATUS_IL (63 << 4)
|
||||
#define CR_STATUS_CRS (63 << 10)
|
||||
#define CR_STATUS_PRS (63 << 16)
|
||||
#define CR_STATUS_NMI (1 << 22)
|
||||
#define CR_STATUS_RSIE (1 << 23)
|
||||
#define CR_ESTATUS (CR_BASE + 1)
|
||||
#define CR_BSTATUS (CR_BASE + 2)
|
||||
#define CR_IENABLE (CR_BASE + 3)
|
||||
#define CR_IPENDING (CR_BASE + 4)
|
||||
#define CR_CPUID (CR_BASE + 5)
|
||||
#define CR_CTL6 (CR_BASE + 6)
|
||||
#define CR_EXCEPTION (CR_BASE + 7)
|
||||
#define CR_PTEADDR (CR_BASE + 8)
|
||||
#define CR_PTEADDR_PTBASE_SHIFT 22
|
||||
#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
|
||||
#define CR_PTEADDR_VPN_SHIFT 2
|
||||
#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
|
||||
#define CR_TLBACC (CR_BASE + 9)
|
||||
#define CR_TLBACC_IGN_SHIFT 25
|
||||
#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT)
|
||||
#define CR_TLBACC_C (1 << 24)
|
||||
#define CR_TLBACC_R (1 << 23)
|
||||
#define CR_TLBACC_W (1 << 22)
|
||||
#define CR_TLBACC_X (1 << 21)
|
||||
#define CR_TLBACC_G (1 << 20)
|
||||
#define CR_TLBACC_PFN_MASK 0x000FFFFF
|
||||
#define CR_TLBMISC (CR_BASE + 10)
|
||||
#define CR_TLBMISC_WAY_SHIFT 20
|
||||
#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT)
|
||||
#define CR_TLBMISC_RD (1 << 19)
|
||||
#define CR_TLBMISC_WR (1 << 18)
|
||||
#define CR_TLBMISC_PID_SHIFT 4
|
||||
#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT)
|
||||
#define CR_TLBMISC_DBL (1 << 3)
|
||||
#define CR_TLBMISC_BAD (1 << 2)
|
||||
#define CR_TLBMISC_PERM (1 << 1)
|
||||
#define CR_TLBMISC_D (1 << 0)
|
||||
#define CR_ENCINJ (CR_BASE + 11)
|
||||
#define CR_BADADDR (CR_BASE + 12)
|
||||
#define CR_CONFIG (CR_BASE + 13)
|
||||
#define CR_MPUBASE (CR_BASE + 14)
|
||||
#define CR_MPUACC (CR_BASE + 15)
|
||||
enum {
|
||||
CR_STATUS = 0,
|
||||
CR_ESTATUS = 1,
|
||||
CR_BSTATUS = 2,
|
||||
CR_IENABLE = 3,
|
||||
CR_IPENDING = 4,
|
||||
CR_CPUID = 5,
|
||||
CR_EXCEPTION = 7,
|
||||
CR_PTEADDR = 8,
|
||||
CR_TLBACC = 9,
|
||||
CR_TLBMISC = 10,
|
||||
CR_ENCINJ = 11,
|
||||
CR_BADADDR = 12,
|
||||
CR_CONFIG = 13,
|
||||
CR_MPUBASE = 14,
|
||||
CR_MPUACC = 15,
|
||||
};
|
||||
|
||||
/* Other registers */
|
||||
#define R_PC 64
|
||||
FIELD(CR_STATUS, PIE, 0, 1)
|
||||
FIELD(CR_STATUS, U, 1, 1)
|
||||
FIELD(CR_STATUS, EH, 2, 1)
|
||||
FIELD(CR_STATUS, IH, 3, 1)
|
||||
FIELD(CR_STATUS, IL, 4, 6)
|
||||
FIELD(CR_STATUS, CRS, 10, 6)
|
||||
FIELD(CR_STATUS, PRS, 16, 6)
|
||||
FIELD(CR_STATUS, NMI, 22, 1)
|
||||
FIELD(CR_STATUS, RSIE, 23, 1)
|
||||
FIELD(CR_STATUS, SRS, 31, 1) /* only in sstatus */
|
||||
|
||||
#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK
|
||||
#define CR_STATUS_U R_CR_STATUS_U_MASK
|
||||
#define CR_STATUS_EH R_CR_STATUS_EH_MASK
|
||||
#define CR_STATUS_IH R_CR_STATUS_IH_MASK
|
||||
#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK
|
||||
#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK
|
||||
#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK
|
||||
|
||||
FIELD(CR_EXCEPTION, CAUSE, 2, 5)
|
||||
FIELD(CR_EXCEPTION, ECCFTL, 31, 1)
|
||||
|
||||
FIELD(CR_PTEADDR, VPN, 2, 20)
|
||||
FIELD(CR_PTEADDR, PTBASE, 22, 10)
|
||||
|
||||
FIELD(CR_TLBACC, PFN, 0, 20)
|
||||
FIELD(CR_TLBACC, G, 20, 1)
|
||||
FIELD(CR_TLBACC, X, 21, 1)
|
||||
FIELD(CR_TLBACC, W, 22, 1)
|
||||
FIELD(CR_TLBACC, R, 23, 1)
|
||||
FIELD(CR_TLBACC, C, 24, 1)
|
||||
FIELD(CR_TLBACC, IG, 25, 7)
|
||||
|
||||
#define CR_TLBACC_C R_CR_TLBACC_C_MASK
|
||||
#define CR_TLBACC_R R_CR_TLBACC_R_MASK
|
||||
#define CR_TLBACC_W R_CR_TLBACC_W_MASK
|
||||
#define CR_TLBACC_X R_CR_TLBACC_X_MASK
|
||||
#define CR_TLBACC_G R_CR_TLBACC_G_MASK
|
||||
|
||||
FIELD(CR_TLBMISC, D, 0, 1)
|
||||
FIELD(CR_TLBMISC, PERM, 1, 1)
|
||||
FIELD(CR_TLBMISC, BAD, 2, 1)
|
||||
FIELD(CR_TLBMISC, DBL, 3, 1)
|
||||
FIELD(CR_TLBMISC, PID, 4, 14)
|
||||
FIELD(CR_TLBMISC, WE, 18, 1)
|
||||
FIELD(CR_TLBMISC, RD, 19, 1)
|
||||
FIELD(CR_TLBMISC, WAY, 20, 4)
|
||||
FIELD(CR_TLBMISC, EE, 24, 1)
|
||||
|
||||
#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK
|
||||
#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK
|
||||
#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK
|
||||
#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK
|
||||
#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK
|
||||
#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK
|
||||
#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK
|
||||
|
||||
/* Exceptions */
|
||||
#define EXCP_BREAK 0x1000
|
||||
#define EXCP_SEMIHOST 0x1001
|
||||
#define EXCP_RESET 0
|
||||
#define EXCP_PRESET 1
|
||||
#define EXCP_IRQ 2
|
||||
|
@ -142,20 +174,27 @@ struct Nios2CPUClass {
|
|||
#define EXCP_UNALIGN 6
|
||||
#define EXCP_UNALIGND 7
|
||||
#define EXCP_DIV 8
|
||||
#define EXCP_SUPERA 9
|
||||
#define EXCP_SUPERA_X 9
|
||||
#define EXCP_SUPERI 10
|
||||
#define EXCP_SUPERD 11
|
||||
#define EXCP_TLBD 12
|
||||
#define EXCP_TLBX 13
|
||||
#define EXCP_TLBR 14
|
||||
#define EXCP_TLBW 15
|
||||
#define EXCP_SUPERA_D 11
|
||||
#define EXCP_TLB_X 12
|
||||
#define EXCP_TLB_D (0x1000 | EXCP_TLB_X)
|
||||
#define EXCP_PERM_X 13
|
||||
#define EXCP_PERM_R 14
|
||||
#define EXCP_PERM_W 15
|
||||
#define EXCP_MPUI 16
|
||||
#define EXCP_MPUD 17
|
||||
|
||||
#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
|
||||
|
||||
struct CPUArchState {
|
||||
uint32_t regs[NUM_CORE_REGS];
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
uint32_t regs[NUM_GP_REGS];
|
||||
#else
|
||||
uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS];
|
||||
/* Pointer into shadow_regs for the current register set. */
|
||||
uint32_t *regs;
|
||||
#endif
|
||||
uint32_t ctrl[NUM_CR_REGS];
|
||||
uint32_t pc;
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
Nios2MMU mmu;
|
||||
|
@ -163,6 +202,11 @@ struct CPUArchState {
|
|||
int error_code;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t writable;
|
||||
uint32_t readonly;
|
||||
} ControlRegState;
|
||||
|
||||
/**
|
||||
* Nios2CPU:
|
||||
* @env: #CPUNios2State
|
||||
|
@ -177,7 +221,10 @@ struct ArchCPU {
|
|||
CPUNegativeOffsetState neg;
|
||||
CPUNios2State env;
|
||||
|
||||
bool diverr_present;
|
||||
bool mmu_present;
|
||||
bool eic_present;
|
||||
|
||||
uint32_t pid_num_bits;
|
||||
uint32_t tlb_num_ways;
|
||||
uint32_t tlb_num_entries;
|
||||
|
@ -186,9 +233,31 @@ struct ArchCPU {
|
|||
uint32_t reset_addr;
|
||||
uint32_t exception_addr;
|
||||
uint32_t fast_tlb_miss_addr;
|
||||
|
||||
/* Bits within each control register which are reserved or readonly. */
|
||||
ControlRegState cr_state[NUM_CR_REGS];
|
||||
|
||||
/* External Interrupt Controller Interface */
|
||||
uint32_t rha; /* Requested handler address */
|
||||
uint32_t ril; /* Requested interrupt level */
|
||||
uint32_t rrs; /* Requested register set */
|
||||
bool rnmi; /* Requested nonmaskable interrupt */
|
||||
};
|
||||
|
||||
|
||||
static inline bool nios2_cr_reserved(const ControlRegState *s)
|
||||
{
|
||||
return (s->writable | s->readonly) == 0;
|
||||
}
|
||||
|
||||
static inline void nios2_update_crs(CPUNios2State *env)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
|
||||
env->regs = env->shadow_regs[crs];
|
||||
#endif
|
||||
}
|
||||
|
||||
void nios2_tcg_init(void);
|
||||
void nios2_cpu_do_interrupt(CPUState *cs);
|
||||
void dump_mmu(CPUNios2State *env);
|
||||
|
@ -197,6 +266,8 @@ hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
|||
G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
uintptr_t retaddr);
|
||||
G_NORETURN void nios2_cpu_loop_exit_advance(CPUNios2State *env,
|
||||
uintptr_t retaddr);
|
||||
|
||||
void do_nios2_semihosting(CPUNios2State *env);
|
||||
|
||||
|
@ -212,36 +283,35 @@ void do_nios2_semihosting(CPUNios2State *env);
|
|||
|
||||
static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
|
||||
{
|
||||
return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
|
||||
return (env->ctrl[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
|
||||
MMU_SUPERVISOR_IDX;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
void nios2_cpu_record_sigsegv(CPUState *cpu, vaddr addr,
|
||||
MMUAccessType access_type,
|
||||
bool maperr, uintptr_t ra);
|
||||
#else
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
bool probe, uintptr_t retaddr);
|
||||
#endif
|
||||
|
||||
static inline int cpu_interrupts_enabled(CPUNios2State *env)
|
||||
{
|
||||
return env->regs[CR_STATUS] & CR_STATUS_PIE;
|
||||
}
|
||||
|
||||
typedef CPUNios2State CPUArchState;
|
||||
typedef Nios2CPU ArchCPU;
|
||||
|
||||
#include "exec/cpu-all.h"
|
||||
|
||||
FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */
|
||||
FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */
|
||||
FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */
|
||||
|
||||
static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
|
||||
target_ulong *cs_base, uint32_t *flags)
|
||||
{
|
||||
*pc = env->regs[R_PC];
|
||||
unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
|
||||
|
||||
*pc = env->pc;
|
||||
*cs_base = 0;
|
||||
*flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
|
||||
*flags = (env->ctrl[CR_STATUS] & CR_STATUS_U)
|
||||
| (crs ? 0 : R_TBFLAGS_CRS0_MASK)
|
||||
| (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK);
|
||||
}
|
||||
|
||||
#endif /* NIOS2_CPU_H */
|
||||
|
|
|
@ -28,176 +28,234 @@
|
|||
#include "exec/helper-proto.h"
|
||||
#include "semihosting/semihost.h"
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
||||
static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
|
||||
uint32_t tlbmisc_set, bool is_break)
|
||||
{
|
||||
CPUNios2State *env = &cpu->env;
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint32_t old_status = env->ctrl[CR_STATUS];
|
||||
uint32_t new_status = old_status;
|
||||
|
||||
/* With shadow regs, exceptions are always taken into CRS 0. */
|
||||
new_status &= ~R_CR_STATUS_CRS_MASK;
|
||||
env->regs = env->shadow_regs[0];
|
||||
|
||||
if ((old_status & CR_STATUS_EH) == 0) {
|
||||
int r_ea = R_EA, cr_es = CR_ESTATUS;
|
||||
|
||||
if (is_break) {
|
||||
r_ea = R_BA;
|
||||
cr_es = CR_BSTATUS;
|
||||
}
|
||||
env->ctrl[cr_es] = old_status;
|
||||
env->regs[r_ea] = env->pc;
|
||||
|
||||
if (cpu->mmu_present) {
|
||||
new_status |= CR_STATUS_EH;
|
||||
|
||||
/*
|
||||
* There are 4 bits that are always written.
|
||||
* Explicitly clear them, to be set via the argument.
|
||||
*/
|
||||
env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
|
||||
CR_TLBMISC_PERM |
|
||||
CR_TLBMISC_BAD |
|
||||
CR_TLBMISC_DBL);
|
||||
env->ctrl[CR_TLBMISC] |= tlbmisc_set;
|
||||
}
|
||||
|
||||
/*
|
||||
* With shadow regs, and EH == 0, PRS is set from CRS.
|
||||
* At least, so says Table 3-9, and some other text,
|
||||
* though Table 3-38 says otherwise.
|
||||
*/
|
||||
new_status = FIELD_DP32(new_status, CR_STATUS, PRS,
|
||||
FIELD_EX32(old_status, CR_STATUS, CRS));
|
||||
}
|
||||
|
||||
new_status &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||
|
||||
env->ctrl[CR_STATUS] = new_status;
|
||||
if (!is_break) {
|
||||
env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE,
|
||||
cs->exception_index);
|
||||
}
|
||||
env->pc = exception_addr;
|
||||
}
|
||||
|
||||
static void do_iic_irq(Nios2CPU *cpu)
|
||||
{
|
||||
do_exception(cpu, cpu->exception_addr, 0, false);
|
||||
}
|
||||
|
||||
static void do_eic_irq(Nios2CPU *cpu)
|
||||
{
|
||||
CPUNios2State *env = &cpu->env;
|
||||
uint32_t old_status = env->ctrl[CR_STATUS];
|
||||
uint32_t new_status = old_status;
|
||||
uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS);
|
||||
uint32_t new_rs = cpu->rrs;
|
||||
|
||||
new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs);
|
||||
new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril);
|
||||
new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi);
|
||||
new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U);
|
||||
new_status |= CR_STATUS_IH;
|
||||
|
||||
if (!(new_status & CR_STATUS_EH)) {
|
||||
new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs);
|
||||
if (new_rs == 0) {
|
||||
env->ctrl[CR_ESTATUS] = old_status;
|
||||
} else {
|
||||
if (new_rs != old_rs) {
|
||||
old_status |= CR_STATUS_SRS;
|
||||
}
|
||||
env->shadow_regs[new_rs][R_SSTATUS] = old_status;
|
||||
}
|
||||
env->shadow_regs[new_rs][R_EA] = env->pc;
|
||||
}
|
||||
|
||||
env->ctrl[CR_STATUS] = new_status;
|
||||
nios2_update_crs(env);
|
||||
|
||||
env->pc = cpu->rha;
|
||||
}
|
||||
|
||||
void nios2_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
CPUNios2State *env = &cpu->env;
|
||||
cs->exception_index = -1;
|
||||
env->regs[R_EA] = env->regs[R_PC] + 4;
|
||||
}
|
||||
uint32_t tlbmisc_set = 0;
|
||||
|
||||
void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr,
|
||||
MMUAccessType access_type,
|
||||
bool maperr, uintptr_t retaddr)
|
||||
{
|
||||
/* FIXME: Disentangle kuser page from linux-user sigsegv handling. */
|
||||
cs->exception_index = 0xaa;
|
||||
cpu_loop_exit_restore(cs, retaddr);
|
||||
}
|
||||
if (qemu_loglevel_mask(CPU_LOG_INT)) {
|
||||
const char *name = NULL;
|
||||
|
||||
#else /* !CONFIG_USER_ONLY */
|
||||
|
||||
void nios2_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
CPUNios2State *env = &cpu->env;
|
||||
switch (cs->exception_index) {
|
||||
case EXCP_IRQ:
|
||||
name = "interrupt";
|
||||
break;
|
||||
case EXCP_TLB_X:
|
||||
case EXCP_TLB_D:
|
||||
if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
|
||||
name = "TLB MISS (double)";
|
||||
} else {
|
||||
name = "TLB MISS (fast)";
|
||||
}
|
||||
break;
|
||||
case EXCP_PERM_R:
|
||||
case EXCP_PERM_W:
|
||||
case EXCP_PERM_X:
|
||||
name = "TLB PERM";
|
||||
break;
|
||||
case EXCP_SUPERA_X:
|
||||
case EXCP_SUPERA_D:
|
||||
name = "SUPERVISOR (address)";
|
||||
break;
|
||||
case EXCP_SUPERI:
|
||||
name = "SUPERVISOR (insn)";
|
||||
break;
|
||||
case EXCP_ILLEGAL:
|
||||
name = "ILLEGAL insn";
|
||||
break;
|
||||
case EXCP_UNALIGN:
|
||||
name = "Misaligned (data)";
|
||||
break;
|
||||
case EXCP_UNALIGND:
|
||||
name = "Misaligned (destination)";
|
||||
break;
|
||||
case EXCP_DIV:
|
||||
name = "DIV error";
|
||||
break;
|
||||
case EXCP_TRAP:
|
||||
name = "TRAP insn";
|
||||
break;
|
||||
case EXCP_BREAK:
|
||||
name = "BREAK insn";
|
||||
break;
|
||||
case EXCP_SEMIHOST:
|
||||
name = "SEMIHOST insn";
|
||||
break;
|
||||
}
|
||||
if (name) {
|
||||
qemu_log("%s at pc=0x%08x\n", name, env->pc);
|
||||
} else {
|
||||
qemu_log("Unknown exception %d at pc=0x%08x\n",
|
||||
cs->exception_index, env->pc);
|
||||
}
|
||||
}
|
||||
|
||||
switch (cs->exception_index) {
|
||||
case EXCP_IRQ:
|
||||
assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
|
||||
|
||||
env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
|
||||
env->regs[CR_STATUS] |= CR_STATUS_IH;
|
||||
env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||
|
||||
env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
|
||||
env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
|
||||
|
||||
env->regs[R_EA] = env->regs[R_PC] + 4;
|
||||
env->regs[R_PC] = cpu->exception_addr;
|
||||
break;
|
||||
|
||||
case EXCP_TLBD:
|
||||
if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
|
||||
qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
|
||||
env->regs[R_PC]);
|
||||
|
||||
/* Fast TLB miss */
|
||||
/* Variation from the spec. Table 3-35 of the cpu reference shows
|
||||
* estatus not being changed for TLB miss but this appears to
|
||||
* be incorrect. */
|
||||
env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
|
||||
env->regs[CR_STATUS] |= CR_STATUS_EH;
|
||||
env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||
|
||||
env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
|
||||
env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
|
||||
|
||||
env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
|
||||
env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
|
||||
|
||||
env->regs[R_EA] = env->regs[R_PC] + 4;
|
||||
env->regs[R_PC] = cpu->fast_tlb_miss_addr;
|
||||
/* Note that PC is advanced for interrupts as well. */
|
||||
env->pc += 4;
|
||||
if (cpu->eic_present) {
|
||||
do_eic_irq(cpu);
|
||||
} else {
|
||||
qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
|
||||
env->regs[R_PC]);
|
||||
|
||||
/* Double TLB miss */
|
||||
env->regs[CR_STATUS] |= CR_STATUS_EH;
|
||||
env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||
|
||||
env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
|
||||
env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
|
||||
|
||||
env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
|
||||
|
||||
env->regs[R_PC] = cpu->exception_addr;
|
||||
do_iic_irq(cpu);
|
||||
}
|
||||
break;
|
||||
|
||||
case EXCP_TLBR:
|
||||
case EXCP_TLBW:
|
||||
case EXCP_TLBX:
|
||||
qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
|
||||
|
||||
env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
|
||||
env->regs[CR_STATUS] |= CR_STATUS_EH;
|
||||
env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||
|
||||
env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
|
||||
env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
|
||||
|
||||
if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
|
||||
env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
|
||||
case EXCP_TLB_D:
|
||||
tlbmisc_set = CR_TLBMISC_D;
|
||||
/* fall through */
|
||||
case EXCP_TLB_X:
|
||||
if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
|
||||
tlbmisc_set |= CR_TLBMISC_DBL;
|
||||
/*
|
||||
* Normally, we don't write to tlbmisc unless !EH,
|
||||
* so do it manually for the double-tlb miss exception.
|
||||
*/
|
||||
env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
|
||||
CR_TLBMISC_PERM |
|
||||
CR_TLBMISC_BAD);
|
||||
env->ctrl[CR_TLBMISC] |= tlbmisc_set;
|
||||
do_exception(cpu, cpu->exception_addr, 0, false);
|
||||
} else {
|
||||
tlbmisc_set |= CR_TLBMISC_WE;
|
||||
do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false);
|
||||
}
|
||||
|
||||
env->regs[R_EA] = env->regs[R_PC] + 4;
|
||||
env->regs[R_PC] = cpu->exception_addr;
|
||||
break;
|
||||
|
||||
case EXCP_SUPERA:
|
||||
case EXCP_PERM_R:
|
||||
case EXCP_PERM_W:
|
||||
tlbmisc_set = CR_TLBMISC_D;
|
||||
/* fall through */
|
||||
case EXCP_PERM_X:
|
||||
tlbmisc_set |= CR_TLBMISC_PERM;
|
||||
if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) {
|
||||
tlbmisc_set |= CR_TLBMISC_WE;
|
||||
}
|
||||
do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
|
||||
break;
|
||||
|
||||
case EXCP_SUPERA_D:
|
||||
case EXCP_UNALIGN:
|
||||
tlbmisc_set = CR_TLBMISC_D;
|
||||
/* fall through */
|
||||
case EXCP_SUPERA_X:
|
||||
case EXCP_UNALIGND:
|
||||
tlbmisc_set |= CR_TLBMISC_BAD;
|
||||
do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
|
||||
break;
|
||||
|
||||
case EXCP_SUPERI:
|
||||
case EXCP_SUPERD:
|
||||
qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
|
||||
env->regs[R_PC]);
|
||||
|
||||
if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
|
||||
env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
|
||||
env->regs[R_EA] = env->regs[R_PC] + 4;
|
||||
}
|
||||
|
||||
env->regs[CR_STATUS] |= CR_STATUS_EH;
|
||||
env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||
|
||||
env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
|
||||
env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
|
||||
|
||||
env->regs[R_PC] = cpu->exception_addr;
|
||||
break;
|
||||
|
||||
case EXCP_ILLEGAL:
|
||||
case EXCP_DIV:
|
||||
case EXCP_TRAP:
|
||||
qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
|
||||
env->regs[R_PC]);
|
||||
|
||||
if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
|
||||
env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
|
||||
env->regs[R_EA] = env->regs[R_PC] + 4;
|
||||
}
|
||||
|
||||
env->regs[CR_STATUS] |= CR_STATUS_EH;
|
||||
env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||
|
||||
env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
|
||||
env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
|
||||
|
||||
env->regs[R_PC] = cpu->exception_addr;
|
||||
do_exception(cpu, cpu->exception_addr, 0, false);
|
||||
break;
|
||||
|
||||
case EXCP_BREAK:
|
||||
qemu_log_mask(CPU_LOG_INT, "BREAK exception at pc=%x\n",
|
||||
env->regs[R_PC]);
|
||||
/* The semihosting instruction is "break 1". */
|
||||
if (semihosting_enabled() &&
|
||||
cpu_ldl_code(env, env->regs[R_PC]) == 0x003da07a) {
|
||||
qemu_log_mask(CPU_LOG_INT, "Entering semihosting\n");
|
||||
env->regs[R_PC] += 4;
|
||||
do_nios2_semihosting(env);
|
||||
break;
|
||||
}
|
||||
do_exception(cpu, cpu->exception_addr, 0, true);
|
||||
break;
|
||||
|
||||
if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
|
||||
env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
|
||||
env->regs[R_BA] = env->regs[R_PC] + 4;
|
||||
}
|
||||
|
||||
env->regs[CR_STATUS] |= CR_STATUS_EH;
|
||||
env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||
|
||||
env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
|
||||
env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
|
||||
|
||||
env->regs[R_PC] = cpu->exception_addr;
|
||||
case EXCP_SEMIHOST:
|
||||
do_nios2_semihosting(env);
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_abort(cs, "unhandled exception type=%d\n",
|
||||
cs->exception_index);
|
||||
break;
|
||||
cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,9 +290,9 @@ void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
|||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
CPUNios2State *env = &cpu->env;
|
||||
|
||||
env->regs[CR_BADADDR] = addr;
|
||||
env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
|
||||
helper_raise_exception(env, EXCP_UNALIGN);
|
||||
env->ctrl[CR_BADADDR] = addr;
|
||||
cs->exception_index = EXCP_UNALIGN;
|
||||
nios2_cpu_loop_exit_advance(env, retaddr);
|
||||
}
|
||||
|
||||
bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
|
@ -243,7 +301,7 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
|||
{
|
||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||
CPUNios2State *env = &cpu->env;
|
||||
unsigned int excp = EXCP_TLBD;
|
||||
unsigned int excp;
|
||||
target_ulong vaddr, paddr;
|
||||
Nios2MMULookup lu;
|
||||
unsigned int hit;
|
||||
|
@ -270,9 +328,10 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
|||
if (probe) {
|
||||
return false;
|
||||
}
|
||||
cs->exception_index = EXCP_SUPERA;
|
||||
env->regs[CR_BADADDR] = address;
|
||||
cpu_loop_exit_restore(cs, retaddr);
|
||||
cs->exception_index = (access_type == MMU_INST_FETCH
|
||||
? EXCP_SUPERA_X : EXCP_SUPERA_D);
|
||||
env->ctrl[CR_BADADDR] = address;
|
||||
nios2_cpu_loop_exit_advance(env, retaddr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,25 +350,23 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
|||
}
|
||||
|
||||
/* Permission violation */
|
||||
excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR :
|
||||
access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX);
|
||||
excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R :
|
||||
access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X);
|
||||
} else {
|
||||
excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D);
|
||||
}
|
||||
|
||||
if (probe) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (access_type == MMU_INST_FETCH) {
|
||||
env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
|
||||
} else {
|
||||
env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
|
||||
}
|
||||
env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
|
||||
env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
|
||||
env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
|
||||
env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D,
|
||||
access_type != MMU_INST_FETCH);
|
||||
env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN,
|
||||
address >> TARGET_PAGE_BITS);
|
||||
env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR];
|
||||
|
||||
cs->exception_index = excp;
|
||||
env->regs[CR_BADADDR] = address;
|
||||
cpu_loop_exit_restore(cs, retaddr);
|
||||
env->ctrl[CR_BADADDR] = address;
|
||||
nios2_cpu_loop_exit_advance(env, retaddr);
|
||||
}
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
|
|
@ -19,8 +19,13 @@
|
|||
*/
|
||||
|
||||
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32)
|
||||
DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, s32, env, s32, s32)
|
||||
DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32)
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
DEF_HELPER_3(eret, noreturn, env, i32, i32)
|
||||
DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32)
|
||||
DEF_HELPER_3(wrprs, void, env, i32, i32)
|
||||
DEF_HELPER_2(mmu_write_tlbacc, void, env, i32)
|
||||
DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32)
|
||||
DEF_HELPER_2(mmu_write_pteaddr, void, env, i32)
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
nios2_ss = ss.source_set()
|
||||
nios2_ss.add(files(
|
||||
'cpu.c',
|
||||
'helper.c',
|
||||
'nios2-semi.c',
|
||||
'op_helper.c',
|
||||
'translate.c',
|
||||
))
|
||||
|
||||
nios2_softmmu_ss = ss.source_set()
|
||||
nios2_softmmu_ss.add(files('monitor.c', 'mmu.c'))
|
||||
nios2_softmmu_ss.add(files(
|
||||
'helper.c',
|
||||
'monitor.c',
|
||||
'mmu.c'
|
||||
))
|
||||
|
||||
target_arch += {'nios2': nios2_ss}
|
||||
target_softmmu_arch += {'nios2': nios2_softmmu_ss}
|
||||
|
|
|
@ -33,7 +33,7 @@ unsigned int mmu_translate(CPUNios2State *env,
|
|||
target_ulong vaddr, int rw, int mmu_idx)
|
||||
{
|
||||
Nios2CPU *cpu = env_archcpu(env);
|
||||
int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
|
||||
int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
|
||||
int vpn = vaddr >> 12;
|
||||
int way, n_ways = cpu->tlb_num_ways;
|
||||
|
||||
|
@ -49,7 +49,7 @@ unsigned int mmu_translate(CPUNios2State *env,
|
|||
}
|
||||
|
||||
lu->vaddr = vaddr & TARGET_PAGE_MASK;
|
||||
lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
|
||||
lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS;
|
||||
lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
|
||||
((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
|
||||
((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
|
||||
|
@ -86,27 +86,27 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
|
|||
CPUState *cs = env_cpu(env);
|
||||
Nios2CPU *cpu = env_archcpu(env);
|
||||
|
||||
trace_nios2_mmu_write_tlbacc(v >> CR_TLBACC_IGN_SHIFT,
|
||||
trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG),
|
||||
(v & CR_TLBACC_C) ? 'C' : '.',
|
||||
(v & CR_TLBACC_R) ? 'R' : '.',
|
||||
(v & CR_TLBACC_W) ? 'W' : '.',
|
||||
(v & CR_TLBACC_X) ? 'X' : '.',
|
||||
(v & CR_TLBACC_G) ? 'G' : '.',
|
||||
v & CR_TLBACC_PFN_MASK);
|
||||
FIELD_EX32(v, CR_TLBACC, PFN));
|
||||
|
||||
/* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
|
||||
if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
|
||||
int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
|
||||
int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
|
||||
int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
|
||||
int g = (v & CR_TLBACC_G) ? 1 : 0;
|
||||
int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
|
||||
if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) {
|
||||
int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY);
|
||||
int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN);
|
||||
int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
|
||||
int g = FIELD_EX32(v, CR_TLBACC, G);
|
||||
int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000;
|
||||
Nios2TLBEntry *entry =
|
||||
&env->mmu.tlb[(way * cpu->tlb_num_ways) +
|
||||
(vpn & env->mmu.tlb_entry_mask)];
|
||||
uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
|
||||
uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
|
||||
CR_TLBACC_X | CR_TLBACC_PFN_MASK);
|
||||
CR_TLBACC_X | R_CR_TLBACC_PFN_MASK);
|
||||
|
||||
if ((entry->tag != newTag) || (entry->data != newData)) {
|
||||
if (entry->tag & (1 << 10)) {
|
||||
|
@ -117,10 +117,9 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
|
|||
entry->data = newData;
|
||||
}
|
||||
/* Auto-increment tlbmisc.WAY */
|
||||
env->regs[CR_TLBMISC] =
|
||||
(env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
|
||||
(((way + 1) & (cpu->tlb_num_ways - 1)) <<
|
||||
CR_TLBMISC_WAY_SHIFT);
|
||||
env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC],
|
||||
CR_TLBMISC, WAY,
|
||||
(way + 1) & (cpu->tlb_num_ways - 1));
|
||||
}
|
||||
|
||||
/* Writes to TLBACC don't change the read-back value */
|
||||
|
@ -130,40 +129,41 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
|
|||
void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v)
|
||||
{
|
||||
Nios2CPU *cpu = env_archcpu(env);
|
||||
uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID);
|
||||
uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
|
||||
uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY);
|
||||
|
||||
trace_nios2_mmu_write_tlbmisc(v >> CR_TLBMISC_WAY_SHIFT,
|
||||
trace_nios2_mmu_write_tlbmisc(way,
|
||||
(v & CR_TLBMISC_RD) ? 'R' : '.',
|
||||
(v & CR_TLBMISC_WR) ? 'W' : '.',
|
||||
(v & CR_TLBMISC_WE) ? 'W' : '.',
|
||||
(v & CR_TLBMISC_DBL) ? '2' : '.',
|
||||
(v & CR_TLBMISC_BAD) ? 'B' : '.',
|
||||
(v & CR_TLBMISC_PERM) ? 'P' : '.',
|
||||
(v & CR_TLBMISC_D) ? 'D' : '.',
|
||||
(v & CR_TLBMISC_PID_MASK) >> 4);
|
||||
new_pid);
|
||||
|
||||
if ((v & CR_TLBMISC_PID_MASK) !=
|
||||
(env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
|
||||
mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
|
||||
CR_TLBMISC_PID_SHIFT);
|
||||
if (new_pid != old_pid) {
|
||||
mmu_flush_pid(env, old_pid);
|
||||
}
|
||||
|
||||
/* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
|
||||
if (v & CR_TLBMISC_RD) {
|
||||
int way = (v >> CR_TLBMISC_WAY_SHIFT);
|
||||
int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
|
||||
int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN);
|
||||
Nios2TLBEntry *entry =
|
||||
&env->mmu.tlb[(way * cpu->tlb_num_ways) +
|
||||
(vpn & env->mmu.tlb_entry_mask)];
|
||||
|
||||
env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
|
||||
env->regs[CR_TLBACC] |= entry->data;
|
||||
env->regs[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
|
||||
env->regs[CR_TLBMISC] =
|
||||
(v & ~CR_TLBMISC_PID_MASK) |
|
||||
((entry->tag & ((1 << cpu->pid_num_bits) - 1)) <<
|
||||
CR_TLBMISC_PID_SHIFT);
|
||||
env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
|
||||
env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
|
||||
env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK;
|
||||
env->ctrl[CR_TLBACC] |= entry->data;
|
||||
env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
|
||||
env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID,
|
||||
entry->tag &
|
||||
((1 << cpu->pid_num_bits) - 1));
|
||||
env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR],
|
||||
CR_PTEADDR, VPN,
|
||||
entry->tag >> TARGET_PAGE_BITS);
|
||||
} else {
|
||||
env->regs[CR_TLBMISC] = v;
|
||||
env->ctrl[CR_TLBMISC] = v;
|
||||
}
|
||||
|
||||
env->mmu.tlbmisc_wr = v;
|
||||
|
@ -171,12 +171,12 @@ void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v)
|
|||
|
||||
void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v)
|
||||
{
|
||||
trace_nios2_mmu_write_pteaddr(v >> CR_PTEADDR_PTBASE_SHIFT,
|
||||
(v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT);
|
||||
trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE),
|
||||
FIELD_EX32(v, CR_PTEADDR, VPN));
|
||||
|
||||
/* Writes to PTEADDR don't change the read-back VPN value */
|
||||
env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
|
||||
(env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
|
||||
env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) |
|
||||
(env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK));
|
||||
env->mmu.pteaddr_wr = v;
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ void dump_mmu(CPUNios2State *env)
|
|||
entry->tag >> 12,
|
||||
entry->tag & ((1 << cpu->pid_num_bits) - 1),
|
||||
(entry->tag & (1 << 11)) ? 'G' : '-',
|
||||
entry->data & CR_TLBACC_PFN_MASK,
|
||||
FIELD_EX32(entry->data, CR_TLBACC, PFN),
|
||||
(entry->data & CR_TLBACC_C) ? 'C' : '-',
|
||||
(entry->data & CR_TLBACC_R) ? 'R' : '-',
|
||||
(entry->data & CR_TLBACC_W) ? 'W' : '-',
|
||||
|
|
|
@ -30,3 +30,91 @@ void helper_raise_exception(CPUNios2State *env, uint32_t index)
|
|||
cs->exception_index = index;
|
||||
cpu_loop_exit(cs);
|
||||
}
|
||||
|
||||
void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
/*
|
||||
* Note that PC is advanced for all hardware exceptions.
|
||||
* Do this here, rather than in restore_state_to_opc(),
|
||||
* lest we affect QEMU internal exceptions, like EXCP_DEBUG.
|
||||
*/
|
||||
cpu_restore_state(cs, retaddr, true);
|
||||
env->pc += 4;
|
||||
cpu_loop_exit(cs);
|
||||
}
|
||||
|
||||
static void maybe_raise_div(CPUNios2State *env, uintptr_t ra)
|
||||
{
|
||||
Nios2CPU *cpu = env_archcpu(env);
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
if (cpu->diverr_present) {
|
||||
cs->exception_index = EXCP_DIV;
|
||||
nios2_cpu_loop_exit_advance(env, ra);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t helper_divs(CPUNios2State *env, int32_t num, int32_t den)
|
||||
{
|
||||
if (unlikely(den == 0) || unlikely(den == -1 && num == INT32_MIN)) {
|
||||
maybe_raise_div(env, GETPC());
|
||||
return num; /* undefined */
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
|
||||
uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den)
|
||||
{
|
||||
if (unlikely(den == 0)) {
|
||||
maybe_raise_div(env, GETPC());
|
||||
return num; /* undefined */
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
|
||||
{
|
||||
Nios2CPU *cpu = env_archcpu(env);
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
if (unlikely(new_pc & 3)) {
|
||||
env->ctrl[CR_BADADDR] = new_pc;
|
||||
cs->exception_index = EXCP_UNALIGND;
|
||||
nios2_cpu_loop_exit_advance(env, GETPC());
|
||||
}
|
||||
|
||||
/*
|
||||
* None of estatus, bstatus, or sstatus have constraints on write;
|
||||
* do not allow reserved fields in status to be set.
|
||||
* When shadow registers are enabled, eret *does* restore CRS.
|
||||
* Rather than testing eic_present to decide, mask CRS out of
|
||||
* the set of readonly fields.
|
||||
*/
|
||||
new_status &= cpu->cr_state[CR_STATUS].writable |
|
||||
(cpu->cr_state[CR_STATUS].readonly & R_CR_STATUS_CRS_MASK);
|
||||
|
||||
env->ctrl[CR_STATUS] = new_status;
|
||||
env->pc = new_pc;
|
||||
nios2_update_crs(env);
|
||||
cpu_loop_exit(cs);
|
||||
}
|
||||
|
||||
/*
|
||||
* RDPRS and WRPRS are implemented out of line so that if PRS == CRS,
|
||||
* all of the tcg global temporaries are synced back to ENV.
|
||||
*/
|
||||
uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno)
|
||||
{
|
||||
unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
|
||||
return env->shadow_regs[prs][regno];
|
||||
}
|
||||
|
||||
void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val)
|
||||
{
|
||||
unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
|
||||
env->shadow_regs[prs][regno] = val;
|
||||
}
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Link script for the Nios2 10m50-ghrd board.
|
||||
*
|
||||
* Copyright Linaro Ltd 2022
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
tpf (rx) : ORIGIN = 0xc0000000, LENGTH = 1K
|
||||
ram (rwx) : ORIGIN = 0xc8000000, LENGTH = 128M
|
||||
}
|
||||
|
||||
PHDRS
|
||||
{
|
||||
RAM PT_LOAD;
|
||||
}
|
||||
|
||||
ENTRY(_start)
|
||||
EXTERN(_start)
|
||||
EXTERN(_interrupt)
|
||||
EXTERN(_fast_tlb_miss)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Begin at the (hardcoded) _interrupt entry point. */
|
||||
.text 0xc8000120 : {
|
||||
*(.text.intr)
|
||||
*(.text .text.* .gnu.linkonce.t.*)
|
||||
} >ram :RAM
|
||||
|
||||
.rodata : ALIGN(4) {
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
} > ram :RAM
|
||||
|
||||
.eh_frame_hdr : ALIGN (4) {
|
||||
KEEP (*(.eh_frame_hdr))
|
||||
*(.eh_frame_entry .eh_frame_entry.*)
|
||||
} >ram :RAM
|
||||
.eh_frame : ALIGN (4) {
|
||||
KEEP (*(.eh_frame)) *(.eh_frame.*)
|
||||
} >ram :RAM
|
||||
|
||||
.data : ALIGN(4) {
|
||||
*(.shdata)
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
. = ALIGN(4);
|
||||
_gp = ABSOLUTE(. + 0x8000);
|
||||
*(.got.plt) *(.got)
|
||||
*(.lit8)
|
||||
*(.lit4)
|
||||
*(.sdata .sdata.* .gnu.linkonce.s.*)
|
||||
} >ram :RAM
|
||||
|
||||
.bss : ALIGN(4) {
|
||||
__bss_start = ABSOLUTE(.);
|
||||
*(.sbss .sbss.* .gnu.linkonce.sb.*)
|
||||
*(.scommon)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
__bss_end = ABSOLUTE(.);
|
||||
} >ram :RAM
|
||||
|
||||
__stack = ORIGIN(ram) + LENGTH(ram);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# Nios2 system tests
|
||||
#
|
||||
# Copyright Linaro Ltd 2022
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
|
||||
NIOS2_SYSTEM_SRC = $(SRC_PATH)/tests/tcg/nios2
|
||||
VPATH += $(NIOS2_SYSTEM_SRC)
|
||||
|
||||
# These objects provide the basic boot code and helper functions for all tests
|
||||
CRT_OBJS = boot.o intr.o $(MINILIB_OBJS)
|
||||
LINK_SCRIPT = $(NIOS2_SYSTEM_SRC)/10m50-ghrd.ld
|
||||
|
||||
CFLAGS += -nostdlib -g -O0 $(MINILIB_INC)
|
||||
LDFLAGS += -Wl,-T$(LINK_SCRIPT) -static -nostdlib $(CRT_OBJS) -lgcc
|
||||
|
||||
%.o: %.S
|
||||
$(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@, AS, $@)
|
||||
|
||||
%.o: %.c
|
||||
$(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@, CC, $@)
|
||||
|
||||
# Build and link the tests
|
||||
%: %.o $(LINK_SCRIPT) $(CRT_OBJS)
|
||||
$(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS), LD, $@)
|
||||
|
||||
# FIXME: nios2 semihosting writes to stdout, not a chardev
|
||||
QEMU_OPTS = -M 10m50-ghrd,vic=on -semihosting >$@.out -kernel
|
||||
|
||||
memory: CFLAGS+=-DCHECK_UNALIGNED=0
|
||||
TESTS += $(MULTIARCH_TESTS)
|
||||
TESTS += test-shadow-1
|
|
@ -1,11 +0,0 @@
|
|||
# nios2 specific test tweaks
|
||||
|
||||
# Currently nios2 signal handling is broken
|
||||
run-signals: signals
|
||||
$(call skip-test, $<, "BROKEN")
|
||||
run-plugin-signals-with-%:
|
||||
$(call skip-test, $<, "BROKEN")
|
||||
run-linux-test: linux-test
|
||||
$(call skip-test, $<, "BROKEN")
|
||||
run-plugin-linux-test-with-%:
|
||||
$(call skip-test, $<, "BROKEN")
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Minimal Nios2 system boot code.
|
||||
*
|
||||
* Copyright Linaro Ltd 2022
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "semicall.h"
|
||||
|
||||
.text
|
||||
.set noat
|
||||
|
||||
_start:
|
||||
/* Linker script defines stack at end of ram. */
|
||||
movia sp, __stack
|
||||
|
||||
/* Install trampoline to _fast_tlb_miss at hardcoded vector. */
|
||||
movia r4, 0xc0000100
|
||||
movia r5, _ftm_tramp
|
||||
movi r6, .L__ftm_end - _ftm_tramp
|
||||
call memcpy
|
||||
|
||||
/* Zero the bss to satisfy C. */
|
||||
movia r4, __bss_start
|
||||
movia r6, __bss_end
|
||||
sub r6, r6, r4
|
||||
movi r5, 0
|
||||
call memset
|
||||
|
||||
/* Test! */
|
||||
call main
|
||||
|
||||
/* Exit with main's return value. */
|
||||
movi r4, HOSTED_EXIT
|
||||
mov r5, r2
|
||||
semihosting_call
|
||||
|
||||
.globl _start
|
||||
.type _start, @function
|
||||
.size _start, . - _start
|
||||
|
||||
_ftm_tramp:
|
||||
movia et, _fast_tlb_miss
|
||||
jmp et
|
||||
.L__ftm_end:
|
||||
|
||||
.type _ftm_tramp, @function
|
||||
.size _ftm_tramp, . - _ftm_tramp
|
||||
|
||||
#define dst r4
|
||||
#define src r5
|
||||
#define len r6
|
||||
|
||||
memcpy:
|
||||
/* Store return value right away, per API */
|
||||
mov r2, dst
|
||||
|
||||
/* Check for both dst and src aligned. */
|
||||
or at, dst, src
|
||||
andi at, at, 3
|
||||
bne at, zero, .L_mc_test1
|
||||
|
||||
/* Copy blocks of 8. */
|
||||
|
||||
movi at, 8
|
||||
bltu len, at, .L_mc_test4
|
||||
|
||||
.L_mc_loop8:
|
||||
ldw r8, 0(src)
|
||||
ldw r9, 4(src)
|
||||
addi src, src, 8
|
||||
addi dst, dst, 8
|
||||
subi len, len, 8
|
||||
stw r8, -8(dst)
|
||||
stw r9, -4(dst)
|
||||
bgeu len, at, .L_mc_loop8
|
||||
|
||||
/* Copy final aligned block of 4. */
|
||||
|
||||
.L_mc_test4:
|
||||
movi at, 4
|
||||
bltu len, at, .L_mc_test1
|
||||
|
||||
ldw r8, 0(src)
|
||||
addi src, src, 4
|
||||
addi dst, dst, 4
|
||||
subi len, len, 4
|
||||
stw r8, -4(dst)
|
||||
|
||||
/* Copy single bytes to finish. */
|
||||
|
||||
.L_mc_test1:
|
||||
beq len, zero, .L_mc_done
|
||||
|
||||
.L_mc_loop1:
|
||||
ldb r8, 0(src)
|
||||
addi src, src, 1
|
||||
addi dst, dst, 1
|
||||
subi len, len, 1
|
||||
stb r8, -1(dst)
|
||||
bne len, zero, .L_mc_loop1
|
||||
|
||||
.L_mc_done:
|
||||
ret
|
||||
|
||||
#undef dst
|
||||
#undef src
|
||||
#undef len
|
||||
|
||||
.global memcpy
|
||||
.type memcpy, @function
|
||||
.size memcpy, . - memcpy
|
||||
|
||||
#define dst r4
|
||||
#define val r5
|
||||
#define len r6
|
||||
|
||||
memset:
|
||||
/* Store return value right away, per API */
|
||||
mov r2, dst
|
||||
|
||||
/* Check for small blocks; fall back to bytewise. */
|
||||
movi r3, 8
|
||||
bltu len, r3, .L_ms_test1
|
||||
|
||||
/* Replicate the byte across the word. */
|
||||
andi val, val, 0xff
|
||||
slli at, val, 8
|
||||
or val, val, at
|
||||
slli at, val, 16
|
||||
or val, val, at
|
||||
|
||||
/* Check for destination alignment; realign if needed. */
|
||||
andi at, dst, 3
|
||||
bne at, zero, .L_ms_align
|
||||
|
||||
/* Set blocks of 8. */
|
||||
|
||||
.L_ms_loop8:
|
||||
stw val, 0(dst)
|
||||
stw val, 4(dst)
|
||||
addi dst, dst, 8
|
||||
subi len, len, 8
|
||||
bgeu len, r3, .L_ms_loop8
|
||||
|
||||
/* Set final aligned block of 4. */
|
||||
|
||||
.L_ms_test4:
|
||||
movi at, 4
|
||||
bltu len, at, .L_ms_test1
|
||||
|
||||
stw r8, 0(dst)
|
||||
addi dst, dst, 4
|
||||
subi len, len, 4
|
||||
stw r8, -4(dst)
|
||||
|
||||
/* Set single bytes to finish. */
|
||||
|
||||
.L_ms_test1:
|
||||
beq len, zero, .L_ms_done
|
||||
|
||||
.L_ms_loop1:
|
||||
stb r8, 0(dst)
|
||||
addi dst, dst, 1
|
||||
subi len, len, 1
|
||||
bne len, zero, .L_ms_loop1
|
||||
|
||||
.L_ms_done:
|
||||
ret
|
||||
|
||||
/* Realign for a large block, len >= 8. */
|
||||
.L_ms_align:
|
||||
andi at, dst, 1
|
||||
beq at, zero, 2f
|
||||
|
||||
stb val, 0(dst)
|
||||
addi dst, dst, 1
|
||||
subi len, len, 1
|
||||
|
||||
2: andi at, dst, 2
|
||||
beq at, zero, 4f
|
||||
|
||||
sth val, 0(dst)
|
||||
addi dst, dst, 2
|
||||
subi len, len, 2
|
||||
|
||||
4: bgeu len, r3, .L_ms_loop8
|
||||
br .L_ms_test4
|
||||
|
||||
#undef dst
|
||||
#undef val
|
||||
#undef len
|
||||
|
||||
.global memset
|
||||
.type memset, @function
|
||||
.size memset, . - memset
|
||||
|
||||
/*
|
||||
* void __sys_outc(char c);
|
||||
*/
|
||||
__sys_outc:
|
||||
subi sp, sp, 16
|
||||
stb r4, 0(sp) /* buffer[0] = c */
|
||||
movi at, 1
|
||||
stw at, 4(sp) /* STDOUT_FILENO */
|
||||
stw sp, 8(sp) /* buffer */
|
||||
stw at, 12(sp) /* len */
|
||||
|
||||
movi r4, HOSTED_WRITE
|
||||
addi r5, sp, 4
|
||||
semihosting_call
|
||||
|
||||
addi sp, sp, 16
|
||||
ret
|
||||
|
||||
.global __sys_outc
|
||||
.type __sys_outc, @function
|
||||
.size __sys_outc, . - __sys_outc
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Minimal Nios2 system boot code -- exit on interrupt.
|
||||
*
|
||||
* Copyright Linaro Ltd 2022
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "semicall.h"
|
||||
|
||||
.section .text.intr, "ax"
|
||||
.global _interrupt
|
||||
.type _interrupt, @function
|
||||
|
||||
_interrupt:
|
||||
rdctl r5, exception /* extract exception.CAUSE */
|
||||
srli r5, r5, 2
|
||||
movi r4, HOSTED_EXIT
|
||||
semihosting_call
|
||||
|
||||
.size _interrupt, . - _interrupt
|
||||
|
||||
.text
|
||||
.global _fast_tlb_miss
|
||||
.type _fast_tlb_miss, @function
|
||||
|
||||
_fast_tlb_miss:
|
||||
movi r5, 32
|
||||
movi r4, HOSTED_EXIT
|
||||
semihosting_call
|
||||
|
||||
.size _fast_tlb_miss, . - _fast_tlb_miss
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Nios2 semihosting interface.
|
||||
*
|
||||
* Copyright Linaro Ltd 2022
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SEMICALL_H
|
||||
#define SEMICALL_H
|
||||
|
||||
#define HOSTED_EXIT 0
|
||||
#define HOSTED_INIT_SIM 1
|
||||
#define HOSTED_OPEN 2
|
||||
#define HOSTED_CLOSE 3
|
||||
#define HOSTED_READ 4
|
||||
#define HOSTED_WRITE 5
|
||||
#define HOSTED_LSEEK 6
|
||||
#define HOSTED_RENAME 7
|
||||
#define HOSTED_UNLINK 8
|
||||
#define HOSTED_STAT 9
|
||||
#define HOSTED_FSTAT 10
|
||||
#define HOSTED_GETTIMEOFDAY 11
|
||||
#define HOSTED_ISATTY 12
|
||||
#define HOSTED_SYSTEM 13
|
||||
|
||||
#define semihosting_call break 1
|
||||
|
||||
#endif /* SEMICALL_H */
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Regression test for TCG indirect global lowering.
|
||||
*
|
||||
* Copyright Linaro Ltd 2022
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "semicall.h"
|
||||
|
||||
.text
|
||||
.set noat
|
||||
.align 2
|
||||
.globl main
|
||||
.type main, @function
|
||||
|
||||
main:
|
||||
/* Initialize r0 in shadow register set 1. */
|
||||
movhi at, 1 /* PRS=1, CRS=0, RSIE=0, PIE=0 */
|
||||
wrctl status, at
|
||||
wrprs zero, zero
|
||||
|
||||
/* Change current register set to 1. */
|
||||
movi at, 1 << 10 /* PRS=0, CRS=1, RSIE=0, PIE=0 */
|
||||
wrctl estatus, at
|
||||
movia ea, 1f
|
||||
eret
|
||||
|
||||
/* Load address for callr, then end TB. */
|
||||
1: movia at, 3f
|
||||
br 2f
|
||||
|
||||
/* Test case! TCG abort on indirect lowering across brcond. */
|
||||
2: callr at
|
||||
|
||||
/* exit(0) */
|
||||
3: movi r4, HOSTED_EXIT
|
||||
movi r5, 0
|
||||
semihosting_call
|
||||
|
||||
.size main, . - main
|
Loading…
Reference in New Issue