Third RISC-V PR for QEMU 7.2

* Update qtest comment
 * Fix coverity issue with Ibex SPI
 * Move load_image_to_fw_cfg() to common location
 * Enable booting S-mode firmware from pflash on virt machine
 * Add disas support for vector instructions
 * Priority level fixes for PLIC
 * Fixup TLB size calculation when using PMP
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAmNJFR8ACgkQIeENKd+X
 cFTOzgf+Mg4vy3PpY/hDuYJwZyYrgcY9M/VwUFONUD5TL1ehweuEeu5NF/iJpzfP
 ywjvESxhFpGQ97zSH10IbTxQwP5fifE7JMlC4ncYTTLQYk43kiYmSM5MAbxgEC44
 PgF5/WVUWI8tDJhzfAEII17AohtTc9rzWcoXh+oLX53IB0V7qh4Eq0+Rm/i/yO5I
 oD70deU+DegHb4ka6w6k2nHEhi9IoNA0uslQrQzKVr/WQPE/1TVkmvy0u3tiFSoI
 0MFXQjCirzdJoNU+5Wq3F0ygPMupMopOnidaMR8wH9fk3pb7hzzOve5wQRM+EtIv
 W2QGnWNaiR7n3UeGWYnh7aidcJ7Dfw==
 =O3mB
 -----END PGP SIGNATURE-----

Merge tag 'pull-riscv-to-apply-20221014' of https://github.com/alistair23/qemu into staging

Third RISC-V PR for QEMU 7.2

* Update qtest comment
* Fix coverity issue with Ibex SPI
* Move load_image_to_fw_cfg() to common location
* Enable booting S-mode firmware from pflash on virt machine
* Add disas support for vector instructions
* Priority level fixes for PLIC
* Fixup TLB size calculation when using PMP

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAmNJFR8ACgkQIeENKd+X
# cFTOzgf+Mg4vy3PpY/hDuYJwZyYrgcY9M/VwUFONUD5TL1ehweuEeu5NF/iJpzfP
# ywjvESxhFpGQ97zSH10IbTxQwP5fifE7JMlC4ncYTTLQYk43kiYmSM5MAbxgEC44
# PgF5/WVUWI8tDJhzfAEII17AohtTc9rzWcoXh+oLX53IB0V7qh4Eq0+Rm/i/yO5I
# oD70deU+DegHb4ka6w6k2nHEhi9IoNA0uslQrQzKVr/WQPE/1TVkmvy0u3tiFSoI
# 0MFXQjCirzdJoNU+5Wq3F0ygPMupMopOnidaMR8wH9fk3pb7hzzOve5wQRM+EtIv
# W2QGnWNaiR7n3UeGWYnh7aidcJ7Dfw==
# =O3mB
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 14 Oct 2022 03:51:59 EDT
# gpg:                using RSA key F6C4AC46D4934868D3B8CE8F21E10D29DF977054
# gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: F6C4 AC46 D493 4868 D3B8  CE8F 21E1 0D29 DF97 7054

* tag 'pull-riscv-to-apply-20221014' of https://github.com/alistair23/qemu:
  target/riscv: pmp: Fixup TLB size calculation
  hw/intc: sifive_plic: change interrupt priority register to WARL field
  hw/intc: sifive_plic: fix hard-coded max priority level
  disas/riscv.c: rvv: Add disas support for vector instructions
  hw/riscv: virt: Enable booting S-mode firmware from pflash
  hw/riscv: virt: Move create_fw_cfg() prior to loading kernel
  hw/arm, loongarch: Move load_image_to_fw_cfg() to common location
  hw/ssi: ibex_spi: fixup/add rw1c functionality
  hw/ssi: ibex_spi: fixup coverity issue
  hw/riscv: Update comment for qtest check in riscv_find_firmware()

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2022-10-16 15:53:13 -04:00
commit 5c2439a92c
12 changed files with 1675 additions and 165 deletions

File diff suppressed because it is too large Load Diff

View File

