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:
Richard Henderson 2022-04-26 13:12:37 -07:00
commit 88d5814e6b
26 changed files with 2113 additions and 710 deletions

View File

@ -1 +1,2 @@
TARGET_ARCH=nios2
TARGET_ALIGNED_ONLY=y

View File

@ -84,3 +84,6 @@ config GOLDFISH_PIC
config M68K_IRQC
bool
config NIOS2_VIC
bool

View File

@ -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'))

313
hw/intc/nios2_vic.c Normal file
View File

@ -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);

View File

@ -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);

View File

@ -3,6 +3,7 @@ config NIOS2_10M50
select NIOS2
select SERIAL
select ALTERA_TIMER
select NIOS2_VIC
config NIOS2_GENERIC_NOMMU
bool

View File

@ -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 */

View File

@ -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]);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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,

View File

@ -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 */

View File

@ -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 */

View File

@ -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)

View File

@ -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}

View File

@ -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' : '-',

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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")

218
tests/tcg/nios2/boot.S Normal file
View File

@ -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

31
tests/tcg/nios2/intr.S Normal file
View File

@ -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

View File

@ -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 */

View File

@ -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