Compare commits
55 Commits
numa-next-
...
v0.14.0
Author | SHA1 | Date | |
---|---|---|---|
|
0850f81099 | ||
|
6a7999b222 | ||
|
e3c8fc83aa | ||
|
bd2483faf1 | ||
|
7083b66b45 | ||
|
9de12c453d | ||
|
c7e9df3bc6 | ||
|
038a866f81 | ||
|
64a216f58e | ||
|
12597b0608 | ||
|
e37dcdfb8d | ||
|
5c9596112c | ||
|
16e07bc282 | ||
|
607a375709 | ||
|
ac12a5af0b | ||
|
b03088c32f | ||
|
eee37d310c | ||
|
28637533d6 | ||
|
23e4cff984 | ||
|
0893194783 | ||
|
e5f1c19665 | ||
|
343c1de916 | ||
|
b75568889f | ||
|
a3dfab563e | ||
|
c1f1ffff21 | ||
|
44d631a001 | ||
|
28b0e1cd0e | ||
|
ddebe9d473 | ||
|
f17f8b687c | ||
|
d27dd7e1a2 | ||
|
c3a965c943 | ||
|
abc9997416 | ||
|
ed97a4f2cf | ||
|
8f7dfd6bda | ||
|
e81c113d28 | ||
|
d4e6590ab8 | ||
|
f188c02db7 | ||
|
7edb1c3a51 | ||
|
fd08f20c23 | ||
|
8798240196 | ||
|
ffbda4e682 | ||
|
81cd8f6047 | ||
|
1299aa0d03 | ||
|
8faaf42a4c | ||
|
f05929b182 | ||
|
bc3aaac57b | ||
|
602c075070 | ||
|
cb5281b199 | ||
|
9a121a2fbf | ||
|
366c2452b1 | ||
|
bbd9827cc7 | ||
|
d7f88b4bbd | ||
|
ea01a58014 | ||
|
0833073edf | ||
|
0e1272f22b |
@@ -244,6 +244,7 @@ hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o
|
||||
hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o
|
||||
hw-obj-$(CONFIG_IDE_VIA) += ide/via.o
|
||||
hw-obj-$(CONFIG_AHCI) += ide/ahci.o
|
||||
hw-obj-$(CONFIG_AHCI) += ide/ich.o
|
||||
|
||||
# SCSI layer
|
||||
hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include "monitor.h"
|
||||
#include "block-migration.h"
|
||||
#include "migration.h"
|
||||
#include "blockdev.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS)
|
||||
@@ -299,6 +300,8 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
||||
bmds->completed_sectors = 0;
|
||||
bmds->shared_base = block_mig_state.shared_base;
|
||||
alloc_aio_bitmap(bmds);
|
||||
drive_get_ref(drive_get_by_blockdev(bs));
|
||||
bdrv_set_in_use(bs, 1);
|
||||
|
||||
block_mig_state.total_sector_sum += sectors;
|
||||
|
||||
@@ -533,8 +536,12 @@ static void blk_mig_cleanup(Monitor *mon)
|
||||
BlkMigDevState *bmds;
|
||||
BlkMigBlock *blk;
|
||||
|
||||
set_dirty_tracking(0);
|
||||
|
||||
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
|
||||
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
|
||||
bdrv_set_in_use(bmds->bs, 0);
|
||||
drive_put_ref(drive_get_by_blockdev(bmds->bs));
|
||||
qemu_free(bmds->aio_bitmap);
|
||||
qemu_free(bmds);
|
||||
}
|
||||
@@ -545,8 +552,6 @@ static void blk_mig_cleanup(Monitor *mon)
|
||||
qemu_free(blk);
|
||||
}
|
||||
|
||||
set_dirty_tracking(0);
|
||||
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
|
||||
|
13
block.c
13
block.c
@@ -1132,6 +1132,8 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
|
||||
return -ENOTSUP;
|
||||
if (bs->read_only)
|
||||
return -EACCES;
|
||||
if (bdrv_in_use(bs))
|
||||
return -EBUSY;
|
||||
ret = drv->bdrv_truncate(bs, offset);
|
||||
if (ret == 0) {
|
||||
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
|
||||
@@ -2774,6 +2776,17 @@ int64_t bdrv_get_dirty_count(BlockDriverState *bs)
|
||||
return bs->dirty_count;
|
||||
}
|
||||
|
||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
||||
{
|
||||
assert(bs->in_use != in_use);
|
||||
bs->in_use = in_use;
|
||||
}
|
||||
|
||||
int bdrv_in_use(BlockDriverState *bs)
|
||||
{
|
||||
return bs->in_use;
|
||||
}
|
||||
|
||||
int bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags)
|
||||
|
2
block.h
2
block.h
@@ -241,6 +241,8 @@ void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors);
|
||||
int64_t bdrv_get_dirty_count(BlockDriverState *bs);
|
||||
|
||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
|
||||
int bdrv_in_use(BlockDriverState *bs);
|
||||
|
||||
typedef enum {
|
||||
BLKDBG_L1_UPDATE,
|
||||
|
@@ -515,13 +515,16 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* FIXME Order */
|
||||
if (l2_offset)
|
||||
qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
|
||||
/* First allocate a new L2 table (and do COW if needed) */
|
||||
ret = l2_allocate(bs, l1_index, &l2_table);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Then decrease the refcount of the old table */
|
||||
if (l2_offset) {
|
||||
qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
|
||||
}
|
||||
l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED;
|
||||
}
|
||||
|
||||
@@ -878,11 +881,11 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED);
|
||||
ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
if (decompress_buffer(s->cluster_cache, s->cluster_size,
|
||||
s->cluster_data + sector_offset, csize) < 0) {
|
||||
return -1;
|
||||
return -EIO;
|
||||
}
|
||||
s->cluster_cache_offset = coffset;
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "aes.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu-error.h"
|
||||
#include "qerror.h"
|
||||
|
||||
/*
|
||||
Differences with QCOW:
|
||||
@@ -59,7 +60,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
|
||||
if (buf_size >= sizeof(QCowHeader) &&
|
||||
be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == QCOW_VERSION)
|
||||
be32_to_cpu(cow_header->version) >= QCOW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
@@ -163,10 +164,18 @@ static int qcow2_open(BlockDriverState *bs, int flags)
|
||||
be64_to_cpus(&header.snapshots_offset);
|
||||
be32_to_cpus(&header.nb_snapshots);
|
||||
|
||||
if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) {
|
||||
if (header.magic != QCOW_MAGIC) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (header.version != QCOW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version), "QCOW version %d", header.version);
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "qcow2", version);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
if (header.cluster_bits < MIN_CLUSTER_BITS ||
|
||||
header.cluster_bits > MAX_CLUSTER_BITS) {
|
||||
ret = -EINVAL;
|
||||
@@ -355,7 +364,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
else
|
||||
n1 = bs->total_sectors - sector_num;
|
||||
|
||||
qemu_iovec_memset(qiov, 0, 512 * (nb_sectors - n1));
|
||||
qemu_iovec_memset_skip(qiov, 0, 512 * (nb_sectors - n1), 512 * n1);
|
||||
|
||||
return n1;
|
||||
}
|
||||
@@ -478,10 +487,11 @@ static void qcow2_aio_read_cb(void *opaque, int ret)
|
||||
if (n1 > 0) {
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
|
||||
&acb->hd_qiov, acb->cur_nr_sectors,
|
||||
qcow2_aio_read_cb, acb);
|
||||
if (acb->hd_aiocb == NULL)
|
||||
&acb->hd_qiov, n1, qcow2_aio_read_cb, acb);
|
||||
if (acb->hd_aiocb == NULL) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb);
|
||||
if (ret < 0)
|
||||
@@ -496,8 +506,10 @@ static void qcow2_aio_read_cb(void *opaque, int ret)
|
||||
}
|
||||
} else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* add AIO support for compressed blocks ? */
|
||||
if (qcow2_decompress_cluster(bs, acb->cluster_offset) < 0)
|
||||
ret = qcow2_decompress_cluster(bs, acb->cluster_offset);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
qemu_iovec_from_buffer(&acb->hd_qiov,
|
||||
s->cluster_cache + index_in_cluster * 512,
|
||||
@@ -975,7 +987,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
*/
|
||||
BlockDriver* drv = bdrv_find_format("qcow2");
|
||||
assert(drv != NULL);
|
||||
ret = bdrv_open(bs, filename, BDRV_O_RDWR | BDRV_O_NO_FLUSH, drv);
|
||||
ret = bdrv_open(bs, filename,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "trace.h"
|
||||
#include "qed.h"
|
||||
#include "qerror.h"
|
||||
|
||||
static void qed_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
@@ -311,7 +312,13 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (s->header.features & ~QED_FEATURE_MASK) {
|
||||
return -ENOTSUP; /* image uses unsupported feature bits */
|
||||
/* image uses unsupported feature bits */
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%" PRIx64,
|
||||
s->header.features & ~QED_FEATURE_MASK);
|
||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "QED", buf);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!qed_is_cluster_size_valid(s->header.cluster_size)) {
|
||||
return -EINVAL;
|
||||
|
@@ -119,13 +119,13 @@ void uuid_unparse(const uuid_t uu, char *out);
|
||||
#if !defined(CONFIG_UUID)
|
||||
void uuid_generate(uuid_t out)
|
||||
{
|
||||
memset(out, 0, sizeof(out));
|
||||
memset(out, 0, sizeof(uuid_t));
|
||||
}
|
||||
|
||||
int uuid_is_null(const uuid_t uu)
|
||||
{
|
||||
uuid_t null_uuid = { 0 };
|
||||
return memcmp(uu, null_uuid, sizeof(uu)) == 0;
|
||||
return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0;
|
||||
}
|
||||
|
||||
void uuid_unparse(const uuid_t uu, char *out)
|
||||
|
@@ -199,6 +199,7 @@ struct BlockDriverState {
|
||||
char device_name[32];
|
||||
unsigned long *dirty_bitmap;
|
||||
int64_t dirty_count;
|
||||
int in_use; /* users other than guest access, eg. block migration */
|
||||
QTAILQ_ENTRY(BlockDriverState) list;
|
||||
void *private;
|
||||
};
|
||||
|
34
blockdev.c
34
blockdev.c
@@ -71,7 +71,7 @@ void blockdev_auto_del(BlockDriverState *bs)
|
||||
DriveInfo *dinfo = drive_get_by_blockdev(bs);
|
||||
|
||||
if (dinfo && dinfo->auto_del) {
|
||||
drive_uninit(dinfo);
|
||||
drive_put_ref(dinfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,14 +178,28 @@ static void bdrv_format_print(void *opaque, const char *name)
|
||||
error_printf(" %s", name);
|
||||
}
|
||||
|
||||
void drive_uninit(DriveInfo *dinfo)
|
||||
static void drive_uninit(DriveInfo *dinfo)
|
||||
{
|
||||
qemu_opts_del(dinfo->opts);
|
||||
bdrv_delete(dinfo->bdrv);
|
||||
qemu_free(dinfo->id);
|
||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||
qemu_free(dinfo);
|
||||
}
|
||||
|
||||
void drive_put_ref(DriveInfo *dinfo)
|
||||
{
|
||||
assert(dinfo->refcount);
|
||||
if (--dinfo->refcount == 0) {
|
||||
drive_uninit(dinfo);
|
||||
}
|
||||
}
|
||||
|
||||
void drive_get_ref(DriveInfo *dinfo)
|
||||
{
|
||||
dinfo->refcount++;
|
||||
}
|
||||
|
||||
static int parse_block_error_action(const char *buf, int is_read)
|
||||
{
|
||||
if (!strcmp(buf, "ignore")) {
|
||||
@@ -453,6 +467,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
dinfo->bus = bus_id;
|
||||
dinfo->unit = unit_id;
|
||||
dinfo->opts = opts;
|
||||
dinfo->refcount = 1;
|
||||
if (serial)
|
||||
strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
|
||||
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
|
||||
@@ -511,7 +526,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
} else if (ro == 1) {
|
||||
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
|
||||
error_report("readonly not supported by this bus type");
|
||||
return NULL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,12 +536,19 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
if (ret < 0) {
|
||||
error_report("could not open disk image %s: %s",
|
||||
file, strerror(-ret));
|
||||
return NULL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bdrv_key_required(dinfo->bdrv))
|
||||
autostart = 0;
|
||||
return dinfo;
|
||||
|
||||
err:
|
||||
bdrv_delete(dinfo->bdrv);
|
||||
qemu_free(dinfo->id);
|
||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||
qemu_free(dinfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void do_commit(Monitor *mon, const QDict *qdict)
|
||||
@@ -712,6 +734,10 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
qerror_report(QERR_DEVICE_NOT_FOUND, id);
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_in_use(bs)) {
|
||||
qerror_report(QERR_DEVICE_IN_USE, id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* quiesce block driver; prevent further io */
|
||||
qemu_aio_flush();
|
||||
|
@@ -36,13 +36,15 @@ struct DriveInfo {
|
||||
QemuOpts *opts;
|
||||
char serial[BLOCK_SERIAL_STRLEN + 1];
|
||||
QTAILQ_ENTRY(DriveInfo) next;
|
||||
int refcount;
|
||||
};
|
||||
|
||||
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
|
||||
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
|
||||
int drive_get_max_bus(BlockInterfaceType type);
|
||||
DriveInfo *drive_get_next(BlockInterfaceType type);
|
||||
void drive_uninit(DriveInfo *dinfo);
|
||||
void drive_get_ref(DriveInfo *dinfo);
|
||||
void drive_put_ref(DriveInfo *dinfo);
|
||||
DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
|
||||
|
||||
QemuOpts *drive_def(const char *optstr);
|
||||
|
31
cutils.c
31
cutils.c
@@ -267,6 +267,37 @@ void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count)
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count,
|
||||
size_t skip)
|
||||
{
|
||||
int i;
|
||||
size_t done;
|
||||
void *iov_base;
|
||||
uint64_t iov_len;
|
||||
|
||||
done = 0;
|
||||
for (i = 0; (i < qiov->niov) && (done != count); i++) {
|
||||
if (skip >= qiov->iov[i].iov_len) {
|
||||
/* Skip the whole iov */
|
||||
skip -= qiov->iov[i].iov_len;
|
||||
continue;
|
||||
} else {
|
||||
/* Skip only part (or nothing) of the iov */
|
||||
iov_base = (uint8_t*) qiov->iov[i].iov_base + skip;
|
||||
iov_len = qiov->iov[i].iov_len - skip;
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
if (done + iov_len > count) {
|
||||
memset(iov_base, c, count - done);
|
||||
break;
|
||||
} else {
|
||||
memset(iov_base, c, iov_len);
|
||||
}
|
||||
done += iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Sets a specific flag */
|
||||
int fcntl_setfl(int fd, int flag)
|
||||
|
@@ -822,7 +822,7 @@ ETEXI
|
||||
|
||||
{
|
||||
.name = "snapshot_blkdev",
|
||||
.args_type = "device:s,snapshot_file:s?,format:s?",
|
||||
.args_type = "device:B,snapshot_file:s?,format:s?",
|
||||
.params = "device [new-image-file] [format]",
|
||||
.help = "initiates a live snapshot\n\t\t\t"
|
||||
"of device. If a new image file is specified, the\n\t\t\t"
|
||||
|
53
hw/apic.c
53
hw/apic.c
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
#include "hw.h"
|
||||
#include "apic.h"
|
||||
#include "ioapic.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "host-utils.h"
|
||||
#include "sysbus.h"
|
||||
@@ -57,7 +58,8 @@
|
||||
|
||||
#define ESR_ILLEGAL_ADDRESS (1 << 7)
|
||||
|
||||
#define APIC_SV_ENABLE (1 << 8)
|
||||
#define APIC_SV_DIRECTED_IO (1<<12)
|
||||
#define APIC_SV_ENABLE (1<<8)
|
||||
|
||||
#define MAX_APICS 255
|
||||
#define MAX_APIC_WORDS 8
|
||||
@@ -370,19 +372,36 @@ static int apic_get_arb_pri(APICState *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* <0 - low prio interrupt,
|
||||
* 0 - no interrupt,
|
||||
* >0 - interrupt number
|
||||
*/
|
||||
static int apic_irq_pending(APICState *s)
|
||||
{
|
||||
int irrv, ppr;
|
||||
irrv = get_highest_priority_int(s->irr);
|
||||
if (irrv < 0) {
|
||||
return 0;
|
||||
}
|
||||
ppr = apic_get_ppr(s);
|
||||
if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return irrv;
|
||||
}
|
||||
|
||||
/* signal the CPU if an irq is pending */
|
||||
static void apic_update_irq(APICState *s)
|
||||
{
|
||||
int irrv, ppr;
|
||||
if (!(s->spurious_vec & APIC_SV_ENABLE))
|
||||
if (!(s->spurious_vec & APIC_SV_ENABLE)) {
|
||||
return;
|
||||
irrv = get_highest_priority_int(s->irr);
|
||||
if (irrv < 0)
|
||||
return;
|
||||
ppr = apic_get_ppr(s);
|
||||
if (ppr && (irrv & 0xf0) <= (ppr & 0xf0))
|
||||
return;
|
||||
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
if (apic_irq_pending(s) > 0) {
|
||||
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
void apic_reset_irq_delivered(void)
|
||||
@@ -420,8 +439,9 @@ static void apic_eoi(APICState *s)
|
||||
if (isrv < 0)
|
||||
return;
|
||||
reset_bit(s->isr, isrv);
|
||||
/* XXX: send the EOI packet to the APIC bus to allow the I/O APIC to
|
||||
set the remote IRR bit for level triggered interrupts. */
|
||||
if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && get_bit(s->tmr, isrv)) {
|
||||
ioapic_eoi_broadcast(isrv);
|
||||
}
|
||||
apic_update_irq(s);
|
||||
}
|
||||
|
||||
@@ -587,12 +607,13 @@ int apic_get_interrupt(DeviceState *d)
|
||||
if (!(s->spurious_vec & APIC_SV_ENABLE))
|
||||
return -1;
|
||||
|
||||
/* XXX: spurious IRQ handling */
|
||||
intno = get_highest_priority_int(s->irr);
|
||||
if (intno < 0)
|
||||
intno = apic_irq_pending(s);
|
||||
|
||||
if (intno == 0) {
|
||||
return -1;
|
||||
if (s->tpr && intno <= s->tpr)
|
||||
} else if (intno < 0) {
|
||||
return s->spurious_vec & 0xff;
|
||||
}
|
||||
reset_bit(s->irr, intno);
|
||||
set_bit(s->isr, intno);
|
||||
apic_update_irq(s);
|
||||
|
485
hw/ide/ahci.c
485
hw/ide/ahci.c
@@ -19,47 +19,6 @@
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
* lspci dump of a ICH-9 real device in IDE mode (hopefully close enough):
|
||||
*
|
||||
* 00:1f.2 SATA controller [0106]: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922] (rev 02) (prog-if 01 [AHCI 1.0])
|
||||
* Subsystem: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922]
|
||||
* Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
|
||||
* Status: Cap+ 66MHz+ UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
|
||||
* Latency: 0
|
||||
* Interrupt: pin B routed to IRQ 222
|
||||
* Region 0: I/O ports at d000 [size=8]
|
||||
* Region 1: I/O ports at cc00 [size=4]
|
||||
* Region 2: I/O ports at c880 [size=8]
|
||||
* Region 3: I/O ports at c800 [size=4]
|
||||
* Region 4: I/O ports at c480 [size=32]
|
||||
* Region 5: Memory at febf9000 (32-bit, non-prefetchable) [size=2K]
|
||||
* Capabilities: [80] Message Signalled Interrupts: Mask- 64bit- Count=1/16 Enable+
|
||||
* Address: fee0f00c Data: 41d9
|
||||
* Capabilities: [70] Power Management version 3
|
||||
* Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot+,D3cold-)
|
||||
* Status: D0 PME-Enable- DSel=0 DScale=0 PME-
|
||||
* Capabilities: [a8] SATA HBA <?>
|
||||
* Capabilities: [b0] Vendor Specific Information <?>
|
||||
* Kernel driver in use: ahci
|
||||
* Kernel modules: ahci
|
||||
* 00: 86 80 22 29 07 04 b0 02 02 01 06 01 00 00 00 00
|
||||
* 10: 01 d0 00 00 01 cc 00 00 81 c8 00 00 01 c8 00 00
|
||||
* 20: 81 c4 00 00 00 90 bf fe 00 00 00 00 86 80 22 29
|
||||
* 30: 00 00 00 00 80 00 00 00 00 00 00 00 0f 02 00 00
|
||||
* 40: 00 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* 70: 01 a8 03 40 08 00 00 00 00 00 00 00 00 00 00 00
|
||||
* 80: 05 70 09 00 0c f0 e0 fe d9 41 00 00 00 00 00 00
|
||||
* 90: 40 00 0f 82 93 01 00 00 00 00 00 00 00 00 00 00
|
||||
* a0: ac 00 00 00 0a 00 12 00 12 b0 10 00 48 00 00 00
|
||||
* b0: 09 00 06 20 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* f0: 00 00 00 00 00 00 00 00 86 0f 02 00 00 00 00 00
|
||||
*
|
||||
*/
|
||||
|
||||
#include <hw/hw.h>
|
||||
@@ -72,6 +31,7 @@
|
||||
#include "cpu-common.h"
|
||||
#include "internal.h"
|
||||
#include <hw/ide/pci.h>
|
||||
#include <hw/ide/ahci.h>
|
||||
|
||||
/* #define DEBUG_AHCI */
|
||||
|
||||
@@ -83,308 +43,11 @@ do { fprintf(stderr, "ahci: %s: [%d] ", __FUNCTION__, port); \
|
||||
#define DPRINTF(port, fmt, ...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define AHCI_PCI_BAR 5
|
||||
#define AHCI_MAX_PORTS 32
|
||||
#define AHCI_MAX_SG 168 /* hardware max is 64K */
|
||||
#define AHCI_DMA_BOUNDARY 0xffffffff
|
||||
#define AHCI_USE_CLUSTERING 0
|
||||
#define AHCI_MAX_CMDS 32
|
||||
#define AHCI_CMD_SZ 32
|
||||
#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ)
|
||||
#define AHCI_RX_FIS_SZ 256
|
||||
#define AHCI_CMD_TBL_CDB 0x40
|
||||
#define AHCI_CMD_TBL_HDR_SZ 0x80
|
||||
#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16))
|
||||
#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS)
|
||||
#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \
|
||||
AHCI_RX_FIS_SZ)
|
||||
|
||||
#define AHCI_IRQ_ON_SG (1 << 31)
|
||||
#define AHCI_CMD_ATAPI (1 << 5)
|
||||
#define AHCI_CMD_WRITE (1 << 6)
|
||||
#define AHCI_CMD_PREFETCH (1 << 7)
|
||||
#define AHCI_CMD_RESET (1 << 8)
|
||||
#define AHCI_CMD_CLR_BUSY (1 << 10)
|
||||
|
||||
#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */
|
||||
#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */
|
||||
#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */
|
||||
|
||||
/* global controller registers */
|
||||
#define HOST_CAP 0x00 /* host capabilities */
|
||||
#define HOST_CTL 0x04 /* global host control */
|
||||
#define HOST_IRQ_STAT 0x08 /* interrupt status */
|
||||
#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */
|
||||
#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */
|
||||
|
||||
/* HOST_CTL bits */
|
||||
#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */
|
||||
#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */
|
||||
#define HOST_CTL_AHCI_EN (1 << 31) /* AHCI enabled */
|
||||
|
||||
/* HOST_CAP bits */
|
||||
#define HOST_CAP_SSC (1 << 14) /* Slumber capable */
|
||||
#define HOST_CAP_AHCI (1 << 18) /* AHCI only */
|
||||
#define HOST_CAP_CLO (1 << 24) /* Command List Override support */
|
||||
#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */
|
||||
#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */
|
||||
#define HOST_CAP_64 (1 << 31) /* PCI DAC (64-bit DMA) support */
|
||||
|
||||
/* registers for each SATA port */
|
||||
#define PORT_LST_ADDR 0x00 /* command list DMA addr */
|
||||
#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */
|
||||
#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */
|
||||
#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */
|
||||
#define PORT_IRQ_STAT 0x10 /* interrupt status */
|
||||
#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */
|
||||
#define PORT_CMD 0x18 /* port command */
|
||||
#define PORT_TFDATA 0x20 /* taskfile data */
|
||||
#define PORT_SIG 0x24 /* device TF signature */
|
||||
#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */
|
||||
#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */
|
||||
#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */
|
||||
#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */
|
||||
#define PORT_CMD_ISSUE 0x38 /* command issue */
|
||||
#define PORT_RESERVED 0x3c /* reserved */
|
||||
|
||||
/* PORT_IRQ_{STAT,MASK} bits */
|
||||
#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */
|
||||
#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */
|
||||
#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */
|
||||
#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */
|
||||
#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */
|
||||
#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */
|
||||
#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */
|
||||
#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */
|
||||
|
||||
#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */
|
||||
#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */
|
||||
#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */
|
||||
#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */
|
||||
#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */
|
||||
#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */
|
||||
#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */
|
||||
#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */
|
||||
#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */
|
||||
|
||||
#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \
|
||||
PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \
|
||||
PORT_IRQ_UNK_FIS)
|
||||
#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \
|
||||
PORT_IRQ_HBUS_DATA_ERR)
|
||||
#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \
|
||||
PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \
|
||||
PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS)
|
||||
|
||||
/* PORT_CMD bits */
|
||||
#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */
|
||||
#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */
|
||||
#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */
|
||||
#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */
|
||||
#define PORT_CMD_CLO (1 << 3) /* Command list override */
|
||||
#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */
|
||||
#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */
|
||||
#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */
|
||||
|
||||
#define PORT_CMD_ICC_MASK (0xf << 28) /* i/f ICC state mask */
|
||||
#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */
|
||||
#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */
|
||||
#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */
|
||||
|
||||
#define PORT_IRQ_STAT_DHRS (1 << 0) /* Device to Host Register FIS */
|
||||
#define PORT_IRQ_STAT_PSS (1 << 1) /* PIO Setup FIS */
|
||||
#define PORT_IRQ_STAT_DSS (1 << 2) /* DMA Setup FIS */
|
||||
#define PORT_IRQ_STAT_SDBS (1 << 3) /* Set Device Bits */
|
||||
#define PORT_IRQ_STAT_UFS (1 << 4) /* Unknown FIS */
|
||||
#define PORT_IRQ_STAT_DPS (1 << 5) /* Descriptor Processed */
|
||||
#define PORT_IRQ_STAT_PCS (1 << 6) /* Port Connect Change Status */
|
||||
#define PORT_IRQ_STAT_DMPS (1 << 7) /* Device Mechanical Presence
|
||||
Status */
|
||||
#define PORT_IRQ_STAT_PRCS (1 << 22) /* File Ready Status */
|
||||
#define PORT_IRQ_STAT_IPMS (1 << 23) /* Incorrect Port Multiplier
|
||||
Status */
|
||||
#define PORT_IRQ_STAT_OFS (1 << 24) /* Overflow Status */
|
||||
#define PORT_IRQ_STAT_INFS (1 << 26) /* Interface Non-Fatal Error
|
||||
Status */
|
||||
#define PORT_IRQ_STAT_IFS (1 << 27) /* Interface Fatal Error */
|
||||
#define PORT_IRQ_STAT_HBDS (1 << 28) /* Host Bus Data Error Status */
|
||||
#define PORT_IRQ_STAT_HBFS (1 << 29) /* Host Bus Fatal Error Status */
|
||||
#define PORT_IRQ_STAT_TFES (1 << 30) /* Task File Error Status */
|
||||
#define PORT_IRQ_STAT_CPDS (1 << 31) /* Code Port Detect Status */
|
||||
|
||||
/* ap->flags bits */
|
||||
#define AHCI_FLAG_NO_NCQ (1 << 24)
|
||||
#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */
|
||||
#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */
|
||||
#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */
|
||||
#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */
|
||||
|
||||
#define ATA_SRST (1 << 2) /* software reset */
|
||||
|
||||
#define STATE_RUN 0
|
||||
#define STATE_RESET 1
|
||||
|
||||
#define SATA_SCR_SSTATUS_DET_NODEV 0x0
|
||||
#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3
|
||||
|
||||
#define SATA_SCR_SSTATUS_SPD_NODEV 0x00
|
||||
#define SATA_SCR_SSTATUS_SPD_GEN1 0x10
|
||||
|
||||
#define SATA_SCR_SSTATUS_IPM_NODEV 0x000
|
||||
#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100
|
||||
|
||||
#define AHCI_SCR_SCTL_DET 0xf
|
||||
|
||||
#define SATA_FIS_TYPE_REGISTER_H2D 0x27
|
||||
#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80
|
||||
|
||||
#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f
|
||||
#define AHCI_CMD_HDR_PRDT_LEN 16
|
||||
|
||||
#define SATA_SIGNATURE_CDROM 0xeb140000
|
||||
#define SATA_SIGNATURE_DISK 0x00000101
|
||||
|
||||
#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20
|
||||
/* Shouldn't this be 0x2c? */
|
||||
|
||||
#define SATA_PORTS 4
|
||||
|
||||
#define AHCI_PORT_REGS_START_ADDR 0x100
|
||||
#define AHCI_PORT_REGS_END_ADDR (AHCI_PORT_REGS_START_ADDR + SATA_PORTS * 0x80)
|
||||
#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f
|
||||
|
||||
#define AHCI_NUM_COMMAND_SLOTS 31
|
||||
#define AHCI_SUPPORTED_SPEED 20
|
||||
#define AHCI_SUPPORTED_SPEED_GEN1 1
|
||||
#define AHCI_VERSION_1_0 0x10000
|
||||
|
||||
#define AHCI_PROGMODE_MAJOR_REV_1 1
|
||||
|
||||
#define AHCI_COMMAND_TABLE_ACMD 0x40
|
||||
|
||||
#define IDE_FEATURE_DMA 1
|
||||
|
||||
#define READ_FPDMA_QUEUED 0x60
|
||||
#define WRITE_FPDMA_QUEUED 0x61
|
||||
|
||||
#define RES_FIS_DSFIS 0x00
|
||||
#define RES_FIS_PSFIS 0x20
|
||||
#define RES_FIS_RFIS 0x40
|
||||
#define RES_FIS_SDBFIS 0x58
|
||||
#define RES_FIS_UFIS 0x60
|
||||
|
||||
typedef struct AHCIControlRegs {
|
||||
uint32_t cap;
|
||||
uint32_t ghc;
|
||||
uint32_t irqstatus;
|
||||
uint32_t impl;
|
||||
uint32_t version;
|
||||
} AHCIControlRegs;
|
||||
|
||||
typedef struct AHCIPortRegs {
|
||||
uint32_t lst_addr;
|
||||
uint32_t lst_addr_hi;
|
||||
uint32_t fis_addr;
|
||||
uint32_t fis_addr_hi;
|
||||
uint32_t irq_stat;
|
||||
uint32_t irq_mask;
|
||||
uint32_t cmd;
|
||||
uint32_t unused0;
|
||||
uint32_t tfdata;
|
||||
uint32_t sig;
|
||||
uint32_t scr_stat;
|
||||
uint32_t scr_ctl;
|
||||
uint32_t scr_err;
|
||||
uint32_t scr_act;
|
||||
uint32_t cmd_issue;
|
||||
uint32_t reserved;
|
||||
} AHCIPortRegs;
|
||||
|
||||
typedef struct AHCICmdHdr {
|
||||
uint32_t opts;
|
||||
uint32_t status;
|
||||
uint64_t tbl_addr;
|
||||
uint32_t reserved[4];
|
||||
} __attribute__ ((packed)) AHCICmdHdr;
|
||||
|
||||
typedef struct AHCI_SG {
|
||||
uint64_t addr;
|
||||
uint32_t reserved;
|
||||
uint32_t flags_size;
|
||||
} __attribute__ ((packed)) AHCI_SG;
|
||||
|
||||
typedef struct AHCIDevice AHCIDevice;
|
||||
|
||||
typedef struct NCQTransferState {
|
||||
AHCIDevice *drive;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
QEMUSGList sglist;
|
||||
int is_read;
|
||||
uint16_t sector_count;
|
||||
uint64_t lba;
|
||||
uint8_t tag;
|
||||
int slot;
|
||||
int used;
|
||||
} NCQTransferState;
|
||||
|
||||
struct AHCIDevice {
|
||||
IDEDMA dma;
|
||||
IDEBus port;
|
||||
int port_no;
|
||||
uint32_t port_state;
|
||||
uint32_t finished;
|
||||
AHCIPortRegs port_regs;
|
||||
struct AHCIState *hba;
|
||||
QEMUBH *check_bh;
|
||||
uint8_t *lst;
|
||||
uint8_t *res_fis;
|
||||
int dma_status;
|
||||
int done_atapi_packet;
|
||||
int busy_slot;
|
||||
BlockDriverCompletionFunc *dma_cb;
|
||||
AHCICmdHdr *cur_cmd;
|
||||
NCQTransferState ncq_tfs[AHCI_MAX_CMDS];
|
||||
};
|
||||
|
||||
typedef struct AHCIState {
|
||||
AHCIDevice dev[SATA_PORTS];
|
||||
AHCIControlRegs control_regs;
|
||||
int mem;
|
||||
qemu_irq irq;
|
||||
} AHCIState;
|
||||
|
||||
typedef struct AHCIPCIState {
|
||||
PCIDevice card;
|
||||
AHCIState ahci;
|
||||
} AHCIPCIState;
|
||||
|
||||
typedef struct NCQFrame {
|
||||
uint8_t fis_type;
|
||||
uint8_t c;
|
||||
uint8_t command;
|
||||
uint8_t sector_count_low;
|
||||
uint8_t lba0;
|
||||
uint8_t lba1;
|
||||
uint8_t lba2;
|
||||
uint8_t fua;
|
||||
uint8_t lba3;
|
||||
uint8_t lba4;
|
||||
uint8_t lba5;
|
||||
uint8_t sector_count_high;
|
||||
uint8_t tag;
|
||||
uint8_t reserved5;
|
||||
uint8_t reserved6;
|
||||
uint8_t control;
|
||||
uint8_t reserved7;
|
||||
uint8_t reserved8;
|
||||
uint8_t reserved9;
|
||||
uint8_t reserved10;
|
||||
} __attribute__ ((packed)) NCQFrame;
|
||||
|
||||
static void check_cmd(AHCIState *s, int port);
|
||||
static int handle_cmd(AHCIState *s,int port,int slot);
|
||||
static void ahci_reset_port(AHCIState *s, int port);
|
||||
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis);
|
||||
static void ahci_init_d2h(AHCIDevice *ad);
|
||||
|
||||
static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
|
||||
{
|
||||
@@ -482,7 +145,7 @@ static void ahci_check_irq(AHCIState *s)
|
||||
|
||||
DPRINTF(-1, "check irq %#x\n", s->control_regs.irqstatus);
|
||||
|
||||
for (i = 0; i < SATA_PORTS; i++) {
|
||||
for (i = 0; i < s->ports; i++) {
|
||||
AHCIPortRegs *pr = &s->dev[i].port_regs;
|
||||
if (pr->irq_stat & pr->irq_mask) {
|
||||
s->control_regs.irqstatus |= (1 << i);
|
||||
@@ -568,6 +231,16 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
|
||||
pr->cmd |= PORT_CMD_FIS_ON;
|
||||
}
|
||||
|
||||
/* XXX usually the FIS would be pending on the bus here and
|
||||
issuing deferred until the OS enables FIS receival.
|
||||
Instead, we only submit it once - which works in most
|
||||
cases, but is a hack. */
|
||||
if ((pr->cmd & PORT_CMD_FIS_ON) &&
|
||||
!s->dev[port].init_d2h_sent) {
|
||||
ahci_init_d2h(&s->dev[port]);
|
||||
s->dev[port].init_d2h_sent = 1;
|
||||
}
|
||||
|
||||
check_cmd(s, port);
|
||||
break;
|
||||
case PORT_TFDATA:
|
||||
@@ -630,7 +303,8 @@ static uint32_t ahci_mem_readl(void *ptr, target_phys_addr_t addr)
|
||||
|
||||
DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val);
|
||||
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
|
||||
(addr < AHCI_PORT_REGS_END_ADDR)) {
|
||||
(addr < (AHCI_PORT_REGS_START_ADDR +
|
||||
(s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
|
||||
val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
|
||||
addr & AHCI_PORT_ADDR_OFFSET_MASK);
|
||||
}
|
||||
@@ -662,7 +336,7 @@ static void ahci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||
case HOST_CTL: /* R/W */
|
||||
if (val & HOST_CTL_RESET) {
|
||||
DPRINTF(-1, "HBA Reset\n");
|
||||
/* FIXME reset? */
|
||||
ahci_reset(container_of(s, AHCIPCIState, ahci));
|
||||
} else {
|
||||
s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
|
||||
ahci_check_irq(s);
|
||||
@@ -682,7 +356,8 @@ static void ahci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||
DPRINTF(-1, "write to unknown register 0x%x\n", (unsigned)addr);
|
||||
}
|
||||
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
|
||||
(addr < AHCI_PORT_REGS_END_ADDR)) {
|
||||
(addr < (AHCI_PORT_REGS_START_ADDR +
|
||||
(s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
|
||||
ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
|
||||
addr & AHCI_PORT_ADDR_OFFSET_MASK, val);
|
||||
}
|
||||
@@ -705,16 +380,16 @@ static void ahci_reg_init(AHCIState *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
s->control_regs.cap = (SATA_PORTS - 1) |
|
||||
s->control_regs.cap = (s->ports - 1) |
|
||||
(AHCI_NUM_COMMAND_SLOTS << 8) |
|
||||
(AHCI_SUPPORTED_SPEED_GEN1 << AHCI_SUPPORTED_SPEED) |
|
||||
HOST_CAP_NCQ | HOST_CAP_AHCI;
|
||||
|
||||
s->control_regs.impl = (1 << SATA_PORTS) - 1;
|
||||
s->control_regs.impl = (1 << s->ports) - 1;
|
||||
|
||||
s->control_regs.version = AHCI_VERSION_1_0;
|
||||
|
||||
for (i = 0; i < SATA_PORTS; i++) {
|
||||
for (i = 0; i < s->ports; i++) {
|
||||
s->dev[i].port_state = STATE_RUN;
|
||||
}
|
||||
}
|
||||
@@ -800,12 +475,29 @@ static void ahci_check_cmd_bh(void *opaque)
|
||||
check_cmd(ad->hba, ad->port_no);
|
||||
}
|
||||
|
||||
static void ahci_init_d2h(AHCIDevice *ad)
|
||||
{
|
||||
uint8_t init_fis[0x20];
|
||||
IDEState *ide_state = &ad->port.ifs[0];
|
||||
|
||||
memset(init_fis, 0, sizeof(init_fis));
|
||||
|
||||
init_fis[4] = 1;
|
||||
init_fis[12] = 1;
|
||||
|
||||
if (ide_state->drive_kind == IDE_CD) {
|
||||
init_fis[5] = ide_state->lcyl;
|
||||
init_fis[6] = ide_state->hcyl;
|
||||
}
|
||||
|
||||
ahci_write_fis_d2h(ad, init_fis);
|
||||
}
|
||||
|
||||
static void ahci_reset_port(AHCIState *s, int port)
|
||||
{
|
||||
AHCIDevice *d = &s->dev[port];
|
||||
AHCIPortRegs *pr = &d->port_regs;
|
||||
IDEState *ide_state = &d->port.ifs[0];
|
||||
uint8_t init_fis[0x20];
|
||||
int i;
|
||||
|
||||
DPRINTF(port, "reset port\n");
|
||||
@@ -820,6 +512,7 @@ static void ahci_reset_port(AHCIState *s, int port)
|
||||
pr->scr_err = 0;
|
||||
pr->scr_act = 0;
|
||||
d->busy_slot = -1;
|
||||
d->init_d2h_sent = 0;
|
||||
|
||||
ide_state = &s->dev[port].port.ifs[0];
|
||||
if (!ide_state->bs) {
|
||||
@@ -842,7 +535,6 @@ static void ahci_reset_port(AHCIState *s, int port)
|
||||
ncq_tfs->used = 0;
|
||||
}
|
||||
|
||||
memset(init_fis, 0, sizeof(init_fis));
|
||||
s->dev[port].port_state = STATE_RUN;
|
||||
if (!ide_state->bs) {
|
||||
s->dev[port].port_regs.sig = 0;
|
||||
@@ -852,8 +544,6 @@ static void ahci_reset_port(AHCIState *s, int port)
|
||||
ide_state->lcyl = 0x14;
|
||||
ide_state->hcyl = 0xeb;
|
||||
DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl);
|
||||
init_fis[5] = ide_state->lcyl;
|
||||
init_fis[6] = ide_state->hcyl;
|
||||
ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
|
||||
} else {
|
||||
s->dev[port].port_regs.sig = SATA_SIGNATURE_DISK;
|
||||
@@ -861,9 +551,7 @@ static void ahci_reset_port(AHCIState *s, int port)
|
||||
}
|
||||
|
||||
ide_state->error = 1;
|
||||
init_fis[4] = 1;
|
||||
init_fis[12] = 1;
|
||||
ahci_write_fis_d2h(d, init_fis);
|
||||
ahci_init_d2h(d);
|
||||
}
|
||||
|
||||
static void debug_print_fis(uint8_t *fis, int cmd_len)
|
||||
@@ -1410,17 +1098,19 @@ static const IDEDMAOps ahci_dma_ops = {
|
||||
.reset = ahci_dma_reset,
|
||||
};
|
||||
|
||||
static void ahci_init(AHCIState *s, DeviceState *qdev)
|
||||
void ahci_init(AHCIState *s, DeviceState *qdev, int ports)
|
||||
{
|
||||
qemu_irq *irqs;
|
||||
int i;
|
||||
|
||||
s->ports = ports;
|
||||
s->dev = qemu_mallocz(sizeof(AHCIDevice) * ports);
|
||||
ahci_reg_init(s);
|
||||
s->mem = cpu_register_io_memory(ahci_readfn, ahci_writefn, s,
|
||||
DEVICE_LITTLE_ENDIAN);
|
||||
irqs = qemu_allocate_irqs(ahci_irq_set, s, SATA_PORTS);
|
||||
irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports);
|
||||
|
||||
for (i = 0; i < SATA_PORTS; i++) {
|
||||
for (i = 0; i < s->ports; i++) {
|
||||
AHCIDevice *ad = &s->dev[i];
|
||||
|
||||
ide_bus_new(&ad->port, qdev, i);
|
||||
@@ -1434,7 +1124,12 @@ static void ahci_init(AHCIState *s, DeviceState *qdev)
|
||||
}
|
||||
}
|
||||
|
||||
static void ahci_pci_map(PCIDevice *pci_dev, int region_num,
|
||||
void ahci_uninit(AHCIState *s)
|
||||
{
|
||||
qemu_free(s->dev);
|
||||
}
|
||||
|
||||
void ahci_pci_map(PCIDevice *pci_dev, int region_num,
|
||||
pcibus_t addr, pcibus_t size, int type)
|
||||
{
|
||||
struct AHCIPCIState *d = (struct AHCIPCIState *)pci_dev;
|
||||
@@ -1443,81 +1138,15 @@ static void ahci_pci_map(PCIDevice *pci_dev, int region_num,
|
||||
cpu_register_physical_memory(addr, size, s->mem);
|
||||
}
|
||||
|
||||
static void ahci_reset(void *opaque)
|
||||
void ahci_reset(void *opaque)
|
||||
{
|
||||
struct AHCIPCIState *d = opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SATA_PORTS; i++) {
|
||||
d->ahci.control_regs.irqstatus = 0;
|
||||
d->ahci.control_regs.ghc = 0;
|
||||
|
||||
for (i = 0; i < d->ahci.ports; i++) {
|
||||
ahci_reset_port(&d->ahci, i);
|
||||
}
|
||||
}
|
||||
|
||||
static int pci_ahci_init(PCIDevice *dev)
|
||||
{
|
||||
struct AHCIPCIState *d;
|
||||
d = DO_UPCAST(struct AHCIPCIState, card, dev);
|
||||
|
||||
pci_config_set_vendor_id(d->card.config, PCI_VENDOR_ID_INTEL);
|
||||
pci_config_set_device_id(d->card.config, PCI_DEVICE_ID_INTEL_82801IR);
|
||||
|
||||
pci_config_set_class(d->card.config, PCI_CLASS_STORAGE_SATA);
|
||||
pci_config_set_revision(d->card.config, 0x02);
|
||||
pci_config_set_prog_interface(d->card.config, AHCI_PROGMODE_MAJOR_REV_1);
|
||||
|
||||
d->card.config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */
|
||||
d->card.config[PCI_LATENCY_TIMER] = 0x00; /* Latency timer */
|
||||
pci_config_set_interrupt_pin(d->card.config, 1);
|
||||
|
||||
/* XXX Software should program this register */
|
||||
d->card.config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */
|
||||
|
||||
qemu_register_reset(ahci_reset, d);
|
||||
|
||||
/* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
|
||||
pci_register_bar(&d->card, 5, 0x1000, PCI_BASE_ADDRESS_SPACE_MEMORY,
|
||||
ahci_pci_map);
|
||||
|
||||
msi_init(dev, 0x50, 1, true, false);
|
||||
|
||||
ahci_init(&d->ahci, &dev->qdev);
|
||||
d->ahci.irq = d->card.irq[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_ahci_uninit(PCIDevice *dev)
|
||||
{
|
||||
struct AHCIPCIState *d;
|
||||
d = DO_UPCAST(struct AHCIPCIState, card, dev);
|
||||
|
||||
if (msi_enabled(dev)) {
|
||||
msi_uninit(dev);
|
||||
}
|
||||
|
||||
qemu_unregister_reset(ahci_reset, d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_ahci_write_config(PCIDevice *pci, uint32_t addr,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
pci_default_write_config(pci, addr, val, len);
|
||||
msi_write_config(pci, addr, val, len);
|
||||
}
|
||||
|
||||
static PCIDeviceInfo ahci_info = {
|
||||
.qdev.name = "ahci",
|
||||
.qdev.size = sizeof(AHCIPCIState),
|
||||
.init = pci_ahci_init,
|
||||
.exit = pci_ahci_uninit,
|
||||
.config_write = pci_ahci_write_config,
|
||||
};
|
||||
|
||||
static void ahci_pci_register_devices(void)
|
||||
{
|
||||
pci_qdev_register(&ahci_info);
|
||||
}
|
||||
|
||||
device_init(ahci_pci_register_devices)
|
||||
|
333
hw/ide/ahci.h
Normal file
333
hw/ide/ahci.h
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* QEMU AHCI Emulation
|
||||
*
|
||||
* Copyright (c) 2010 qiaochong@loongson.cn
|
||||
* Copyright (c) 2010 Roland Elek <elek.roland@gmail.com>
|
||||
* Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de>
|
||||
* Copyright (c) 2010 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* 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
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_IDE_AHCI_H
|
||||
#define HW_IDE_AHCI_H
|
||||
|
||||
#define AHCI_PCI_BAR 5
|
||||
#define AHCI_MAX_PORTS 32
|
||||
#define AHCI_MAX_SG 168 /* hardware max is 64K */
|
||||
#define AHCI_DMA_BOUNDARY 0xffffffff
|
||||
#define AHCI_USE_CLUSTERING 0
|
||||
#define AHCI_MAX_CMDS 32
|
||||
#define AHCI_CMD_SZ 32
|
||||
#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ)
|
||||
#define AHCI_RX_FIS_SZ 256
|
||||
#define AHCI_CMD_TBL_CDB 0x40
|
||||
#define AHCI_CMD_TBL_HDR_SZ 0x80
|
||||
#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16))
|
||||
#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS)
|
||||
#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \
|
||||
AHCI_RX_FIS_SZ)
|
||||
|
||||
#define AHCI_IRQ_ON_SG (1 << 31)
|
||||
#define AHCI_CMD_ATAPI (1 << 5)
|
||||
#define AHCI_CMD_WRITE (1 << 6)
|
||||
#define AHCI_CMD_PREFETCH (1 << 7)
|
||||
#define AHCI_CMD_RESET (1 << 8)
|
||||
#define AHCI_CMD_CLR_BUSY (1 << 10)
|
||||
|
||||
#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */
|
||||
#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */
|
||||
#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */
|
||||
|
||||
/* global controller registers */
|
||||
#define HOST_CAP 0x00 /* host capabilities */
|
||||
#define HOST_CTL 0x04 /* global host control */
|
||||
#define HOST_IRQ_STAT 0x08 /* interrupt status */
|
||||
#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */
|
||||
#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */
|
||||
|
||||
/* HOST_CTL bits */
|
||||
#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */
|
||||
#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */
|
||||
#define HOST_CTL_AHCI_EN (1 << 31) /* AHCI enabled */
|
||||
|
||||
/* HOST_CAP bits */
|
||||
#define HOST_CAP_SSC (1 << 14) /* Slumber capable */
|
||||
#define HOST_CAP_AHCI (1 << 18) /* AHCI only */
|
||||
#define HOST_CAP_CLO (1 << 24) /* Command List Override support */
|
||||
#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */
|
||||
#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */
|
||||
#define HOST_CAP_64 (1 << 31) /* PCI DAC (64-bit DMA) support */
|
||||
|
||||
/* registers for each SATA port */
|
||||
#define PORT_LST_ADDR 0x00 /* command list DMA addr */
|
||||
#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */
|
||||
#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */
|
||||
#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */
|
||||
#define PORT_IRQ_STAT 0x10 /* interrupt status */
|
||||
#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */
|
||||
#define PORT_CMD 0x18 /* port command */
|
||||
#define PORT_TFDATA 0x20 /* taskfile data */
|
||||
#define PORT_SIG 0x24 /* device TF signature */
|
||||
#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */
|
||||
#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */
|
||||
#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */
|
||||
#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */
|
||||
#define PORT_CMD_ISSUE 0x38 /* command issue */
|
||||
#define PORT_RESERVED 0x3c /* reserved */
|
||||
|
||||
/* PORT_IRQ_{STAT,MASK} bits */
|
||||
#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */
|
||||
#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */
|
||||
#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */
|
||||
#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */
|
||||
#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */
|
||||
#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */
|
||||
#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */
|
||||
#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */
|
||||
|
||||
#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */
|
||||
#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */
|
||||
#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */
|
||||
#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */
|
||||
#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */
|
||||
#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */
|
||||
#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */
|
||||
#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */
|
||||
#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */
|
||||
|
||||
#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \
|
||||
PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \
|
||||
PORT_IRQ_UNK_FIS)
|
||||
#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \
|
||||
PORT_IRQ_HBUS_DATA_ERR)
|
||||
#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \
|
||||
PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \
|
||||
PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS)
|
||||
|
||||
/* PORT_CMD bits */
|
||||
#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */
|
||||
#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */
|
||||
#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */
|
||||
#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */
|
||||
#define PORT_CMD_CLO (1 << 3) /* Command list override */
|
||||
#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */
|
||||
#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */
|
||||
#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */
|
||||
|
||||
#define PORT_CMD_ICC_MASK (0xf << 28) /* i/f ICC state mask */
|
||||
#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */
|
||||
#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */
|
||||
#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */
|
||||
|
||||
#define PORT_IRQ_STAT_DHRS (1 << 0) /* Device to Host Register FIS */
|
||||
#define PORT_IRQ_STAT_PSS (1 << 1) /* PIO Setup FIS */
|
||||
#define PORT_IRQ_STAT_DSS (1 << 2) /* DMA Setup FIS */
|
||||
#define PORT_IRQ_STAT_SDBS (1 << 3) /* Set Device Bits */
|
||||
#define PORT_IRQ_STAT_UFS (1 << 4) /* Unknown FIS */
|
||||
#define PORT_IRQ_STAT_DPS (1 << 5) /* Descriptor Processed */
|
||||
#define PORT_IRQ_STAT_PCS (1 << 6) /* Port Connect Change Status */
|
||||
#define PORT_IRQ_STAT_DMPS (1 << 7) /* Device Mechanical Presence
|
||||
Status */
|
||||
#define PORT_IRQ_STAT_PRCS (1 << 22) /* File Ready Status */
|
||||
#define PORT_IRQ_STAT_IPMS (1 << 23) /* Incorrect Port Multiplier
|
||||
Status */
|
||||
#define PORT_IRQ_STAT_OFS (1 << 24) /* Overflow Status */
|
||||
#define PORT_IRQ_STAT_INFS (1 << 26) /* Interface Non-Fatal Error
|
||||
Status */
|
||||
#define PORT_IRQ_STAT_IFS (1 << 27) /* Interface Fatal Error */
|
||||
#define PORT_IRQ_STAT_HBDS (1 << 28) /* Host Bus Data Error Status */
|
||||
#define PORT_IRQ_STAT_HBFS (1 << 29) /* Host Bus Fatal Error Status */
|
||||
#define PORT_IRQ_STAT_TFES (1 << 30) /* Task File Error Status */
|
||||
#define PORT_IRQ_STAT_CPDS (1 << 31) /* Code Port Detect Status */
|
||||
|
||||
/* ap->flags bits */
|
||||
#define AHCI_FLAG_NO_NCQ (1 << 24)
|
||||
#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */
|
||||
#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */
|
||||
#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */
|
||||
#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */
|
||||
|
||||
#define ATA_SRST (1 << 2) /* software reset */
|
||||
|
||||
#define STATE_RUN 0
|
||||
#define STATE_RESET 1
|
||||
|
||||
#define SATA_SCR_SSTATUS_DET_NODEV 0x0
|
||||
#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3
|
||||
|
||||
#define SATA_SCR_SSTATUS_SPD_NODEV 0x00
|
||||
#define SATA_SCR_SSTATUS_SPD_GEN1 0x10
|
||||
|
||||
#define SATA_SCR_SSTATUS_IPM_NODEV 0x000
|
||||
#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100
|
||||
|
||||
#define AHCI_SCR_SCTL_DET 0xf
|
||||
|
||||
#define SATA_FIS_TYPE_REGISTER_H2D 0x27
|
||||
#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80
|
||||
|
||||
#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f
|
||||
#define AHCI_CMD_HDR_PRDT_LEN 16
|
||||
|
||||
#define SATA_SIGNATURE_CDROM 0xeb140000
|
||||
#define SATA_SIGNATURE_DISK 0x00000101
|
||||
|
||||
#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20
|
||||
/* Shouldn't this be 0x2c? */
|
||||
|
||||
#define AHCI_PORT_REGS_START_ADDR 0x100
|
||||
#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f
|
||||
#define AHCI_PORT_ADDR_OFFSET_LEN 0x80
|
||||
|
||||
#define AHCI_NUM_COMMAND_SLOTS 31
|
||||
#define AHCI_SUPPORTED_SPEED 20
|
||||
#define AHCI_SUPPORTED_SPEED_GEN1 1
|
||||
#define AHCI_VERSION_1_0 0x10000
|
||||
|
||||
#define AHCI_PROGMODE_MAJOR_REV_1 1
|
||||
|
||||
#define AHCI_COMMAND_TABLE_ACMD 0x40
|
||||
|
||||
#define IDE_FEATURE_DMA 1
|
||||
|
||||
#define READ_FPDMA_QUEUED 0x60
|
||||
#define WRITE_FPDMA_QUEUED 0x61
|
||||
|
||||
#define RES_FIS_DSFIS 0x00
|
||||
#define RES_FIS_PSFIS 0x20
|
||||
#define RES_FIS_RFIS 0x40
|
||||
#define RES_FIS_SDBFIS 0x58
|
||||
#define RES_FIS_UFIS 0x60
|
||||
|
||||
typedef struct AHCIControlRegs {
|
||||
uint32_t cap;
|
||||
uint32_t ghc;
|
||||
uint32_t irqstatus;
|
||||
uint32_t impl;
|
||||
uint32_t version;
|
||||
} AHCIControlRegs;
|
||||
|
||||
typedef struct AHCIPortRegs {
|
||||
uint32_t lst_addr;
|
||||
uint32_t lst_addr_hi;
|
||||
uint32_t fis_addr;
|
||||
uint32_t fis_addr_hi;
|
||||
uint32_t irq_stat;
|
||||
uint32_t irq_mask;
|
||||
uint32_t cmd;
|
||||
uint32_t unused0;
|
||||
uint32_t tfdata;
|
||||
uint32_t sig;
|
||||
uint32_t scr_stat;
|
||||
uint32_t scr_ctl;
|
||||
uint32_t scr_err;
|
||||
uint32_t scr_act;
|
||||
uint32_t cmd_issue;
|
||||
uint32_t reserved;
|
||||
} AHCIPortRegs;
|
||||
|
||||
typedef struct AHCICmdHdr {
|
||||
uint32_t opts;
|
||||
uint32_t status;
|
||||
uint64_t tbl_addr;
|
||||
uint32_t reserved[4];
|
||||
} __attribute__ ((packed)) AHCICmdHdr;
|
||||
|
||||
typedef struct AHCI_SG {
|
||||
uint64_t addr;
|
||||
uint32_t reserved;
|
||||
uint32_t flags_size;
|
||||
} __attribute__ ((packed)) AHCI_SG;
|
||||
|
||||
typedef struct AHCIDevice AHCIDevice;
|
||||
|
||||
typedef struct NCQTransferState {
|
||||
AHCIDevice *drive;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
QEMUSGList sglist;
|
||||
int is_read;
|
||||
uint16_t sector_count;
|
||||
uint64_t lba;
|
||||
uint8_t tag;
|
||||
int slot;
|
||||
int used;
|
||||
} NCQTransferState;
|
||||
|
||||
struct AHCIDevice {
|
||||
IDEDMA dma;
|
||||
IDEBus port;
|
||||
int port_no;
|
||||
uint32_t port_state;
|
||||
uint32_t finished;
|
||||
AHCIPortRegs port_regs;
|
||||
struct AHCIState *hba;
|
||||
QEMUBH *check_bh;
|
||||
uint8_t *lst;
|
||||
uint8_t *res_fis;
|
||||
int dma_status;
|
||||
int done_atapi_packet;
|
||||
int busy_slot;
|
||||
int init_d2h_sent;
|
||||
BlockDriverCompletionFunc *dma_cb;
|
||||
AHCICmdHdr *cur_cmd;
|
||||
NCQTransferState ncq_tfs[AHCI_MAX_CMDS];
|
||||
};
|
||||
|
||||
typedef struct AHCIState {
|
||||
AHCIDevice *dev;
|
||||
AHCIControlRegs control_regs;
|
||||
int mem;
|
||||
int ports;
|
||||
qemu_irq irq;
|
||||
} AHCIState;
|
||||
|
||||
typedef struct AHCIPCIState {
|
||||
PCIDevice card;
|
||||
AHCIState ahci;
|
||||
} AHCIPCIState;
|
||||
|
||||
typedef struct NCQFrame {
|
||||
uint8_t fis_type;
|
||||
uint8_t c;
|
||||
uint8_t command;
|
||||
uint8_t sector_count_low;
|
||||
uint8_t lba0;
|
||||
uint8_t lba1;
|
||||
uint8_t lba2;
|
||||
uint8_t fua;
|
||||
uint8_t lba3;
|
||||
uint8_t lba4;
|
||||
uint8_t lba5;
|
||||
uint8_t sector_count_high;
|
||||
uint8_t tag;
|
||||
uint8_t reserved5;
|
||||
uint8_t reserved6;
|
||||
uint8_t control;
|
||||
uint8_t reserved7;
|
||||
uint8_t reserved8;
|
||||
uint8_t reserved9;
|
||||
uint8_t reserved10;
|
||||
} __attribute__ ((packed)) NCQFrame;
|
||||
|
||||
void ahci_init(AHCIState *s, DeviceState *qdev, int ports);
|
||||
void ahci_uninit(AHCIState *s);
|
||||
|
||||
void ahci_pci_map(PCIDevice *pci_dev, int region_num,
|
||||
pcibus_t addr, pcibus_t size, int type);
|
||||
|
||||
void ahci_reset(void *opaque);
|
||||
|
||||
#endif /* HW_IDE_AHCI_H */
|
148
hw/ide/ich.c
Normal file
148
hw/ide/ich.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* QEMU ICH Emulation
|
||||
*
|
||||
* Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de>
|
||||
* Copyright (c) 2010 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* 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
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
* lspci dump of a ICH-9 real device
|
||||
*
|
||||
* 00:1f.2 SATA controller [0106]: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922] (rev 02) (prog-if 01 [AHCI 1.0])
|
||||
* Subsystem: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922]
|
||||
* Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
|
||||
* Status: Cap+ 66MHz+ UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
|
||||
* Latency: 0
|
||||
* Interrupt: pin B routed to IRQ 222
|
||||
* Region 0: I/O ports at d000 [size=8]
|
||||
* Region 1: I/O ports at cc00 [size=4]
|
||||
* Region 2: I/O ports at c880 [size=8]
|
||||
* Region 3: I/O ports at c800 [size=4]
|
||||
* Region 4: I/O ports at c480 [size=32]
|
||||
* Region 5: Memory at febf9000 (32-bit, non-prefetchable) [size=2K]
|
||||
* Capabilities: [80] Message Signalled Interrupts: Mask- 64bit- Count=1/16 Enable+
|
||||
* Address: fee0f00c Data: 41d9
|
||||
* Capabilities: [70] Power Management version 3
|
||||
* Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot+,D3cold-)
|
||||
* Status: D0 PME-Enable- DSel=0 DScale=0 PME-
|
||||
* Capabilities: [a8] SATA HBA <?>
|
||||
* Capabilities: [b0] Vendor Specific Information <?>
|
||||
* Kernel driver in use: ahci
|
||||
* Kernel modules: ahci
|
||||
* 00: 86 80 22 29 07 04 b0 02 02 01 06 01 00 00 00 00
|
||||
* 10: 01 d0 00 00 01 cc 00 00 81 c8 00 00 01 c8 00 00
|
||||
* 20: 81 c4 00 00 00 90 bf fe 00 00 00 00 86 80 22 29
|
||||
* 30: 00 00 00 00 80 00 00 00 00 00 00 00 0f 02 00 00
|
||||
* 40: 00 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* 70: 01 a8 03 40 08 00 00 00 00 00 00 00 00 00 00 00
|
||||
* 80: 05 70 09 00 0c f0 e0 fe d9 41 00 00 00 00 00 00
|
||||
* 90: 40 00 0f 82 93 01 00 00 00 00 00 00 00 00 00 00
|
||||
* a0: ac 00 00 00 0a 00 12 00 12 b0 10 00 48 00 00 00
|
||||
* b0: 09 00 06 20 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
* f0: 00 00 00 00 00 00 00 00 86 0f 02 00 00 00 00 00
|
||||
*
|
||||
*/
|
||||
|
||||
#include <hw/hw.h>
|
||||
#include <hw/msi.h>
|
||||
#include <hw/pc.h>
|
||||
#include <hw/pci.h>
|
||||
#include <hw/isa.h>
|
||||
#include "block.h"
|
||||
#include "block_int.h"
|
||||
#include "sysemu.h"
|
||||
#include "dma.h"
|
||||
|
||||
#include <hw/ide/pci.h>
|
||||
#include <hw/ide/ahci.h>
|
||||
|
||||
static int pci_ich9_ahci_init(PCIDevice *dev)
|
||||
{
|
||||
struct AHCIPCIState *d;
|
||||
d = DO_UPCAST(struct AHCIPCIState, card, dev);
|
||||
|
||||
pci_config_set_vendor_id(d->card.config, PCI_VENDOR_ID_INTEL);
|
||||
pci_config_set_device_id(d->card.config, PCI_DEVICE_ID_INTEL_82801IR);
|
||||
|
||||
pci_config_set_class(d->card.config, PCI_CLASS_STORAGE_SATA);
|
||||
pci_config_set_revision(d->card.config, 0x02);
|
||||
pci_config_set_prog_interface(d->card.config, AHCI_PROGMODE_MAJOR_REV_1);
|
||||
|
||||
d->card.config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */
|
||||
d->card.config[PCI_LATENCY_TIMER] = 0x00; /* Latency timer */
|
||||
pci_config_set_interrupt_pin(d->card.config, 1);
|
||||
|
||||
/* XXX Software should program this register */
|
||||
d->card.config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */
|
||||
|
||||
qemu_register_reset(ahci_reset, d);
|
||||
|
||||
/* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
|
||||
pci_register_bar(&d->card, 5, 0x1000, PCI_BASE_ADDRESS_SPACE_MEMORY,
|
||||
ahci_pci_map);
|
||||
|
||||
msi_init(dev, 0x50, 1, true, false);
|
||||
|
||||
ahci_init(&d->ahci, &dev->qdev, 6);
|
||||
d->ahci.irq = d->card.irq[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_ich9_uninit(PCIDevice *dev)
|
||||
{
|
||||
struct AHCIPCIState *d;
|
||||
d = DO_UPCAST(struct AHCIPCIState, card, dev);
|
||||
|
||||
if (msi_enabled(dev)) {
|
||||
msi_uninit(dev);
|
||||
}
|
||||
|
||||
qemu_unregister_reset(ahci_reset, d);
|
||||
ahci_uninit(&d->ahci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_ich9_write_config(PCIDevice *pci, uint32_t addr,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
pci_default_write_config(pci, addr, val, len);
|
||||
msi_write_config(pci, addr, val, len);
|
||||
}
|
||||
|
||||
static PCIDeviceInfo ich_ahci_info[] = {
|
||||
{
|
||||
.qdev.name = "ich9-ahci",
|
||||
.qdev.alias = "ahci",
|
||||
.qdev.size = sizeof(AHCIPCIState),
|
||||
.init = pci_ich9_ahci_init,
|
||||
.exit = pci_ich9_uninit,
|
||||
.config_write = pci_ich9_write_config,
|
||||
},{
|
||||
/* end of list */
|
||||
}
|
||||
};
|
||||
|
||||
static void ich_ahci_register(void)
|
||||
{
|
||||
pci_qdev_register_many(ich_ahci_info);
|
||||
}
|
||||
device_init(ich_ahci_register);
|
230
hw/ioapic.c
230
hw/ioapic.c
@@ -23,6 +23,7 @@
|
||||
#include "hw.h"
|
||||
#include "pc.h"
|
||||
#include "apic.h"
|
||||
#include "ioapic.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "host-utils.h"
|
||||
#include "sysbus.h"
|
||||
@@ -36,19 +37,50 @@
|
||||
#define DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define IOAPIC_LVT_MASKED (1<<16)
|
||||
#define MAX_IOAPICS 1
|
||||
|
||||
#define IOAPIC_TRIGGER_EDGE 0
|
||||
#define IOAPIC_TRIGGER_LEVEL 1
|
||||
#define IOAPIC_VERSION 0x11
|
||||
|
||||
#define IOAPIC_LVT_DEST_SHIFT 56
|
||||
#define IOAPIC_LVT_MASKED_SHIFT 16
|
||||
#define IOAPIC_LVT_TRIGGER_MODE_SHIFT 15
|
||||
#define IOAPIC_LVT_REMOTE_IRR_SHIFT 14
|
||||
#define IOAPIC_LVT_POLARITY_SHIFT 13
|
||||
#define IOAPIC_LVT_DELIV_STATUS_SHIFT 12
|
||||
#define IOAPIC_LVT_DEST_MODE_SHIFT 11
|
||||
#define IOAPIC_LVT_DELIV_MODE_SHIFT 8
|
||||
|
||||
#define IOAPIC_LVT_MASKED (1 << IOAPIC_LVT_MASKED_SHIFT)
|
||||
#define IOAPIC_LVT_REMOTE_IRR (1 << IOAPIC_LVT_REMOTE_IRR_SHIFT)
|
||||
|
||||
#define IOAPIC_TRIGGER_EDGE 0
|
||||
#define IOAPIC_TRIGGER_LEVEL 1
|
||||
|
||||
/*io{apic,sapic} delivery mode*/
|
||||
#define IOAPIC_DM_FIXED 0x0
|
||||
#define IOAPIC_DM_LOWEST_PRIORITY 0x1
|
||||
#define IOAPIC_DM_PMI 0x2
|
||||
#define IOAPIC_DM_NMI 0x4
|
||||
#define IOAPIC_DM_INIT 0x5
|
||||
#define IOAPIC_DM_SIPI 0x5
|
||||
#define IOAPIC_DM_EXTINT 0x7
|
||||
#define IOAPIC_DM_FIXED 0x0
|
||||
#define IOAPIC_DM_LOWEST_PRIORITY 0x1
|
||||
#define IOAPIC_DM_PMI 0x2
|
||||
#define IOAPIC_DM_NMI 0x4
|
||||
#define IOAPIC_DM_INIT 0x5
|
||||
#define IOAPIC_DM_SIPI 0x6
|
||||
#define IOAPIC_DM_EXTINT 0x7
|
||||
#define IOAPIC_DM_MASK 0x7
|
||||
|
||||
#define IOAPIC_VECTOR_MASK 0xff
|
||||
|
||||
#define IOAPIC_IOREGSEL 0x00
|
||||
#define IOAPIC_IOWIN 0x10
|
||||
|
||||
#define IOAPIC_REG_ID 0x00
|
||||
#define IOAPIC_REG_VER 0x01
|
||||
#define IOAPIC_REG_ARB 0x02
|
||||
#define IOAPIC_REG_REDTBL_BASE 0x10
|
||||
#define IOAPIC_ID 0x00
|
||||
|
||||
#define IOAPIC_ID_SHIFT 24
|
||||
#define IOAPIC_ID_MASK 0xf
|
||||
|
||||
#define IOAPIC_VER_ENTRIES_SHIFT 16
|
||||
|
||||
typedef struct IOAPICState IOAPICState;
|
||||
|
||||
@@ -56,11 +88,12 @@ struct IOAPICState {
|
||||
SysBusDevice busdev;
|
||||
uint8_t id;
|
||||
uint8_t ioregsel;
|
||||
|
||||
uint32_t irr;
|
||||
uint64_t ioredtbl[IOAPIC_NUM_PINS];
|
||||
};
|
||||
|
||||
static IOAPICState *ioapics[MAX_IOAPICS];
|
||||
|
||||
static void ioapic_service(IOAPICState *s)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -78,18 +111,22 @@ static void ioapic_service(IOAPICState *s)
|
||||
if (s->irr & mask) {
|
||||
entry = s->ioredtbl[i];
|
||||
if (!(entry & IOAPIC_LVT_MASKED)) {
|
||||
trig_mode = ((entry >> 15) & 1);
|
||||
dest = entry >> 56;
|
||||
dest_mode = (entry >> 11) & 1;
|
||||
delivery_mode = (entry >> 8) & 7;
|
||||
polarity = (entry >> 13) & 1;
|
||||
if (trig_mode == IOAPIC_TRIGGER_EDGE)
|
||||
trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
|
||||
dest = entry >> IOAPIC_LVT_DEST_SHIFT;
|
||||
dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
|
||||
delivery_mode =
|
||||
(entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;
|
||||
polarity = (entry >> IOAPIC_LVT_POLARITY_SHIFT) & 1;
|
||||
if (trig_mode == IOAPIC_TRIGGER_EDGE) {
|
||||
s->irr &= ~mask;
|
||||
if (delivery_mode == IOAPIC_DM_EXTINT)
|
||||
} else {
|
||||
s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
|
||||
}
|
||||
if (delivery_mode == IOAPIC_DM_EXTINT) {
|
||||
vector = pic_read_irq(isa_pic);
|
||||
else
|
||||
vector = entry & 0xff;
|
||||
|
||||
} else {
|
||||
vector = entry & IOAPIC_VECTOR_MASK;
|
||||
}
|
||||
apic_deliver_irq(dest, dest_mode, delivery_mode,
|
||||
vector, polarity, trig_mode);
|
||||
}
|
||||
@@ -105,15 +142,16 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
|
||||
* to GSI 2. GSI maps to ioapic 1-1. This is not
|
||||
* the cleanest way of doing it but it should work. */
|
||||
|
||||
DPRINTF("%s: %s vec %x\n", __func__, level? "raise" : "lower", vector);
|
||||
if (vector == 0)
|
||||
DPRINTF("%s: %s vec %x\n", __func__, level ? "raise" : "lower", vector);
|
||||
if (vector == 0) {
|
||||
vector = 2;
|
||||
|
||||
}
|
||||
if (vector >= 0 && vector < IOAPIC_NUM_PINS) {
|
||||
uint32_t mask = 1 << vector;
|
||||
uint64_t entry = s->ioredtbl[vector];
|
||||
|
||||
if ((entry >> 15) & 1) {
|
||||
if (((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1) ==
|
||||
IOAPIC_TRIGGER_LEVEL) {
|
||||
/* level triggered */
|
||||
if (level) {
|
||||
s->irr |= mask;
|
||||
@@ -131,82 +169,126 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
|
||||
}
|
||||
}
|
||||
|
||||
void ioapic_eoi_broadcast(int vector)
|
||||
{
|
||||
IOAPICState *s;
|
||||
uint64_t entry;
|
||||
int i, n;
|
||||
|
||||
for (i = 0; i < MAX_IOAPICS; i++) {
|
||||
s = ioapics[i];
|
||||
if (!s) {
|
||||
continue;
|
||||
}
|
||||
for (n = 0; n < IOAPIC_NUM_PINS; n++) {
|
||||
entry = s->ioredtbl[n];
|
||||
if ((entry & IOAPIC_LVT_REMOTE_IRR)
|
||||
&& (entry & IOAPIC_VECTOR_MASK) == vector) {
|
||||
s->ioredtbl[n] = entry & ~IOAPIC_LVT_REMOTE_IRR;
|
||||
if (!(entry & IOAPIC_LVT_MASKED) && (s->irr & (1 << n))) {
|
||||
ioapic_service(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
IOAPICState *s = opaque;
|
||||
int index;
|
||||
uint32_t val = 0;
|
||||
|
||||
addr &= 0xff;
|
||||
if (addr == 0x00) {
|
||||
switch (addr & 0xff) {
|
||||
case IOAPIC_IOREGSEL:
|
||||
val = s->ioregsel;
|
||||
} else if (addr == 0x10) {
|
||||
break;
|
||||
case IOAPIC_IOWIN:
|
||||
switch (s->ioregsel) {
|
||||
case 0x00:
|
||||
val = s->id << 24;
|
||||
break;
|
||||
case 0x01:
|
||||
val = 0x11 | ((IOAPIC_NUM_PINS - 1) << 16); /* version 0x11 */
|
||||
break;
|
||||
case 0x02:
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
index = (s->ioregsel - 0x10) >> 1;
|
||||
if (index >= 0 && index < IOAPIC_NUM_PINS) {
|
||||
if (s->ioregsel & 1)
|
||||
val = s->ioredtbl[index] >> 32;
|
||||
else
|
||||
val = s->ioredtbl[index] & 0xffffffff;
|
||||
case IOAPIC_REG_ID:
|
||||
val = s->id << IOAPIC_ID_SHIFT;
|
||||
break;
|
||||
case IOAPIC_REG_VER:
|
||||
val = IOAPIC_VERSION |
|
||||
((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT);
|
||||
break;
|
||||
case IOAPIC_REG_ARB:
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
|
||||
if (index >= 0 && index < IOAPIC_NUM_PINS) {
|
||||
if (s->ioregsel & 1) {
|
||||
val = s->ioredtbl[index] >> 32;
|
||||
} else {
|
||||
val = s->ioredtbl[index] & 0xffffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
DPRINTF("read: %08x = %08x\n", s->ioregsel, val);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
static void
|
||||
ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
IOAPICState *s = opaque;
|
||||
int index;
|
||||
|
||||
addr &= 0xff;
|
||||
if (addr == 0x00) {
|
||||
switch (addr & 0xff) {
|
||||
case IOAPIC_IOREGSEL:
|
||||
s->ioregsel = val;
|
||||
return;
|
||||
} else if (addr == 0x10) {
|
||||
break;
|
||||
case IOAPIC_IOWIN:
|
||||
DPRINTF("write: %08x = %08x\n", s->ioregsel, val);
|
||||
switch (s->ioregsel) {
|
||||
case 0x00:
|
||||
s->id = (val >> 24) & 0xff;
|
||||
return;
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
return;
|
||||
default:
|
||||
index = (s->ioregsel - 0x10) >> 1;
|
||||
if (index >= 0 && index < IOAPIC_NUM_PINS) {
|
||||
if (s->ioregsel & 1) {
|
||||
s->ioredtbl[index] &= 0xffffffff;
|
||||
s->ioredtbl[index] |= (uint64_t)val << 32;
|
||||
} else {
|
||||
s->ioredtbl[index] &= ~0xffffffffULL;
|
||||
s->ioredtbl[index] |= val;
|
||||
}
|
||||
ioapic_service(s);
|
||||
case IOAPIC_REG_ID:
|
||||
s->id = (val >> IOAPIC_ID_SHIFT) & IOAPIC_ID_MASK;
|
||||
break;
|
||||
case IOAPIC_REG_VER:
|
||||
case IOAPIC_REG_ARB:
|
||||
break;
|
||||
default:
|
||||
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
|
||||
if (index >= 0 && index < IOAPIC_NUM_PINS) {
|
||||
if (s->ioregsel & 1) {
|
||||
s->ioredtbl[index] &= 0xffffffff;
|
||||
s->ioredtbl[index] |= (uint64_t)val << 32;
|
||||
} else {
|
||||
s->ioredtbl[index] &= ~0xffffffffULL;
|
||||
s->ioredtbl[index] |= val;
|
||||
}
|
||||
ioapic_service(s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int ioapic_post_load(void *opaque, int version_id)
|
||||
{
|
||||
IOAPICState *s = opaque;
|
||||
|
||||
if (version_id == 1) {
|
||||
/* set sane value */
|
||||
s->irr = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ioapic = {
|
||||
.name = "ioapic",
|
||||
.version_id = 1,
|
||||
.version_id = 3,
|
||||
.post_load = ioapic_post_load,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField []) {
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(id, IOAPICState),
|
||||
VMSTATE_UINT8(ioregsel, IOAPICState),
|
||||
VMSTATE_UNUSED_V(2, 8), /* to account for qemu-kvm's v2 format */
|
||||
VMSTATE_UINT32_V(irr, IOAPICState, 2),
|
||||
VMSTATE_UINT64_ARRAY(ioredtbl, IOAPICState, IOAPIC_NUM_PINS),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
@@ -220,8 +302,9 @@ static void ioapic_reset(DeviceState *d)
|
||||
s->id = 0;
|
||||
s->ioregsel = 0;
|
||||
s->irr = 0;
|
||||
for(i = 0; i < IOAPIC_NUM_PINS; i++)
|
||||
s->ioredtbl[i] = 1 << 16; /* mask LVT */
|
||||
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
|
||||
s->ioredtbl[i] = 1 << IOAPIC_LVT_MASKED_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc * const ioapic_mem_read[3] = {
|
||||
@@ -240,6 +323,11 @@ static int ioapic_init1(SysBusDevice *dev)
|
||||
{
|
||||
IOAPICState *s = FROM_SYSBUS(IOAPICState, dev);
|
||||
int io_memory;
|
||||
static int ioapic_no;
|
||||
|
||||
if (ioapic_no >= MAX_IOAPICS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
io_memory = cpu_register_io_memory(ioapic_mem_read,
|
||||
ioapic_mem_write, s,
|
||||
@@ -248,6 +336,8 @@ static int ioapic_init1(SysBusDevice *dev)
|
||||
|
||||
qdev_init_gpio_in(&dev->qdev, ioapic_set_irq, IOAPIC_NUM_PINS);
|
||||
|
||||
ioapics[ioapic_no++] = s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
20
hw/ioapic.h
Normal file
20
hw/ioapic.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* ioapic.c IOAPIC emulation logic
|
||||
*
|
||||
* Copyright (c) 2011 Jan Kiszka, Siemens AG
|
||||
*
|
||||
* 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
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
void ioapic_eoi_broadcast(int vector);
|
@@ -147,7 +147,7 @@ void drive_hot_add(Monitor *mon, const QDict *qdict)
|
||||
|
||||
err:
|
||||
if (dinfo)
|
||||
drive_uninit(dinfo);
|
||||
drive_put_ref(dinfo);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -117,6 +117,9 @@ static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev)
|
||||
{
|
||||
VirtQueueElement elem;
|
||||
|
||||
if (!virtio_queue_ready(vq)) {
|
||||
return;
|
||||
}
|
||||
while (virtqueue_pop(vq, &elem)) {
|
||||
virtqueue_push(vq, &elem, 0);
|
||||
}
|
||||
|
@@ -1481,7 +1481,7 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
|
||||
struct elf_shdr *shdr;
|
||||
char *strings;
|
||||
struct syminfo *s;
|
||||
struct elf_sym *syms;
|
||||
struct elf_sym *syms, *new_syms;
|
||||
|
||||
shnum = hdr->e_shnum;
|
||||
i = shnum * sizeof(struct elf_shdr);
|
||||
@@ -1550,12 +1550,14 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
|
||||
that we threw away. Whether or not this has any effect on the
|
||||
memory allocation depends on the malloc implementation and how
|
||||
many symbols we managed to discard. */
|
||||
syms = realloc(syms, nsyms * sizeof(*syms));
|
||||
if (syms == NULL) {
|
||||
new_syms = realloc(syms, nsyms * sizeof(*syms));
|
||||
if (new_syms == NULL) {
|
||||
free(s);
|
||||
free(syms);
|
||||
free(strings);
|
||||
return;
|
||||
}
|
||||
syms = new_syms;
|
||||
|
||||
qsort(syms, nsyms, sizeof(*syms), symcmp);
|
||||
|
||||
|
@@ -312,10 +312,8 @@
|
||||
IOCTL(LOOP_CLR_FD, 0, TYPE_INT)
|
||||
IOCTL(LOOP_SET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info)))
|
||||
IOCTL(LOOP_GET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info)))
|
||||
#if 0 /* These have some problems - not fully tested */
|
||||
IOCTL(LOOP_SET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64)))
|
||||
IOCTL(LOOP_GET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64)))
|
||||
#endif
|
||||
IOCTL(LOOP_CHANGE_FD, 0, TYPE_INT)
|
||||
|
||||
IOCTL(MTIOCTOP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_mtop)))
|
||||
|
@@ -322,6 +322,8 @@ void qemu_iovec_reset(QEMUIOVector *qiov);
|
||||
void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf);
|
||||
void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count);
|
||||
void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count);
|
||||
void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count,
|
||||
size_t skip);
|
||||
|
||||
struct Monitor;
|
||||
typedef struct Monitor Monitor;
|
||||
|
@@ -407,6 +407,7 @@ snapshots.
|
||||
* host_drives:: Using host drives
|
||||
* disk_images_fat_images:: Virtual FAT disk images
|
||||
* disk_images_nbd:: NBD access
|
||||
* disk_images_sheepdog:: Sheepdog disk images
|
||||
@end menu
|
||||
|
||||
@node disk_images_quickstart
|
||||
@@ -630,6 +631,57 @@ qemu -cdrom nbd:localhost:exportname=debian-500-ppc-netinst
|
||||
qemu -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst
|
||||
@end example
|
||||
|
||||
@node disk_images_sheepdog
|
||||
@subsection Sheepdog disk images
|
||||
|
||||
Sheepdog is a distributed storage system for QEMU. It provides highly
|
||||
available block level storage volumes that can be attached to
|
||||
QEMU-based virtual machines.
|
||||
|
||||
You can create a Sheepdog disk image with the command:
|
||||
@example
|
||||
qemu-img create sheepdog:@var{image} @var{size}
|
||||
@end example
|
||||
where @var{image} is the Sheepdog image name and @var{size} is its
|
||||
size.
|
||||
|
||||
To import the existing @var{filename} to Sheepdog, you can use a
|
||||
convert command.
|
||||
@example
|
||||
qemu-img convert @var{filename} sheepdog:@var{image}
|
||||
@end example
|
||||
|
||||
You can boot from the Sheepdog disk image with the command:
|
||||
@example
|
||||
qemu sheepdog:@var{image}
|
||||
@end example
|
||||
|
||||
You can also create a snapshot of the Sheepdog image like qcow2.
|
||||
@example
|
||||
qemu-img snapshot -c @var{tag} sheepdog:@var{image}
|
||||
@end example
|
||||
where @var{tag} is a tag name of the newly created snapshot.
|
||||
|
||||
To boot from the Sheepdog snapshot, specify the tag name of the
|
||||
snapshot.
|
||||
@example
|
||||
qemu sheepdog:@var{image}:@var{tag}
|
||||
@end example
|
||||
|
||||
You can create a cloned image from the existing snapshot.
|
||||
@example
|
||||
qemu-img create -b sheepdog:@var{base}:@var{tag} sheepdog:@var{image}
|
||||
@end example
|
||||
where @var{base} is a image name of the source snapshot and @var{tag}
|
||||
is its tag name.
|
||||
|
||||
If the Sheepdog daemon doesn't run on the local host, you need to
|
||||
specify one of the Sheepdog servers to connect to.
|
||||
@example
|
||||
qemu-img create sheepdog:@var{hostname}:@var{port}:@var{image} @var{size}
|
||||
qemu sheepdog:@var{hostname}:@var{port}:@var{image}
|
||||
@end example
|
||||
|
||||
@node pcsys_network
|
||||
@section Network emulation
|
||||
|
||||
|
10
qemu-img.c
10
qemu-img.c
@@ -213,8 +213,9 @@ static BlockDriverState *bdrv_new_open(const char *filename,
|
||||
BlockDriverState *bs;
|
||||
BlockDriver *drv;
|
||||
char password[256];
|
||||
int ret;
|
||||
|
||||
bs = bdrv_new("");
|
||||
bs = bdrv_new("image");
|
||||
|
||||
if (fmt) {
|
||||
drv = bdrv_find_format(fmt);
|
||||
@@ -225,10 +226,13 @@ static BlockDriverState *bdrv_new_open(const char *filename,
|
||||
} else {
|
||||
drv = NULL;
|
||||
}
|
||||
if (bdrv_open(bs, filename, flags, drv) < 0) {
|
||||
error_report("Could not open '%s'", filename);
|
||||
|
||||
ret = bdrv_open(bs, filename, flags, drv);
|
||||
if (ret < 0) {
|
||||
error_report("Could not open '%s': %s", filename, strerror(-ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bdrv_is_encrypted(bs)) {
|
||||
printf("Disk image '%s' is encrypted.\n", filename);
|
||||
if (read_password(password, sizeof(password)) < 0) {
|
||||
|
71
qemu-timer.c
71
qemu-timer.c
@@ -197,8 +197,8 @@ static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
|
||||
t->rearm(t);
|
||||
}
|
||||
|
||||
/* TODO: MIN_TIMER_REARM_US should be optimized */
|
||||
#define MIN_TIMER_REARM_US 250
|
||||
/* TODO: MIN_TIMER_REARM_NS should be optimized */
|
||||
#define MIN_TIMER_REARM_NS 250000
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -635,6 +635,8 @@ void qemu_run_all_timers(void)
|
||||
qemu_run_timers(host_clock);
|
||||
}
|
||||
|
||||
static int64_t qemu_next_alarm_deadline(void);
|
||||
|
||||
#ifdef _WIN32
|
||||
static void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg,
|
||||
DWORD_PTR dwUser, DWORD_PTR dw1,
|
||||
@@ -677,14 +679,7 @@ static void host_alarm_handler(int host_signum)
|
||||
}
|
||||
#endif
|
||||
if (alarm_has_dynticks(t) ||
|
||||
(!use_icount &&
|
||||
qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL],
|
||||
qemu_get_clock(vm_clock))) ||
|
||||
qemu_timer_expired(active_timers[QEMU_CLOCK_REALTIME],
|
||||
qemu_get_clock(rt_clock)) ||
|
||||
qemu_timer_expired(active_timers[QEMU_CLOCK_HOST],
|
||||
qemu_get_clock(host_clock))) {
|
||||
|
||||
qemu_next_alarm_deadline () <= 0) {
|
||||
t->expired = alarm_has_dynticks(t);
|
||||
t->pending = 1;
|
||||
qemu_notify_event();
|
||||
@@ -698,11 +693,11 @@ int64_t qemu_next_deadline(void)
|
||||
|
||||
if (active_timers[QEMU_CLOCK_VIRTUAL]) {
|
||||
delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time -
|
||||
qemu_get_clock(vm_clock);
|
||||
qemu_get_clock_ns(vm_clock);
|
||||
}
|
||||
if (active_timers[QEMU_CLOCK_HOST]) {
|
||||
int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time -
|
||||
qemu_get_clock(host_clock);
|
||||
qemu_get_clock_ns(host_clock);
|
||||
if (hdelta < delta)
|
||||
delta = hdelta;
|
||||
}
|
||||
@@ -713,35 +708,37 @@ int64_t qemu_next_deadline(void)
|
||||
return delta;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#define RTC_FREQ 1024
|
||||
|
||||
static uint64_t qemu_next_deadline_dyntick(void)
|
||||
static int64_t qemu_next_alarm_deadline(void)
|
||||
{
|
||||
int64_t delta;
|
||||
int64_t rtdelta;
|
||||
|
||||
if (use_icount)
|
||||
if (!use_icount && active_timers[QEMU_CLOCK_VIRTUAL]) {
|
||||
delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time -
|
||||
qemu_get_clock(vm_clock);
|
||||
} else {
|
||||
delta = INT32_MAX;
|
||||
else
|
||||
delta = (qemu_next_deadline() + 999) / 1000;
|
||||
|
||||
}
|
||||
if (active_timers[QEMU_CLOCK_HOST]) {
|
||||
int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time -
|
||||
qemu_get_clock_ns(host_clock);
|
||||
if (hdelta < delta)
|
||||
delta = hdelta;
|
||||
}
|
||||
if (active_timers[QEMU_CLOCK_REALTIME]) {
|
||||
rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time -
|
||||
qemu_get_clock(rt_clock))*1000;
|
||||
rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time * 1000000 -
|
||||
qemu_get_clock_ns(rt_clock));
|
||||
if (rtdelta < delta)
|
||||
delta = rtdelta;
|
||||
}
|
||||
|
||||
if (delta < MIN_TIMER_REARM_US)
|
||||
delta = MIN_TIMER_REARM_US;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#define RTC_FREQ 1024
|
||||
|
||||
static void enable_sigio_timer(int fd)
|
||||
{
|
||||
struct sigaction act;
|
||||
@@ -887,8 +884,8 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
|
||||
{
|
||||
timer_t host_timer = (timer_t)(long)t->priv;
|
||||
struct itimerspec timeout;
|
||||
int64_t nearest_delta_us = INT64_MAX;
|
||||
int64_t current_us;
|
||||
int64_t nearest_delta_ns = INT64_MAX;
|
||||
int64_t current_ns;
|
||||
|
||||
assert(alarm_has_dynticks(t));
|
||||
if (!active_timers[QEMU_CLOCK_REALTIME] &&
|
||||
@@ -896,7 +893,9 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
|
||||
!active_timers[QEMU_CLOCK_HOST])
|
||||
return;
|
||||
|
||||
nearest_delta_us = qemu_next_deadline_dyntick();
|
||||
nearest_delta_ns = qemu_next_alarm_deadline();
|
||||
if (nearest_delta_ns < MIN_TIMER_REARM_NS)
|
||||
nearest_delta_ns = MIN_TIMER_REARM_NS;
|
||||
|
||||
/* check whether a timer is already running */
|
||||
if (timer_gettime(host_timer, &timeout)) {
|
||||
@@ -904,14 +903,14 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
|
||||
fprintf(stderr, "Internal timer error: aborting\n");
|
||||
exit(1);
|
||||
}
|
||||
current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000;
|
||||
if (current_us && current_us <= nearest_delta_us)
|
||||
current_ns = timeout.it_value.tv_sec * 1000000000LL + timeout.it_value.tv_nsec;
|
||||
if (current_ns && current_ns <= nearest_delta_ns)
|
||||
return;
|
||||
|
||||
timeout.it_interval.tv_sec = 0;
|
||||
timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */
|
||||
timeout.it_value.tv_sec = nearest_delta_us / 1000000;
|
||||
timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000;
|
||||
timeout.it_value.tv_sec = nearest_delta_ns / 1000000000;
|
||||
timeout.it_value.tv_nsec = nearest_delta_ns % 1000000000;
|
||||
if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) {
|
||||
perror("settime");
|
||||
fprintf(stderr, "Internal timer error: aborting\n");
|
||||
@@ -921,6 +920,8 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
|
||||
|
||||
#endif /* defined(__linux__) */
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
static int unix_start_timer(struct qemu_alarm_timer *t)
|
||||
{
|
||||
struct sigaction act;
|
||||
|
5
qerror.c
5
qerror.c
@@ -200,6 +200,11 @@ static const QErrorStringTable qerror_table[] = {
|
||||
.error_fmt = QERR_UNDEFINED_ERROR,
|
||||
.desc = "An undefined error has ocurred",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
.desc = "'%(device)' uses a %(format) feature which is not "
|
||||
"supported by this qemu version: %(feature)",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_VNC_SERVER_FAILED,
|
||||
.desc = "Could not start VNC server on %(target)",
|
||||
|
3
qerror.h
3
qerror.h
@@ -165,6 +165,9 @@ QError *qobject_to_qerror(const QObject *obj);
|
||||
#define QERR_UNDEFINED_ERROR \
|
||||
"{ 'class': 'UndefinedError', 'data': {} }"
|
||||
|
||||
#define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \
|
||||
"{ 'class': 'UnknownBlockFormatFeature', 'data': { 'device': %s, 'format': %s, 'feature': %s } }"
|
||||
|
||||
#define QERR_VNC_SERVER_FAILED \
|
||||
"{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }"
|
||||
|
||||
|
10
savevm.c
10
savevm.c
@@ -1638,6 +1638,12 @@ static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection
|
||||
static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque)
|
||||
{
|
||||
const VMStateSubsection *sub = vmsd->subsections;
|
||||
|
||||
if (!sub || !sub->needed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) {
|
||||
char idstr[256];
|
||||
int ret;
|
||||
@@ -1650,10 +1656,11 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
idstr[len] = 0;
|
||||
version_id = qemu_get_be32(f);
|
||||
|
||||
sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
|
||||
sub_vmsd = vmstate_get_subsection(sub, idstr);
|
||||
if (sub_vmsd == NULL) {
|
||||
return -ENOENT;
|
||||
}
|
||||
assert(!sub_vmsd->subsections);
|
||||
ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
|
||||
if (ret) {
|
||||
return ret;
|
||||
@@ -1677,6 +1684,7 @@ static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
qemu_put_byte(f, len);
|
||||
qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
|
||||
qemu_put_be32(f, vmsd->version_id);
|
||||
assert(!vmsd->subsections);
|
||||
vmstate_save_state(f, vmsd, opaque);
|
||||
}
|
||||
sub++;
|
||||
|
@@ -23,7 +23,7 @@
|
||||
* Find a nice value for msize
|
||||
* XXX if_maxlinkhdr already in mtu
|
||||
*/
|
||||
#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + sizeof(struct m_hdr ) + 6)
|
||||
#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6)
|
||||
|
||||
void
|
||||
m_init(Slirp *slirp)
|
||||
@@ -65,7 +65,7 @@ m_get(Slirp *slirp)
|
||||
m->m_flags = (flags | M_USEDLIST);
|
||||
|
||||
/* Initialise it */
|
||||
m->m_size = SLIRP_MSIZE - sizeof(struct m_hdr);
|
||||
m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat);
|
||||
m->m_data = m->m_dat;
|
||||
m->m_len = 0;
|
||||
m->m_nextpkt = NULL;
|
||||
|
@@ -137,10 +137,6 @@ DEF_HELPER_2(rsqrte_f32, f32, f32, env)
|
||||
DEF_HELPER_2(recpe_u32, i32, i32, env)
|
||||
DEF_HELPER_2(rsqrte_u32, i32, i32, env)
|
||||
DEF_HELPER_4(neon_tbl, i32, i32, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_add_saturate_u64, i64, i64, i64)
|
||||
DEF_HELPER_2(neon_add_saturate_s64, i64, i64, i64)
|
||||
DEF_HELPER_2(neon_sub_saturate_u64, i64, i64, i64)
|
||||
DEF_HELPER_2(neon_sub_saturate_s64, i64, i64, i64)
|
||||
|
||||
DEF_HELPER_2(add_cc, i32, i32, i32)
|
||||
DEF_HELPER_2(adc_cc, i32, i32, i32)
|
||||
@@ -160,10 +156,18 @@ DEF_HELPER_3(neon_qadd_u8, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qadd_s8, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qadd_u16, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qadd_s16, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qadd_u32, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qadd_s32, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qsub_s16, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qsub_u32, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qsub_s32, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qadd_u64, i64, env, i64, i64)
|
||||
DEF_HELPER_3(neon_qadd_s64, i64, env, i64, i64)
|
||||
DEF_HELPER_3(neon_qsub_u64, i64, env, i64, i64)
|
||||
DEF_HELPER_3(neon_qsub_s64, i64, env, i64, i64)
|
||||
|
||||
DEF_HELPER_2(neon_hadd_s8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_hadd_u8, i32, i32, i32)
|
||||
|
@@ -198,6 +198,28 @@ NEON_VOP_ENV(qadd_u16, neon_u16, 2)
|
||||
#undef NEON_FN
|
||||
#undef NEON_USAT
|
||||
|
||||
uint32_t HELPER(neon_qadd_u32)(CPUState *env, uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t res = a + b;
|
||||
if (res < a) {
|
||||
SET_QC();
|
||||
res = ~0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t HELPER(neon_qadd_u64)(CPUState *env, uint64_t src1, uint64_t src2)
|
||||
{
|
||||
uint64_t res;
|
||||
|
||||
res = src1 + src2;
|
||||
if (res < src1) {
|
||||
SET_QC();
|
||||
res = ~(uint64_t)0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define NEON_SSAT(dest, src1, src2, type) do { \
|
||||
int32_t tmp = (uint32_t)src1 + (uint32_t)src2; \
|
||||
if (tmp != (type)tmp) { \
|
||||
@@ -218,6 +240,28 @@ NEON_VOP_ENV(qadd_s16, neon_s16, 2)
|
||||
#undef NEON_FN
|
||||
#undef NEON_SSAT
|
||||
|
||||
uint32_t HELPER(neon_qadd_s32)(CPUState *env, uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t res = a + b;
|
||||
if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) {
|
||||
SET_QC();
|
||||
res = ~(((int32_t)a >> 31) ^ SIGNBIT);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t HELPER(neon_qadd_s64)(CPUState *env, uint64_t src1, uint64_t src2)
|
||||
{
|
||||
uint64_t res;
|
||||
|
||||
res = src1 + src2;
|
||||
if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) {
|
||||
SET_QC();
|
||||
res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define NEON_USAT(dest, src1, src2, type) do { \
|
||||
uint32_t tmp = (uint32_t)src1 - (uint32_t)src2; \
|
||||
if (tmp != (type)tmp) { \
|
||||
@@ -234,6 +278,29 @@ NEON_VOP_ENV(qsub_u16, neon_u16, 2)
|
||||
#undef NEON_FN
|
||||
#undef NEON_USAT
|
||||
|
||||
uint32_t HELPER(neon_qsub_u32)(CPUState *env, uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t res = a - b;
|
||||
if (res > a) {
|
||||
SET_QC();
|
||||
res = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t HELPER(neon_qsub_u64)(CPUState *env, uint64_t src1, uint64_t src2)
|
||||
{
|
||||
uint64_t res;
|
||||
|
||||
if (src1 < src2) {
|
||||
SET_QC();
|
||||
res = 0;
|
||||
} else {
|
||||
res = src1 - src2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define NEON_SSAT(dest, src1, src2, type) do { \
|
||||
int32_t tmp = (uint32_t)src1 - (uint32_t)src2; \
|
||||
if (tmp != (type)tmp) { \
|
||||
@@ -254,6 +321,28 @@ NEON_VOP_ENV(qsub_s16, neon_s16, 2)
|
||||
#undef NEON_FN
|
||||
#undef NEON_SSAT
|
||||
|
||||
uint32_t HELPER(neon_qsub_s32)(CPUState *env, uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t res = a - b;
|
||||
if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
|
||||
SET_QC();
|
||||
res = ~(((int32_t)a >> 31) ^ SIGNBIT);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t HELPER(neon_qsub_s64)(CPUState *env, uint64_t src1, uint64_t src2)
|
||||
{
|
||||
uint64_t res;
|
||||
|
||||
res = src1 - src2;
|
||||
if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) {
|
||||
SET_QC();
|
||||
res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define NEON_FN(dest, src1, src2) dest = (src1 + src2) >> 1
|
||||
NEON_VOP(hadd_s8, neon_s8, 4)
|
||||
NEON_VOP(hadd_u8, neon_u8, 4)
|
||||
|
@@ -424,52 +424,3 @@ uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i)
|
||||
return ((uint32_t)x >> shift) | (x << (32 - shift));
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t HELPER(neon_add_saturate_s64)(uint64_t src1, uint64_t src2)
|
||||
{
|
||||
uint64_t res;
|
||||
|
||||
res = src1 + src2;
|
||||
if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) {
|
||||
env->QF = 1;
|
||||
res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t HELPER(neon_add_saturate_u64)(uint64_t src1, uint64_t src2)
|
||||
{
|
||||
uint64_t res;
|
||||
|
||||
res = src1 + src2;
|
||||
if (res < src1) {
|
||||
env->QF = 1;
|
||||
res = ~(uint64_t)0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t HELPER(neon_sub_saturate_s64)(uint64_t src1, uint64_t src2)
|
||||
{
|
||||
uint64_t res;
|
||||
|
||||
res = src1 - src2;
|
||||
if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) {
|
||||
env->QF = 1;
|
||||
res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t HELPER(neon_sub_saturate_u64)(uint64_t src1, uint64_t src2)
|
||||
{
|
||||
uint64_t res;
|
||||
|
||||
if (src1 < src2) {
|
||||
env->QF = 1;
|
||||
res = 0;
|
||||
} else {
|
||||
res = src1 - src2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@@ -3539,12 +3539,6 @@ static inline void gen_neon_rsb(int size, TCGv t0, TCGv t1)
|
||||
#define gen_helper_neon_pmin_s32 gen_helper_neon_min_s32
|
||||
#define gen_helper_neon_pmin_u32 gen_helper_neon_min_u32
|
||||
|
||||
/* FIXME: This is wrong. They set the wrong overflow bit. */
|
||||
#define gen_helper_neon_qadd_s32(a, e, b, c) gen_helper_add_saturate(a, b, c)
|
||||
#define gen_helper_neon_qadd_u32(a, e, b, c) gen_helper_add_usaturate(a, b, c)
|
||||
#define gen_helper_neon_qsub_s32(a, e, b, c) gen_helper_sub_saturate(a, b, c)
|
||||
#define gen_helper_neon_qsub_u32(a, e, b, c) gen_helper_sub_usaturate(a, b, c)
|
||||
|
||||
#define GEN_NEON_INTEGER_OP_ENV(name) do { \
|
||||
switch ((size << 1) | u) { \
|
||||
case 0: \
|
||||
@@ -4233,16 +4227,20 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
|
||||
switch (op) {
|
||||
case 1: /* VQADD */
|
||||
if (u) {
|
||||
gen_helper_neon_add_saturate_u64(CPU_V001);
|
||||
gen_helper_neon_qadd_u64(cpu_V0, cpu_env,
|
||||
cpu_V0, cpu_V1);
|
||||
} else {
|
||||
gen_helper_neon_add_saturate_s64(CPU_V001);
|
||||
gen_helper_neon_qadd_s64(cpu_V0, cpu_env,
|
||||
cpu_V0, cpu_V1);
|
||||
}
|
||||
break;
|
||||
case 5: /* VQSUB */
|
||||
if (u) {
|
||||
gen_helper_neon_sub_saturate_u64(CPU_V001);
|
||||
gen_helper_neon_qsub_u64(cpu_V0, cpu_env,
|
||||
cpu_V0, cpu_V1);
|
||||
} else {
|
||||
gen_helper_neon_sub_saturate_s64(CPU_V001);
|
||||
gen_helper_neon_qsub_s64(cpu_V0, cpu_env,
|
||||
cpu_V0, cpu_V1);
|
||||
}
|
||||
break;
|
||||
case 8: /* VSHL */
|
||||
@@ -4686,7 +4684,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
|
||||
}
|
||||
if (op == 1 || op == 3) {
|
||||
/* Accumulate. */
|
||||
neon_load_reg64(cpu_V0, rd + pass);
|
||||
neon_load_reg64(cpu_V1, rd + pass);
|
||||
tcg_gen_add_i64(cpu_V0, cpu_V0, cpu_V1);
|
||||
} else if (op == 4 || (op == 5 && u)) {
|
||||
/* Insert */
|
||||
@@ -4750,7 +4748,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
|
||||
if (op == 1 || op == 3) {
|
||||
/* Accumulate. */
|
||||
tmp2 = neon_load_reg(rd, pass);
|
||||
gen_neon_add(size, tmp2, tmp);
|
||||
gen_neon_add(size, tmp, tmp2);
|
||||
dead_tmp(tmp2);
|
||||
} else if (op == 4 || (op == 5 && u)) {
|
||||
/* Insert */
|
||||
|
@@ -1147,8 +1147,8 @@ void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
|
||||
if (cenv == env) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qemu_inject_x86_mce(env, 1, 0xa000000000000000, 0, 0, 0);
|
||||
qemu_inject_x86_mce(env, 1, MCI_STATUS_VAL | MCI_STATUS_UC,
|
||||
MCG_STATUS_MCIP | MCG_STATUS_RIPV, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -960,9 +960,9 @@ static void _decode_opc(DisasContext * ctx)
|
||||
tcg_gen_andi_i32(t1, cpu_sr, SR_T);
|
||||
tcg_gen_sub_i32(REG(B11_8), t0, t1);
|
||||
tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
|
||||
tcg_gen_setcond_i32(TCG_COND_GE, t1, REG(B11_8), t0);
|
||||
tcg_gen_setcondi_i32(TCG_COND_GTU, t1, t0, 0);
|
||||
tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
|
||||
tcg_gen_setcondi_i32(TCG_COND_GE, t1, t0, 0);
|
||||
tcg_gen_setcond_i32(TCG_COND_GTU, t1, REG(B11_8), t0);
|
||||
tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
|
8
ui/sdl.c
8
ui/sdl.c
@@ -388,12 +388,16 @@ static void sdl_process_key(SDL_KeyboardEvent *ev)
|
||||
else
|
||||
modifiers_state[keycode] = 1;
|
||||
break;
|
||||
#define QEMU_SDL_VERSION ((SDL_MAJOR_VERSION << 8) + SDL_MINOR_VERSION)
|
||||
#if QEMU_SDL_VERSION < 0x102 || QEMU_SDL_VERSION == 0x102 && SDL_PATCHLEVEL < 14
|
||||
/* SDL versions before 1.2.14 don't support key up for caps/num lock. */
|
||||
case 0x45: /* num lock */
|
||||
case 0x3a: /* caps lock */
|
||||
/* SDL does not send the key up event, so we generate it */
|
||||
kbd_put_keycode(keycode);
|
||||
kbd_put_keycode(keycode | SCANCODE_UP);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* now send the key code */
|
||||
@@ -831,6 +835,10 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
|
||||
setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
|
||||
}
|
||||
|
||||
/* Enable normal up/down events for Caps-Lock and Num-Lock keys.
|
||||
* This requires SDL >= 1.2.14. */
|
||||
setenv("SDL_DISABLE_LOCK_KEYS", "1", 1);
|
||||
|
||||
flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
|
||||
if (SDL_Init (flags)) {
|
||||
fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
|
||||
|
@@ -227,6 +227,10 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
|
||||
|
||||
if (job->vs->csock == -1) {
|
||||
vnc_unlock_display(job->vs->vd);
|
||||
/* output mutex must be locked before going to
|
||||
* disconnected:
|
||||
*/
|
||||
vnc_lock_output(job->vs);
|
||||
goto disconnected;
|
||||
}
|
||||
|
||||
|
4
vl.c
4
vl.c
@@ -738,7 +738,7 @@ void add_boot_device_path(int32_t bootindex, DeviceState *dev,
|
||||
|
||||
node = qemu_mallocz(sizeof(FWBootEntry));
|
||||
node->bootindex = bootindex;
|
||||
node->suffix = strdup(suffix);
|
||||
node->suffix = suffix ? qemu_strdup(suffix) : NULL;
|
||||
node->dev = dev;
|
||||
|
||||
QTAILQ_FOREACH(i, &fw_boot_order, link) {
|
||||
@@ -785,7 +785,7 @@ char *get_boot_devices_list(uint32_t *size)
|
||||
} else if (devpath) {
|
||||
bootpath = devpath;
|
||||
} else {
|
||||
bootpath = strdup(i->suffix);
|
||||
bootpath = qemu_strdup(i->suffix);
|
||||
assert(bootpath);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user