@ -822,55 +822,6 @@ static void do_cpu_reset(void *opaque)
}
}
/**
* load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified
* by key.
* @fw_cfg: The firmware config instance to store the data in.
* @size_key: The firmware config key to store the size of the loaded
* data under, with fw_cfg_add_i32().
* @data_key: The firmware config key to store the loaded data under,
* with fw_cfg_add_bytes().
* @image_name: The name of the image file to load. If it is NULL, the
* function returns without doing anything.
* @try_decompress: Whether the image should be decompressed (gunzipped) before
* adding it to fw_cfg. If decompression fails, the image is
* loaded as-is.
*
* In case of failure, the function prints an error message to stderr and the
* process exits with status 1.
*/
static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress)
{
size_t size = -1;
uint8_t *data;
if (image_name == NULL) {
return;
}
if (try_decompress) {
size = load_image_gzipped_buffer(image_name,
LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
}
if (size == (size_t)-1) {
gchar *contents;
gsize length;
if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
error_report("failed to load \"%s\"", image_name);
exit(1);
}
size = length;
data = (uint8_t *)contents;
}
fw_cfg_add_i32(fw_cfg, size_key, size);
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
static int do_arm_linux_init(Object *obj, void *opaque)
{
if (object_dynamic_cast(obj, TYPE_ARM_LINUX_BOOT_IF)) {

View File

@ -180,8 +180,18 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) {
uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
plic->source_priority[irq] = value & 7;
sifive_plic_update(plic);
if (((plic->num_priorities + 1) & plic->num_priorities) == 0) {
/*
* if "num_priorities + 1" is power-of-2, make each register bit of
* interrupt priority WARL (Write-Any-Read-Legal). Just filter
* out the access to unsupported priority bits.
*/
plic->source_priority[irq] = value % (plic->num_priorities + 1);
sifive_plic_update(plic);
} else if (value <= plic->num_priorities) {
plic->source_priority[irq] = value;
sifive_plic_update(plic);
}
} else if (addr_between(addr, plic->pending_base,
plic->num_sources >> 3)) {
qemu_log_mask(LOG_GUEST_ERROR,
@ -205,7 +215,16 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
uint32_t contextid = (addr & (plic->context_stride - 1));
if (contextid == 0) {
if (value <= plic->num_priorities) {
if (((plic->num_priorities + 1) & plic->num_priorities) == 0) {
/*
* if "num_priorities + 1" is power-of-2, each register bit of
* interrupt priority is WARL (Write-Any-Read-Legal). Just
* filter out the access to unsupported priority bits.
*/
plic->target_priority[addrid] = value %
(plic->num_priorities + 1);
sifive_plic_update(plic);
} else if (value <= plic->num_priorities) {
plic->target_priority[addrid] = value;
sifive_plic_update(plic);
}

View File

@ -598,39 +598,6 @@ static void reset_load_elf(void *opaque)
}
}
/* Load an image file into an fw_cfg entry identified by key. */
static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress)
{
size_t size = -1;
uint8_t *data;
if (image_name == NULL) {
return;
}
if (try_decompress) {
size = load_image_gzipped_buffer(image_name,
LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
}
if (size == (size_t)-1) {
gchar *contents;
gsize length;
if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
error_report("failed to load \"%s\"", image_name);
exit(1);
}
size = length;
data = (uint8_t *)contents;
}
fw_cfg_add_i32(fw_cfg, size_key, size);
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
static void fw_cfg_add_kernel_info(FWCfgState *fw_cfg)
{
/*

View File

@ -41,6 +41,7 @@
#include "qapi/error.h"
#include "hw/acpi/aml-build.h"
#include "hw/pci/pci_bus.h"
#include "hw/loader.h"
#define FW_CFG_FILE_SLOTS_DFLT 0x20
@ -1221,6 +1222,37 @@ FWCfgState *fw_cfg_find(void)
return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL));
}
void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress)
{
size_t size = -1;
uint8_t *data;
if (image_name == NULL) {
return;
}
if (try_decompress) {
size = load_image_gzipped_buffer(image_name,
LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
}
if (size == (size_t)-1) {
gchar *contents;
gsize length;
if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
error_report("failed to load \"%s\"", image_name);
exit(1);
}
size = length;
data = (uint8_t *)contents;
}
fw_cfg_add_i32(fw_cfg, size_key, size);
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
static void fw_cfg_class_init(ObjectClass *klass, void *data)
{

View File

@ -111,8 +111,8 @@ char *riscv_find_firmware(const char *firmware_filename)
if (filename == NULL) {
if (!qtest_enabled()) {
/*
* We only ship plain binary bios images in the QEMU source.
* With Spike machine that uses ELF images as the default bios,
* We only ship OpenSBI binary bios images in the QEMU source.
* For machines that use images other than the default bios,
* running QEMU test will complain hence let's suppress the error
* report for QEMU testing.
*/
@ -338,3 +338,32 @@ void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr)
riscv_cpu->env.fdt_addr = fdt_addr;
}
}
void riscv_setup_firmware_boot(MachineState *machine)
{
if (machine->kernel_filename) {
FWCfgState *fw_cfg;
fw_cfg = fw_cfg_find();
assert(fw_cfg);
/*
* Expose the kernel, the command line, and the initrd in fw_cfg.
* We don't process them here at all, it's all left to the
* firmware.
*/
load_image_to_fw_cfg(fw_cfg,
FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
machine->kernel_filename,
true);
load_image_to_fw_cfg(fw_cfg,
FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
machine->initrd_filename, false);
if (machine->kernel_cmdline) {
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
strlen(machine->kernel_cmdline) + 1);
fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
machine->kernel_cmdline);
}
}
}

