2011-03-08 01:32:35 +03:00
|
|
|
/*
|
2011-04-13 02:29:36 +04:00
|
|
|
* QEMU model of the Milkymist minimac2 block.
|
2011-03-08 01:32:35 +03:00
|
|
|
*
|
2011-04-13 02:29:36 +04:00
|
|
|
* Copyright (c) 2011 Michael Walle <michael@walle.cc>
|
2011-03-08 01:32:35 +03:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
2020-10-23 15:44:24 +03:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2011-03-08 01:32:35 +03:00
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Specification available at:
|
2011-04-13 02:29:36 +04:00
|
|
|
* not available yet
|
2011-03-08 01:32:35 +03:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-01-26 21:16:57 +03:00
|
|
|
#include "qemu/osdep.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 11:01:28 +03:00
|
|
|
#include "qapi/error.h"
|
2020-09-03 23:43:22 +03:00
|
|
|
#include "qom/object.h"
|
2016-01-19 23:51:44 +03:00
|
|
|
#include "cpu.h" /* FIXME: why does this use TARGET_PAGE_ALIGN? */
|
2019-08-12 08:23:42 +03:00
|
|
|
#include "hw/irq.h"
|
2019-08-12 08:23:51 +03:00
|
|
|
#include "hw/qdev-properties.h"
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/sysbus.h"
|
2019-08-12 08:23:45 +03:00
|
|
|
#include "migration/vmstate.h"
|
2011-03-08 01:32:35 +03:00
|
|
|
#include "trace.h"
|
2012-10-24 10:43:34 +04:00
|
|
|
#include "net/net.h"
|
2018-10-29 16:00:34 +03:00
|
|
|
#include "qemu/log.h"
|
2019-05-23 17:35:07 +03:00
|
|
|
#include "qemu/module.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/error-report.h"
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
#include <zlib.h>
|
|
|
|
|
|
|
|
enum {
|
|
|
|
R_SETUP = 0,
|
|
|
|
R_MDIO,
|
|
|
|
R_STATE0,
|
|
|
|
R_COUNT0,
|
|
|
|
R_STATE1,
|
|
|
|
R_COUNT1,
|
|
|
|
R_TXCOUNT,
|
|
|
|
R_MAX
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
2011-04-13 02:29:36 +04:00
|
|
|
SETUP_PHY_RST = (1<<0),
|
2011-03-08 01:32:35 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
MDIO_DO = (1<<0),
|
|
|
|
MDIO_DI = (1<<1),
|
|
|
|
MDIO_OE = (1<<2),
|
|
|
|
MDIO_CLK = (1<<3),
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
STATE_EMPTY = 0,
|
|
|
|
STATE_LOADED = 1,
|
|
|
|
STATE_PENDING = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
MDIO_OP_WRITE = 1,
|
|
|
|
MDIO_OP_READ = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum mdio_state {
|
|
|
|
MDIO_STATE_IDLE,
|
|
|
|
MDIO_STATE_READING,
|
|
|
|
MDIO_STATE_WRITING,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
R_PHY_ID1 = 2,
|
|
|
|
R_PHY_ID2 = 3,
|
|
|
|
R_PHY_MAX = 32
|
|
|
|
};
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
#define MINIMAC2_MTU 1530
|
|
|
|
#define MINIMAC2_BUFFER_SIZE 2048
|
2011-03-08 01:32:35 +03:00
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
struct MilkymistMinimac2MdioState {
|
2011-03-08 01:32:35 +03:00
|
|
|
int last_clk;
|
|
|
|
int count;
|
|
|
|
uint32_t data;
|
|
|
|
uint16_t data_out;
|
|
|
|
int state;
|
|
|
|
|
|
|
|
uint8_t phy_addr;
|
|
|
|
uint8_t reg_addr;
|
|
|
|
};
|
2011-04-13 02:29:36 +04:00
|
|
|
typedef struct MilkymistMinimac2MdioState MilkymistMinimac2MdioState;
|
2011-03-08 01:32:35 +03:00
|
|
|
|
2013-07-27 14:13:17 +04:00
|
|
|
#define TYPE_MILKYMIST_MINIMAC2 "milkymist-minimac2"
|
2020-09-16 21:25:19 +03:00
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(MilkymistMinimac2State, MILKYMIST_MINIMAC2)
|
2013-07-27 14:13:17 +04:00
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
struct MilkymistMinimac2State {
|
2013-07-27 14:13:17 +04:00
|
|
|
SysBusDevice parent_obj;
|
|
|
|
|
2011-03-08 01:32:35 +03:00
|
|
|
NICState *nic;
|
|
|
|
NICConf conf;
|
|
|
|
char *phy_model;
|
2011-08-08 22:32:25 +04:00
|
|
|
MemoryRegion buffers;
|
|
|
|
MemoryRegion regs_region;
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
qemu_irq rx_irq;
|
|
|
|
qemu_irq tx_irq;
|
|
|
|
|
|
|
|
uint32_t regs[R_MAX];
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
MilkymistMinimac2MdioState mdio;
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
uint16_t phy_regs[R_PHY_MAX];
|
2011-04-13 02:29:36 +04:00
|
|
|
|
|
|
|
uint8_t *rx0_buf;
|
|
|
|
uint8_t *rx1_buf;
|
|
|
|
uint8_t *tx_buf;
|
2011-03-08 01:32:35 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static const uint8_t preamble_sfd[] = {
|
|
|
|
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5
|
|
|
|
};
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static void minimac2_mdio_write_reg(MilkymistMinimac2State *s,
|
2011-03-08 01:32:35 +03:00
|
|
|
uint8_t phy_addr, uint8_t reg_addr, uint16_t value)
|
|
|
|
{
|
2011-04-13 02:29:36 +04:00
|
|
|
trace_milkymist_minimac2_mdio_write(phy_addr, reg_addr, value);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
/* nop */
|
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static uint16_t minimac2_mdio_read_reg(MilkymistMinimac2State *s,
|
2011-03-08 01:32:35 +03:00
|
|
|
uint8_t phy_addr, uint8_t reg_addr)
|
|
|
|
{
|
|
|
|
uint16_t r = s->phy_regs[reg_addr];
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
trace_milkymist_minimac2_mdio_read(phy_addr, reg_addr, r);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static void minimac2_update_mdio(MilkymistMinimac2State *s)
|
2011-03-08 01:32:35 +03:00
|
|
|
{
|
2011-04-13 02:29:36 +04:00
|
|
|
MilkymistMinimac2MdioState *m = &s->mdio;
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
/* detect rising clk edge */
|
|
|
|
if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) {
|
|
|
|
/* shift data in */
|
|
|
|
int bit = ((s->regs[R_MDIO] & MDIO_DO)
|
|
|
|
&& (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0;
|
|
|
|
m->data = (m->data << 1) | bit;
|
|
|
|
|
|
|
|
/* check for sync */
|
|
|
|
if (m->data == 0xffffffff) {
|
|
|
|
m->count = 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->count == 16) {
|
|
|
|
uint8_t start = (m->data >> 14) & 0x3;
|
|
|
|
uint8_t op = (m->data >> 12) & 0x3;
|
|
|
|
uint8_t ta = (m->data) & 0x3;
|
|
|
|
|
|
|
|
if (start == 1 && op == MDIO_OP_WRITE && ta == 2) {
|
|
|
|
m->state = MDIO_STATE_WRITING;
|
|
|
|
} else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) {
|
|
|
|
m->state = MDIO_STATE_READING;
|
|
|
|
} else {
|
|
|
|
m->state = MDIO_STATE_IDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->state != MDIO_STATE_IDLE) {
|
|
|
|
m->phy_addr = (m->data >> 7) & 0x1f;
|
|
|
|
m->reg_addr = (m->data >> 2) & 0x1f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->state == MDIO_STATE_READING) {
|
2011-04-13 02:29:36 +04:00
|
|
|
m->data_out = minimac2_mdio_read_reg(s, m->phy_addr,
|
2011-03-08 01:32:35 +03:00
|
|
|
m->reg_addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->count < 16 && m->state == MDIO_STATE_READING) {
|
|
|
|
int bit = (m->data_out & 0x8000) ? 1 : 0;
|
|
|
|
m->data_out <<= 1;
|
|
|
|
|
|
|
|
if (bit) {
|
|
|
|
s->regs[R_MDIO] |= MDIO_DI;
|
|
|
|
} else {
|
|
|
|
s->regs[R_MDIO] &= ~MDIO_DI;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->count == 0 && m->state) {
|
|
|
|
if (m->state == MDIO_STATE_WRITING) {
|
|
|
|
uint16_t data = m->data & 0xffff;
|
2011-04-13 02:29:36 +04:00
|
|
|
minimac2_mdio_write_reg(s, m->phy_addr, m->reg_addr, data);
|
2011-03-08 01:32:35 +03:00
|
|
|
}
|
|
|
|
m->state = MDIO_STATE_IDLE;
|
|
|
|
}
|
|
|
|
m->count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t assemble_frame(uint8_t *buf, size_t size,
|
|
|
|
const uint8_t *payload, size_t payload_size)
|
|
|
|
{
|
|
|
|
uint32_t crc;
|
|
|
|
|
|
|
|
if (size < payload_size + 12) {
|
2018-10-29 16:00:34 +03:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "milkymist_minimac2: frame too big "
|
|
|
|
"(%zd bytes)\n", payload_size);
|
2011-03-08 01:32:35 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* prepend preamble and sfd */
|
|
|
|
memcpy(buf, preamble_sfd, 8);
|
|
|
|
|
|
|
|
/* now copy the payload */
|
|
|
|
memcpy(buf + 8, payload, payload_size);
|
|
|
|
|
|
|
|
/* pad frame if needed */
|
|
|
|
if (payload_size < 60) {
|
|
|
|
memset(buf + payload_size + 8, 0, 60 - payload_size);
|
|
|
|
payload_size = 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* append fcs */
|
|
|
|
crc = cpu_to_le32(crc32(0, buf + 8, payload_size));
|
|
|
|
memcpy(buf + payload_size + 8, &crc, 4);
|
|
|
|
|
|
|
|
return payload_size + 12;
|
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static void minimac2_tx(MilkymistMinimac2State *s)
|
2011-03-08 01:32:35 +03:00
|
|
|
{
|
|
|
|
uint32_t txcount = s->regs[R_TXCOUNT];
|
2011-04-13 02:29:36 +04:00
|
|
|
uint8_t *buf = s->tx_buf;
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
if (txcount < 64) {
|
2011-06-22 16:03:54 +04:00
|
|
|
error_report("milkymist_minimac2: ethernet frame too small (%u < %u)",
|
2011-03-08 01:32:35 +03:00
|
|
|
txcount, 64);
|
2011-04-13 02:29:36 +04:00
|
|
|
goto err;
|
2011-03-08 01:32:35 +03:00
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
if (txcount > MINIMAC2_MTU) {
|
2011-06-22 16:03:54 +04:00
|
|
|
error_report("milkymist_minimac2: MTU exceeded (%u > %u)",
|
2011-04-13 02:29:36 +04:00
|
|
|
txcount, MINIMAC2_MTU);
|
|
|
|
goto err;
|
2011-03-08 01:32:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (memcmp(buf, preamble_sfd, 8) != 0) {
|
2011-04-13 02:29:36 +04:00
|
|
|
error_report("milkymist_minimac2: frame doesn't contain the preamble "
|
2011-06-22 16:03:54 +04:00
|
|
|
"and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)",
|
2011-03-08 01:32:35 +03:00
|
|
|
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
|
2011-04-13 02:29:36 +04:00
|
|
|
goto err;
|
2011-03-08 01:32:35 +03:00
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
trace_milkymist_minimac2_tx_frame(txcount - 12);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
/* send packet, skipping preamble and sfd */
|
2013-01-30 15:12:22 +04:00
|
|
|
qemu_send_packet_raw(qemu_get_queue(s->nic), buf + 8, txcount - 12);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
s->regs[R_TXCOUNT] = 0;
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
err:
|
|
|
|
trace_milkymist_minimac2_pulse_irq_tx();
|
2011-03-08 01:32:35 +03:00
|
|
|
qemu_irq_pulse(s->tx_irq);
|
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static void update_rx_interrupt(MilkymistMinimac2State *s)
|
|
|
|
{
|
|
|
|
if (s->regs[R_STATE0] == STATE_PENDING
|
|
|
|
|| s->regs[R_STATE1] == STATE_PENDING) {
|
|
|
|
trace_milkymist_minimac2_raise_irq_rx();
|
|
|
|
qemu_irq_raise(s->rx_irq);
|
|
|
|
} else {
|
|
|
|
trace_milkymist_minimac2_lower_irq_rx();
|
|
|
|
qemu_irq_lower(s->rx_irq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
static ssize_t minimac2_rx(NetClientState *nc, const uint8_t *buf, size_t size)
|
2011-03-08 01:32:35 +03:00
|
|
|
{
|
2013-01-30 15:12:23 +04:00
|
|
|
MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
uint32_t r_count;
|
|
|
|
uint32_t r_state;
|
2011-04-13 02:29:36 +04:00
|
|
|
uint8_t *rx_buf;
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
size_t frame_size;
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
trace_milkymist_minimac2_rx_frame(buf, size);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
/* choose appropriate slot */
|
|
|
|
if (s->regs[R_STATE0] == STATE_LOADED) {
|
|
|
|
r_count = R_COUNT0;
|
|
|
|
r_state = R_STATE0;
|
2011-04-13 02:29:36 +04:00
|
|
|
rx_buf = s->rx0_buf;
|
2011-03-08 01:32:35 +03:00
|
|
|
} else if (s->regs[R_STATE1] == STATE_LOADED) {
|
|
|
|
r_count = R_COUNT1;
|
|
|
|
r_state = R_STATE1;
|
2011-04-13 02:29:36 +04:00
|
|
|
rx_buf = s->rx1_buf;
|
2011-03-08 01:32:35 +03:00
|
|
|
} else {
|
2015-07-15 13:19:09 +03:00
|
|
|
return 0;
|
2011-03-08 01:32:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* assemble frame */
|
2011-04-13 02:29:36 +04:00
|
|
|
frame_size = assemble_frame(rx_buf, MINIMAC2_BUFFER_SIZE, buf, size);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
if (frame_size == 0) {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
trace_milkymist_minimac2_rx_transfer(rx_buf, frame_size);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
/* update slot */
|
|
|
|
s->regs[r_count] = frame_size;
|
|
|
|
s->regs[r_state] = STATE_PENDING;
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
update_rx_interrupt(s);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2011-08-08 22:32:25 +04:00
|
|
|
static uint64_t
|
2012-10-23 14:30:10 +04:00
|
|
|
minimac2_read(void *opaque, hwaddr addr, unsigned size)
|
2011-03-08 01:32:35 +03:00
|
|
|
{
|
2011-04-13 02:29:36 +04:00
|
|
|
MilkymistMinimac2State *s = opaque;
|
2011-03-08 01:32:35 +03:00
|
|
|
uint32_t r = 0;
|
|
|
|
|
|
|
|
addr >>= 2;
|
|
|
|
switch (addr) {
|
|
|
|
case R_SETUP:
|
|
|
|
case R_MDIO:
|
|
|
|
case R_STATE0:
|
|
|
|
case R_COUNT0:
|
|
|
|
case R_STATE1:
|
|
|
|
case R_COUNT1:
|
|
|
|
case R_TXCOUNT:
|
|
|
|
r = s->regs[addr];
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-10-29 16:00:34 +03:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
"milkymist_minimac2_rd%d: 0x%" HWADDR_PRIx "\n",
|
|
|
|
size, addr << 2);
|
2011-03-08 01:32:35 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
trace_milkymist_minimac2_memory_read(addr << 2, r);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-07-15 13:19:09 +03:00
|
|
|
static int minimac2_can_rx(MilkymistMinimac2State *s)
|
|
|
|
{
|
|
|
|
if (s->regs[R_STATE0] == STATE_LOADED) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (s->regs[R_STATE1] == STATE_LOADED) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-03-08 01:32:35 +03:00
|
|
|
static void
|
2012-10-23 14:30:10 +04:00
|
|
|
minimac2_write(void *opaque, hwaddr addr, uint64_t value,
|
2011-08-08 22:32:25 +04:00
|
|
|
unsigned size)
|
2011-03-08 01:32:35 +03:00
|
|
|
{
|
2011-04-13 02:29:36 +04:00
|
|
|
MilkymistMinimac2State *s = opaque;
|
2011-03-08 01:32:35 +03:00
|
|
|
|
2013-06-07 14:59:19 +04:00
|
|
|
trace_milkymist_minimac2_memory_write(addr, value);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
|
|
|
addr >>= 2;
|
|
|
|
switch (addr) {
|
|
|
|
case R_MDIO:
|
|
|
|
{
|
|
|
|
/* MDIO_DI is read only */
|
|
|
|
int mdio_di = (s->regs[R_MDIO] & MDIO_DI);
|
|
|
|
s->regs[R_MDIO] = value;
|
|
|
|
if (mdio_di) {
|
|
|
|
s->regs[R_MDIO] |= mdio_di;
|
|
|
|
} else {
|
|
|
|
s->regs[R_MDIO] &= ~mdio_di;
|
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
minimac2_update_mdio(s);
|
2011-03-08 01:32:35 +03:00
|
|
|
} break;
|
|
|
|
case R_TXCOUNT:
|
|
|
|
s->regs[addr] = value;
|
|
|
|
if (value > 0) {
|
2011-04-13 02:29:36 +04:00
|
|
|
minimac2_tx(s);
|
2011-03-08 01:32:35 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case R_STATE0:
|
|
|
|
case R_STATE1:
|
2011-04-13 02:29:36 +04:00
|
|
|
s->regs[addr] = value;
|
|
|
|
update_rx_interrupt(s);
|
2015-07-15 13:19:09 +03:00
|
|
|
if (minimac2_can_rx(s)) {
|
|
|
|
qemu_flush_queued_packets(qemu_get_queue(s->nic));
|
|
|
|
}
|
2011-04-13 02:29:36 +04:00
|
|
|
break;
|
|
|
|
case R_SETUP:
|
|
|
|
case R_COUNT0:
|
2011-03-08 01:32:35 +03:00
|
|
|
case R_COUNT1:
|
|
|
|
s->regs[addr] = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-10-29 16:00:34 +03:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
"milkymist_minimac2_wr%d: 0x%" HWADDR_PRIx
|
|
|
|
" = 0x%" PRIx64 "\n",
|
|
|
|
size, addr << 2, value);
|
2011-03-08 01:32:35 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-08 22:32:25 +04:00
|
|
|
static const MemoryRegionOps minimac2_ops = {
|
|
|
|
.read = minimac2_read,
|
|
|
|
.write = minimac2_write,
|
|
|
|
.valid = {
|
|
|
|
.min_access_size = 4,
|
|
|
|
.max_access_size = 4,
|
|
|
|
},
|
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
2011-03-08 01:32:35 +03:00
|
|
|
};
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static void milkymist_minimac2_reset(DeviceState *d)
|
2011-03-08 01:32:35 +03:00
|
|
|
{
|
2013-07-27 14:13:17 +04:00
|
|
|
MilkymistMinimac2State *s = MILKYMIST_MINIMAC2(d);
|
2011-03-08 01:32:35 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < R_MAX; i++) {
|
|
|
|
s->regs[i] = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < R_PHY_MAX; i++) {
|
|
|
|
s->phy_regs[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* defaults */
|
|
|
|
s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */
|
|
|
|
s->phy_regs[R_PHY_ID2] = 0x161a;
|
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static NetClientInfo net_milkymist_minimac2_info = {
|
qapi: Change Netdev into a flat union
This is a mostly-mechanical conversion that creates a new flat
union 'Netdev' QAPI type that covers all the branches of the
former 'NetClientOptions' simple union, where the branches are
now listed in a new 'NetClientDriver' enum rather than generated
from the simple union. The existence of a flat union has no
change to the command line syntax accepted for new code, and
will make it possible for a future patch to switch the QMP
command to parse a boxed union for no change to valid QMP; but
it does have some ripple effect on the C code when dealing with
the new types.
While making the conversion, note that the 'NetLegacy' type
remains unchanged: it applies only to legacy command line options,
and will not be ported to QMP, so it should remain a wrapper
around a simple union; to avoid confusion, the type named
'NetClientOptions' is now gone, and we introduce 'NetLegacyOptions'
in its place. Then, in the C code, we convert from NetLegacy to
Netdev as soon as possible, so that the bulk of the net stack
only has to deal with one QAPI type, not two. Note that since
the old legacy code always rejected 'hubport', we can just omit
that branch from the new 'NetLegacyOptions' simple union.
Based on an idea originally by Zoltán Kővágó <DirtY.iCE.hu@gmail.com>:
Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com>
although the sed script in that patch no longer applies due to
other changes in the tree since then, and I also did some manual
cleanups (such as fixing whitespace to keep checkpatch happy).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1468468228-27827-13-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
[Fixup from Eric squashed in]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-07-14 06:50:23 +03:00
|
|
|
.type = NET_CLIENT_DRIVER_NIC,
|
2011-03-08 01:32:35 +03:00
|
|
|
.size = sizeof(NICState),
|
2011-04-13 02:29:36 +04:00
|
|
|
.receive = minimac2_rx,
|
2011-03-08 01:32:35 +03:00
|
|
|
};
|
|
|
|
|
2018-10-01 09:37:57 +03:00
|
|
|
static void milkymist_minimac2_realize(DeviceState *dev, Error **errp)
|
2011-03-08 01:32:35 +03:00
|
|
|
{
|
2018-10-01 09:37:57 +03:00
|
|
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
2013-07-27 14:13:17 +04:00
|
|
|
MilkymistMinimac2State *s = MILKYMIST_MINIMAC2(dev);
|
2011-04-13 02:29:36 +04:00
|
|
|
size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
2013-07-27 14:13:17 +04:00
|
|
|
sysbus_init_irq(sbd, &s->rx_irq);
|
|
|
|
sysbus_init_irq(sbd, &s->tx_irq);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
2013-06-07 05:25:08 +04:00
|
|
|
memory_region_init_io(&s->regs_region, OBJECT(dev), &minimac2_ops, s,
|
2011-08-31 18:48:47 +04:00
|
|
|
"milkymist-minimac2", R_MAX * 4);
|
2013-07-27 14:13:17 +04:00
|
|
|
sysbus_init_mmio(sbd, &s->regs_region);
|
2011-03-08 01:32:35 +03:00
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
/* register buffers memory */
|
2017-07-07 17:42:49 +03:00
|
|
|
memory_region_init_ram_nomigrate(&s->buffers, OBJECT(dev), "milkymist-minimac2.buffers",
|
Fix bad error handling after memory_region_init_ram()
Symptom:
$ qemu-system-x86_64 -m 10000000
Unexpected error in ram_block_add() at /work/armbru/qemu/exec.c:1456:
upstream-qemu: cannot set up guest memory 'pc.ram': Cannot allocate memory
Aborted (core dumped)
Root cause: commit ef701d7 screwed up handling of out-of-memory
conditions. Before the commit, we report the error and exit(1), in
one place, ram_block_add(). The commit lifts the error handling up
the call chain some, to three places. Fine. Except it uses
&error_abort in these places, changing the behavior from exit(1) to
abort(), and thus undoing the work of commit 3922825 "exec: Don't
abort when we can't allocate guest memory".
The three places are:
* memory_region_init_ram()
Commit 4994653 (right after commit ef701d7) lifted the error
handling further, through memory_region_init_ram(), multiplying the
incorrect use of &error_abort. Later on, imitation of existing
(bad) code may have created more.
* memory_region_init_ram_ptr()
The &error_abort is still there.
* memory_region_init_rom_device()
Doesn't need fixing, because commit 33e0eb5 (soon after commit
ef701d7) lifted the error handling further, and in the process
changed it from &error_abort to passing it up the call chain.
Correct, because the callers are realize() methods.
Fix the error handling after memory_region_init_ram() with a
Coccinelle semantic patch:
@r@
expression mr, owner, name, size, err;
position p;
@@
memory_region_init_ram(mr, owner, name, size,
(
- &error_abort
+ &error_fatal
|
err@p
)
);
@script:python@
p << r.p;
@@
print "%s:%s:%s" % (p[0].file, p[0].line, p[0].column)
When the last argument is &error_abort, it gets replaced by
&error_fatal. This is the fix.
If the last argument is anything else, its position is reported. This
lets us check the fix is complete. Four positions get reported:
* ram_backend_memory_alloc()
Error is passed up the call chain, ultimately through
user_creatable_complete(). As far as I can tell, it's callers all
handle the error sanely.
* fsl_imx25_realize(), fsl_imx31_realize(), dp8393x_realize()
DeviceClass.realize() methods, errors handled sanely further up the
call chain.
We're good. Test case again behaves:
$ qemu-system-x86_64 -m 10000000
qemu-system-x86_64: cannot set up guest memory 'pc.ram': Cannot allocate memory
[Exit 1 ]
The next commits will repair the rest of commit ef701d7's damage.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1441983105-26376-3-git-send-email-armbru@redhat.com>
Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
2015-09-11 17:51:43 +03:00
|
|
|
buffers_size, &error_fatal);
|
2011-12-20 17:59:12 +04:00
|
|
|
vmstate_register_ram_global(&s->buffers);
|
2011-08-08 22:32:25 +04:00
|
|
|
s->rx0_buf = memory_region_get_ram_ptr(&s->buffers);
|
2011-04-13 02:29:36 +04:00
|
|
|
s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
|
|
|
|
s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE;
|
|
|
|
|
2013-07-27 14:13:17 +04:00
|
|
|
sysbus_init_mmio(sbd, &s->buffers);
|
2011-04-13 02:29:36 +04:00
|
|
|
|
2011-03-08 01:32:35 +03:00
|
|
|
qemu_macaddr_default_if_unset(&s->conf.macaddr);
|
2011-04-13 02:29:36 +04:00
|
|
|
s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf,
|
2013-07-27 14:13:17 +04:00
|
|
|
object_get_typename(OBJECT(dev)), dev->id, s);
|
2013-01-30 15:12:22 +04:00
|
|
|
qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
|
2011-03-08 01:32:35 +03:00
|
|
|
}
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static const VMStateDescription vmstate_milkymist_minimac2_mdio = {
|
|
|
|
.name = "milkymist-minimac2-mdio",
|
2011-03-08 01:32:35 +03:00
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
2014-04-16 18:01:33 +04:00
|
|
|
.fields = (VMStateField[]) {
|
2011-04-13 02:29:36 +04:00
|
|
|
VMSTATE_INT32(last_clk, MilkymistMinimac2MdioState),
|
|
|
|
VMSTATE_INT32(count, MilkymistMinimac2MdioState),
|
|
|
|
VMSTATE_UINT32(data, MilkymistMinimac2MdioState),
|
|
|
|
VMSTATE_UINT16(data_out, MilkymistMinimac2MdioState),
|
|
|
|
VMSTATE_INT32(state, MilkymistMinimac2MdioState),
|
|
|
|
VMSTATE_UINT8(phy_addr, MilkymistMinimac2MdioState),
|
|
|
|
VMSTATE_UINT8(reg_addr, MilkymistMinimac2MdioState),
|
2011-03-08 01:32:35 +03:00
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-04-13 02:29:36 +04:00
|
|
|
static const VMStateDescription vmstate_milkymist_minimac2 = {
|
|
|
|
.name = "milkymist-minimac2",
|
2011-03-08 01:32:35 +03:00
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
2014-04-16 18:01:33 +04:00
|
|
|
.fields = (VMStateField[]) {
|
2011-04-13 02:29:36 +04:00
|
|
|
VMSTATE_UINT32_ARRAY(regs, MilkymistMinimac2State, R_MAX),
|
|
|
|
VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimac2State, R_PHY_MAX),
|
|
|
|
VMSTATE_STRUCT(mdio, MilkymistMinimac2State, 0,
|
|
|
|
vmstate_milkymist_minimac2_mdio, MilkymistMinimac2MdioState),
|
2011-03-08 01:32:35 +03:00
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-01-24 23:12:29 +04:00
|
|
|
static Property milkymist_minimac2_properties[] = {
|
|
|
|
DEFINE_NIC_PROPERTIES(MilkymistMinimac2State, conf),
|
|
|
|
DEFINE_PROP_STRING("phy_model", MilkymistMinimac2State, phy_model),
|
|
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
|
|
};
|
|
|
|
|
|
|
|
static void milkymist_minimac2_class_init(ObjectClass *klass, void *data)
|
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2012-01-24 23:12:29 +04:00
|
|
|
|
2018-10-01 09:37:57 +03:00
|
|
|
dc->realize = milkymist_minimac2_realize;
|
2011-12-08 07:34:16 +04:00
|
|
|
dc->reset = milkymist_minimac2_reset;
|
|
|
|
dc->vmsd = &vmstate_milkymist_minimac2;
|
2020-01-10 18:30:32 +03:00
|
|
|
device_class_set_props(dc, milkymist_minimac2_properties);
|
2012-01-24 23:12:29 +04:00
|
|
|
}
|
|
|
|
|
2013-01-10 19:19:07 +04:00
|
|
|
static const TypeInfo milkymist_minimac2_info = {
|
2013-07-27 14:13:17 +04:00
|
|
|
.name = TYPE_MILKYMIST_MINIMAC2,
|
2011-12-08 07:34:16 +04:00
|
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
|
|
.instance_size = sizeof(MilkymistMinimac2State),
|
|
|
|
.class_init = milkymist_minimac2_class_init,
|
2011-03-08 01:32:35 +03:00
|
|
|
};
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
static void milkymist_minimac2_register_types(void)
|
2011-03-08 01:32:35 +03:00
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
type_register_static(&milkymist_minimac2_info);
|
2011-03-08 01:32:35 +03:00
|
|
|
}
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
type_init(milkymist_minimac2_register_types)
|