diff options
author | Avi Kivity <avi@redhat.com> | 2009-06-03 13:21:49 +0300 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2009-06-03 13:25:06 +0300 |
commit | 0936eb0a32e9442e6722bd517d4bfa43278cebaf (patch) | |
tree | 56d987c9f50c3111e087097304d9ee4e8c511c6c | |
parent | Provide a kvm-free pic implementation (diff) | |
parent | Update maintainer list. (diff) | |
download | qemu-kvm-0936eb0a32e9442e6722bd517d4bfa43278cebaf.tar.gz qemu-kvm-0936eb0a32e9442e6722bd517d4bfa43278cebaf.tar.bz2 qemu-kvm-0936eb0a32e9442e6722bd517d4bfa43278cebaf.zip |
Merge branch 'master' of git://git.sv.gnu.org/qemu
* 'master' of git://git.sv.gnu.org/qemu: (40 commits)
Update maintainer list.
Install keymaps from new location
vvfat: one more missing BlockDriver C99 initializer conversion
Move keymaps into pc-bios
kvm: Mark full address range dirty on live migration start
Add detection of pthread library name
User networking: Show active connections
User Networking: Enable removal of redirections
Allow monitor interaction when using migrate -exec
fully split aio_pool from BlockDriver
qcow: add qcow_aio_setup helper
raw-posix: fix hdev_create
fix raw_pread_aligned return value
VNC: Fix memory allocation (wrong structure size).
Drop bdrv_create2
qcow2: Update multiple refcounts at once
qcow2: Refactor update_refcount
qcow/qcow2: Drop synchronous qcow_write()
e1000: Ignore reset command
Fix output of uninitialized strings
...
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | MAINTAINERS | 4 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | Makefile.hw | 2 | ||||
-rw-r--r-- | Makefile.target | 23 | ||||
-rw-r--r-- | block.c | 120 | ||||
-rw-r--r-- | block/curl.c | 27 | ||||
-rw-r--r-- | block/qcow.c | 111 | ||||
-rw-r--r-- | block/qcow2.c | 185 | ||||
-rw-r--r-- | block/raw-posix.c | 94 | ||||
-rw-r--r-- | block/vvfat.c | 18 | ||||
-rw-r--r-- | block_int.h | 13 | ||||
-rwxr-xr-x | configure | 44 | ||||
-rw-r--r-- | cpu-exec.c | 68 | ||||
-rw-r--r-- | dis-asm.h | 2 | ||||
-rw-r--r-- | disas.c | 3 | ||||
-rw-r--r-- | dma-helpers.c | 32 | ||||
-rw-r--r-- | dma.h | 2 | ||||
-rw-r--r-- | elf.h | 2 | ||||
-rw-r--r-- | gdbstub.c | 32 | ||||
-rw-r--r-- | hw/arm_gic.c | 2 | ||||
-rw-r--r-- | hw/armv7m.c | 2 | ||||
-rw-r--r-- | hw/axis_dev88.c | 6 | ||||
-rw-r--r-- | hw/e1000.c | 11 | ||||
-rw-r--r-- | hw/esp.c | 2 | ||||
-rw-r--r-- | hw/etraxfs.c | 6 | ||||
-rw-r--r-- | hw/etraxfs_pic.c | 2 | ||||
-rw-r--r-- | hw/hw.h | 1 | ||||
-rw-r--r-- | hw/integratorcp.c | 4 | ||||
-rw-r--r-- | hw/microblaze_pic_cpu.c | 50 | ||||
-rw-r--r-- | hw/mpcore.c | 6 | ||||
-rw-r--r-- | hw/musicpal.c | 4 | ||||
-rw-r--r-- | hw/pcnet.c | 2 | ||||
-rw-r--r-- | hw/petalogix_s3adsp1800_mmu.c | 199 | ||||
-rw-r--r-- | hw/pl190.c | 2 | ||||
-rw-r--r-- | hw/qdev.c | 13 | ||||
-rw-r--r-- | hw/qdev.h | 5 | ||||
-rw-r--r-- | hw/realview.c | 2 | ||||
-rw-r--r-- | hw/sun4m.c | 2 | ||||
-rw-r--r-- | hw/syborg.c | 2 | ||||
-rw-r--r-- | hw/syborg_interrupt.c | 2 | ||||
-rw-r--r-- | hw/versatilepb.c | 6 | ||||
-rw-r--r-- | hw/xilinx.h | 50 | ||||
-rw-r--r-- | hw/xilinx_ethlite.c | 235 | ||||
-rw-r--r-- | hw/xilinx_intc.c | 167 | ||||
-rw-r--r-- | hw/xilinx_timer.c | 224 | ||||
-rw-r--r-- | hw/xilinx_uartlite.c | 218 | ||||
-rw-r--r-- | linux-user/elfload.c | 22 | ||||
-rw-r--r-- | linux-user/main.c | 96 | ||||
-rw-r--r-- | linux-user/microblaze/syscall.h | 45 | ||||
-rw-r--r-- | linux-user/microblaze/syscall_nr.h | 369 | ||||
-rw-r--r-- | linux-user/microblaze/target_signal.h | 29 | ||||
-rw-r--r-- | linux-user/microblaze/termbits.h | 213 | ||||
-rw-r--r-- | linux-user/signal.c | 216 | ||||
-rw-r--r-- | linux-user/syscall.c | 2 | ||||
-rw-r--r-- | linux-user/syscall_defs.h | 73 | ||||
-rwxr-xr-x | microblaze-dis.c | 846 | ||||
-rw-r--r-- | migration-exec.c | 33 | ||||
-rw-r--r-- | monitor.c | 6 | ||||
-rw-r--r-- | net.c | 117 | ||||
-rw-r--r-- | net.h | 2 | ||||
-rw-r--r-- | pc-bios/keymaps/ar (renamed from keymaps/ar) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/common (renamed from keymaps/common) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/da (renamed from keymaps/da) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/de (renamed from keymaps/de) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/de-ch (renamed from keymaps/de-ch) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/en-gb (renamed from keymaps/en-gb) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/en-us (renamed from keymaps/en-us) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/es (renamed from keymaps/es) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/et (renamed from keymaps/et) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/fi (renamed from keymaps/fi) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/fo (renamed from keymaps/fo) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/fr (renamed from keymaps/fr) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/fr-be (renamed from keymaps/fr-be) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/fr-ca (renamed from keymaps/fr-ca) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/fr-ch (renamed from keymaps/fr-ch) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/hr (renamed from keymaps/hr) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/hu (renamed from keymaps/hu) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/is (renamed from keymaps/is) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/it (renamed from keymaps/it) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/ja (renamed from keymaps/ja) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/lt (renamed from keymaps/lt) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/lv (renamed from keymaps/lv) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/mk (renamed from keymaps/mk) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/modifiers (renamed from keymaps/modifiers) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/nl (renamed from keymaps/nl) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/nl-be (renamed from keymaps/nl-be) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/no (renamed from keymaps/no) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/pl (renamed from keymaps/pl) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/pt (renamed from keymaps/pt) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/pt-br (renamed from keymaps/pt-br) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/ru (renamed from keymaps/ru) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/sl (renamed from keymaps/sl) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/sv (renamed from keymaps/sv) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/th (renamed from keymaps/th) | 0 | ||||
-rw-r--r-- | pc-bios/keymaps/tr (renamed from keymaps/tr) | 0 | ||||
-rw-r--r-- | qemu-doc.texi | 4 | ||||
-rw-r--r-- | savevm.c | 12 | ||||
-rw-r--r-- | slirp/libslirp.h | 5 | ||||
-rw-r--r-- | slirp/slirp.c | 47 | ||||
-rw-r--r-- | target-microblaze/cpu.h | 311 | ||||
-rw-r--r-- | target-microblaze/exec.h | 57 | ||||
-rw-r--r-- | target-microblaze/helper.c | 255 | ||||
-rw-r--r-- | target-microblaze/helper.h | 19 | ||||
-rw-r--r-- | target-microblaze/machine.c | 11 | ||||
-rw-r--r-- | target-microblaze/microblaze-decode.h | 52 | ||||
-rw-r--r-- | target-microblaze/mmu.c | 257 | ||||
-rw-r--r-- | target-microblaze/mmu.h | 88 | ||||
-rw-r--r-- | target-microblaze/op_helper.c | 216 | ||||
-rw-r--r-- | target-microblaze/translate.c | 1395 | ||||
-rw-r--r-- | targphys.h | 2 | ||||
-rw-r--r-- | vl.c | 8 | ||||
-rw-r--r-- | vnc.c | 3 |
112 files changed, 6384 insertions, 436 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index a5bbec247..080e1c0a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19,6 +19,7 @@ M68K Paul Brook SH4 ? CRIS Edgar E. Iglesias Alpha ? +MicroBlaze Edgar E. Iglesias Machines (sorted by CPU): ------------------------- @@ -58,7 +59,10 @@ SH4 r2d.c Magnus Damm CRIS etraxfs.c Edgar E. Iglesias + axis_dev88.c Edgar E. Iglesias Alpha +MicroBlaze + petalogix_s3adsp1800.c Edgar E. Iglesias Generic Subsystems: ------------------- @@ -319,7 +319,7 @@ endif ifndef CONFIG_WIN32 $(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps" set -e; for x in $(KEYMAPS); do \ - $(INSTALL_DATA) $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \ + $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \ done endif for d in $(TARGET_DIRS); do \ diff --git a/Makefile.hw b/Makefile.hw index 010e63c8a..6accb3bc7 100644 --- a/Makefile.hw +++ b/Makefile.hw @@ -29,6 +29,8 @@ OBJS+= lsi53c895a.o esp.o OBJS+= dma-helpers.o sysbus.o all: $(HWLIB) +# Dummy command so that make thinks it has done something + @true $(HWLIB): $(OBJS) diff --git a/Makefile.target b/Makefile.target index 0097aa89d..781144487 100644 --- a/Makefile.target +++ b/Makefile.target @@ -137,6 +137,8 @@ kvm-all.o: CFLAGS+=$(KVM_CFLAGS) CFLAGS += $(KVM_CFLAGS) all: $(PROGS) +# Dummy command so that make thinks it has done something + @true ######################################################### # cpu emulator library @@ -233,6 +235,12 @@ endif ifeq ($(findstring ppc, $(TARGET_BASE_ARCH) $(ARCH)),ppc) LIBOBJS+=ppc-dis.o endif +ifeq ($(findstring microblaze, $(TARGET_BASE_ARCH) $(ARCH)),microblaze) +LIBOBJS+=microblaze-dis.o +ifndef CONFIG_USER_ONLY +LIBOBJS+= mmu.o +endif +endif ifeq ($(findstring mips, $(TARGET_BASE_ARCH) $(ARCH)),mips) LIBOBJS+=mips-dis.o endif @@ -679,6 +687,21 @@ OBJS+= pflash_cfi01.o OBJS+= vmware_vga.o CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE endif +ifeq ($(TARGET_BASE_ARCH), microblaze) +OBJS+= petalogix_s3adsp1800_mmu.o + +OBJS+= microblaze_pic_cpu.o +OBJS+= xilinx_intc.o +OBJS+= xilinx_timer.o +OBJS+= xilinx_uartlite.o +OBJS+= xilinx_ethlite.o + +OBJS+= pflash_cfi02.o +ifdef FDT_LIBS +OBJS+= device_tree.o +LIBS+= $(FDT_LIBS) +endif +endif ifeq ($(TARGET_BASE_ARCH), cris) # Boards OBJS+= cris_pic_cpu.o etraxfs.o axis_dev88.o @@ -48,23 +48,12 @@ #define SECTOR_BITS 9 #define SECTOR_SIZE (1 << SECTOR_BITS) -typedef struct BlockDriverAIOCBSync { - BlockDriverAIOCB common; - QEMUBH *bh; - int ret; - /* vector translation state */ - QEMUIOVector *qiov; - uint8_t *bounce; - int is_write; -} BlockDriverAIOCBSync; - static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); -static void bdrv_aio_cancel_em(BlockDriverAIOCB *acb); static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, @@ -138,21 +127,17 @@ void path_combine(char *dest, int dest_size, } } - void bdrv_register(BlockDriver *bdrv) { if (!bdrv->bdrv_aio_readv) { /* add AIO emulation layer */ bdrv->bdrv_aio_readv = bdrv_aio_readv_em; bdrv->bdrv_aio_writev = bdrv_aio_writev_em; - bdrv->bdrv_aio_cancel = bdrv_aio_cancel_em; - bdrv->aiocb_size = sizeof(BlockDriverAIOCBSync); } else if (!bdrv->bdrv_read) { /* add synchronous IO emulation layer */ bdrv->bdrv_read = bdrv_read_em; bdrv->bdrv_write = bdrv_write_em; } - aio_pool_init(&bdrv->aio_pool, bdrv->aiocb_size, bdrv->bdrv_aio_cancel); bdrv->next = first_drv; first_drv = bdrv; } @@ -184,42 +169,6 @@ BlockDriver *bdrv_find_format(const char *format_name) return NULL; } -int bdrv_create2(BlockDriver *drv, - const char *filename, int64_t size_in_sectors, - const char *backing_file, const char *backing_format, - int flags) -{ - QEMUOptionParameter *options; - - options = parse_option_parameters("", drv->create_options, NULL); - - // Process flags - if (flags & ~(BLOCK_FLAG_ENCRYPT | BLOCK_FLAG_COMPAT6 | BLOCK_FLAG_COMPRESS)) { - return -ENOTSUP; - } - - if (flags & BLOCK_FLAG_ENCRYPT) { - set_option_parameter_int(options, BLOCK_OPT_ENCRYPT, 1); - } - if (flags & BLOCK_FLAG_COMPAT6) { - set_option_parameter_int(options, BLOCK_OPT_COMPAT6, 1); - } - - // Add size to options - set_option_parameter_int(options, BLOCK_OPT_SIZE, size_in_sectors * 512); - - // Backing files - if ((backing_file != NULL && set_option_parameter(options, - BLOCK_OPT_BACKING_FILE, backing_file)) - || (backing_format != NULL && set_option_parameter(options, - BLOCK_OPT_BACKING_FMT, backing_format))) - { - return -ENOTSUP; - } - - return bdrv_create(drv, filename, options); -} - int bdrv_create(BlockDriver *drv, const char* filename, QEMUOptionParameter *options) { @@ -392,6 +341,8 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, BlockDriverState *bs1; int64_t total_size; int is_protocol = 0; + BlockDriver *bdrv_qcow2; + QEMUOptionParameter *options; /* if snapshot, we create a temporary backing file and open it instead of opening 'filename' directly */ @@ -419,14 +370,23 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, else realpath(filename, backing_filename); - ret = bdrv_create2(bdrv_find_format("qcow2"), tmp_filename, - total_size, backing_filename, - (drv ? drv->format_name : NULL), 0); + bdrv_qcow2 = bdrv_find_format("qcow2"); + options = parse_option_parameters("", bdrv_qcow2->create_options, NULL); + + set_option_parameter_int(options, BLOCK_OPT_SIZE, total_size * 512); + set_option_parameter(options, BLOCK_OPT_BACKING_FILE, backing_filename); + if (drv) { + set_option_parameter(options, BLOCK_OPT_BACKING_FMT, + drv->format_name); + } + + ret = bdrv_create(bdrv_qcow2, tmp_filename, options); if (ret < 0) { return ret; } + filename = tmp_filename; - drv = bdrv_find_format("qcow2"); + drv = bdrv_qcow2; bs->is_temporary = 1; } @@ -1394,6 +1354,28 @@ void bdrv_aio_cancel(BlockDriverAIOCB *acb) /**************************************************************/ /* async block device emulation */ +typedef struct BlockDriverAIOCBSync { + BlockDriverAIOCB common; + QEMUBH *bh; + int ret; + /* vector translation state */ + QEMUIOVector *qiov; + uint8_t *bounce; + int is_write; +} BlockDriverAIOCBSync; + +static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb) +{ + BlockDriverAIOCBSync *acb = (BlockDriverAIOCBSync *)blockacb; + qemu_bh_cancel(acb->bh); + qemu_aio_release(acb); +} + +static AIOPool bdrv_em_aio_pool = { + .aiocb_size = sizeof(BlockDriverAIOCBSync), + .cancel = bdrv_aio_cancel_em, +}; + static void bdrv_aio_bh_cb(void *opaque) { BlockDriverAIOCBSync *acb = opaque; @@ -1417,7 +1399,7 @@ static BlockDriverAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs, { BlockDriverAIOCBSync *acb; - acb = qemu_aio_get(bs, cb, opaque); + acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque); acb->is_write = is_write; acb->qiov = qiov; acb->bounce = qemu_blockalign(bs, qiov->size); @@ -1451,14 +1433,6 @@ static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs, return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); } - -static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb) -{ - BlockDriverAIOCBSync *acb = (BlockDriverAIOCBSync *)blockacb; - qemu_bh_cancel(acb->bh); - qemu_aio_release(acb); -} - /**************************************************************/ /* sync block device emulation */ @@ -1520,16 +1494,8 @@ void bdrv_init(void) module_call_init(MODULE_INIT_BLOCK); } -void aio_pool_init(AIOPool *pool, int aiocb_size, - void (*cancel)(BlockDriverAIOCB *acb)) -{ - pool->aiocb_size = aiocb_size; - pool->cancel = cancel; - pool->free_aiocb = NULL; -} - -void *qemu_aio_get_pool(AIOPool *pool, BlockDriverState *bs, - BlockDriverCompletionFunc *cb, void *opaque) +void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) { BlockDriverAIOCB *acb; @@ -1546,12 +1512,6 @@ void *qemu_aio_get_pool(AIOPool *pool, BlockDriverState *bs, return acb; } -void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb, - void *opaque) -{ - return qemu_aio_get_pool(&bs->drv->aio_pool, bs, cb, opaque); -} - void qemu_aio_release(void *p) { BlockDriverAIOCB *acb = (BlockDriverAIOCB *)p; diff --git a/block/curl.c b/block/curl.c index 5d1487dea..e1a553f7a 100644 --- a/block/curl.c +++ b/block/curl.c @@ -349,6 +349,16 @@ out_noclean: return -EINVAL; } +static void curl_aio_cancel(BlockDriverAIOCB *blockacb) +{ + // Do we have to implement canceling? Seems to work without... +} + +static AIOPool curl_aio_pool = { + .aiocb_size = sizeof(CURLAIOCB), + .cancel = curl_aio_cancel, +}; + static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) @@ -359,7 +369,7 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, size_t end; CURLState *state; - acb = qemu_aio_get(bs, cb, opaque); + acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque); if (!acb) return NULL; @@ -406,11 +416,6 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, return &acb->common; } -static void curl_aio_cancel(BlockDriverAIOCB *blockacb) -{ - // Do we have to implement canceling? Seems to work without... -} - static void curl_close(BlockDriverState *bs) { BDRVCURLState *s = bs->opaque; @@ -450,9 +455,7 @@ static BlockDriver bdrv_http = { .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, - .aiocb_size = sizeof(CURLAIOCB), .bdrv_aio_readv = curl_aio_readv, - .bdrv_aio_cancel = curl_aio_cancel, }; static BlockDriver bdrv_https = { @@ -464,9 +467,7 @@ static BlockDriver bdrv_https = { .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, - .aiocb_size = sizeof(CURLAIOCB), .bdrv_aio_readv = curl_aio_readv, - .bdrv_aio_cancel = curl_aio_cancel, }; static BlockDriver bdrv_ftp = { @@ -478,9 +479,7 @@ static BlockDriver bdrv_ftp = { .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, - .aiocb_size = sizeof(CURLAIOCB), .bdrv_aio_readv = curl_aio_readv, - .bdrv_aio_cancel = curl_aio_cancel, }; static BlockDriver bdrv_ftps = { @@ -492,9 +491,7 @@ static BlockDriver bdrv_ftps = { .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, - .aiocb_size = sizeof(CURLAIOCB), .bdrv_aio_readv = curl_aio_readv, - .bdrv_aio_cancel = curl_aio_cancel, }; static BlockDriver bdrv_tftp = { @@ -506,9 +503,7 @@ static BlockDriver bdrv_tftp = { .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, - .aiocb_size = sizeof(CURLAIOCB), .bdrv_aio_readv = curl_aio_readv, - .bdrv_aio_cancel = curl_aio_cancel, }; static void curl_block_init(void) diff --git a/block/qcow.c b/block/qcow.c index 6ecf2e8e0..329b364b7 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -488,41 +488,6 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num, } #endif -static int qcow_write(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) -{ - BDRVQcowState *s = bs->opaque; - int ret, index_in_cluster, n; - uint64_t cluster_offset; - - while (nb_sectors > 0) { - index_in_cluster = sector_num & (s->cluster_sectors - 1); - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) - n = nb_sectors; - cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0, - index_in_cluster, - index_in_cluster + n); - if (!cluster_offset) - return -1; - if (s->crypt_method) { - encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, - &s->aes_encrypt_key); - ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, - s->cluster_data, n * 512); - } else { - ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); - } - if (ret != n * 512) - return -1; - nb_sectors -= n; - sector_num += n; - buf += n * 512; - } - s->cluster_cache_offset = -1; /* disable compressed cache */ - return 0; -} - typedef struct QCowAIOCB { BlockDriverAIOCB common; int64_t sector_num; @@ -538,6 +503,44 @@ typedef struct QCowAIOCB { BlockDriverAIOCB *hd_aiocb; } QCowAIOCB; +static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) +{ + QCowAIOCB *acb = (QCowAIOCB *)blockacb; + if (acb->hd_aiocb) + bdrv_aio_cancel(acb->hd_aiocb); + qemu_aio_release(acb); +} + +static AIOPool qcow_aio_pool = { + .aiocb_size = sizeof(QCowAIOCB), + .cancel = qcow_aio_cancel, +}; + +static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque, int is_write) +{ + QCowAIOCB *acb; + + acb = qemu_aio_get(&qcow_aio_pool, bs, cb, opaque); + if (!acb) + return NULL; + acb->hd_aiocb = NULL; + acb->sector_num = sector_num; + acb->qiov = qiov; + if (qiov->niov > 1) { + acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size); + if (is_write) + qemu_iovec_to_buffer(qiov, acb->buf); + } else { + acb->buf = (uint8_t *)qiov->iov->iov_base; + } + acb->nb_sectors = nb_sectors; + acb->n = 0; + acb->cluster_offset = 0; + return acb; +} + static void qcow_aio_read_cb(void *opaque, int ret) { QCowAIOCB *acb = opaque; @@ -635,19 +638,9 @@ static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs, { QCowAIOCB *acb; - acb = qemu_aio_get(bs, cb, opaque); + acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); if (!acb) return NULL; - acb->hd_aiocb = NULL; - acb->sector_num = sector_num; - acb->qiov = qiov; - if (qiov->niov > 1) - acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size); - else - acb->buf = (uint8_t *)qiov->iov->iov_base; - acb->nb_sectors = nb_sectors; - acb->n = 0; - acb->cluster_offset = 0; qcow_aio_read_cb(acb, 0); return &acb->common; @@ -730,33 +723,15 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs, s->cluster_cache_offset = -1; /* disable compressed cache */ - acb = qemu_aio_get(bs, cb, opaque); + acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); if (!acb) return NULL; - acb->hd_aiocb = NULL; - acb->sector_num = sector_num; - acb->qiov = qiov; - if (qiov->niov > 1) { - acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size); - qemu_iovec_to_buffer(qiov, acb->buf); - } else { - acb->buf = (uint8_t *)qiov->iov->iov_base; - } - acb->nb_sectors = nb_sectors; - acb->n = 0; + qcow_aio_write_cb(acb, 0); return &acb->common; } -static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) -{ - QCowAIOCB *acb = (QCowAIOCB *)blockacb; - if (acb->hd_aiocb) - bdrv_aio_cancel(acb->hd_aiocb); - qemu_aio_release(acb); -} - static void qcow_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; @@ -904,7 +879,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, if (ret != Z_STREAM_END || out_len >= s->cluster_size) { /* could not compress: write normal cluster */ - qcow_write(bs, sector_num, buf, s->cluster_sectors); + bdrv_write(bs, sector_num, buf, s->cluster_sectors); } else { cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, out_len, 0, 0); @@ -953,8 +928,6 @@ static BlockDriver bdrv_qcow = { .bdrv_make_empty = qcow_make_empty, .bdrv_aio_readv = qcow_aio_readv, .bdrv_aio_writev = qcow_aio_writev, - .bdrv_aio_cancel = qcow_aio_cancel, - .aiocb_size = sizeof(QCowAIOCB), .bdrv_write_compressed = qcow_write_compressed, .bdrv_get_info = qcow_get_info, diff --git a/block/qcow2.c b/block/qcow2.c index 77f433e81..d1611d14c 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -173,7 +173,7 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index); static int update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, int addend); -static void update_refcount(BlockDriverState *bs, +static int update_refcount(BlockDriverState *bs, int64_t offset, int64_t length, int addend); static int64_t alloc_clusters(BlockDriverState *bs, int64_t size); @@ -1229,46 +1229,6 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num, return 0; } -static int qcow_write(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) -{ - BDRVQcowState *s = bs->opaque; - int ret, index_in_cluster, n; - uint64_t cluster_offset; - int n_end; - QCowL2Meta l2meta; - - while (nb_sectors > 0) { - index_in_cluster = sector_num & (s->cluster_sectors - 1); - n_end = index_in_cluster + nb_sectors; - if (s->crypt_method && - n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) - n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors; - cluster_offset = alloc_cluster_offset(bs, sector_num << 9, - index_in_cluster, - n_end, &n, &l2meta); - if (!cluster_offset) - return -1; - if (s->crypt_method) { - encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, - &s->aes_encrypt_key); - ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, - s->cluster_data, n * 512); - } else { - ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); - } - if (ret != n * 512 || alloc_cluster_link_l2(bs, cluster_offset, &l2meta) < 0) { - free_any_clusters(bs, cluster_offset, l2meta.nb_clusters); - return -1; - } - nb_sectors -= n; - sector_num += n; - buf += n * 512; - } - s->cluster_cache_offset = -1; /* disable compressed cache */ - return 0; -} - typedef struct QCowAIOCB { BlockDriverAIOCB common; int64_t sector_num; @@ -1286,6 +1246,19 @@ typedef struct QCowAIOCB { QCowL2Meta l2meta; } QCowAIOCB; +static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) +{ + QCowAIOCB *acb = (QCowAIOCB *)blockacb; + if (acb->hd_aiocb) + bdrv_aio_cancel(acb->hd_aiocb); + qemu_aio_release(acb); +} + +static AIOPool qcow_aio_pool = { + .aiocb_size = sizeof(QCowAIOCB), + .cancel = qcow_aio_cancel, +}; + static void qcow_aio_read_cb(void *opaque, int ret); static void qcow_aio_read_bh(void *opaque) { @@ -1415,7 +1388,7 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs, { QCowAIOCB *acb; - acb = qemu_aio_get(bs, cb, opaque); + acb = qemu_aio_get(&qcow_aio_pool, bs, cb, opaque); if (!acb) return NULL; acb->hd_aiocb = NULL; @@ -1538,14 +1511,6 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs, return &acb->common; } -static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) -{ - QCowAIOCB *acb = (QCowAIOCB *)blockacb; - if (acb->hd_aiocb) - bdrv_aio_cancel(acb->hd_aiocb); - qemu_aio_release(acb); -} - static void qcow_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; @@ -1834,7 +1799,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, if (ret != Z_STREAM_END || out_len >= s->cluster_size) { /* could not compress: write normal cluster */ - qcow_write(bs, sector_num, buf, s->cluster_sectors); + bdrv_write(bs, sector_num, buf, s->cluster_sectors); } else { cluster_offset = alloc_compressed_cluster_offset(bs, sector_num << 9, out_len); @@ -2548,29 +2513,25 @@ static int grow_refcount_table(BlockDriverState *bs, int min_size) return -EIO; } -/* addend must be 1 or -1 */ -/* XXX: cache several refcount block clusters ? */ -static int update_cluster_refcount(BlockDriverState *bs, - int64_t cluster_index, - int addend) + +static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) { BDRVQcowState *s = bs->opaque; int64_t offset, refcount_block_offset; - int ret, refcount_table_index, block_index, refcount; + int ret, refcount_table_index; uint64_t data64; + /* Find L1 index and grow refcount table if needed */ refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); if (refcount_table_index >= s->refcount_table_size) { - if (addend < 0) - return -EINVAL; ret = grow_refcount_table(bs, refcount_table_index + 1); if (ret < 0) return ret; } + + /* Load or allocate the refcount block */ refcount_block_offset = s->refcount_table[refcount_table_index]; if (!refcount_block_offset) { - if (addend < 0) - return -EINVAL; /* create a new refcount block */ /* Note: we cannot update the refcount now to avoid recursion */ offset = alloc_clusters_noref(bs, s->cluster_size); @@ -2595,43 +2556,105 @@ static int update_cluster_refcount(BlockDriverState *bs, return -EIO; } } - /* we can update the count and save it */ - block_index = cluster_index & - ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); - refcount = be16_to_cpu(s->refcount_block_cache[block_index]); - refcount += addend; - if (refcount < 0 || refcount > 0xffff) - return -EINVAL; - if (refcount == 0 && cluster_index < s->free_cluster_index) { - s->free_cluster_index = cluster_index; + + return refcount_block_offset; +} + +/* addend must be 1 or -1 */ +static int update_cluster_refcount(BlockDriverState *bs, + int64_t cluster_index, + int addend) +{ + BDRVQcowState *s = bs->opaque; + int ret; + + ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend); + if (ret < 0) { + return ret; } - s->refcount_block_cache[block_index] = cpu_to_be16(refcount); - if (bdrv_pwrite(s->hd, - refcount_block_offset + (block_index << REFCOUNT_SHIFT), - &s->refcount_block_cache[block_index], 2) != 2) - return -EIO; - return refcount; + + return get_refcount(bs, cluster_index); } -static void update_refcount(BlockDriverState *bs, +/* XXX: cache several refcount block clusters ? */ +static int update_refcount(BlockDriverState *bs, int64_t offset, int64_t length, int addend) { BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; + int64_t refcount_block_offset = 0; + int64_t table_index = -1, old_table_index; + int first_index = -1, last_index = -1; #ifdef DEBUG_ALLOC2 printf("update_refcount: offset=%lld size=%lld addend=%d\n", offset, length, addend); #endif if (length <= 0) - return; + return -EINVAL; start = offset & ~(s->cluster_size - 1); last = (offset + length - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last; - cluster_offset += s->cluster_size) { - update_cluster_refcount(bs, cluster_offset >> s->cluster_bits, addend); + cluster_offset += s->cluster_size) + { + int block_index, refcount; + int64_t cluster_index = cluster_offset >> s->cluster_bits; + + /* Only write refcount block to disk when we are done with it */ + old_table_index = table_index; + table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); + if ((old_table_index >= 0) && (table_index != old_table_index)) { + size_t size = (last_index - first_index + 1) << REFCOUNT_SHIFT; + if (bdrv_pwrite(s->hd, + refcount_block_offset + (first_index << REFCOUNT_SHIFT), + &s->refcount_block_cache[first_index], size) != size) + { + return -EIO; + } + + first_index = -1; + last_index = -1; + } + + /* Load the refcount block and allocate it if needed */ + refcount_block_offset = alloc_refcount_block(bs, cluster_index); + if (refcount_block_offset < 0) { + return refcount_block_offset; + } + + /* we can update the count and save it */ + block_index = cluster_index & + ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); + if (first_index == -1 || block_index < first_index) { + first_index = block_index; + } + if (block_index > last_index) { + last_index = block_index; + } + + refcount = be16_to_cpu(s->refcount_block_cache[block_index]); + refcount += addend; + if (refcount < 0 || refcount > 0xffff) + return -EINVAL; + if (refcount == 0 && cluster_index < s->free_cluster_index) { + s->free_cluster_index = cluster_index; + } + s->refcount_block_cache[block_index] = cpu_to_be16(refcount); } + + /* Write last changed block to disk */ + if (refcount_block_offset != 0) { + size_t size = (last_index - first_index + 1) << REFCOUNT_SHIFT; + if (bdrv_pwrite(s->hd, + refcount_block_offset + (first_index << REFCOUNT_SHIFT), + &s->refcount_block_cache[first_index], size) != size) + { + return -EIO; + } + } + + return 0; } /* @@ -2980,8 +3003,6 @@ static BlockDriver bdrv_qcow2 = { .bdrv_aio_readv = qcow_aio_readv, .bdrv_aio_writev = qcow_aio_writev, - .bdrv_aio_cancel = qcow_aio_cancel, - .aiocb_size = sizeof(QCowAIOCB), .bdrv_write_compressed = qcow_write_compressed, .bdrv_snapshot_create = qcow_snapshot_create, diff --git a/block/raw-posix.c b/block/raw-posix.c index 04b709f93..fdbf937dc 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -251,7 +251,7 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset, label__raw_read__success: - return ret; + return (ret < 0) ? -errno : ret; } /* @@ -597,6 +597,45 @@ static int posix_aio_init(void) return 0; } +static void raw_aio_remove(RawAIOCB *acb) +{ + RawAIOCB **pacb; + + /* remove the callback from the queue */ + pacb = &posix_aio_state->first_aio; + for(;;) { + if (*pacb == NULL) { + fprintf(stderr, "raw_aio_remove: aio request not found!\n"); + break; + } else if (*pacb == acb) { + *pacb = acb->next; + qemu_aio_release(acb); + break; + } + pacb = &(*pacb)->next; + } +} + +static void raw_aio_cancel(BlockDriverAIOCB *blockacb) +{ + int ret; + RawAIOCB *acb = (RawAIOCB *)blockacb; + + ret = qemu_paio_cancel(acb->aiocb.aio_fildes, &acb->aiocb); + if (ret == QEMU_PAIO_NOTCANCELED) { + /* fail safe: if the aio could not be canceled, we wait for + it */ + while (qemu_paio_error(&acb->aiocb) == EINPROGRESS); + } + + raw_aio_remove(acb); +} + +static AIOPool raw_aio_pool = { + .aiocb_size = sizeof(RawAIOCB), + .cancel = raw_aio_cancel, +}; + static RawAIOCB *raw_aio_setup(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) @@ -607,7 +646,7 @@ static RawAIOCB *raw_aio_setup(BlockDriverState *bs, int64_t sector_num, if (fd_open(bs) < 0) return NULL; - acb = qemu_aio_get(bs, cb, opaque); + acb = qemu_aio_get(&raw_aio_pool, bs, cb, opaque); if (!acb) return NULL; acb->aiocb.aio_fildes = s->fd; @@ -631,25 +670,6 @@ static RawAIOCB *raw_aio_setup(BlockDriverState *bs, int64_t sector_num, return acb; } -static void raw_aio_remove(RawAIOCB *acb) -{ - RawAIOCB **pacb; - - /* remove the callback from the queue */ - pacb = &posix_aio_state->first_aio; - for(;;) { - if (*pacb == NULL) { - fprintf(stderr, "raw_aio_remove: aio request not found!\n"); - break; - } else if (*pacb == acb) { - *pacb = acb->next; - qemu_aio_release(acb); - break; - } - pacb = &(*pacb)->next; - } -} - static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) @@ -681,21 +701,6 @@ static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs, } return &acb->common; } - -static void raw_aio_cancel(BlockDriverAIOCB *blockacb) -{ - int ret; - RawAIOCB *acb = (RawAIOCB *)blockacb; - - ret = qemu_paio_cancel(acb->aiocb.aio_fildes, &acb->aiocb); - if (ret == QEMU_PAIO_NOTCANCELED) { - /* fail safe: if the aio could not be canceled, we wait for - it */ - while (qemu_paio_error(&acb->aiocb) == EINPROGRESS); - } - - raw_aio_remove(acb); -} #else /* CONFIG_AIO */ static int posix_aio_init(void) { @@ -869,8 +874,6 @@ static BlockDriver bdrv_raw = { #ifdef CONFIG_AIO .bdrv_aio_readv = raw_aio_readv, .bdrv_aio_writev = raw_aio_writev, - .bdrv_aio_cancel = raw_aio_cancel, - .aiocb_size = sizeof(RawAIOCB), #endif .bdrv_truncate = raw_truncate, @@ -1203,7 +1206,7 @@ static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs, if (fd_open(bs) < 0) return NULL; - acb = qemu_aio_get(bs, cb, opaque); + acb = qemu_aio_get(&raw_aio_pool, bs, cb, opaque); if (!acb) return NULL; acb->aiocb.aio_fildes = s->fd; @@ -1374,7 +1377,6 @@ static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs, } #endif /* !linux && !FreeBSD */ -#if defined(__linux__) || defined(__FreeBSD__) static int hdev_create(const char *filename, QEMUOptionParameter *options) { int fd; @@ -1396,7 +1398,7 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options) if (fstat(fd, &stat_buf) < 0) ret = -EIO; - else if (!S_ISBLK(stat_buf.st_mode)) + else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) ret = -EIO; else if (lseek(fd, 0, SEEK_END) < total_size * 512) ret = -ENOSPC; @@ -1405,14 +1407,6 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options) return ret; } -#else /* !(linux || freebsd) */ - -static int hdev_create(const char *filename, QEMUOptionParameter *options) -{ - return -ENOTSUP; -} -#endif - static BlockDriver bdrv_host_device = { .format_name = "host_device", .instance_size = sizeof(BDRVRawState), @@ -1424,8 +1418,6 @@ static BlockDriver bdrv_host_device = { #ifdef CONFIG_AIO .bdrv_aio_readv = raw_aio_readv, .bdrv_aio_writev = raw_aio_writev, - .bdrv_aio_cancel = raw_aio_cancel, - .aiocb_size = sizeof(RawAIOCB), #endif .bdrv_read = raw_read, diff --git a/block/vvfat.c b/block/vvfat.c index 13960e95a..1e37b9f9f 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2762,14 +2762,15 @@ static void write_target_close(BlockDriverState *bs) { } static BlockDriver vvfat_write_target = { - "vvfat_write_target", 0, NULL, NULL, NULL, - write_target_commit, - write_target_close, - NULL, NULL, NULL + .format_name = "vvfat_write_target", + .bdrv_write = write_target_commit, + .bdrv_close = write_target_close, }; static int enable_write_target(BDRVVVFATState *s) { + BlockDriver *bdrv_qcow; + QEMUOptionParameter *options; int size = sector2cluster(s, s->sector_count); s->used_clusters = calloc(size, 1); @@ -2777,8 +2778,13 @@ static int enable_write_target(BDRVVVFATState *s) s->qcow_filename = qemu_malloc(1024); get_tmp_filename(s->qcow_filename, 1024); - if (bdrv_create2(bdrv_find_format("qcow"), - s->qcow_filename, s->sector_count, "fat:", NULL, 0) < 0) + + bdrv_qcow = bdrv_find_format("qcow"); + options = parse_option_parameters("", bdrv_qcow->create_options, NULL); + set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512); + set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:"); + + if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0) return -1; s->qcow = bdrv_new(""); if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0) diff --git a/block_int.h b/block_int.h index 0820ed1d5..8d0da7cfd 100644 --- a/block_int.h +++ b/block_int.h @@ -67,8 +67,6 @@ struct BlockDriver { BlockDriverAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); - void (*bdrv_aio_cancel)(BlockDriverAIOCB *acb); - int aiocb_size; const char *protocol_name; int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); @@ -102,8 +100,6 @@ struct BlockDriver { unsigned long int req, void *buf, BlockDriverCompletionFunc *cb, void *opaque); - AIOPool aio_pool; - /* List of options for creating images, terminated by name == NULL */ QEMUOptionParameter *create_options; @@ -173,13 +169,8 @@ struct BlockDriverAIOCB { void get_tmp_filename(char *filename, int size); -void aio_pool_init(AIOPool *pool, int aiocb_size, - void (*cancel)(BlockDriverAIOCB *acb)); - -void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb, - void *opaque); -void *qemu_aio_get_pool(AIOPool *pool, BlockDriverState *bs, - BlockDriverCompletionFunc *cb, void *opaque); +void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque); void qemu_aio_release(void *p); void *qemu_blockalign(BlockDriverState *bs, size_t size); @@ -127,6 +127,9 @@ case "$cpu" in m68k) cpu="m68k" ;; + microblaze) + cpu="microblaze" + ;; mips) cpu="mips" ;; @@ -718,6 +721,7 @@ x86_64-softmmu \ arm-softmmu \ cris-softmmu \ m68k-softmmu \ +microblaze-softmmu \ mips-softmmu \ mipsel-softmmu \ mips64-softmmu \ @@ -740,6 +744,7 @@ arm-linux-user \ armeb-linux-user \ cris-linux-user \ m68k-linux-user \ +microblaze-linux-user \ mips-linux-user \ mipsel-linux-user \ ppc-linux-user \ @@ -1244,18 +1249,22 @@ fi ########################################## # pthread probe +PTHREADLIBS_LIST="-lpthread -lpthreadGC2" PTHREADLIBS="" if test "$pthread" = yes; then pthread=no cat > $TMPC << EOF #include <pthread.h> -int main(void) { pthread_mutex_t lock; return 0; } +int main(void) { pthread_create(0,0,0,0); return 0; } EOF - if $cc $ARCH_CFLAGS -o $TMPE $PTHREADLIBS $TMPC 2> /dev/null > /dev/null ; then - pthread=yes - PTHREADLIBS="-lpthread" - fi + for pthread_lib in $PTHREADLIBS_LIST; do + if $cc $ARCH_CFLAGS -o $TMPE $TMPC $pthread_lib 2> /dev/null > /dev/null ; then + pthread=yes + PTHREADLIBS="$pthread_lib" + break + fi + done fi if test "$pthread" = no; then @@ -1580,6 +1589,10 @@ case "$cpu" in echo "ARCH=m68k" >> $config_mak echo "#define HOST_M68K 1" >> $config_h ;; + microblaze) + echo "ARCH=microblaze" >> $config_mak + echo "#define HOST_MICROBLAZE 1" >> $config_h + ;; mips) echo "ARCH=mips" >> $config_mak echo "#define HOST_MIPS 1" >> $config_h @@ -1896,7 +1909,14 @@ if test `expr "$target_list" : ".*softmmu.*"` != 0 ; then fi echo "TOOLS=$tools" >> $config_mak -test -f ${config_h}~ && cmp -s $config_h ${config_h}~ && mv ${config_h}~ $config_h +if test -f ${config_h}~ ; then + if cmp -s $config_h ${config_h}~ ; then + mv ${config_h}~ $config_h + else + rm ${config_h}~ + fi +fi + config_host_mak=${config_mak} for target in $target_list; do @@ -1907,6 +1927,7 @@ target_cpu=`echo $target | cut -d '-' -f 1` target_bigendian="no" [ "$target_cpu" = "armeb" ] && target_bigendian=yes [ "$target_cpu" = "m68k" ] && target_bigendian=yes +[ "$target_cpu" = "microblaze" ] && target_bigendian=yes [ "$target_cpu" = "mips" ] && target_bigendian=yes [ "$target_cpu" = "mipsn32" ] && target_bigendian=yes [ "$target_cpu" = "mips64" ] && target_bigendian=yes @@ -2103,7 +2124,15 @@ case "$target_cpu" in gdb_xml_files="cf-core.xml cf-fp.xml" target_phys_bits=32 ;; - mips|mipsel) + microblaze) + echo "TARGET_ARCH=microblaze" >> $config_mak + echo "#define TARGET_ARCH \"microblaze\"" >> $config_h + echo "#define TARGET_MICROBLAZE 1" >> $config_h + bflt="yes" + target_nptl="yes" + target_phys_bits=32 + ;; + mips|mipsel) echo "TARGET_ARCH=mips" >> $config_mak echo "#define TARGET_ARCH \"mips\"" >> $config_h echo "#define TARGET_MIPS 1" >> $config_h @@ -2240,6 +2269,7 @@ echo "TARGET_XML_FILES=$list" >> $config_mak if test "$target_cpu" = "arm" \ -o "$target_cpu" = "armeb" \ -o "$target_cpu" = "m68k" \ + -o "$target_cpu" = "microblaze" \ -o "$target_cpu" = "mips" \ -o "$target_cpu" = "mipsel" \ -o "$target_cpu" = "mipsn32" \ diff --git a/cpu-exec.c b/cpu-exec.c index 777fa5fef..71102958a 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -250,6 +250,7 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_ALPHA) #elif defined(TARGET_ARM) #elif defined(TARGET_PPC) +#elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) #elif defined(TARGET_CRIS) @@ -305,6 +306,8 @@ int cpu_exec(CPUState *env1) env->old_exception = -1; #elif defined(TARGET_PPC) do_interrupt(env); +#elif defined(TARGET_MICROBLAZE) + do_interrupt(env); #elif defined(TARGET_MIPS) do_interrupt(env); #elif defined(TARGET_SPARC) @@ -374,7 +377,8 @@ int cpu_exec(CPUState *env1) cpu_loop_exit(); } #if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \ - defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) + defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \ + defined(TARGET_MICROBLAZE) if (interrupt_request & CPU_INTERRUPT_HALT) { env->interrupt_request &= ~CPU_INTERRUPT_HALT; env->halted = 1; @@ -443,6 +447,15 @@ int cpu_exec(CPUState *env1) env->interrupt_request &= ~CPU_INTERRUPT_HARD; next_tb = 0; } +#elif defined(TARGET_MICROBLAZE) + if ((interrupt_request & CPU_INTERRUPT_HARD) + && (env->sregs[SR_MSR] & MSR_IE) + && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)) + && !(env->iflags & (D_FLAG | IMM_FLAG))) { + env->exception_index = EXCP_IRQ; + do_interrupt(env); + next_tb = 0; + } #elif defined(TARGET_MIPS) if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) && @@ -572,6 +585,8 @@ int cpu_exec(CPUState *env1) env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); log_cpu_state(env, 0); +#elif defined(TARGET_MICROBLAZE) + log_cpu_state(env, 0); #elif defined(TARGET_MIPS) log_cpu_state(env, 0); #elif defined(TARGET_SH4) @@ -689,6 +704,7 @@ int cpu_exec(CPUState *env1) env->cc_op = CC_OP_FLAGS; env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); +#elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) #elif defined(TARGET_IA64) @@ -1036,6 +1052,56 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, return 1; } +#elif defined (TARGET_MICROBLAZE) +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(h2g(address), pc, puc)) { + return 1; + } + + /* see if it is an MMU fault */ + ret = cpu_mb_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + if (ret == 1) { +#if 0 + printf("PF exception: PC=0x" TARGET_FMT_lx " error=0x%x %p\n", + env->PC, env->error_code, tb); +#endif + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + cpu_loop_exit(); + } else { + /* activate soft MMU for this block */ + cpu_resume_from_signal(env, puc); + } + /* never comes here */ + return 1; +} + #elif defined (TARGET_SH4) static inline int handle_cpu_signal(unsigned long pc, unsigned long address, int is_write, sigset_t *old_set, @@ -218,6 +218,7 @@ enum bfd_architecture #define bfd_mach_cris_v0_v10 255 #define bfd_mach_cris_v32 32 #define bfd_mach_cris_v10_v32 1032 + bfd_arch_microblaze, /* Xilinx MicroBlaze. */ bfd_arch_last }; #define bfd_mach_s390_31 31 @@ -400,6 +401,7 @@ extern int print_insn_tic30 PARAMS ((bfd_vma, disassemble_info*)); extern int print_insn_ppc PARAMS ((bfd_vma, disassemble_info*)); extern int print_insn_s390 PARAMS ((bfd_vma, disassemble_info*)); extern int print_insn_crisv32 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_microblaze PARAMS ((bfd_vma, disassemble_info*)); #if 0 /* Fetch the disassembler for a given BFD, if that support is available. */ @@ -195,6 +195,9 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags) #elif defined(TARGET_CRIS) disasm_info.mach = bfd_mach_cris_v32; print_insn = print_insn_crisv32; +#elif defined(TARGET_MICROBLAZE) + disasm_info.mach = bfd_arch_microblaze; + print_insn = print_insn_microblaze; #else fprintf(out, "0x" TARGET_FMT_lx ": Asm output not supported on this arch\n", code); diff --git a/dma-helpers.c b/dma-helpers.c index f9eb2240b..712ed897f 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -10,8 +10,6 @@ #include "dma.h" #include "block_int.h" -static AIOPool dma_aio_pool; - void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint) { qsg->sg = qemu_malloc(alloc_hint * sizeof(ScatterGatherEntry)); @@ -132,12 +130,26 @@ static void dma_bdrv_cb(void *opaque, int ret) } } +static void dma_aio_cancel(BlockDriverAIOCB *acb) +{ + DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common); + + if (dbs->acb) { + bdrv_aio_cancel(dbs->acb); + } +} + +static AIOPool dma_aio_pool = { + .aiocb_size = sizeof(DMAAIOCB), + .cancel = dma_aio_cancel, +}; + static BlockDriverAIOCB *dma_bdrv_io( BlockDriverState *bs, QEMUSGList *sg, uint64_t sector_num, BlockDriverCompletionFunc *cb, void *opaque, int is_write) { - DMAAIOCB *dbs = qemu_aio_get_pool(&dma_aio_pool, bs, cb, opaque); + DMAAIOCB *dbs = qemu_aio_get(&dma_aio_pool, bs, cb, opaque); dbs->acb = NULL; dbs->bs = bs; @@ -170,17 +182,3 @@ BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs, { return dma_bdrv_io(bs, sg, sector, cb, opaque, 1); } - -static void dma_aio_cancel(BlockDriverAIOCB *acb) -{ - DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common); - - if (dbs->acb) { - bdrv_aio_cancel(dbs->acb); - } -} - -void dma_helper_init(void) -{ - aio_pool_init(&dma_aio_pool, sizeof(DMAAIOCB), dma_aio_cancel); -} @@ -38,6 +38,4 @@ BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs, BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs, QEMUSGList *sg, uint64_t sector, BlockDriverCompletionFunc *cb, void *opaque); -void dma_helper_init(void); - #endif @@ -119,6 +119,8 @@ typedef int64_t Elf64_Sxword; */ #define EM_S390_OLD 0xA390 +#define EM_XILINX_MICROBLAZE 0xBAAB + /* This is the info that is needed to parse the dynamic section of the file */ #define DT_NULL 0 #define DT_NEEDED 1 @@ -1157,6 +1157,36 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n) return 4; } +#elif defined (TARGET_MICROBLAZE) + +#define NUM_CORE_REGS (32 + 5) + +static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + GET_REG32(env->regs[n]); + } else { + GET_REG32(env->sregs[n - 32]); + } + return 0; +} + +static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n) +{ + uint32_t tmp; + + if (n > NUM_CORE_REGS) + return 0; + + tmp = ldl_p(mem_buf); + + if (n < 32) { + env->regs[n] = tmp; + } else { + env->sregs[n - 32] = tmp; + } + return 4; +} #elif defined (TARGET_CRIS) #define NUM_CORE_REGS 49 @@ -1529,6 +1559,8 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) s->c_cpu->pc = pc; #elif defined (TARGET_MIPS) s->c_cpu->active_tc.PC = pc; +#elif defined (TARGET_MICROBLAZE) + s->c_cpu->sregs[SR_PC] = pc; #elif defined (TARGET_CRIS) s->c_cpu->pc = pc; #elif defined (TARGET_ALPHA) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 5dc944836..817234da9 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -721,7 +721,7 @@ static void gic_init(gic_state *s) { int i; - qdev_init_irq_sink(&s->busdev.qdev, gic_set_irq, GIC_NIRQ - 32); + qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, GIC_NIRQ - 32); for (i = 0; i < NCPU; i++) { sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } diff --git a/hw/armv7m.c b/hw/armv7m.c index 6be9940bc..e3d00ffd7 100644 --- a/hw/armv7m.c +++ b/hw/armv7m.c @@ -186,7 +186,7 @@ qemu_irq *armv7m_init(int flash_size, int sram_size, cpu_pic = arm_pic_init_cpu(env); sysbus_connect_irq(sysbus_from_qdev(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]); for (i = 0; i < 64; i++) { - pic[i] = qdev_get_irq_sink(nvic, i); + pic[i] = qdev_get_gpio_in(nvic, i); } image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL); diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c index 60b719b2f..fc527cbec 100644 --- a/hw/axis_dev88.c +++ b/hw/axis_dev88.c @@ -304,10 +304,10 @@ void axisdev88_init (ram_addr_t ram_size, sysbus_connect_irq(s, 0, cpu_irq[0]); sysbus_connect_irq(s, 1, cpu_irq[1]); for (i = 0; i < 30; i++) { - irq[i] = qdev_get_irq_sink(dev, i); + irq[i] = qdev_get_gpio_in(dev, i); } - nmi[0] = qdev_get_irq_sink(dev, 30); - nmi[1] = qdev_get_irq_sink(dev, 31); + nmi[0] = qdev_get_gpio_in(dev, 30); + nmi[1] = qdev_get_gpio_in(dev, 31); etraxfs_dmac = etraxfs_dmac_init(env, 0x30000000, 10); for (i = 0; i < 10; i++) { diff --git a/hw/e1000.c b/hw/e1000.c index c5eb54699..26657922c 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -190,6 +190,13 @@ rxbufsize(uint32_t v) } static void +set_ctrl(E1000State *s, int index, uint32_t val) +{ + /* RST is self clearing */ + s->mac_reg[CTRL] = val & ~E1000_CTRL_RST; +} + +static void set_rx_control(E1000State *s, int index, uint32_t val) { s->mac_reg[RCTL] = val; @@ -783,12 +790,12 @@ enum { NREADOPS = ARRAY_SIZE(macreg_readops) }; static void (*macreg_writeops[])(E1000State *, int, uint32_t) = { putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC), putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH), - putreg(RDBAL), putreg(LEDCTL), putreg(CTRL), putreg(VET), + putreg(RDBAL), putreg(LEDCTL), putreg(VET), [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl, [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics, [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt, [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr, - [EECD] = set_eecd, [RCTL] = set_rx_control, + [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl, [RA ... RA+31] = &mac_writereg, [MTA ... MTA+127] = &mac_writereg, [VFTA ... VFTA+127] = &mac_writereg, @@ -684,7 +684,7 @@ static void esp_init1(SysBusDevice *dev) register_savevm("esp", -1, 3, esp_save, esp_load, s); qemu_register_reset(esp_reset, 0, s); - qdev_init_irq_sink(&dev->qdev, parent_esp_reset, 1); + qdev_init_gpio_in(&dev->qdev, parent_esp_reset, 1); scsi_bus_new(&dev->qdev, esp_scsi_attach); } diff --git a/hw/etraxfs.c b/hw/etraxfs.c index f82e1cd0f..1e200553e 100644 --- a/hw/etraxfs.c +++ b/hw/etraxfs.c @@ -90,10 +90,10 @@ void bareetraxfs_init (ram_addr_t ram_size, /* FIXME: Is there a proper way to signal vectors to the CPU core? */ qdev_set_prop_ptr(dev, "interrupt_vector", &env->interrupt_vector); for (i = 0; i < 30; i++) { - irq[i] = qdev_get_irq_sink(dev, i); + irq[i] = qdev_get_gpio_in(dev, i); } - nmi[0] = qdev_get_irq_sink(dev, 30); - nmi[1] = qdev_get_irq_sink(dev, 31); + nmi[0] = qdev_get_gpio_in(dev, 30); + nmi[1] = qdev_get_gpio_in(dev, 31); etraxfs_dmac = etraxfs_dmac_init(env, 0x30000000, 10); for (i = 0; i < 10; i++) { diff --git a/hw/etraxfs_pic.c b/hw/etraxfs_pic.c index da9d0c3be..585e7850f 100644 --- a/hw/etraxfs_pic.c +++ b/hw/etraxfs_pic.c @@ -141,7 +141,7 @@ static void etraxfs_pic_init(SysBusDevice *dev) int intr_vect_regs; s->interrupt_vector = qdev_get_prop_ptr(&dev->qdev, "interrupt_vector"); - qdev_init_irq_sink(&dev->qdev, irq_handler, 32); + qdev_init_gpio_in(&dev->qdev, irq_handler, 32); sysbus_init_irq(dev, &s->parent_irq); sysbus_init_irq(dev, &s->parent_nmi); @@ -51,6 +51,7 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode); QEMUFile *qemu_fopen_socket(int fd); QEMUFile *qemu_popen(FILE *popen_file, const char *mode); QEMUFile *qemu_popen_cmd(const char *command, const char *mode); +int qemu_popen_fd(QEMUFile *f); void qemu_fflush(QEMUFile *f); int qemu_fclose(QEMUFile *f); void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); diff --git a/hw/integratorcp.c b/hw/integratorcp.c index 493866c92..b6fbe1523 100644 --- a/hw/integratorcp.c +++ b/hw/integratorcp.c @@ -378,7 +378,7 @@ static void icp_pic_init(SysBusDevice *dev) icp_pic_state *s = FROM_SYSBUS(icp_pic_state, dev); int iomemtype; - qdev_init_irq_sink(&dev->qdev, icp_pic_set_irq, 32); + qdev_init_gpio_in(&dev->qdev, icp_pic_set_irq, 32); sysbus_init_irq(dev, &s->parent_irq); sysbus_init_irq(dev, &s->parent_fiq); iomemtype = cpu_register_io_memory(0, icp_pic_readfn, @@ -484,7 +484,7 @@ static void integratorcp_init(ram_addr_t ram_size, cpu_pic[ARM_PIC_CPU_IRQ], cpu_pic[ARM_PIC_CPU_FIQ], NULL); for (i = 0; i < 32; i++) { - pic[i] = qdev_get_irq_sink(dev, i); + pic[i] = qdev_get_gpio_in(dev, i); } sysbus_create_simple("integrator_pic", 0xca000000, pic[26]); sysbus_create_varargs("integrator_pit", 0x13000000, diff --git a/hw/microblaze_pic_cpu.c b/hw/microblaze_pic_cpu.c new file mode 100644 index 000000000..7c59382fb --- /dev/null +++ b/hw/microblaze_pic_cpu.c @@ -0,0 +1,50 @@ +/* + * QEMU MicroBlaze CPU interrupt wrapper logic. + * + * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "pc.h" + +#define D(x) + +void pic_info(Monitor *mon) +{} +void irq_info(Monitor *mon) +{} + +static void microblaze_pic_cpu_handler(void *opaque, int irq, int level) +{ + CPUState *env = (CPUState *)opaque; + int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD; + + if (level) + cpu_interrupt(env, type); + else + cpu_reset_interrupt(env, type); +} + +qemu_irq *microblaze_pic_init_cpu(CPUState *env); +qemu_irq *microblaze_pic_init_cpu(CPUState *env) +{ + return qemu_allocate_irqs(microblaze_pic_cpu_handler, env, 2); +} diff --git a/hw/mpcore.c b/hw/mpcore.c index 6df9a91ad..a5eddd9cc 100644 --- a/hw/mpcore.c +++ b/hw/mpcore.c @@ -320,17 +320,17 @@ static void realview_mpcore_init(SysBusDevice *dev) priv = sysbus_create_simple("arm11mpcore_priv", MPCORE_PRIV_BASE, NULL); sysbus_pass_irq(dev, sysbus_from_qdev(priv)); for (i = 0; i < 32; i++) { - s->cpuic[i] = qdev_get_irq_sink(priv, i); + s->cpuic[i] = qdev_get_gpio_in(priv, i); } /* ??? IRQ routing is hardcoded to "normal" mode. */ for (n = 0; n < 4; n++) { gic = sysbus_create_simple("realview_gic", 0x10040000 + n * 0x10000, s->cpuic[10 + n]); for (i = 0; i < 64; i++) { - s->rvic[n][i] = qdev_get_irq_sink(gic, i); + s->rvic[n][i] = qdev_get_gpio_in(gic, i); } } - qdev_init_irq_sink(&dev->qdev, mpcore_rirq_set_irq, 64); + qdev_init_gpio_in(&dev->qdev, mpcore_rirq_set_irq, 64); } static void mpcore_register_devices(void) diff --git a/hw/musicpal.c b/hw/musicpal.c index 1e0aff5cb..9389af958 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -1040,7 +1040,7 @@ static void mv88w8618_pic_init(SysBusDevice *dev) mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state, dev); int iomemtype; - qdev_init_irq_sink(&dev->qdev, mv88w8618_pic_set_irq, 32); + qdev_init_gpio_in(&dev->qdev, mv88w8618_pic_set_irq, 32); sysbus_init_irq(dev, &s->parent_irq); iomemtype = cpu_register_io_memory(0, mv88w8618_pic_readfn, mv88w8618_pic_writefn, s); @@ -1534,7 +1534,7 @@ static void musicpal_init(ram_addr_t ram_size, dev = sysbus_create_simple("mv88w8618_pic", MP_PIC_BASE, cpu_pic[ARM_PIC_CPU_IRQ]); for (i = 0; i < 32; i++) { - pic[i] = qdev_get_irq_sink(dev, i); + pic[i] = qdev_get_gpio_in(dev, i); } sysbus_create_varargs("mv88w8618_pit", MP_PIT_BASE, pic[MP_TIMER1_IRQ], pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], diff --git a/hw/pcnet.c b/hw/pcnet.c index 2c22f4a50..c44ba7edf 100644 --- a/hw/pcnet.c +++ b/hw/pcnet.c @@ -2127,7 +2127,7 @@ static void lance_init(SysBusDevice *dev) s->dma_opaque = qdev_get_prop_ptr(&dev->qdev, "dma"); - qdev_init_irq_sink(&dev->qdev, parent_lance_reset, 1); + qdev_init_gpio_in(&dev->qdev, parent_lance_reset, 1); sysbus_init_mmio(dev, 4, s->mmio_index); diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c new file mode 100644 index 000000000..80097df81 --- /dev/null +++ b/hw/petalogix_s3adsp1800_mmu.c @@ -0,0 +1,199 @@ +/* + * Model of Petalogix linux reference design targeting Xilinx Spartan 3ADSP-1800 + * boards. + * + * Copyright (c) 2009 Edgar E. Iglesias. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "hw.h" +#include "net.h" +#include "flash.h" +#include "sysemu.h" +#include "devices.h" +#include "boards.h" +#include "device_tree.h" +#include "xilinx.h" + +#define LMB_BRAM_SIZE (128 * 1024) +#define FLASH_SIZE (16 * 1024 * 1024) + +static uint32_t bootstrap_pc; + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + cpu_reset(env); + env->sregs[SR_PC] = bootstrap_pc; +} + +#define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb" +static int petalogix_load_device_tree(target_phys_addr_t addr, + uint32_t ramsize, + target_phys_addr_t initrd_base, + target_phys_addr_t initrd_size, + const char *kernel_cmdline) +{ +#ifdef HAVE_FDT + void *fdt; + char *path = NULL; + int pathlen; + int r; +#endif + int fdt_size; + +#ifdef HAVE_FDT + /* Try the local "mb.dtb" override. */ + fdt = load_device_tree("mb.dtb", &fdt_size); + if (!fdt) { + pathlen = snprintf(NULL, 0, "%s/%s", + bios_dir, BINARY_DEVICE_TREE_FILE) + 1; + path = qemu_malloc(pathlen); + snprintf(path, pathlen, "%s/%s", bios_dir, BINARY_DEVICE_TREE_FILE); + fdt = load_device_tree(BINARY_DEVICE_TREE_FILE, &fdt_size); + free(path); + if (!fdt) + return 0; + } + + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); + if (r < 0) + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + cpu_physical_memory_write (addr, (void *)fdt, fdt_size); +#else + /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob + to the kernel. */ + fdt_size = load_image_targphys("mb.dtb", addr, 0x10000); + if (fdt_size < 0) { + fdt_size = load_image_targphys(BINARY_DEVICE_TREE_FILE, addr, 0x10000); + } + + if (kernel_cmdline) { + fprintf(stderr, + "Warning: missing libfdt, cannot pass cmdline to kernel!\n"); + } +#endif + return fdt_size; +} + +static void +petalogix_s3adsp1800_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + DeviceState *dev; + CPUState *env; + int kernel_size; + int i; + target_phys_addr_t ddr_base = 0x90000000; + ram_addr_t phys_lmb_bram; + ram_addr_t phys_ram; + ram_addr_t phys_flash; + qemu_irq irq[32], *cpu_irq; + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = "microblaze"; + } + env = cpu_init(cpu_model); + + env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */ + qemu_register_reset(main_cpu_reset, 0, env); + + /* Attach emulated BRAM through the LMB. */ + phys_lmb_bram = qemu_ram_alloc(LMB_BRAM_SIZE); + cpu_register_physical_memory(0x00000000, LMB_BRAM_SIZE, + phys_lmb_bram | IO_MEM_RAM); + + phys_ram = qemu_ram_alloc(ram_size); + cpu_register_physical_memory(ddr_base, ram_size, phys_ram | IO_MEM_RAM); + + phys_flash = qemu_ram_alloc(FLASH_SIZE); + i = drive_get_index(IF_PFLASH, 0, 0); + pflash_cfi02_register(0xa0000000, phys_flash, + i != -1 ? drives_table[i].bdrv : NULL, (64 * 1024), + FLASH_SIZE >> 16, + 1, 1, 0x0000, 0x0000, 0x0000, 0x0000, + 0x555, 0x2aa); + + cpu_irq = microblaze_pic_init_cpu(env); + dev = xilinx_intc_create(0x81800000, cpu_irq[0], 2); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(dev, i); + } + + sysbus_create_simple("xilinx,uartlite", 0x84000000, irq[3]); + /* 2 timers at irq 2 @ 62 Mhz. */ + xilinx_timer_create(0x83c00000, irq[0], 2, 62 * 1000000); + xilinx_ethlite_create(&nd_table[0], 0x81000000, irq[1], 0, 0); + + if (kernel_filename) { + uint64_t entry, low, high; + int kcmdline_len; + uint32_t base32; + + /* Boots a kernel elf binary. */ + kernel_size = load_elf(kernel_filename, 0, + &entry, &low, &high); + base32 = entry; + if (base32 == 0xc0000000) { + kernel_size = load_elf(kernel_filename, -0x30000000LL, + &entry, NULL, NULL); + } + /* Always boot into physical ram. */ + bootstrap_pc = ddr_base + (entry & 0x0fffffff); + if (kernel_size < 0) { + /* If we failed loading ELF's try a raw image. */ + kernel_size = load_image_targphys(kernel_filename, ddr_base, + ram_size); + bootstrap_pc = ddr_base; + } + + env->regs[5] = ddr_base + kernel_size; + if (kernel_cmdline && (kcmdline_len = strlen(kernel_cmdline))) { + pstrcpy_targphys(env->regs[5], 256, kernel_cmdline); + } + env->regs[6] = 0; + /* Provide a device-tree. */ + env->regs[7] = ddr_base + kernel_size + 256; + petalogix_load_device_tree(env->regs[7], ram_size, + env->regs[6], 0, + kernel_cmdline); + } + + env->sregs[SR_PC] = bootstrap_pc; +} + +static QEMUMachine petalogix_s3adsp1800_machine = { + .name = "petalogix-s3adsp1800", + .desc = "Petalogix linux refdesign for xilinx Spartan 3ADSP1800", + .init = petalogix_s3adsp1800_init, + .is_default = 1 +}; + +static void petalogix_s3adsp1800_machine_init(void) +{ + qemu_register_machine(&petalogix_s3adsp1800_machine); +} + +machine_init(petalogix_s3adsp1800_machine_init); diff --git a/hw/pl190.c b/hw/pl190.c index b8c2018d3..10857683d 100644 --- a/hw/pl190.c +++ b/hw/pl190.c @@ -235,7 +235,7 @@ static void pl190_init(SysBusDevice *dev) iomemtype = cpu_register_io_memory(0, pl190_readfn, pl190_writefn, s); sysbus_init_mmio(dev, 0x1000, iomemtype); - qdev_init_irq_sink(&dev->qdev, pl190_set_irq, 32); + qdev_init_gpio_in(&dev->qdev, pl190_set_irq, 32); sysbus_init_irq(dev, &s->irq); sysbus_init_irq(dev, &s->fiq); pl190_reset(s); @@ -156,19 +156,6 @@ void qdev_set_netdev(DeviceState *dev, NICInfo *nd) } -qemu_irq qdev_get_irq_sink(DeviceState *dev, int n) -{ - assert(n >= 0 && n < dev->num_irq_sink); - return dev->irq_sink[n]; -} - -/* Register device IRQ sinks. */ -void qdev_init_irq_sink(DeviceState *dev, qemu_irq_handler handler, int nirq) -{ - dev->num_irq_sink = nirq; - dev->irq_sink = qemu_allocate_irqs(handler, dev, nirq); -} - /* Get a character (serial) device interface. */ CharDriverState *qdev_init_chardev(DeviceState *dev) { @@ -17,8 +17,6 @@ struct DeviceState { DeviceType *type; BusState *parent_bus; DeviceProperty *props; - int num_irq_sink; - qemu_irq *irq_sink; int num_gpio_out; qemu_irq *gpio_out; int num_gpio_in; @@ -55,7 +53,6 @@ void qdev_set_prop_int(DeviceState *dev, const char *name, uint64_t value); void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value); void qdev_set_netdev(DeviceState *dev, NICInfo *nd); -qemu_irq qdev_get_irq_sink(DeviceState *dev, int n); qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); @@ -77,7 +74,7 @@ struct DeviceInfo { void qdev_register(const char *name, int size, DeviceInfo *info); /* Register device properties. */ -void qdev_init_irq_sink(DeviceState *dev, qemu_irq_handler handler, int nirq); +/* GPIO inputs also double as IRQ sinks. */ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n); void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n); diff --git a/hw/realview.c b/hw/realview.c index 535f90762..62d8bf503 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -84,7 +84,7 @@ static void realview_init(ram_addr_t ram_size, cpu_irq[3], NULL); } for (n = 0; n < 64; n++) { - pic[n] = qdev_get_irq_sink(dev, n); + pic[n] = qdev_get_gpio_in(dev, n); } sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]); diff --git a/hw/sun4m.c b/hw/sun4m.c index af9f8719a..4ba9e89ed 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -379,7 +379,7 @@ static void lance_init(NICInfo *nd, target_phys_addr_t leaddr, s = sysbus_from_qdev(dev); sysbus_mmio_map(s, 0, leaddr); sysbus_connect_irq(s, 0, irq); - *reset = qdev_get_irq_sink(dev, 0); + *reset = qdev_get_gpio_in(dev, 0); } static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, diff --git a/hw/syborg.c b/hw/syborg.c index ae0ca6702..5ca9977b2 100644 --- a/hw/syborg.c +++ b/hw/syborg.c @@ -58,7 +58,7 @@ static void syborg_init(ram_addr_t ram_size, dev = sysbus_create_simple("syborg,interrupt", 0xC0000000, cpu_pic[ARM_PIC_CPU_IRQ]); for (i = 0; i < 64; i++) { - pic[i] = qdev_get_irq_sink(dev, i); + pic[i] = qdev_get_gpio_in(dev, i); } sysbus_create_simple("syborg,rtc", 0xC0001000, NULL); diff --git a/hw/syborg_interrupt.c b/hw/syborg_interrupt.c index e3fbbf7bf..29e0d1a74 100644 --- a/hw/syborg_interrupt.c +++ b/hw/syborg_interrupt.c @@ -209,7 +209,7 @@ static void syborg_int_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->parent_irq); s->num_irqs = qdev_get_prop_int(&dev->qdev, "num-interrupts", 64); - qdev_init_irq_sink(&dev->qdev, syborg_int_set_irq, s->num_irqs); + qdev_init_gpio_in(&dev->qdev, syborg_int_set_irq, s->num_irqs); iomemtype = cpu_register_io_memory(0, syborg_int_readfn, syborg_int_writefn, s); sysbus_init_mmio(dev, 0x1000, iomemtype); diff --git a/hw/versatilepb.c b/hw/versatilepb.c index eed97d6db..03cf4d8f3 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -135,7 +135,7 @@ static void vpb_sic_init(SysBusDevice *dev) int iomemtype; int i; - qdev_init_irq_sink(&dev->qdev, vpb_sic_set_irq, 32); + qdev_init_gpio_in(&dev->qdev, vpb_sic_set_irq, 32); for (i = 0; i < 32; i++) { sysbus_init_irq(dev, &s->parent[i]); } @@ -188,12 +188,12 @@ static void versatile_init(ram_addr_t ram_size, dev = sysbus_create_varargs("pl190", 0x10140000, cpu_pic[0], cpu_pic[1], NULL); for (n = 0; n < 32; n++) { - pic[n] = qdev_get_irq_sink(dev, n); + pic[n] = qdev_get_gpio_in(dev, n); } dev = sysbus_create_simple("versatilepb_sic", 0x10003000, NULL); for (n = 0; n < 32; n++) { sysbus_connect_irq(sysbus_from_qdev(dev), n, pic[n]); - sic[n] = qdev_get_irq_sink(dev, n); + sic[n] = qdev_get_gpio_in(dev, n); } sysbus_create_simple("pl050_keyboard", 0x10006000, sic[3]); diff --git a/hw/xilinx.h b/hw/xilinx.h new file mode 100644 index 000000000..9707a0e0b --- /dev/null +++ b/hw/xilinx.h @@ -0,0 +1,50 @@ + +/* OPB Interrupt Controller. */ +qemu_irq *microblaze_pic_init_cpu(CPUState *env); + +static inline DeviceState * +xilinx_intc_create(target_phys_addr_t base, qemu_irq irq, int kind_of_intr) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "xilinx,intc"); + qdev_set_prop_int(dev, "kind-of-intr", kind_of_intr); + qdev_init(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + return dev; +} + +/* OPB Timer/Counter. */ +static inline DeviceState * +xilinx_timer_create(target_phys_addr_t base, qemu_irq irq, int nr, int freq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "xilinx,timer"); + qdev_set_prop_int(dev, "nr-timers", nr); + qdev_set_prop_int(dev, "frequency", freq); + qdev_init(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + return dev; +} + +/* XPS Ethernet Lite MAC. */ +static inline DeviceState * +xilinx_ethlite_create(NICInfo *nd, target_phys_addr_t base, qemu_irq irq, + int txpingpong, int rxpingpong) +{ + DeviceState *dev; + + qemu_check_nic_model(nd, "xilinx-ethlite"); + + dev = qdev_create(NULL, "xilinx,ethlite"); + qdev_set_netdev(dev, nd); + qdev_set_prop_int(dev, "txpingpong", txpingpong); + qdev_set_prop_int(dev, "rxpingpong", rxpingpong); + qdev_init(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + return dev; +} diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c new file mode 100644 index 000000000..780e9c05a --- /dev/null +++ b/hw/xilinx_ethlite.c @@ -0,0 +1,235 @@ +/* + * QEMU model of the Xilinx Ethernet Lite MAC. + * + * Copyright (c) 2009 Edgar E. Iglesias. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "hw.h" +#include "net.h" + +#define D(x) +#define R_TX_BUF0 0 +#define R_TX_LEN0 (0x07f4 / 4) +#define R_TX_GIE0 (0x07f8 / 4) +#define R_TX_CTRL0 (0x07fc / 4) +#define R_TX_BUF1 (0x0800 / 4) +#define R_TX_LEN1 (0x0ff4 / 4) +#define R_TX_CTRL1 (0x0ffc / 4) + +#define R_RX_BUF0 (0x1000 / 4) +#define R_RX_CTRL0 (0x17fc / 4) +#define R_RX_BUF1 (0x1800 / 4) +#define R_RX_CTRL1 (0x1ffc / 4) +#define R_MAX (0x2000 / 4) + +#define GIE_GIE 0x80000000 + +#define CTRL_I 0x8 +#define CTRL_P 0x2 +#define CTRL_S 0x1 + +struct xlx_ethlite +{ + SysBusDevice busdev; + qemu_irq irq; + VLANClientState *vc; + + unsigned int c_tx_pingpong; + unsigned int c_rx_pingpong; + unsigned int txbuf; + unsigned int rxbuf; + + uint8_t macaddr[6]; + uint32_t regs[R_MAX]; +}; + +static inline void eth_pulse_irq(struct xlx_ethlite *s) +{ + /* Only the first gie reg is active. */ + if (s->regs[R_TX_GIE0] & GIE_GIE) { + qemu_irq_pulse(s->irq); + } +} + +static uint32_t eth_readl (void *opaque, target_phys_addr_t addr) +{ + struct xlx_ethlite *s = opaque; + uint32_t r = 0; + + addr >>= 2; + + switch (addr) + { + case R_TX_GIE0: + case R_TX_LEN0: + case R_TX_LEN1: + case R_TX_CTRL1: + case R_TX_CTRL0: + case R_RX_CTRL1: + case R_RX_CTRL0: + r = s->regs[addr]; + D(qemu_log("%s %x=%x\n", __func__, addr * 4, r)); + break; + + /* Rx packet data is endian fixed at the way into the rx rams. This + * speeds things up because the ethlite MAC does not have a len + * register. That means the CPU will issue MMIO reads for the entire + * 2k rx buffer even for small packets. + */ + default: + r = s->regs[addr]; + break; + } + return r; +} + +static void +eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct xlx_ethlite *s = opaque; + unsigned int base = 0; + + addr >>= 2; + switch (addr) + { + case R_TX_CTRL0: + case R_TX_CTRL1: + if (addr == R_TX_CTRL1) + base = 0x800 / 4; + + D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); + if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { + qemu_send_packet(s->vc, + (void *) &s->regs[base], + s->regs[base + R_TX_LEN0]); + D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0])); + if (s->regs[base + R_TX_CTRL0] & CTRL_I) + eth_pulse_irq(s); + } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { + memcpy(&s->macaddr[0], &s->regs[base], 6); + if (s->regs[base + R_TX_CTRL0] & CTRL_I) + eth_pulse_irq(s); + } + + /* We are fast and get ready pretty much immediately so + we actually never flip the S nor P bits to one. */ + s->regs[addr] = value & ~(CTRL_P | CTRL_S); + break; + + /* Keep these native. */ + case R_TX_LEN0: + case R_TX_LEN1: + case R_TX_GIE0: + case R_RX_CTRL0: + case R_RX_CTRL1: + D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); + s->regs[addr] = value; + break; + + /* Packet data, make sure it stays BE. */ + default: + s->regs[addr] = cpu_to_be32(value); + break; + } +} + +static CPUReadMemoryFunc *eth_read[] = { + NULL, NULL, ð_readl, +}; + +static CPUWriteMemoryFunc *eth_write[] = { + NULL, NULL, ð_writel, +}; + +static int eth_can_rx(void *opaque) +{ + struct xlx_ethlite *s = opaque; + int r; + r = !(s->regs[R_RX_CTRL0] & CTRL_S); + qemu_log("%s %d\n", __func__, r); + return r; +} + +static void eth_rx(void *opaque, const uint8_t *buf, int size) +{ + struct xlx_ethlite *s = opaque; + unsigned int rxbase = s->rxbuf * (0x800 / 4); + int i; + + /* DA filter. */ + if (!(buf[0] & 0x80) && memcmp(&s->macaddr[0], buf, 6)) + return; + + if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) { + D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0])); + return; + } + + D(qemu_log("%s %d rxbase=%x\n", __func__, size, rxbase)); + memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); + + /* Bring it into host endianess. */ + for (i = 0; i < ((size + 3) / 4); i++) { + uint32_t d = s->regs[rxbase + R_RX_BUF0 + i]; + s->regs[rxbase + R_RX_BUF0 + i] = be32_to_cpu(d); + } + + s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; + if (s->regs[rxbase + R_RX_CTRL0] & CTRL_I) + eth_pulse_irq(s); + + /* If c_rx_pingpong was set flip buffers. */ + s->rxbuf ^= s->c_rx_pingpong; + return; +} + +static void eth_cleanup(VLANClientState *vc) +{ + struct xlx_ethlite *s = vc->opaque; + qemu_free(s); +} + +static void xilinx_ethlite_init(SysBusDevice *dev) +{ + struct xlx_ethlite *s = FROM_SYSBUS(typeof (*s), dev); + int regs; + + sysbus_init_irq(dev, &s->irq); + s->c_tx_pingpong = qdev_get_prop_int(&dev->qdev, "txpingpong", 1); + s->c_rx_pingpong = qdev_get_prop_int(&dev->qdev, "rxpingpong", 1); + s->rxbuf = 0; + + regs = cpu_register_io_memory(0, eth_read, eth_write, s); + sysbus_init_mmio(dev, R_MAX * 4, regs); + + qdev_get_macaddr(&dev->qdev, s->macaddr); + s->vc = qdev_get_vlan_client(&dev->qdev, + eth_rx, eth_can_rx, eth_cleanup, s); +} + +static void xilinx_ethlite_register(void) +{ + sysbus_register_dev("xilinx,ethlite", sizeof (struct xlx_ethlite), + xilinx_ethlite_init); +} + +device_init(xilinx_ethlite_register) diff --git a/hw/xilinx_intc.c b/hw/xilinx_intc.c new file mode 100644 index 000000000..0b17fae28 --- /dev/null +++ b/hw/xilinx_intc.c @@ -0,0 +1,167 @@ +/* + * QEMU Xilinx OPB Interrupt Controller. + * + * Copyright (c) 2009 Edgar E. Iglesias. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "hw.h" + +#define D(x) + +#define R_ISR 0 +#define R_IPR 1 +#define R_IER 2 +#define R_IAR 3 +#define R_SIE 4 +#define R_CIE 5 +#define R_IVR 6 +#define R_MER 7 +#define R_MAX 8 + +struct xlx_pic +{ + SysBusDevice busdev; + qemu_irq parent_irq; + + /* Configuration reg chosen at synthesis-time. QEMU populates + the bits at board-setup. */ + uint32_t c_kind_of_intr; + + /* Runtime control registers. */ + uint32_t regs[R_MAX]; +}; + +static void update_irq(struct xlx_pic *p) +{ + uint32_t i; + /* Update the pending register. */ + p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER]; + + /* Update the vector register. */ + for (i = 0; i < 32; i++) { + if (p->regs[R_IPR] & (1 << i)) + break; + } + if (i == 32) + i = ~0; + + p->regs[R_IVR] = i; + if ((p->regs[R_MER] & 1) && p->regs[R_IPR]) { + qemu_irq_raise(p->parent_irq); + } else { + qemu_irq_lower(p->parent_irq); + } +} + +static uint32_t pic_readl (void *opaque, target_phys_addr_t addr) +{ + struct xlx_pic *p = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) + { + default: + if (addr < ARRAY_SIZE(p->regs)) + r = p->regs[addr]; + break; + + } + D(printf("%s %x=%x\n", __func__, addr * 4, r)); + return r; +} + +static void +pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct xlx_pic *p = opaque; + + addr >>= 2; + D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); + switch (addr) + { + case R_IAR: + p->regs[R_ISR] &= ~value; /* ACK. */ + break; + case R_SIE: + p->regs[R_IER] |= value; /* Atomic set ie. */ + break; + case R_CIE: + p->regs[R_IER] &= ~value; /* Atomic clear ie. */ + break; + default: + if (addr < ARRAY_SIZE(p->regs)) + p->regs[addr] = value; + break; + } + update_irq(p); +} + +static CPUReadMemoryFunc *pic_read[] = { + NULL, NULL, + &pic_readl, +}; + +static CPUWriteMemoryFunc *pic_write[] = { + NULL, NULL, + &pic_writel, +}; + +static void irq_handler(void *opaque, int irq, int level) +{ + struct xlx_pic *p = opaque; + + if (!(p->regs[R_MER] & 2)) { + qemu_irq_lower(p->parent_irq); + return; + } + + /* Update source flops. Don't clear unless level triggered. + Edge triggered interrupts only go away when explicitely acked to + the interrupt controller. */ + if (!(p->c_kind_of_intr & (1 << irq)) || level) { + p->regs[R_ISR] &= ~(1 << irq); + p->regs[R_ISR] |= (level << irq); + } + update_irq(p); +} + +static void xilinx_intc_init(SysBusDevice *dev) +{ + struct xlx_pic *p = FROM_SYSBUS(typeof (*p), dev); + int pic_regs; + + p->c_kind_of_intr = qdev_get_prop_int(&dev->qdev, "kind-of-intr", 0); + qdev_init_gpio_in(&dev->qdev, irq_handler, 32); + sysbus_init_irq(dev, &p->parent_irq); + + pic_regs = cpu_register_io_memory(0, pic_read, pic_write, p); + sysbus_init_mmio(dev, R_MAX * 4, pic_regs); +} + +static void xilinx_intc_register(void) +{ + sysbus_register_dev("xilinx,intc", sizeof (struct xlx_pic), + xilinx_intc_init); +} + +device_init(xilinx_intc_register) diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c new file mode 100644 index 000000000..d0e8f1862 --- /dev/null +++ b/hw/xilinx_timer.c @@ -0,0 +1,224 @@ +/* + * QEMU model of the Xilinx timer block. + * + * Copyright (c) 2009 Edgar E. Iglesias. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "sysemu.h" +#include "qemu-timer.h" + +#define D(x) + +#define R_TCSR 0 +#define R_TLR 1 +#define R_TCR 2 +#define R_MAX 4 + +#define TCSR_MDT (1<<0) +#define TCSR_UDT (1<<1) +#define TCSR_GENT (1<<2) +#define TCSR_CAPT (1<<3) +#define TCSR_ARHT (1<<4) +#define TCSR_LOAD (1<<5) +#define TCSR_ENIT (1<<6) +#define TCSR_ENT (1<<7) +#define TCSR_TINT (1<<8) +#define TCSR_PWMA (1<<9) +#define TCSR_ENALL (1<<10) + +struct xlx_timer +{ + QEMUBH *bh; + ptimer_state *ptimer; + void *parent; + int nr; /* for debug. */ + + unsigned long timer_div; + + uint32_t regs[R_MAX]; +}; + +struct timerblock +{ + SysBusDevice busdev; + qemu_irq irq; + unsigned int nr_timers; + struct xlx_timer *timers; +}; + +static inline unsigned int timer_from_addr(target_phys_addr_t addr) +{ + /* Timers get a 4x32bit control reg area each. */ + return addr >> 2; +} + +static void timer_update_irq(struct timerblock *t) +{ + unsigned int i, irq = 0; + uint32_t csr; + + for (i = 0; i < t->nr_timers; i++) { + csr = t->timers[i].regs[R_TCSR]; + irq |= (csr & TCSR_TINT) && (csr & TCSR_ENIT); + } + + /* All timers within the same slave share a single IRQ line. */ + qemu_set_irq(t->irq, !!irq); +} + +static uint32_t timer_readl (void *opaque, target_phys_addr_t addr) +{ + struct timerblock *t = opaque; + struct xlx_timer *xt; + uint32_t r = 0; + unsigned int timer; + + addr >>= 2; + timer = timer_from_addr(addr); + xt = &t->timers[timer]; + /* Further decoding to address a specific timers reg. */ + addr &= 0x3; + switch (addr) + { + case R_TCR: + r = ptimer_get_count(xt->ptimer); + if (!(xt->regs[R_TCSR] & TCSR_UDT)) + r = ~r; + D(qemu_log("xlx_timer t=%d read counter=%x udt=%d\n", + timer, r, xt->regs[R_TCSR] & TCSR_UDT)); + break; + default: + if (addr < ARRAY_SIZE(xt->regs)) + r = xt->regs[addr]; + break; + + } + D(printf("%s timer=%d %x=%x\n", __func__, timer, addr * 4, r)); + return r; +} + +static void timer_enable(struct xlx_timer *xt) +{ + uint64_t count; + + D(printf("%s timer=%d down=%d\n", __func__, + xt->nr, xt->regs[R_TCSR] & TCSR_UDT)); + + ptimer_stop(xt->ptimer); + + if (xt->regs[R_TCSR] & TCSR_UDT) + count = xt->regs[R_TLR]; + else + count = ~0 - xt->regs[R_TLR]; + ptimer_set_count(xt->ptimer, count); + ptimer_run(xt->ptimer, 1); +} + +static void +timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct timerblock *t = opaque; + struct xlx_timer *xt; + unsigned int timer; + + addr >>= 2; + timer = timer_from_addr(addr); + xt = &t->timers[timer]; + D(printf("%s addr=%x val=%x (timer=%d off=%d)\n", + __func__, addr * 4, value, timer, addr & 3)); + /* Further decoding to address a specific timers reg. */ + addr &= 3; + switch (addr) + { + case R_TCSR: + if (value & TCSR_TINT) + value &= ~TCSR_TINT; + + xt->regs[addr] = value; + if (value & TCSR_ENT) + timer_enable(xt); + break; + + default: + if (addr < ARRAY_SIZE(xt->regs)) + xt->regs[addr] = value; + break; + } + timer_update_irq(t); +} + +static CPUReadMemoryFunc *timer_read[] = { + NULL, NULL, + &timer_readl, +}; + +static CPUWriteMemoryFunc *timer_write[] = { + NULL, NULL, + &timer_writel, +}; + +static void timer_hit(void *opaque) +{ + struct xlx_timer *xt = opaque; + struct timerblock *t = xt->parent; + D(printf("%s %d\n", __func__, timer)); + xt->regs[R_TCSR] |= TCSR_TINT; + + if (xt->regs[R_TCSR] & TCSR_ARHT) + timer_enable(xt); + timer_update_irq(t); +} + +static void xilinx_timer_init(SysBusDevice *dev) +{ + struct timerblock *t = FROM_SYSBUS(typeof (*t), dev); + unsigned int i; + int timer_regs, freq_hz; + + /* All timers share a single irq line. */ + sysbus_init_irq(dev, &t->irq); + + /* Init all the ptimers. */ + freq_hz = qdev_get_prop_int(&dev->qdev, "frequency", 2); + t->nr_timers = qdev_get_prop_int(&dev->qdev, "nr-timers", 2); + t->timers = qemu_mallocz(sizeof t->timers[0] * t->nr_timers); + for (i = 0; i < t->nr_timers; i++) { + struct xlx_timer *xt = &t->timers[i]; + + xt->parent = t; + xt->nr = i; + xt->bh = qemu_bh_new(timer_hit, xt); + xt->ptimer = ptimer_init(xt->bh); + ptimer_set_freq(xt->ptimer, freq_hz); + } + + timer_regs = cpu_register_io_memory(0, timer_read, timer_write, t); + sysbus_init_mmio(dev, R_MAX * 4 * t->nr_timers, timer_regs); +} + +static void xilinx_timer_register(void) +{ + sysbus_register_dev("xilinx,timer", sizeof (struct timerblock), + xilinx_timer_init); +} + +device_init(xilinx_timer_register) diff --git a/hw/xilinx_uartlite.c b/hw/xilinx_uartlite.c new file mode 100644 index 000000000..5cfb36c48 --- /dev/null +++ b/hw/xilinx_uartlite.c @@ -0,0 +1,218 @@ +/* + * QEMU model of Xilinx uartlite. + * + * Copyright (c) 2009 Edgar E. Iglesias. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "qemu-char.h" + +#define DUART(x) + +#define R_RX 0 +#define R_TX 1 +#define R_STATUS 2 +#define R_CTRL 3 +#define R_MAX 4 + +#define STATUS_RXVALID 0x01 +#define STATUS_RXFULL 0x02 +#define STATUS_TXEMPTY 0x04 +#define STATUS_TXFULL 0x08 +#define STATUS_IE 0x10 +#define STATUS_OVERRUN 0x20 +#define STATUS_FRAME 0x40 +#define STATUS_PARITY 0x80 + +#define CONTROL_RST_TX 0x01 +#define CONTROL_RST_RX 0x02 +#define CONTROL_IE 0x10 + +struct xlx_uartlite +{ + SysBusDevice busdev; + CharDriverState *chr; + qemu_irq irq; + + uint8_t rx_fifo[8]; + unsigned int rx_fifo_pos; + unsigned int rx_fifo_len; + + uint32_t regs[R_MAX]; +}; + +static void uart_update_irq(struct xlx_uartlite *s) +{ + unsigned int irq; + + if (s->rx_fifo_len) + s->regs[R_STATUS] |= STATUS_IE; + + irq = (s->regs[R_STATUS] & STATUS_IE) && (s->regs[R_CTRL] & CONTROL_IE); + qemu_set_irq(s->irq, irq); +} + +static void uart_update_status(struct xlx_uartlite *s) +{ + uint32_t r; + + r = s->regs[R_STATUS]; + r &= ~7; + r |= 1 << 2; /* Tx fifo is always empty. We are fast :) */ + r |= (s->rx_fifo_len == sizeof (s->rx_fifo)) << 1; + r |= (!!s->rx_fifo_len); + s->regs[R_STATUS] = r; +} + +static uint32_t uart_readl (void *opaque, target_phys_addr_t addr) +{ + struct xlx_uartlite *s = opaque; + uint32_t r = 0; + addr >>= 2; + switch (addr) + { + case R_RX: + r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 7]; + if (s->rx_fifo_len) + s->rx_fifo_len--; + uart_update_status(s); + uart_update_irq(s); + break; + + default: + if (addr < ARRAY_SIZE(s->regs)) + r = s->regs[addr]; + DUART(qemu_log("%s addr=%x v=%x\n", __func__, addr, r)); + break; + } + return r; +} + +static void +uart_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct xlx_uartlite *s = opaque; + unsigned char ch = value; + + addr >>= 2; + switch (addr) + { + case R_STATUS: + hw_error("write to UART STATUS?\n"); + break; + + case R_CTRL: + if (value & CONTROL_RST_RX) { + s->rx_fifo_pos = 0; + s->rx_fifo_len = 0; + } + s->regs[addr] = value; + break; + + case R_TX: + if (s->chr) + qemu_chr_write(s->chr, &ch, 1); + + s->regs[addr] = value; + + /* hax. */ + s->regs[R_STATUS] |= STATUS_IE; + break; + + default: + DUART(printf("%s addr=%x v=%x\n", __func__, addr, value)); + if (addr < ARRAY_SIZE(s->regs)) + s->regs[addr] = value; + break; + } + uart_update_status(s); + uart_update_irq(s); +} + +static CPUReadMemoryFunc *uart_read[] = { + &uart_readl, + &uart_readl, + &uart_readl, +}; + +static CPUWriteMemoryFunc *uart_write[] = { + &uart_writel, + &uart_writel, + &uart_writel, +}; + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + struct xlx_uartlite *s = opaque; + + /* Got a byte. */ + if (s->rx_fifo_len >= 8) { + printf("WARNING: UART dropped char.\n"); + return; + } + s->rx_fifo[s->rx_fifo_pos] = *buf; + s->rx_fifo_pos++; + s->rx_fifo_pos &= 0x7; + s->rx_fifo_len++; + + uart_update_status(s); + uart_update_irq(s); +} + +static int uart_can_rx(void *opaque) +{ + struct xlx_uartlite *s = opaque; + int r; + + r = s->rx_fifo_len < sizeof(s->rx_fifo); + if (!r) + printf("cannot receive!\n"); + return r; +} + +static void uart_event(void *opaque, int event) +{ + +} + +static void xilinx_uartlite_init(SysBusDevice *dev) +{ + struct xlx_uartlite *s = FROM_SYSBUS(typeof (*s), dev); + int uart_regs; + + sysbus_init_irq(dev, &s->irq); + + uart_update_status(s); + uart_regs = cpu_register_io_memory(0, uart_read, uart_write, s); + sysbus_init_mmio(dev, R_MAX * 4, uart_regs); + + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); +} + +static void xilinx_uart_register(void) +{ + sysbus_register_dev("xilinx,uartlite", sizeof (struct xlx_uartlite), + xilinx_uartlite_init); +} + +device_init(xilinx_uart_register) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 508cb379b..190ad143e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -453,6 +453,28 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i #endif /* TARGET_MIPS */ +#ifdef TARGET_MICROBLAZE + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_XILINX_MICROBLAZE ) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_MIPS + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->pc = infop->entry; + regs->r1 = infop->start_stack; + +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#endif /* TARGET_MICROBLAZE */ + #ifdef TARGET_SH4 #define ELF_START_MMAP 0x80000000 diff --git a/linux-user/main.c b/linux-user/main.c index 17cdfa6d9..4832d3f86 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2036,6 +2036,66 @@ void cpu_loop (CPUState *env) } #endif +#ifdef TARGET_MICROBLAZE +void cpu_loop (CPUState *env) +{ + int trapnr, ret; + target_siginfo_t info; + + while (1) { + trapnr = cpu_mb_exec (env); + switch (trapnr) { + case 0xaa: + { + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_BREAK: + /* Return address is 4 bytes after the call. */ + env->regs[14] += 4; + ret = do_syscall(env, + env->regs[12], + env->regs[5], + env->regs[6], + env->regs[7], + env->regs[8], + env->regs[9], + env->regs[10]); + env->regs[3] = ret; + env->sregs[SR_PC] = env->regs[14]; + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig (env, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, &info); + } + } + break; + default: + printf ("Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(env, stderr, fprintf, 0); + exit (1); + } + process_pending_signals (env); + } +} +#endif + #ifdef TARGET_M68K void cpu_loop(CPUM68KState *env) @@ -2698,6 +2758,42 @@ int main(int argc, char **argv, char **envp) env->sr = regs->sr; ts->sim_syscalls = 1; } +#elif defined(TARGET_MICROBLAZE) + { + env->regs[0] = regs->r0; + env->regs[1] = regs->r1; + env->regs[2] = regs->r2; + env->regs[3] = regs->r3; + env->regs[4] = regs->r4; + env->regs[5] = regs->r5; + env->regs[6] = regs->r6; + env->regs[7] = regs->r7; + env->regs[8] = regs->r8; + env->regs[9] = regs->r9; + env->regs[10] = regs->r10; + env->regs[11] = regs->r11; + env->regs[12] = regs->r12; + env->regs[13] = regs->r13; + env->regs[14] = regs->r14; + env->regs[15] = regs->r15; + env->regs[16] = regs->r16; + env->regs[17] = regs->r17; + env->regs[18] = regs->r18; + env->regs[19] = regs->r19; + env->regs[20] = regs->r20; + env->regs[21] = regs->r21; + env->regs[22] = regs->r22; + env->regs[23] = regs->r23; + env->regs[24] = regs->r24; + env->regs[25] = regs->r25; + env->regs[26] = regs->r26; + env->regs[27] = regs->r27; + env->regs[28] = regs->r28; + env->regs[29] = regs->r29; + env->regs[30] = regs->r30; + env->regs[31] = regs->r31; + env->sregs[SR_PC] = regs->pc; + } #elif defined(TARGET_MIPS) { int i; diff --git a/linux-user/microblaze/syscall.h b/linux-user/microblaze/syscall.h new file mode 100644 index 000000000..db1f98ae6 --- /dev/null +++ b/linux-user/microblaze/syscall.h @@ -0,0 +1,45 @@ +#define UNAME_MACHINE "microblaze" + +/* We use microblaze_reg_t to keep things similar to the kernel sources. */ +typedef uint32_t microblaze_reg_t; + +struct target_pt_regs { + microblaze_reg_t r0; + microblaze_reg_t r1; + microblaze_reg_t r2; + microblaze_reg_t r3; + microblaze_reg_t r4; + microblaze_reg_t r5; + microblaze_reg_t r6; + microblaze_reg_t r7; + microblaze_reg_t r8; + microblaze_reg_t r9; + microblaze_reg_t r10; + microblaze_reg_t r11; + microblaze_reg_t r12; + microblaze_reg_t r13; + microblaze_reg_t r14; + microblaze_reg_t r15; + microblaze_reg_t r16; + microblaze_reg_t r17; + microblaze_reg_t r18; + microblaze_reg_t r19; + microblaze_reg_t r20; + microblaze_reg_t r21; + microblaze_reg_t r22; + microblaze_reg_t r23; + microblaze_reg_t r24; + microblaze_reg_t r25; + microblaze_reg_t r26; + microblaze_reg_t r27; + microblaze_reg_t r28; + microblaze_reg_t r29; + microblaze_reg_t r30; + microblaze_reg_t r31; + microblaze_reg_t pc; + microblaze_reg_t msr; + microblaze_reg_t ear; + microblaze_reg_t esr; + microblaze_reg_t fsr; + uint32_t kernel_mode; +}; diff --git a/linux-user/microblaze/syscall_nr.h b/linux-user/microblaze/syscall_nr.h new file mode 100644 index 000000000..3e641cdb4 --- /dev/null +++ b/linux-user/microblaze/syscall_nr.h @@ -0,0 +1,369 @@ +#define TARGET_NR_restart_syscall 0 /* ok */ +#define TARGET_NR_exit 1 /* ok */ +#define TARGET_NR_fork 2 /* not for no MMU - weird */ +#define TARGET_NR_read 3 /* ok */ +#define TARGET_NR_write 4 /* ok */ +#define TARGET_NR_open 5 /* openat */ +#define TARGET_NR_close 6 /* ok */ +#define TARGET_NR_waitpid 7 /* waitid */ +#define TARGET_NR_creat 8 /* openat */ +#define TARGET_NR_link 9 /* linkat */ +#define TARGET_NR_unlink 10 /* unlinkat */ +#define TARGET_NR_execve 11 /* ok */ +#define TARGET_NR_chdir 12 /* ok */ +#define TARGET_NR_time 13 /* obsolete -> sys_gettimeofday */ +#define TARGET_NR_mknod 14 /* mknodat */ +#define TARGET_NR_chmod 15 /* fchmodat */ +#define TARGET_NR_lchown 16 /* ok */ +#define TARGET_NR_break 17 /* don't know */ +#define TARGET_NR_oldstat 18 /* remove */ +#define TARGET_NR_lseek 19 /* ok */ +#define TARGET_NR_getpid 20 /* ok */ +#define TARGET_NR_mount 21 /* ok */ +#define TARGET_NR_umount 22 /* ok */ /* use only umount2 */ +#define TARGET_NR_setuid 23 /* ok */ +#define TARGET_NR_getuid 24 /* ok */ +#define TARGET_NR_stime 25 /* obsolete -> sys_settimeofday */ +#define TARGET_NR_ptrace 26 /* ok */ +#define TARGET_NR_alarm 27 /* obsolete -> sys_setitimer */ +#define TARGET_NR_oldfstat 28 /* remove */ +#define TARGET_NR_pause 29 /* obsolete -> sys_rt_sigtimedwait */ +#define TARGET_NR_utime 30 /* obsolete -> sys_utimesat */ +#define TARGET_NR_stty 31 /* remove */ +#define TARGET_NR_gtty 32 /* remove */ +#define TARGET_NR_access 33 /* faccessat */ +#define TARGET_NR_nice 34 /* can be implemented by sys_setpriority */ +#define TARGET_NR_ftime 35 /* remove */ +#define TARGET_NR_sync 36 /* ok */ +#define TARGET_NR_kill 37 /* ok */ +#define TARGET_NR_rename 38 /* renameat */ +#define TARGET_NR_mkdir 39 /* mkdirat */ +#define TARGET_NR_rmdir 40 /* unlinkat */ +#define TARGET_NR_dup 41 /* ok */ +#define TARGET_NR_pipe 42 /* ok */ +#define TARGET_NR_times 43 /* ok */ +#define TARGET_NR_prof 44 /* remove */ +#define TARGET_NR_brk 45 /* ok -mmu, nommu specific */ +#define TARGET_NR_setgid 46 /* ok */ +#define TARGET_NR_getgid 47 /* ok */ +#define TARGET_NR_signal 48 /* obsolete -> sys_rt_sigaction */ +#define TARGET_NR_geteuid 49 /* ok */ +#define TARGET_NR_getegid 50 /* ok */ +#define TARGET_NR_acct 51 /* add it and then I can disable it */ +#define TARGET_NR_umount2 52 /* remove */ +#define TARGET_NR_lock 53 /* remove */ +#define TARGET_NR_ioctl 54 /* ok */ +#define TARGET_NR_fcntl 55 /* ok -> 64bit version*/ +#define TARGET_NR_mpx 56 /* remove */ +#define TARGET_NR_setpgid 57 /* ok */ +#define TARGET_NR_ulimit 58 /* remove */ +#define TARGET_NR_oldolduname 59 /* remove */ +#define TARGET_NR_umask 60 /* ok */ +#define TARGET_NR_chroot 61 /* ok */ +#define TARGET_NR_ustat 62 /* obsolete -> statfs64 */ +#define TARGET_NR_dup2 63 /* ok */ +#define TARGET_NR_getppid 64 /* ok */ +#define TARGET_NR_getpgrp 65 /* obsolete -> sys_getpgid */ +#define TARGET_NR_setsid 66 /* ok */ +#define TARGET_NR_sigaction 67 /* obsolete -> rt_sigaction */ +#define TARGET_NR_sgetmask 68 /* obsolete -> sys_rt_sigprocmask */ +#define TARGET_NR_ssetmask 69 /* obsolete ->sys_rt_sigprocmask */ +#define TARGET_NR_setreuid 70 /* ok */ +#define TARGET_NR_setregid 71 /* ok */ +#define TARGET_NR_sigsuspend 72 /* obsolete -> rt_sigsuspend */ +#define TARGET_NR_sigpending 73 /* obsolete -> sys_rt_sigpending */ +#define TARGET_NR_sethostname 74 /* ok */ +#define TARGET_NR_setrlimit 75 /* ok */ +#define TARGET_NR_getrlimit 76 /* ok Back compatible 2Gig limited rlimit */ +#define TARGET_NR_getrusage 77 /* ok */ +#define TARGET_NR_gettimeofday 78 /* ok */ +#define TARGET_NR_settimeofday 79 /* ok */ +#define TARGET_NR_getgroups 80 /* ok */ +#define TARGET_NR_setgroups 81 /* ok */ +#define TARGET_NR_select 82 /* obsolete -> sys_pselect7 */ +#define TARGET_NR_symlink 83 /* symlinkat */ +#define TARGET_NR_oldlstat 84 /* remove */ +#define TARGET_NR_readlink 85 /* obsolete -> sys_readlinkat */ +#define TARGET_NR_uselib 86 /* remove */ +#define TARGET_NR_swapon 87 /* ok */ +#define TARGET_NR_reboot 88 /* ok */ +#define TARGET_NR_readdir 89 /* remove ? */ +#define TARGET_NR_mmap 90 /* obsolete -> sys_mmap2 */ +#define TARGET_NR_munmap 91 /* ok - mmu and nommu */ +#define TARGET_NR_truncate 92 /* ok or truncate64 */ +#define TARGET_NR_ftruncate 93 /* ok or ftruncate64 */ +#define TARGET_NR_fchmod 94 /* ok */ +#define TARGET_NR_fchown 95 /* ok */ +#define TARGET_NR_getpriority 96 /* ok */ +#define TARGET_NR_setpriority 97 /* ok */ +#define TARGET_NR_profil 98 /* remove */ +#define TARGET_NR_statfs 99 /* ok or statfs64 */ +#define TARGET_NR_fstatfs 100 /* ok or fstatfs64 */ +#define TARGET_NR_ioperm 101 /* remove */ +#define TARGET_NR_socketcall 102 /* remove */ +#define TARGET_NR_syslog 103 /* ok */ +#define TARGET_NR_setitimer 104 /* ok */ +#define TARGET_NR_getitimer 105 /* ok */ +#define TARGET_NR_stat 106 /* remove */ +#define TARGET_NR_lstat 107 /* remove */ +#define TARGET_NR_fstat 108 /* remove */ +#define TARGET_NR_olduname 109 /* remove */ +#define TARGET_NR_iopl 110 /* remove */ +#define TARGET_NR_vhangup 111 /* ok */ +#define TARGET_NR_idle 112 /* remove */ +#define TARGET_NR_vm86old 113 /* remove */ +#define TARGET_NR_wait4 114 /* obsolete -> waitid */ +#define TARGET_NR_swapoff 115 /* ok */ +#define TARGET_NR_sysinfo 116 /* ok */ +#define TARGET_NR_ipc 117 /* remove - direct call */ +#define TARGET_NR_fsync 118 /* ok */ +#define TARGET_NR_sigreturn 119 /* obsolete -> sys_rt_sigreturn */ +#define TARGET_NR_clone 120 /* ok */ +#define TARGET_NR_setdomainname 121 /* ok */ +#define TARGET_NR_uname 122 /* remove */ +#define TARGET_NR_modify_ldt 123 /* remove */ +#define TARGET_NR_adjtimex 124 /* ok */ +#define TARGET_NR_mprotect 125 /* remove */ +#define TARGET_NR_sigprocmask 126 /* obsolete -> sys_rt_sigprocmask */ +#define TARGET_NR_create_module 127 /* remove */ +#define TARGET_NR_init_module 128 /* ok */ +#define TARGET_NR_delete_module 129 /* ok */ +#define TARGET_NR_get_kernel_syms 130 /* remove */ +#define TARGET_NR_quotactl 131 /* ok */ +#define TARGET_NR_getpgid 132 /* ok */ +#define TARGET_NR_fchdir 133 /* ok */ +#define TARGET_NR_bdflush 134 /* remove */ +#define TARGET_NR_sysfs 135 /* needed for busybox */ +#define TARGET_NR_personality 136 /* ok */ +#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ +#define TARGET_NR_setfsuid 138 /* ok */ +#define TARGET_NR_setfsgid 139 /* ok */ +#define TARGET_NR__llseek 140 /* remove only lseek */ +#define TARGET_NR_getdents 141 /* ok or getdents64 */ +#define TARGET_NR__newselect 142 /* remove */ +#define TARGET_NR_flock 143 /* ok */ +#define TARGET_NR_msync 144 /* remove */ +#define TARGET_NR_readv 145 /* ok */ +#define TARGET_NR_writev 146 /* ok */ +#define TARGET_NR_getsid 147 /* ok */ +#define TARGET_NR_fdatasync 148 /* ok */ +#define TARGET_NR__sysctl 149 /* remove */ +#define TARGET_NR_mlock 150 /* ok - nommu or mmu */ +#define TARGET_NR_munlock 151 /* ok - nommu or mmu */ +#define TARGET_NR_mlockall 152 /* ok - nommu or mmu */ +#define TARGET_NR_munlockall 153 /* ok - nommu or mmu */ +#define TARGET_NR_sched_setparam 154 /* ok */ +#define TARGET_NR_sched_getparam 155 /* ok */ +#define TARGET_NR_sched_setscheduler 156 /* ok */ +#define TARGET_NR_sched_getscheduler 157 /* ok */ +#define TARGET_NR_sched_yield 158 /* ok */ +#define TARGET_NR_sched_get_priority_max 159 /* ok */ +#define TARGET_NR_sched_get_priority_min 160 /* ok */ +#define TARGET_NR_sched_rr_get_interval 161 /* ok */ +#define TARGET_NR_nanosleep 162 /* ok */ +#define TARGET_NR_mremap 163 /* ok - nommu or mmu */ +#define TARGET_NR_setresuid 164 /* ok */ +#define TARGET_NR_getresuid 165 /* ok */ +#define TARGET_NR_vm86 166 /* remove */ +#define TARGET_NR_query_module 167 /* ok */ +#define TARGET_NR_poll 168 /* obsolete -> sys_ppoll */ +#define TARGET_NR_nfsservctl 169 /* ok */ +#define TARGET_NR_setresgid 170 /* ok */ +#define TARGET_NR_getresgid 171 /* ok */ +#define TARGET_NR_prctl 172 /* ok */ +#define TARGET_NR_rt_sigreturn 173 /* ok */ +#define TARGET_NR_rt_sigaction 174 /* ok */ +#define TARGET_NR_rt_sigprocmask 175 /* ok */ +#define TARGET_NR_rt_sigpending 176 /* ok */ +#define TARGET_NR_rt_sigtimedwait 177 /* ok */ +#define TARGET_NR_rt_sigqueueinfo 178 /* ok */ +#define TARGET_NR_rt_sigsuspend 179 /* ok */ +#define TARGET_NR_pread64 180 /* ok */ +#define TARGET_NR_pwrite64 181 /* ok */ +#define TARGET_NR_chown 182 /* obsolete -> fchownat */ +#define TARGET_NR_getcwd 183 /* ok */ +#define TARGET_NR_capget 184 /* ok */ +#define TARGET_NR_capset 185 /* ok */ +#define TARGET_NR_sigaltstack 186 /* remove */ +#define TARGET_NR_sendfile 187 /* ok -> exist 64bit version*/ +#define TARGET_NR_getpmsg 188 /* remove - some people actually want streams */ +#define TARGET_NR_putpmsg 189 /* remove - some people actually want streams */ +#define TARGET_NR_vfork 190 /* for noMMU - group with clone -> maybe remove */ +#define TARGET_NR_ugetrlimit 191 /* remove - SuS compliant getrlimit */ +#define TARGET_NR_mmap2 192 /* ok */ +#define TARGET_NR_truncate64 193 /* ok */ +#define TARGET_NR_ftruncate64 194 /* ok */ +#define TARGET_NR_stat64 195 /* remove _ARCH_WANT_STAT64 */ +#define TARGET_NR_lstat64 196 /* remove _ARCH_WANT_STAT64 */ +#define TARGET_NR_fstat64 197 /* remove _ARCH_WANT_STAT64 */ +#define TARGET_NR_lchown32 198 /* ok - without 32 */ +#define TARGET_NR_getuid32 199 /* ok - without 32 */ +#define TARGET_NR_getgid32 200 /* ok - without 32 */ +#define TARGET_NR_geteuid32 201 /* ok - without 32 */ +#define TARGET_NR_getegid32 202 /* ok - without 32 */ +#define TARGET_NR_setreuid32 203 /* ok - without 32 */ +#define TARGET_NR_setregid32 204 /* ok - without 32 */ +#define TARGET_NR_getgroups32 205 /* ok - without 32 */ +#define TARGET_NR_setgroups32 206 /* ok - without 32 */ +#define TARGET_NR_fchown32 207 /* ok - without 32 */ +#define TARGET_NR_setresuid32 208 /* ok - without 32 */ +#define TARGET_NR_getresuid32 209 /* ok - without 32 */ +#define TARGET_NR_setresgid32 210 /* ok - without 32 */ +#define TARGET_NR_getresgid32 211 /* ok - without 32 */ +#define TARGET_NR_chown32 212 /* ok - without 32 -obsolete -> fchownat */ +#define TARGET_NR_setuid32 213 /* ok - without 32 */ +#define TARGET_NR_setgid32 214 /* ok - without 32 */ +#define TARGET_NR_setfsuid32 215 /* ok - without 32 */ +#define TARGET_NR_setfsgid32 216 /* ok - without 32 */ +#define TARGET_NR_pivot_root 217 /* ok */ +#define TARGET_NR_mincore 218 /* ok */ +#define TARGET_NR_madvise 219 /* ok */ +//#define TARGET_NR_madvise1 219 /* remove delete when C lib stub is removed */ +#define TARGET_NR_getdents64 220 /* ok */ +#define TARGET_NR_fcntl64 221 /* ok */ +/* 223 is unused */ +#define TARGET_NR_gettid 224 /* ok */ +#define TARGET_NR_readahead 225 /* ok */ +#define TARGET_NR_setxattr 226 /* ok */ +#define TARGET_NR_lsetxattr 227 /* ok */ +#define TARGET_NR_fsetxattr 228 /* ok */ +#define TARGET_NR_getxattr 229 /* ok */ +#define TARGET_NR_lgetxattr 230 /* ok */ +#define TARGET_NR_fgetxattr 231 /* ok */ +#define TARGET_NR_listxattr 232 /* ok */ +#define TARGET_NR_llistxattr 233 /* ok */ +#define TARGET_NR_flistxattr 234 /* ok */ +#define TARGET_NR_removexattr 235 /* ok */ +#define TARGET_NR_lremovexattr 236 /* ok */ +#define TARGET_NR_fremovexattr 237 /* ok */ +#define TARGET_NR_tkill 238 /* ok */ +#define TARGET_NR_sendfile64 239 /* ok */ +#define TARGET_NR_futex 240 /* ok */ +#define TARGET_NR_sched_setaffinity 241 /* ok */ +#define TARGET_NR_sched_getaffinity 242 /* ok */ +#define TARGET_NR_set_thread_area 243 /* remove */ +#define TARGET_NR_get_thread_area 244 /* remove */ +#define TARGET_NR_io_setup 245 /* ok */ +#define TARGET_NR_io_destroy 246 /* ok */ +#define TARGET_NR_io_getevents 247 /* ok */ +#define TARGET_NR_io_submit 248 /* ok */ +#define TARGET_NR_io_cancel 249 /* ok */ +#define TARGET_NR_fadvise64 250 /* remove -> sys_fadvise64_64 */ +/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */ +#define TARGET_NR_exit_group 252 /* ok */ +#define TARGET_NR_lookup_dcookie 253 /* ok */ +#define TARGET_NR_epoll_create 254 /* ok */ +#define TARGET_NR_epoll_ctl 255 /* ok */ +#define TARGET_NR_epoll_wait 256 /* obsolete -> sys_epoll_pwait */ +#define TARGET_NR_remap_file_pages 257 /* only for mmu */ +#define TARGET_NR_set_tid_address 258 /* ok */ +#define TARGET_NR_timer_create 259 /* ok */ +#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1) /* 260 */ /* ok */ +#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2) /* 261 */ /* ok */ +#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3) /* 262 */ /* ok */ +#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4) /* 263 */ /* ok */ +#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5) /* 264 */ /* ok */ +#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6) /* 265 */ /* ok */ +#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7) /* 266 */ /* ok */ +#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8) /* 267 */ /* ok */ +#define TARGET_NR_statfs64 268 /* ok */ +#define TARGET_NR_fstatfs64 269 /* ok */ +#define TARGET_NR_tgkill 270 /* ok */ +#define TARGET_NR_utimes 271 /* obsolete -> sys_futimesat */ +#define TARGET_NR_fadvise64_64 272 /* ok */ +#define TARGET_NR_vserver 273 /* ok */ +#define TARGET_NR_mbind 274 /* only for mmu */ +#define TARGET_NR_get_mempolicy 275 /* only for mmu */ +#define TARGET_NR_set_mempolicy 276 /* only for mmu */ +#define TARGET_NR_mq_open 277 /* ok */ +#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1) /* 278 */ /* ok */ +#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2) /* 279 */ /* ok */ +#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3) /* 280 */ /* ok */ +#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4) /* 281 */ /* ok */ +#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5) /* 282 */ /* ok */ +#define TARGET_NR_kexec_load 283 /* ok */ +#define TARGET_NR_waitid 284 /* ok */ +/* #define TARGET_NR_sys_setaltroot 285 */ +#define TARGET_NR_add_key 286 /* ok */ +#define TARGET_NR_request_key 287 /* ok */ +#define TARGET_NR_keyctl 288 /* ok */ +#define TARGET_NR_ioprio_set 289 /* ok */ +#define TARGET_NR_ioprio_get 290 /* ok */ +#define TARGET_NR_inotify_init 291 /* ok */ +#define TARGET_NR_inotify_add_watch 292 /* ok */ +#define TARGET_NR_inotify_rm_watch 293 /* ok */ +#define TARGET_NR_migrate_pages 294 /* mmu */ +#define TARGET_NR_openat 295 /* ok */ +#define TARGET_NR_mkdirat 296 /* ok */ +#define TARGET_NR_mknodat 297 /* ok */ +#define TARGET_NR_fchownat 298 /* ok */ +#define TARGET_NR_futimesat 299 /* obsolete -> sys_utimesat */ +#define TARGET_NR_fstatat64 300 /* stat64 */ +#define TARGET_NR_unlinkat 301 /* ok */ +#define TARGET_NR_renameat 302 /* ok */ +#define TARGET_NR_linkat 303 /* ok */ +#define TARGET_NR_symlinkat 304 /* ok */ +#define TARGET_NR_readlinkat 305 /* ok */ +#define TARGET_NR_fchmodat 306 /* ok */ +#define TARGET_NR_faccessat 307 /* ok */ +#define TARGET_NR_pselect6 308 /* obsolete -> sys_pselect7 */ +#define TARGET_NR_ppoll 309 /* ok */ +#define TARGET_NR_unshare 310 /* ok */ +#define TARGET_NR_set_robust_list 311 /* ok */ +#define TARGET_NR_get_robust_list 312 /* ok */ +#define TARGET_NR_splice 313 /* ok */ +#define TARGET_NR_sync_file_range 314 /* ok */ +#define TARGET_NR_tee 315 /* ok */ +#define TARGET_NR_vmsplice 316 /* ok */ +#define TARGET_NR_move_pages 317 /* mmu */ +#define TARGET_NR_getcpu 318 /* ok */ +#define TARGET_NR_epoll_pwait 319 /* ok */ +#define TARGET_NR_utimensat 320 /* ok */ +#define TARGET_NR_signalfd 321 /* ok */ +#define TARGET_NR_timerfd_create 322 /* ok */ +#define TARGET_NR_eventfd 323 /* ok */ +#define TARGET_NR_fallocate 324 /* ok */ +#define TARGET_NR_semtimedop 325 /* ok - semaphore group */ +#define TARGET_NR_timerfd_settime 326 /* ok */ +#define TARGET_NR_timerfd_gettime 327 /* ok */ +/* sysv ipc syscalls */ +#define TARGET_NR_semctl 328 /* ok */ +#define TARGET_NR_semget 329 /* ok */ +#define TARGET_NR_semop 330 /* ok */ +#define TARGET_NR_msgctl 331 /* ok */ +#define TARGET_NR_msgget 332 /* ok */ +#define TARGET_NR_msgrcv 333 /* ok */ +#define TARGET_NR_msgsnd 334 /* ok */ +#define TARGET_NR_shmat 335 /* ok */ +#define TARGET_NR_shmctl 336 /* ok */ +#define TARGET_NR_shmdt 337 /* ok */ +#define TARGET_NR_shmget 338 /* ok */ + + +#define TARGET_NR_signalfd4 339 /* new */ +#define TARGET_NR_eventfd2 340 /* new */ +#define TARGET_NR_epoll_create1 341 /* new */ +#define TARGET_NR_dup3 342 /* new */ +#define TARGET_NR_pipe2 343 /* new */ +#define TARGET_NR_inotify_init1 344 /* new */ +#define TARGET_NR_socket 345 /* new */ +#define TARGET_NR_socketpair 346 /* new */ +#define TARGET_NR_bind 347 /* new */ +#define TARGET_NR_listen 348 /* new */ +#define TARGET_NR_accept 349 /* new */ +#define TARGET_NR_connect 350 /* new */ +#define TARGET_NR_getsockname 351 /* new */ +#define TARGET_NR_getpeername 352 /* new */ +#define TARGET_NR_sendto 353 /* new */ +#define TARGET_NR_send 354 /* new */ +#define TARGET_NR_recvfrom 355 /* new */ +#define TARGET_NR_recv 356 /* new */ +#define TARGET_NR_setsockopt 357 /* new */ +#define TARGET_NR_getsockopt 358 /* new */ +#define TARGET_NR_shutdown 359 /* new */ +#define TARGET_NR_sendmsg 360 /* new */ +#define TARGET_NR_recvmsg 361 /* new */ +#define TARGET_NR_accept04 362 /* new */ + +#define TARGET_NR_syscalls 363 + diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h new file mode 100644 index 000000000..3d1f7a723 --- /dev/null +++ b/linux-user/microblaze/target_signal.h @@ -0,0 +1,29 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_ulong ss_size; + abi_long ss_flags; +} target_stack_t; + + +/* + * sigaltstack controls + */ +#define TARGET_SS_ONSTACK 1 +#define TARGET_SS_DISABLE 2 + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_SIGSTKSZ 8192 + +static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) +{ + return state->regs[14]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/microblaze/termbits.h b/linux-user/microblaze/termbits.h new file mode 100644 index 000000000..fc82ca084 --- /dev/null +++ b/linux-user/microblaze/termbits.h @@ -0,0 +1,213 @@ +/* from asm/termbits.h */ + +#define TARGET_NCCS 19 + +struct target_termios { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ +}; + +/* c_iflag bits */ +#define TARGET_IGNBRK 0000001 +#define TARGET_BRKINT 0000002 +#define TARGET_IGNPAR 0000004 +#define TARGET_PARMRK 0000010 +#define TARGET_INPCK 0000020 +#define TARGET_ISTRIP 0000040 +#define TARGET_INLCR 0000100 +#define TARGET_IGNCR 0000200 +#define TARGET_ICRNL 0000400 +#define TARGET_IUCLC 0001000 +#define TARGET_IXON 0002000 +#define TARGET_IXANY 0004000 +#define TARGET_IXOFF 0010000 +#define TARGET_IMAXBEL 0020000 + +/* c_oflag bits */ +#define TARGET_OPOST 0000001 +#define TARGET_OLCUC 0000002 +#define TARGET_ONLCR 0000004 +#define TARGET_OCRNL 0000010 +#define TARGET_ONOCR 0000020 +#define TARGET_ONLRET 0000040 +#define TARGET_OFILL 0000100 +#define TARGET_OFDEL 0000200 +#define TARGET_NLDLY 0000400 +#define TARGET_NL0 0000000 +#define TARGET_NL1 0000400 +#define TARGET_CRDLY 0003000 +#define TARGET_CR0 0000000 +#define TARGET_CR1 0001000 +#define TARGET_CR2 0002000 +#define TARGET_CR3 0003000 +#define TARGET_TABDLY 0014000 +#define TARGET_TAB0 0000000 +#define TARGET_TAB1 0004000 +#define TARGET_TAB2 0010000 +#define TARGET_TAB3 0014000 +#define TARGET_XTABS 0014000 +#define TARGET_BSDLY 0020000 +#define TARGET_BS0 0000000 +#define TARGET_BS1 0020000 +#define TARGET_VTDLY 0040000 +#define TARGET_VT0 0000000 +#define TARGET_VT1 0040000 +#define TARGET_FFDLY 0100000 +#define TARGET_FF0 0000000 +#define TARGET_FF1 0100000 + +/* c_cflag bit meaning */ +#define TARGET_CBAUD 0010017 +#define TARGET_B0 0000000 /* hang up */ +#define TARGET_B50 0000001 +#define TARGET_B75 0000002 +#define TARGET_B110 0000003 +#define TARGET_B134 0000004 +#define TARGET_B150 0000005 +#define TARGET_B200 0000006 +#define TARGET_B300 0000007 +#define TARGET_B600 0000010 +#define TARGET_B1200 0000011 +#define TARGET_B1800 0000012 +#define TARGET_B2400 0000013 +#define TARGET_B4800 0000014 +#define TARGET_B9600 0000015 +#define TARGET_B19200 0000016 +#define TARGET_B38400 0000017 +#define TARGET_EXTA B19200 +#define TARGET_EXTB B38400 +#define TARGET_CSIZE 0000060 +#define TARGET_CS5 0000000 +#define TARGET_CS6 0000020 +#define TARGET_CS7 0000040 +#define TARGET_CS8 0000060 +#define TARGET_CSTOPB 0000100 +#define TARGET_CREAD 0000200 +#define TARGET_PARENB 0000400 +#define TARGET_PARODD 0001000 +#define TARGET_HUPCL 0002000 +#define TARGET_CLOCAL 0004000 +#define TARGET_CBAUDEX 0010000 +#define TARGET_B57600 0010001 +#define TARGET_B115200 0010002 +#define TARGET_B230400 0010003 +#define TARGET_B460800 0010004 +#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ +#define TARGET_CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define TARGET_ISIG 0000001 +#define TARGET_ICANON 0000002 +#define TARGET_XCASE 0000004 +#define TARGET_ECHO 0000010 +#define TARGET_ECHOE 0000020 +#define TARGET_ECHOK 0000040 +#define TARGET_ECHONL 0000100 +#define TARGET_NOFLSH 0000200 +#define TARGET_TOSTOP 0000400 +#define TARGET_ECHOCTL 0001000 +#define TARGET_ECHOPRT 0002000 +#define TARGET_ECHOKE 0004000 +#define TARGET_FLUSHO 0010000 +#define TARGET_PENDIN 0040000 +#define TARGET_IEXTEN 0100000 + +/* c_cc character offsets */ +#define TARGET_VINTR 0 +#define TARGET_VQUIT 1 +#define TARGET_VERASE 2 +#define TARGET_VKILL 3 +#define TARGET_VEOF 4 +#define TARGET_VTIME 5 +#define TARGET_VMIN 6 +#define TARGET_VSWTC 7 +#define TARGET_VSTART 8 +#define TARGET_VSTOP 9 +#define TARGET_VSUSP 10 +#define TARGET_VEOL 11 +#define TARGET_VREPRINT 12 +#define TARGET_VDISCARD 13 +#define TARGET_VWERASE 14 +#define TARGET_VLNEXT 15 +#define TARGET_VEOL2 16 + +/* ioctls */ + +#define TARGET_TCGETS 0x5401 +#define TARGET_TCSETS 0x5402 +#define TARGET_TCSETSW 0x5403 +#define TARGET_TCSETSF 0x5404 +#define TARGET_TCGETA 0x5405 +#define TARGET_TCSETA 0x5406 +#define TARGET_TCSETAW 0x5407 +#define TARGET_TCSETAF 0x5408 +#define TARGET_TCSBRK 0x5409 +#define TARGET_TCXONC 0x540A +#define TARGET_TCFLSH 0x540B + +#define TARGET_TIOCEXCL 0x540C +#define TARGET_TIOCNXCL 0x540D +#define TARGET_TIOCSCTTY 0x540E +#define TARGET_TIOCGPGRP 0x540F +#define TARGET_TIOCSPGRP 0x5410 +#define TARGET_TIOCOUTQ 0x5411 +#define TARGET_TIOCSTI 0x5412 +#define TARGET_TIOCGWINSZ 0x5413 +#define TARGET_TIOCSWINSZ 0x5414 +#define TARGET_TIOCMGET 0x5415 +#define TARGET_TIOCMBIS 0x5416 +#define TARGET_TIOCMBIC 0x5417 +#define TARGET_TIOCMSET 0x5418 +#define TARGET_TIOCGSOFTCAR 0x5419 +#define TARGET_TIOCSSOFTCAR 0x541A +#define TARGET_FIONREAD 0x541B +#define TARGET_TIOCINQ TARGET_FIONREAD +#define TARGET_TIOCLINUX 0x541C +#define TARGET_TIOCCONS 0x541D +#define TARGET_TIOCGSERIAL 0x541E +#define TARGET_TIOCSSERIAL 0x541F +#define TARGET_TIOCPKT 0x5420 +#define TARGET_FIONBIO 0x5421 +#define TARGET_TIOCNOTTY 0x5422 +#define TARGET_TIOCSETD 0x5423 +#define TARGET_TIOCGETD 0x5424 +#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ +#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ +#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */ + +#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */ +#define TARGET_FIOCLEX 0x5451 +#define TARGET_FIOASYNC 0x5452 +#define TARGET_TIOCSERCONFIG 0x5453 +#define TARGET_TIOCSERGWILD 0x5454 +#define TARGET_TIOCSERSWILD 0x5455 +#define TARGET_TIOCGLCKTRMIOS 0x5456 +#define TARGET_TIOCSLCKTRMIOS 0x5457 +#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define TARGET_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ +#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ + +/* Used for packet mode */ +#define TARGET_TIOCPKT_DATA 0 +#define TARGET_TIOCPKT_FLUSHREAD 1 +#define TARGET_TIOCPKT_FLUSHWRITE 2 +#define TARGET_TIOCPKT_STOP 4 +#define TARGET_TIOCPKT_START 8 +#define TARGET_TIOCPKT_NOSTOP 16 +#define TARGET_TIOCPKT_DOSTOP 32 + +#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 9c9c7eb63..371927e2a 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3011,6 +3011,222 @@ badframe: force_sig(TARGET_SIGSEGV); return 0; } +#elif defined(TARGET_MICROBLAZE) + +struct target_sigcontext { + struct target_pt_regs regs; /* needs to be first */ + uint32_t oldmask; +}; + +/* Signal frames. */ +struct target_signal_frame { + struct target_sigcontext sc; + uint32_t extramask[TARGET_NSIG_WORDS - 1]; + uint32_t tramp[2]; +}; + +struct rt_signal_frame { + struct siginfo info; + struct ucontext uc; + uint32_t tramp[2]; +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUState *env) +{ + __put_user(env->regs[0], &sc->regs.r0); + __put_user(env->regs[1], &sc->regs.r1); + __put_user(env->regs[2], &sc->regs.r2); + __put_user(env->regs[3], &sc->regs.r3); + __put_user(env->regs[4], &sc->regs.r4); + __put_user(env->regs[5], &sc->regs.r5); + __put_user(env->regs[6], &sc->regs.r6); + __put_user(env->regs[7], &sc->regs.r7); + __put_user(env->regs[8], &sc->regs.r8); + __put_user(env->regs[9], &sc->regs.r9); + __put_user(env->regs[10], &sc->regs.r10); + __put_user(env->regs[11], &sc->regs.r11); + __put_user(env->regs[12], &sc->regs.r12); + __put_user(env->regs[13], &sc->regs.r13); + __put_user(env->regs[14], &sc->regs.r14); + __put_user(env->regs[15], &sc->regs.r15); + __put_user(env->regs[16], &sc->regs.r16); + __put_user(env->regs[17], &sc->regs.r17); + __put_user(env->regs[18], &sc->regs.r18); + __put_user(env->regs[19], &sc->regs.r19); + __put_user(env->regs[20], &sc->regs.r20); + __put_user(env->regs[21], &sc->regs.r21); + __put_user(env->regs[22], &sc->regs.r22); + __put_user(env->regs[23], &sc->regs.r23); + __put_user(env->regs[24], &sc->regs.r24); + __put_user(env->regs[25], &sc->regs.r25); + __put_user(env->regs[26], &sc->regs.r26); + __put_user(env->regs[27], &sc->regs.r27); + __put_user(env->regs[28], &sc->regs.r28); + __put_user(env->regs[29], &sc->regs.r29); + __put_user(env->regs[30], &sc->regs.r30); + __put_user(env->regs[31], &sc->regs.r31); + __put_user(env->sregs[SR_PC], &sc->regs.pc); +} + +static void restore_sigcontext(struct target_sigcontext *sc, CPUState *env) +{ + __get_user(env->regs[0], &sc->regs.r0); + __get_user(env->regs[1], &sc->regs.r1); + __get_user(env->regs[2], &sc->regs.r2); + __get_user(env->regs[3], &sc->regs.r3); + __get_user(env->regs[4], &sc->regs.r4); + __get_user(env->regs[5], &sc->regs.r5); + __get_user(env->regs[6], &sc->regs.r6); + __get_user(env->regs[7], &sc->regs.r7); + __get_user(env->regs[8], &sc->regs.r8); + __get_user(env->regs[9], &sc->regs.r9); + __get_user(env->regs[10], &sc->regs.r10); + __get_user(env->regs[11], &sc->regs.r11); + __get_user(env->regs[12], &sc->regs.r12); + __get_user(env->regs[13], &sc->regs.r13); + __get_user(env->regs[14], &sc->regs.r14); + __get_user(env->regs[15], &sc->regs.r15); + __get_user(env->regs[16], &sc->regs.r16); + __get_user(env->regs[17], &sc->regs.r17); + __get_user(env->regs[18], &sc->regs.r18); + __get_user(env->regs[19], &sc->regs.r19); + __get_user(env->regs[20], &sc->regs.r20); + __get_user(env->regs[21], &sc->regs.r21); + __get_user(env->regs[22], &sc->regs.r22); + __get_user(env->regs[23], &sc->regs.r23); + __get_user(env->regs[24], &sc->regs.r24); + __get_user(env->regs[25], &sc->regs.r25); + __get_user(env->regs[26], &sc->regs.r26); + __get_user(env->regs[27], &sc->regs.r27); + __get_user(env->regs[28], &sc->regs.r28); + __get_user(env->regs[29], &sc->regs.r29); + __get_user(env->regs[30], &sc->regs.r30); + __get_user(env->regs[31], &sc->regs.r31); + __get_user(env->sregs[SR_PC], &sc->regs.pc); +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPUState *env, int frame_size) +{ + abi_ulong sp = env->regs[1]; + + if ((ka->sa_flags & SA_ONSTACK) != 0 && !on_sig_stack(sp)) + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + + return ((sp - frame_size) & -8UL); +} + +static void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + int err = 0; + int i; + + frame_addr = get_sigframe(ka, env, sizeof *frame); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto badframe; + + /* Save the mask. */ + err |= __put_user(set->sig[0], &frame->sc.oldmask); + if (err) + goto badframe; + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + if (__put_user(set->sig[i], &frame->extramask[i - 1])) + goto badframe; + } + + setup_sigcontext(&frame->sc, env); + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + /* minus 8 is offset to cater for "rtsd r15,8" offset */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[15] = ((unsigned long)ka->sa_restorer)-8; + } else { + uint32_t t; + /* Note, these encodings are _big endian_! */ + /* addi r12, r0, __NR_sigreturn */ + t = 0x31800000UL | TARGET_NR_sigreturn; + err |= __put_user(t, frame->tramp + 0); + /* brki r14, 0x8 */ + t = 0xb9cc0008UL; + err |= __put_user(t, frame->tramp + 1); + + /* Return from sighandler will jump to the tramp. + Negative 8 offset because return is rtsd r15, 8 */ + env->regs[15] = ((unsigned long)frame->tramp) - 8; + } + + if (err) + goto badframe; + + /* Set up registers for signal handler */ + env->regs[1] = (unsigned long) frame; + /* Signal handler args: */ + env->regs[5] = sig; /* Arg 0: signum */ + env->regs[6] = (unsigned long) &frame->sc; /* arg 1: sigcontext */ + + /* Offset of 4 to handle microblaze rtid r14, 0 */ + env->sregs[SR_PC] = (unsigned long)ka->_sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; + badframe: + unlock_user_struct(frame, frame_addr, 1); + force_sig(TARGET_SIGSEGV); +} + +static void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *env) +{ + fprintf(stderr, "Microblaze setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + target_sigset_t target_set; + sigset_t set; + int i; + + frame_addr = env->regs[R_SP]; + /* Make sure the guest isn't playing games. */ + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) + goto badframe; + + /* Restore blocked signals */ + if (__get_user(target_set.sig[0], &frame->sc.oldmask)) + goto badframe; + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + if (__get_user(target_set.sig[i], &frame->extramask[i - 1])) + goto badframe; + } + target_to_host_sigset_internal(&set, &target_set); + sigprocmask(SIG_SETMASK, &set, NULL); + + restore_sigcontext(&frame->sc, env); + /* We got here through a sigreturn syscall, our path back is via an + rtb insn so setup r14 for that. */ + env->regs[14] = env->sregs[SR_PC]; + + unlock_user_struct(frame, frame_addr, 0); + return env->regs[10]; + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); +} + +long do_rt_sigreturn(CPUState *env) +{ + fprintf(stderr, "Microblaze do_rt_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} + #elif defined(TARGET_CRIS) struct target_sigcontext { diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b58220d9a..a0915a455 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4854,7 +4854,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_mmap case TARGET_NR_mmap: -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) { abi_ulong *v; abi_ulong v1, v2, v3, v4, v5, v6; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 7f0b0dff1..e2ba0bbc1 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -59,7 +59,8 @@ #define TARGET_IOC_READ 2U #elif defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ - defined(TARGET_SPARC) || defined(TARGET_MIPS) + defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) || \ + defined(TARGET_MIPS) #define TARGET_IOC_SIZEBITS 13 #define TARGET_IOC_DIRBITS 3 @@ -286,7 +287,7 @@ struct target_sigaction; int do_sigaction(int sig, const struct target_sigaction *act, struct target_sigaction *oact); -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined (TARGET_SH4) || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) +#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined (TARGET_SH4) || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) #if defined(TARGET_SPARC) #define TARGET_SA_NOCLDSTOP 8u @@ -1170,6 +1171,54 @@ struct __attribute__((__packed__)) target_stat64 { unsigned int __unused5; }; +#elif defined(TARGET_MICROBLAZE) + +struct target_stat { + abi_ulong st_dev; + abi_ulong st_ino; + unsigned int st_mode; + unsigned short st_nlink; + unsigned int st_uid; + unsigned int st_gid; + abi_ulong st_rdev; + abi_ulong st_size; + abi_ulong st_blksize; + abi_ulong st_blocks; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; + abi_ulong __unused4; + abi_ulong __unused5; +}; + +/* FIXME: Microblaze no-mmu user-space has a difference stat64 layout... */ +struct __attribute__((__packed__)) target_stat64 { + uint64_t st_dev; + uint64_t st_ino; + uint32_t st_mode; + uint32_t st_nlink; + uint32_t st_uid; + uint32_t st_gid; + uint64_t st_rdev; + uint32_t __pad2; + + int64_t st_size; + int32_t st_blksize; + int64_t st_blocks; /* Number 512-byte blocks allocated. */ + + int target_st_atime; + unsigned int target_st_atime_nsec; + int target_st_mtime; + unsigned int target_st_mtime_nsec; + int target_st_ctime; + unsigned int target_st_ctime_nsec; + uint32_t __unused4; + uint32_t __unused5; +}; + #elif defined(TARGET_M68K) struct target_stat { @@ -1719,6 +1768,24 @@ struct target_statfs64 { #define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ #define TARGET_O_LARGEFILE 0200000 #define TARGET_O_DIRECT 0400000 /* direct disk access hint */ +#elif defined (TARGET_MICROBLAZE) +#define TARGET_O_ACCMODE 0003 +#define TARGET_O_RDONLY 00 +#define TARGET_O_WRONLY 01 +#define TARGET_O_RDWR 02 +#define TARGET_O_CREAT 0100 /* not fcntl */ +#define TARGET_O_EXCL 0200 /* not fcntl */ +#define TARGET_O_NOCTTY 0400 /* not fcntl */ +#define TARGET_O_TRUNC 01000 /* not fcntl */ +#define TARGET_O_APPEND 02000 +#define TARGET_O_NONBLOCK 04000 +#define TARGET_O_NDELAY TARGET_O_NONBLOCK +#define TARGET_O_SYNC 010000 +#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */ +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_LARGEFILE 0200000 +#define TARGET_O_DIRECT 0400000 /* direct disk access hint */ #elif defined (TARGET_SPARC) #define TARGET_O_RDONLY 0x0000 #define TARGET_O_WRONLY 0x0001 @@ -1806,7 +1873,7 @@ struct target_flock { struct target_flock64 { short l_type; short l_whence; -#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) || defined(TARGET_SPARC) || defined(TARGET_HPPA) +#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) || defined(TARGET_SPARC) || defined(TARGET_HPPA) || defined (TARGET_MICROBLAZE) int __pad; #endif unsigned long long l_start; diff --git a/microblaze-dis.c b/microblaze-dis.c new file mode 100755 index 000000000..0caacc0bc --- /dev/null +++ b/microblaze-dis.c @@ -0,0 +1,846 @@ +/* Disassemble Xilinx microblaze instructions. + Copyright (C) 1993, 1999, 2000 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* + * Copyright (c) 2001 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Xilinx, Inc. The name of the Company may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Xilinx, Inc. + */ + + +#include <stdio.h> +#define STATIC_TABLE +#define DEFINE_TABLE + +#ifndef MICROBLAZE_OPC +#define MICROBLAZE_OPC +/* Assembler instructions for Xilinx's microblaze processor + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* + * Copyright (c) 2001 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Xilinx, Inc. The name of the Company may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Xilinx, Inc. + */ + + +#ifndef MICROBLAZE_OPCM +#define MICROBLAZE_OPCM + +/* + * Copyright (c) 2001 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Xilinx, Inc. The name of the Company may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Xilinx, Inc. + * $Header: + */ + +enum microblaze_instr { + add, rsub, addc, rsubc, addk, rsubk, addkc, rsubkc, cmp, cmpu, + addi, rsubi, addic, rsubic, addik, rsubik, addikc, rsubikc, mul, + idiv, idivu, bsll, bsra, bsrl, get, put, nget, nput, cget, cput, + ncget, ncput, muli, bslli, bsrai, bsrli, mului, or, and, xor, + andn, pcmpbf, pcmpbc, pcmpeq, pcmpne, sra, src, srl, sext8, sext16, wic, wdc, mts, mfs, br, brd, + brld, bra, brad, brald, microblaze_brk, beq, beqd, bne, bned, blt, + bltd, ble, bled, bgt, bgtd, bge, bged, ori, andi, xori, andni, + imm, rtsd, rtid, rtbd, rted, bri, brid, brlid, brai, braid, bralid, + brki, beqi, beqid, bnei, bneid, blti, bltid, blei, bleid, bgti, + bgtid, bgei, bgeid, lbu, lhu, lw, sb, sh, sw, lbui, lhui, lwi, + sbi, shi, swi, msrset, msrclr, tuqula, fadd, frsub, fmul, fdiv, + fcmp_lt, fcmp_eq, fcmp_le, fcmp_gt, fcmp_ne, fcmp_ge, fcmp_un, invalid_inst } ; + +enum microblaze_instr_type { + arithmetic_inst, logical_inst, mult_inst, div_inst, branch_inst, + return_inst, immediate_inst, special_inst, memory_load_inst, + memory_store_inst, barrel_shift_inst, anyware_inst }; + +#define INST_WORD_SIZE 4 + +/* gen purpose regs go from 0 to 31 */ +/* mask is reg num - max_reg_num, ie reg_num - 32 in this case */ + +#define REG_PC_MASK 0x8000 +#define REG_MSR_MASK 0x8001 +#define REG_EAR_MASK 0x8003 +#define REG_ESR_MASK 0x8005 +#define REG_FSR_MASK 0x8007 + +#define MIN_REGNUM 0 +#define MAX_REGNUM 31 + +#define REG_PC 32 /* PC */ +#define REG_MSR 33 /* machine status reg */ +#define REG_EAR 35 /* Exception reg */ +#define REG_ESR 37 /* Exception reg */ +#define REG_FSR 39 /* FPU Status reg */ + +/* alternate names for gen purpose regs */ +#define REG_SP 1 /* stack pointer */ +#define REG_ROSDP 2 /* read-only small data pointer */ +#define REG_RWSDP 13 /* read-write small data pointer */ + +/* Assembler Register - Used in Delay Slot Optimization */ +#define REG_AS 18 +#define REG_ZERO 0 + +#define RD_LOW 21 /* low bit for RD */ +#define RA_LOW 16 /* low bit for RA */ +#define RB_LOW 11 /* low bit for RB */ +#define IMM_LOW 0 /* low bit for immediate */ + +#define RD_MASK 0x03E00000 +#define RA_MASK 0x001F0000 +#define RB_MASK 0x0000F800 +#define IMM_MASK 0x0000FFFF + +// imm mask for barrel shifts +#define IMM5_MASK 0x0000001F + + +// imm mask for get, put instructions +#define IMM12_MASK 0x00000FFF + +// imm mask for msrset, msrclr instructions +#define IMM14_MASK 0x00003FFF + +#endif /* MICROBLAZE-OPCM */ + +#define INST_TYPE_RD_R1_R2 0 +#define INST_TYPE_RD_R1_IMM 1 +#define INST_TYPE_RD_R1_UNSIGNED_IMM 2 +#define INST_TYPE_RD_R1 3 +#define INST_TYPE_RD_R2 4 +#define INST_TYPE_RD_IMM 5 +#define INST_TYPE_R2 6 +#define INST_TYPE_R1_R2 7 +#define INST_TYPE_R1_IMM 8 +#define INST_TYPE_IMM 9 +#define INST_TYPE_SPECIAL_R1 10 +#define INST_TYPE_RD_SPECIAL 11 +#define INST_TYPE_R1 12 + // new instn type for barrel shift imms +#define INST_TYPE_RD_R1_IMM5 13 +#define INST_TYPE_RD_IMM12 14 +#define INST_TYPE_R1_IMM12 15 + + // new insn type for insn cache +#define INST_TYPE_RD_R1_SPECIAL 16 + +// new insn type for msrclr, msrset insns. +#define INST_TYPE_RD_IMM14 17 + +// new insn type for tuqula rd - addik rd, r0, 42 +#define INST_TYPE_RD 18 + +#define INST_TYPE_NONE 25 + + + +#define INST_PC_OFFSET 1 /* instructions where the label address is resolved as a PC offset (for branch label)*/ +#define INST_NO_OFFSET 0 /* instructions where the label address is resolved as an absolute value (for data mem or abs address)*/ + +#define IMMVAL_MASK_NON_SPECIAL 0x0000 +#define IMMVAL_MASK_MTS 0x4000 +#define IMMVAL_MASK_MFS 0x0000 + +#define OPCODE_MASK_H 0xFC000000 /* High 6 bits only */ +#define OPCODE_MASK_H1 0xFFE00000 /* High 11 bits */ +#define OPCODE_MASK_H2 0xFC1F0000 /* High 6 and bits 20-16 */ +#define OPCODE_MASK_H12 0xFFFF0000 /* High 16 */ +#define OPCODE_MASK_H4 0xFC0007FF /* High 6 and low 11 bits */ +#define OPCODE_MASK_H13S 0xFFE0FFF0 /* High 11 and 15:1 bits and last nibble of last byte for spr */ +#define OPCODE_MASK_H23S 0xFC1FFFF0 /* High 6, 20-16 and 15:1 bits and last nibble of last byte for spr */ +#define OPCODE_MASK_H34 0xFC00FFFF /* High 6 and low 16 bits */ +#define OPCODE_MASK_H14 0xFFE007FF /* High 11 and low 11 bits */ +#define OPCODE_MASK_H24 0xFC1F07FF /* High 6, bits 20-16 and low 11 bits */ +#define OPCODE_MASK_H124 0xFFFF07FF /* High 16, and low 11 bits */ +#define OPCODE_MASK_H1234 0xFFFFFFFF /* All 32 bits */ +#define OPCODE_MASK_H3 0xFC000600 /* High 6 bits and bits 21, 22 */ +#define OPCODE_MASK_H32 0xFC00F000 /* High 6 bits and bit 16, 17, 18 and 19*/ +#define OPCODE_MASK_H34B 0xFC0000FF /* High 6 bits and low 8 bits */ + +// New Mask for msrset, msrclr insns. +#define OPCODE_MASK_H23N 0xFC1FC000 /* High 6 and bits 12 - 18 */ + +#define DELAY_SLOT 1 +#define NO_DELAY_SLOT 0 + +#define MAX_OPCODES 149 + +struct op_code_struct { + const char *name; + short inst_type; /* registers and immediate values involved */ + short inst_offset_type; /* immediate vals offset from PC? (= 1 for branches) */ + short delay_slots; /* info about delay slots needed after this instr. */ + short immval_mask; + unsigned long bit_sequence; /* all the fixed bits for the op are set and all the variable bits (reg names, imm vals) are set to 0 */ + unsigned long opcode_mask; /* which bits define the opcode */ + enum microblaze_instr instr; + enum microblaze_instr_type instr_type; + /* more info about output format here */ +} opcodes[MAX_OPCODES] = + +{ + {"add", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x00000000, OPCODE_MASK_H4, add, arithmetic_inst }, + {"rsub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H4, rsub, arithmetic_inst }, + {"addc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x08000000, OPCODE_MASK_H4, addc, arithmetic_inst }, + {"rsubc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x0C000000, OPCODE_MASK_H4, rsubc, arithmetic_inst }, + {"addk", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x10000000, OPCODE_MASK_H4, addk, arithmetic_inst }, + {"rsubk", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000000, OPCODE_MASK_H4, rsubk, arithmetic_inst }, + {"cmp", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000001, OPCODE_MASK_H4, cmp, arithmetic_inst }, + {"cmpu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000003, OPCODE_MASK_H4, cmpu, arithmetic_inst }, + {"addkc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x18000000, OPCODE_MASK_H4, addkc, arithmetic_inst }, + {"rsubkc",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x1C000000, OPCODE_MASK_H4, rsubkc, arithmetic_inst }, + {"addi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x20000000, OPCODE_MASK_H, addi, arithmetic_inst }, + {"rsubi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x24000000, OPCODE_MASK_H, rsubi, arithmetic_inst }, + {"addic", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x28000000, OPCODE_MASK_H, addic, arithmetic_inst }, + {"rsubic",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x2C000000, OPCODE_MASK_H, rsubic, arithmetic_inst }, + {"addik", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x30000000, OPCODE_MASK_H, addik, arithmetic_inst }, + {"rsubik",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x34000000, OPCODE_MASK_H, rsubik, arithmetic_inst }, + {"addikc",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x38000000, OPCODE_MASK_H, addikc, arithmetic_inst }, + {"rsubikc",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x3C000000, OPCODE_MASK_H, rsubikc, arithmetic_inst }, + {"mul", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000000, OPCODE_MASK_H4, mul, mult_inst }, + {"idiv", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x48000000, OPCODE_MASK_H4, idiv, div_inst }, + {"idivu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x48000002, OPCODE_MASK_H4, idivu, div_inst }, + {"bsll", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000400, OPCODE_MASK_H3, bsll, barrel_shift_inst }, + {"bsra", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000200, OPCODE_MASK_H3, bsra, barrel_shift_inst }, + {"bsrl", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000000, OPCODE_MASK_H3, bsrl, barrel_shift_inst }, + {"get", INST_TYPE_RD_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000000, OPCODE_MASK_H32, get, anyware_inst }, + {"put", INST_TYPE_R1_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008000, OPCODE_MASK_H32, put, anyware_inst }, + {"nget", INST_TYPE_RD_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004000, OPCODE_MASK_H32, nget, anyware_inst }, + {"nput", INST_TYPE_R1_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C000, OPCODE_MASK_H32, nput, anyware_inst }, + {"cget", INST_TYPE_RD_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002000, OPCODE_MASK_H32, cget, anyware_inst }, + {"cput", INST_TYPE_R1_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A000, OPCODE_MASK_H32, cput, anyware_inst }, + {"ncget", INST_TYPE_RD_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006000, OPCODE_MASK_H32, ncget, anyware_inst }, + {"ncput", INST_TYPE_R1_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E000, OPCODE_MASK_H32, ncput, anyware_inst }, + {"muli", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x60000000, OPCODE_MASK_H, muli, mult_inst }, + {"bslli", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000400, OPCODE_MASK_H3, bslli, barrel_shift_inst }, + {"bsrai", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000200, OPCODE_MASK_H3, bsrai, barrel_shift_inst }, + {"bsrli", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000000, OPCODE_MASK_H3, bsrli, barrel_shift_inst }, + {"or", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000000, OPCODE_MASK_H4, or, logical_inst }, + {"and", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x84000000, OPCODE_MASK_H4, and, logical_inst }, + {"xor", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x88000000, OPCODE_MASK_H4, xor, logical_inst }, + {"andn", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x8C000000, OPCODE_MASK_H4, andn, logical_inst }, + {"pcmpbf",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000400, OPCODE_MASK_H4, pcmpbf, logical_inst }, + {"pcmpbc",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x84000400, OPCODE_MASK_H4, pcmpbc, logical_inst }, + {"pcmpeq",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x88000400, OPCODE_MASK_H4, pcmpeq, logical_inst }, + {"pcmpne",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x8C000400, OPCODE_MASK_H4, pcmpne, logical_inst }, + {"sra", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000001, OPCODE_MASK_H34, sra, logical_inst }, + {"src", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000021, OPCODE_MASK_H34, src, logical_inst }, + {"srl", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000041, OPCODE_MASK_H34, srl, logical_inst }, + {"sext8", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000060, OPCODE_MASK_H34, sext8, logical_inst }, + {"sext16",INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000061, OPCODE_MASK_H34, sext16, logical_inst }, + {"wic", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000068, OPCODE_MASK_H34B, wic, special_inst }, + {"wdc", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000064, OPCODE_MASK_H34B, wdc, special_inst }, + {"mts", INST_TYPE_SPECIAL_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MTS, 0x9400C000, OPCODE_MASK_H13S, mts, special_inst }, + {"mfs", INST_TYPE_RD_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MFS, 0x94008000, OPCODE_MASK_H23S, mfs, special_inst }, + {"br", INST_TYPE_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98000000, OPCODE_MASK_H124, br, branch_inst }, + {"brd", INST_TYPE_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98100000, OPCODE_MASK_H124, brd, branch_inst }, + {"brld", INST_TYPE_RD_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98140000, OPCODE_MASK_H24, brld, branch_inst }, + {"bra", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98080000, OPCODE_MASK_H124, bra, branch_inst }, + {"brad", INST_TYPE_R2, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98180000, OPCODE_MASK_H124, brad, branch_inst }, + {"brald", INST_TYPE_RD_R2, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x981C0000, OPCODE_MASK_H24, brald, branch_inst }, + {"brk", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x980C0000, OPCODE_MASK_H24, microblaze_brk, branch_inst }, + {"beq", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C000000, OPCODE_MASK_H14, beq, branch_inst }, + {"beqd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E000000, OPCODE_MASK_H14, beqd, branch_inst }, + {"bne", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C200000, OPCODE_MASK_H14, bne, branch_inst }, + {"bned", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E200000, OPCODE_MASK_H14, bned, branch_inst }, + {"blt", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C400000, OPCODE_MASK_H14, blt, branch_inst }, + {"bltd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E400000, OPCODE_MASK_H14, bltd, branch_inst }, + {"ble", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C600000, OPCODE_MASK_H14, ble, branch_inst }, + {"bled", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E600000, OPCODE_MASK_H14, bled, branch_inst }, + {"bgt", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C800000, OPCODE_MASK_H14, bgt, branch_inst }, + {"bgtd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E800000, OPCODE_MASK_H14, bgtd, branch_inst }, + {"bge", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9CA00000, OPCODE_MASK_H14, bge, branch_inst }, + {"bged", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9EA00000, OPCODE_MASK_H14, bged, branch_inst }, + {"ori", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA0000000, OPCODE_MASK_H, ori, logical_inst }, + {"andi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA4000000, OPCODE_MASK_H, andi, logical_inst }, + {"xori", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA8000000, OPCODE_MASK_H, xori, logical_inst }, + {"andni", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xAC000000, OPCODE_MASK_H, andni, logical_inst }, + {"imm", INST_TYPE_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB0000000, OPCODE_MASK_H12, imm, immediate_inst }, + {"rtsd", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6000000, OPCODE_MASK_H1, rtsd, return_inst }, + {"rtid", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6200000, OPCODE_MASK_H1, rtid, return_inst }, + {"rtbd", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6400000, OPCODE_MASK_H1, rtbd, return_inst }, + {"rted", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6800000, OPCODE_MASK_H1, rted, return_inst }, + {"bri", INST_TYPE_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8000000, OPCODE_MASK_H12, bri, branch_inst }, + {"brid", INST_TYPE_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8100000, OPCODE_MASK_H12, brid, branch_inst }, + {"brlid", INST_TYPE_RD_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8140000, OPCODE_MASK_H2, brlid, branch_inst }, + {"brai", INST_TYPE_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8080000, OPCODE_MASK_H12, brai, branch_inst }, + {"braid", INST_TYPE_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8180000, OPCODE_MASK_H12, braid, branch_inst }, + {"bralid",INST_TYPE_RD_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB81C0000, OPCODE_MASK_H2, bralid, branch_inst }, + {"brki", INST_TYPE_RD_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB80C0000, OPCODE_MASK_H2, brki, branch_inst }, + {"beqi", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC000000, OPCODE_MASK_H1, beqi, branch_inst }, + {"beqid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE000000, OPCODE_MASK_H1, beqid, branch_inst }, + {"bnei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC200000, OPCODE_MASK_H1, bnei, branch_inst }, + {"bneid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE200000, OPCODE_MASK_H1, bneid, branch_inst }, + {"blti", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC400000, OPCODE_MASK_H1, blti, branch_inst }, + {"bltid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE400000, OPCODE_MASK_H1, bltid, branch_inst }, + {"blei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC600000, OPCODE_MASK_H1, blei, branch_inst }, + {"bleid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE600000, OPCODE_MASK_H1, bleid, branch_inst }, + {"bgti", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC800000, OPCODE_MASK_H1, bgti, branch_inst }, + {"bgtid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE800000, OPCODE_MASK_H1, bgtid, branch_inst }, + {"bgei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBCA00000, OPCODE_MASK_H1, bgei, branch_inst }, + {"bgeid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBEA00000, OPCODE_MASK_H1, bgeid, branch_inst }, + {"lbu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC0000000, OPCODE_MASK_H4, lbu, memory_load_inst }, + {"lhu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC4000000, OPCODE_MASK_H4, lhu, memory_load_inst }, + {"lw", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC8000000, OPCODE_MASK_H4, lw, memory_load_inst }, + {"sb", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD0000000, OPCODE_MASK_H4, sb, memory_store_inst }, + {"sh", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD4000000, OPCODE_MASK_H4, sh, memory_store_inst }, + {"sw", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD8000000, OPCODE_MASK_H4, sw, memory_store_inst }, + {"lbui", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE0000000, OPCODE_MASK_H, lbui, memory_load_inst }, + {"lhui", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE4000000, OPCODE_MASK_H, lhui, memory_load_inst }, + {"lwi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE8000000, OPCODE_MASK_H, lwi, memory_load_inst }, + {"sbi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF0000000, OPCODE_MASK_H, sbi, memory_store_inst }, + {"shi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF4000000, OPCODE_MASK_H, shi, memory_store_inst }, + {"swi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF8000000, OPCODE_MASK_H, swi, memory_store_inst }, + {"nop", INST_TYPE_NONE, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000000, OPCODE_MASK_H1234, invalid_inst, logical_inst }, /* translates to or r0, r0, r0 */ + {"la", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x30000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* la translates to addik */ + {"tuqula",INST_TYPE_RD, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x3000002A, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* tuqula rd translates to addik rd, r0, 42 */ + {"not", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA800FFFF, OPCODE_MASK_H34, invalid_inst, logical_inst }, /* not translates to xori rd,ra,-1 */ + {"neg", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* neg translates to rsub rd, ra, r0 */ + {"rtb", INST_TYPE_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6000004, OPCODE_MASK_H1, invalid_inst, return_inst }, /* rtb translates to rts rd, 4 */ + {"sub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* sub translates to rsub rd, rb, ra */ + {"lmi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE8000000, OPCODE_MASK_H, invalid_inst, memory_load_inst }, + {"smi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF8000000, OPCODE_MASK_H, invalid_inst, memory_store_inst }, + {"msrset",INST_TYPE_RD_IMM14, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94100000, OPCODE_MASK_H23N, msrset, special_inst }, + {"msrclr",INST_TYPE_RD_IMM14, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94110000, OPCODE_MASK_H23N, msrclr, special_inst }, + {"fadd", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000000, OPCODE_MASK_H4, fadd, arithmetic_inst }, + {"frsub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000080, OPCODE_MASK_H4, frsub, arithmetic_inst }, + {"fmul", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000100, OPCODE_MASK_H4, fmul, arithmetic_inst }, + {"fdiv", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000180, OPCODE_MASK_H4, fdiv, arithmetic_inst }, + {"fcmp.lt", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000210, OPCODE_MASK_H4, fcmp_lt, arithmetic_inst }, + {"fcmp.eq", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000220, OPCODE_MASK_H4, fcmp_eq, arithmetic_inst }, + {"fcmp.le", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000230, OPCODE_MASK_H4, fcmp_le, arithmetic_inst }, + {"fcmp.gt", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000240, OPCODE_MASK_H4, fcmp_gt, arithmetic_inst }, + {"fcmp.ne", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000250, OPCODE_MASK_H4, fcmp_ne, arithmetic_inst }, + {"fcmp.ge", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000260, OPCODE_MASK_H4, fcmp_ge, arithmetic_inst }, + {"fcmp.un", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000200, OPCODE_MASK_H4, fcmp_un, arithmetic_inst }, + {""} +}; + +/* prefix for register names */ +char register_prefix[] = "r"; +char special_register_prefix[] = "spr"; +char fsl_register_prefix[] = "rfsl"; + + +/* #defines for valid immediate range */ +#define MIN_IMM 0x80000000 +#define MAX_IMM 0x7fffffff + +#define MIN_IMM12 0x000 +#define MAX_IMM12 0x7ff + +#define MIN_IMM14 0x0000 +#define MAX_IMM14 0x1fff + +#endif /* MICROBLAZE_OPC */ + +#include "dis-asm.h" +#include <strings.h> + +#define get_field_rd(instr) get_field(instr, RD_MASK, RD_LOW) +#define get_field_r1(instr) get_field(instr, RA_MASK, RA_LOW) +#define get_field_r2(instr) get_field(instr, RB_MASK, RB_LOW) +#define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) +#define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) + +static char * +get_field (long instr, long mask, unsigned short low) +{ + char tmpstr[25]; + sprintf(tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low)); + return(strdup(tmpstr)); +} + +static char * +get_field_imm (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} + +static char * +get_field_imm5 (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} + +static char * +get_field_imm12 (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%s%d", fsl_register_prefix, (short)((instr & IMM12_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} + +static char * +get_field_imm14 (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%d", (short)((instr & IMM14_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} + +#if 0 +static char * +get_field_unsigned_imm (long instr) +{ + char tmpstr[25]; + sprintf(tmpstr, "%d", (int)((instr & IMM_MASK) >> IMM_LOW)); + return(strdup(tmpstr)); +} +#endif + +/* + char * + get_field_special (instr) + long instr; + { + char tmpstr[25]; + + sprintf(tmpstr, "%s%s", register_prefix, (((instr & IMM_MASK) >> IMM_LOW) & REG_MSR_MASK) == 0 ? "pc" : "msr"); + + return(strdup(tmpstr)); + } +*/ + +static char * +get_field_special (long instr, struct op_code_struct * op) +{ + char tmpstr[25]; + char spr[5]; + + switch ( (((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ) { + case REG_MSR_MASK : + strcpy(spr, "msr"); + break; + case REG_PC_MASK : + strcpy(spr, "pc"); + break; + case REG_EAR_MASK : + strcpy(spr, "ear"); + break; + case REG_ESR_MASK : + strcpy(spr, "esr"); + break; + case REG_FSR_MASK : + strcpy(spr, "fsr"); + break; + default : + strcpy(spr, "pc"); + break; + } + + sprintf(tmpstr, "%s%s", register_prefix, spr); + return(strdup(tmpstr)); +} + +static unsigned long +read_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info, + struct op_code_struct ** opr) +{ + unsigned char ibytes[4]; + int status; + struct op_code_struct * op; + unsigned long inst; + + status = info->read_memory_func (memaddr, ibytes, 4, info); + + if (status != 0) + { + info->memory_error_func (status, memaddr, info); + return 0; + } + + if (info->endian == BFD_ENDIAN_BIG) + inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3]; + else if (info->endian == BFD_ENDIAN_LITTLE) + inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0]; + else + abort (); + + /* Just a linear search of the table. */ + for (op = opcodes; op->name != 0; op ++) + if (op->bit_sequence == (inst & op->opcode_mask)) + break; + + *opr = op; + return inst; +} + + +int +print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) +{ + fprintf_ftype fprintf = info->fprintf_func; + void * stream = info->stream; + unsigned long inst, prev_inst; + struct op_code_struct * op, *pop; + int immval = 0; + boolean immfound = false; + static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */ + static int prev_insn_vma = -1; /*init the prev insn vma */ + int curr_insn_vma = info->buffer_vma; + + info->bytes_per_chunk = 4; + + inst = read_insn_microblaze (memaddr, info, &op); + if (inst == 0) + return -1; + + if (prev_insn_vma == curr_insn_vma) { + if (memaddr-(info->bytes_per_chunk) == prev_insn_addr) { + prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop); + if (prev_inst == 0) + return -1; + if (pop->instr == imm) { + immval = (get_int_field_imm(prev_inst) << 16) & 0xffff0000; + immfound = true; + } + else { + immval = 0; + immfound = false; + } + } + } + /* make curr insn as prev insn */ + prev_insn_addr = memaddr; + prev_insn_vma = curr_insn_vma; + + if (op->name == 0) + fprintf (stream, ".short 0x%04x", inst); + else + { + fprintf (stream, "%s", op->name); + + switch (op->inst_type) + { + case INST_TYPE_RD_R1_R2: + fprintf(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_r2(inst)); + break; + case INST_TYPE_RD_R1_IMM: + fprintf(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); + if (info->print_address_func && get_int_field_r1(inst) == 0 && info->symbol_at_address_func) { + if (immfound) + immval |= (get_int_field_imm(inst) & 0x0000ffff); + else { + immval = get_int_field_imm(inst); + if (immval & 0x8000) + immval |= 0xFFFF0000; + } + if (immval > 0 && info->symbol_at_address_func(immval, info)) { + fprintf (stream, "\t// "); + info->print_address_func (immval, info); + } + } + break; + case INST_TYPE_RD_R1_IMM5: + fprintf(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); + break; + case INST_TYPE_RD_IMM12: + fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_imm12(inst)); + break; + case INST_TYPE_R1_IMM12: + fprintf(stream, "\t%s, %s", get_field_r1(inst), get_field_imm12(inst)); + break; + case INST_TYPE_RD_SPECIAL: + fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_special(inst, op)); + break; + case INST_TYPE_SPECIAL_R1: + fprintf(stream, "\t%s, %s", get_field_special(inst, op), get_field_r1(inst)); + break; + case INST_TYPE_RD_R1: + fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_r1(inst)); + break; + case INST_TYPE_R1_R2: + fprintf(stream, "\t%s, %s", get_field_r1(inst), get_field_r2(inst)); + break; + case INST_TYPE_R1_IMM: + fprintf(stream, "\t%s, %s", get_field_r1(inst), get_field_imm(inst)); + /* The non-pc relative instructions are returns, which shouldn't + have a label printed */ + if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET && info->symbol_at_address_func) { + if (immfound) + immval |= (get_int_field_imm(inst) & 0x0000ffff); + else { + immval = get_int_field_imm(inst); + if (immval & 0x8000) + immval |= 0xFFFF0000; + } + immval += memaddr; + if (immval > 0 && info->symbol_at_address_func(immval, info)) { + fprintf (stream, "\t// "); + info->print_address_func (immval, info); + } else { + fprintf (stream, "\t\t// "); + fprintf (stream, "%x", immval); + } + } + break; + case INST_TYPE_RD_IMM: + fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_imm(inst)); + if (info->print_address_func && info->symbol_at_address_func) { + if (immfound) + immval |= (get_int_field_imm(inst) & 0x0000ffff); + else { + immval = get_int_field_imm(inst); + if (immval & 0x8000) + immval |= 0xFFFF0000; + } + if (op->inst_offset_type == INST_PC_OFFSET) + immval += (int) memaddr; + if (info->symbol_at_address_func(immval, info)) { + fprintf (stream, "\t// "); + info->print_address_func (immval, info); + } + } + break; + case INST_TYPE_IMM: + fprintf(stream, "\t%s", get_field_imm(inst)); + if (info->print_address_func && info->symbol_at_address_func && op->instr != imm) { + if (immfound) + immval |= (get_int_field_imm(inst) & 0x0000ffff); + else { + immval = get_int_field_imm(inst); + if (immval & 0x8000) + immval |= 0xFFFF0000; + } + if (op->inst_offset_type == INST_PC_OFFSET) + immval += (int) memaddr; + if (immval > 0 && info->symbol_at_address_func(immval, info)) { + fprintf (stream, "\t// "); + info->print_address_func (immval, info); + } else if (op->inst_offset_type == INST_PC_OFFSET) { + fprintf (stream, "\t\t// "); + fprintf (stream, "%x", immval); + } + } + break; + case INST_TYPE_RD_R2: + fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); + break; + case INST_TYPE_R2: + fprintf(stream, "\t%s", get_field_r2(inst)); + break; + case INST_TYPE_R1: + fprintf(stream, "\t%s", get_field_r1(inst)); + break; + case INST_TYPE_RD_R1_SPECIAL: + fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); + break; + case INST_TYPE_RD_IMM14: + fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_imm14(inst)); + break; + /* For tuqula instruction */ + case INST_TYPE_RD: + fprintf(stream, "\t%s", get_field_rd(inst)); + break; + + default: + /* if the disassembler lags the instruction set */ + fprintf (stream, "\tundecoded operands, inst is 0x%04x", inst); + break; + } + } + + /* Say how many bytes we consumed? */ + return 4; +} + +#if 0 +static enum microblaze_instr +get_insn_microblaze (long inst, boolean *isunsignedimm, + enum microblaze_instr_type *insn_type, + short *delay_slots ) +{ + struct op_code_struct * op; + *isunsignedimm = false; + + /* Just a linear search of the table. */ + for (op = opcodes; op->name != 0; op ++) + if (op->bit_sequence == (inst & op->opcode_mask)) + break; + + if (op->name == 0) + return invalid_inst; + else { + *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM); + *insn_type = op->instr_type; + *delay_slots = op->delay_slots; + return op->instr; + } +} +#endif + +#if 0 +static short +get_delay_slots_microblaze ( long inst ) +{ + boolean isunsignedimm; + enum microblaze_instr_type insn_type; + enum microblaze_instr op; + short delay_slots; + + op = get_insn_microblaze( inst, &isunsignedimm, &insn_type, &delay_slots); + if (op == invalid_inst) + return 0; + else + return delay_slots; +} +#endif + +#if 0 +static enum microblaze_instr +microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *imm) +{ + enum microblaze_instr op; + boolean t1; + enum microblaze_instr_type t2; + short t3; + + op = get_insn_microblaze(insn, &t1, &t2, &t3); + *rd = (insn & RD_MASK) >> RD_LOW; + *ra = (insn & RA_MASK) >> RA_LOW; + *rb = (insn & RB_MASK) >> RB_LOW; + t3 = (insn & IMM_MASK) >> IMM_LOW; + *imm = (int) t3; + return (op); +} +#endif + +#if 0 +static unsigned long +microblaze_get_target_address (long inst, boolean immfound, int immval, + long pcval, long r1val, long r2val, + boolean *targetvalid, + boolean *unconditionalbranch) +{ + struct op_code_struct * op; + long targetaddr = 0; + + *unconditionalbranch = false; + /* Just a linear search of the table. */ + for (op = opcodes; op->name != 0; op ++) + if (op->bit_sequence == (inst & op->opcode_mask)) + break; + + if (op->name == 0) { + *targetvalid = false; + } else if (op->instr_type == branch_inst) { + switch (op->inst_type) { + case INST_TYPE_R2: + *unconditionalbranch = true; + /* fallthru */ + case INST_TYPE_RD_R2: + case INST_TYPE_R1_R2: + targetaddr = r2val; + *targetvalid = true; + if (op->inst_offset_type == INST_PC_OFFSET) + targetaddr += pcval; + break; + case INST_TYPE_IMM: + *unconditionalbranch = true; + /* fallthru */ + case INST_TYPE_RD_IMM: + case INST_TYPE_R1_IMM: + if (immfound) { + targetaddr = (immval << 16) & 0xffff0000; + targetaddr |= (get_int_field_imm(inst) & 0x0000ffff); + } else { + targetaddr = get_int_field_imm(inst); + if (targetaddr & 0x8000) + targetaddr |= 0xFFFF0000; + } + if (op->inst_offset_type == INST_PC_OFFSET) + targetaddr += pcval; + *targetvalid = true; + break; + default: + *targetvalid = false; + break; + } + } else if (op->instr_type == return_inst) { + if (immfound) { + targetaddr = (immval << 16) & 0xffff0000; + targetaddr |= (get_int_field_imm(inst) & 0x0000ffff); + } else { + targetaddr = get_int_field_imm(inst); + if (targetaddr & 0x8000) + targetaddr |= 0xFFFF0000; + } + targetaddr += r1val; + *targetvalid = true; + } else { + *targetvalid = false; + } + return targetaddr; +} +#endif diff --git a/migration-exec.c b/migration-exec.c index f0869a114..0dd5aff31 100644 --- a/migration-exec.c +++ b/migration-exec.c @@ -104,17 +104,11 @@ err_after_alloc: return NULL; } -int exec_start_incoming_migration(const char *command) +static void exec_accept_incoming_migration(void *opaque) { + QEMUFile *f = opaque; int ret; - QEMUFile *f; - dprintf("Attempting to start an incoming migration\n"); - f = qemu_popen_cmd(command, "r"); - if(f == NULL) { - dprintf("Unable to apply qemu wrapper to popen file\n"); - return -errno; - } vm_stop(0); /* just in case */ ret = qemu_loadvm_state(f); if (ret < 0) { @@ -123,11 +117,28 @@ int exec_start_incoming_migration(const char *command) } qemu_announce_self(); dprintf("successfully loaded vm state\n"); + /* we've successfully migrated, close the fd */ + qemu_set_fd_handler2(qemu_popen_fd(f), NULL, NULL, NULL, NULL); vm_start(); - qemu_fclose(f); - return 0; err: qemu_fclose(f); - return -errno; +} + +int exec_start_incoming_migration(const char *command) +{ + QEMUFile *f; + + dprintf("Attempting to start an incoming migration\n"); + f = qemu_popen_cmd(command, "r"); + if(f == NULL) { + dprintf("Unable to apply qemu wrapper to popen file\n"); + return -errno; + } + + qemu_set_fd_handler2(qemu_popen_fd(f), NULL, + exec_accept_incoming_migration, NULL, + (void *)(unsigned long)f); + + return 0; } @@ -1783,8 +1783,10 @@ static const mon_cmd_t mon_cmds[] = { { "host_net_remove", "is", net_host_device_remove, "vlan_id name", "remove host VLAN client" }, #ifdef CONFIG_SLIRP - { "host_net_redir", "s", net_slirp_redir, - "[tcp|udp]:host-port:[guest-host]:guest-port", "redirect TCP or UDP connections from host to guest (requires -net user)" }, + { "host_net_redir", "ss?", net_slirp_redir, + "[tcp|udp]:host-port:[guest-host]:guest-port", "redirect TCP or UDP connections from host to guest (requires -net user)\n" + "host_net_redir remove [tcp:|udp:]host-port -- remove redirection\n" + "host_net_redir list -- show all redirections" }, #endif { "balloon", "i", do_balloon, "target", "request VM to change it's memory allocation (in MB)" }, @@ -603,7 +603,82 @@ static int net_slirp_init(VLANState *vlan, const char *model, const char *name) return 0; } -void net_slirp_redir(Monitor *mon, const char *redir_str) +static void net_slirp_redir_print(void *opaque, int is_udp, + struct in_addr *laddr, u_int lport, + struct in_addr *faddr, u_int fport) +{ + Monitor *mon = (Monitor *)opaque; + uint32_t h_addr; + uint32_t g_addr; + char buf[16]; + + h_addr = ntohl(faddr->s_addr); + g_addr = ntohl(laddr->s_addr); + + monitor_printf(mon, " %s |", is_udp ? "udp" : "tcp" ); + snprintf(buf, 15, "%d.%d.%d.%d", (h_addr >> 24) & 0xff, + (h_addr >> 16) & 0xff, + (h_addr >> 8) & 0xff, + (h_addr) & 0xff); + monitor_printf(mon, " %15s |", buf); + monitor_printf(mon, " %5d |", fport); + + snprintf(buf, 15, "%d.%d.%d.%d", (g_addr >> 24) & 0xff, + (g_addr >> 16) & 0xff, + (g_addr >> 8) & 0xff, + (g_addr) & 0xff); + monitor_printf(mon, " %15s |", buf); + monitor_printf(mon, " %5d\n", lport); + +} + +static void net_slirp_redir_list(Monitor *mon) +{ + if (!mon) + return; + + monitor_printf(mon, " Prot | Host Addr | HPort | Guest Addr | GPort\n"); + monitor_printf(mon, " | | | | \n"); + slirp_redir_loop(net_slirp_redir_print, mon); +} + +static void net_slirp_redir_rm(Monitor *mon, const char *port_str) +{ + int host_port; + char buf[256] = ""; + const char *p = port_str; + int is_udp = 0; + int n; + + if (!mon) + return; + + if (!port_str || !port_str[0]) + goto fail_syntax; + + get_str_sep(buf, sizeof(buf), &p, ':'); + + if (!strcmp(buf, "tcp") || buf[0] == '\0') { + is_udp = 0; + } else if (!strcmp(buf, "udp")) { + is_udp = 1; + } else { + goto fail_syntax; + } + + host_port = atoi(p); + + n = slirp_redir_rm(is_udp, host_port); + + monitor_printf(mon, "removed %d redirections to %s port %d\n", n, + is_udp ? "udp" : "tcp", host_port); + return; + + fail_syntax: + monitor_printf(mon, "invalid format\n"); +} + +void net_slirp_redir(Monitor *mon, const char *redir_str, const char *redir_opt2) { int is_udp; char buf[256], *r; @@ -616,6 +691,16 @@ void net_slirp_redir(Monitor *mon, const char *redir_str) slirp_init(slirp_restrict, slirp_ip); } + if (!strcmp(redir_str, "remove")) { + net_slirp_redir_rm(mon, redir_opt2); + return; + } + + if (!strcmp(redir_str, "list")) { + net_slirp_redir_list(mon); + return; + } + p = redir_str; if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) goto fail_syntax; @@ -2027,8 +2112,7 @@ int net_client_init(const char *device, const char *p) int idx = nic_get_free_idx(); if (check_params(nic_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } if (idx == -1 || nb_nics >= MAX_NICS) { @@ -2078,8 +2162,7 @@ int net_client_init(const char *device, const char *p) "vlan", "name", "hostname", "restrict", "ip", NULL }; if (check_params(slirp_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } if (get_param_value(buf, sizeof(buf), "hostname", p)) { @@ -2129,8 +2212,7 @@ int net_client_init(const char *device, const char *p) char ifname[64]; if (check_params(tap_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) { @@ -2150,8 +2232,7 @@ int net_client_init(const char *device, const char *p) vlan->nb_host_devs++; if (get_param_value(buf, sizeof(buf), "fd", p) > 0) { if (check_params(fd_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } fd = strtol(buf, NULL, 0); @@ -2163,8 +2244,7 @@ int net_client_init(const char *device, const char *p) "vlan", "name", "ifname", "script", "downscript", NULL }; if (check_params(tap_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) { @@ -2184,8 +2264,7 @@ int net_client_init(const char *device, const char *p) if (get_param_value(buf, sizeof(buf), "fd", p) > 0) { int fd; if (check_params(fd_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } fd = strtol(buf, NULL, 0); @@ -2197,8 +2276,7 @@ int net_client_init(const char *device, const char *p) "vlan", "name", "listen", NULL }; if (check_params(listen_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } ret = net_socket_listen_init(vlan, device, name, buf); @@ -2207,8 +2285,7 @@ int net_client_init(const char *device, const char *p) "vlan", "name", "connect", NULL }; if (check_params(connect_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } ret = net_socket_connect_init(vlan, device, name, buf); @@ -2217,8 +2294,7 @@ int net_client_init(const char *device, const char *p) "vlan", "name", "mcast", NULL }; if (check_params(mcast_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } ret = net_socket_mcast_init(vlan, device, name, buf); @@ -2238,8 +2314,7 @@ int net_client_init(const char *device, const char *p) int vde_port, vde_mode; if (check_params(vde_params, p) < 0) { - fprintf(stderr, "qemu: invalid parameter '%s' in '%s'\n", - buf, p); + fprintf(stderr, "qemu: invalid parameter in '%s'\n", p); return -1; } vlan->nb_host_devs++; @@ -119,7 +119,7 @@ int net_client_init(const char *device, const char *p); void net_client_uninit(NICInfo *nd); int net_client_parse(const char *str); void net_slirp_smb(const char *exported_dir); -void net_slirp_redir(Monitor *mon, const char *redir_str); +void net_slirp_redir(Monitor *mon, const char *redir_str, const char *redir_opt2); void net_cleanup(void); int slirp_is_inited(void); void net_client_check(void); diff --git a/keymaps/ar b/pc-bios/keymaps/ar index c430c03bb..c430c03bb 100644 --- a/keymaps/ar +++ b/pc-bios/keymaps/ar diff --git a/keymaps/common b/pc-bios/keymaps/common index adc56c77d..adc56c77d 100644 --- a/keymaps/common +++ b/pc-bios/keymaps/common diff --git a/keymaps/da b/pc-bios/keymaps/da index 3884dcf14..3884dcf14 100644 --- a/keymaps/da +++ b/pc-bios/keymaps/da diff --git a/keymaps/de b/pc-bios/keymaps/de index ed929c743..ed929c743 100644 --- a/keymaps/de +++ b/pc-bios/keymaps/de diff --git a/keymaps/de-ch b/pc-bios/keymaps/de-ch index 852f8b8db..852f8b8db 100644 --- a/keymaps/de-ch +++ b/pc-bios/keymaps/de-ch diff --git a/keymaps/en-gb b/pc-bios/keymaps/en-gb index b45f06c7c..b45f06c7c 100644 --- a/keymaps/en-gb +++ b/pc-bios/keymaps/en-gb diff --git a/keymaps/en-us b/pc-bios/keymaps/en-us index f5784bbb3..f5784bbb3 100644 --- a/keymaps/en-us +++ b/pc-bios/keymaps/en-us diff --git a/keymaps/es b/pc-bios/keymaps/es index 0c29eec5a..0c29eec5a 100644 --- a/keymaps/es +++ b/pc-bios/keymaps/es diff --git a/keymaps/et b/pc-bios/keymaps/et index 85541a377..85541a377 100644 --- a/keymaps/et +++ b/pc-bios/keymaps/et diff --git a/keymaps/fi b/pc-bios/keymaps/fi index 2a4e0f045..2a4e0f045 100644 --- a/keymaps/fi +++ b/pc-bios/keymaps/fi diff --git a/keymaps/fo b/pc-bios/keymaps/fo index c00d9d4de..c00d9d4de 100644 --- a/keymaps/fo +++ b/pc-bios/keymaps/fo diff --git a/keymaps/fr b/pc-bios/keymaps/fr index ba5a176c3..ba5a176c3 100644 --- a/keymaps/fr +++ b/pc-bios/keymaps/fr diff --git a/keymaps/fr-be b/pc-bios/keymaps/fr-be index 62f71286e..62f71286e 100644 --- a/keymaps/fr-be +++ b/pc-bios/keymaps/fr-be diff --git a/keymaps/fr-ca b/pc-bios/keymaps/fr-ca index b645208e4..b645208e4 100644 --- a/keymaps/fr-ca +++ b/pc-bios/keymaps/fr-ca diff --git a/keymaps/fr-ch b/pc-bios/keymaps/fr-ch index 4620d2033..4620d2033 100644 --- a/keymaps/fr-ch +++ b/pc-bios/keymaps/fr-ch diff --git a/keymaps/hr b/pc-bios/keymaps/hr index 613aa6925..613aa6925 100644 --- a/keymaps/hr +++ b/pc-bios/keymaps/hr diff --git a/keymaps/hu b/pc-bios/keymaps/hu index 8aba44441..8aba44441 100644 --- a/keymaps/hu +++ b/pc-bios/keymaps/hu diff --git a/keymaps/is b/pc-bios/keymaps/is index 21dc1fd3c..21dc1fd3c 100644 --- a/keymaps/is +++ b/pc-bios/keymaps/is diff --git a/keymaps/it b/pc-bios/keymaps/it index 00ca73a3e..00ca73a3e 100644 --- a/keymaps/it +++ b/pc-bios/keymaps/it diff --git a/keymaps/ja b/pc-bios/keymaps/ja index 9d90a78c8..9d90a78c8 100644 --- a/keymaps/ja +++ b/pc-bios/keymaps/ja diff --git a/keymaps/lt b/pc-bios/keymaps/lt index 3d9d619ea..3d9d619ea 100644 --- a/keymaps/lt +++ b/pc-bios/keymaps/lt diff --git a/keymaps/lv b/pc-bios/keymaps/lv index 1d9172791..1d9172791 100644 --- a/keymaps/lv +++ b/pc-bios/keymaps/lv diff --git a/keymaps/mk b/pc-bios/keymaps/mk index 18c150484..18c150484 100644 --- a/keymaps/mk +++ b/pc-bios/keymaps/mk diff --git a/keymaps/modifiers b/pc-bios/keymaps/modifiers index d73b7a663..d73b7a663 100644 --- a/keymaps/modifiers +++ b/pc-bios/keymaps/modifiers diff --git a/keymaps/nl b/pc-bios/keymaps/nl index b4892f930..b4892f930 100644 --- a/keymaps/nl +++ b/pc-bios/keymaps/nl diff --git a/keymaps/nl-be b/pc-bios/keymaps/nl-be index 34fc881ad..34fc881ad 100644 --- a/keymaps/nl-be +++ b/pc-bios/keymaps/nl-be diff --git a/keymaps/no b/pc-bios/keymaps/no index 40a64790d..40a64790d 100644 --- a/keymaps/no +++ b/pc-bios/keymaps/no diff --git a/keymaps/pl b/pc-bios/keymaps/pl index 09c600d35..09c600d35 100644 --- a/keymaps/pl +++ b/pc-bios/keymaps/pl diff --git a/keymaps/pt b/pc-bios/keymaps/pt index c6941f651..c6941f651 100644 --- a/keymaps/pt +++ b/pc-bios/keymaps/pt diff --git a/keymaps/pt-br b/pc-bios/keymaps/pt-br index 54bafc5dc..54bafc5dc 100644 --- a/keymaps/pt-br +++ b/pc-bios/keymaps/pt-br diff --git a/keymaps/ru b/pc-bios/keymaps/ru index b3e7d24de..b3e7d24de 100644 --- a/keymaps/ru +++ b/pc-bios/keymaps/ru diff --git a/keymaps/sl b/pc-bios/keymaps/sl index 56835a92c..56835a92c 100644 --- a/keymaps/sl +++ b/pc-bios/keymaps/sl diff --git a/keymaps/sv b/pc-bios/keymaps/sv index 5d9080eff..5d9080eff 100644 --- a/keymaps/sv +++ b/pc-bios/keymaps/sv diff --git a/keymaps/th b/pc-bios/keymaps/th index b65b6da5d..b65b6da5d 100644 --- a/keymaps/th +++ b/pc-bios/keymaps/th diff --git a/keymaps/tr b/pc-bios/keymaps/tr index 5650e1e93..5650e1e93 100644 --- a/keymaps/tr +++ b/pc-bios/keymaps/tr diff --git a/qemu-doc.texi b/qemu-doc.texi index 2b6461a3e..837c2a4e5 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -92,9 +92,11 @@ For system emulation, the following hardware targets are supported: @item Gumstix "Connex" and "Verdex" motherboards (PXA255/270). @item Siemens SX1 smartphone (OMAP310 processor) @item Syborg SVP base model (ARM Cortex-A8). +@item AXIS-Devboard88 (CRISv32 ETRAX-FS). +@item Petalogix Spartan 3aDSP1800 MMU ref design (MicroBlaze). @end itemize -For user emulation, x86, PowerPC, ARM, 32-bit MIPS, Sparc32/64 and ColdFire(m68k) CPUs are supported. +For user emulation, x86, PowerPC, ARM, 32-bit MIPS, Sparc32/64, ColdFire(m68k), CRISv32 and MicroBlaze CPUs are supported. @node Installation @chapter Installation @@ -244,7 +244,6 @@ QEMUFile *qemu_popen(FILE *popen_file, const char *mode) } else { s->file = qemu_fopen_ops(s, popen_put_buffer, NULL, popen_close, NULL, NULL); } - fprintf(stderr, "qemu_popen: returning result of qemu_fopen_ops\n"); return s->file; } @@ -260,6 +259,17 @@ QEMUFile *qemu_popen_cmd(const char *command, const char *mode) return qemu_popen(popen_file, mode); } +int qemu_popen_fd(QEMUFile *f) +{ + QEMUFilePopen *p; + int fd; + + p = (QEMUFilePopen *)f->opaque; + fd = fileno(p->popen_file); + + return fd; +} + QEMUFile *qemu_fopen_socket(int fd) { QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket)); diff --git a/slirp/libslirp.h b/slirp/libslirp.h index a1cd70e97..b2313b43c 100644 --- a/slirp/libslirp.h +++ b/slirp/libslirp.h @@ -18,6 +18,11 @@ void slirp_input(const uint8_t *pkt, int pkt_len); int slirp_can_output(void); void slirp_output(const uint8_t *pkt, int pkt_len); +void slirp_redir_loop(void (*func)(void *opaque, int is_udp, + struct in_addr *laddr, u_int lport, + struct in_addr *faddr, u_int fport), + void *opaque); +int slirp_redir_rm(int is_udp, int host_port); int slirp_redir(int is_udp, int host_port, struct in_addr guest_addr, int guest_port); int slirp_add_exec(int do_pty, const void *args, int addr_low_byte, diff --git a/slirp/slirp.c b/slirp/slirp.c index 04d3dede2..9cab73124 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -734,6 +734,53 @@ void if_encap(const uint8_t *ip_data, int ip_data_len) } } +static void _slirp_redir_loop(void (*func)(void *opaque, int is_udp, + struct in_addr *laddr, u_int lport, + struct in_addr *faddr, u_int fport), + void *opaque, int is_udp) +{ + struct socket *head = (is_udp ? &udb : &tcb); + struct socket *so; + + for (so = head->so_next; so != head; so = so->so_next) { + func(opaque, is_udp, + &so->so_laddr, ntohs(so->so_lport), + &so->so_faddr, ntohs(so->so_fport)); + } +} + +void slirp_redir_loop(void (*func)(void *opaque, int is_udp, + struct in_addr *laddr, u_int lport, + struct in_addr *faddr, u_int fport), + void *opaque) +{ + _slirp_redir_loop(func, opaque, 0); + _slirp_redir_loop(func, opaque, 1); +} + +/* Unlistens a redirection + * + * Return value: number of redirs removed */ +int slirp_redir_rm(int is_udp, int host_port) +{ + struct socket *so; + struct socket *head = (is_udp ? &udb : &tcb); + int fport = htons(host_port); + int n = 0; + + loop_again: + for (so = head->so_next; so != head; so = so->so_next) { + if (so->so_fport == fport) { + close(so->s); + sofree(so); + n++; + goto loop_again; + } + } + + return n; +} + int slirp_redir(int is_udp, int host_port, struct in_addr guest_addr, int guest_port) { diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h new file mode 100644 index 000000000..97f708c5a --- /dev/null +++ b/target-microblaze/cpu.h @@ -0,0 +1,311 @@ +/* + * MicroBlaze virtual CPU header + * + * Copyright (c) 2009 Edgar E. Iglesias + * + * 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ +#ifndef CPU_MICROBLAZE_H +#define CPU_MICROBLAZE_H + +#define TARGET_LONG_BITS 32 + +#define CPUState struct CPUMBState + +#include "cpu-defs.h" +struct CPUMBState; +#if !defined(CONFIG_USER_ONLY) +#include "mmu.h" +#endif + +#define TARGET_HAS_ICE 1 + +#define ELF_MACHINE EM_XILINX_MICROBLAZE + +#define EXCP_NMI 1 +#define EXCP_MMU 2 +#define EXCP_IRQ 3 +#define EXCP_BREAK 4 +#define EXCP_HW_BREAK 5 + +/* Register aliases. R0 - R15 */ +#define R_SP 1 +#define SR_PC 0 +#define SR_MSR 1 +#define SR_EAR 3 +#define SR_ESR 5 +#define SR_FSR 7 +#define SR_BTR 0xb +#define SR_EDR 0xd + +/* MSR flags. */ +#define MSR_BE (1<<0) /* 0x001 */ +#define MSR_IE (1<<1) /* 0x002 */ +#define MSR_C (1<<2) /* 0x004 */ +#define MSR_BIP (1<<3) /* 0x008 */ +#define MSR_FSL (1<<4) /* 0x010 */ +#define MSR_ICE (1<<5) /* 0x020 */ +#define MSR_DZ (1<<6) /* 0x040 */ +#define MSR_DCE (1<<7) /* 0x080 */ +#define MSR_EE (1<<8) /* 0x100 */ +#define MSR_EIP (1<<9) /* 0x200 */ +#define MSR_CC (1<<31) + +/* Machine State Register (MSR) Fields */ +#define MSR_UM (1<<11) /* User Mode */ +#define MSR_UMS (1<<12) /* User Mode Save */ +#define MSR_VM (1<<13) /* Virtual Mode */ +#define MSR_VMS (1<<14) /* Virtual Mode Save */ + +#define MSR_KERNEL MSR_EE|MSR_VM +//#define MSR_USER MSR_KERNEL|MSR_UM|MSR_IE +#define MSR_KERNEL_VMS MSR_EE|MSR_VMS +//#define MSR_USER_VMS MSR_KERNEL_VMS|MSR_UMS|MSR_IE + +/* Exception State Register (ESR) Fields */ +#define ESR_DIZ (1<<11) /* Zone Protection */ +#define ESR_S (1<<10) /* Store instruction */ + + + +/* Version reg. */ +/* Basic PVR mask */ +#define PVR0_PVR_FULL_MASK 0x80000000 +#define PVR0_USE_BARREL_MASK 0x40000000 +#define PVR0_USE_DIV_MASK 0x20000000 +#define PVR0_USE_HW_MUL_MASK 0x10000000 +#define PVR0_USE_FPU_MASK 0x08000000 +#define PVR0_USE_EXC_MASK 0x04000000 +#define PVR0_USE_ICACHE_MASK 0x02000000 +#define PVR0_USE_DCACHE_MASK 0x01000000 +#define PVR0_USE_MMU 0x00800000 /* new */ +#define PVR0_VERSION_MASK 0x0000FF00 +#define PVR0_USER1_MASK 0x000000FF + +/* User 2 PVR mask */ +#define PVR1_USER2_MASK 0xFFFFFFFF + +/* Configuration PVR masks */ +#define PVR2_D_OPB_MASK 0x80000000 +#define PVR2_D_LMB_MASK 0x40000000 +#define PVR2_I_OPB_MASK 0x20000000 +#define PVR2_I_LMB_MASK 0x10000000 +#define PVR2_INTERRUPT_IS_EDGE_MASK 0x08000000 +#define PVR2_EDGE_IS_POSITIVE_MASK 0x04000000 +#define PVR2_D_PLB_MASK 0x02000000 /* new */ +#define PVR2_I_PLB_MASK 0x01000000 /* new */ +#define PVR2_INTERCONNECT 0x00800000 /* new */ +#define PVR2_USE_EXTEND_FSL 0x00080000 /* new */ +#define PVR2_USE_FSL_EXC 0x00040000 /* new */ +#define PVR2_USE_MSR_INSTR 0x00020000 +#define PVR2_USE_PCMP_INSTR 0x00010000 +#define PVR2_AREA_OPTIMISED 0x00008000 +#define PVR2_USE_BARREL_MASK 0x00004000 +#define PVR2_USE_DIV_MASK 0x00002000 +#define PVR2_USE_HW_MUL_MASK 0x00001000 +#define PVR2_USE_FPU_MASK 0x00000800 +#define PVR2_USE_MUL64_MASK 0x00000400 +#define PVR2_USE_FPU2_MASK 0x00000200 /* new */ +#define PVR2_USE_IPLBEXC 0x00000100 +#define PVR2_USE_DPLBEXC 0x00000080 +#define PVR2_OPCODE_0x0_ILL_MASK 0x00000040 +#define PVR2_UNALIGNED_EXC_MASK 0x00000020 +#define PVR2_ILL_OPCODE_EXC_MASK 0x00000010 +#define PVR2_IOPB_BUS_EXC_MASK 0x00000008 +#define PVR2_DOPB_BUS_EXC_MASK 0x00000004 +#define PVR2_DIV_ZERO_EXC_MASK 0x00000002 +#define PVR2_FPU_EXC_MASK 0x00000001 + +/* Debug and exception PVR masks */ +#define PVR3_DEBUG_ENABLED_MASK 0x80000000 +#define PVR3_NUMBER_OF_PC_BRK_MASK 0x1E000000 +#define PVR3_NUMBER_OF_RD_ADDR_BRK_MASK 0x00380000 +#define PVR3_NUMBER_OF_WR_ADDR_BRK_MASK 0x0000E000 +#define PVR3_FSL_LINKS_MASK 0x00000380 + +/* ICache config PVR masks */ +#define PVR4_USE_ICACHE_MASK 0x80000000 +#define PVR4_ICACHE_ADDR_TAG_BITS_MASK 0x7C000000 +#define PVR4_ICACHE_USE_FSL_MASK 0x02000000 +#define PVR4_ICACHE_ALLOW_WR_MASK 0x01000000 +#define PVR4_ICACHE_LINE_LEN_MASK 0x00E00000 +#define PVR4_ICACHE_BYTE_SIZE_MASK 0x001F0000 + +/* DCache config PVR masks */ +#define PVR5_USE_DCACHE_MASK 0x80000000 +#define PVR5_DCACHE_ADDR_TAG_BITS_MASK 0x7C000000 +#define PVR5_DCACHE_USE_FSL_MASK 0x02000000 +#define PVR5_DCACHE_ALLOW_WR_MASK 0x01000000 +#define PVR5_DCACHE_LINE_LEN_MASK 0x00E00000 +#define PVR5_DCACHE_BYTE_SIZE_MASK 0x001F0000 + +/* ICache base address PVR mask */ +#define PVR6_ICACHE_BASEADDR_MASK 0xFFFFFFFF + +/* ICache high address PVR mask */ +#define PVR7_ICACHE_HIGHADDR_MASK 0xFFFFFFFF + +/* DCache base address PVR mask */ +#define PVR8_DCACHE_BASEADDR_MASK 0xFFFFFFFF + +/* DCache high address PVR mask */ +#define PVR9_DCACHE_HIGHADDR_MASK 0xFFFFFFFF + +/* Target family PVR mask */ +#define PVR10_TARGET_FAMILY_MASK 0xFF000000 + +/* MMU descrtiption */ +#define PVR11_USE_MMU 0xC0000000 +#define PVR11_MMU_ITLB_SIZE 0x38000000 +#define PVR11_MMU_DTLB_SIZE 0x07000000 +#define PVR11_MMU_TLB_ACCESS 0x00C00000 +#define PVR11_MMU_ZONES 0x003C0000 +/* MSR Reset value PVR mask */ +#define PVR11_MSR_RESET_VALUE_MASK 0x000007FF + + + +/* CPU flags. */ + +/* Condition codes. */ +#define CC_GE 5 +#define CC_GT 4 +#define CC_LE 3 +#define CC_LT 2 +#define CC_NE 1 +#define CC_EQ 0 + +#define NB_MMU_MODES 3 +typedef struct CPUMBState { + uint32_t debug; + uint32_t btaken; + uint32_t btarget; + uint32_t bimm; + + uint32_t imm; + uint32_t regs[33]; + uint32_t sregs[24]; + + /* Internal flags. */ +#define IMM_FLAG 4 +#define DRTI_FLAG (1 << 16) +#define DRTE_FLAG (1 << 17) +#define DRTB_FLAG (1 << 18) +#define D_FLAG (1 << 19) /* Bit in ESR. */ +/* TB dependant CPUState. */ +#define IFLAGS_TB_MASK (D_FLAG | IMM_FLAG | DRTI_FLAG | DRTE_FLAG | DRTB_FLAG) + uint32_t iflags; + + struct { + uint32_t regs[16]; + } pvr; + +#if !defined(CONFIG_USER_ONLY) + /* Unified MMU. */ + struct microblaze_mmu mmu; +#endif + + CPU_COMMON +} CPUMBState; + +CPUState *cpu_mb_init(const char *cpu_model); +int cpu_mb_exec(CPUState *s); +void cpu_mb_close(CPUState *s); +void do_interrupt(CPUState *env); +/* you can call this signal handler from your SIGBUS and SIGSEGV + signal handlers to inform the virtual CPU of exceptions. non zero + is returned if the signal was handled by the virtual CPU. */ +int cpu_mb_signal_handler(int host_signum, void *pinfo, + void *puc); + +enum { + CC_OP_DYNAMIC, /* Use env->cc_op */ + CC_OP_FLAGS, + CC_OP_CMP, +}; + +/* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ +#define TARGET_PAGE_BITS 12 +#define MMAP_SHIFT TARGET_PAGE_BITS + +#define cpu_init cpu_mb_init +#define cpu_exec cpu_mb_exec +#define cpu_gen_code cpu_mb_gen_code +#define cpu_signal_handler cpu_mb_signal_handler + +#define CPU_SAVE_VERSION 1 + +/* MMU modes definitions */ +#define MMU_MODE0_SUFFIX _nommu +#define MMU_MODE1_SUFFIX _kernel +#define MMU_MODE2_SUFFIX _user +#define MMU_NOMMU_IDX 0 +#define MMU_KERNEL_IDX 1 +#define MMU_USER_IDX 2 +/* See NB_MMU_MODES further up the file. */ + +static inline int cpu_mmu_index (CPUState *env) +{ + /* Are we in nommu mode?. */ + if (!(env->sregs[SR_MSR] & MSR_VM)) + return MMU_NOMMU_IDX; + + if (env->sregs[SR_MSR] & MSR_UM) + return MMU_USER_IDX; + return MMU_KERNEL_IDX; +} + +int cpu_mb_handle_mmu_fault(CPUState *env, target_ulong address, int rw, + int mmu_idx, int is_softmmu); + +#if defined(CONFIG_USER_ONLY) +static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) +{ + if (newsp) + env->regs[R_SP] = newsp; + env->regs[3] = 0; +} +#endif + +static inline void cpu_set_tls(CPUState *env, target_ulong newtls) +{ +} + +static inline int cpu_interrupts_enabled(CPUState *env) +{ + return env->sregs[SR_MSR] & MSR_IE; +} + +#include "cpu-all.h" +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->sregs[SR_PC] = tb->pc; +} + +static inline target_ulong cpu_get_pc(CPUState *env) +{ + return env->sregs[SR_PC]; +} + +static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, + target_ulong *cs_base, int *flags) +{ + *pc = env->sregs[SR_PC]; + *cs_base = 0; + *flags = env->iflags & IFLAGS_TB_MASK; +} +#endif diff --git a/target-microblaze/exec.h b/target-microblaze/exec.h new file mode 100644 index 000000000..55045bb92 --- /dev/null +++ b/target-microblaze/exec.h @@ -0,0 +1,57 @@ +/* + * Microblaze execution defines + * + * Copyright (c) 2009 Edgar E. Iglesias + * + * 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ +#include "dyngen-exec.h" + +register struct CPUMBState *env asm(AREG0); + +#include "cpu.h" +#include "exec-all.h" + +static inline void env_to_regs(void) +{ +} + +static inline void regs_to_env(void) +{ +} + +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif + +void cpu_mb_flush_flags(CPUMBState *env, int cc_op); + +static inline int cpu_has_work(CPUState *env) +{ + return (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)); +} + +static inline int cpu_halted(CPUState *env) { + if (!env->halted) + return 0; + + /* IRQ, NMI and GURU execeptions wakes us up. */ + if (env->interrupt_request + & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)) { + env->halted = 0; + return 0; + } + return EXCP_HALTED; +} diff --git a/target-microblaze/helper.c b/target-microblaze/helper.c new file mode 100644 index 000000000..00936594d --- /dev/null +++ b/target-microblaze/helper.c @@ -0,0 +1,255 @@ +/* + * MicroBlaze helper routines. + * + * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com> + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include "config.h" +#include "cpu.h" +#include "exec-all.h" +#include "host-utils.h" + +#define D(x) +#define DMMU(x) + +#if defined(CONFIG_USER_ONLY) + +void do_interrupt (CPUState *env) +{ + env->exception_index = -1; + env->regs[14] = env->sregs[SR_PC]; +} + +int cpu_mb_handle_mmu_fault(CPUState * env, target_ulong address, int rw, + int mmu_idx, int is_softmmu) +{ + env->exception_index = 0xaa; + cpu_dump_state(env, stderr, fprintf, 0); + return 1; +} + +target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr) +{ + return addr; +} + +#else /* !CONFIG_USER_ONLY */ + +int cpu_mb_handle_mmu_fault (CPUState *env, target_ulong address, int rw, + int mmu_idx, int is_softmmu) +{ + unsigned int hit; + unsigned int mmu_available; + int r = 1; + int prot; + + mmu_available = 0; + if (env->pvr.regs[0] & PVR0_USE_MMU) { + mmu_available = 1; + if ((env->pvr.regs[0] & PVR0_PVR_FULL_MASK) + && (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) { + mmu_available = 0; + } + } + + /* Translate if the MMU is available and enabled. */ + if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) { + target_ulong vaddr, paddr; + struct microblaze_mmu_lookup lu; + + hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx); + if (hit) { + vaddr = address & TARGET_PAGE_MASK; + paddr = lu.paddr + vaddr - lu.vaddr; + + DMMU(qemu_log("MMU map mmu=%d v=%x p=%x prot=%x\n", + mmu_idx, vaddr, paddr, lu.prot)); + r = tlb_set_page(env, vaddr, + paddr, lu.prot, mmu_idx, is_softmmu); + } else { + env->sregs[SR_EAR] = address; + DMMU(qemu_log("mmu=%d miss addr=%x\n", mmu_idx, vaddr)); + + switch (lu.err) { + case ERR_PROT: + env->sregs[SR_ESR] = rw == 2 ? 17 : 16; + env->sregs[SR_ESR] |= (rw == 1) << 10; + break; + case ERR_MISS: + env->sregs[SR_ESR] = rw == 2 ? 19 : 18; + env->sregs[SR_ESR] |= (rw == 1) << 10; + break; + default: + abort(); + break; + } + + if (env->exception_index == EXCP_MMU) { + cpu_abort(env, "recursive faults\n"); + } + + /* TLB miss. */ + env->exception_index = EXCP_MMU; + } + } else { + /* MMU disabled or not available. */ + address &= TARGET_PAGE_MASK; + prot = PAGE_BITS; + r = tlb_set_page(env, address, address, prot, mmu_idx, is_softmmu); + } + return r; +} + +void do_interrupt(CPUState *env) +{ + uint32_t t; + + /* IMM flag cannot propagate accross a branch and into the dslot. */ + assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG))); + assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); +/* assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions. */ + switch (env->exception_index) { + case EXCP_MMU: + env->regs[17] = env->sregs[SR_PC]; + + /* Exception breaks branch + dslot sequence? */ + if (env->iflags & D_FLAG) { + D(qemu_log("D_FLAG set at exception bimm=%d\n", env->bimm)); + env->sregs[SR_ESR] |= 1 << 12 ; + env->sregs[SR_BTR] = env->btarget; + + /* Reexecute the branch. */ + env->regs[17] -= 4; + /* was the branch immprefixed?. */ + if (env->bimm) { + qemu_log_mask(CPU_LOG_INT, + "bimm exception at pc=%x iflags=%x\n", + env->sregs[SR_PC], env->iflags); + env->regs[17] -= 4; + log_cpu_state_mask(CPU_LOG_INT, env, 0); + } + } else if (env->iflags & IMM_FLAG) { + D(qemu_log("IMM_FLAG set at exception\n")); + env->regs[17] -= 4; + } + + /* Disable the MMU. */ + t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; + env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); + env->sregs[SR_MSR] |= t; + /* Exception in progress. */ + env->sregs[SR_MSR] |= MSR_EIP; + + qemu_log_mask(CPU_LOG_INT, + "exception at pc=%x ear=%x iflags=%x\n", + env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); + log_cpu_state_mask(CPU_LOG_INT, env, 0); + env->iflags &= ~(IMM_FLAG | D_FLAG); + env->sregs[SR_PC] = 0x20; + break; + + case EXCP_IRQ: + assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))); + assert(env->sregs[SR_MSR] & MSR_IE); + assert(!(env->iflags & D_FLAG)); + + t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; + +#if 0 +#include "disas.h" + +/* Useful instrumentation when debugging interrupt issues in either + the models or in sw. */ + { + const char *sym; + + sym = lookup_symbol(env->sregs[SR_PC]); + if (sym + && (!strcmp("netif_rx", sym) + || !strcmp("process_backlog", sym))) { + + qemu_log( + "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n", + env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags, + sym); + + log_cpu_state(env, 0); + } + } +#endif + qemu_log_mask(CPU_LOG_INT, + "interrupt at pc=%x msr=%x %x iflags=%x\n", + env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); + + env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ + | MSR_UM | MSR_IE); + env->sregs[SR_MSR] |= t; + + env->regs[14] = env->sregs[SR_PC]; + env->sregs[SR_PC] = 0x10; + //log_cpu_state_mask(CPU_LOG_INT, env, 0); + break; + + case EXCP_BREAK: + case EXCP_HW_BREAK: + assert(!(env->iflags & IMM_FLAG)); + assert(!(env->iflags & D_FLAG)); + t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; + qemu_log_mask(CPU_LOG_INT, + "break at pc=%x msr=%x %x iflags=%x\n", + env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); + log_cpu_state_mask(CPU_LOG_INT, env, 0); + env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); + env->sregs[SR_MSR] |= t; + env->sregs[SR_MSR] |= MSR_BIP; + if (env->exception_index == EXCP_HW_BREAK) { + env->regs[16] = env->sregs[SR_PC]; + env->sregs[SR_MSR] |= MSR_BIP; + env->sregs[SR_PC] = 0x18; + } else + env->sregs[SR_PC] = env->btarget; + break; + default: + cpu_abort(env, "unhandled exception type=%d\n", + env->exception_index); + break; + } +} + +target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr) +{ + target_ulong vaddr, paddr = 0; + struct microblaze_mmu_lookup lu; + unsigned int hit; + + if (env->sregs[SR_MSR] & MSR_VM) { + hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); + if (hit) { + vaddr = addr & TARGET_PAGE_MASK; + paddr = lu.paddr + vaddr - lu.vaddr; + } else + paddr = 0; /* ???. */ + } else + paddr = addr & TARGET_PAGE_MASK; + + return paddr; +} +#endif diff --git a/target-microblaze/helper.h b/target-microblaze/helper.h new file mode 100644 index 000000000..8c5361ecd --- /dev/null +++ b/target-microblaze/helper.h @@ -0,0 +1,19 @@ +#include "def-helper.h" + +DEF_HELPER_1(raise_exception, void, i32) +DEF_HELPER_0(debug, void) +DEF_HELPER_4(addkc, i32, i32, i32, i32, i32) +DEF_HELPER_4(subkc, i32, i32, i32, i32, i32) +DEF_HELPER_2(cmp, i32, i32, i32) +DEF_HELPER_2(cmpu, i32, i32, i32) + +DEF_HELPER_2(divs, i32, i32, i32) +DEF_HELPER_2(divu, i32, i32, i32) + +DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32) +#if !defined(CONFIG_USER_ONLY) +DEF_HELPER_1(mmu_read, i32, i32) +DEF_HELPER_2(mmu_write, void, i32, i32) +#endif + +#include "def-helper.h" diff --git a/target-microblaze/machine.c b/target-microblaze/machine.c new file mode 100644 index 000000000..1be1c351b --- /dev/null +++ b/target-microblaze/machine.c @@ -0,0 +1,11 @@ +#include "hw/hw.h" +#include "hw/boards.h" + +void cpu_save(QEMUFile *f, void *opaque) +{ +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return 0; +} diff --git a/target-microblaze/microblaze-decode.h b/target-microblaze/microblaze-decode.h new file mode 100644 index 000000000..27b01288c --- /dev/null +++ b/target-microblaze/microblaze-decode.h @@ -0,0 +1,52 @@ +/* + * MicroBlaze insn decoding macros. + * + * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com> + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ + +/* Convenient binary macros. */ +#define HEX__(n) 0x##n##LU +#define B8__(x) ((x&0x0000000FLU)?1:0) \ + + ((x&0x000000F0LU)?2:0) \ + + ((x&0x00000F00LU)?4:0) \ + + ((x&0x0000F000LU)?8:0) \ + + ((x&0x000F0000LU)?16:0) \ + + ((x&0x00F00000LU)?32:0) \ + + ((x&0x0F000000LU)?64:0) \ + + ((x&0xF0000000LU)?128:0) +#define B8(d) ((unsigned char)B8__(HEX__(d))) + +/* Decode logic, mask and value. */ +#define DEC_ADD {B8(00000000), B8(00110001)} +#define DEC_SUB {B8(00000001), B8(00110001)} +#define DEC_AND {B8(00100001), B8(00110101)} +#define DEC_XOR {B8(00100010), B8(00110111)} +#define DEC_OR {B8(00100000), B8(00110111)} +#define DEC_BIT {B8(00100100), B8(00111111)} +#define DEC_MSR {B8(00100101), B8(00111111)} + +#define DEC_BARREL {B8(00010001), B8(00110111)} +#define DEC_MUL {B8(00010000), B8(00110111)} +#define DEC_DIV {B8(00010010), B8(00110111)} + +#define DEC_LD {B8(00110000), B8(00110100)} +#define DEC_ST {B8(00110100), B8(00110100)} +#define DEC_IMM {B8(00101100), B8(00111111)} + +#define DEC_BR {B8(00100110), B8(00110111)} +#define DEC_BCC {B8(00100111), B8(00110111)} +#define DEC_RTS {B8(00101101), B8(00111111)} diff --git a/target-microblaze/mmu.c b/target-microblaze/mmu.c new file mode 100644 index 000000000..d14373ce4 --- /dev/null +++ b/target-microblaze/mmu.c @@ -0,0 +1,257 @@ +/* + * Microblaze MMU emulation for qemu. + * + * Copyright (c) 2009 Edgar E. Iglesias + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "config.h" +#include "cpu.h" +#include "exec-all.h" + +#define D(x) + +static unsigned int tlb_decode_size(unsigned int f) +{ + static const unsigned int sizes[] = { + 1 * 1024, 4 * 1024, 16 * 1024, 64 * 1024, 256 * 1024, + 1 * 1024 * 1024, 4 * 1024 * 1024, 16 * 1024 * 1024 + }; + assert(f < ARRAY_SIZE(sizes)); + return sizes[f]; +} + +static void mmu_flush_idx(CPUState *env, unsigned int idx) +{ + struct microblaze_mmu *mmu = &env->mmu; + unsigned int tlb_size; + uint32_t tlb_tag, end, t; + + t = mmu->rams[RAM_TAG][idx]; + if (!(t & TLB_VALID)) + return; + + tlb_tag = t & TLB_EPN_MASK; + tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); + end = tlb_tag + tlb_size; + + while (tlb_tag < end) { + tlb_flush_page(env, tlb_tag); + tlb_tag += TARGET_PAGE_SIZE; + } +} + +static void mmu_change_pid(CPUState *env, unsigned int newpid) +{ + struct microblaze_mmu *mmu = &env->mmu; + unsigned int i; + unsigned int tlb_size; + uint32_t tlb_tag, mask, t; + + if (newpid & ~0xff) + qemu_log("Illegal rpid=%x\n", newpid); + + for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { + /* Lookup and decode. */ + t = mmu->rams[RAM_TAG][i]; + if (t & TLB_VALID) { + tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); + mask = ~(tlb_size - 1); + + tlb_tag = t & TLB_EPN_MASK; + if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i])) + mmu_flush_idx(env, i); + } + } +} + +/* rw - 0 = read, 1 = write, 2 = fetch. */ +unsigned int mmu_translate(struct microblaze_mmu *mmu, + struct microblaze_mmu_lookup *lu, + target_ulong vaddr, int rw, int mmu_idx) +{ + unsigned int i, hit = 0; + unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel; + unsigned int tlb_size; + uint32_t tlb_tag, tlb_rpn, mask, t0; + + lu->err = ERR_MISS; + for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { + uint32_t t, d; + + /* Lookup and decode. */ + t = mmu->rams[RAM_TAG][i]; + D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID)); + if (t & TLB_VALID) { + tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); + if (tlb_size < TARGET_PAGE_SIZE) { + qemu_log("%d pages not supported\n", tlb_size); + abort(); + } + + mask = ~(tlb_size - 1); + tlb_tag = t & TLB_EPN_MASK; + if ((vaddr & mask) != (tlb_tag & mask)) { + D(qemu_log("TLB %d vaddr=%x != tag=%x\n", + i, vaddr & mask, tlb_tag & mask)); + continue; + } + if (mmu->tids[i] + && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) { + D(qemu_log("TLB %d pid=%x != tid=%x\n", + i, mmu->regs[MMU_R_PID], mmu->tids[i])); + continue; + } + + /* Bring in the data part. */ + d = mmu->rams[RAM_DATA][i]; + tlb_ex = d & TLB_EX; + tlb_wr = d & TLB_WR; + + /* Now lets see if there is a zone that overrides the protbits. */ + tlb_zsel = (d >> 4) & 0xf; + t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2)); + t0 &= 0x3; + switch (t0) { + case 0: + if (mmu_idx == MMU_USER_IDX) + continue; + break; + case 2: + if (mmu_idx != MMU_USER_IDX) { + tlb_ex = 1; + tlb_wr = 1; + } + break; + case 3: + tlb_ex = 1; + tlb_wr = 1; + break; + } + + + lu->err = ERR_PROT; + lu->prot = PAGE_READ; + if (tlb_wr) + lu->prot |= PAGE_WRITE; + else if (rw == 1) + goto done; + if (tlb_ex) + lu->prot |=PAGE_EXEC; + else if (rw == 2) { + goto done; + } + + tlb_rpn = d & TLB_RPN_MASK; + + lu->vaddr = tlb_tag; + lu->paddr = tlb_rpn; + lu->size = tlb_size; + lu->err = ERR_HIT; + lu->idx = i; + hit = 1; + goto done; + } + } +done: + D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", + vaddr, rw, tlb_wr, tlb_ex, hit)); + return hit; +} + +/* Writes/reads to the MMU's special regs end up here. */ +uint32_t mmu_read(CPUState *env, uint32_t rn) +{ + unsigned int i; + uint32_t r; + + switch (rn) { + /* Reads to HI/LO trig reads from the mmu rams. */ + case MMU_R_TLBLO: + case MMU_R_TLBHI: + i = env->mmu.regs[MMU_R_TLBX] & 0xff; + r = env->mmu.rams[rn & 1][i]; + if (rn == MMU_R_TLBHI) + env->mmu.regs[MMU_R_PID] = env->mmu.tids[i]; + break; + default: + r = env->mmu.regs[rn]; + break; + } + D(qemu_log("%s rn=%d=%x\n", __func__, rn, r)); + return r; +} + +void mmu_write(CPUState *env, uint32_t rn, uint32_t v) +{ + unsigned int i; + D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn])); + + switch (rn) { + /* Writes to HI/LO trig writes to the mmu rams. */ + case MMU_R_TLBLO: + case MMU_R_TLBHI: + i = env->mmu.regs[MMU_R_TLBX] & 0xff; + if (rn == MMU_R_TLBHI) { + if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) + qemu_log("invalidating index %x at pc=%x\n", + i, env->sregs[SR_PC]); + env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; + mmu_flush_idx(env, i); + } + env->mmu.rams[rn & 1][i] = v; + + D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v)); + break; + case MMU_R_ZPR: + /* Changes to the zone protection reg flush the QEMU TLB. + Fortunately, these are very uncommon. */ + if (v != env->mmu.regs[rn]) { + tlb_flush(env, 1); + } + env->mmu.regs[rn] = v; + break; + case MMU_R_PID: + if (v != env->mmu.regs[rn]) { + mmu_change_pid(env, v); + env->mmu.regs[rn] = v; + } + break; + case MMU_R_TLBSX: + { + struct microblaze_mmu_lookup lu; + int hit; + hit = mmu_translate(&env->mmu, &lu, + v & TLB_EPN_MASK, 0, cpu_mmu_index(env)); + if (hit) { + env->mmu.regs[MMU_R_TLBX] = lu.idx; + } else + env->mmu.regs[MMU_R_TLBX] |= 0x80000000; + break; + } + default: + env->mmu.regs[rn] = v; + break; + } +} + +void mmu_init(struct microblaze_mmu *mmu) +{ + memset(mmu, 0, sizeof *mmu); +} diff --git a/target-microblaze/mmu.h b/target-microblaze/mmu.h new file mode 100644 index 000000000..975933396 --- /dev/null +++ b/target-microblaze/mmu.h @@ -0,0 +1,88 @@ +/* + * Microblaze MMU emulation for qemu. + * + * Copyright (c) 2009 Edgar E. Iglesias + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ + +#define MMU_R_PID 0 +#define MMU_R_ZPR 1 +#define MMU_R_TLBX 2 +#define MMU_R_TLBLO 3 +#define MMU_R_TLBHI 4 +#define MMU_R_TLBSX 5 + +#define RAM_DATA 1 +#define RAM_TAG 0 + +/* Tag portion */ +#define TLB_EPN_MASK 0xFFFFFC00 /* Effective Page Number */ +#define TLB_PAGESZ_MASK 0x00000380 +#define TLB_PAGESZ(x) (((x) & 0x7) << 7) +#define PAGESZ_1K 0 +#define PAGESZ_4K 1 +#define PAGESZ_16K 2 +#define PAGESZ_64K 3 +#define PAGESZ_256K 4 +#define PAGESZ_1M 5 +#define PAGESZ_4M 6 +#define PAGESZ_16M 7 +#define TLB_VALID 0x00000040 /* Entry is valid */ + +/* Data portion */ +#define TLB_RPN_MASK 0xFFFFFC00 /* Real Page Number */ +#define TLB_PERM_MASK 0x00000300 +#define TLB_EX 0x00000200 /* Instruction execution allowed */ +#define TLB_WR 0x00000100 /* Writes permitted */ +#define TLB_ZSEL_MASK 0x000000F0 +#define TLB_ZSEL(x) (((x) & 0xF) << 4) +#define TLB_ATTR_MASK 0x0000000F +#define TLB_W 0x00000008 /* Caching is write-through */ +#define TLB_I 0x00000004 /* Caching is inhibited */ +#define TLB_M 0x00000002 /* Memory is coherent */ +#define TLB_G 0x00000001 /* Memory is guarded from prefetch */ + +#define TLB_ENTRIES 64 + +struct microblaze_mmu +{ + /* Data and tag brams. */ + uint32_t rams[2][TLB_ENTRIES]; + /* We keep a separate ram for the tids to avoid the 48 bit tag width. */ + uint8_t tids[TLB_ENTRIES]; + /* Control flops. */ + uint32_t regs[8];; +}; + +struct microblaze_mmu_lookup +{ + uint32_t paddr; + uint32_t vaddr; + unsigned int size; + unsigned int idx; + int prot; + enum { + ERR_PROT, ERR_MISS, ERR_HIT + } err; +}; + +void mmu_flip_um(CPUState *env, unsigned int um); +unsigned int mmu_translate(struct microblaze_mmu *mmu, + struct microblaze_mmu_lookup *lu, + target_ulong vaddr, int rw, int mmu_idx); +uint32_t mmu_read(CPUState *env, uint32_t rn); +void mmu_write(CPUState *env, uint32_t rn, uint32_t v); +void mmu_init(struct microblaze_mmu *mmu); diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c new file mode 100644 index 000000000..1a0776d2c --- /dev/null +++ b/target-microblaze/op_helper.c @@ -0,0 +1,216 @@ +/* + * Microblaze helper routines. + * + * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com>. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ + +#include <assert.h> +#include "exec.h" +#include "helper.h" +#include "host-utils.h" + +#define D(x) + +#if !defined(CONFIG_USER_ONLY) +#define MMUSUFFIX _mmu +#define SHIFT 0 +#include "softmmu_template.h" +#define SHIFT 1 +#include "softmmu_template.h" +#define SHIFT 2 +#include "softmmu_template.h" +#define SHIFT 3 +#include "softmmu_template.h" + +/* Try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr) +{ + TranslationBlock *tb; + CPUState *saved_env; + unsigned long pc; + int ret; + + /* XXX: hack to restore env in all cases, even if not called from + generated code */ + saved_env = env; + env = cpu_single_env; + + ret = cpu_mb_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); + if (unlikely(ret)) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, NULL); + } + } + cpu_loop_exit(); + } + env = saved_env; +} +#endif + +void helper_raise_exception(uint32_t index) +{ + env->exception_index = index; + cpu_loop_exit(); +} + +void helper_debug(void) +{ + int i; + + qemu_log("PC=%8.8x\n", env->sregs[SR_PC]); + for (i = 0; i < 32; i++) { + qemu_log("r%2.2d=%8.8x ", i, env->regs[i]); + if ((i + 1) % 4 == 0) + qemu_log("\n"); + } + qemu_log("\n\n"); +} + +static inline uint32_t compute_carry(uint32_t a, uint32_t b, uint32_t cin) +{ + uint32_t cout = 0; + + if ((b == ~0) && cin) + cout = 1; + else if ((~0 - a) < (b + cin)) + cout = 1; + return cout; +} + +uint32_t helper_cmp(uint32_t a, uint32_t b) +{ + uint32_t t; + + t = b + ~a + 1; + if ((b & 0x80000000) ^ (a & 0x80000000)) + t = (t & 0x7fffffff) | (b & 0x80000000); + return t; +} + +uint32_t helper_cmpu(uint32_t a, uint32_t b) +{ + uint32_t t; + + t = b + ~a + 1; + if ((b & 0x80000000) ^ (a & 0x80000000)) + t = (t & 0x7fffffff) | (a & 0x80000000); + return t; +} + +uint32_t helper_addkc(uint32_t a, uint32_t b, uint32_t k, uint32_t c) +{ + uint32_t d, cf = 0, ncf; + + if (c) + cf = env->sregs[SR_MSR] >> 31; + assert(cf == 0 || cf == 1); + d = a + b + cf; + + if (!k) { + ncf = compute_carry(a, b, cf); + assert(ncf == 0 || ncf == 1); + if (ncf) + env->sregs[SR_MSR] |= MSR_C | MSR_CC; + else + env->sregs[SR_MSR] &= ~(MSR_C | MSR_CC); + } + D(qemu_log("%x = %x + %x cf=%d ncf=%d k=%d c=%d\n", + d, a, b, cf, ncf, k, c)); + return d; +} + +uint32_t helper_subkc(uint32_t a, uint32_t b, uint32_t k, uint32_t c) +{ + uint32_t d, cf = 1, ncf; + + if (c) + cf = env->sregs[SR_MSR] >> 31; + assert(cf == 0 || cf == 1); + d = b + ~a + cf; + + if (!k) { + ncf = compute_carry(b, ~a, cf); + assert(ncf == 0 || ncf == 1); + if (ncf) + env->sregs[SR_MSR] |= MSR_C | MSR_CC; + else + env->sregs[SR_MSR] &= ~(MSR_C | MSR_CC); + } + D(qemu_log("%x = %x + %x cf=%d ncf=%d k=%d c=%d\n", + d, a, b, cf, ncf, k, c)); + return d; +} + +static inline int div_prepare(uint32_t a, uint32_t b) +{ + if (b == 0) { + env->sregs[SR_MSR] |= MSR_DZ; + /* FIXME: Raise the div by zero exception. */ + return 0; + } + env->sregs[SR_MSR] &= ~MSR_DZ; + return 1; +} + +uint32_t helper_divs(uint32_t a, uint32_t b) +{ + if (!div_prepare(a, b)) + return 0; + return (int32_t)a / (int32_t)b; +} + +uint32_t helper_divu(uint32_t a, uint32_t b) +{ + if (!div_prepare(a, b)) + return 0; + return a / b; +} + +uint32_t helper_pcmpbf(uint32_t a, uint32_t b) +{ + unsigned int i; + uint32_t mask = 0xff000000; + + for (i = 0; i < 4; i++) { + if ((a & mask) == (b & mask)) + return i + 1; + mask >>= 8; + } + return 0; +} + +#if !defined(CONFIG_USER_ONLY) +/* Writes/reads to the MMU's special regs end up here. */ +uint32_t helper_mmu_read(uint32_t rn) +{ + return mmu_read(env, rn); +} + +void helper_mmu_write(uint32_t rn, uint32_t v) +{ + mmu_write(env, rn, v); +} +#endif diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c new file mode 100644 index 000000000..33fff9c2c --- /dev/null +++ b/target-microblaze/translate.c @@ -0,0 +1,1395 @@ +/* + * Xilinx MicroBlaze emulation for qemu: main translation routines. + * + * Copyright (c) 2009 Edgar E. Iglesias. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <assert.h> + +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" +#include "tcg-op.h" +#include "helper.h" +#include "microblaze-decode.h" +#include "qemu-common.h" + +#define GEN_HELPER 1 +#include "helper.h" + +#define SIM_COMPAT 0 +#define DISAS_GNU 1 +#define DISAS_MB 1 +#if DISAS_MB && !SIM_COMPAT +# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) +#else +# define LOG_DIS(...) do { } while (0) +#endif + +#define D(x) + +#define EXTRACT_FIELD(src, start, end) \ + (((src) >> start) & ((1 << (end - start + 1)) - 1)) + +static TCGv env_debug; +static TCGv_ptr cpu_env; +static TCGv cpu_R[32]; +static TCGv cpu_SR[18]; +static TCGv env_imm; +static TCGv env_btaken; +static TCGv env_btarget; +static TCGv env_iflags; + +#include "gen-icount.h" + +/* This is the state at translation time. */ +typedef struct DisasContext { + CPUState *env; + target_ulong pc, ppc; + target_ulong cache_pc; + + /* Decoder. */ + int type_b; + uint32_t ir; + uint8_t opcode; + uint8_t rd, ra, rb; + uint16_t imm; + + unsigned int cpustate_changed; + unsigned int delayed_branch; + unsigned int tb_flags, synced_flags; /* tb dependent flags. */ + unsigned int clear_imm; + int is_jmp; + +#define JMP_NOJMP 0 +#define JMP_DIRECT 1 +#define JMP_INDIRECT 2 + unsigned int jmp; + uint32_t jmp_pc; + + int abort_at_next_insn; + int nr_nops; + struct TranslationBlock *tb; + int singlestep_enabled; +} DisasContext; + +const static char *regnames[] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", +}; + +const static char *special_regnames[] = +{ + "rpc", "rmsr", "sr2", "sr3", "sr4", "sr5", "sr6", "sr7", + "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15", + "sr16", "sr17", "sr18" +}; + +/* Sign extend at translation time. */ +static inline int sign_extend(unsigned int val, unsigned int width) +{ + int sval; + + /* LSL. */ + val <<= 31 - width; + sval = val; + /* ASR. */ + sval >>= 31 - width; + return sval; +} + +static inline void t_sync_flags(DisasContext *dc) +{ + /* Synch the tb dependant flags between translator and runtime. */ + if (dc->tb_flags != dc->synced_flags) { + tcg_gen_movi_tl(env_iflags, dc->tb_flags); + dc->synced_flags = dc->tb_flags; + } +} + +static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) +{ + TCGv_i32 tmp = tcg_const_i32(index); + + t_sync_flags(dc); + tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + gen_helper_raise_exception(tmp); + tcg_temp_free_i32(tmp); + dc->is_jmp = DISAS_UPDATE; +} + +static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) +{ + TranslationBlock *tb; + tb = dc->tb; + if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_tl(cpu_SR[SR_PC], dest); + tcg_gen_exit_tb((long)tb + n); + } else { + tcg_gen_movi_tl(cpu_SR[SR_PC], dest); + tcg_gen_exit_tb(0); + } +} + +static inline TCGv *dec_alu_op_b(DisasContext *dc) +{ + if (dc->type_b) { + if (dc->tb_flags & IMM_FLAG) + tcg_gen_ori_tl(env_imm, env_imm, dc->imm); + else + tcg_gen_movi_tl(env_imm, (int32_t)((int16_t)dc->imm)); + return &env_imm; + } else + return &cpu_R[dc->rb]; +} + +static void dec_add(DisasContext *dc) +{ + unsigned int k, c; + + k = dc->opcode & 4; + c = dc->opcode & 2; + + LOG_DIS("add%s%s%s r%d r%d r%d\n", + dc->type_b ? "i" : "", k ? "k" : "", c ? "c" : "", + dc->rd, dc->ra, dc->rb); + + if (k && !c && dc->rd) + tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + else if (dc->rd) + gen_helper_addkc(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)), + tcg_const_tl(k), tcg_const_tl(c)); + else { + TCGv d = tcg_temp_new(); + gen_helper_addkc(d, cpu_R[dc->ra], *(dec_alu_op_b(dc)), + tcg_const_tl(k), tcg_const_tl(c)); + tcg_temp_free(d); + } +} + +static void dec_sub(DisasContext *dc) +{ + unsigned int u, cmp, k, c; + + u = dc->imm & 2; + k = dc->opcode & 4; + c = dc->opcode & 2; + cmp = (dc->imm & 1) && (!dc->type_b) && k; + + if (cmp) { + LOG_DIS("cmp%s r%d, r%d ir=%x\n", u ? "u" : "", dc->rd, dc->ra, dc->ir); + if (dc->rd) { + if (u) + gen_helper_cmpu(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + else + gen_helper_cmp(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + } + } else { + LOG_DIS("sub%s%s r%d, r%d r%d\n", + k ? "k" : "", c ? "c" : "", dc->rd, dc->ra, dc->rb); + + if (!k || c) { + TCGv t; + t = tcg_temp_new(); + if (dc->rd) + gen_helper_subkc(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)), + tcg_const_tl(k), tcg_const_tl(c)); + else + gen_helper_subkc(t, cpu_R[dc->ra], *(dec_alu_op_b(dc)), + tcg_const_tl(k), tcg_const_tl(c)); + tcg_temp_free(t); + } + else if (dc->rd) + tcg_gen_sub_tl(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + } +} + +static void dec_pattern(DisasContext *dc) +{ + unsigned int mode; + int l1; + + mode = dc->opcode & 3; + switch (mode) { + case 0: + /* pcmpbf. */ + LOG_DIS("pcmpbf r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); + if (dc->rd) + gen_helper_pcmpbf(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 2: + LOG_DIS("pcmpeq r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); + if (dc->rd) { + TCGv t0 = tcg_temp_local_new(); + l1 = gen_new_label(); + tcg_gen_movi_tl(t0, 1); + tcg_gen_brcond_tl(TCG_COND_EQ, + cpu_R[dc->ra], cpu_R[dc->rb], l1); + tcg_gen_movi_tl(t0, 0); + gen_set_label(l1); + tcg_gen_mov_tl(cpu_R[dc->rd], t0); + tcg_temp_free(t0); + } + break; + case 3: + LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); + l1 = gen_new_label(); + if (dc->rd) { + TCGv t0 = tcg_temp_local_new(); + tcg_gen_movi_tl(t0, 1); + tcg_gen_brcond_tl(TCG_COND_NE, + cpu_R[dc->ra], cpu_R[dc->rb], l1); + tcg_gen_movi_tl(t0, 0); + gen_set_label(l1); + tcg_gen_mov_tl(cpu_R[dc->rd], t0); + tcg_temp_free(t0); + } + break; + default: + cpu_abort(dc->env, + "unsupported pattern insn opcode=%x\n", dc->opcode); + break; + } +} + +static void dec_and(DisasContext *dc) +{ + unsigned int not; + + if (!dc->type_b && (dc->imm & (1 << 10))) { + dec_pattern(dc); + return; + } + + not = dc->opcode & (1 << 1); + LOG_DIS("and%s\n", not ? "n" : ""); + + if (!dc->rd) + return; + + if (not) { + TCGv t = tcg_temp_new(); + tcg_gen_not_tl(t, *(dec_alu_op_b(dc))); + tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], t); + tcg_temp_free(t); + } else + tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +} + +static void dec_or(DisasContext *dc) +{ + if (!dc->type_b && (dc->imm & (1 << 10))) { + dec_pattern(dc); + return; + } + + LOG_DIS("or r%d r%d r%d imm=%x\n", dc->rd, dc->ra, dc->rb, dc->imm); + if (dc->rd) + tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +} + +static void dec_xor(DisasContext *dc) +{ + if (!dc->type_b && (dc->imm & (1 << 10))) { + dec_pattern(dc); + return; + } + + LOG_DIS("xor r%d\n", dc->rd); + if (dc->rd) + tcg_gen_xor_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +} + +static void read_carry(DisasContext *dc, TCGv d) +{ + tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31); +} + +static void write_carry(DisasContext *dc, TCGv v) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_shli_tl(t0, v, 31); + tcg_gen_sari_tl(t0, t0, 31); + tcg_gen_mov_tl(env_debug, t0); + tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC)); + tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], + ~(MSR_C | MSR_CC)); + tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); + tcg_temp_free(t0); +} + + +static inline void msr_read(DisasContext *dc, TCGv d) +{ + tcg_gen_mov_tl(d, cpu_SR[SR_MSR]); +} + +static inline void msr_write(DisasContext *dc, TCGv v) +{ + dc->cpustate_changed = 1; + tcg_gen_mov_tl(cpu_SR[SR_MSR], v); + /* PVR, we have a processor version register. */ + tcg_gen_ori_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], (1 << 10)); +} + +static void dec_msr(DisasContext *dc) +{ + TCGv t0, t1; + unsigned int sr, to, rn; + + sr = dc->imm & ((1 << 14) - 1); + to = dc->imm & (1 << 14); + dc->type_b = 1; + if (to) + dc->cpustate_changed = 1; + + /* msrclr and msrset. */ + if (!(dc->imm & (1 << 15))) { + unsigned int clr = dc->ir & (1 << 16); + + LOG_DIS("msr%s r%d imm=%x\n", clr ? "clr" : "set", + dc->rd, dc->imm); + if (dc->rd) + msr_read(dc, cpu_R[dc->rd]); + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + msr_read(dc, t0); + tcg_gen_mov_tl(t1, *(dec_alu_op_b(dc))); + + if (clr) { + tcg_gen_not_tl(t1, t1); + tcg_gen_and_tl(t0, t0, t1); + } else + tcg_gen_or_tl(t0, t0, t1); + msr_write(dc, t0); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); + dc->is_jmp = DISAS_UPDATE; + return; + } + +#if !defined(CONFIG_USER_ONLY) + /* Catch read/writes to the mmu block. */ + if ((sr & ~0xff) == 0x1000) { + sr &= 7; + LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); + if (to) + gen_helper_mmu_write(tcg_const_tl(sr), cpu_R[dc->ra]); + else + gen_helper_mmu_read(cpu_R[dc->rd], tcg_const_tl(sr)); + return; + } +#endif + + if (to) { + LOG_DIS("m%ss sr%x r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); + switch (sr) { + case 0: + break; + case 1: + msr_write(dc, cpu_R[dc->ra]); + break; + case 0x3: + tcg_gen_mov_tl(cpu_SR[SR_EAR], cpu_R[dc->ra]); + break; + case 0x5: + tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]); + break; + case 0x7: + /* Ignored at the moment. */ + break; + default: + cpu_abort(dc->env, "unknown mts reg %x\n", sr); + break; + } + } else { + LOG_DIS("m%ss r%d sr%x imm=%x\n", to ? "t" : "f", dc->rd, sr, dc->imm); + + switch (sr) { + case 0: + tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + break; + case 1: + msr_read(dc, cpu_R[dc->rd]); + break; + case 0x3: + tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_EAR]); + break; + case 0x5: + tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]); + break; + case 0x7: + tcg_gen_movi_tl(cpu_R[dc->rd], 0); + break; + case 0xb: + tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]); + break; + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200a: + case 0x200b: + case 0x200c: + rn = sr & 0xf; + tcg_gen_ld_tl(cpu_R[dc->rd], + cpu_env, offsetof(CPUState, pvr.regs[rn])); + break; + default: + cpu_abort(dc->env, "unknown mfs reg %x\n", sr); + break; + } + } +} + +/* 64-bit signed mul, lower result in d and upper in d2. */ +static void t_gen_muls(TCGv d, TCGv d2, TCGv a, TCGv b) +{ + TCGv_i64 t0, t1; + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(t0, a); + tcg_gen_ext_i32_i64(t1, b); + tcg_gen_mul_i64(t0, t0, t1); + + tcg_gen_trunc_i64_i32(d, t0); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_trunc_i64_i32(d2, t0); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +/* 64-bit unsigned muls, lower result in d and upper in d2. */ +static void t_gen_mulu(TCGv d, TCGv d2, TCGv a, TCGv b) +{ + TCGv_i64 t0, t1; + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(t0, a); + tcg_gen_extu_i32_i64(t1, b); + tcg_gen_mul_i64(t0, t0, t1); + + tcg_gen_trunc_i64_i32(d, t0); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_trunc_i64_i32(d2, t0); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +/* Multiplier unit. */ +static void dec_mul(DisasContext *dc) +{ + TCGv d[2]; + unsigned int subcode; + + subcode = dc->imm & 3; + d[0] = tcg_temp_new(); + d[1] = tcg_temp_new(); + + if (dc->type_b) { + LOG_DIS("muli r%d r%d %x\n", dc->rd, dc->ra, dc->imm); + t_gen_mulu(cpu_R[dc->rd], d[1], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + goto done; + } + + switch (subcode) { + case 0: + LOG_DIS("mul r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); + t_gen_mulu(cpu_R[dc->rd], d[1], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 1: + LOG_DIS("mulh r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); + t_gen_muls(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 2: + LOG_DIS("mulhsu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); + t_gen_muls(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 3: + LOG_DIS("mulhu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); + t_gen_mulu(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + default: + cpu_abort(dc->env, "unknown MUL insn %x\n", subcode); + break; + } +done: + tcg_temp_free(d[0]); + tcg_temp_free(d[1]); +} + +/* Div unit. */ +static void dec_div(DisasContext *dc) +{ + unsigned int u; + + u = dc->imm & 2; + LOG_DIS("div\n"); + + /* FIXME: support div by zero exceptions. */ + if (u) + gen_helper_divu(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + else + gen_helper_divs(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + if (!dc->rd) + tcg_gen_movi_tl(cpu_R[dc->rd], 0); +} + +static void dec_barrel(DisasContext *dc) +{ + TCGv t0; + unsigned int s, t; + + s = dc->imm & (1 << 10); + t = dc->imm & (1 << 9); + + LOG_DIS("bs%s%s r%d r%d r%d\n", + s ? "l" : "r", t ? "a" : "l", dc->rd, dc->ra, dc->rb); + + t0 = tcg_temp_new(); + + tcg_gen_mov_tl(t0, *(dec_alu_op_b(dc))); + tcg_gen_andi_tl(t0, t0, 31); + + if (s) + tcg_gen_shl_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + else { + if (t) + tcg_gen_sar_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + else + tcg_gen_shr_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + } +} + +static void dec_bit(DisasContext *dc) +{ + TCGv t0, t1; + unsigned int op; + + op = dc->ir & ((1 << 8) - 1); + switch (op) { + case 0x21: + /* src. */ + t0 = tcg_temp_new(); + + LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); + tcg_gen_andi_tl(t0, cpu_R[dc->ra], 1); + if (dc->rd) { + t1 = tcg_temp_new(); + read_carry(dc, t1); + tcg_gen_shli_tl(t1, t1, 31); + + tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->rd], t1); + tcg_temp_free(t1); + } + + /* Update carry. */ + write_carry(dc, t0); + tcg_temp_free(t0); + break; + + case 0x1: + case 0x41: + /* srl. */ + t0 = tcg_temp_new(); + LOG_DIS("srl r%d r%d\n", dc->rd, dc->ra); + + /* Update carry. */ + tcg_gen_andi_tl(t0, cpu_R[dc->ra], 1); + write_carry(dc, t0); + tcg_temp_free(t0); + if (dc->rd) { + if (op == 0x41) + tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + else + tcg_gen_sari_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + } + break; + case 0x60: + LOG_DIS("ext8s r%d r%d\n", dc->rd, dc->ra); + tcg_gen_ext8s_i32(cpu_R[dc->rd], cpu_R[dc->ra]); + break; + case 0x61: + LOG_DIS("ext16s r%d r%d\n", dc->rd, dc->ra); + tcg_gen_ext16s_i32(cpu_R[dc->rd], cpu_R[dc->ra]); + break; + case 0x64: + /* wdc. */ + LOG_DIS("wdc r%d\n", dc->ra); + break; + case 0x68: + /* wic. */ + LOG_DIS("wic r%d\n", dc->ra); + break; + default: + cpu_abort(dc->env, "unknown bit oc=%x op=%x rd=%d ra=%d rb=%d\n", + dc->pc, op, dc->rd, dc->ra, dc->rb); + break; + } +} + +static inline void sync_jmpstate(DisasContext *dc) +{ + if (dc->jmp == JMP_DIRECT) { + dc->jmp = JMP_INDIRECT; + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_movi_tl(env_btarget, dc->jmp_pc); + } +} + +static void dec_imm(DisasContext *dc) +{ + LOG_DIS("imm %x\n", dc->imm << 16); + tcg_gen_movi_tl(env_imm, (dc->imm << 16)); + dc->tb_flags |= IMM_FLAG; + dc->clear_imm = 0; +} + +static inline void gen_load(DisasContext *dc, TCGv dst, TCGv addr, + unsigned int size) +{ + int mem_index = cpu_mmu_index(dc->env); + + if (size == 1) { + tcg_gen_qemu_ld8u(dst, addr, mem_index); + } else if (size == 2) { + tcg_gen_qemu_ld16u(dst, addr, mem_index); + } else if (size == 4) { + tcg_gen_qemu_ld32u(dst, addr, mem_index); + } else + cpu_abort(dc->env, "Incorrect load size %d\n", size); +} + +static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) +{ + unsigned int extimm = dc->tb_flags & IMM_FLAG; + + /* Treat the fast cases first. */ + if (!dc->type_b) { + *t = tcg_temp_new(); + tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]); + return t; + } + /* Immediate. */ + if (!extimm) { + if (dc->imm == 0) { + return &cpu_R[dc->ra]; + } + *t = tcg_temp_new(); + tcg_gen_movi_tl(*t, (int32_t)((int16_t)dc->imm)); + tcg_gen_add_tl(*t, cpu_R[dc->ra], *t); + } else { + *t = tcg_temp_new(); + tcg_gen_add_tl(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + } + + return t; +} + +static void dec_load(DisasContext *dc) +{ + TCGv t, *addr; + unsigned int size; + + size = 1 << (dc->opcode & 3); + + LOG_DIS("l %x %d\n", dc->opcode, size); + t_sync_flags(dc); + addr = compute_ldst_addr(dc, &t); + + /* If we get a fault on a dslot, the jmpstate better be in sync. */ + sync_jmpstate(dc); + if (dc->rd) + gen_load(dc, cpu_R[dc->rd], *addr, size); + else { + gen_load(dc, env_imm, *addr, size); + } + + if (addr == &t) + tcg_temp_free(t); +} + +static void gen_store(DisasContext *dc, TCGv addr, TCGv val, + unsigned int size) +{ + int mem_index = cpu_mmu_index(dc->env); + + if (size == 1) + tcg_gen_qemu_st8(val, addr, mem_index); + else if (size == 2) { + tcg_gen_qemu_st16(val, addr, mem_index); + } else if (size == 4) { + tcg_gen_qemu_st32(val, addr, mem_index); + } else + cpu_abort(dc->env, "Incorrect store size %d\n", size); +} + +static void dec_store(DisasContext *dc) +{ + TCGv t, *addr; + unsigned int size; + + size = 1 << (dc->opcode & 3); + + LOG_DIS("s%d%s\n", size, dc->type_b ? "i" : ""); + t_sync_flags(dc); + /* If we get a fault on a dslot, the jmpstate better be in sync. */ + sync_jmpstate(dc); + addr = compute_ldst_addr(dc, &t); + gen_store(dc, *addr, cpu_R[dc->rd], size); + if (addr == &t) + tcg_temp_free(t); +} + +static inline void eval_cc(DisasContext *dc, unsigned int cc, + TCGv d, TCGv a, TCGv b) +{ + int l1; + + switch (cc) { + case CC_EQ: + l1 = gen_new_label(); + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_brcond_tl(TCG_COND_EQ, a, b, l1); + tcg_gen_movi_tl(env_btaken, 0); + gen_set_label(l1); + break; + case CC_NE: + l1 = gen_new_label(); + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_brcond_tl(TCG_COND_NE, a, b, l1); + tcg_gen_movi_tl(env_btaken, 0); + gen_set_label(l1); + break; + case CC_LT: + l1 = gen_new_label(); + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_brcond_tl(TCG_COND_LT, a, b, l1); + tcg_gen_movi_tl(env_btaken, 0); + gen_set_label(l1); + break; + case CC_LE: + l1 = gen_new_label(); + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_brcond_tl(TCG_COND_LE, a, b, l1); + tcg_gen_movi_tl(env_btaken, 0); + gen_set_label(l1); + break; + case CC_GE: + l1 = gen_new_label(); + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_brcond_tl(TCG_COND_GE, a, b, l1); + tcg_gen_movi_tl(env_btaken, 0); + gen_set_label(l1); + break; + case CC_GT: + l1 = gen_new_label(); + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_brcond_tl(TCG_COND_GT, a, b, l1); + tcg_gen_movi_tl(env_btaken, 0); + gen_set_label(l1); + break; + default: + cpu_abort(dc->env, "Unknown condition code %x.\n", cc); + break; + } +} + +static void eval_cond_jmp(DisasContext *dc, TCGv pc_true, TCGv pc_false) +{ + int l1; + + l1 = gen_new_label(); + /* Conditional jmp. */ + tcg_gen_mov_tl(cpu_SR[SR_PC], pc_false); + tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1); + tcg_gen_mov_tl(cpu_SR[SR_PC], pc_true); + gen_set_label(l1); +} + +static void dec_bcc(DisasContext *dc) +{ + unsigned int cc; + unsigned int dslot; + + cc = EXTRACT_FIELD(dc->ir, 21, 23); + dslot = dc->ir & (1 << 25); + LOG_DIS("bcc%s r%d %x\n", dslot ? "d" : "", dc->ra, dc->imm); + + dc->delayed_branch = 1; + if (dslot) { + dc->delayed_branch = 2; + dc->tb_flags |= D_FLAG; + tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + cpu_env, offsetof(CPUState, bimm)); + } + + tcg_gen_movi_tl(env_btarget, dc->pc); + tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0)); + dc->jmp = JMP_INDIRECT; +} + +static void dec_br(DisasContext *dc) +{ + unsigned int dslot, link, abs; + + dslot = dc->ir & (1 << 20); + abs = dc->ir & (1 << 19); + link = dc->ir & (1 << 18); + LOG_DIS("br%s%s%s%s imm=%x\n", + abs ? "a" : "", link ? "l" : "", + dc->type_b ? "i" : "", dslot ? "d" : "", + dc->imm); + + dc->delayed_branch = 1; + if (dslot) { + dc->delayed_branch = 2; + dc->tb_flags |= D_FLAG; + tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + cpu_env, offsetof(CPUState, bimm)); + } + if (link && dc->rd) + tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + + dc->jmp = JMP_INDIRECT; + if (abs) { + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_mov_tl(env_btarget, *(dec_alu_op_b(dc))); + if (link && !(dc->tb_flags & IMM_FLAG) + && (dc->imm == 8 || dc->imm == 0x18)) + t_gen_raise_exception(dc, EXCP_BREAK); + if (dc->imm == 0) + t_gen_raise_exception(dc, EXCP_DEBUG); + } else { + if (dc->tb_flags & IMM_FLAG) { + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_movi_tl(env_btarget, dc->pc); + tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + } else { + dc->jmp = JMP_DIRECT; + dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); + } + } +} + +static inline void do_rti(DisasContext *dc) +{ + TCGv t0, t1; + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_SR[SR_MSR], 1); + tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_IE); + tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + + tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_tl(t1, t1, t0); + msr_write(dc, t1); + tcg_temp_free(t1); + tcg_temp_free(t0); + dc->tb_flags &= ~DRTI_FLAG; +} + +static inline void do_rtb(DisasContext *dc) +{ + TCGv t0, t1; + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_SR[SR_MSR], ~MSR_BIP); + tcg_gen_shri_tl(t0, t1, 1); + tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + + tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_tl(t1, t1, t0); + msr_write(dc, t1); + tcg_temp_free(t1); + tcg_temp_free(t0); + dc->tb_flags &= ~DRTB_FLAG; +} + +static inline void do_rte(DisasContext *dc) +{ + TCGv t0, t1; + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_EE); + tcg_gen_andi_tl(t1, t1, ~MSR_EIP); + tcg_gen_shri_tl(t0, t1, 1); + tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + + tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_tl(t1, t1, t0); + msr_write(dc, t1); + tcg_temp_free(t1); + tcg_temp_free(t0); + dc->tb_flags &= ~DRTE_FLAG; +} + +static void dec_rts(DisasContext *dc) +{ + unsigned int b_bit, i_bit, e_bit; + + i_bit = dc->ir & (1 << 21); + b_bit = dc->ir & (1 << 22); + e_bit = dc->ir & (1 << 23); + + dc->delayed_branch = 2; + dc->tb_flags |= D_FLAG; + tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + cpu_env, offsetof(CPUState, bimm)); + + if (i_bit) { + LOG_DIS("rtid ir=%x\n", dc->ir); + dc->tb_flags |= DRTI_FLAG; + } else if (b_bit) { + LOG_DIS("rtbd ir=%x\n", dc->ir); + dc->tb_flags |= DRTB_FLAG; + } else if (e_bit) { + LOG_DIS("rted ir=%x\n", dc->ir); + dc->tb_flags |= DRTE_FLAG; + } else + LOG_DIS("rts ir=%x\n", dc->ir); + + tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); +} + +static void dec_null(DisasContext *dc) +{ + qemu_log ("unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); + dc->abort_at_next_insn = 1; +} + +static struct decoder_info { + struct { + uint32_t bits; + uint32_t mask; + }; + void (*dec)(DisasContext *dc); +} decinfo[] = { + {DEC_ADD, dec_add}, + {DEC_SUB, dec_sub}, + {DEC_AND, dec_and}, + {DEC_XOR, dec_xor}, + {DEC_OR, dec_or}, + {DEC_BIT, dec_bit}, + {DEC_BARREL, dec_barrel}, + {DEC_LD, dec_load}, + {DEC_ST, dec_store}, + {DEC_IMM, dec_imm}, + {DEC_BR, dec_br}, + {DEC_BCC, dec_bcc}, + {DEC_RTS, dec_rts}, + {DEC_MUL, dec_mul}, + {DEC_DIV, dec_div}, + {DEC_MSR, dec_msr}, + {{0, 0}, dec_null} +}; + +static inline void decode(DisasContext *dc) +{ + uint32_t ir; + int i; + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) + tcg_gen_debug_insn_start(dc->pc); + + dc->ir = ir = ldl_code(dc->pc); + LOG_DIS("%8.8x\t", dc->ir); + + if (dc->ir) + dc->nr_nops = 0; + else { + LOG_DIS("nr_nops=%d\t", dc->nr_nops); + dc->nr_nops++; + if (dc->nr_nops > 4) + cpu_abort(dc->env, "fetching nop sequence\n"); + } + /* bit 2 seems to indicate insn type. */ + dc->type_b = ir & (1 << 29); + + dc->opcode = EXTRACT_FIELD(ir, 26, 31); + dc->rd = EXTRACT_FIELD(ir, 21, 25); + dc->ra = EXTRACT_FIELD(ir, 16, 20); + dc->rb = EXTRACT_FIELD(ir, 11, 15); + dc->imm = EXTRACT_FIELD(ir, 0, 15); + + /* Large switch for all insns. */ + for (i = 0; i < ARRAY_SIZE(decinfo); i++) { + if ((dc->opcode & decinfo[i].mask) == decinfo[i].bits) { + decinfo[i].dec(dc); + break; + } + } +} + + +static void check_breakpoint(CPUState *env, DisasContext *dc) +{ + CPUBreakpoint *bp; + + if (unlikely(!TAILQ_EMPTY(&env->breakpoints))) { + TAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc->pc) { + t_gen_raise_exception(dc, EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + } + } + } +} + +/* generate intermediate code for basic block 'tb'. */ +static void +gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb, + int search_pc) +{ + uint16_t *gen_opc_end; + uint32_t pc_start; + int j, lj; + struct DisasContext ctx; + struct DisasContext *dc = &ctx; + uint32_t next_page_start, org_flags; + target_ulong npc; + int num_insns; + int max_insns; + + qemu_log_try_set_file(stderr); + + pc_start = tb->pc; + dc->env = env; + dc->tb = tb; + org_flags = dc->synced_flags = dc->tb_flags = tb->flags; + + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + + dc->is_jmp = DISAS_NEXT; + dc->jmp = 0; + dc->delayed_branch = !!(dc->tb_flags & D_FLAG); + dc->ppc = pc_start; + dc->pc = pc_start; + dc->cache_pc = -1; + dc->singlestep_enabled = env->singlestep_enabled; + dc->cpustate_changed = 0; + dc->abort_at_next_insn = 0; + dc->nr_nops = 0; + + if (pc_start & 3) + cpu_abort(env, "Microblaze: unaligned PC=%x\n", pc_start); + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { +#if !SIM_COMPAT + qemu_log("--------------\n"); + log_cpu_state(env, 0); +#endif + } + + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + lj = -1; + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) + max_insns = CF_COUNT_MASK; + + gen_icount_start(); + do + { +#if SIM_COMPAT + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + gen_helper_debug(); + } +#endif + check_breakpoint(env, dc); + + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) + gen_opc_instr_start[lj++] = 0; + } + gen_opc_pc[lj] = dc->pc; + gen_opc_instr_start[lj] = 1; + gen_opc_icount[lj] = num_insns; + } + + /* Pretty disas. */ + LOG_DIS("%8.8x:\t", dc->pc); + + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + gen_io_start(); + + dc->clear_imm = 1; + decode(dc); + if (dc->clear_imm) + dc->tb_flags &= ~IMM_FLAG; + dc->ppc = dc->pc; + dc->pc += 4; + num_insns++; + + if (dc->delayed_branch) { + dc->delayed_branch--; + if (!dc->delayed_branch) { + if (dc->tb_flags & DRTI_FLAG) + do_rti(dc); + if (dc->tb_flags & DRTB_FLAG) + do_rtb(dc); + if (dc->tb_flags & DRTE_FLAG) + do_rte(dc); + /* Clear the delay slot flag. */ + dc->tb_flags &= ~D_FLAG; + /* If it is a direct jump, try direct chaining. */ + if (dc->jmp != JMP_DIRECT) { + eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc)); + dc->is_jmp = DISAS_JUMP; + } + break; + } + } + if (env->singlestep_enabled) + break; + } while (!dc->is_jmp && !dc->cpustate_changed + && gen_opc_ptr < gen_opc_end + && !singlestep + && (dc->pc < next_page_start) + && num_insns < max_insns); + + npc = dc->pc; + if (dc->jmp == JMP_DIRECT) { + if (dc->tb_flags & D_FLAG) { + dc->is_jmp = DISAS_UPDATE; + tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + sync_jmpstate(dc); + } else + npc = dc->jmp_pc; + } + + if (tb->cflags & CF_LAST_IO) + gen_io_end(); + /* Force an update if the per-tb cpu state has changed. */ + if (dc->is_jmp == DISAS_NEXT + && (dc->cpustate_changed || org_flags != dc->tb_flags)) { + dc->is_jmp = DISAS_UPDATE; + tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + } + t_sync_flags(dc); + + if (unlikely(env->singlestep_enabled)) { + t_gen_raise_exception(dc, EXCP_DEBUG); + if (dc->is_jmp == DISAS_NEXT) + tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + } else { + switch(dc->is_jmp) { + case DISAS_NEXT: + gen_goto_tb(dc, 1, npc); + break; + default: + case DISAS_JUMP: + case DISAS_UPDATE: + /* indicate that the hash table must be used + to find the next TB */ + tcg_gen_exit_tb(0); + break; + case DISAS_TB_JUMP: + /* nothing more to generate */ + break; + } + } + gen_icount_end(tb, num_insns); + *gen_opc_ptr = INDEX_op_end; + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) + gen_opc_instr_start[lj++] = 0; + } else { + tb->size = dc->pc - pc_start; + tb->icount = num_insns; + } + +#ifdef DEBUG_DISAS +#if !SIM_COMPAT + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("\n"); +#if DISAS_GNU + log_target_disas(pc_start, dc->pc - pc_start, 0); +#endif + qemu_log("\nisize=%d osize=%zd\n", + dc->pc - pc_start, gen_opc_ptr - gen_opc_buf); + } +#endif +#endif + assert(!dc->abort_at_next_insn); +} + +void gen_intermediate_code (CPUState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 0); +} + +void gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 1); +} + +void cpu_dump_state (CPUState *env, FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags) +{ + int i; + + if (!env || !f) + return; + + cpu_fprintf(f, "IN: PC=%x %s\n", + env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); + cpu_fprintf(f, "rmsr=%x resr=%x debug[%x] imm=%x iflags=%x\n", + env->sregs[SR_MSR], env->sregs[SR_ESR], + env->debug, env->imm, env->iflags); + cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s)\n", + env->btaken, env->btarget, + (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", + (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel"); + for (i = 0; i < 32; i++) { + cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); + if ((i + 1) % 4 == 0) + cpu_fprintf(f, "\n"); + } + cpu_fprintf(f, "\n\n"); +} + +CPUState *cpu_mb_init (const char *cpu_model) +{ + CPUState *env; + static int tcg_initialized = 0; + int i; + + env = qemu_mallocz(sizeof(CPUState)); + + cpu_exec_init(env); + cpu_reset(env); + + env->pvr.regs[0] = PVR0_PVR_FULL_MASK \ + | PVR0_USE_BARREL_MASK \ + | PVR0_USE_DIV_MASK \ + | PVR0_USE_HW_MUL_MASK \ + | PVR0_USE_EXC_MASK \ + | PVR0_USE_ICACHE_MASK \ + | PVR0_USE_DCACHE_MASK \ + | PVR0_USE_MMU \ + | (0xb << 8); + env->pvr.regs[2] = PVR2_D_OPB_MASK \ + | PVR2_D_LMB_MASK \ + | PVR2_I_OPB_MASK \ + | PVR2_I_LMB_MASK \ + | PVR2_USE_MSR_INSTR \ + | PVR2_USE_PCMP_INSTR \ + | PVR2_USE_BARREL_MASK \ + | PVR2_USE_DIV_MASK \ + | PVR2_USE_HW_MUL_MASK \ + | PVR2_USE_MUL64_MASK \ + | 0; + env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */ + env->pvr.regs[11] = PVR11_USE_MMU; + + if (tcg_initialized) + return env; + + tcg_initialized = 1; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + + env_debug = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, debug), + "debug0"); + env_iflags = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, iflags), + "iflags"); + env_imm = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, imm), + "imm"); + env_btarget = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, btarget), + "btarget"); + env_btaken = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, btaken), + "btaken"); + for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { + cpu_R[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, regs[i]), + regnames[i]); + } + for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { + cpu_SR[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, sregs[i]), + special_regnames[i]); + } +#define GEN_HELPER 2 +#include "helper.h" + + return env; +} + +void cpu_reset (CPUState *env) +{ + if (qemu_loglevel_mask(CPU_LOG_RESET)) { + qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + log_cpu_state(env, 0); + } + + memset(env, 0, offsetof(CPUMBState, breakpoints)); + tlb_flush(env, 1); + + env->sregs[SR_MSR] = 0; +#if defined(CONFIG_USER_ONLY) + /* start in user mode with interrupts enabled. */ + env->pvr.regs[10] = 0x0c000000; /* Spartan 3a dsp. */ +#else + mmu_init(&env->mmu); +#endif +} + +void gen_pc_load(CPUState *env, struct TranslationBlock *tb, + unsigned long searched_pc, int pc_pos, void *puc) +{ + env->sregs[SR_PC] = gen_opc_pc[pc_pos]; +} diff --git a/targphys.h b/targphys.h index 81a9c3733..99ab23c7a 100644 --- a/targphys.h +++ b/targphys.h @@ -12,9 +12,11 @@ #if TARGET_PHYS_ADDR_BITS == 32 typedef uint32_t target_phys_addr_t; +#define TARGET_PHYS_ADDR_MAX UINT32_MAX #define TARGET_FMT_plx "%08x" #elif TARGET_PHYS_ADDR_BITS == 64 typedef uint64_t target_phys_addr_t; +#define TARGET_PHYS_ADDR_MAX UINT64_MAX #define TARGET_FMT_plx "%016" PRIx64 #endif #endif @@ -2257,8 +2257,7 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) "boot", NULL }; if (check_params(params, str) < 0) { - fprintf(stderr, "qemu: unknown parameter '%s' in '%s'\n", - buf, str); + fprintf(stderr, "qemu: unknown parameter in '%s'\n", str); return -1; } @@ -3287,7 +3286,7 @@ static int ram_save_live(QEMUFile *f, int stage, void *opaque) { ram_addr_t addr; - if (cpu_physical_sync_dirty_bitmap(0, last_ram_offset) != 0) { + if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { qemu_file_set_error(f); return 0; } @@ -5232,7 +5231,7 @@ int main(int argc, char **argv, char **envp) break; #endif case QEMU_OPTION_redir: - net_slirp_redir(NULL, optarg); + net_slirp_redir(NULL, optarg, NULL); break; #endif case QEMU_OPTION_bt: @@ -5902,7 +5901,6 @@ int main(int argc, char **argv, char **envp) cpu_exec_init_all(tb_size * 1024 * 1024); bdrv_init(); - dma_helper_init(); /* we always create the cdrom drive, even if no disk is there */ @@ -2033,9 +2033,8 @@ static void vnc_listen_read(void *opaque) void vnc_display_init(DisplayState *ds) { - VncDisplay *vs; + VncDisplay *vs = qemu_mallocz(sizeof(*vs)); - vs = qemu_mallocz(sizeof(VncState)); dcl = qemu_mallocz(sizeof(DisplayChangeListener)); ds->opaque = vs; |