View File

@ -1267,7 +1267,30 @@ static void virt_machine_done(Notifier *notifier, void *data)
RISCV64_BIOS_BIN, start_addr, NULL);
}
if (machine->kernel_filename) {
/*
* Init fw_cfg. Must be done before riscv_load_fdt, otherwise the device
* tree cannot be altered and we get FDT_ERR_NOSPACE.
*/
s->fw_cfg = create_fw_cfg(machine);
rom_set_fw(s->fw_cfg);
if (drive_get(IF_PFLASH, 0, 1)) {
/*
* S-mode FW like EDK2 will be kept in second plash (unit 1).
* When both kernel, initrd and pflash options are provided in the
* command line, the kernel and initrd will be copied to the fw_cfg
* table and opensbi will jump to the flash address which is the
* entry point of S-mode FW. It is the job of the S-mode FW to load
* the kernel and initrd using fw_cfg table.
*
* If only pflash is given but not -kernel, then it is the job of
* of the S-mode firmware to locate and load the kernel.
* In either case, the next_addr for opensbi will be the flash address.
*/
riscv_setup_firmware_boot(machine);
kernel_entry = virt_memmap[VIRT_FLASH].base +
virt_memmap[VIRT_FLASH].size / 2;
} else if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0],
firmware_end_addr);
@ -1300,13 +1323,6 @@ static void virt_machine_done(Notifier *notifier, void *data)
start_addr = virt_memmap[VIRT_FLASH].base;
}
/*
* Init fw_cfg. Must be done before riscv_load_fdt, otherwise the device
* tree cannot be altered and we get FDT_ERR_NOSPACE.
*/
s->fw_cfg = create_fw_cfg(machine);
rom_set_fw(s->fw_cfg);
/* Compute the fdt load address in dram */
fdt_load_addr = riscv_load_fdt(memmap[VIRT_DRAM].base,
machine->ram_size, machine->fdt);

View File

@ -108,18 +108,22 @@ static inline uint8_t div4_round_up(uint8_t dividend)
static void ibex_spi_rxfifo_reset(IbexSPIHostState *s)
{
uint32_t data = s->regs[IBEX_SPI_HOST_STATUS];
/* Empty the RX FIFO and assert RXEMPTY */
fifo8_reset(&s->rx_fifo);
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK;
data = FIELD_DP32(data, STATUS, RXFULL, 0);
data = FIELD_DP32(data, STATUS, RXEMPTY, 1);
s->regs[IBEX_SPI_HOST_STATUS] = data;
}
static void ibex_spi_txfifo_reset(IbexSPIHostState *s)
{
uint32_t data = s->regs[IBEX_SPI_HOST_STATUS];
/* Empty the TX FIFO and assert TXEMPTY */
fifo8_reset(&s->tx_fifo);
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXEMPTY_MASK;
data = FIELD_DP32(data, STATUS, TXFULL, 0);
data = FIELD_DP32(data, STATUS, TXEMPTY, 1);
s->regs[IBEX_SPI_HOST_STATUS] = data;
}
static void ibex_spi_host_reset(DeviceState *dev)
@ -162,37 +166,38 @@ static void ibex_spi_host_reset(DeviceState *dev)
*/
static void ibex_spi_host_irq(IbexSPIHostState *s)
{
bool error_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE]
& R_INTR_ENABLE_ERROR_MASK;
bool event_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE]
& R_INTR_ENABLE_SPI_EVENT_MASK;
bool err_pending = s->regs[IBEX_SPI_HOST_INTR_STATE]
& R_INTR_STATE_ERROR_MASK;
bool status_pending = s->regs[IBEX_SPI_HOST_INTR_STATE]
& R_INTR_STATE_SPI_EVENT_MASK;
uint32_t intr_test_reg = s->regs[IBEX_SPI_HOST_INTR_TEST];
uint32_t intr_en_reg = s->regs[IBEX_SPI_HOST_INTR_ENABLE];
uint32_t intr_state_reg = s->regs[IBEX_SPI_HOST_INTR_STATE];
uint32_t err_en_reg = s->regs[IBEX_SPI_HOST_ERROR_ENABLE];
uint32_t event_en_reg = s->regs[IBEX_SPI_HOST_EVENT_ENABLE];
uint32_t err_status_reg = s->regs[IBEX_SPI_HOST_ERROR_STATUS];
uint32_t status_reg = s->regs[IBEX_SPI_HOST_STATUS];
bool error_en = FIELD_EX32(intr_en_reg, INTR_ENABLE, ERROR);
bool event_en = FIELD_EX32(intr_en_reg, INTR_ENABLE, SPI_EVENT);
bool err_pending = FIELD_EX32(intr_state_reg, INTR_STATE, ERROR);
bool status_pending = FIELD_EX32(intr_state_reg, INTR_STATE, SPI_EVENT);
int err_irq = 0, event_irq = 0;
/* Error IRQ enabled and Error IRQ Cleared */
if (error_en && !err_pending) {
/* Event enabled, Interrupt Test Error */
if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_ERROR_MASK) {
if (FIELD_EX32(intr_test_reg, INTR_TEST, ERROR)) {
err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
& R_ERROR_ENABLE_CMDBUSY_MASK) &&
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CMDBUSY_MASK) {
} else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CMDBUSY) &&
FIELD_EX32(err_status_reg, ERROR_STATUS, CMDBUSY)) {
/* Wrote to COMMAND when not READY */
err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
& R_ERROR_ENABLE_CMDINVAL_MASK) &&
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CMDINVAL_MASK) {
} else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CMDINVAL) &&
FIELD_EX32(err_status_reg, ERROR_STATUS, CMDINVAL)) {
/* Invalid command segment */
err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
& R_ERROR_ENABLE_CSIDINVAL_MASK) &&
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CSIDINVAL_MASK) {
} else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CSIDINVAL) &&
FIELD_EX32(err_status_reg, ERROR_STATUS, CSIDINVAL)) {
/* Invalid value for CSID */
err_irq = 1;
}
@ -204,22 +209,19 @@ static void ibex_spi_host_irq(IbexSPIHostState *s)
/* Event IRQ Enabled and Event IRQ Cleared */
if (event_en && !status_pending) {
if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_SPI_EVENT_MASK) {
if (FIELD_EX32(intr_test_reg, INTR_STATE, SPI_EVENT)) {
/* Event enabled, Interrupt Test Event */
event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
& R_EVENT_ENABLE_READY_MASK) &&
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
} else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, READY) &&
FIELD_EX32(status_reg, STATUS, READY)) {
/* SPI Host ready for next command */
event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
& R_EVENT_ENABLE_TXEMPTY_MASK) &&
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_TXEMPTY_MASK)) {
} else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, TXEMPTY) &&
FIELD_EX32(status_reg, STATUS, TXEMPTY)) {
/* SPI TXEMPTY, TXFIFO drained */
event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
& R_EVENT_ENABLE_RXFULL_MASK) &&
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_RXFULL_MASK)) {
} else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, RXFULL) &&
FIELD_EX32(status_reg, STATUS, RXFULL)) {
/* SPI RXFULL, RXFIFO full */
event_irq = 1;
}
@ -232,10 +234,11 @@ static void ibex_spi_host_irq(IbexSPIHostState *s)
static void ibex_spi_host_transfer(IbexSPIHostState *s)
{
uint32_t rx, tx;
uint32_t rx, tx, data;
/* Get num of one byte transfers */
uint8_t segment_len = ((s->regs[IBEX_SPI_HOST_COMMAND] & R_COMMAND_LEN_MASK)
>> R_COMMAND_LEN_SHIFT);
uint8_t segment_len = FIELD_EX32(s->regs[IBEX_SPI_HOST_COMMAND],
COMMAND, LEN);
while (segment_len > 0) {
if (fifo8_is_empty(&s->tx_fifo)) {
/* Assert Stall */
@ -262,22 +265,21 @@ static void ibex_spi_host_transfer(IbexSPIHostState *s)
--segment_len;
}
data = s->regs[IBEX_SPI_HOST_STATUS];
/* Assert Ready */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK;
data = FIELD_DP32(data, STATUS, READY, 1);
/* Set RXQD */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXQD_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= (R_STATUS_RXQD_MASK
& div4_round_up(segment_len));
data = FIELD_DP32(data, STATUS, RXQD, div4_round_up(segment_len));
/* Set TXQD */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= (fifo8_num_used(&s->tx_fifo) / 4)
& R_STATUS_TXQD_MASK;
data = FIELD_DP32(data, STATUS, TXQD, fifo8_num_used(&s->tx_fifo) / 4);
/* Clear TXFULL */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK;
/* Assert TXEMPTY and drop remaining bytes that exceed segment_len */
ibex_spi_txfifo_reset(s);
data = FIELD_DP32(data, STATUS, TXFULL, 0);
/* Reset RXEMPTY */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXEMPTY_MASK;
data = FIELD_DP32(data, STATUS, RXEMPTY, 0);
/* Update register status */
s->regs[IBEX_SPI_HOST_STATUS] = data;
/* Drop remaining bytes that exceed segment_len */
ibex_spi_txfifo_reset(s);
ibex_spi_host_irq(s);
}
@ -340,7 +342,7 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
{
IbexSPIHostState *s = opaque;
uint32_t val32 = val64;
uint32_t shift_mask = 0xff;
uint32_t shift_mask = 0xff, status = 0, data = 0;
uint8_t txqd_len;
trace_ibex_spi_host_write(addr, size, val64);
@ -350,7 +352,17 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
switch (addr) {
/* Skipping any R/O registers */
case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE:
case IBEX_SPI_HOST_INTR_STATE:
/* rw1c status register */
if (FIELD_EX32(val32, INTR_STATE, ERROR)) {
data = FIELD_DP32(data, INTR_STATE, ERROR, 0);
}
if (FIELD_EX32(val32, INTR_STATE, SPI_EVENT)) {
data = FIELD_DP32(data, INTR_STATE, SPI_EVENT, 0);
}
s->regs[addr] = data;
break;
case IBEX_SPI_HOST_INTR_ENABLE:
s->regs[addr] = val32;
break;
case IBEX_SPI_HOST_INTR_TEST:
@ -397,22 +409,24 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
s->regs[addr] = val32;
/* STALL, IP not enabled */
if (!(s->regs[IBEX_SPI_HOST_CONTROL] & R_CONTROL_SPIEN_MASK)) {
if (!(FIELD_EX32(s->regs[IBEX_SPI_HOST_CONTROL],
CONTROL, SPIEN))) {
return;
}
/* SPI not ready, IRQ Error */
if (!(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
if (!(FIELD_EX32(s->regs[IBEX_SPI_HOST_STATUS],
STATUS, READY))) {
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= R_ERROR_STATUS_CMDBUSY_MASK;
ibex_spi_host_irq(s);
return;
}
/* Assert Not Ready */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_READY_MASK;
if (((val32 & R_COMMAND_DIRECTION_MASK) >> R_COMMAND_DIRECTION_SHIFT)
!= BIDIRECTIONAL_TRANSFER) {
qemu_log_mask(LOG_UNIMP,
if (FIELD_EX32(val32, COMMAND, DIRECTION) != BIDIRECTIONAL_TRANSFER) {
qemu_log_mask(LOG_UNIMP,
"%s: Rx Only/Tx Only are not supported\n", __func__);
}
@ -452,8 +466,8 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
return;
}
/* Byte ordering is set by the IP */
if ((s->regs[IBEX_SPI_HOST_STATUS] &
R_STATUS_BYTEORDER_MASK) == 0) {
status = s->regs[IBEX_SPI_HOST_STATUS];
if (FIELD_EX32(status, STATUS, BYTEORDER) == 0) {
/* LE: LSB transmitted first (default for ibex processor) */
shift_mask = 0xff << (i * 8);
} else {
@ -464,18 +478,18 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
fifo8_push(&s->tx_fifo, (val32 & shift_mask) >> (i * 8));
}
status = s->regs[IBEX_SPI_HOST_STATUS];
/* Reset TXEMPTY */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXEMPTY_MASK;
status = FIELD_DP32(status, STATUS, TXEMPTY, 0);
/* Update TXQD */
txqd_len = (s->regs[IBEX_SPI_HOST_STATUS] &
R_STATUS_TXQD_MASK) >> R_STATUS_TXQD_SHIFT;
txqd_len = FIELD_EX32(status, STATUS, TXQD);
/* Partial bytes (size < 4) are padded, in words. */
txqd_len += 1;
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= txqd_len;
status = FIELD_DP32(status, STATUS, TXQD, txqd_len);
/* Assert Ready */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK;
status = FIELD_DP32(status, STATUS, READY, 1);
/* Update register status */
s->regs[IBEX_SPI_HOST_STATUS] = status;
break;
case IBEX_SPI_HOST_ERROR_ENABLE:
s->regs[addr] = val32;
@ -491,7 +505,27 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
* When an error occurs, the corresponding bit must be cleared
* here before issuing any further commands
*/
s->regs[addr] = val32;
status = s->regs[addr];
/* rw1c status register */
if (FIELD_EX32(val32, ERROR_STATUS, CMDBUSY)) {
status = FIELD_DP32(status, ERROR_STATUS, CMDBUSY, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, OVERFLOW)) {
status = FIELD_DP32(status, ERROR_STATUS, OVERFLOW, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, UNDERFLOW)) {
status = FIELD_DP32(status, ERROR_STATUS, UNDERFLOW, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, CMDINVAL)) {
status = FIELD_DP32(status, ERROR_STATUS, CMDINVAL, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, CSIDINVAL)) {
status = FIELD_DP32(status, ERROR_STATUS, CSIDINVAL, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, ACCESSINVAL)) {
status = FIELD_DP32(status, ERROR_STATUS, ACCESSINVAL, 0);
}
s->regs[addr] = status;
break;
case IBEX_SPI_HOST_EVENT_ENABLE:
/* Controls which classes of SPI events raise an interrupt. */

View File

@ -364,4 +364,25 @@ bool fw_cfg_dma_enabled(void *opaque);
*/
const char *fw_cfg_arch_key_name(uint16_t key);
/**
* load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified
* by key.
* @fw_cfg: The firmware config instance to store the data in.
* @size_key: The firmware config key to store the size of the loaded
* data under, with fw_cfg_add_i32().
* @data_key: The firmware config key to store the loaded data under,
* with fw_cfg_add_bytes().
* @image_name: The name of the image file to load. If it is NULL, the
* function returns without doing anything.
* @try_decompress: Whether the image should be decompressed (gunzipped) before
* adding it to fw_cfg. If decompression fails, the image is
* loaded as-is.
*
* In case of failure, the function prints an error message to stderr and the
* process exits with status 1.
*/
void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress);
#endif

View File

@ -57,5 +57,6 @@ void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base,
uint32_t reset_vec_size,
uint64_t kernel_entry);
void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr);
void riscv_setup_firmware_boot(MachineState *machine);
#endif /* RISCV_BOOT_H */

View File

@ -40,7 +40,7 @@
OBJECT_CHECK(IbexSPIHostState, (obj), TYPE_IBEX_SPI_HOST)
/* SPI Registers */
#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw */
#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw1c */
#define IBEX_SPI_HOST_INTR_ENABLE (0x04 / 4) /* rw */
#define IBEX_SPI_HOST_INTR_TEST (0x08 / 4) /* wo */
#define IBEX_SPI_HOST_ALERT_TEST (0x0c / 4) /* wo */
@ -54,7 +54,7 @@
#define IBEX_SPI_HOST_TXDATA (0x28 / 4)
#define IBEX_SPI_HOST_ERROR_ENABLE (0x2c / 4) /* rw */
#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw */
#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw1c */
#define IBEX_SPI_HOST_EVENT_ENABLE (0x34 / 4) /* rw */
/* FIFO Len in Bytes */

View File

@ -628,6 +628,18 @@ bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
}
if (*tlb_size != 0) {
/*
* At this point we have a tlb_size that is the smallest possible size
* That fits within a TARGET_PAGE_SIZE and the PMP region.
*
* If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
* This means the result isn't cached in the TLB and is only used for
* a single translation.
*/
if (*tlb_size < TARGET_PAGE_SIZE) {
*tlb_size = 1;
}
return true;
}