From 9d935509fdb48e47cc46e81d2b9d466b18b546ba Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 17 Dec 2013 19:42:25 +0000 Subject: [PATCH 01/62] target-arm: add support for v8 AES instructions This adds support for the AESE/AESD/AESMC/AESIMC instructions that are available on some v8 implementations of Aarch32. Signed-off-by: Ard Biesheuvel Message-id: 1386266078-6976-1-git-send-email-ard.biesheuvel@linaro.org Signed-off-by: Peter Maydell --- target-arm/Makefile.objs | 1 + target-arm/cpu.c | 1 + target-arm/cpu.h | 1 + target-arm/crypto_helper.c | 281 +++++++++++++++++++++++++++++++++++++ target-arm/helper.h | 3 + target-arm/translate.c | 26 ++++ 6 files changed, 313 insertions(+) create mode 100644 target-arm/crypto_helper.c diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index 356fbfcdfd..f428b5b7d7 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -6,3 +6,4 @@ obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o obj-y += gdbstub.o obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o +obj-y += crypto_helper.o diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 0635e78ec2..a03743f4ee 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -249,6 +249,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) set_feature(env, ARM_FEATURE_V7); set_feature(env, ARM_FEATURE_ARM_DIV); set_feature(env, ARM_FEATURE_LPAE); + set_feature(env, ARM_FEATURE_V8_AES); } if (arm_feature(env, ARM_FEATURE_V7)) { set_feature(env, ARM_FEATURE_VAPA); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index c3f007fc53..00fd80d3a7 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -467,6 +467,7 @@ enum arm_features { ARM_FEATURE_LPAE, /* has Large Physical Address Extension */ ARM_FEATURE_V8, ARM_FEATURE_AARCH64, /* supports 64 bit mode */ + ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target-arm/crypto_helper.c b/target-arm/crypto_helper.c new file mode 100644 index 0000000000..f94be69ac5 --- /dev/null +++ b/target-arm/crypto_helper.c @@ -0,0 +1,281 @@ +/* + * crypto_helper.c - emulate v8 Crypto Extensions instructions + * + * Copyright (C) 2013 Linaro Ltd + * + * 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. + */ + +#include + +#include "cpu.h" +#include "exec/exec-all.h" +#include "helper.h" + +union AES_STATE { + uint8_t bytes[16]; + uint32_t cols[4]; + uint64_t l[2]; +}; + +void HELPER(crypto_aese)(CPUARMState *env, uint32_t rd, uint32_t rm, + uint32_t decrypt) +{ + static uint8_t const sbox[][256] = { { + /* S-box for encryption */ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 + }, { + /* S-box for decryption */ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, + 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, + 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, + 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, + 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, + 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, + 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, + 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, + 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, + 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, + 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, + 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d + } }; + static uint8_t const shift[][16] = { + /* ShiftRows permutation vector for encryption */ + { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 }, + /* ShiftRows permutation vector for decryption */ + { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 }, + }; + union AES_STATE rk = { .l = { + float64_val(env->vfp.regs[rm]), + float64_val(env->vfp.regs[rm + 1]) + } }; + union AES_STATE st = { .l = { + float64_val(env->vfp.regs[rd]), + float64_val(env->vfp.regs[rd + 1]) + } }; + int i; + + assert(decrypt < 2); + + /* xor state vector with round key */ + rk.l[0] ^= st.l[0]; + rk.l[1] ^= st.l[1]; + + /* combine ShiftRows operation and sbox substitution */ + for (i = 0; i < 16; i++) { + st.bytes[i] = sbox[decrypt][rk.bytes[shift[decrypt][i]]]; + } + + env->vfp.regs[rd] = make_float64(st.l[0]); + env->vfp.regs[rd + 1] = make_float64(st.l[1]); +} + +void HELPER(crypto_aesmc)(CPUARMState *env, uint32_t rd, uint32_t rm, + uint32_t decrypt) +{ + static uint32_t const mc[][256] = { { + /* MixColumns lookup table */ + 0x00000000, 0x03010102, 0x06020204, 0x05030306, + 0x0c040408, 0x0f05050a, 0x0a06060c, 0x0907070e, + 0x18080810, 0x1b090912, 0x1e0a0a14, 0x1d0b0b16, + 0x140c0c18, 0x170d0d1a, 0x120e0e1c, 0x110f0f1e, + 0x30101020, 0x33111122, 0x36121224, 0x35131326, + 0x3c141428, 0x3f15152a, 0x3a16162c, 0x3917172e, + 0x28181830, 0x2b191932, 0x2e1a1a34, 0x2d1b1b36, + 0x241c1c38, 0x271d1d3a, 0x221e1e3c, 0x211f1f3e, + 0x60202040, 0x63212142, 0x66222244, 0x65232346, + 0x6c242448, 0x6f25254a, 0x6a26264c, 0x6927274e, + 0x78282850, 0x7b292952, 0x7e2a2a54, 0x7d2b2b56, + 0x742c2c58, 0x772d2d5a, 0x722e2e5c, 0x712f2f5e, + 0x50303060, 0x53313162, 0x56323264, 0x55333366, + 0x5c343468, 0x5f35356a, 0x5a36366c, 0x5937376e, + 0x48383870, 0x4b393972, 0x4e3a3a74, 0x4d3b3b76, + 0x443c3c78, 0x473d3d7a, 0x423e3e7c, 0x413f3f7e, + 0xc0404080, 0xc3414182, 0xc6424284, 0xc5434386, + 0xcc444488, 0xcf45458a, 0xca46468c, 0xc947478e, + 0xd8484890, 0xdb494992, 0xde4a4a94, 0xdd4b4b96, + 0xd44c4c98, 0xd74d4d9a, 0xd24e4e9c, 0xd14f4f9e, + 0xf05050a0, 0xf35151a2, 0xf65252a4, 0xf55353a6, + 0xfc5454a8, 0xff5555aa, 0xfa5656ac, 0xf95757ae, + 0xe85858b0, 0xeb5959b2, 0xee5a5ab4, 0xed5b5bb6, + 0xe45c5cb8, 0xe75d5dba, 0xe25e5ebc, 0xe15f5fbe, + 0xa06060c0, 0xa36161c2, 0xa66262c4, 0xa56363c6, + 0xac6464c8, 0xaf6565ca, 0xaa6666cc, 0xa96767ce, + 0xb86868d0, 0xbb6969d2, 0xbe6a6ad4, 0xbd6b6bd6, + 0xb46c6cd8, 0xb76d6dda, 0xb26e6edc, 0xb16f6fde, + 0x907070e0, 0x937171e2, 0x967272e4, 0x957373e6, + 0x9c7474e8, 0x9f7575ea, 0x9a7676ec, 0x997777ee, + 0x887878f0, 0x8b7979f2, 0x8e7a7af4, 0x8d7b7bf6, + 0x847c7cf8, 0x877d7dfa, 0x827e7efc, 0x817f7ffe, + 0x9b80801b, 0x98818119, 0x9d82821f, 0x9e83831d, + 0x97848413, 0x94858511, 0x91868617, 0x92878715, + 0x8388880b, 0x80898909, 0x858a8a0f, 0x868b8b0d, + 0x8f8c8c03, 0x8c8d8d01, 0x898e8e07, 0x8a8f8f05, + 0xab90903b, 0xa8919139, 0xad92923f, 0xae93933d, + 0xa7949433, 0xa4959531, 0xa1969637, 0xa2979735, + 0xb398982b, 0xb0999929, 0xb59a9a2f, 0xb69b9b2d, + 0xbf9c9c23, 0xbc9d9d21, 0xb99e9e27, 0xba9f9f25, + 0xfba0a05b, 0xf8a1a159, 0xfda2a25f, 0xfea3a35d, + 0xf7a4a453, 0xf4a5a551, 0xf1a6a657, 0xf2a7a755, + 0xe3a8a84b, 0xe0a9a949, 0xe5aaaa4f, 0xe6abab4d, + 0xefacac43, 0xecadad41, 0xe9aeae47, 0xeaafaf45, + 0xcbb0b07b, 0xc8b1b179, 0xcdb2b27f, 0xceb3b37d, + 0xc7b4b473, 0xc4b5b571, 0xc1b6b677, 0xc2b7b775, + 0xd3b8b86b, 0xd0b9b969, 0xd5baba6f, 0xd6bbbb6d, + 0xdfbcbc63, 0xdcbdbd61, 0xd9bebe67, 0xdabfbf65, + 0x5bc0c09b, 0x58c1c199, 0x5dc2c29f, 0x5ec3c39d, + 0x57c4c493, 0x54c5c591, 0x51c6c697, 0x52c7c795, + 0x43c8c88b, 0x40c9c989, 0x45caca8f, 0x46cbcb8d, + 0x4fcccc83, 0x4ccdcd81, 0x49cece87, 0x4acfcf85, + 0x6bd0d0bb, 0x68d1d1b9, 0x6dd2d2bf, 0x6ed3d3bd, + 0x67d4d4b3, 0x64d5d5b1, 0x61d6d6b7, 0x62d7d7b5, + 0x73d8d8ab, 0x70d9d9a9, 0x75dadaaf, 0x76dbdbad, + 0x7fdcdca3, 0x7cdddda1, 0x79dedea7, 0x7adfdfa5, + 0x3be0e0db, 0x38e1e1d9, 0x3de2e2df, 0x3ee3e3dd, + 0x37e4e4d3, 0x34e5e5d1, 0x31e6e6d7, 0x32e7e7d5, + 0x23e8e8cb, 0x20e9e9c9, 0x25eaeacf, 0x26ebebcd, + 0x2fececc3, 0x2cededc1, 0x29eeeec7, 0x2aefefc5, + 0x0bf0f0fb, 0x08f1f1f9, 0x0df2f2ff, 0x0ef3f3fd, + 0x07f4f4f3, 0x04f5f5f1, 0x01f6f6f7, 0x02f7f7f5, + 0x13f8f8eb, 0x10f9f9e9, 0x15fafaef, 0x16fbfbed, + 0x1ffcfce3, 0x1cfdfde1, 0x19fefee7, 0x1affffe5, + }, { + /* Inverse MixColumns lookup table */ + 0x00000000, 0x0b0d090e, 0x161a121c, 0x1d171b12, + 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, + 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, + 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, + 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, + 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, + 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, + 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, + 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, + 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, + 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, + 0x0fe75793, 0x04ea5e9d, 0x19fd458f, 0x12f04c81, + 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, + 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, + 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, + 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, + 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, + 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, + 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, + 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, + 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, + 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, + 0x1ed5ae3d, 0x15d8a733, 0x08cfbc21, 0x03c2b52f, + 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, + 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, + 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, + 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, + 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, + 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, + 0x1132f9ae, 0x1a3ff0a0, 0x0728ebb2, 0x0c25e2bc, + 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, + 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, + 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, + 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, + 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, + 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, + 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, + 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, + 0x1f6234d1, 0x146f3ddf, 0x097826cd, 0x02752fc3, + 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, + 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, + 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, + 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, + 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, + 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, + 0x10856342, 0x1b886a4c, 0x069f715e, 0x0d927850, + 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, + 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, + 0x01b79aec, 0x0aba93e2, 0x17ad88f0, 0x1ca081fe, + 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, + 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, + 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, + 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, + 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, + 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, + 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, + 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, + 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, + 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, + 0x0e50cd7f, 0x055dc471, 0x184adf63, 0x1347d66d, + 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, + 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, + 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, + 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d, + } }; + union AES_STATE st = { .l = { + float64_val(env->vfp.regs[rm]), + float64_val(env->vfp.regs[rm + 1]) + } }; + int i; + + assert(decrypt < 2); + + for (i = 0; i < 16; i += 4) { + st.cols[i >> 2] = cpu_to_le32( + mc[decrypt][st.bytes[i]] ^ + rol32(mc[decrypt][st.bytes[i + 1]], 8) ^ + rol32(mc[decrypt][st.bytes[i + 2]], 16) ^ + rol32(mc[decrypt][st.bytes[i + 3]], 24)); + } + + env->vfp.regs[rd] = make_float64(st.l[0]); + env->vfp.regs[rd + 1] = make_float64(st.l[1]); +} diff --git a/target-arm/helper.h b/target-arm/helper.h index d459a39e46..496eb6511c 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -463,4 +463,7 @@ DEF_HELPER_3(neon_qzip8, void, env, i32, i32) DEF_HELPER_3(neon_qzip16, void, env, i32, i32) DEF_HELPER_3(neon_qzip32, void, env, i32, i32) +DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32) +DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32) + #include "exec/def-helper.h" diff --git a/target-arm/translate.c b/target-arm/translate.c index 8c479ff9a8..913146752a 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -4592,6 +4592,8 @@ static const uint8_t neon_3r_sizes[] = { #define NEON_2RM_VREV16 2 #define NEON_2RM_VPADDL 4 #define NEON_2RM_VPADDL_U 5 +#define NEON_2RM_AESE 6 /* Includes AESD */ +#define NEON_2RM_AESMC 7 /* Includes AESIMC */ #define NEON_2RM_VCLS 8 #define NEON_2RM_VCLZ 9 #define NEON_2RM_VCNT 10 @@ -4649,6 +4651,8 @@ static const uint8_t neon_2rm_sizes[] = { [NEON_2RM_VREV16] = 0x1, [NEON_2RM_VPADDL] = 0x7, [NEON_2RM_VPADDL_U] = 0x7, + [NEON_2RM_AESE] = 0x1, + [NEON_2RM_AESMC] = 0x1, [NEON_2RM_VCLS] = 0x7, [NEON_2RM_VCLZ] = 0x7, [NEON_2RM_VCNT] = 0x1, @@ -6184,6 +6188,28 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp3); break; + case NEON_2RM_AESE: case NEON_2RM_AESMC: + if (!arm_feature(env, ARM_FEATURE_V8_AES) + || ((rm | rd) & 1)) { + return 1; + } + tmp = tcg_const_i32(rd); + tmp2 = tcg_const_i32(rm); + + /* Bit 6 is the lowest opcode bit; it distinguishes between + * encryption (AESE/AESMC) and decryption (AESD/AESIMC) + */ + tmp3 = tcg_const_i32(extract32(insn, 6, 1)); + + if (op == NEON_2RM_AESE) { + gen_helper_crypto_aese(cpu_env, tmp, tmp2, tmp3); + } else { + gen_helper_crypto_aesmc(cpu_env, tmp, tmp2, tmp3); + } + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp3); + break; default: elementwise: for (pass = 0; pass < (q ? 4 : 2); pass++) { From 4b6fedcac0f51157ef042cde80d5dc5d0c9ef8a4 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Tue, 17 Dec 2013 19:42:26 +0000 Subject: [PATCH 02/62] rename pflash_t member width to bank_width Rename the 'width' member of the pflash_t structure in preparation for adding a bank_width member. Signed-off-by: Roy Franz Reviewed-by: Peter Maydell Message-id: 1386279359-32286-2-git-send-email-roy.franz@linaro.org Signed-off-by: Peter Maydell --- hw/block/pflash_cfi01.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 018a9677ba..a0d7a161f4 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -71,7 +71,7 @@ struct pflash_t { BlockDriverState *bs; uint32_t nb_blocs; uint64_t sector_len; - uint8_t width; + uint8_t bank_width; uint8_t be; uint8_t wcycle; /* if 0, the flash is read normally */ int ro; @@ -126,10 +126,11 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, ret = -1; boff = offset & 0xFF; /* why this here ?? */ - if (pfl->width == 2) + if (pfl->bank_width == 2) { boff = boff >> 1; - else if (pfl->width == 4) + } else if (pfl->bank_width == 4) { boff = boff >> 2; + } #if 0 DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n", @@ -665,7 +666,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) pfl->cfi_table[0x28] = 0x02; pfl->cfi_table[0x29] = 0x00; /* Max number of bytes in multi-bytes write */ - if (pfl->width == 1) { + if (pfl->bank_width == 1) { pfl->cfi_table[0x2A] = 0x08; } else { pfl->cfi_table[0x2A] = 0x0B; @@ -706,7 +707,7 @@ static Property pflash_cfi01_properties[] = { DEFINE_PROP_DRIVE("drive", struct pflash_t, bs), DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), - DEFINE_PROP_UINT8("width", struct pflash_t, width, 0), + DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), @@ -745,8 +746,8 @@ pflash_t *pflash_cfi01_register(hwaddr base, DeviceState *qdev, const char *name, hwaddr size, BlockDriverState *bs, - uint32_t sector_len, int nb_blocs, int width, - uint16_t id0, uint16_t id1, + uint32_t sector_len, int nb_blocs, + int bank_width, uint16_t id0, uint16_t id1, uint16_t id2, uint16_t id3, int be) { DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); @@ -756,7 +757,7 @@ pflash_t *pflash_cfi01_register(hwaddr base, } qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); qdev_prop_set_uint64(dev, "sector-length", sector_len); - qdev_prop_set_uint8(dev, "width", width); + qdev_prop_set_uint8(dev, "width", bank_width); qdev_prop_set_uint8(dev, "big-endian", !!be); qdev_prop_set_uint16(dev, "id0", id0); qdev_prop_set_uint16(dev, "id1", id1); From 1997b48527c38fe8cdbbb3df82ed79aa3ee88b83 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Tue, 17 Dec 2013 19:42:26 +0000 Subject: [PATCH 03/62] Add device-width property to pflash_cfi01 The width of the devices that make up the flash interface is required to mask certain commands, in particular the write length for buffered writes. This length will be presented to each device on the interface by the program writing the flash, and the flash emulation code needs to be able to determine the length of the write as recieved by each flash device. The device-width defaults to the bank width which should maintain existing behavior for platforms that don't need this change. This change is required to support buffered writes on the vexpress platform that has a 32 bit flash interface with 2 16 bit devices on it. Signed-off-by: Roy Franz Message-id: 1386279359-32286-3-git-send-email-roy.franz@linaro.org Signed-off-by: Peter Maydell --- hw/block/pflash_cfi01.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index a0d7a161f4..a458ad62e9 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -40,6 +40,7 @@ #include "hw/block/flash.h" #include "block/block.h" #include "qemu/timer.h" +#include "qemu/bitops.h" #include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "hw/sysbus.h" @@ -72,6 +73,7 @@ struct pflash_t { uint32_t nb_blocs; uint64_t sector_len; uint8_t bank_width; + uint8_t device_width; /* If 0, device width not specified. */ uint8_t be; uint8_t wcycle; /* if 0, the flash is read normally */ int ro; @@ -379,6 +381,14 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, break; case 0xe8: + /* Mask writeblock size based on device width, or bank width if + * device width not specified. + */ + if (pfl->device_width) { + value = extract32(value, 0, pfl->device_width * 8); + } else { + value = extract32(value, 0, pfl->bank_width * 8); + } DPRINTF("%s: block write of %x bytes\n", __func__, value); pfl->counter = value; pfl->wcycle++; @@ -708,6 +718,7 @@ static Property pflash_cfi01_properties[] = { DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), + DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0), DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), From 2003889f67755d47ab355c7813c587adb204eeea Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Tue, 17 Dec 2013 19:42:26 +0000 Subject: [PATCH 04/62] return status for each NOR flash device Now that we know how wide each flash device that makes up the bank is, return status for each device in the bank. Leave existing code that treats 32 bit wide banks as composed of two 16 bit devices as otherwise we may break configurations that do not set the device_width propery. Signed-off-by: Roy Franz Message-id: 1386279359-32286-4-git-send-email-roy.franz@linaro.org Signed-off-by: Peter Maydell --- hw/block/pflash_cfi01.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index a458ad62e9..82a2519113 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -193,9 +193,20 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, case 0x60: /* Block /un)lock */ case 0x70: /* Status Register */ case 0xe8: /* Write block */ - /* Status register read */ + /* Status register read. Return status from each device in + * bank. + */ ret = pfl->status; - if (width > 2) { + if (pfl->device_width && width > pfl->device_width) { + int shift = pfl->device_width * 8; + while (shift + pfl->device_width * 8 <= width * 8) { + ret |= pfl->status << shift; + shift += pfl->device_width * 8; + } + } else if (!pfl->device_width && width > 2) { + /* Handle 32 bit flash cases where device width is not + * set. (Existing behavior before device width added.) + */ ret |= pfl->status << 16; } DPRINTF("%s: status %x\n", __func__, ret); From b8433303fbc5a4a694adb0f0aff5059442ae63e3 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Tue, 17 Dec 2013 19:42:26 +0000 Subject: [PATCH 05/62] Set proper device-width for vexpress flash Create vexpress specific pflash registration function which properly configures the device-width of 16 bits (2 bytes) for the NOR flash on the vexpress platform. This change is required for buffered flash writes to work properly. Signed-off-by: Roy Franz Message-id: 1386279359-32286-5-git-send-email-roy.franz@linaro.org Signed-off-by: Peter Maydell --- hw/arm/vexpress.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index f48de00a1a..939b468bc8 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -480,6 +480,36 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt) } } + +/* Open code a private version of pflash registration since we + * need to set non-default device width for VExpress platform. + */ +static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, + DriveInfo *di) +{ + DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + + if (di && qdev_prop_set_drive(dev, "drive", di->bdrv)) { + abort(); + } + + qdev_prop_set_uint32(dev, "num-blocks", + VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE); + qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE); + qdev_prop_set_uint8(dev, "width", 4); + qdev_prop_set_uint8(dev, "device-width", 2); + qdev_prop_set_uint8(dev, "big-endian", 0); + qdev_prop_set_uint16(dev, "id0", 0x00); + qdev_prop_set_uint16(dev, "id1", 0x89); + qdev_prop_set_uint16(dev, "id2", 0x00); + qdev_prop_set_uint16(dev, "id3", 0x18); + qdev_prop_set_string(dev, "name", name); + qdev_init_nofail(dev); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01"); +} + static void vexpress_common_init(VEDBoardInfo *daughterboard, QEMUMachineInitArgs *args) { @@ -561,11 +591,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard, sysbus_create_simple("pl111", map[VE_CLCD], pic[14]); dinfo = drive_get_next(IF_PFLASH); - pflash0 = pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0", - VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, - VEXPRESS_FLASH_SECT_SIZE, - VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4, - 0x00, 0x89, 0x00, 0x18, 0); + pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0", + dinfo); if (!pflash0) { fprintf(stderr, "vexpress: error registering flash 0.\n"); exit(1); @@ -580,11 +607,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard, } dinfo = drive_get_next(IF_PFLASH); - if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1", - VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, - VEXPRESS_FLASH_SECT_SIZE, - VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4, - 0x00, 0x89, 0x00, 0x18, 0)) { + if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1", + dinfo)) { fprintf(stderr, "vexpress: error registering flash 1.\n"); exit(1); } From fa21a7b13e97120c789eda1dbae87a9bcb1efe09 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Tue, 17 Dec 2013 19:42:27 +0000 Subject: [PATCH 06/62] Add max device width parameter for NOR devices For handling CFI and device ID reads, we need to not only know the width that a NOR flash device is configured for, but also its maximum width. The maximum width addressing mode is used for multi-width parts no matter which width they are configured for. The most common case is x16 parts that also support x8 mode. When configured for x8 operation these devices respond to CFI and device ID requests differently than native x8 NOR parts. Signed-off-by: Roy Franz Message-id: 1386279359-32286-6-git-send-email-roy.franz@linaro.org [PMM: Added comment explaining the semantics of width vs device-width vs max-device-width] Signed-off-by: Peter Maydell --- hw/block/pflash_cfi01.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 82a2519113..de7a6c0847 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -74,6 +74,7 @@ struct pflash_t { uint64_t sector_len; uint8_t bank_width; uint8_t device_width; /* If 0, device width not specified. */ + uint8_t max_device_width; /* max device width in bytes */ uint8_t be; uint8_t wcycle; /* if 0, the flash is read normally */ int ro; @@ -635,6 +636,13 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) pfl->ro = 0; } + /* Default to devices being used at their maximum device width. This was + * assumed before the device_width support was added. + */ + if (!pfl->max_device_width) { + pfl->max_device_width = pfl->device_width; + } + pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); pfl->wcycle = 0; pfl->cmd = 0; @@ -728,8 +736,25 @@ static Property pflash_cfi01_properties[] = { DEFINE_PROP_DRIVE("drive", struct pflash_t, bs), DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), + /* width here is the overall width of this QEMU device in bytes. + * The QEMU device may be emulating a number of flash devices + * wired up in parallel; the width of each individual flash + * device should be specified via device-width. If the individual + * devices have a maximum width which is greater than the width + * they are being used for, this maximum width should be set via + * max-device-width (which otherwise defaults to device-width). + * So for instance a 32-bit wide QEMU flash device made from four + * 16-bit flash devices used in 8-bit wide mode would be configured + * with width = 4, device-width = 1, max-device-width = 2. + * + * If device-width is not specified we default to backwards + * compatible behaviour which is a bad emulation of two + * 16 bit devices making up a 32 bit wide QEMU device. This + * is deprecated for new uses of this device. + */ DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0), + DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0), DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), From 4433e660e3ff19747d9ca7fd3873407ecfb276bf Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Tue, 17 Dec 2013 19:42:27 +0000 Subject: [PATCH 07/62] Fix CFI query responses for NOR flash This change fixes the CFI query responses to handle NOR device widths that are different from the bank width. Support is also added for multi-width devices in a x8 configuration. This is typically x8/x16 devices, but the CFI specification mentions x8/x32 devices so those should be supported as well if they exist. The query response data is now replicated per-device in the bank, and is adjusted for x16 or x32 parts configured in x8 mode. The existing code is left in place for boards that have not been updated to specify an explicit device_width. The VExpress board has been updated in an earlier patch in this series so this is the only board currently affected. Signed-off-by: Roy Franz Message-id: 1386279359-32286-7-git-send-email-roy.franz@linaro.org [PMM: fixed a few formatting nits] Signed-off-by: Peter Maydell --- hw/block/pflash_cfi01.c | 105 +++++++++++++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 11 deletions(-) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index de7a6c0847..8fd50fb748 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -119,6 +119,67 @@ static void pflash_timer (void *opaque) pfl->cmd = 0; } +/* Perform a CFI query based on the bank width of the flash. + * If this code is called we know we have a device_width set for + * this flash. + */ +static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) +{ + int i; + uint32_t resp = 0; + hwaddr boff; + + /* Adjust incoming offset to match expected device-width + * addressing. CFI query addresses are always specified in terms of + * the maximum supported width of the device. This means that x8 + * devices and x8/x16 devices in x8 mode behave differently. For + * devices that are not used at their max width, we will be + * provided with addresses that use higher address bits than + * expected (based on the max width), so we will shift them lower + * so that they will match the addresses used when + * device_width==max_device_width. + */ + boff = offset >> (ctz32(pfl->bank_width) + + ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); + + if (boff > pfl->cfi_len) { + return 0; + } + /* Now we will construct the CFI response generated by a single + * device, then replicate that for all devices that make up the + * bus. For wide parts used in x8 mode, CFI query responses + * are different than native byte-wide parts. + */ + resp = pfl->cfi_table[boff]; + if (pfl->device_width != pfl->max_device_width) { + /* The only case currently supported is x8 mode for a + * wider part. + */ + if (pfl->device_width != 1 || pfl->bank_width > 4) { + DPRINTF("%s: Unsupported device configuration: " + "device_width=%d, max_device_width=%d\n", + __func__, pfl->device_width, + pfl->max_device_width); + return 0; + } + /* CFI query data is repeated, rather than zero padded for + * wide devices used in x8 mode. + */ + for (i = 1; i < pfl->max_device_width; i++) { + resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]); + } + } + /* Replicate responses for each device in bank. */ + if (pfl->device_width < pfl->bank_width) { + for (i = pfl->device_width; + i < pfl->bank_width; i += pfl->device_width) { + resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); + } + } + + return resp; +} + static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, int width, int be) { @@ -127,13 +188,6 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, uint8_t *p; ret = -1; - boff = offset & 0xFF; /* why this here ?? */ - - if (pfl->bank_width == 2) { - boff = boff >> 1; - } else if (pfl->bank_width == 4) { - boff = boff >> 2; - } #if 0 DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n", @@ -213,6 +267,13 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, DPRINTF("%s: status %x\n", __func__, ret); break; case 0x90: + boff = offset & 0xFF; + if (pfl->bank_width == 2) { + boff = boff >> 1; + } else if (pfl->bank_width == 4) { + boff = boff >> 2; + } + switch (boff) { case 0: ret = pfl->ident0 << 8 | pfl->ident1; @@ -230,10 +291,32 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, } break; case 0x98: /* Query mode */ - if (boff > pfl->cfi_len) - ret = 0; - else - ret = pfl->cfi_table[boff]; + if (!pfl->device_width) { + /* Preserve old behavior if device width not specified */ + boff = offset & 0xFF; + if (pfl->bank_width == 2) { + boff = boff >> 1; + } else if (pfl->bank_width == 4) { + boff = boff >> 2; + } + + if (boff > pfl->cfi_len) { + ret = 0; + } else { + ret = pfl->cfi_table[boff]; + } + } else { + /* If we have a read larger than the bank_width, combine multiple + * CFI queries into a single response. + */ + int i; + for (i = 0; i < width; i += pfl->bank_width) { + ret = deposit32(ret, i * 8, pfl->bank_width * 8, + pflash_cfi_query(pfl, + offset + i * pfl->bank_width)); + } + } + break; } return ret; From 0163a2dc80b52553a478fa6e60f09cef4b338d42 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Tue, 17 Dec 2013 19:42:27 +0000 Subject: [PATCH 08/62] Fix NOR flash device ID reading Fix NOR flash manufacturer and device ID reading. This now properly takes into account device widths and device max widths as required. The reading of these IDs uses the same max_width dependent addressing as CFI queries. The old code remains for chips that don't specify a device width, as the new code relies on a device width being set in order to properly operate. The existing code seems very broken. Only ident0 and ident1 are used in the new code, as other fields relate to the lock state of blocks in flash. The VExpress flash configuration has been updated to match the new code, as the existing definition was 'wrong' in order to return the expected results with the broken device ID code. Signed-off-by: Roy Franz Message-id: 1386279359-32286-8-git-send-email-roy.franz@linaro.org Signed-off-by: Peter Maydell --- hw/arm/vexpress.c | 6 +-- hw/block/pflash_cfi01.c | 105 ++++++++++++++++++++++++++++++++-------- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 939b468bc8..aaa863e481 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -499,10 +499,10 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, qdev_prop_set_uint8(dev, "width", 4); qdev_prop_set_uint8(dev, "device-width", 2); qdev_prop_set_uint8(dev, "big-endian", 0); - qdev_prop_set_uint16(dev, "id0", 0x00); - qdev_prop_set_uint16(dev, "id1", 0x89); + qdev_prop_set_uint16(dev, "id0", 0x89); + qdev_prop_set_uint16(dev, "id1", 0x18); qdev_prop_set_uint16(dev, "id2", 0x00); - qdev_prop_set_uint16(dev, "id3", 0x18); + qdev_prop_set_uint16(dev, "id3", 0x00); qdev_prop_set_string(dev, "name", name); qdev_init_nofail(dev); diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 8fd50fb748..0c95d53dca 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -180,6 +180,58 @@ static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) return resp; } + + +/* Perform a device id query based on the bank width of the flash. */ +static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) +{ + int i; + uint32_t resp; + hwaddr boff; + + /* Adjust incoming offset to match expected device-width + * addressing. Device ID read addresses are always specified in + * terms of the maximum supported width of the device. This means + * that x8 devices and x8/x16 devices in x8 mode behave + * differently. For devices that are not used at their max width, + * we will be provided with addresses that use higher address bits + * than expected (based on the max width), so we will shift them + * lower so that they will match the addresses used when + * device_width==max_device_width. + */ + boff = offset >> (ctz32(pfl->bank_width) + + ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); + + /* Mask off upper bits which may be used in to query block + * or sector lock status at other addresses. + * Offsets 2/3 are block lock status, is not emulated. + */ + switch (boff & 0xFF) { + case 0: + resp = pfl->ident0; + DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); + break; + case 1: + resp = pfl->ident1; + DPRINTF("%s: Device ID Code %04x\n", __func__, ret); + break; + default: + DPRINTF("%s: Read Device Information offset=%x\n", __func__, + (unsigned)offset); + return 0; + break; + } + /* Replicate responses for each device in bank. */ + if (pfl->device_width < pfl->bank_width) { + for (i = pfl->device_width; + i < pfl->bank_width; i += pfl->device_width) { + resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); + } + } + + return resp; +} + static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, int width, int be) { @@ -267,27 +319,40 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, DPRINTF("%s: status %x\n", __func__, ret); break; case 0x90: - boff = offset & 0xFF; - if (pfl->bank_width == 2) { - boff = boff >> 1; - } else if (pfl->bank_width == 4) { - boff = boff >> 2; - } + if (!pfl->device_width) { + /* Preserve old behavior if device width not specified */ + boff = offset & 0xFF; + if (pfl->bank_width == 2) { + boff = boff >> 1; + } else if (pfl->bank_width == 4) { + boff = boff >> 2; + } - switch (boff) { - case 0: - ret = pfl->ident0 << 8 | pfl->ident1; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); - break; - case 1: - ret = pfl->ident2 << 8 | pfl->ident3; - DPRINTF("%s: Device ID Code %04x\n", __func__, ret); - break; - default: - DPRINTF("%s: Read Device Information boff=%x\n", __func__, - (unsigned)boff); - ret = 0; - break; + switch (boff) { + case 0: + ret = pfl->ident0 << 8 | pfl->ident1; + DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); + break; + case 1: + ret = pfl->ident2 << 8 | pfl->ident3; + DPRINTF("%s: Device ID Code %04x\n", __func__, ret); + break; + default: + DPRINTF("%s: Read Device Information boff=%x\n", __func__, + (unsigned)boff); + ret = 0; + break; + } + } else { + /* If we have a read larger than the bank_width, combine multiple + * manufacturer/device ID queries into a single response. + */ + int i; + for (i = 0; i < width; i += pfl->bank_width) { + ret = deposit32(ret, i * 8, pfl->bank_width * 8, + pflash_devid_query(pfl, + offset + i * pfl->bank_width)); + } } break; case 0x98: /* Query mode */ From 3671cd879a2666ca1d6e8820a319924be25d6746 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 17 Dec 2013 19:42:27 +0000 Subject: [PATCH 09/62] target-arm/helper.c: Allow cp15.c15 dummy override The cp15.c15 space is implementation defined. Currently there is a dummy placeholder register RAZing it. Allow overriding of this RAZ so implementations of specific registers can take precedence. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: ed1bacec56dae00cb398c798f8240e8e685f949c.1387160489.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- target-arm/helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 5e5e5aad2b..71d6be3793 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1338,7 +1338,8 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { */ { .name = "C15_IMPDEF", .cp = 15, .crn = 15, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL1_RW, + .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE | ARM_CP_OVERRIDE, .resetvalue = 0 }, REGINFO_SENTINEL }; From d8ba780b6a17020aadea479ad96ed9fe3bb10661 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 17 Dec 2013 19:42:28 +0000 Subject: [PATCH 10/62] target-arm: Define and use ARM_FEATURE_CBAR Some processors (notably A9 within Highbank) define and use the CP15 configuration base address (CBAR). This is vendor specific so its best implemented as a CPU property (otherwise we would need vendor specific child classes for every ARM implementation). This patch prepares support for converting CBAR reset value to a CPU property by moving the CP registration out of the CPU init fn, as registration will need to happen at realize time to pick up any property updates. The easiest way to do this is via definition of a new ARM_FEATURE to flag the existence of the register. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: 9f697ef1e2ee60a3b9ef971a7f3bc3fa6752a9b7.1387160489.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- target-arm/cpu.c | 12 +++--------- target-arm/cpu.h | 1 + target-arm/helper.c | 9 +++++++++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index a03743f4ee..f21e995e2f 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -617,6 +617,7 @@ static void cortex_a9_initfn(Object *obj) * and valid configurations; we don't model A9UP). */ set_feature(&cpu->env, ARM_FEATURE_V7MP); + set_feature(&cpu->env, ARM_FEATURE_CBAR); cpu->midr = 0x410fc090; cpu->reset_fpsid = 0x41033090; cpu->mvfr0 = 0x11110222; @@ -639,15 +640,7 @@ static void cortex_a9_initfn(Object *obj) cpu->clidr = (1 << 27) | (1 << 24) | 3; cpu->ccsidr[0] = 0xe00fe015; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x200fe015; /* 16k L1 icache. */ - { - ARMCPRegInfo cbar = { - .name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, - .opc2 = 0, .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar, - .fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address) - }; - define_one_arm_cp_reg(cpu, &cbar); - define_arm_cp_regs(cpu, cortexa9_cp_reginfo); - } + define_arm_cp_regs(cpu, cortexa9_cp_reginfo); } #ifndef CONFIG_USER_ONLY @@ -686,6 +679,7 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CBAR); set_feature(&cpu->env, ARM_FEATURE_LPAE); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; cpu->midr = 0x412fc0f1; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 00fd80d3a7..c6c19e9641 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -468,6 +468,7 @@ enum arm_features { ARM_FEATURE_V8, ARM_FEATURE_AARCH64, /* supports 64 bit mode */ ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */ + ARM_FEATURE_CBAR, /* has cp15 CBAR */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target-arm/helper.c b/target-arm/helper.c index 71d6be3793..cfbb14cbf3 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1745,6 +1745,15 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &auxcr); } + if (arm_feature(env, ARM_FEATURE_CBAR)) { + ARMCPRegInfo cbar = { + .name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, .opc2 = 0, + .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar, + .fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address) + }; + define_one_arm_cp_reg(cpu, &cbar); + } + /* Generic registers whose values depend on the implementation */ { ARMCPRegInfo sctlr = { From 07a5b0d21e450842e3474546366593a5893e8c61 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 17 Dec 2013 19:42:28 +0000 Subject: [PATCH 11/62] target-arm/cpu: Convert reset CBAR to a property The reset value of the CP15 CBAR is a vendor (machine) configurable property. If ARM_FEATURE_CBAR is set, add it as a property at post_init time. Signed-off-by: Peter Crosthwaite Message-id: 2f1eec3f912135deea6252360e03645003d12e0a.1387160489.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- target-arm/cpu.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index f21e995e2f..fd04b361f9 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "qemu-common.h" #include "hw/qdev-properties.h" +#include "qapi/qmp/qerror.h" #if !defined(CONFIG_USER_ONLY) #include "hw/loader.h" #endif @@ -231,6 +232,21 @@ static void arm_cpu_initfn(Object *obj) } } +static Property arm_cpu_reset_cbar_property = + DEFINE_PROP_UINT32("reset-cbar", ARMCPU, reset_cbar, 0); + +static void arm_cpu_post_init(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + Error *err = NULL; + + if (arm_feature(&cpu->env, ARM_FEATURE_CBAR)) { + qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property, + &err); + assert_no_error(err); + } +} + static void arm_cpu_finalizefn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -994,6 +1010,7 @@ static const TypeInfo arm_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(ARMCPU), .instance_init = arm_cpu_initfn, + .instance_post_init = arm_cpu_post_init, .instance_finalize = arm_cpu_finalizefn, .abstract = true, .class_size = sizeof(ARMCPUClass), From f282f29626b6ce65641fb5f8cb631aaade0e51c5 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 17 Dec 2013 19:42:28 +0000 Subject: [PATCH 12/62] arm/highbank: Use object_new() rather than cpu_arm_init() To allow the machine model to set device properties before CPU realization. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: 8c671e500390c8be0cc363e887e32867d1d1b0d2.1387160489.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/highbank.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index fe98ef10cb..1d19d8f25e 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -26,6 +26,7 @@ #include "hw/boards.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" +#include "qemu/error-report.h" #define SMP_BOOT_ADDR 0x100 #define SMP_BOOT_REG 0x40 @@ -229,10 +230,15 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine) } for (n = 0; n < smp_cpus; n++) { + ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); ARMCPU *cpu; - cpu = cpu_arm_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); + Error *err = NULL; + + cpu = ARM_CPU(object_new(object_class_get_name(oc))); + + object_property_set_bool(OBJECT(cpu), true, "realized", &err); + if (err) { + error_report("%s", error_get_pretty(err)); exit(1); } From c0f1ead985e024dd8d01bba650bc753083a2cc2a Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 17 Dec 2013 19:42:28 +0000 Subject: [PATCH 13/62] arm/highbank: Fix CBAR initialisation Fix the CBAR initialisation by using the newly defined static property. CBAR is now set before realization, so the intended value is now actually used. So I have kind of tested this. I booted an ARM kernel on Highbank with the stock Highbank DTB. It doesn't boot (and I will be doing something wrong), but before this patch I got this: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at /workspaces/pcrost/public/linux2.git/arch/arm/mm/ioremap.c:301 __arm_ioremap_pfn_caller+0x180/0x198() CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 3.13.0-rc1-next-20131126-dirty #2 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0x78/0x90) [] (dump_stack) from [] (warn_slowpath_common+0x68/0x84) [] (warn_slowpath_common) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null) from [] (__arm_ioremap_pfn_caller+0x180/0x198) [] (__arm_ioremap_pfn_caller) from [] (__arm_ioremap_caller+0x54/0x5c) [] (__arm_ioremap_caller) from [] (__arm_ioremap+0x18/0x1c) [] (__arm_ioremap) from [] (highbank_init_irq+0x34/0x8c) [] (highbank_init_irq) from [] (init_IRQ+0x28/0x2c) [] (init_IRQ) from [] (start_kernel+0x234/0x398) [] (start_kernel) from [<00008074>] (0x8074) ---[ end trace 3406ff24bd97382f ]--- Which disappears with this patch. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: fedec366aaa512d75093635f523d1dbcb3358361.1387160489.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/highbank.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 1d19d8f25e..cb32325082 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -236,14 +236,16 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine) cpu = ARM_CPU(object_new(object_class_get_name(oc))); + object_property_set_int(OBJECT(cpu), GIC_BASE_ADDR, "reset-cbar", &err); + if (err) { + error_report("%s", error_get_pretty(err)); + exit(1); + } object_property_set_bool(OBJECT(cpu), true, "realized", &err); if (err) { error_report("%s", error_get_pretty(err)); exit(1); } - - /* This will become a QOM property eventually */ - cpu->reset_cbar = GIC_BASE_ADDR; cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ); } From d8bbdcf8d6cb71735014dbd22baf274aea46066f Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 17 Dec 2013 19:42:28 +0000 Subject: [PATCH 14/62] arm/xilinx_zynq: Use object_new() rather than cpu_arm_init() To allow the machine model to set device properties before CPU realization. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: e57658b4506b26ab6b6fadbe6d7827f669f51895.1387160489.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xilinx_zynq.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 46924a0391..1c954a3282 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -25,6 +25,7 @@ #include "sysemu/blockdev.h" #include "hw/loader.h" #include "hw/ssi.h" +#include "qemu/error-report.h" #define NUM_SPI_FLASHES 4 #define NUM_QSPI_FLASHES 2 @@ -102,6 +103,7 @@ static void zynq_init(QEMUMachineInitArgs *args) const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; + ObjectClass *cpu_oc; ARMCPU *cpu; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ext_ram = g_new(MemoryRegion, 1); @@ -110,15 +112,19 @@ static void zynq_init(QEMUMachineInitArgs *args) SysBusDevice *busdev; qemu_irq pic[64]; NICInfo *nd; + Error *err = NULL; int n; if (!cpu_model) { cpu_model = "cortex-a9"; } + cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); - cpu = cpu_arm_init(cpu_model); - if (!cpu) { - fprintf(stderr, "Unable to find CPU definition\n"); + cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc))); + + object_property_set_bool(OBJECT(cpu), true, "realized", &err); + if (err) { + error_report("%s", error_get_pretty(err)); exit(1); } From c25771281ea17c2a09c86ac6a74672f2ec297f8d Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 17 Dec 2013 19:42:29 +0000 Subject: [PATCH 15/62] arm/xilinx_zynq: Implement CBAR initialisation Fix the CBAR initialisation by using the newly defined static property. Zynq will now correctly init the CBAR to the SCU base address. Needed to boot Linux on the xilinx_zynq machine model. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: 8db7d57ebe5418fed397fcc86ea719f98446c178.1387160489.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xilinx_zynq.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 1c954a3282..17251c7a65 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -36,6 +36,8 @@ #define IRQ_OFFSET 32 /* pic interrupts start from index 32 */ +#define MPCORE_PERIPHBASE 0xF8F00000 + static const int dma_irqs[8] = { 46, 47, 48, 49, 72, 73, 74, 75 }; @@ -122,6 +124,11 @@ static void zynq_init(QEMUMachineInitArgs *args) cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc))); + object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err); + if (err) { + error_report("%s", error_get_pretty(err)); + exit(1); + } object_property_set_bool(OBJECT(cpu), true, "realized", &err); if (err) { error_report("%s", error_get_pretty(err)); @@ -160,7 +167,7 @@ static void zynq_init(QEMUMachineInitArgs *args) qdev_prop_set_uint32(dev, "num-cpu", 1); qdev_init_nofail(dev); busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, 0xF8F00000); + sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); From e2cddeeb97a47cd58384cfd86ae79c9bbe829255 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 17 Dec 2013 19:42:29 +0000 Subject: [PATCH 16/62] arm/highbank.c: Fix MPCore periphbase name GIC_BASE_ADDR is not the base address of the GIC. Its clear from the code that this is the base address of the MPCore. Rename to MPCORE_PERIPHBASE accordingly. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: 90798bd3507205c16238b8b19a1a58c5437cf7ca.1387160489.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/highbank.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index cb32325082..c75b425c01 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -28,11 +28,11 @@ #include "exec/address-spaces.h" #include "qemu/error-report.h" -#define SMP_BOOT_ADDR 0x100 -#define SMP_BOOT_REG 0x40 -#define GIC_BASE_ADDR 0xfff10000 +#define SMP_BOOT_ADDR 0x100 +#define SMP_BOOT_REG 0x40 +#define MPCORE_PERIPHBASE 0xfff10000 -#define NIRQ_GIC 160 +#define NIRQ_GIC 160 /* Board init. */ @@ -55,7 +55,7 @@ static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) 0xe1110001, /* tst r1, r1 */ 0x0afffffb, /* beq */ 0xe12fff11, /* bx r1 */ - GIC_BASE_ADDR /* privbase: gic address. */ + MPCORE_PERIPHBASE /* privbase: MPCore peripheral base address. */ }; for (n = 0; n < ARRAY_SIZE(smpboot); n++) { smpboot[n] = tswap32(smpboot[n]); @@ -236,7 +236,8 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine) cpu = ARM_CPU(object_new(object_class_get_name(oc))); - object_property_set_int(OBJECT(cpu), GIC_BASE_ADDR, "reset-cbar", &err); + object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", + &err); if (err) { error_report("%s", error_get_pretty(err)); exit(1); @@ -287,7 +288,7 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine) qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC); qdev_init_nofail(dev); busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, GIC_BASE_ADDR); + sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); } From 68e0a40a5342e798a76ff4c9bf33837e30099ef7 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 17 Dec 2013 19:42:29 +0000 Subject: [PATCH 17/62] ARM: cpu: add "reset_hivecs" property Add an ARM CPU property for the reset value of hivecs as it is a board/SoC configurable setting. The existence of the property is conditional on the ARM CPU not being M class. Signed-off-by: Antony Pavlov Signed-off-by: Peter Crosthwaite Message-id: b04216c6bda4bd163f44a55bba552d0e8267481f.1387160489.git.peter.crosthwaite@xilinx.com [ PC Changes: * Elaborated commit message * refactored to use qdev_property_add_static ] Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- target-arm/cpu-qom.h | 1 + target-arm/cpu.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index f32178a9db..afbd4222c5 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -139,6 +139,7 @@ typedef struct ARMCPU { uint32_t ccsidr[16]; uint32_t reset_cbar; uint32_t reset_auxcr; + bool reset_hivecs; } ARMCPU; #define TYPE_AARCH64_CPU "aarch64-cpu" diff --git a/target-arm/cpu.c b/target-arm/cpu.c index fd04b361f9..3bce1d6fec 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -235,6 +235,9 @@ static void arm_cpu_initfn(Object *obj) static Property arm_cpu_reset_cbar_property = DEFINE_PROP_UINT32("reset-cbar", ARMCPU, reset_cbar, 0); +static Property arm_cpu_reset_hivecs_property = + DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false); + static void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -245,6 +248,12 @@ static void arm_cpu_post_init(Object *obj) &err); assert_no_error(err); } + + if (!arm_feature(&cpu->env, ARM_FEATURE_M)) { + qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property, + &err); + assert_no_error(err); + } } static void arm_cpu_finalizefn(Object *obj) @@ -307,6 +316,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) set_feature(env, ARM_FEATURE_PXN); } + if (cpu->reset_hivecs) { + cpu->reset_sctlr |= (1 << 13); + } + register_cp_regs_for_features(cpu); arm_cpu_register_gdb_regs_for_features(cpu); From 387f980676aedcc67baddbf5fd1c9169f331f30b Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 17 Dec 2013 19:42:29 +0000 Subject: [PATCH 18/62] ARM: arm_cpu_reset: make it possible to use high vectors for reset_exc If hivecs are being used on reset, the CPU should come out of reset at the hivecs reset vector (0xFFFF0000) Signed-off-by: Antony Pavlov Signed-off-by: Peter Crosthwaite Message-id: 3afc69c4f58f60aa2bbee7b91574a4eb414b1c23.1387160489.git.peter.crosthwaite@xilinx.com [ PC Changes: * Fixed Grammar error in commit message * Elaborated commit message. ] Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- target-arm/cpu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 3bce1d6fec..8288c7db39 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -121,6 +121,11 @@ static void arm_cpu_reset(CPUState *s) env->regs[15] = pc & ~1; } } + + if (env->cp15.c1_sys & (1 << 13)) { + env->regs[15] = 0xFFFF0000; + } + env->vfp.xregs[ARM_VFP_FPEXC] = 0; #endif set_flush_to_zero(1, &env->vfp.standard_fp_status); From b197ebd410f0298ec078c3048f9cfb9f6bfc3b3c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:29 +0000 Subject: [PATCH 19/62] target-arm/kvm: Split 32 bit only code into its own file Split ARM KVM support code which is 32 bit specific out into its own file, which we only compile on 32 bit hosts. This will give us a place to add the 64 bit support code without adding lots of ifdefs to kvm.c. Signed-off-by: Peter Maydell Message-id: 1385645602-18662-2-git-send-email-peter.maydell@linaro.org Reviewed-by: Christoffer Dall --- target-arm/Makefile.objs | 1 + target-arm/kvm.c | 491 ------------------------------------- target-arm/kvm32.c | 515 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 516 insertions(+), 491 deletions(-) create mode 100644 target-arm/kvm32.c diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index f428b5b7d7..869ecd05af 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -1,6 +1,7 @@ obj-y += arm-semi.o obj-$(CONFIG_SOFTMMU) += machine.o obj-$(CONFIG_KVM) += kvm.o +obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o diff --git a/target-arm/kvm.c b/target-arm/kvm.c index f865dac871..5cdb3b95c6 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -100,120 +100,6 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray) } } -static inline void set_feature(uint64_t *features, int feature) -{ - *features |= 1ULL << feature; -} - -bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) -{ - /* Identify the feature bits corresponding to the host CPU, and - * fill out the ARMHostCPUClass fields accordingly. To do this - * we have to create a scratch VM, create a single CPU inside it, - * and then query that CPU for the relevant ID registers. - */ - int i, ret, fdarray[3]; - uint32_t midr, id_pfr0, id_isar0, mvfr1; - uint64_t features = 0; - /* Old kernels may not know about the PREFERRED_TARGET ioctl: however - * we know these will only support creating one kind of guest CPU, - * which is its preferred CPU type. - */ - static const uint32_t cpus_to_try[] = { - QEMU_KVM_ARM_TARGET_CORTEX_A15, - QEMU_KVM_ARM_TARGET_NONE - }; - struct kvm_vcpu_init init; - struct kvm_one_reg idregs[] = { - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 0, 0, 0), - .addr = (uintptr_t)&midr, - }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 1, 0, 0), - .addr = (uintptr_t)&id_pfr0, - }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 2, 0, 0), - .addr = (uintptr_t)&id_isar0, - }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1, - .addr = (uintptr_t)&mvfr1, - }, - }; - - if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { - return false; - } - - ahcc->target = init.target; - - /* This is not strictly blessed by the device tree binding docs yet, - * but in practice the kernel does not care about this string so - * there is no point maintaining an KVM_ARM_TARGET_* -> string table. - */ - ahcc->dtb_compatible = "arm,arm-v7"; - - for (i = 0; i < ARRAY_SIZE(idregs); i++) { - ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]); - if (ret) { - break; - } - } - - kvm_arm_destroy_scratch_host_vcpu(fdarray); - - if (ret) { - return false; - } - - /* Now we've retrieved all the register information we can - * set the feature bits based on the ID register fields. - * We can assume any KVM supporting CPU is at least a v7 - * with VFPv3, LPAE and the generic timers; this in turn implies - * most of the other feature bits, but a few must be tested. - */ - set_feature(&features, ARM_FEATURE_V7); - set_feature(&features, ARM_FEATURE_VFP3); - set_feature(&features, ARM_FEATURE_LPAE); - set_feature(&features, ARM_FEATURE_GENERIC_TIMER); - - switch (extract32(id_isar0, 24, 4)) { - case 1: - set_feature(&features, ARM_FEATURE_THUMB_DIV); - break; - case 2: - set_feature(&features, ARM_FEATURE_ARM_DIV); - set_feature(&features, ARM_FEATURE_THUMB_DIV); - break; - default: - break; - } - - if (extract32(id_pfr0, 12, 4) == 1) { - set_feature(&features, ARM_FEATURE_THUMB2EE); - } - if (extract32(mvfr1, 20, 4) == 1) { - set_feature(&features, ARM_FEATURE_VFP_FP16); - } - if (extract32(mvfr1, 12, 4) == 1) { - set_feature(&features, ARM_FEATURE_NEON); - } - if (extract32(mvfr1, 28, 4) == 1) { - /* FMAC support implies VFPv4 */ - set_feature(&features, ARM_FEATURE_VFP4); - } - - ahcc->features = features; - - return true; -} - static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data) { ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc); @@ -265,144 +151,6 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) return cpu->cpu_index; } -static bool reg_syncs_via_tuple_list(uint64_t regidx) -{ - /* Return true if the regidx is a register we should synchronize - * via the cpreg_tuples array (ie is not a core reg we sync by - * hand in kvm_arch_get/put_registers()) - */ - switch (regidx & KVM_REG_ARM_COPROC_MASK) { - case KVM_REG_ARM_CORE: - case KVM_REG_ARM_VFP: - return false; - default: - return true; - } -} - -static int compare_u64(const void *a, const void *b) -{ - if (*(uint64_t *)a > *(uint64_t *)b) { - return 1; - } - if (*(uint64_t *)a < *(uint64_t *)b) { - return -1; - } - return 0; -} - -int kvm_arch_init_vcpu(CPUState *cs) -{ - struct kvm_vcpu_init init; - int i, ret, arraylen; - uint64_t v; - struct kvm_one_reg r; - struct kvm_reg_list rl; - struct kvm_reg_list *rlp; - ARMCPU *cpu = ARM_CPU(cs); - - if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) { - fprintf(stderr, "KVM is not supported for this guest CPU type\n"); - return -EINVAL; - } - - init.target = cpu->kvm_target; - memset(init.features, 0, sizeof(init.features)); - if (cpu->start_powered_off) { - init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF; - } - ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); - if (ret) { - return ret; - } - /* Query the kernel to make sure it supports 32 VFP - * registers: QEMU's "cortex-a15" CPU is always a - * VFP-D32 core. The simplest way to do this is just - * to attempt to read register d31. - */ - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31; - r.addr = (uintptr_t)(&v); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); - if (ret == -ENOENT) { - return -EINVAL; - } - - /* Populate the cpreg list based on the kernel's idea - * of what registers exist (and throw away the TCG-created list). - */ - rl.n = 0; - ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl); - if (ret != -E2BIG) { - return ret; - } - rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t)); - rlp->n = rl.n; - ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp); - if (ret) { - goto out; - } - /* Sort the list we get back from the kernel, since cpreg_tuples - * must be in strictly ascending order. - */ - qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64); - - for (i = 0, arraylen = 0; i < rlp->n; i++) { - if (!reg_syncs_via_tuple_list(rlp->reg[i])) { - continue; - } - switch (rlp->reg[i] & KVM_REG_SIZE_MASK) { - case KVM_REG_SIZE_U32: - case KVM_REG_SIZE_U64: - break; - default: - fprintf(stderr, "Can't handle size of register in kernel list\n"); - ret = -EINVAL; - goto out; - } - - arraylen++; - } - - cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen); - cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen); - cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes, - arraylen); - cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values, - arraylen); - cpu->cpreg_array_len = arraylen; - cpu->cpreg_vmstate_array_len = arraylen; - - for (i = 0, arraylen = 0; i < rlp->n; i++) { - uint64_t regidx = rlp->reg[i]; - if (!reg_syncs_via_tuple_list(regidx)) { - continue; - } - cpu->cpreg_indexes[arraylen] = regidx; - arraylen++; - } - assert(cpu->cpreg_array_len == arraylen); - - if (!write_kvmstate_to_list(cpu)) { - /* Shouldn't happen unless kernel is inconsistent about - * what registers exist. - */ - fprintf(stderr, "Initial read of kernel register state failed\n"); - ret = -EINVAL; - goto out; - } - - /* Save a copy of the initial register values so that we can - * feed it back to the kernel on VCPU reset. - */ - cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values, - cpu->cpreg_array_len * - sizeof(cpu->cpreg_values[0])); - -out: - g_free(rlp); - return ret; -} - /* We track all the KVM devices which need their memory addresses * passing to the kernel in a list of these structures. * When board init is complete we run through the list and @@ -563,232 +311,6 @@ bool write_list_to_kvmstate(ARMCPU *cpu) return ok; } -typedef struct Reg { - uint64_t id; - int offset; -} Reg; - -#define COREREG(KERNELNAME, QEMUFIELD) \ - { \ - KVM_REG_ARM | KVM_REG_SIZE_U32 | \ - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \ - offsetof(CPUARMState, QEMUFIELD) \ - } - -#define VFPSYSREG(R) \ - { \ - KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \ - KVM_REG_ARM_VFP_##R, \ - offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \ - } - -static const Reg regs[] = { - /* R0_usr .. R14_usr */ - COREREG(usr_regs.uregs[0], regs[0]), - COREREG(usr_regs.uregs[1], regs[1]), - COREREG(usr_regs.uregs[2], regs[2]), - COREREG(usr_regs.uregs[3], regs[3]), - COREREG(usr_regs.uregs[4], regs[4]), - COREREG(usr_regs.uregs[5], regs[5]), - COREREG(usr_regs.uregs[6], regs[6]), - COREREG(usr_regs.uregs[7], regs[7]), - COREREG(usr_regs.uregs[8], usr_regs[0]), - COREREG(usr_regs.uregs[9], usr_regs[1]), - COREREG(usr_regs.uregs[10], usr_regs[2]), - COREREG(usr_regs.uregs[11], usr_regs[3]), - COREREG(usr_regs.uregs[12], usr_regs[4]), - COREREG(usr_regs.uregs[13], banked_r13[0]), - COREREG(usr_regs.uregs[14], banked_r14[0]), - /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */ - COREREG(svc_regs[0], banked_r13[1]), - COREREG(svc_regs[1], banked_r14[1]), - COREREG(svc_regs[2], banked_spsr[1]), - COREREG(abt_regs[0], banked_r13[2]), - COREREG(abt_regs[1], banked_r14[2]), - COREREG(abt_regs[2], banked_spsr[2]), - COREREG(und_regs[0], banked_r13[3]), - COREREG(und_regs[1], banked_r14[3]), - COREREG(und_regs[2], banked_spsr[3]), - COREREG(irq_regs[0], banked_r13[4]), - COREREG(irq_regs[1], banked_r14[4]), - COREREG(irq_regs[2], banked_spsr[4]), - /* R8_fiq .. R14_fiq and SPSR_fiq */ - COREREG(fiq_regs[0], fiq_regs[0]), - COREREG(fiq_regs[1], fiq_regs[1]), - COREREG(fiq_regs[2], fiq_regs[2]), - COREREG(fiq_regs[3], fiq_regs[3]), - COREREG(fiq_regs[4], fiq_regs[4]), - COREREG(fiq_regs[5], banked_r13[5]), - COREREG(fiq_regs[6], banked_r14[5]), - COREREG(fiq_regs[7], banked_spsr[5]), - /* R15 */ - COREREG(usr_regs.uregs[15], regs[15]), - /* VFP system registers */ - VFPSYSREG(FPSID), - VFPSYSREG(MVFR1), - VFPSYSREG(MVFR0), - VFPSYSREG(FPEXC), - VFPSYSREG(FPINST), - VFPSYSREG(FPINST2), -}; - -int kvm_arch_put_registers(CPUState *cs, int level) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - struct kvm_one_reg r; - int mode, bn; - int ret, i; - uint32_t cpsr, fpscr; - - /* Make sure the banked regs are properly set */ - mode = env->uncached_cpsr & CPSR_M; - bn = bank_number(mode); - if (mode == ARM_CPU_MODE_FIQ) { - memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t)); - } else { - memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); - } - env->banked_r13[bn] = env->regs[13]; - env->banked_r14[bn] = env->regs[14]; - env->banked_spsr[bn] = env->spsr; - - /* Now we can safely copy stuff down to the kernel */ - for (i = 0; i < ARRAY_SIZE(regs); i++) { - r.id = regs[i].id; - r.addr = (uintptr_t)(env) + regs[i].offset; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); - if (ret) { - return ret; - } - } - - /* Special cases which aren't a single CPUARMState field */ - cpsr = cpsr_read(env); - r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr); - r.addr = (uintptr_t)(&cpsr); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); - if (ret) { - return ret; - } - - /* VFP registers */ - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; - for (i = 0; i < 32; i++) { - r.addr = (uintptr_t)(&env->vfp.regs[i]); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); - if (ret) { - return ret; - } - r.id++; - } - - r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | - KVM_REG_ARM_VFP_FPSCR; - fpscr = vfp_get_fpscr(env); - r.addr = (uintptr_t)&fpscr; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); - if (ret) { - return ret; - } - - /* Note that we do not call write_cpustate_to_list() - * here, so we are only writing the tuple list back to - * KVM. This is safe because nothing can change the - * CPUARMState cp15 fields (in particular gdb accesses cannot) - * and so there are no changes to sync. In fact syncing would - * be wrong at this point: for a constant register where TCG and - * KVM disagree about its value, the preceding write_list_to_cpustate() - * would not have had any effect on the CPUARMState value (since the - * register is read-only), and a write_cpustate_to_list() here would - * then try to write the TCG value back into KVM -- this would either - * fail or incorrectly change the value the guest sees. - * - * If we ever want to allow the user to modify cp15 registers via - * the gdb stub, we would need to be more clever here (for instance - * tracking the set of registers kvm_arch_get_registers() successfully - * managed to update the CPUARMState with, and only allowing those - * to be written back up into the kernel). - */ - if (!write_list_to_kvmstate(cpu)) { - return EINVAL; - } - - return ret; -} - -int kvm_arch_get_registers(CPUState *cs) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - struct kvm_one_reg r; - int mode, bn; - int ret, i; - uint32_t cpsr, fpscr; - - for (i = 0; i < ARRAY_SIZE(regs); i++) { - r.id = regs[i].id; - r.addr = (uintptr_t)(env) + regs[i].offset; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); - if (ret) { - return ret; - } - } - - /* Special cases which aren't a single CPUARMState field */ - r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr); - r.addr = (uintptr_t)(&cpsr); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); - if (ret) { - return ret; - } - cpsr_write(env, cpsr, 0xffffffff); - - /* Make sure the current mode regs are properly set */ - mode = env->uncached_cpsr & CPSR_M; - bn = bank_number(mode); - if (mode == ARM_CPU_MODE_FIQ) { - memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); - } else { - memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t)); - } - env->regs[13] = env->banked_r13[bn]; - env->regs[14] = env->banked_r14[bn]; - env->spsr = env->banked_spsr[bn]; - - /* VFP registers */ - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; - for (i = 0; i < 32; i++) { - r.addr = (uintptr_t)(&env->vfp.regs[i]); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); - if (ret) { - return ret; - } - r.id++; - } - - r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | - KVM_REG_ARM_VFP_FPSCR; - r.addr = (uintptr_t)&fpscr; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); - if (ret) { - return ret; - } - vfp_set_fpscr(env, fpscr); - - if (!write_kvmstate_to_list(cpu)) { - return EINVAL; - } - /* Note that it's OK to have registers which aren't in CPUState, - * so we can ignore a failure return here. - */ - write_list_to_cpustate(cpu); - - return 0; -} - void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { } @@ -802,19 +324,6 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) return 0; } -void kvm_arch_reset_vcpu(CPUState *cs) -{ - /* Feed the kernel back its initial register state */ - ARMCPU *cpu = ARM_CPU(cs); - - memmove(cpu->cpreg_values, cpu->cpreg_reset_values, - cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0])); - - if (!write_list_to_kvmstate(cpu)) { - abort(); - } -} - bool kvm_arch_stop_on_emulation_error(CPUState *cs) { return true; diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c new file mode 100644 index 0000000000..a4fde07969 --- /dev/null +++ b/target-arm/kvm32.c @@ -0,0 +1,515 @@ +/* + * ARM implementation of KVM hooks, 32 bit specific code. + * + * Copyright Christoffer Dall 2009-2010 + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include +#include +#include +#include + +#include + +#include "qemu-common.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "kvm_arm.h" +#include "cpu.h" +#include "hw/arm/arm.h" + +static inline void set_feature(uint64_t *features, int feature) +{ + *features |= 1ULL << feature; +} + +bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) +{ + /* Identify the feature bits corresponding to the host CPU, and + * fill out the ARMHostCPUClass fields accordingly. To do this + * we have to create a scratch VM, create a single CPU inside it, + * and then query that CPU for the relevant ID registers. + */ + int i, ret, fdarray[3]; + uint32_t midr, id_pfr0, id_isar0, mvfr1; + uint64_t features = 0; + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however + * we know these will only support creating one kind of guest CPU, + * which is its preferred CPU type. + */ + static const uint32_t cpus_to_try[] = { + QEMU_KVM_ARM_TARGET_CORTEX_A15, + QEMU_KVM_ARM_TARGET_NONE + }; + struct kvm_vcpu_init init; + struct kvm_one_reg idregs[] = { + { + .id = KVM_REG_ARM | KVM_REG_SIZE_U32 + | ENCODE_CP_REG(15, 0, 0, 0, 0, 0), + .addr = (uintptr_t)&midr, + }, + { + .id = KVM_REG_ARM | KVM_REG_SIZE_U32 + | ENCODE_CP_REG(15, 0, 0, 1, 0, 0), + .addr = (uintptr_t)&id_pfr0, + }, + { + .id = KVM_REG_ARM | KVM_REG_SIZE_U32 + | ENCODE_CP_REG(15, 0, 0, 2, 0, 0), + .addr = (uintptr_t)&id_isar0, + }, + { + .id = KVM_REG_ARM | KVM_REG_SIZE_U32 + | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1, + .addr = (uintptr_t)&mvfr1, + }, + }; + + if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { + return false; + } + + ahcc->target = init.target; + + /* This is not strictly blessed by the device tree binding docs yet, + * but in practice the kernel does not care about this string so + * there is no point maintaining an KVM_ARM_TARGET_* -> string table. + */ + ahcc->dtb_compatible = "arm,arm-v7"; + + for (i = 0; i < ARRAY_SIZE(idregs); i++) { + ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]); + if (ret) { + break; + } + } + + kvm_arm_destroy_scratch_host_vcpu(fdarray); + + if (ret) { + return false; + } + + /* Now we've retrieved all the register information we can + * set the feature bits based on the ID register fields. + * We can assume any KVM supporting CPU is at least a v7 + * with VFPv3, LPAE and the generic timers; this in turn implies + * most of the other feature bits, but a few must be tested. + */ + set_feature(&features, ARM_FEATURE_V7); + set_feature(&features, ARM_FEATURE_VFP3); + set_feature(&features, ARM_FEATURE_LPAE); + set_feature(&features, ARM_FEATURE_GENERIC_TIMER); + + switch (extract32(id_isar0, 24, 4)) { + case 1: + set_feature(&features, ARM_FEATURE_THUMB_DIV); + break; + case 2: + set_feature(&features, ARM_FEATURE_ARM_DIV); + set_feature(&features, ARM_FEATURE_THUMB_DIV); + break; + default: + break; + } + + if (extract32(id_pfr0, 12, 4) == 1) { + set_feature(&features, ARM_FEATURE_THUMB2EE); + } + if (extract32(mvfr1, 20, 4) == 1) { + set_feature(&features, ARM_FEATURE_VFP_FP16); + } + if (extract32(mvfr1, 12, 4) == 1) { + set_feature(&features, ARM_FEATURE_NEON); + } + if (extract32(mvfr1, 28, 4) == 1) { + /* FMAC support implies VFPv4 */ + set_feature(&features, ARM_FEATURE_VFP4); + } + + ahcc->features = features; + + return true; +} + +static bool reg_syncs_via_tuple_list(uint64_t regidx) +{ + /* Return true if the regidx is a register we should synchronize + * via the cpreg_tuples array (ie is not a core reg we sync by + * hand in kvm_arch_get/put_registers()) + */ + switch (regidx & KVM_REG_ARM_COPROC_MASK) { + case KVM_REG_ARM_CORE: + case KVM_REG_ARM_VFP: + return false; + default: + return true; + } +} + +static int compare_u64(const void *a, const void *b) +{ + if (*(uint64_t *)a > *(uint64_t *)b) { + return 1; + } + if (*(uint64_t *)a < *(uint64_t *)b) { + return -1; + } + return 0; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + struct kvm_vcpu_init init; + int i, ret, arraylen; + uint64_t v; + struct kvm_one_reg r; + struct kvm_reg_list rl; + struct kvm_reg_list *rlp; + ARMCPU *cpu = ARM_CPU(cs); + + if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) { + fprintf(stderr, "KVM is not supported for this guest CPU type\n"); + return -EINVAL; + } + + init.target = cpu->kvm_target; + memset(init.features, 0, sizeof(init.features)); + if (cpu->start_powered_off) { + init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF; + } + ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); + if (ret) { + return ret; + } + /* Query the kernel to make sure it supports 32 VFP + * registers: QEMU's "cortex-a15" CPU is always a + * VFP-D32 core. The simplest way to do this is just + * to attempt to read register d31. + */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31; + r.addr = (uintptr_t)(&v); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret == -ENOENT) { + return -EINVAL; + } + + /* Populate the cpreg list based on the kernel's idea + * of what registers exist (and throw away the TCG-created list). + */ + rl.n = 0; + ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl); + if (ret != -E2BIG) { + return ret; + } + rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t)); + rlp->n = rl.n; + ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp); + if (ret) { + goto out; + } + /* Sort the list we get back from the kernel, since cpreg_tuples + * must be in strictly ascending order. + */ + qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64); + + for (i = 0, arraylen = 0; i < rlp->n; i++) { + if (!reg_syncs_via_tuple_list(rlp->reg[i])) { + continue; + } + switch (rlp->reg[i] & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + case KVM_REG_SIZE_U64: + break; + default: + fprintf(stderr, "Can't handle size of register in kernel list\n"); + ret = -EINVAL; + goto out; + } + + arraylen++; + } + + cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen); + cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen); + cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes, + arraylen); + cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values, + arraylen); + cpu->cpreg_array_len = arraylen; + cpu->cpreg_vmstate_array_len = arraylen; + + for (i = 0, arraylen = 0; i < rlp->n; i++) { + uint64_t regidx = rlp->reg[i]; + if (!reg_syncs_via_tuple_list(regidx)) { + continue; + } + cpu->cpreg_indexes[arraylen] = regidx; + arraylen++; + } + assert(cpu->cpreg_array_len == arraylen); + + if (!write_kvmstate_to_list(cpu)) { + /* Shouldn't happen unless kernel is inconsistent about + * what registers exist. + */ + fprintf(stderr, "Initial read of kernel register state failed\n"); + ret = -EINVAL; + goto out; + } + + /* Save a copy of the initial register values so that we can + * feed it back to the kernel on VCPU reset. + */ + cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values, + cpu->cpreg_array_len * + sizeof(cpu->cpreg_values[0])); + +out: + g_free(rlp); + return ret; +} + +typedef struct Reg { + uint64_t id; + int offset; +} Reg; + +#define COREREG(KERNELNAME, QEMUFIELD) \ + { \ + KVM_REG_ARM | KVM_REG_SIZE_U32 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \ + offsetof(CPUARMState, QEMUFIELD) \ + } + +#define VFPSYSREG(R) \ + { \ + KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \ + KVM_REG_ARM_VFP_##R, \ + offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \ + } + +static const Reg regs[] = { + /* R0_usr .. R14_usr */ + COREREG(usr_regs.uregs[0], regs[0]), + COREREG(usr_regs.uregs[1], regs[1]), + COREREG(usr_regs.uregs[2], regs[2]), + COREREG(usr_regs.uregs[3], regs[3]), + COREREG(usr_regs.uregs[4], regs[4]), + COREREG(usr_regs.uregs[5], regs[5]), + COREREG(usr_regs.uregs[6], regs[6]), + COREREG(usr_regs.uregs[7], regs[7]), + COREREG(usr_regs.uregs[8], usr_regs[0]), + COREREG(usr_regs.uregs[9], usr_regs[1]), + COREREG(usr_regs.uregs[10], usr_regs[2]), + COREREG(usr_regs.uregs[11], usr_regs[3]), + COREREG(usr_regs.uregs[12], usr_regs[4]), + COREREG(usr_regs.uregs[13], banked_r13[0]), + COREREG(usr_regs.uregs[14], banked_r14[0]), + /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */ + COREREG(svc_regs[0], banked_r13[1]), + COREREG(svc_regs[1], banked_r14[1]), + COREREG(svc_regs[2], banked_spsr[1]), + COREREG(abt_regs[0], banked_r13[2]), + COREREG(abt_regs[1], banked_r14[2]), + COREREG(abt_regs[2], banked_spsr[2]), + COREREG(und_regs[0], banked_r13[3]), + COREREG(und_regs[1], banked_r14[3]), + COREREG(und_regs[2], banked_spsr[3]), + COREREG(irq_regs[0], banked_r13[4]), + COREREG(irq_regs[1], banked_r14[4]), + COREREG(irq_regs[2], banked_spsr[4]), + /* R8_fiq .. R14_fiq and SPSR_fiq */ + COREREG(fiq_regs[0], fiq_regs[0]), + COREREG(fiq_regs[1], fiq_regs[1]), + COREREG(fiq_regs[2], fiq_regs[2]), + COREREG(fiq_regs[3], fiq_regs[3]), + COREREG(fiq_regs[4], fiq_regs[4]), + COREREG(fiq_regs[5], banked_r13[5]), + COREREG(fiq_regs[6], banked_r14[5]), + COREREG(fiq_regs[7], banked_spsr[5]), + /* R15 */ + COREREG(usr_regs.uregs[15], regs[15]), + /* VFP system registers */ + VFPSYSREG(FPSID), + VFPSYSREG(MVFR1), + VFPSYSREG(MVFR0), + VFPSYSREG(FPEXC), + VFPSYSREG(FPINST), + VFPSYSREG(FPINST2), +}; + +int kvm_arch_put_registers(CPUState *cs, int level) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + struct kvm_one_reg r; + int mode, bn; + int ret, i; + uint32_t cpsr, fpscr; + + /* Make sure the banked regs are properly set */ + mode = env->uncached_cpsr & CPSR_M; + bn = bank_number(mode); + if (mode == ARM_CPU_MODE_FIQ) { + memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t)); + } else { + memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); + } + env->banked_r13[bn] = env->regs[13]; + env->banked_r14[bn] = env->regs[14]; + env->banked_spsr[bn] = env->spsr; + + /* Now we can safely copy stuff down to the kernel */ + for (i = 0; i < ARRAY_SIZE(regs); i++) { + r.id = regs[i].id; + r.addr = (uintptr_t)(env) + regs[i].offset; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + } + + /* Special cases which aren't a single CPUARMState field */ + cpsr = cpsr_read(env); + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr); + r.addr = (uintptr_t)(&cpsr); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + + /* VFP registers */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; + for (i = 0; i < 32; i++) { + r.addr = (uintptr_t)(&env->vfp.regs[i]); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + r.id++; + } + + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | + KVM_REG_ARM_VFP_FPSCR; + fpscr = vfp_get_fpscr(env); + r.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + + /* Note that we do not call write_cpustate_to_list() + * here, so we are only writing the tuple list back to + * KVM. This is safe because nothing can change the + * CPUARMState cp15 fields (in particular gdb accesses cannot) + * and so there are no changes to sync. In fact syncing would + * be wrong at this point: for a constant register where TCG and + * KVM disagree about its value, the preceding write_list_to_cpustate() + * would not have had any effect on the CPUARMState value (since the + * register is read-only), and a write_cpustate_to_list() here would + * then try to write the TCG value back into KVM -- this would either + * fail or incorrectly change the value the guest sees. + * + * If we ever want to allow the user to modify cp15 registers via + * the gdb stub, we would need to be more clever here (for instance + * tracking the set of registers kvm_arch_get_registers() successfully + * managed to update the CPUARMState with, and only allowing those + * to be written back up into the kernel). + */ + if (!write_list_to_kvmstate(cpu)) { + return EINVAL; + } + + return ret; +} + +int kvm_arch_get_registers(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + struct kvm_one_reg r; + int mode, bn; + int ret, i; + uint32_t cpsr, fpscr; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + r.id = regs[i].id; + r.addr = (uintptr_t)(env) + regs[i].offset; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + } + + /* Special cases which aren't a single CPUARMState field */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr); + r.addr = (uintptr_t)(&cpsr); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + cpsr_write(env, cpsr, 0xffffffff); + + /* Make sure the current mode regs are properly set */ + mode = env->uncached_cpsr & CPSR_M; + bn = bank_number(mode); + if (mode == ARM_CPU_MODE_FIQ) { + memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); + } else { + memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t)); + } + env->regs[13] = env->banked_r13[bn]; + env->regs[14] = env->banked_r14[bn]; + env->spsr = env->banked_spsr[bn]; + + /* VFP registers */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; + for (i = 0; i < 32; i++) { + r.addr = (uintptr_t)(&env->vfp.regs[i]); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + r.id++; + } + + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | + KVM_REG_ARM_VFP_FPSCR; + r.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + vfp_set_fpscr(env, fpscr); + + if (!write_kvmstate_to_list(cpu)) { + return EINVAL; + } + /* Note that it's OK to have registers which aren't in CPUState, + * so we can ignore a failure return here. + */ + write_list_to_cpustate(cpu); + + return 0; +} + +void kvm_arch_reset_vcpu(CPUState *cs) +{ + /* Feed the kernel back its initial register state */ + ARMCPU *cpu = ARM_CPU(cs); + + memmove(cpu->cpreg_values, cpu->cpreg_reset_values, + cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0])); + + if (!write_list_to_kvmstate(cpu)) { + abort(); + } +} From d356312fdc8640af929e0dbab61c6e514d47feb8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:30 +0000 Subject: [PATCH 20/62] target-arm: Clean up handling of AArch64 PSTATE The env->pstate field is a little odd since it doesn't strictly speaking represent an architectural register. However it's convenient for QEMU to use it to hold the various PSTATE architectural bits in the same format the architecture specifies for SPSR registers (since this is the same format the kernel uses for signal handlers and the KVM register). Add some structure to how we deal with it: * document what env->pstate is * add some #defines for various bits in it * add helpers for reading/writing it taking account of caching of NZCV, and use them where appropriate * reset it on startup Signed-off-by: Peter Maydell Message-id: 1385645602-18662-3-git-send-email-peter.maydell@linaro.org Reviewed-by: Christoffer Dall --- linux-user/signal.c | 6 ++-- target-arm/cpu.c | 6 ++++ target-arm/cpu.h | 70 ++++++++++++++++++++++++++++++++------ target-arm/gdbstub64.c | 4 +-- target-arm/translate-a64.c | 12 ++++--- 5 files changed, 78 insertions(+), 20 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 7751c47ef1..4e7148a2d6 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1171,7 +1171,7 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf, } __put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); __put_user(env->pc, &sf->uc.tuc_mcontext.pc); - __put_user(env->pstate, &sf->uc.tuc_mcontext.pstate); + __put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate); __put_user(/*current->thread.fault_address*/ 0, &sf->uc.tuc_mcontext.fault_address); @@ -1210,6 +1210,7 @@ static int target_restore_sigframe(CPUARMState *env, struct target_aux_context *aux = (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved; uint32_t magic, size; + uint64_t pstate; target_to_host_sigset(&set, &sf->uc.tuc_sigmask); sigprocmask(SIG_SETMASK, &set, NULL); @@ -1220,7 +1221,8 @@ static int target_restore_sigframe(CPUARMState *env, __get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); __get_user(env->pc, &sf->uc.tuc_mcontext.pc); - __get_user(env->pstate, &sf->uc.tuc_mcontext.pstate); + __get_user(pstate, &sf->uc.tuc_mcontext.pstate); + pstate_write(env, pstate); __get_user(magic, &aux->fpsimd.head.magic); __get_user(size, &aux->fpsimd.head.size); diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 8288c7db39..408d207865 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -89,6 +89,12 @@ static void arm_cpu_reset(CPUState *s) if (arm_feature(env, ARM_FEATURE_AARCH64)) { /* 64 bit CPUs always start in 64 bit mode */ env->aarch64 = 1; +#if defined(CONFIG_USER_ONLY) + env->pstate = PSTATE_MODE_EL0t; +#else + env->pstate = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F + | PSTATE_MODE_EL1h; +#endif } #if defined(CONFIG_USER_ONLY) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index c6c19e9641..3af4ed36dd 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -113,8 +113,15 @@ typedef struct CPUARMState { /* Regs for A64 mode. */ uint64_t xregs[32]; uint64_t pc; - /* TODO: pstate doesn't correspond to an architectural register; - * it would be better modelled as the underlying fields. + /* PSTATE isn't an architectural register for ARMv8. However, it is + * convenient for us to assemble the underlying state into a 32 bit format + * identical to the architectural format used for the SPSR. (This is also + * what the Linux kernel's 'pstate' field in signal handlers and KVM's + * 'pstate' register are.) Of the PSTATE bits: + * NZCV are kept in the split out env->CF/VF/NF/ZF, (which have the same + * semantics as for AArch32, as described in the comments on each field) + * nRW (also known as M[4]) is kept, inverted, in env->aarch64 + * all other bits are stored in their correct places in env->pstate */ uint32_t pstate; uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */ @@ -309,15 +316,6 @@ static inline bool is_a64(CPUARMState *env) return env->aarch64; } -#define PSTATE_N_SHIFT 3 -#define PSTATE_N (1 << PSTATE_N_SHIFT) -#define PSTATE_Z_SHIFT 2 -#define PSTATE_Z (1 << PSTATE_Z_SHIFT) -#define PSTATE_C_SHIFT 1 -#define PSTATE_C (1 << PSTATE_C_SHIFT) -#define PSTATE_V_SHIFT 0 -#define PSTATE_V (1 << PSTATE_V_SHIFT) - /* 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. */ @@ -352,6 +350,56 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw, /* Execution state bits. MRS read as zero, MSR writes ignored. */ #define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J) +/* Bit definitions for ARMv8 SPSR (PSTATE) format. + * Only these are valid when in AArch64 mode; in + * AArch32 mode SPSRs are basically CPSR-format. + */ +#define PSTATE_M (0xFU) +#define PSTATE_nRW (1U << 4) +#define PSTATE_F (1U << 6) +#define PSTATE_I (1U << 7) +#define PSTATE_A (1U << 8) +#define PSTATE_D (1U << 9) +#define PSTATE_IL (1U << 20) +#define PSTATE_SS (1U << 21) +#define PSTATE_V (1U << 28) +#define PSTATE_C (1U << 29) +#define PSTATE_Z (1U << 30) +#define PSTATE_N (1U << 31) +#define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V) +#define CACHED_PSTATE_BITS (PSTATE_NZCV) +/* Mode values for AArch64 */ +#define PSTATE_MODE_EL3h 13 +#define PSTATE_MODE_EL3t 12 +#define PSTATE_MODE_EL2h 9 +#define PSTATE_MODE_EL2t 8 +#define PSTATE_MODE_EL1h 5 +#define PSTATE_MODE_EL1t 4 +#define PSTATE_MODE_EL0t 0 + +/* Return the current PSTATE value. For the moment we don't support 32<->64 bit + * interprocessing, so we don't attempt to sync with the cpsr state used by + * the 32 bit decoder. + */ +static inline uint32_t pstate_read(CPUARMState *env) +{ + int ZF; + + ZF = (env->ZF == 0); + return (env->NF & 0x80000000) | (ZF << 30) + | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) + | env->pstate; +} + +static inline void pstate_write(CPUARMState *env, uint32_t val) +{ + env->ZF = (~val) & PSTATE_Z; + env->NF = val; + env->CF = (val >> 29) & 1; + env->VF = (val << 3) & 0x80000000; + env->pstate = val & ~CACHED_PSTATE_BITS; +} + /* Return the current CPSR value. */ uint32_t cpsr_read(CPUARMState *env); /* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */ diff --git a/target-arm/gdbstub64.c b/target-arm/gdbstub64.c index 7cb6a7c0e0..e8a82952a4 100644 --- a/target-arm/gdbstub64.c +++ b/target-arm/gdbstub64.c @@ -37,7 +37,7 @@ int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_reg64(mem_buf, env->pc); break; case 33: - return gdb_get_reg32(mem_buf, env->pstate); + return gdb_get_reg32(mem_buf, pstate_read(env)); } /* Unknown register. */ return 0; @@ -65,7 +65,7 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 8; case 33: /* CPSR */ - env->pstate = tmp; + pstate_write(env, tmp); return 4; } /* Unknown register. */ diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index f120088607..932b6013ce 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -67,6 +67,7 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f, { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; + uint32_t psr = pstate_read(env); int i; cpu_fprintf(f, "PC=%016"PRIx64" SP=%016"PRIx64"\n", @@ -79,11 +80,12 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f, cpu_fprintf(f, " "); } } - cpu_fprintf(f, "PSTATE=%c%c%c%c\n", - env->pstate & PSTATE_N ? 'n' : '.', - env->pstate & PSTATE_Z ? 'z' : '.', - env->pstate & PSTATE_C ? 'c' : '.', - env->pstate & PSTATE_V ? 'v' : '.'); + cpu_fprintf(f, "PSTATE=%08x (flags %c%c%c%c)\n", + psr, + psr & PSTATE_N ? 'N' : '-', + psr & PSTATE_Z ? 'Z' : '-', + psr & PSTATE_C ? 'C' : '-', + psr & PSTATE_V ? 'V' : '-'); cpu_fprintf(f, "\n"); } From 26861c7ce06c055786323ff4c65af74d735d1c19 Mon Sep 17 00:00:00 2001 From: "Mian M. Hamayun" Date: Tue, 17 Dec 2013 19:42:30 +0000 Subject: [PATCH 21/62] target-arm: Add minimal KVM AArch64 support Add the bare minimum set of functions needed for control of an AArch64 KVM vcpu: * CPU initialization * minimal get/put register functions which only handle the basic state of the CPU Signed-off-by: Mian M. Hamayun Signed-off-by: Peter Maydell Message-id: 1385645602-18662-4-git-send-email-peter.maydell@linaro.org [PMM: significantly overhauled; most notably: * code lives in kvm64.c rather than using #ifdefs * support '-cpu host' rather than implicitly using whatever the host's CPU is regardless of what the user requests * fix bug attempting to get/set nonexistent X[31] * fix bug writing 64 bit kernel pstate into uint32_t env field ] Signed-off-by: Peter Maydell Reviewed-by: Christoffer Dall --- target-arm/Makefile.objs | 1 + target-arm/kvm.c | 4 + target-arm/kvm64.c | 204 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 target-arm/kvm64.c diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index 869ecd05af..190499f068 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -2,6 +2,7 @@ obj-y += arm-semi.o obj-$(CONFIG_SOFTMMU) += machine.o obj-$(CONFIG_KVM) += kvm.o obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o +obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 5cdb3b95c6..1d2688dda7 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -128,7 +128,11 @@ static void kvm_arm_host_cpu_initfn(Object *obj) static const TypeInfo host_arm_cpu_type_info = { .name = TYPE_ARM_HOST_CPU, +#ifdef TARGET_AARCH64 + .parent = TYPE_AARCH64_CPU, +#else .parent = TYPE_ARM_CPU, +#endif .instance_init = kvm_arm_host_cpu_initfn, .class_init = kvm_arm_host_cpu_class_init, .class_size = sizeof(ARMHostCPUClass), diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c new file mode 100644 index 0000000000..1b7ca90374 --- /dev/null +++ b/target-arm/kvm64.c @@ -0,0 +1,204 @@ +/* + * ARM implementation of KVM hooks, 64 bit specific code + * + * Copyright Mian-M. Hamayun 2013, Virtual Open Systems + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include +#include +#include +#include + +#include + +#include "qemu-common.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "kvm_arm.h" +#include "cpu.h" +#include "hw/arm/arm.h" + +static inline void set_feature(uint64_t *features, int feature) +{ + *features |= 1ULL << feature; +} + +bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) +{ + /* Identify the feature bits corresponding to the host CPU, and + * fill out the ARMHostCPUClass fields accordingly. To do this + * we have to create a scratch VM, create a single CPU inside it, + * and then query that CPU for the relevant ID registers. + * For AArch64 we currently don't care about ID registers at + * all; we just want to know the CPU type. + */ + int fdarray[3]; + uint64_t features = 0; + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however + * we know these will only support creating one kind of guest CPU, + * which is its preferred CPU type. Fortunately these old kernels + * support only a very limited number of CPUs. + */ + static const uint32_t cpus_to_try[] = { + KVM_ARM_TARGET_AEM_V8, + KVM_ARM_TARGET_FOUNDATION_V8, + KVM_ARM_TARGET_CORTEX_A57, + QEMU_KVM_ARM_TARGET_NONE + }; + struct kvm_vcpu_init init; + + if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { + return false; + } + + ahcc->target = init.target; + ahcc->dtb_compatible = "arm,arm-v8"; + + kvm_arm_destroy_scratch_host_vcpu(fdarray); + + /* We can assume any KVM supporting CPU is at least a v8 + * with VFPv4+Neon; this in turn implies most of the other + * feature bits. + */ + set_feature(&features, ARM_FEATURE_V8); + set_feature(&features, ARM_FEATURE_VFP4); + set_feature(&features, ARM_FEATURE_NEON); + set_feature(&features, ARM_FEATURE_AARCH64); + + ahcc->features = features; + + return true; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + struct kvm_vcpu_init init; + int ret; + + if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || + !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + fprintf(stderr, "KVM is not supported for this guest CPU type\n"); + return -EINVAL; + } + + init.target = cpu->kvm_target; + memset(init.features, 0, sizeof(init.features)); + if (cpu->start_powered_off) { + init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF; + } + ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); + + /* TODO : support for save/restore/reset of system regs via tuple list */ + + return ret; +} + +#define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + +int kvm_arch_put_registers(CPUState *cs, int level) +{ + struct kvm_one_reg reg; + uint64_t val; + int i; + int ret; + + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + for (i = 0; i < 31; i++) { + reg.id = AARCH64_CORE_REG(regs.regs[i]); + reg.addr = (uintptr_t) &env->xregs[i]; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + } + + reg.id = AARCH64_CORE_REG(regs.sp); + reg.addr = (uintptr_t) &env->xregs[31]; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + + /* Note that KVM thinks pstate is 64 bit but we use a uint32_t */ + val = pstate_read(env); + reg.id = AARCH64_CORE_REG(regs.pstate); + reg.addr = (uintptr_t) &val; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + + reg.id = AARCH64_CORE_REG(regs.pc); + reg.addr = (uintptr_t) &env->pc; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + + /* TODO: + * SP_EL1 + * ELR_EL1 + * SPSR[] + * FP state + * system registers + */ + return ret; +} + +int kvm_arch_get_registers(CPUState *cs) +{ + struct kvm_one_reg reg; + uint64_t val; + int i; + int ret; + + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + for (i = 0; i < 31; i++) { + reg.id = AARCH64_CORE_REG(regs.regs[i]); + reg.addr = (uintptr_t) &env->xregs[i]; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + } + + reg.id = AARCH64_CORE_REG(regs.sp); + reg.addr = (uintptr_t) &env->xregs[31]; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + + reg.id = AARCH64_CORE_REG(regs.pstate); + reg.addr = (uintptr_t) &val; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + pstate_write(env, val); + + reg.id = AARCH64_CORE_REG(regs.pc); + reg.addr = (uintptr_t) &env->pc; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + + /* TODO: other registers */ + return ret; +} + +void kvm_arch_reset_vcpu(CPUState *cs) +{ +} From 70a5f682f34c04d13164eb0d55241b7378f02030 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:30 +0000 Subject: [PATCH 22/62] configure: Enable KVM for aarch64 host/target combination Enable KVM if the host and target CPU are both aarch64. Note that host aarch64 + target arm is not valid for KVM acceleration: the 64 bit kernel does not support the ioctl interface for 32 bit CPUs. 32 bit VMs on 64 bit hosts need to be created using the 64 bit ioctl interface; when QEMU supports this it will be on the arch64-softmmu target with a -cpu parameter for a 32 bit CPU, which is still an aarch64/aarch64 combination as far as configure is concerned. Signed-off-by: Peter Maydell Message-id: 1385645602-18662-5-git-send-email-peter.maydell@linaro.org Reviewed-by: Christoffer Dall --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index edfea9591c..02c94e2043 100755 --- a/configure +++ b/configure @@ -4550,7 +4550,7 @@ case "$target_name" in *) esac case "$target_name" in - arm|i386|x86_64|ppcemb|ppc|ppc64|s390x) + aarch64|arm|i386|x86_64|ppcemb|ppc|ppc64|s390x) # Make sure the target and host cpus are compatible if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \ \( "$target_name" = "$cpu" -o \ From 47b1da8134610c10a672b249808dbc763308668e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:30 +0000 Subject: [PATCH 23/62] hw/arm/boot: Allow easier swapping in of different loader code For AArch64 we will obviously require a different set of primary and secondary boot loader code fragments. However currently we hardcode the offsets into the loader code where we must write the entrypoint and other data into arm_load_kernel(). This makes it hard to substitute a different loader fragment, so switch to a more flexible scheme where instead of a raw array of instructions we use an array of (instruction, fixup-type) pairs that indicate which words need special action or data written into them. Signed-off-by: Peter Maydell Message-id: 1385645602-18662-6-git-send-email-peter.maydell@linaro.org Reviewed-by: Christoffer Dall Reviewed-by: Peter Crosthwaite --- hw/arm/boot.c | 152 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 45 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 55d552f3a8..0c05a64c8c 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -20,15 +20,33 @@ #define KERNEL_ARGS_ADDR 0x100 #define KERNEL_LOAD_ADDR 0x00010000 +typedef enum { + FIXUP_NONE = 0, /* do nothing */ + FIXUP_TERMINATOR, /* end of insns */ + FIXUP_BOARDID, /* overwrite with board ID number */ + FIXUP_ARGPTR, /* overwrite with pointer to kernel args */ + FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */ + FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */ + FIXUP_BOOTREG, /* overwrite with boot register address */ + FIXUP_DSB, /* overwrite with correct DSB insn for cpu */ + FIXUP_MAX, +} FixupType; + +typedef struct ARMInsnFixup { + uint32_t insn; + FixupType fixup; +} ARMInsnFixup; + /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ -static uint32_t bootloader[] = { - 0xe3a00000, /* mov r0, #0 */ - 0xe59f1004, /* ldr r1, [pc, #4] */ - 0xe59f2004, /* ldr r2, [pc, #4] */ - 0xe59ff004, /* ldr pc, [pc, #4] */ - 0, /* Board ID */ - 0, /* Address of kernel args. Set by integratorcp_init. */ - 0 /* Kernel entry point. Set by integratorcp_init. */ +static const ARMInsnFixup bootloader[] = { + { 0xe3a00000 }, /* mov r0, #0 */ + { 0xe59f1004 }, /* ldr r1, [pc, #4] */ + { 0xe59f2004 }, /* ldr r2, [pc, #4] */ + { 0xe59ff004 }, /* ldr pc, [pc, #4] */ + { 0, FIXUP_BOARDID }, + { 0, FIXUP_ARGPTR }, + { 0, FIXUP_ENTRYPOINT }, + { 0, FIXUP_TERMINATOR } }; /* Handling for secondary CPU boot in a multicore system. @@ -48,39 +66,83 @@ static uint32_t bootloader[] = { #define DSB_INSN 0xf57ff04f #define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */ -static uint32_t smpboot[] = { - 0xe59f2028, /* ldr r2, gic_cpu_if */ - 0xe59f0028, /* ldr r0, startaddr */ - 0xe3a01001, /* mov r1, #1 */ - 0xe5821000, /* str r1, [r2] - set GICC_CTLR.Enable */ - 0xe3a010ff, /* mov r1, #0xff */ - 0xe5821004, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */ - DSB_INSN, /* dsb */ - 0xe320f003, /* wfi */ - 0xe5901000, /* ldr r1, [r0] */ - 0xe1110001, /* tst r1, r1 */ - 0x0afffffb, /* beq */ - 0xe12fff11, /* bx r1 */ - 0, /* gic_cpu_if: base address of GIC CPU interface */ - 0 /* bootreg: Boot register address is held here */ +static const ARMInsnFixup smpboot[] = { + { 0xe59f2028 }, /* ldr r2, gic_cpu_if */ + { 0xe59f0028 }, /* ldr r0, bootreg_addr */ + { 0xe3a01001 }, /* mov r1, #1 */ + { 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */ + { 0xe3a010ff }, /* mov r1, #0xff */ + { 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */ + { 0, FIXUP_DSB }, /* dsb */ + { 0xe320f003 }, /* wfi */ + { 0xe5901000 }, /* ldr r1, [r0] */ + { 0xe1110001 }, /* tst r1, r1 */ + { 0x0afffffb }, /* beq */ + { 0xe12fff11 }, /* bx r1 */ + { 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */ + { 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */ + { 0, FIXUP_TERMINATOR } }; +static void write_bootloader(const char *name, hwaddr addr, + const ARMInsnFixup *insns, uint32_t *fixupcontext) +{ + /* Fix up the specified bootloader fragment and write it into + * guest memory using rom_add_blob_fixed(). fixupcontext is + * an array giving the values to write in for the fixup types + * which write a value into the code array. + */ + int i, len; + uint32_t *code; + + len = 0; + while (insns[len].fixup != FIXUP_TERMINATOR) { + len++; + } + + code = g_new0(uint32_t, len); + + for (i = 0; i < len; i++) { + uint32_t insn = insns[i].insn; + FixupType fixup = insns[i].fixup; + + switch (fixup) { + case FIXUP_NONE: + break; + case FIXUP_BOARDID: + case FIXUP_ARGPTR: + case FIXUP_ENTRYPOINT: + case FIXUP_GIC_CPU_IF: + case FIXUP_BOOTREG: + case FIXUP_DSB: + insn = fixupcontext[fixup]; + break; + default: + abort(); + } + code[i] = tswap32(insn); + } + + rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr); + + g_free(code); +} + static void default_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) { - int n; - smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr; - smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr; - for (n = 0; n < ARRAY_SIZE(smpboot); n++) { - /* Replace DSB with the pre-v7 DSB if necessary. */ - if (!arm_feature(&cpu->env, ARM_FEATURE_V7) && - smpboot[n] == DSB_INSN) { - smpboot[n] = CP15_DSB_INSN; - } - smpboot[n] = tswap32(smpboot[n]); + uint32_t fixupcontext[FIXUP_MAX]; + + fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr; + fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr; + if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { + fixupcontext[FIXUP_DSB] = DSB_INSN; + } else { + fixupcontext[FIXUP_DSB] = CP15_DSB_INSN; } - rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), - info->smp_loader_start); + + write_bootloader("smpboot", info->smp_loader_start, + smpboot, fixupcontext); } static void default_reset_secondary(ARMCPU *cpu, @@ -354,7 +416,6 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) CPUState *cs = CPU(cpu); int kernel_size; int initrd_size; - int n; int is_linux = 0; uint64_t elf_entry; hwaddr entry; @@ -420,6 +481,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) } info->entry = entry; if (is_linux) { + uint32_t fixupcontext[FIXUP_MAX]; + if (info->initrd_filename) { initrd_size = load_ramdisk(info->initrd_filename, info->initrd_start, @@ -441,7 +504,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) } info->initrd_size = initrd_size; - bootloader[4] = info->board_id; + fixupcontext[FIXUP_BOARDID] = info->board_id; /* for device tree boot, we pass the DTB directly in r2. Otherwise * we point to the kernel args. @@ -456,9 +519,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) if (load_dtb(dtb_start, info)) { exit(1); } - bootloader[5] = dtb_start; + fixupcontext[FIXUP_ARGPTR] = dtb_start; } else { - bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR; + fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR; if (info->ram_size >= (1ULL << 32)) { fprintf(stderr, "qemu: RAM size must be less than 4GB to boot" " Linux kernel using ATAGS (try passing a device tree" @@ -466,12 +529,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) exit(1); } } - bootloader[6] = entry; - for (n = 0; n < sizeof(bootloader) / 4; n++) { - bootloader[n] = tswap32(bootloader[n]); - } - rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader), - info->loader_start); + fixupcontext[FIXUP_ENTRYPOINT] = entry; + + write_bootloader("bootloader", info->loader_start, + bootloader, fixupcontext); + if (info->nb_cpus > 1) { info->write_secondary_boot(cpu, info); } From 4d9ebf751a5a98e5dc2e26baf2344e744f4fa7b9 Mon Sep 17 00:00:00 2001 From: "Mian M. Hamayun" Date: Tue, 17 Dec 2013 19:42:30 +0000 Subject: [PATCH 24/62] hw/arm/boot: Add boot support for AArch64 processor This commit adds support for booting a single AArch64 CPU by setting appropriate registers. The bootloader includes placeholders for Board-ID that are used to implement uniform indexing across different bootloaders. Signed-off-by: Mian M. Hamayun Signed-off-by: Peter Maydell Message-id: 1385645602-18662-7-git-send-email-peter.maydell@linaro.org [PMM: * updated to use ARMInsnFixup style bootloader fragments * dropped virt.c additions * use runtime checks for "is this an AArch64 core" rather than ifdefs * drop some unnecessary setting of registers in reset hook ] Signed-off-by: Peter Maydell Reviewed-by: Christoffer Dall --- hw/arm/boot.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 0c05a64c8c..90e95341d7 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -17,8 +17,13 @@ #include "sysemu/device_tree.h" #include "qemu/config-file.h" +/* Kernel boot protocol is specified in the kernel docs + * Documentation/arm/Booting and Documentation/arm64/booting.txt + * They have different preferred image load offsets from system RAM base. + */ #define KERNEL_ARGS_ADDR 0x100 #define KERNEL_LOAD_ADDR 0x00010000 +#define KERNEL64_LOAD_ADDR 0x00080000 typedef enum { FIXUP_NONE = 0, /* do nothing */ @@ -37,6 +42,20 @@ typedef struct ARMInsnFixup { FixupType fixup; } ARMInsnFixup; +static const ARMInsnFixup bootloader_aarch64[] = { + { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */ + { 0xaa1f03e1 }, /* mov x1, xzr */ + { 0xaa1f03e2 }, /* mov x2, xzr */ + { 0xaa1f03e3 }, /* mov x3, xzr */ + { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */ + { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */ + { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */ + { 0 }, /* .word @DTB Higher 32-bits */ + { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */ + { 0 }, /* .word @Kernel Entry Higher 32-bits */ + { 0, FIXUP_TERMINATOR } +}; + /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ static const ARMInsnFixup bootloader[] = { { 0xe3a00000 }, /* mov r0, #0 */ @@ -396,7 +415,12 @@ static void do_cpu_reset(void *opaque) env->thumb = info->entry & 1; } else { if (CPU(cpu) == first_cpu) { - env->regs[15] = info->loader_start; + if (env->aarch64) { + env->pc = info->loader_start; + } else { + env->regs[15] = info->loader_start; + } + if (!info->dtb_filename) { if (old_param) { set_kernel_args_old(info); @@ -418,8 +442,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) int initrd_size; int is_linux = 0; uint64_t elf_entry; - hwaddr entry; + hwaddr entry, kernel_load_offset; int big_endian; + static const ARMInsnFixup *primary_loader; /* Load the kernel. */ if (!info->kernel_filename) { @@ -429,6 +454,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) return; } + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + primary_loader = bootloader_aarch64; + kernel_load_offset = KERNEL64_LOAD_ADDR; + } else { + primary_loader = bootloader; + kernel_load_offset = KERNEL_LOAD_ADDR; + } + info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); if (!info->secondary_cpu_reset_hook) { @@ -469,9 +502,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) &is_linux); } if (kernel_size < 0) { - entry = info->loader_start + KERNEL_LOAD_ADDR; + entry = info->loader_start + kernel_load_offset; kernel_size = load_image_targphys(info->kernel_filename, entry, - info->ram_size - KERNEL_LOAD_ADDR); + info->ram_size - kernel_load_offset); is_linux = 1; } if (kernel_size < 0) { @@ -532,7 +565,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) fixupcontext[FIXUP_ENTRYPOINT] = entry; write_bootloader("bootloader", info->loader_start, - bootloader, fixupcontext); + primary_loader, fixupcontext); if (info->nb_cpus > 1) { info->write_secondary_boot(cpu, info); From 013424d436b83f7ba8366b1d40bf82c4f6716f5e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:31 +0000 Subject: [PATCH 25/62] default-configs: Add config for aarch64-softmmu Add a config for aarch64-softmmu; this enables building of this target. The resulting executable doesn't know about any 64 bit CPUs, but all the 32 bit CPUs and board models work. Signed-off-by: Peter Maydell Message-id: 1385645602-18662-8-git-send-email-peter.maydell@linaro.org Reviewed-by: Peter Crosthwaite --- default-configs/aarch64-softmmu.mak | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 default-configs/aarch64-softmmu.mak diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak new file mode 100644 index 0000000000..6d3b5c7a46 --- /dev/null +++ b/default-configs/aarch64-softmmu.mak @@ -0,0 +1,6 @@ +# Default configuration for aarch64-softmmu + +# We support all the 32 bit boards so need all their config +include arm-softmmu.mak + +# Currently no 64-bit specific config requirements From 40f860cd6c1aa0d3399e3f8158f20bdc5b2bfbfe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:31 +0000 Subject: [PATCH 26/62] target-arm: Split A64 from A32/T32 gen_intermediate_code_internal() The A32/T32 gen_intermediate_code_internal() is complicated because it has to deal with: * conditionally executed instructions * Thumb IT blocks * kernel helper page * M profile exception-exit special casing None of these apply to A64, so putting the "this is A64 so call the A64 decoder" check in the middle of the A32/T32 loop is confusing and means the A64 decoder's handling of things like conditional jump and singlestepping has to take account of the conditional-execution jumps the main loop might emit. Refactor the code to give A64 its own gen_intermediate_code_internal function instead. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 209 ++++++++++++++++++++++++++++++++++++- target-arm/translate.c | 62 +++++------ target-arm/translate.h | 20 +++- 3 files changed, 246 insertions(+), 45 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 932b6013ce..a713137980 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -28,6 +28,8 @@ #include "translate.h" #include "qemu/host-utils.h" +#include "exec/gen-icount.h" + #include "helper.h" #define GEN_HELPER 1 #include "helper.h" @@ -106,7 +108,42 @@ static void gen_exception_insn(DisasContext *s, int offset, int excp) { gen_a64_set_pc_im(s->pc - offset); gen_exception(excp); - s->is_jmp = DISAS_JUMP; + s->is_jmp = DISAS_EXC; +} + +static inline bool use_goto_tb(DisasContext *s, int n, uint64_t dest) +{ + /* No direct tb linking with singlestep or deterministic io */ + if (s->singlestep_enabled || (s->tb->cflags & CF_LAST_IO)) { + return false; + } + + /* Only link tbs from inside the same guest page */ + if ((s->tb->pc & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) { + return false; + } + + return true; +} + +static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) +{ + TranslationBlock *tb; + + tb = s->tb; + if (use_goto_tb(s, n, dest)) { + tcg_gen_goto_tb(n); + gen_a64_set_pc_im(dest); + tcg_gen_exit_tb((tcg_target_long)tb + n); + s->is_jmp = DISAS_TB_JUMP; + } else { + gen_a64_set_pc_im(dest); + if (s->singlestep_enabled) { + gen_exception(EXCP_DEBUG); + } + tcg_gen_exit_tb(0); + s->is_jmp = DISAS_JUMP; + } } static void real_unallocated_encoding(DisasContext *s) @@ -120,7 +157,7 @@ static void real_unallocated_encoding(DisasContext *s) real_unallocated_encoding(s); \ } while (0) -void disas_a64_insn(CPUARMState *env, DisasContext *s) +static void disas_a64_insn(CPUARMState *env, DisasContext *s) { uint32_t insn; @@ -133,9 +170,171 @@ void disas_a64_insn(CPUARMState *env, DisasContext *s) unallocated_encoding(s); break; } +} - if (unlikely(s->singlestep_enabled) && (s->is_jmp == DISAS_TB_JUMP)) { - /* go through the main loop for single step */ - s->is_jmp = DISAS_JUMP; +void gen_intermediate_code_internal_a64(ARMCPU *cpu, + TranslationBlock *tb, + bool search_pc) +{ + CPUState *cs = CPU(cpu); + CPUARMState *env = &cpu->env; + DisasContext dc1, *dc = &dc1; + CPUBreakpoint *bp; + uint16_t *gen_opc_end; + int j, lj; + target_ulong pc_start; + target_ulong next_page_start; + int num_insns; + int max_insns; + + pc_start = tb->pc; + + dc->tb = tb; + + gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; + + dc->is_jmp = DISAS_NEXT; + dc->pc = pc_start; + dc->singlestep_enabled = cs->singlestep_enabled; + dc->condjmp = 0; + + dc->aarch64 = 1; + dc->thumb = 0; + dc->bswap_code = 0; + dc->condexec_mask = 0; + dc->condexec_cond = 0; +#if !defined(CONFIG_USER_ONLY) + dc->user = 0; +#endif + dc->vfp_enabled = 0; + dc->vec_len = 0; + dc->vec_stride = 0; + + 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_tb_start(); + + tcg_clear_temp_count(); + + do { + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc->pc) { + gen_exception_insn(dc, 0, EXCP_DEBUG); + /* Advance PC so that clearing the breakpoint will + invalidate this TB. */ + dc->pc += 2; + goto done_generating; + } + } + } + + if (search_pc) { + j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) { + tcg_ctx.gen_opc_instr_start[lj++] = 0; + } + } + tcg_ctx.gen_opc_pc[lj] = dc->pc; + tcg_ctx.gen_opc_instr_start[lj] = 1; + tcg_ctx.gen_opc_icount[lj] = num_insns; + } + + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { + tcg_gen_debug_insn_start(dc->pc); + } + + disas_a64_insn(env, dc); + + if (tcg_check_temp_count()) { + fprintf(stderr, "TCG temporary leak before "TARGET_FMT_lx"\n", + dc->pc); + } + + /* Translation stops when a conditional branch is encountered. + * Otherwise the subsequent code could get translated several times. + * Also stop translation when a page boundary is reached. This + * ensures prefetch aborts occur at the right place. + */ + num_insns++; + } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end && + !cs->singlestep_enabled && + !singlestep && + dc->pc < next_page_start && + num_insns < max_insns); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + if (unlikely(cs->singlestep_enabled) && dc->is_jmp != DISAS_EXC) { + /* Note that this means single stepping WFI doesn't halt the CPU. + * For conditional branch insns this is harmless unreachable code as + * gen_goto_tb() has already handled emitting the debug exception + * (and thus a tb-jump is not possible when singlestepping). + */ + assert(dc->is_jmp != DISAS_TB_JUMP); + if (dc->is_jmp != DISAS_JUMP) { + gen_a64_set_pc_im(dc->pc); + } + gen_exception(EXCP_DEBUG); + } else { + switch (dc->is_jmp) { + case DISAS_NEXT: + gen_goto_tb(dc, 1, dc->pc); + 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: + case DISAS_EXC: + case DISAS_SWI: + break; + case DISAS_WFI: + /* This is a special case because we don't want to just halt the CPU + * if trying to debug across a WFI. + */ + gen_helper_wfi(cpu_env); + break; + } + } + +done_generating: + gen_tb_end(tb, num_insns); + *tcg_ctx.gen_opc_ptr = INDEX_op_end; + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("----------------\n"); + qemu_log("IN: %s\n", lookup_symbol(pc_start)); + log_target_disas(env, pc_start, dc->pc - pc_start, + dc->thumb | (dc->bswap_code << 1)); + qemu_log("\n"); + } +#endif + if (search_pc) { + j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + lj++; + while (lj <= j) { + tcg_ctx.gen_opc_instr_start[lj++] = 0; + } + } else { + tb->size = dc->pc - pc_start; + tb->icount = num_insns; } } diff --git a/target-arm/translate.c b/target-arm/translate.c index 913146752a..679b3c3451 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -56,11 +56,6 @@ static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; #define IS_USER(s) (s->user) #endif -/* These instructions trap after executing, so defer them until after the - conditional execution state has been updated. */ -#define DISAS_WFI 4 -#define DISAS_SWI 5 - TCGv_ptr cpu_env; /* We reuse the same 64-bit temporaries for efficiency. */ static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; @@ -900,11 +895,7 @@ DO_GEN_ST(32, MO_TEUL) static inline void gen_set_pc_im(DisasContext *s, target_ulong val) { - if (s->aarch64) { - gen_a64_set_pc_im(val); - } else { - tcg_gen_movi_i32(cpu_R[15], val); - } + tcg_gen_movi_i32(cpu_R[15], val); } /* Force a TB lookup after an instruction that changes the CPU state. */ @@ -10243,6 +10234,15 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, int max_insns; /* generate intermediate code */ + + /* The A64 decoder has its own top level loop, because it doesn't need + * the A32/T32 complexity to do with conditional execution/IT blocks/etc. + */ + if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { + gen_intermediate_code_internal_a64(cpu, tb, search_pc); + return; + } + pc_start = tb->pc; dc->tb = tb; @@ -10254,31 +10254,18 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, dc->singlestep_enabled = cs->singlestep_enabled; dc->condjmp = 0; - if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { - dc->aarch64 = 1; - dc->thumb = 0; - dc->bswap_code = 0; - dc->condexec_mask = 0; - dc->condexec_cond = 0; + dc->aarch64 = 0; + dc->thumb = ARM_TBFLAG_THUMB(tb->flags); + dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); + dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; + dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; #if !defined(CONFIG_USER_ONLY) - dc->user = 0; + dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0); #endif - dc->vfp_enabled = 0; - dc->vec_len = 0; - dc->vec_stride = 0; - } else { - dc->aarch64 = 0; - dc->thumb = ARM_TBFLAG_THUMB(tb->flags); - dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); - dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; - dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; -#if !defined(CONFIG_USER_ONLY) - dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0); -#endif - dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags); - dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); - dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); - } + dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags); + dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); + dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); + cpu_F0s = tcg_temp_new_i32(); cpu_F1s = tcg_temp_new_i32(); cpu_F0d = tcg_temp_new_i64(); @@ -10340,7 +10327,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, do { #ifdef CONFIG_USER_ONLY /* Intercept jump to the magic kernel page. */ - if (!dc->aarch64 && dc->pc >= 0xffff0000) { + if (dc->pc >= 0xffff0000) { /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_exception(EXCP_KERNEL_TRAP); @@ -10388,9 +10375,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, tcg_gen_debug_insn_start(dc->pc); } - if (dc->aarch64) { - disas_a64_insn(env, dc); - } else if (dc->thumb) { + if (dc->thumb) { disas_thumb_insn(env, dc); if (dc->condexec_mask) { dc->condexec_cond = (dc->condexec_cond & 0xe) @@ -10585,8 +10570,9 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, int pc_pos) { if (is_a64(env)) { env->pc = tcg_ctx.gen_opc_pc[pc_pos]; + env->condexec_bits = 0; } else { env->regs[15] = tcg_ctx.gen_opc_pc[pc_pos]; + env->condexec_bits = gen_opc_condexec_bits[pc_pos]; } - env->condexec_bits = gen_opc_condexec_bits[pc_pos]; } diff --git a/target-arm/translate.h b/target-arm/translate.h index 67c776053b..878918176f 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -28,16 +28,32 @@ typedef struct DisasContext { extern TCGv_ptr cpu_env; +/* target-specific extra values for is_jmp */ +/* These instructions trap after executing, so the A32/T32 decoder must + * defer them until after the conditional execution state has been updated. + * WFI also needs special handling when single-stepping. + */ +#define DISAS_WFI 4 +#define DISAS_SWI 5 +/* For instructions which unconditionally cause an exception we can skip + * emitting unreachable code at the end of the TB in the A64 decoder + */ +#define DISAS_EXC 6 + #ifdef TARGET_AARCH64 void a64_translate_init(void); -void disas_a64_insn(CPUARMState *env, DisasContext *s); +void gen_intermediate_code_internal_a64(ARMCPU *cpu, + TranslationBlock *tb, + bool search_pc); void gen_a64_set_pc_im(uint64_t val); #else static inline void a64_translate_init(void) { } -static inline void disas_a64_insn(CPUARMState *env, DisasContext *s) +static inline void gen_intermediate_code_internal_a64(ARMCPU *cpu, + TranslationBlock *tb, + bool search_pc) { } From 5ce4f35781028ce1aee3341e6002f925fdc7aaf3 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:31 +0000 Subject: [PATCH 27/62] target-arm: A64: add set_pc cpu method When executing translation blocks we need to be able to recover our program counter. Add a method to set it for AArch64 CPUs. This covers user-mode, but for system mode emulation we will need to check if the CPU is in an AArch32 execution state. Signed-off-by: Alexander Graf Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/cpu64.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index 3e99c2140a..04ce87951c 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -68,11 +68,22 @@ static void aarch64_cpu_finalizefn(Object *obj) { } +static void aarch64_cpu_set_pc(CPUState *cs, vaddr value) +{ + ARMCPU *cpu = ARM_CPU(cs); + /* + * TODO: this will need updating for system emulation, + * when the core may be in AArch32 mode. + */ + cpu->env.pc = value; +} + static void aarch64_cpu_class_init(ObjectClass *oc, void *data) { CPUClass *cc = CPU_CLASS(oc); cc->dump_state = aarch64_cpu_dump_state; + cc->set_pc = aarch64_cpu_set_pc; cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; cc->gdb_num_core_regs = 34; From f903fa22f4d7f3a20c4d0f42e7585ed80a3ec051 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:31 +0000 Subject: [PATCH 28/62] target-arm: A64: provide functions for accessing FPCR and FPSR The information which AArch32 holds in the FPSCR is split for AArch64 into two logically distinct registers, FPSR and FPCR. Since they are carefully arranged to use non-overlapping bits, we leave the underlying state in the same place, and provide accessor functions which just update the appropriate bits via vfp_get_fpscr() and vfp_set_fpscr(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/cpu.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 3af4ed36dd..56ed591164 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -447,6 +447,34 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) uint32_t vfp_get_fpscr(CPUARMState *env); void vfp_set_fpscr(CPUARMState *env, uint32_t val); +/* For A64 the FPSCR is split into two logically distinct registers, + * FPCR and FPSR. However since they still use non-overlapping bits + * we store the underlying state in fpscr and just mask on read/write. + */ +#define FPSR_MASK 0xf800009f +#define FPCR_MASK 0x07f79f00 +static inline uint32_t vfp_get_fpsr(CPUARMState *env) +{ + return vfp_get_fpscr(env) & FPSR_MASK; +} + +static inline void vfp_set_fpsr(CPUARMState *env, uint32_t val) +{ + uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPSR_MASK) | (val & FPSR_MASK); + vfp_set_fpscr(env, new_fpscr); +} + +static inline uint32_t vfp_get_fpcr(CPUARMState *env) +{ + return vfp_get_fpscr(env) & FPCR_MASK; +} + +static inline void vfp_set_fpcr(CPUARMState *env, uint32_t val) +{ + uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPCR_MASK) | (val & FPCR_MASK); + vfp_set_fpscr(env, new_fpscr); +} + enum arm_cpu_mode { ARM_CPU_MODE_USR = 0x10, ARM_CPU_MODE_FIQ = 0x11, From 6a66942735569ec7f8b761c1205e6f4c50962fe4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:32 +0000 Subject: [PATCH 29/62] target-arm: Support fp registers in gdb stub Register the aarch64-fpu XML and implement the necessary read/write handlers so we can support reading and writing of FP registers in the gdb stub. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- configure | 2 +- gdb-xml/aarch64-fpu.xml | 86 +++++++++++++++++++++++++++++++++++++++++ target-arm/helper.c | 48 ++++++++++++++++++++++- 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 gdb-xml/aarch64-fpu.xml diff --git a/configure b/configure index 02c94e2043..07b6be34ff 100755 --- a/configure +++ b/configure @@ -4438,7 +4438,7 @@ case "$target_name" in aarch64) TARGET_BASE_ARCH=arm bflt="yes" - gdb_xml_files="aarch64-core.xml" + gdb_xml_files="aarch64-core.xml aarch64-fpu.xml" ;; cris) ;; diff --git a/gdb-xml/aarch64-fpu.xml b/gdb-xml/aarch64-fpu.xml new file mode 100644 index 0000000000..997197e5e5 --- /dev/null +++ b/gdb-xml/aarch64-fpu.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/target-arm/helper.c b/target-arm/helper.c index cfbb14cbf3..6ebd7dc7bc 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -65,6 +65,48 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) return 0; } +static int aarch64_fpu_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) +{ + switch (reg) { + case 0 ... 31: + /* 128 bit FP register */ + stfq_le_p(buf, env->vfp.regs[reg * 2]); + stfq_le_p(buf + 8, env->vfp.regs[reg * 2 + 1]); + return 16; + case 32: + /* FPSR */ + stl_p(buf, vfp_get_fpsr(env)); + return 4; + case 33: + /* FPCR */ + stl_p(buf, vfp_get_fpcr(env)); + return 4; + default: + return 0; + } +} + +static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) +{ + switch (reg) { + case 0 ... 31: + /* 128 bit FP register */ + env->vfp.regs[reg * 2] = ldfq_le_p(buf); + env->vfp.regs[reg * 2 + 1] = ldfq_le_p(buf + 8); + return 16; + case 32: + /* FPSR */ + vfp_set_fpsr(env, ldl_p(buf)); + return 4; + case 33: + /* FPCR */ + vfp_set_fpcr(env, ldl_p(buf)); + return 4; + default: + return 0; + } +} + static int raw_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) { @@ -1795,7 +1837,11 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; - if (arm_feature(env, ARM_FEATURE_NEON)) { + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, + aarch64_fpu_gdb_set_reg, + 34, "aarch64-fpu.xml", 0); + } else if (arm_feature(env, ARM_FEATURE_NEON)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, 51, "arm-neon.xml", 0); } else if (arm_feature(env, ARM_FEATURE_VFP3)) { From d3e35a1fe4562ee3f9f1af91ab02d62cf31b9488 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:32 +0000 Subject: [PATCH 30/62] target-arm: A64: add stubs for a64 specific helpers We will need helpers that only make sense with AArch64. Add helper-a64.{c,h} files as stubs that we can fill with these helpers in the following patches. Signed-off-by: Alexander Graf Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/Makefile.objs | 2 +- target-arm/helper-a64.c | 25 +++++++++++++++++++++++++ target-arm/helper-a64.h | 18 ++++++++++++++++++ target-arm/helper.h | 4 ++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 target-arm/helper-a64.c create mode 100644 target-arm/helper-a64.h diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index 190499f068..dcd167e0d8 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -7,5 +7,5 @@ obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o obj-y += gdbstub.o -obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o +obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o obj-y += crypto_helper.o diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c new file mode 100644 index 0000000000..adb8428105 --- /dev/null +++ b/target-arm/helper-a64.c @@ -0,0 +1,25 @@ +/* + * AArch64 specific helpers + * + * Copyright (c) 2013 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "cpu.h" +#include "exec/gdbstub.h" +#include "helper.h" +#include "qemu/host-utils.h" +#include "sysemu/sysemu.h" +#include "qemu/bitops.h" diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h new file mode 100644 index 0000000000..dd283061dd --- /dev/null +++ b/target-arm/helper-a64.h @@ -0,0 +1,18 @@ +/* + * AArch64 specific helper definitions + * + * Copyright (c) 2013 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ diff --git a/target-arm/helper.h b/target-arm/helper.h index 496eb6511c..73d67dcc17 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -466,4 +466,8 @@ DEF_HELPER_3(neon_qzip32, void, env, i32, i32) DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32) DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32) +#ifdef TARGET_AARCH64 +#include "helper-a64.h" +#endif + #include "exec/def-helper.h" From ad7ee8a290d08a2fe9d408af2461d1f583d96f7d Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Tue, 17 Dec 2013 19:42:32 +0000 Subject: [PATCH 31/62] target-arm: A64: provide skeleton for a64 insn decoding Provide a skeleton for a64 instruction decoding in translate-a64.c, by dividing instructions into the classes defined by the ARM Architecture Reference Manual(DDI0487A_a) section C3. Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 370 ++++++++++++++++++++++++++++++++++++- 1 file changed, 362 insertions(+), 8 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index a713137980..8e16cb1a1c 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -146,17 +146,348 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) } } -static void real_unallocated_encoding(DisasContext *s) +static void unallocated_encoding(DisasContext *s) { - fprintf(stderr, "Unknown instruction: %#x\n", s->insn); gen_exception_insn(s, 4, EXCP_UDEF); } -#define unallocated_encoding(s) do { \ - fprintf(stderr, "unallocated encoding at line: %d\n", __LINE__); \ - real_unallocated_encoding(s); \ - } while (0) +#define unsupported_encoding(s, insn) \ + do { \ + qemu_log_mask(LOG_UNIMP, \ + "%s:%d: unsupported instruction encoding 0x%08x " \ + "at pc=%016" PRIx64 "\n", \ + __FILE__, __LINE__, insn, s->pc - 4); \ + unallocated_encoding(s); \ + } while (0); +/* + * the instruction disassembly implemented here matches + * the instruction encoding classifications in chapter 3 (C3) + * of the ARM Architecture Reference Manual (DDI0487A_a) + */ + +/* Unconditional branch (immediate) */ +static void disas_uncond_b_imm(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Compare & branch (immediate) */ +static void disas_comp_b_imm(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Test & branch (immediate) */ +static void disas_test_b_imm(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Conditional branch (immediate) */ +static void disas_cond_b_imm(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* System */ +static void disas_system(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Exception generation */ +static void disas_exc(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Unconditional branch (register) */ +static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* C3.2 Branches, exception generating and system instructions */ +static void disas_b_exc_sys(DisasContext *s, uint32_t insn) +{ + switch (extract32(insn, 25, 7)) { + case 0x0a: case 0x0b: + case 0x4a: case 0x4b: /* Unconditional branch (immediate) */ + disas_uncond_b_imm(s, insn); + break; + case 0x1a: case 0x5a: /* Compare & branch (immediate) */ + disas_comp_b_imm(s, insn); + break; + case 0x1b: case 0x5b: /* Test & branch (immediate) */ + disas_test_b_imm(s, insn); + break; + case 0x2a: /* Conditional branch (immediate) */ + disas_cond_b_imm(s, insn); + break; + case 0x6a: /* Exception generation / System */ + if (insn & (1 << 24)) { + disas_system(s, insn); + } else { + disas_exc(s, insn); + } + break; + case 0x6b: /* Unconditional branch (register) */ + disas_uncond_b_reg(s, insn); + break; + default: + unallocated_encoding(s); + break; + } +} + +/* Load/store exclusive */ +static void disas_ldst_excl(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Load register (literal) */ +static void disas_ld_lit(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Load/store pair (all forms) */ +static void disas_ldst_pair(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Load/store register (all forms) */ +static void disas_ldst_reg(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* AdvSIMD load/store multiple structures */ +static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* AdvSIMD load/store single structure */ +static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* C3.3 Loads and stores */ +static void disas_ldst(DisasContext *s, uint32_t insn) +{ + switch (extract32(insn, 24, 6)) { + case 0x08: /* Load/store exclusive */ + disas_ldst_excl(s, insn); + break; + case 0x18: case 0x1c: /* Load register (literal) */ + disas_ld_lit(s, insn); + break; + case 0x28: case 0x29: + case 0x2c: case 0x2d: /* Load/store pair (all forms) */ + disas_ldst_pair(s, insn); + break; + case 0x38: case 0x39: + case 0x3c: case 0x3d: /* Load/store register (all forms) */ + disas_ldst_reg(s, insn); + break; + case 0x0c: /* AdvSIMD load/store multiple structures */ + disas_ldst_multiple_struct(s, insn); + break; + case 0x0d: /* AdvSIMD load/store single structure */ + disas_ldst_single_struct(s, insn); + break; + default: + unallocated_encoding(s); + break; + } +} + +/* PC-rel. addressing */ +static void disas_pc_rel_adr(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Add/subtract (immediate) */ +static void disas_add_sub_imm(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Logical (immediate) */ +static void disas_logic_imm(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Move wide (immediate) */ +static void disas_movw_imm(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Bitfield */ +static void disas_bitfield(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Extract */ +static void disas_extract(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* C3.4 Data processing - immediate */ +static void disas_data_proc_imm(DisasContext *s, uint32_t insn) +{ + switch (extract32(insn, 23, 6)) { + case 0x20: case 0x21: /* PC-rel. addressing */ + disas_pc_rel_adr(s, insn); + break; + case 0x22: case 0x23: /* Add/subtract (immediate) */ + disas_add_sub_imm(s, insn); + break; + case 0x24: /* Logical (immediate) */ + disas_logic_imm(s, insn); + break; + case 0x25: /* Move wide (immediate) */ + disas_movw_imm(s, insn); + break; + case 0x26: /* Bitfield */ + disas_bitfield(s, insn); + break; + case 0x27: /* Extract */ + disas_extract(s, insn); + break; + default: + unallocated_encoding(s); + break; + } +} + +/* Logical (shifted register) */ +static void disas_logic_reg(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Add/subtract (extended register) */ +static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Add/subtract (shifted register) */ +static void disas_add_sub_reg(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Data-processing (3 source) */ +static void disas_data_proc_3src(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Add/subtract (with carry) */ +static void disas_adc_sbc(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Conditional compare (immediate) */ +static void disas_cc_imm(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Conditional compare (register) */ +static void disas_cc_reg(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Conditional select */ +static void disas_cond_select(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Data-processing (1 source) */ +static void disas_data_proc_1src(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* Data-processing (2 source) */ +static void disas_data_proc_2src(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* C3.5 Data processing - register */ +static void disas_data_proc_reg(DisasContext *s, uint32_t insn) +{ + switch (extract32(insn, 24, 5)) { + case 0x0a: /* Logical (shifted register) */ + disas_logic_reg(s, insn); + break; + case 0x0b: /* Add/subtract */ + if (insn & (1 << 21)) { /* (extended register) */ + disas_add_sub_ext_reg(s, insn); + } else { + disas_add_sub_reg(s, insn); + } + break; + case 0x1b: /* Data-processing (3 source) */ + disas_data_proc_3src(s, insn); + break; + case 0x1a: + switch (extract32(insn, 21, 3)) { + case 0x0: /* Add/subtract (with carry) */ + disas_adc_sbc(s, insn); + break; + case 0x2: /* Conditional compare */ + if (insn & (1 << 11)) { /* (immediate) */ + disas_cc_imm(s, insn); + } else { /* (register) */ + disas_cc_reg(s, insn); + } + break; + case 0x4: /* Conditional select */ + disas_cond_select(s, insn); + break; + case 0x6: /* Data-processing */ + if (insn & (1 << 30)) { /* (1 source) */ + disas_data_proc_1src(s, insn); + } else { /* (2 source) */ + disas_data_proc_2src(s, insn); + } + break; + default: + unallocated_encoding(s); + break; + } + break; + default: + unallocated_encoding(s); + break; + } +} + +/* C3.6 Data processing - SIMD and floating point */ +static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) +{ + unsupported_encoding(s, insn); +} + +/* C3.1 A64 instruction index by encoding */ static void disas_a64_insn(CPUARMState *env, DisasContext *s) { uint32_t insn; @@ -165,10 +496,33 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) s->insn = insn; s->pc += 4; - switch ((insn >> 24) & 0x1f) { - default: + switch (extract32(insn, 25, 4)) { + case 0x0: case 0x1: case 0x2: case 0x3: /* UNALLOCATED */ unallocated_encoding(s); break; + case 0x8: case 0x9: /* Data processing - immediate */ + disas_data_proc_imm(s, insn); + break; + case 0xa: case 0xb: /* Branch, exception generation and system insns */ + disas_b_exc_sys(s, insn); + break; + case 0x4: + case 0x6: + case 0xc: + case 0xe: /* Loads and stores */ + disas_ldst(s, insn); + break; + case 0x5: + case 0xd: /* Data processing - register */ + disas_data_proc_reg(s, insn); + break; + case 0x7: + case 0xf: /* Data processing - SIMD and floating point */ + disas_data_proc_simd_fp(s, insn); + break; + default: + assert(FALSE); /* all 15 cases should be handled above */ + break; } } From 87462e0f41fccc353f9c902caed563ab7cbdd8ed Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Tue, 17 Dec 2013 19:42:32 +0000 Subject: [PATCH 32/62] target-arm: A64: expand decoding skeleton for system instructions Decode the various kinds of system instructions: hints (HINT), which include NOP, YIELD, WFE, WFI, SEV, SEL sync instructions, which include CLREX, DSB, DMB, ISB msr_i, which move immediate to processor state field sys, which include all SYS and SYSL instructions msr, which move from a gp register to a system register mrs, which move from a system register to a gp register Provide implementations where they are trivial nops. Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 131 ++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 8e16cb1a1c..1e2b371d4b 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -190,12 +190,139 @@ static void disas_cond_b_imm(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* System */ -static void disas_system(DisasContext *s, uint32_t insn) +/* C5.6.68 HINT */ +static void handle_hint(DisasContext *s, uint32_t insn, + unsigned int op1, unsigned int op2, unsigned int crm) +{ + unsigned int selector = crm << 3 | op2; + + if (op1 != 3) { + unallocated_encoding(s); + return; + } + + switch (selector) { + case 0: /* NOP */ + return; + case 1: /* YIELD */ + case 2: /* WFE */ + case 3: /* WFI */ + case 4: /* SEV */ + case 5: /* SEVL */ + /* we treat all as NOP at least for now */ + return; + default: + /* default specified as NOP equivalent */ + return; + } +} + +/* CLREX, DSB, DMB, ISB */ +static void handle_sync(DisasContext *s, uint32_t insn, + unsigned int op1, unsigned int op2, unsigned int crm) +{ + if (op1 != 3) { + unallocated_encoding(s); + return; + } + + switch (op2) { + case 2: /* CLREX */ + unsupported_encoding(s, insn); + return; + case 4: /* DSB */ + case 5: /* DMB */ + case 6: /* ISB */ + /* We don't emulate caches so barriers are no-ops */ + return; + default: + unallocated_encoding(s); + return; + } +} + +/* C5.6.130 MSR (immediate) - move immediate to processor state field */ +static void handle_msr_i(DisasContext *s, uint32_t insn, + unsigned int op1, unsigned int op2, unsigned int crm) { unsupported_encoding(s, insn); } +/* C5.6.204 SYS */ +static void handle_sys(DisasContext *s, uint32_t insn, unsigned int l, + unsigned int op1, unsigned int op2, + unsigned int crn, unsigned int crm, unsigned int rt) +{ + unsupported_encoding(s, insn); +} + +/* C5.6.129 MRS - move from system register */ +static void handle_mrs(DisasContext *s, uint32_t insn, unsigned int op0, + unsigned int op1, unsigned int op2, + unsigned int crn, unsigned int crm, unsigned int rt) +{ + unsupported_encoding(s, insn); +} + +/* C5.6.131 MSR (register) - move to system register */ +static void handle_msr(DisasContext *s, uint32_t insn, unsigned int op0, + unsigned int op1, unsigned int op2, + unsigned int crn, unsigned int crm, unsigned int rt) +{ + unsupported_encoding(s, insn); +} + +/* C3.2.4 System + * 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0 + * +---------------------+---+-----+-----+-------+-------+-----+------+ + * | 1 1 0 1 0 1 0 1 0 0 | L | op0 | op1 | CRn | CRm | op2 | Rt | + * +---------------------+---+-----+-----+-------+-------+-----+------+ + */ +static void disas_system(DisasContext *s, uint32_t insn) +{ + unsigned int l, op0, op1, crn, crm, op2, rt; + l = extract32(insn, 21, 1); + op0 = extract32(insn, 19, 2); + op1 = extract32(insn, 16, 3); + crn = extract32(insn, 12, 4); + crm = extract32(insn, 8, 4); + op2 = extract32(insn, 5, 3); + rt = extract32(insn, 0, 5); + + if (op0 == 0) { + if (l || rt != 31) { + unallocated_encoding(s); + return; + } + switch (crn) { + case 2: /* C5.6.68 HINT */ + handle_hint(s, insn, op1, op2, crm); + break; + case 3: /* CLREX, DSB, DMB, ISB */ + handle_sync(s, insn, op1, op2, crm); + break; + case 4: /* C5.6.130 MSR (immediate) */ + handle_msr_i(s, insn, op1, op2, crm); + break; + default: + unallocated_encoding(s); + break; + } + return; + } + + if (op0 == 1) { + /* C5.6.204 SYS */ + handle_sys(s, insn, l, op1, op2, crn, crm, rt); + } else if (l) { /* op0 > 1 */ + /* C5.6.129 MRS - move from system register */ + handle_mrs(s, insn, op0, op1, op2, crn, crm, rt); + } else { + /* C5.6.131 MSR (register) - move to system register */ + handle_msr(s, insn, op0, op1, op2, crn, crm, rt); + } +} + /* Exception generation */ static void disas_exc(DisasContext *s, uint32_t insn) { From 11e169de9940b9dc057e534ecf864c542fafb425 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:32 +0000 Subject: [PATCH 33/62] target-arm: A64: add support for B and BL insns Implement the B and BL instructions (PC relative branches and calls). For convenience in managing TCG temporaries which might be generated if a source register is the zero-register XZR, we provide a simple mechanism for creating a new temp which is automatically freed at the end of decode of the instruction. Signed-off-by: Alexander Graf [claudio: renamed functions, adapted to new decoder layout] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 64 ++++++++++++++++++++++++++++++++++++-- target-arm/translate.h | 3 ++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 1e2b371d4b..ead5658d46 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -160,16 +160,71 @@ static void unallocated_encoding(DisasContext *s) unallocated_encoding(s); \ } while (0); +static void init_tmp_a64_array(DisasContext *s) +{ +#ifdef CONFIG_DEBUG_TCG + int i; + for (i = 0; i < ARRAY_SIZE(s->tmp_a64); i++) { + TCGV_UNUSED_I64(s->tmp_a64[i]); + } +#endif + s->tmp_a64_count = 0; +} + +static void free_tmp_a64(DisasContext *s) +{ + int i; + for (i = 0; i < s->tmp_a64_count; i++) { + tcg_temp_free_i64(s->tmp_a64[i]); + } + init_tmp_a64_array(s); +} + +static TCGv_i64 new_tmp_a64(DisasContext *s) +{ + assert(s->tmp_a64_count < TMP_A64_MAX); + return s->tmp_a64[s->tmp_a64_count++] = tcg_temp_new_i64(); +} + +static TCGv_i64 new_tmp_a64_zero(DisasContext *s) +{ + TCGv_i64 t = new_tmp_a64(s); + tcg_gen_movi_i64(t, 0); + return t; +} + +static TCGv_i64 cpu_reg(DisasContext *s, int reg) +{ + if (reg == 31) { + return new_tmp_a64_zero(s); + } else { + return cpu_X[reg]; + } +} + /* * the instruction disassembly implemented here matches * the instruction encoding classifications in chapter 3 (C3) * of the ARM Architecture Reference Manual (DDI0487A_a) */ -/* Unconditional branch (immediate) */ +/* C3.2.7 Unconditional branch (immediate) + * 31 30 26 25 0 + * +----+-----------+-------------------------------------+ + * | op | 0 0 1 0 1 | imm26 | + * +----+-----------+-------------------------------------+ + */ static void disas_uncond_b_imm(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + uint64_t addr = s->pc + sextract32(insn, 0, 26) * 4 - 4; + + if (insn & (1 << 31)) { + /* C5.6.26 BL Branch with link */ + tcg_gen_movi_i64(cpu_reg(s, 30), s->pc); + } + + /* C5.6.20 B Branch / C5.6.26 BL Branch with link */ + gen_goto_tb(s, 0, addr); } /* Compare & branch (immediate) */ @@ -651,6 +706,9 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) assert(FALSE); /* all 15 cases should be handled above */ break; } + + /* if we allocated any temporaries, free them here */ + free_tmp_a64(s); } void gen_intermediate_code_internal_a64(ARMCPU *cpu, @@ -691,6 +749,8 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, dc->vec_len = 0; dc->vec_stride = 0; + init_tmp_a64_array(dc); + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; lj = -1; num_insns = 0; diff --git a/target-arm/translate.h b/target-arm/translate.h index 878918176f..23a45daff8 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -24,6 +24,9 @@ typedef struct DisasContext { int vec_len; int vec_stride; int aarch64; +#define TMP_A64_MAX 16 + int tmp_a64_count; + TCGv_i64 tmp_a64[TMP_A64_MAX]; } DisasContext; extern TCGv_ptr cpu_env; From b001c8c3d6855b0b52fc0fdd63b5a93fd326bf0c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:33 +0000 Subject: [PATCH 34/62] target-arm: A64: add support for BR, BLR and RET insns Implement BR, BLR and RET. This is all of the 'unconditional branch (register)' instruction category except for ERET and DPRS (which are system mode only). Signed-off-by: Alexander Graf [claudio: reimplemented on top of new decoder structure] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 43 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index ead5658d46..b69466585f 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -384,10 +384,49 @@ static void disas_exc(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* Unconditional branch (register) */ +/* C3.2.7 Unconditional branch (register) + * 31 25 24 21 20 16 15 10 9 5 4 0 + * +---------------+-------+-------+-------+------+-------+ + * | 1 1 0 1 0 1 1 | opc | op2 | op3 | Rn | op4 | + * +---------------+-------+-------+-------+------+-------+ + */ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int opc, op2, op3, rn, op4; + + opc = extract32(insn, 21, 4); + op2 = extract32(insn, 16, 5); + op3 = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + op4 = extract32(insn, 0, 5); + + if (op4 != 0x0 || op3 != 0x0 || op2 != 0x1f) { + unallocated_encoding(s); + return; + } + + switch (opc) { + case 0: /* BR */ + case 2: /* RET */ + break; + case 1: /* BLR */ + tcg_gen_movi_i64(cpu_reg(s, 30), s->pc); + break; + case 4: /* ERET */ + case 5: /* DRPS */ + if (rn != 0x1f) { + unallocated_encoding(s); + } else { + unsupported_encoding(s, insn); + } + return; + default: + unallocated_encoding(s); + return; + } + + tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn)); + s->is_jmp = DISAS_JUMP; } /* C3.2 Branches, exception generating and system instructions */ From 39fb730aed8c5f7b0058845cb9feac0d4b177985 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:33 +0000 Subject: [PATCH 35/62] target-arm: A64: add support for conditional branches This patch adds emulation for the conditional branch (b.cond) instruction. Signed-off-by: Alexander Graf [claudio: adapted to new decoder structure, reused arm infrastructure for checking the flags] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 29 +++++++++++++++++++++++++++-- target-arm/translate.c | 14 +++++++++----- target-arm/translate.h | 2 ++ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index b69466585f..4eb2992754 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -239,10 +239,35 @@ static void disas_test_b_imm(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* Conditional branch (immediate) */ +/* C3.2.2 / C5.6.19 Conditional branch (immediate) + * 31 25 24 23 5 4 3 0 + * +---------------+----+---------------------+----+------+ + * | 0 1 0 1 0 1 0 | o1 | imm19 | o0 | cond | + * +---------------+----+---------------------+----+------+ + */ static void disas_cond_b_imm(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int cond; + uint64_t addr; + + if ((insn & (1 << 4)) || (insn & (1 << 24))) { + unallocated_encoding(s); + return; + } + addr = s->pc + sextract32(insn, 5, 19) * 4 - 4; + cond = extract32(insn, 0, 4); + + if (cond < 0x0e) { + /* genuinely conditional branches */ + int label_match = gen_new_label(); + arm_gen_test_cc(cond, label_match); + gen_goto_tb(s, 0, s->pc); + gen_set_label(label_match); + gen_goto_tb(s, 1, addr); + } else { + /* 0xe and 0xf are both "always" conditions */ + gen_goto_tb(s, 0, addr); + } } /* C5.6.68 HINT */ diff --git a/target-arm/translate.c b/target-arm/translate.c index 679b3c3451..1403ecf216 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -671,7 +671,11 @@ static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b) } #undef PAS_OP -static void gen_test_cc(int cc, int label) +/* + * generate a conditional branch based on ARM condition code cc. + * This is common between ARM and Aarch64 targets. + */ +void arm_gen_test_cc(int cc, int label) { TCGv_i32 tmp; int inv; @@ -7131,7 +7135,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) /* if not always execute, we generate a conditional jump to next instruction */ s->condlabel = gen_new_label(); - gen_test_cc(cond ^ 1, s->condlabel); + arm_gen_test_cc(cond ^ 1, s->condlabel); s->condjmp = 1; } if ((insn & 0x0f900000) == 0x03000000) { @@ -9148,7 +9152,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw op = (insn >> 22) & 0xf; /* Generate a conditional jump to next instruction. */ s->condlabel = gen_new_label(); - gen_test_cc(op ^ 1, s->condlabel); + arm_gen_test_cc(op ^ 1, s->condlabel); s->condjmp = 1; /* offset[11:1] = insn[10:0] */ @@ -9505,7 +9509,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) cond = s->condexec_cond; if (cond != 0x0e) { /* Skip conditional when condition is AL. */ s->condlabel = gen_new_label(); - gen_test_cc(cond ^ 1, s->condlabel); + arm_gen_test_cc(cond ^ 1, s->condlabel); s->condjmp = 1; } } @@ -10178,7 +10182,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } /* generate a conditional jump to next instruction */ s->condlabel = gen_new_label(); - gen_test_cc(cond ^ 1, s->condlabel); + arm_gen_test_cc(cond ^ 1, s->condlabel); s->condjmp = 1; /* jump to the offset */ diff --git a/target-arm/translate.h b/target-arm/translate.h index 23a45daff8..a6f6b3e699 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -65,4 +65,6 @@ static inline void gen_a64_set_pc_im(uint64_t val) } #endif +void arm_gen_test_cc(int cc, int label); + #endif /* TARGET_ARM_TRANSLATE_H */ From db0f79589c16fe7f697716f4cadc903a2575ef55 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:33 +0000 Subject: [PATCH 36/62] target-arm: A64: add support for 'test and branch' imm This patch adds emulation for the test and branch insns, TBZ and TBNZ. Signed-off-by: Alexander Graf [claudio: adapted for new decoder always compare with 0 remove a TCG temporary ] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 4eb2992754..1d04303980 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -233,10 +233,33 @@ static void disas_comp_b_imm(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* Test & branch (immediate) */ +/* C3.2.5 Test & branch (immediate) + * 31 30 25 24 23 19 18 5 4 0 + * +----+-------------+----+-------+-------------+------+ + * | b5 | 0 1 1 0 1 1 | op | b40 | imm14 | Rt | + * +----+-------------+----+-------+-------------+------+ + */ static void disas_test_b_imm(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int bit_pos, op, rt; + uint64_t addr; + int label_match; + TCGv_i64 tcg_cmp; + + bit_pos = (extract32(insn, 31, 1) << 5) | extract32(insn, 19, 5); + op = extract32(insn, 24, 1); /* 0: TBZ; 1: TBNZ */ + addr = s->pc + sextract32(insn, 5, 14) * 4 - 4; + rt = extract32(insn, 0, 5); + + tcg_cmp = tcg_temp_new_i64(); + tcg_gen_andi_i64(tcg_cmp, cpu_reg(s, rt), (1ULL << bit_pos)); + label_match = gen_new_label(); + tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ, + tcg_cmp, 0, label_match); + tcg_temp_free_i64(tcg_cmp); + gen_goto_tb(s, 0, s->pc); + gen_set_label(label_match); + gen_goto_tb(s, 1, addr); } /* C3.2.2 / C5.6.19 Conditional branch (immediate) From 60e5338831e35e7b407b1670f5fe936859ea5490 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:33 +0000 Subject: [PATCH 37/62] target-arm: A64: add support for compare and branch imm This patch adds emulation for the compare and branch insns, CBZ and CBNZ. Signed-off-by: Alexander Graf [claudio: adapted to new decoder, compare with immediate 0, introduce read_cpu_reg to get the 0 extension on (!sf)] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 46 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 1d04303980..5ae3a858ce 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -202,6 +202,25 @@ static TCGv_i64 cpu_reg(DisasContext *s, int reg) } } +/* read a cpu register in 32bit/64bit mode. Returns a TCGv_i64 + * representing the register contents. This TCGv is an auto-freed + * temporary so it need not be explicitly freed, and may be modified. + */ +static TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) +{ + TCGv_i64 v = new_tmp_a64(s); + if (reg != 31) { + if (sf) { + tcg_gen_mov_i64(v, cpu_X[reg]); + } else { + tcg_gen_ext32u_i64(v, cpu_X[reg]); + } + } else { + tcg_gen_movi_i64(v, 0); + } + return v; +} + /* * the instruction disassembly implemented here matches * the instruction encoding classifications in chapter 3 (C3) @@ -227,10 +246,33 @@ static void disas_uncond_b_imm(DisasContext *s, uint32_t insn) gen_goto_tb(s, 0, addr); } -/* Compare & branch (immediate) */ +/* C3.2.1 Compare & branch (immediate) + * 31 30 25 24 23 5 4 0 + * +----+-------------+----+---------------------+--------+ + * | sf | 0 1 1 0 1 0 | op | imm19 | Rt | + * +----+-------------+----+---------------------+--------+ + */ static void disas_comp_b_imm(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int sf, op, rt; + uint64_t addr; + int label_match; + TCGv_i64 tcg_cmp; + + sf = extract32(insn, 31, 1); + op = extract32(insn, 24, 1); /* 0: CBZ; 1: CBNZ */ + rt = extract32(insn, 0, 5); + addr = s->pc + sextract32(insn, 5, 19) * 4 - 4; + + tcg_cmp = read_cpu_reg(s, rt, sf); + label_match = gen_new_label(); + + tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ, + tcg_cmp, 0, label_match); + + gen_goto_tb(s, 0, s->pc); + gen_set_label(label_match); + gen_goto_tb(s, 1, addr); } /* C3.2.5 Test & branch (immediate) From e952d8c77a59dd31b5a4332f19e19f43dc90bd68 Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Tue, 17 Dec 2013 19:42:33 +0000 Subject: [PATCH 38/62] target-arm: A64: add support for conditional select This patch adds support for the instruction group "C3.5.6 Conditional select": CSEL, CSINC, CSINV, CSNEG. Signed-off-by: Claudio Fontana [PMM: Improved code generated in the nomatch case as per RTH suggestions] Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 67 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 5ae3a858ce..13eedf4bc7 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -724,10 +724,73 @@ static void disas_cc_reg(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* Conditional select */ +/* C3.5.6 Conditional select + * 31 30 29 28 21 20 16 15 12 11 10 9 5 4 0 + * +----+----+---+-----------------+------+------+-----+------+------+ + * | sf | op | S | 1 1 0 1 0 1 0 0 | Rm | cond | op2 | Rn | Rd | + * +----+----+---+-----------------+------+------+-----+------+------+ + */ static void disas_cond_select(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int sf, else_inv, rm, cond, else_inc, rn, rd; + TCGv_i64 tcg_rd, tcg_src; + + if (extract32(insn, 29, 1) || extract32(insn, 11, 1)) { + /* S == 1 or op2<1> == 1 */ + unallocated_encoding(s); + return; + } + sf = extract32(insn, 31, 1); + else_inv = extract32(insn, 30, 1); + rm = extract32(insn, 16, 5); + cond = extract32(insn, 12, 4); + else_inc = extract32(insn, 10, 1); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + if (rd == 31) { + /* silly no-op write; until we use movcond we must special-case + * this to avoid a dead temporary across basic blocks. + */ + return; + } + + tcg_rd = cpu_reg(s, rd); + + if (cond >= 0x0e) { /* condition "always" */ + tcg_src = read_cpu_reg(s, rn, sf); + tcg_gen_mov_i64(tcg_rd, tcg_src); + } else { + /* OPTME: we could use movcond here, at the cost of duplicating + * a lot of the arm_gen_test_cc() logic. + */ + int label_match = gen_new_label(); + int label_continue = gen_new_label(); + + arm_gen_test_cc(cond, label_match); + /* nomatch: */ + tcg_src = cpu_reg(s, rm); + + if (else_inv && else_inc) { + tcg_gen_neg_i64(tcg_rd, tcg_src); + } else if (else_inv) { + tcg_gen_not_i64(tcg_rd, tcg_src); + } else if (else_inc) { + tcg_gen_addi_i64(tcg_rd, tcg_src, 1); + } else { + tcg_gen_mov_i64(tcg_rd, tcg_src); + } + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + tcg_gen_br(label_continue); + /* match: */ + gen_set_label(label_match); + tcg_src = read_cpu_reg(s, rn, sf); + tcg_gen_mov_i64(tcg_rd, tcg_src); + /* continue: */ + gen_set_label(label_continue); + } } /* Data-processing (1 source) */ From 832ffa1ce073f010fd1c766361b2e35ce3f105d3 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:34 +0000 Subject: [PATCH 39/62] target-arm: A64: add support for logical (shifted register) Add support for the instructions described in "C3.5.10 Logical (shifted register)". We store the flags in the same locations as the 32 bit decoder. This is slightly awkward when calculating 64 bit results, but seems a better tradeoff than having to rework the whole 32 bit decoder and also make 32 bit result calculation in A64 awkward. Signed-off-by: Alexander Graf [claudio: some refactoring to avoid hidden allocation of temps, rework flags, use enums for shift types, renaming of functions] Signed-off-by: Claudio Fontana [PMM: Use TCG's andc/orc/eqv ops rather than manually inverting] Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 197 +++++++++++++++++++++++++++++++++++-- 1 file changed, 191 insertions(+), 6 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 13eedf4bc7..a459ce6c44 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -36,7 +36,7 @@ static TCGv_i64 cpu_X[32]; static TCGv_i64 cpu_pc; -static TCGv_i32 pstate; +static TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; static const char *regnames[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", @@ -45,6 +45,13 @@ static const char *regnames[] = { "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp" }; +enum a64_shift_type { + A64_SHIFT_TYPE_LSL = 0, + A64_SHIFT_TYPE_LSR = 1, + A64_SHIFT_TYPE_ASR = 2, + A64_SHIFT_TYPE_ROR = 3 +}; + /* initialize TCG globals. */ void a64_translate_init(void) { @@ -59,9 +66,10 @@ void a64_translate_init(void) regnames[i]); } - pstate = tcg_global_mem_new_i32(TCG_AREG0, - offsetof(CPUARMState, pstate), - "pstate"); + cpu_NF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, NF), "NF"); + cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), "ZF"); + cpu_CF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, CF), "CF"); + cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF"); } void aarch64_cpu_dump_state(CPUState *cs, FILE *f, @@ -221,6 +229,33 @@ static TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) return v; } +/* Set ZF and NF based on a 64 bit result. This is alas fiddlier + * than the 32 bit equivalent. + */ +static inline void gen_set_NZ64(TCGv_i64 result) +{ + TCGv_i64 flag = tcg_temp_new_i64(); + + tcg_gen_setcondi_i64(TCG_COND_NE, flag, result, 0); + tcg_gen_trunc_i64_i32(cpu_ZF, flag); + tcg_gen_shri_i64(flag, result, 32); + tcg_gen_trunc_i64_i32(cpu_NF, flag); + tcg_temp_free_i64(flag); +} + +/* Set NZCV as for a logical operation: NZ as per result, CV cleared. */ +static inline void gen_logic_CC(int sf, TCGv_i64 result) +{ + if (sf) { + gen_set_NZ64(result); + } else { + tcg_gen_trunc_i64_i32(cpu_ZF, result); + tcg_gen_trunc_i64_i32(cpu_NF, result); + } + tcg_gen_movi_i32(cpu_CF, 0); + tcg_gen_movi_i32(cpu_VF, 0); +} + /* * the instruction disassembly implemented here matches * the instruction encoding classifications in chapter 3 (C3) @@ -682,10 +717,160 @@ static void disas_data_proc_imm(DisasContext *s, uint32_t insn) } } -/* Logical (shifted register) */ +/* Shift a TCGv src by TCGv shift_amount, put result in dst. + * Note that it is the caller's responsibility to ensure that the + * shift amount is in range (ie 0..31 or 0..63) and provide the ARM + * mandated semantics for out of range shifts. + */ +static void shift_reg(TCGv_i64 dst, TCGv_i64 src, int sf, + enum a64_shift_type shift_type, TCGv_i64 shift_amount) +{ + switch (shift_type) { + case A64_SHIFT_TYPE_LSL: + tcg_gen_shl_i64(dst, src, shift_amount); + break; + case A64_SHIFT_TYPE_LSR: + tcg_gen_shr_i64(dst, src, shift_amount); + break; + case A64_SHIFT_TYPE_ASR: + if (!sf) { + tcg_gen_ext32s_i64(dst, src); + } + tcg_gen_sar_i64(dst, sf ? src : dst, shift_amount); + break; + case A64_SHIFT_TYPE_ROR: + if (sf) { + tcg_gen_rotr_i64(dst, src, shift_amount); + } else { + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(t0, src); + tcg_gen_trunc_i64_i32(t1, shift_amount); + tcg_gen_rotr_i32(t0, t0, t1); + tcg_gen_extu_i32_i64(dst, t0); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } + break; + default: + assert(FALSE); /* all shift types should be handled */ + break; + } + + if (!sf) { /* zero extend final result */ + tcg_gen_ext32u_i64(dst, dst); + } +} + +/* Shift a TCGv src by immediate, put result in dst. + * The shift amount must be in range (this should always be true as the + * relevant instructions will UNDEF on bad shift immediates). + */ +static void shift_reg_imm(TCGv_i64 dst, TCGv_i64 src, int sf, + enum a64_shift_type shift_type, unsigned int shift_i) +{ + assert(shift_i < (sf ? 64 : 32)); + + if (shift_i == 0) { + tcg_gen_mov_i64(dst, src); + } else { + TCGv_i64 shift_const; + + shift_const = tcg_const_i64(shift_i); + shift_reg(dst, src, sf, shift_type, shift_const); + tcg_temp_free_i64(shift_const); + } +} + +/* C3.5.10 Logical (shifted register) + * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 + * +----+-----+-----------+-------+---+------+--------+------+------+ + * | sf | opc | 0 1 0 1 0 | shift | N | Rm | imm6 | Rn | Rd | + * +----+-----+-----------+-------+---+------+--------+------+------+ + */ static void disas_logic_reg(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + TCGv_i64 tcg_rd, tcg_rn, tcg_rm; + unsigned int sf, opc, shift_type, invert, rm, shift_amount, rn, rd; + + sf = extract32(insn, 31, 1); + opc = extract32(insn, 29, 2); + shift_type = extract32(insn, 22, 2); + invert = extract32(insn, 21, 1); + rm = extract32(insn, 16, 5); + shift_amount = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + if (!sf && (shift_amount & (1 << 5))) { + unallocated_encoding(s); + return; + } + + tcg_rd = cpu_reg(s, rd); + + if (opc == 1 && shift_amount == 0 && shift_type == 0 && rn == 31) { + /* Unshifted ORR and ORN with WZR/XZR is the standard encoding for + * register-register MOV and MVN, so it is worth special casing. + */ + tcg_rm = cpu_reg(s, rm); + if (invert) { + tcg_gen_not_i64(tcg_rd, tcg_rm); + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + } else { + if (sf) { + tcg_gen_mov_i64(tcg_rd, tcg_rm); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_rm); + } + } + return; + } + + tcg_rm = read_cpu_reg(s, rm, sf); + + if (shift_amount) { + shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, shift_amount); + } + + tcg_rn = cpu_reg(s, rn); + + switch (opc | (invert << 2)) { + case 0: /* AND */ + case 3: /* ANDS */ + tcg_gen_and_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 1: /* ORR */ + tcg_gen_or_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 2: /* EOR */ + tcg_gen_xor_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 4: /* BIC */ + case 7: /* BICS */ + tcg_gen_andc_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 5: /* ORN */ + tcg_gen_orc_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 6: /* EON */ + tcg_gen_eqv_i64(tcg_rd, tcg_rn, tcg_rm); + break; + default: + assert(FALSE); + break; + } + + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + + if (opc == 3) { + gen_logic_CC(sf, tcg_rd); + } } /* Add/subtract (extended register) */ From 15bfe8b650a0fd40940cb8f4bfc8c57d6940173e Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:34 +0000 Subject: [PATCH 40/62] target-arm: A64: add support for ADR and ADRP Add support for the instructions described in "C3.4.6 PC-rel. addressing" (ADR and ADRP). Signed-off-by: Alexander Graf [claudio: adapted to new decoder structure] Signed-off-by: Claudio Fontana Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- target-arm/translate-a64.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index a459ce6c44..9677d01563 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -653,10 +653,31 @@ static void disas_ldst(DisasContext *s, uint32_t insn) } } -/* PC-rel. addressing */ +/* C3.4.6 PC-rel. addressing + * 31 30 29 28 24 23 5 4 0 + * +----+-------+-----------+-------------------+------+ + * | op | immlo | 1 0 0 0 0 | immhi | Rd | + * +----+-------+-----------+-------------------+------+ + */ static void disas_pc_rel_adr(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int page, rd; + uint64_t base; + int64_t offset; + + page = extract32(insn, 31, 1); + /* SignExtend(immhi:immlo) -> offset */ + offset = ((int64_t)sextract32(insn, 5, 19) << 2) | extract32(insn, 29, 2); + rd = extract32(insn, 0, 5); + base = s->pc - 4; + + if (page) { + /* ADRP (page based) */ + base &= ~0xfff; + offset <<= 12; + } + + tcg_gen_movi_i64(cpu_reg(s, rd), base + offset); } /* Add/subtract (immediate) */ From e801de93d0155c0c14d6b4dea1b3577ca36e214b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:34 +0000 Subject: [PATCH 41/62] target-arm: A64: add support for EXTR This patch adds emulation support for the EXTR instruction. Signed-off-by: Alexander Graf [claudio: adapted for new decoder, removed a few temporaries, fixed the 32bit bug, added checks for more unallocated cases] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 49 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 9677d01563..f9336ded93 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -704,10 +704,55 @@ static void disas_bitfield(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* Extract */ +/* C3.4.3 Extract + * 31 30 29 28 23 22 21 20 16 15 10 9 5 4 0 + * +----+------+-------------+---+----+------+--------+------+------+ + * | sf | op21 | 1 0 0 1 1 1 | N | o0 | Rm | imms | Rn | Rd | + * +----+------+-------------+---+----+------+--------+------+------+ + */ static void disas_extract(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int sf, n, rm, imm, rn, rd, bitsize, op21, op0; + + sf = extract32(insn, 31, 1); + n = extract32(insn, 22, 1); + rm = extract32(insn, 16, 5); + imm = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + op21 = extract32(insn, 29, 2); + op0 = extract32(insn, 21, 1); + bitsize = sf ? 64 : 32; + + if (sf != n || op21 || op0 || imm >= bitsize) { + unallocated_encoding(s); + } else { + TCGv_i64 tcg_rd, tcg_rm, tcg_rn; + + tcg_rd = cpu_reg(s, rd); + + if (imm) { + /* OPTME: we can special case rm==rn as a rotate */ + tcg_rm = read_cpu_reg(s, rm, sf); + tcg_rn = read_cpu_reg(s, rn, sf); + tcg_gen_shri_i64(tcg_rm, tcg_rm, imm); + tcg_gen_shli_i64(tcg_rn, tcg_rn, bitsize - imm); + tcg_gen_or_i64(tcg_rd, tcg_rm, tcg_rn); + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + } else { + /* tcg shl_i32/shl_i64 is undefined for 32/64 bit shifts, + * so an extract from bit 0 is a special case. + */ + if (sf) { + tcg_gen_mov_i64(tcg_rd, cpu_reg(s, rm)); + } else { + tcg_gen_ext32u_i64(tcg_rd, cpu_reg(s, rm)); + } + } + + } } /* C3.4 Data processing - immediate */ From 8220e911c240df5b4b2a1473f0ba9feddc154c45 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:34 +0000 Subject: [PATCH 42/62] target-arm: A64: add support for 2-src data processing and DIV This patch adds support for decoding 2-src data processing insns, and the first users, UDIV and SDIV. Signed-off-by: Alexander Graf [claudio: adapted to new decoder adding the 2-src decoding level, always zero-extend result in 32bit mode] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/helper-a64.c | 21 +++++++++++ target-arm/helper-a64.h | 2 ++ target-arm/translate-a64.c | 72 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index adb8428105..abb98c00b5 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -23,3 +23,24 @@ #include "qemu/host-utils.h" #include "sysemu/sysemu.h" #include "qemu/bitops.h" + +/* C2.4.7 Multiply and divide */ +/* special cases for 0 and LLONG_MIN are mandated by the standard */ +uint64_t HELPER(udiv64)(uint64_t num, uint64_t den) +{ + if (den == 0) { + return 0; + } + return num / den; +} + +int64_t HELPER(sdiv64)(int64_t num, int64_t den) +{ + if (den == 0) { + return 0; + } + if (num == LLONG_MIN && den == -1) { + return LLONG_MIN; + } + return num / den; +} diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h index dd283061dd..e0d65066ab 100644 --- a/target-arm/helper-a64.h +++ b/target-arm/helper-a64.h @@ -16,3 +16,5 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index f9336ded93..a0bc876b8d 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1050,10 +1050,78 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* Data-processing (2 source) */ +static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, + unsigned int rm, unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_n, tcg_m, tcg_rd; + tcg_rd = cpu_reg(s, rd); + + if (!sf && is_signed) { + tcg_n = new_tmp_a64(s); + tcg_m = new_tmp_a64(s); + tcg_gen_ext32s_i64(tcg_n, cpu_reg(s, rn)); + tcg_gen_ext32s_i64(tcg_m, cpu_reg(s, rm)); + } else { + tcg_n = read_cpu_reg(s, rn, sf); + tcg_m = read_cpu_reg(s, rm, sf); + } + + if (is_signed) { + gen_helper_sdiv64(tcg_rd, tcg_n, tcg_m); + } else { + gen_helper_udiv64(tcg_rd, tcg_n, tcg_m); + } + + if (!sf) { /* zero extend final result */ + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } +} + +/* C3.5.8 Data-processing (2 source) + * 31 30 29 28 21 20 16 15 10 9 5 4 0 + * +----+---+---+-----------------+------+--------+------+------+ + * | sf | 0 | S | 1 1 0 1 0 1 1 0 | Rm | opcode | Rn | Rd | + * +----+---+---+-----------------+------+--------+------+------+ + */ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int sf, rm, opcode, rn, rd; + sf = extract32(insn, 31, 1); + rm = extract32(insn, 16, 5); + opcode = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + if (extract32(insn, 29, 1)) { + unallocated_encoding(s); + return; + } + + switch (opcode) { + case 2: /* UDIV */ + handle_div(s, false, sf, rm, rn, rd); + break; + case 3: /* SDIV */ + handle_div(s, true, sf, rm, rn, rd); + break; + case 8: /* LSLV */ + case 9: /* LSRV */ + case 10: /* ASRV */ + case 11: /* RORV */ + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: /* CRC32 */ + unsupported_encoding(s, insn); + break; + default: + unallocated_encoding(s); + break; + } } /* C3.5 Data processing - register */ From 6c1adc919b6a81e008b919c53902b4877ef4d737 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:34 +0000 Subject: [PATCH 43/62] target-arm: A64: add support for 2-src shift reg insns This adds 2-src variable shift register instructions: C5.6.115 LSLV, C5.6.118 LSRV, C5.6.17 ASRV, C5.6.154 RORV Signed-off-by: Alexander Graf [claudio: adapted to new decoder, use enums for shift types] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index a0bc876b8d..acf30c3d30 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1077,6 +1077,20 @@ static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, } } +/* C5.6.115 LSLV, C5.6.118 LSRV, C5.6.17 ASRV, C5.6.154 RORV */ +static void handle_shift_reg(DisasContext *s, + enum a64_shift_type shift_type, unsigned int sf, + unsigned int rm, unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_shift = tcg_temp_new_i64(); + TCGv_i64 tcg_rd = cpu_reg(s, rd); + TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); + + tcg_gen_andi_i64(tcg_shift, cpu_reg(s, rm), sf ? 63 : 31); + shift_reg(tcg_rd, tcg_rn, sf, shift_type, tcg_shift); + tcg_temp_free_i64(tcg_shift); +} + /* C3.5.8 Data-processing (2 source) * 31 30 29 28 21 20 16 15 10 9 5 4 0 * +----+---+---+-----------------+------+--------+------+------+ @@ -1105,9 +1119,17 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) handle_div(s, true, sf, rm, rn, rd); break; case 8: /* LSLV */ + handle_shift_reg(s, A64_SHIFT_TYPE_LSL, sf, rm, rn, rd); + break; case 9: /* LSRV */ + handle_shift_reg(s, A64_SHIFT_TYPE_LSR, sf, rm, rn, rd); + break; case 10: /* ASRV */ + handle_shift_reg(s, A64_SHIFT_TYPE_ASR, sf, rm, rn, rd); + break; case 11: /* RORV */ + handle_shift_reg(s, A64_SHIFT_TYPE_ROR, sf, rm, rn, rd); + break; case 16: case 17: case 18: From 680ead216e666e5cc192fe86adf30563999a5dd8 Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Tue, 17 Dec 2013 19:42:35 +0000 Subject: [PATCH 44/62] target-arm: A64: add support for 1-src data processing and CLZ This patch adds support for decoding 1-src data processing insns, and the first user, C5.6.40 CLZ (count leading zeroes). Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/helper-a64.c | 5 ++++ target-arm/helper-a64.h | 1 + target-arm/translate-a64.c | 52 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index abb98c00b5..e4c534640e 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -44,3 +44,8 @@ int64_t HELPER(sdiv64)(int64_t num, int64_t den) } return num / den; } + +uint64_t HELPER(clz64)(uint64_t x) +{ + return clz64(x); +} diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h index e0d65066ab..b10b6c38e7 100644 --- a/target-arm/helper-a64.h +++ b/target-arm/helper-a64.h @@ -18,3 +18,4 @@ */ DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64) +DEF_HELPER_FLAGS_1(clz64, TCG_CALL_NO_RWG_SE, i64, i64) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index acf30c3d30..e5481da0f6 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1044,10 +1044,58 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) } } -/* Data-processing (1 source) */ +static void handle_clz(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd, tcg_rn; + tcg_rd = cpu_reg(s, rd); + tcg_rn = cpu_reg(s, rn); + + if (sf) { + gen_helper_clz64(tcg_rd, tcg_rn); + } else { + TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn); + gen_helper_clz(tcg_tmp32, tcg_tmp32); + tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); + tcg_temp_free_i32(tcg_tmp32); + } +} + +/* C3.5.7 Data-processing (1 source) + * 31 30 29 28 21 20 16 15 10 9 5 4 0 + * +----+---+---+-----------------+---------+--------+------+------+ + * | sf | 1 | S | 1 1 0 1 0 1 1 0 | opcode2 | opcode | Rn | Rd | + * +----+---+---+-----------------+---------+--------+------+------+ + */ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int sf, opcode, rn, rd; + + if (extract32(insn, 29, 1) || extract32(insn, 16, 5)) { + unallocated_encoding(s); + return; + } + + sf = extract32(insn, 31, 1); + opcode = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + switch (opcode) { + case 0: /* RBIT */ + case 1: /* REV16 */ + case 2: /* REV32 */ + case 3: /* REV64 */ + unsupported_encoding(s, insn); + break; + case 4: /* CLZ */ + handle_clz(s, sf, rn, rd); + break; + case 5: /* CLS */ + unsupported_encoding(s, insn); + break; + } } static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, From 82e14b02a2bd822af6db2ef728a1698b9a24e50c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:35 +0000 Subject: [PATCH 45/62] target-arm: A64: add support for 1-src RBIT insn This adds support for the C5.6.147 RBIT instruction. Signed-off-by: Alexander Graf [claudio: adapted to new decoder, use bswap64, make RBIT part standalone from the rest of the patch, splitting REV into a separate patch] Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/helper-a64.c | 18 ++++++++++++++++++ target-arm/helper-a64.h | 1 + target-arm/translate-a64.c | 20 ++++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index e4c534640e..cccaac6230 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -49,3 +49,21 @@ uint64_t HELPER(clz64)(uint64_t x) { return clz64(x); } + +uint64_t HELPER(rbit64)(uint64_t x) +{ + /* assign the correct byte position */ + x = bswap64(x); + + /* assign the correct nibble position */ + x = ((x & 0xf0f0f0f0f0f0f0f0ULL) >> 4) + | ((x & 0x0f0f0f0f0f0f0f0fULL) << 4); + + /* assign the correct bit position */ + x = ((x & 0x8888888888888888ULL) >> 3) + | ((x & 0x4444444444444444ULL) >> 1) + | ((x & 0x2222222222222222ULL) << 1) + | ((x & 0x1111111111111111ULL) << 3); + + return x; +} diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h index b10b6c38e7..9959139157 100644 --- a/target-arm/helper-a64.h +++ b/target-arm/helper-a64.h @@ -19,3 +19,4 @@ DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64) DEF_HELPER_FLAGS_1(clz64, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index e5481da0f6..0ed21fcf74 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1062,6 +1062,24 @@ static void handle_clz(DisasContext *s, unsigned int sf, } } +static void handle_rbit(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd, tcg_rn; + tcg_rd = cpu_reg(s, rd); + tcg_rn = cpu_reg(s, rn); + + if (sf) { + gen_helper_rbit64(tcg_rd, tcg_rn); + } else { + TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn); + gen_helper_rbit(tcg_tmp32, tcg_tmp32); + tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); + tcg_temp_free_i32(tcg_tmp32); + } +} + /* C3.5.7 Data-processing (1 source) * 31 30 29 28 21 20 16 15 10 9 5 4 0 * +----+---+---+-----------------+---------+--------+------+------+ @@ -1084,6 +1102,8 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) switch (opcode) { case 0: /* RBIT */ + handle_rbit(s, sf, rn, rd); + break; case 1: /* REV16 */ case 2: /* REV32 */ case 3: /* REV64 */ From 453232096c4e93ec871f7ff97abfc0bf54258c95 Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Tue, 17 Dec 2013 19:42:35 +0000 Subject: [PATCH 46/62] target-arm: A64: add support for 1-src REV insns This adds support for C5.6.149 REV, C5.6.151 REV32, C5.6.150 REV16. Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 73 +++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 0ed21fcf74..3c702a98af 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1080,6 +1080,73 @@ static void handle_rbit(DisasContext *s, unsigned int sf, } } +/* C5.6.149 REV with sf==1, opcode==3 ("REV64") */ +static void handle_rev64(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + if (!sf) { + unallocated_encoding(s); + return; + } + tcg_gen_bswap64_i64(cpu_reg(s, rd), cpu_reg(s, rn)); +} + +/* C5.6.149 REV with sf==0, opcode==2 + * C5.6.151 REV32 (sf==1, opcode==2) + */ +static void handle_rev32(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd = cpu_reg(s, rd); + + if (sf) { + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); + + /* bswap32_i64 requires zero high word */ + tcg_gen_ext32u_i64(tcg_tmp, tcg_rn); + tcg_gen_bswap32_i64(tcg_rd, tcg_tmp); + tcg_gen_shri_i64(tcg_tmp, tcg_rn, 32); + tcg_gen_bswap32_i64(tcg_tmp, tcg_tmp); + tcg_gen_concat32_i64(tcg_rd, tcg_rd, tcg_tmp); + + tcg_temp_free_i64(tcg_tmp); + } else { + tcg_gen_ext32u_i64(tcg_rd, cpu_reg(s, rn)); + tcg_gen_bswap32_i64(tcg_rd, tcg_rd); + } +} + +/* C5.6.150 REV16 (opcode==1) */ +static void handle_rev16(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd = cpu_reg(s, rd); + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); + + tcg_gen_andi_i64(tcg_tmp, tcg_rn, 0xffff); + tcg_gen_bswap16_i64(tcg_rd, tcg_tmp); + + tcg_gen_shri_i64(tcg_tmp, tcg_rn, 16); + tcg_gen_andi_i64(tcg_tmp, tcg_tmp, 0xffff); + tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp); + tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, 16, 16); + + if (sf) { + tcg_gen_shri_i64(tcg_tmp, tcg_rn, 32); + tcg_gen_andi_i64(tcg_tmp, tcg_tmp, 0xffff); + tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp); + tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, 32, 16); + + tcg_gen_shri_i64(tcg_tmp, tcg_rn, 48); + tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp); + tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, 48, 16); + } + + tcg_temp_free_i64(tcg_tmp); +} + /* C3.5.7 Data-processing (1 source) * 31 30 29 28 21 20 16 15 10 9 5 4 0 * +----+---+---+-----------------+---------+--------+------+------+ @@ -1105,9 +1172,13 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) handle_rbit(s, sf, rn, rd); break; case 1: /* REV16 */ + handle_rev16(s, sf, rn, rd); + break; case 2: /* REV32 */ + handle_rev32(s, sf, rn, rd); + break; case 3: /* REV64 */ - unsupported_encoding(s, insn); + handle_rev64(s, sf, rn, rd); break; case 4: /* CLZ */ handle_clz(s, sf, rn, rd); From 880777423525685ccaf1bf6967b27cc8a38e5f96 Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Tue, 17 Dec 2013 19:42:35 +0000 Subject: [PATCH 47/62] target-arm: A64: add support for bitfield insns This patch implements the C3.4.2 Bitfield instructions: SBFM, BFM, UBFM. Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 56 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 3c702a98af..2111bcdd10 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -698,10 +698,62 @@ static void disas_movw_imm(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* Bitfield */ +/* C3.4.2 Bitfield + * 31 30 29 28 23 22 21 16 15 10 9 5 4 0 + * +----+-----+-------------+---+------+------+------+------+ + * | sf | opc | 1 0 0 1 1 0 | N | immr | imms | Rn | Rd | + * +----+-----+-------------+---+------+------+------+------+ + */ static void disas_bitfield(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int sf, n, opc, ri, si, rn, rd, bitsize, pos, len; + TCGv_i64 tcg_rd, tcg_tmp; + + sf = extract32(insn, 31, 1); + opc = extract32(insn, 29, 2); + n = extract32(insn, 22, 1); + ri = extract32(insn, 16, 6); + si = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + bitsize = sf ? 64 : 32; + + if (sf != n || ri >= bitsize || si >= bitsize || opc > 2) { + unallocated_encoding(s); + return; + } + + tcg_rd = cpu_reg(s, rd); + tcg_tmp = read_cpu_reg(s, rn, sf); + + /* OPTME: probably worth recognizing common cases of ext{8,16,32}{u,s} */ + + if (opc != 1) { /* SBFM or UBFM */ + tcg_gen_movi_i64(tcg_rd, 0); + } + + /* do the bit move operation */ + if (si >= ri) { + /* Wd = Wn */ + tcg_gen_shri_i64(tcg_tmp, tcg_tmp, ri); + pos = 0; + len = (si - ri) + 1; + } else { + /* Wd<32+s-r,32-r> = Wn */ + pos = bitsize - ri; + len = si + 1; + } + + tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, pos, len); + + if (opc == 0) { /* SBFM - sign extend the destination field */ + tcg_gen_shli_i64(tcg_rd, tcg_rd, 64 - (pos + len)); + tcg_gen_sari_i64(tcg_rd, tcg_rd, 64 - (pos + len)); + } + + if (!sf) { /* zero extend final result */ + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } } /* C3.4.3 Extract From afd3fe4ce56e6fb0d0384ddb8e3c4fac01935c37 Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Tue, 17 Dec 2013 19:42:35 +0000 Subject: [PATCH 48/62] host-utils: add clrsb32/64 - count leading redundant sign bits this patch introduces wrappers for the clrsb builtins, which count the leading redundant sign bits. Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- include/qemu/host-utils.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 0f688c1c00..de85d282d0 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -227,6 +227,38 @@ static inline int cto64(uint64_t val) return ctz64(~val); } +/** + * clrsb32 - count leading redundant sign bits in a 32-bit value. + * @val: The value to search + * + * Returns the number of bits following the sign bit that are equal to it. + * No special cases; output range is [0-31]. + */ +static inline int clrsb32(uint32_t val) +{ +#if QEMU_GNUC_PREREQ(4, 7) + return __builtin_clrsb(val); +#else + return clz32(val ^ ((int32_t)val >> 1)) - 1; +#endif +} + +/** + * clrsb64 - count leading redundant sign bits in a 64-bit value. + * @val: The value to search + * + * Returns the number of bits following the sign bit that are equal to it. + * No special cases; output range is [0-63]. + */ +static inline int clrsb64(uint64_t val) +{ +#if QEMU_GNUC_PREREQ(4, 7) + return __builtin_clrsbll(val); +#else + return clz64(val ^ ((int64_t)val >> 1)) - 1; +#endif +} + /** * ctpop8 - count the population of one bits in an 8-bit value. * @val: The value to search From e80c502023d332fb60866eb378e715ab3f158b72 Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Tue, 17 Dec 2013 19:42:35 +0000 Subject: [PATCH 49/62] target-arm: A64: add support for 1-src CLS insn this patch adds support for the CLS instruction. Signed-off-by: Claudio Fontana Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/helper-a64.c | 10 ++++++++++ target-arm/helper-a64.h | 2 ++ target-arm/translate-a64.c | 20 +++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index cccaac6230..d3f706754f 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -50,6 +50,16 @@ uint64_t HELPER(clz64)(uint64_t x) return clz64(x); } +uint64_t HELPER(cls64)(uint64_t x) +{ + return clrsb64(x); +} + +uint32_t HELPER(cls32)(uint32_t x) +{ + return clrsb32(x); +} + uint64_t HELPER(rbit64)(uint64_t x) { /* assign the correct byte position */ diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h index 9959139157..a163a94322 100644 --- a/target-arm/helper-a64.h +++ b/target-arm/helper-a64.h @@ -19,4 +19,6 @@ DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64) DEF_HELPER_FLAGS_1(clz64, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(cls64, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(cls32, TCG_CALL_NO_RWG_SE, i32, i32) DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 2111bcdd10..2bb1795959 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1114,6 +1114,24 @@ static void handle_clz(DisasContext *s, unsigned int sf, } } +static void handle_cls(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd, tcg_rn; + tcg_rd = cpu_reg(s, rd); + tcg_rn = cpu_reg(s, rn); + + if (sf) { + gen_helper_cls64(tcg_rd, tcg_rn); + } else { + TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn); + gen_helper_cls32(tcg_tmp32, tcg_tmp32); + tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); + tcg_temp_free_i32(tcg_tmp32); + } +} + static void handle_rbit(DisasContext *s, unsigned int sf, unsigned int rn, unsigned int rd) { @@ -1236,7 +1254,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) handle_clz(s, sf, rn, rd); break; case 5: /* CLS */ - unsupported_encoding(s, insn); + handle_cls(s, sf, rn, rd); break; } } From 71b46089303beb7d52a0b9397f5c286a7e66275f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 17 Dec 2013 19:42:36 +0000 Subject: [PATCH 50/62] target-arm: A64: add support for logical (immediate) insns This patch adds support for C3.4.4 Logical (immediate), which include AND, ANDS, ORR, EOR. Signed-off-by: Alexander Graf [claudio: adapted to new decoder, function renaming, removed a TCG temp variable] Signed-off-by: Claudio Fontana [PMM: cleaned up some unnecessary code in logic_imm_decode_wmask and added clarifying commentary on what it's actually doing. Dropped an ext32u that's not needed if we've just done an AND.] Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target-arm/translate-a64.c | 175 ++++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 2 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 2bb1795959..0a76130bb2 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -201,6 +201,21 @@ static TCGv_i64 new_tmp_a64_zero(DisasContext *s) return t; } +/* + * Register access functions + * + * These functions are used for directly accessing a register in where + * changes to the final register value are likely to be made. If you + * need to use a register for temporary calculation (e.g. index type + * operations) use the read_* form. + * + * B1.2.1 Register mappings + * + * In instruction register encoding 31 can refer to ZR (zero register) or + * the SP (stack pointer) depending on context. In QEMU's case we map SP + * to cpu_X[31] and ZR accesses to a temporary which can be discarded. + * This is the point of the _sp forms. + */ static TCGv_i64 cpu_reg(DisasContext *s, int reg) { if (reg == 31) { @@ -210,6 +225,12 @@ static TCGv_i64 cpu_reg(DisasContext *s, int reg) } } +/* register access for when 31 == SP */ +static TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) +{ + return cpu_X[reg]; +} + /* read a cpu register in 32bit/64bit mode. Returns a TCGv_i64 * representing the register contents. This TCGv is an auto-freed * temporary so it need not be explicitly freed, and may be modified. @@ -686,10 +707,160 @@ static void disas_add_sub_imm(DisasContext *s, uint32_t insn) unsupported_encoding(s, insn); } -/* Logical (immediate) */ +/* The input should be a value in the bottom e bits (with higher + * bits zero); returns that value replicated into every element + * of size e in a 64 bit integer. + */ +static uint64_t bitfield_replicate(uint64_t mask, unsigned int e) +{ + assert(e != 0); + while (e < 64) { + mask |= mask << e; + e *= 2; + } + return mask; +} + +/* Return a value with the bottom len bits set (where 0 < len <= 64) */ +static inline uint64_t bitmask64(unsigned int length) +{ + assert(length > 0 && length <= 64); + return ~0ULL >> (64 - length); +} + +/* Simplified variant of pseudocode DecodeBitMasks() for the case where we + * only require the wmask. Returns false if the imms/immr/immn are a reserved + * value (ie should cause a guest UNDEF exception), and true if they are + * valid, in which case the decoded bit pattern is written to result. + */ +static bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, + unsigned int imms, unsigned int immr) +{ + uint64_t mask; + unsigned e, levels, s, r; + int len; + + assert(immn < 2 && imms < 64 && immr < 64); + + /* The bit patterns we create here are 64 bit patterns which + * are vectors of identical elements of size e = 2, 4, 8, 16, 32 or + * 64 bits each. Each element contains the same value: a run + * of between 1 and e-1 non-zero bits, rotated within the + * element by between 0 and e-1 bits. + * + * The element size and run length are encoded into immn (1 bit) + * and imms (6 bits) as follows: + * 64 bit elements: immn = 1, imms = + * 32 bit elements: immn = 0, imms = 0 : + * 16 bit elements: immn = 0, imms = 10 : + * 8 bit elements: immn = 0, imms = 110 : + * 4 bit elements: immn = 0, imms = 1110 : + * 2 bit elements: immn = 0, imms = 11110 : + * Notice that immn = 0, imms = 11111x is the only combination + * not covered by one of the above options; this is reserved. + * Further, all-ones is a reserved pattern. + * + * In all cases the rotation is by immr % e (and immr is 6 bits). + */ + + /* First determine the element size */ + len = 31 - clz32((immn << 6) | (~imms & 0x3f)); + if (len < 1) { + /* This is the immn == 0, imms == 0x11111x case */ + return false; + } + e = 1 << len; + + levels = e - 1; + s = imms & levels; + r = immr & levels; + + if (s == levels) { + /* mustn't be all-ones. */ + return false; + } + + /* Create the value of one element: s+1 set bits rotated + * by r within the element (which is e bits wide)... + */ + mask = bitmask64(s + 1); + mask = (mask >> r) | (mask << (e - r)); + /* ...then replicate the element over the whole 64 bit value */ + mask = bitfield_replicate(mask, e); + *result = mask; + return true; +} + +/* C3.4.4 Logical (immediate) + * 31 30 29 28 23 22 21 16 15 10 9 5 4 0 + * +----+-----+-------------+---+------+------+------+------+ + * | sf | opc | 1 0 0 1 0 0 | N | immr | imms | Rn | Rd | + * +----+-----+-------------+---+------+------+------+------+ + */ static void disas_logic_imm(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + unsigned int sf, opc, is_n, immr, imms, rn, rd; + TCGv_i64 tcg_rd, tcg_rn; + uint64_t wmask; + bool is_and = false; + + sf = extract32(insn, 31, 1); + opc = extract32(insn, 29, 2); + is_n = extract32(insn, 22, 1); + immr = extract32(insn, 16, 6); + imms = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + if (!sf && is_n) { + unallocated_encoding(s); + return; + } + + if (opc == 0x3) { /* ANDS */ + tcg_rd = cpu_reg(s, rd); + } else { + tcg_rd = cpu_reg_sp(s, rd); + } + tcg_rn = cpu_reg(s, rn); + + if (!logic_imm_decode_wmask(&wmask, is_n, imms, immr)) { + /* some immediate field values are reserved */ + unallocated_encoding(s); + return; + } + + if (!sf) { + wmask &= 0xffffffff; + } + + switch (opc) { + case 0x3: /* ANDS */ + case 0x0: /* AND */ + tcg_gen_andi_i64(tcg_rd, tcg_rn, wmask); + is_and = true; + break; + case 0x1: /* ORR */ + tcg_gen_ori_i64(tcg_rd, tcg_rn, wmask); + break; + case 0x2: /* EOR */ + tcg_gen_xori_i64(tcg_rd, tcg_rn, wmask); + break; + default: + assert(FALSE); /* must handle all above */ + break; + } + + if (!sf && !is_and) { + /* zero extend final result; we know we can skip this for AND + * since the immediate had the high 32 bits clear. + */ + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + + if (opc == 3) { /* ANDS */ + gen_logic_CC(sf, tcg_rd); + } } /* Move wide (immediate) */ From c6f09eb4a0ea14b68f2745e87641c79a51057959 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 17 Dec 2013 19:42:36 +0000 Subject: [PATCH 51/62] hw/arm: add very initial support for Canon DIGIC SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DIGIC is Canon Inc.'s name for a family of SoC for digital cameras and camcorders. There is no publicly available specification for DIGIC chips. All information about DIGIC chip internals is based on reverse engineering efforts made by CHDK (http://chdk.wikia.com) and Magic Lantern (http://www.magiclantern.fm) projects contributors. Signed-off-by: Antony Pavlov Reviewed-by: Andreas Färber Reviewed-by: Peter Maydell Reviewed-by: Peter Crosthwaite Message-id: 1387188908-754-2-git-send-email-antonynpavlov@gmail.com Signed-off-by: Peter Maydell --- default-configs/arm-softmmu.mak | 1 + hw/arm/Makefile.objs | 1 + hw/arm/digic.c | 71 +++++++++++++++++++++++++++++++++ include/hw/arm/digic.h | 35 ++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 hw/arm/digic.c create mode 100644 include/hw/arm/digic.h diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index e48f102af6..2135be317b 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -64,6 +64,7 @@ CONFIG_XILINX_SPIPS=y CONFIG_ARM11SCU=y CONFIG_A9SCU=y +CONFIG_DIGIC=y CONFIG_MARVELL_88W8618=y CONFIG_OMAP=y CONFIG_TSC210X=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 78b56149b6..87898075b9 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -4,4 +4,5 @@ obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o +obj-$(CONFIG_DIGIC) += digic.o obj-y += omap1.o omap2.o strongarm.o diff --git a/hw/arm/digic.c b/hw/arm/digic.c new file mode 100644 index 0000000000..2620262f3a --- /dev/null +++ b/hw/arm/digic.c @@ -0,0 +1,71 @@ +/* + * QEMU model of the Canon DIGIC SoC. + * + * Copyright (C) 2013 Antony Pavlov + * + * This model is based on reverse engineering efforts + * made by CHDK (http://chdk.wikia.com) and + * Magic Lantern (http://www.magiclantern.fm) projects + * contributors. + * + * 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. + * + */ + +#include "hw/arm/digic.h" + +static void digic_init(Object *obj) +{ + DigicState *s = DIGIC(obj); + + object_initialize(&s->cpu, sizeof(s->cpu), "arm946-" TYPE_ARM_CPU); + object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); +} + +static void digic_realize(DeviceState *dev, Error **errp) +{ + DigicState *s = DIGIC(dev); + Error *err = NULL; + + object_property_set_bool(OBJECT(&s->cpu), true, "reset-hivecs", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } +} + +static void digic_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = digic_realize; +} + +static const TypeInfo digic_type_info = { + .name = TYPE_DIGIC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(DigicState), + .instance_init = digic_init, + .class_init = digic_class_init, +}; + +static void digic_register_types(void) +{ + type_register_static(&digic_type_info); +} + +type_init(digic_register_types) diff --git a/include/hw/arm/digic.h b/include/hw/arm/digic.h new file mode 100644 index 0000000000..b7d16fb50f --- /dev/null +++ b/include/hw/arm/digic.h @@ -0,0 +1,35 @@ +/* + * Misc Canon DIGIC declarations. + * + * Copyright (C) 2013 Antony Pavlov + * + * 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. + * + */ + +#ifndef HW_ARM_DIGIC_H +#define HW_ARM_DIGIC_H + +#include "cpu.h" + +#define TYPE_DIGIC "digic" + +#define DIGIC(obj) OBJECT_CHECK(DigicState, (obj), TYPE_DIGIC) + +typedef struct DigicState { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + ARMCPU cpu; +} DigicState; + +#endif /* HW_ARM_DIGIC_H */ From d91fd756fb2af16584d338c4ef715f40ea49924c Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 17 Dec 2013 19:42:36 +0000 Subject: [PATCH 52/62] hw/arm/digic: prepare DIGIC-based boards support Also this patch adds initial support for Canon PowerShot A1100 IS compact camera. Signed-off-by: Antony Pavlov Message-id: 1387188908-754-3-git-send-email-antonynpavlov@gmail.com Signed-off-by: Peter Maydell --- hw/arm/Makefile.objs | 1 + hw/arm/digic_boards.c | 84 +++++++++++++++++++++++++++++++++++++++++++ tests/qom-test.c | 1 + 3 files changed, 86 insertions(+) create mode 100644 hw/arm/digic_boards.c diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 87898075b9..d7d37f241d 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,4 +1,5 @@ obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o +obj-$(CONFIG_DIGIC) += digic_boards.o obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c new file mode 100644 index 0000000000..87a9c4bb13 --- /dev/null +++ b/hw/arm/digic_boards.c @@ -0,0 +1,84 @@ +/* + * QEMU model of the Canon DIGIC boards (cameras indeed :). + * + * Copyright (C) 2013 Antony Pavlov + * + * This model is based on reverse engineering efforts + * made by CHDK (http://chdk.wikia.com) and + * Magic Lantern (http://www.magiclantern.fm) projects + * contributors. + * + * See docs here: + * http://magiclantern.wikia.com/wiki/Register_Map + * + * 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. + * + */ + +#include "hw/boards.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" +#include "hw/arm/digic.h" + +typedef struct DigicBoardState { + DigicState *digic; + MemoryRegion ram; +} DigicBoardState; + +typedef struct DigicBoard { + hwaddr ram_size; +} DigicBoard; + +static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size) +{ + memory_region_init_ram(&s->ram, NULL, "ram", ram_size); + memory_region_add_subregion(get_system_memory(), 0, &s->ram); + vmstate_register_ram_global(&s->ram); +} + +static void digic4_board_init(DigicBoard *board) +{ + Error *err = NULL; + + DigicBoardState *s = g_new(DigicBoardState, 1); + + s->digic = DIGIC(object_new(TYPE_DIGIC)); + object_property_set_bool(OBJECT(s->digic), true, "realized", &err); + if (err != NULL) { + error_report("Couldn't realize DIGIC SoC: %s\n", + error_get_pretty(err)); + exit(1); + } + + digic4_board_setup_ram(s, board->ram_size); +} + +static DigicBoard digic4_board_canon_a1100 = { + .ram_size = 64 * 1024 * 1024, +}; + +static void canon_a1100_init(QEMUMachineInitArgs *args) +{ + digic4_board_init(&digic4_board_canon_a1100); +} + +static QEMUMachine canon_a1100 = { + .name = "canon-a1100", + .desc = "Canon PowerShot A1100 IS", + .init = &canon_a1100_init, +}; + +static void digic_register_machines(void) +{ + qemu_register_machine(&canon_a1100); +} + +machine_init(digic_register_machines) diff --git a/tests/qom-test.c b/tests/qom-test.c index 499be40261..f67f7c2f09 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -70,6 +70,7 @@ static const char *arm_machines[] = { "xilinx-zynq-a9", "highbank", "midway", + "canon-a1100", }; static const char *cris_machines[] = { From 576e99cb951e9c1a289555a31cfd5b9040e80037 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 17 Dec 2013 19:42:36 +0000 Subject: [PATCH 53/62] hw/arm/digic: add timer support Signed-off-by: Antony Pavlov Reviewed-by: Peter Crosthwaite Message-id: 1387188908-754-4-git-send-email-antonynpavlov@gmail.com Signed-off-by: Peter Maydell --- hw/arm/digic.c | 28 ++++++ hw/timer/Makefile.objs | 1 + hw/timer/digic-timer.c | 163 +++++++++++++++++++++++++++++++++ include/hw/arm/digic.h | 6 ++ include/hw/timer/digic-timer.h | 46 ++++++++++ 5 files changed, 244 insertions(+) create mode 100644 hw/timer/digic-timer.c create mode 100644 include/hw/timer/digic-timer.h diff --git a/hw/arm/digic.c b/hw/arm/digic.c index 2620262f3a..e8eb0de9a1 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -22,18 +22,35 @@ #include "hw/arm/digic.h" +#define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) + static void digic_init(Object *obj) { DigicState *s = DIGIC(obj); + DeviceState *dev; + int i; object_initialize(&s->cpu, sizeof(s->cpu), "arm946-" TYPE_ARM_CPU); object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); + + for (i = 0; i < DIGIC4_NB_TIMERS; i++) { +#define DIGIC_TIMER_NAME_MLEN 11 + char name[DIGIC_TIMER_NAME_MLEN]; + + object_initialize(&s->timer[i], sizeof(s->timer[i]), TYPE_DIGIC_TIMER); + dev = DEVICE(&s->timer[i]); + qdev_set_parent_bus(dev, sysbus_get_default()); + snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i); + object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL); + } } static void digic_realize(DeviceState *dev, Error **errp) { DigicState *s = DIGIC(dev); Error *err = NULL; + SysBusDevice *sbd; + int i; object_property_set_bool(OBJECT(&s->cpu), true, "reset-hivecs", &err); if (err != NULL) { @@ -46,6 +63,17 @@ static void digic_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } + + for (i = 0; i < DIGIC4_NB_TIMERS; i++) { + object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + sbd = SYS_BUS_DEVICE(&s->timer[i]); + sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i)); + } } static void digic_class_init(ObjectClass *oc, void *data) diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 3ae091c95e..ea9f11f706 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -26,5 +26,6 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o obj-$(CONFIG_SH4) += sh_timer.o obj-$(CONFIG_TUSB6010) += tusb6010.o +obj-$(CONFIG_DIGIC) += digic-timer.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c new file mode 100644 index 0000000000..1fde22c67f --- /dev/null +++ b/hw/timer/digic-timer.c @@ -0,0 +1,163 @@ +/* + * QEMU model of the Canon DIGIC timer block. + * + * Copyright (C) 2013 Antony Pavlov + * + * This model is based on reverse engineering efforts + * made by CHDK (http://chdk.wikia.com) and + * Magic Lantern (http://www.magiclantern.fm) projects + * contributors. + * + * See "Timer/Clock Module" docs here: + * http://magiclantern.wikia.com/wiki/Register_Map + * + * The QEMU model of the OSTimer in PKUnity SoC by Guan Xuetao + * is used as a template. + * + * 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. + * + */ + +#include "hw/sysbus.h" +#include "hw/ptimer.h" +#include "qemu/main-loop.h" + +#include "hw/timer/digic-timer.h" + +static const VMStateDescription vmstate_digic_timer = { + .name = "digic.timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(ptimer, DigicTimerState), + VMSTATE_UINT32(control, DigicTimerState), + VMSTATE_UINT32(relvalue, DigicTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void digic_timer_reset(DeviceState *dev) +{ + DigicTimerState *s = DIGIC_TIMER(dev); + + ptimer_stop(s->ptimer); + s->control = 0; + s->relvalue = 0; +} + +static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size) +{ + DigicTimerState *s = opaque; + uint64_t ret = 0; + + switch (offset) { + case DIGIC_TIMER_CONTROL: + ret = s->control; + break; + case DIGIC_TIMER_RELVALUE: + ret = s->relvalue; + break; + case DIGIC_TIMER_VALUE: + ret = ptimer_get_count(s->ptimer) & 0xffff; + break; + default: + qemu_log_mask(LOG_UNIMP, + "digic-timer: read access to unknown register 0x" + TARGET_FMT_plx, offset); + } + + return ret; +} + +static void digic_timer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + DigicTimerState *s = opaque; + + switch (offset) { + case DIGIC_TIMER_CONTROL: + if (value & DIGIC_TIMER_CONTROL_RST) { + digic_timer_reset((DeviceState *)s); + break; + } + + if (value & DIGIC_TIMER_CONTROL_EN) { + ptimer_run(s->ptimer, 0); + } + + s->control = (uint32_t)value; + break; + + case DIGIC_TIMER_RELVALUE: + s->relvalue = extract32(value, 0, 16); + ptimer_set_limit(s->ptimer, s->relvalue, 1); + break; + + case DIGIC_TIMER_VALUE: + break; + + default: + qemu_log_mask(LOG_UNIMP, + "digic-timer: read access to unknown register 0x" + TARGET_FMT_plx, offset); + } +} + +static const MemoryRegionOps digic_timer_ops = { + .read = digic_timer_read, + .write = digic_timer_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void digic_timer_init(Object *obj) +{ + DigicTimerState *s = DIGIC_TIMER(obj); + + s->ptimer = ptimer_init(NULL); + + /* + * FIXME: there is no documentation on Digic timer + * frequency setup so let it always run at 1 MHz + */ + ptimer_set_freq(s->ptimer, 1 * 1000 * 1000); + + memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s, + TYPE_DIGIC_TIMER, 0x100); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +} + +static void digic_timer_class_init(ObjectClass *klass, void *class_data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = digic_timer_reset; + dc->vmsd = &vmstate_digic_timer; +} + +static const TypeInfo digic_timer_info = { + .name = TYPE_DIGIC_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(DigicTimerState), + .instance_init = digic_timer_init, + .class_init = digic_timer_class_init, +}; + +static void digic_timer_register_type(void) +{ + type_register_static(&digic_timer_info); +} + +type_init(digic_timer_register_type) diff --git a/include/hw/arm/digic.h b/include/hw/arm/digic.h index b7d16fb50f..177a06d64b 100644 --- a/include/hw/arm/digic.h +++ b/include/hw/arm/digic.h @@ -20,16 +20,22 @@ #include "cpu.h" +#include "hw/timer/digic-timer.h" + #define TYPE_DIGIC "digic" #define DIGIC(obj) OBJECT_CHECK(DigicState, (obj), TYPE_DIGIC) +#define DIGIC4_NB_TIMERS 3 + typedef struct DigicState { /*< private >*/ DeviceState parent_obj; /*< public >*/ ARMCPU cpu; + + DigicTimerState timer[DIGIC4_NB_TIMERS]; } DigicState; #endif /* HW_ARM_DIGIC_H */ diff --git a/include/hw/timer/digic-timer.h b/include/hw/timer/digic-timer.h new file mode 100644 index 0000000000..ae913482c6 --- /dev/null +++ b/include/hw/timer/digic-timer.h @@ -0,0 +1,46 @@ +/* + * Canon DIGIC timer block declarations. + * + * Copyright (C) 2013 Antony Pavlov + * + * 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. + * + */ + +#ifndef HW_TIMER_DIGIC_TIMER_H +#define HW_TIMER_DIGIC_TIMER_H + +#include "hw/sysbus.h" +#include "qemu/typedefs.h" +#include "hw/ptimer.h" + +#define TYPE_DIGIC_TIMER "digic-timer" +#define DIGIC_TIMER(obj) OBJECT_CHECK(DigicTimerState, (obj), TYPE_DIGIC_TIMER) + +#define DIGIC_TIMER_CONTROL 0x00 +#define DIGIC_TIMER_CONTROL_RST 0x80000000 +#define DIGIC_TIMER_CONTROL_EN 0x00000001 +#define DIGIC_TIMER_RELVALUE 0x08 +#define DIGIC_TIMER_VALUE 0x0c + +typedef struct DigicTimerState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + ptimer_state *ptimer; + + uint32_t control; + uint32_t relvalue; +} DigicTimerState; + +#endif /* HW_TIMER_DIGIC_TIMER_H */ From 142593c9d700e02b316443bcaa99226720242625 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 17 Dec 2013 19:42:37 +0000 Subject: [PATCH 54/62] hw/arm/digic: add UART support Signed-off-by: Antony Pavlov Reviewed-by: Peter Maydell Reviewed-by: Peter Crosthwaite Message-id: 1387188908-754-5-git-send-email-antonynpavlov@gmail.com Signed-off-by: Peter Maydell --- hw/arm/digic.c | 16 +++ hw/char/Makefile.objs | 1 + hw/char/digic-uart.c | 195 +++++++++++++++++++++++++++++++++++ include/hw/arm/digic.h | 2 + include/hw/char/digic-uart.h | 47 +++++++++ 5 files changed, 261 insertions(+) create mode 100644 hw/char/digic-uart.c create mode 100644 include/hw/char/digic-uart.h diff --git a/hw/arm/digic.c b/hw/arm/digic.c index e8eb0de9a1..ec8c330602 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -24,6 +24,8 @@ #define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) +#define DIGIC_UART_BASE 0xc0800000 + static void digic_init(Object *obj) { DigicState *s = DIGIC(obj); @@ -43,6 +45,11 @@ static void digic_init(Object *obj) snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i); object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL); } + + object_initialize(&s->uart, sizeof(s->uart), TYPE_DIGIC_UART); + dev = DEVICE(&s->uart); + qdev_set_parent_bus(dev, sysbus_get_default()); + object_property_add_child(obj, "uart", OBJECT(&s->uart), NULL); } static void digic_realize(DeviceState *dev, Error **errp) @@ -74,6 +81,15 @@ static void digic_realize(DeviceState *dev, Error **errp) sbd = SYS_BUS_DEVICE(&s->timer[i]); sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i)); } + + object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + sbd = SYS_BUS_DEVICE(&s->uart); + sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE); } static void digic_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index cbd6a006f4..be2a7d953a 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -14,6 +14,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_uart.o obj-$(CONFIG_OMAP) += omap_uart.o obj-$(CONFIG_SH4) += sh_serial.o obj-$(CONFIG_PSERIES) += spapr_vty.o +obj-$(CONFIG_DIGIC) += digic-uart.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c new file mode 100644 index 0000000000..fd8e07713d --- /dev/null +++ b/hw/char/digic-uart.c @@ -0,0 +1,195 @@ +/* + * QEMU model of the Canon DIGIC UART block. + * + * Copyright (C) 2013 Antony Pavlov + * + * This model is based on reverse engineering efforts + * made by CHDK (http://chdk.wikia.com) and + * Magic Lantern (http://www.magiclantern.fm) projects + * contributors. + * + * See "Serial terminal" docs here: + * http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers + * + * The QEMU model of the Milkymist UART block by Michael Walle + * is used as a template. + * + * 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. + * + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "sysemu/char.h" + +#include "hw/char/digic-uart.h" + +enum { + ST_RX_RDY = (1 << 0), + ST_TX_RDY = (1 << 1), +}; + +static uint64_t digic_uart_read(void *opaque, hwaddr addr, + unsigned size) +{ + DigicUartState *s = opaque; + uint64_t ret = 0; + + addr >>= 2; + + switch (addr) { + case R_RX: + s->reg_st &= ~(ST_RX_RDY); + ret = s->reg_rx; + break; + + case R_ST: + ret = s->reg_st; + break; + + default: + qemu_log_mask(LOG_UNIMP, + "digic-uart: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + } + + return ret; +} + +static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + DigicUartState *s = opaque; + unsigned char ch = value; + + addr >>= 2; + + switch (addr) { + case R_TX: + if (s->chr) { + qemu_chr_fe_write_all(s->chr, &ch, 1); + } + break; + + case R_ST: + /* + * Ignore write to R_ST. + * + * The point is that this register is actively used + * during receiving and transmitting symbols, + * but we don't know the function of most of bits. + * + * Ignoring writes to R_ST is only a simplification + * of the model. It has no perceptible side effects + * for existing guests. + */ + break; + + default: + qemu_log_mask(LOG_UNIMP, + "digic-uart: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + } +} + +static const MemoryRegionOps uart_mmio_ops = { + .read = digic_uart_read, + .write = digic_uart_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int uart_can_rx(void *opaque) +{ + DigicUartState *s = opaque; + + return !(s->reg_st & ST_RX_RDY); +} + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + DigicUartState *s = opaque; + + assert(uart_can_rx(opaque)); + + s->reg_st |= ST_RX_RDY; + s->reg_rx = *buf; +} + +static void uart_event(void *opaque, int event) +{ +} + +static void digic_uart_reset(DeviceState *d) +{ + DigicUartState *s = DIGIC_UART(d); + + s->reg_rx = 0; + s->reg_st = ST_TX_RDY; +} + +static void digic_uart_realize(DeviceState *dev, Error **errp) +{ + DigicUartState *s = DIGIC_UART(dev); + + s->chr = qemu_char_get_next_serial(); + if (s->chr) { + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); + } +} + +static void digic_uart_init(Object *obj) +{ + DigicUartState *s = DIGIC_UART(obj); + + memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s, + TYPE_DIGIC_UART, 0x18); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region); +} + +static const VMStateDescription vmstate_digic_uart = { + .name = "digic-uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(reg_rx, DigicUartState), + VMSTATE_UINT32(reg_st, DigicUartState), + VMSTATE_END_OF_LIST() + } +}; + +static void digic_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = digic_uart_realize; + dc->reset = digic_uart_reset; + dc->vmsd = &vmstate_digic_uart; +} + +static const TypeInfo digic_uart_info = { + .name = TYPE_DIGIC_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(DigicUartState), + .instance_init = digic_uart_init, + .class_init = digic_uart_class_init, +}; + +static void digic_uart_register_types(void) +{ + type_register_static(&digic_uart_info); +} + +type_init(digic_uart_register_types) diff --git a/include/hw/arm/digic.h b/include/hw/arm/digic.h index 177a06d64b..a739d6ae65 100644 --- a/include/hw/arm/digic.h +++ b/include/hw/arm/digic.h @@ -21,6 +21,7 @@ #include "cpu.h" #include "hw/timer/digic-timer.h" +#include "hw/char/digic-uart.h" #define TYPE_DIGIC "digic" @@ -36,6 +37,7 @@ typedef struct DigicState { ARMCPU cpu; DigicTimerState timer[DIGIC4_NB_TIMERS]; + DigicUartState uart; } DigicState; #endif /* HW_ARM_DIGIC_H */ diff --git a/include/hw/char/digic-uart.h b/include/hw/char/digic-uart.h new file mode 100644 index 0000000000..ef83a3059c --- /dev/null +++ b/include/hw/char/digic-uart.h @@ -0,0 +1,47 @@ +/* + * Canon DIGIC UART block declarations. + * + * Copyright (C) 2013 Antony Pavlov + * + * 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. + * + */ + +#ifndef HW_CHAR_DIGIC_UART_H +#define HW_CHAR_DIGIC_UART_H + +#include "hw/sysbus.h" +#include "qemu/typedefs.h" + +#define TYPE_DIGIC_UART "digic-uart" +#define DIGIC_UART(obj) \ + OBJECT_CHECK(DigicUartState, (obj), TYPE_DIGIC_UART) + +enum { + R_TX = 0x00, + R_RX, + R_ST = (0x14 >> 2), + R_MAX +}; + +typedef struct DigicUartState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion regs_region; + CharDriverState *chr; + + uint32_t reg_rx; + uint32_t reg_st; +} DigicUartState; + +#endif /* HW_CHAR_DIGIC_UART_H */ From 04234a37b1a89f3f119ef59c143a7b2e0a1ab40e Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 17 Dec 2013 19:42:37 +0000 Subject: [PATCH 55/62] hw/arm/digic: add NOR ROM support Signed-off-by: Antony Pavlov Message-id: 1387188908-754-6-git-send-email-antonynpavlov@gmail.com [PMM: don't try to load ROM blob if qtest_enabled()] Signed-off-by: Peter Maydell --- hw/arm/digic_boards.c | 78 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index 87a9c4bb13..32fc30a69d 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -27,6 +27,14 @@ #include "exec/address-spaces.h" #include "qemu/error-report.h" #include "hw/arm/digic.h" +#include "hw/block/flash.h" +#include "hw/loader.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" + +#define DIGIC4_ROM0_BASE 0xf0000000 +#define DIGIC4_ROM1_BASE 0xf8000000 +#define DIGIC4_ROM_MAX_SIZE 0x08000000 typedef struct DigicBoardState { DigicState *digic; @@ -35,6 +43,10 @@ typedef struct DigicBoardState { typedef struct DigicBoard { hwaddr ram_size; + void (*add_rom0)(DigicBoardState *, hwaddr, const char *); + const char *rom0_def_filename; + void (*add_rom1)(DigicBoardState *, hwaddr, const char *); + const char *rom1_def_filename; } DigicBoard; static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size) @@ -59,10 +71,76 @@ static void digic4_board_init(DigicBoard *board) } digic4_board_setup_ram(s, board->ram_size); + + if (board->add_rom0) { + board->add_rom0(s, DIGIC4_ROM0_BASE, board->rom0_def_filename); + } + + if (board->add_rom1) { + board->add_rom1(s, DIGIC4_ROM1_BASE, board->rom1_def_filename); + } +} + +static void digic_load_rom(DigicBoardState *s, hwaddr addr, + hwaddr max_size, const char *def_filename) +{ + target_long rom_size; + const char *filename; + + if (qtest_enabled()) { + /* qtest runs no code so don't attempt a ROM load which + * could fail and result in a spurious test failure. + */ + return; + } + + if (bios_name) { + filename = bios_name; + } else { + filename = def_filename; + } + + if (filename) { + char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); + + if (!fn) { + error_report("Couldn't find rom image '%s'.\n", filename); + exit(1); + } + + rom_size = load_image_targphys(fn, addr, max_size); + if (rom_size < 0 || rom_size > max_size) { + error_report("Couldn't load rom image '%s'.\n", filename); + exit(1); + } + } +} + +/* + * Samsung K8P3215UQB + * 64M Bit (4Mx16) Page Mode / Multi-Bank NOR Flash Memory + */ +static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr, + const char *def_filename) +{ +#define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024) +#define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024) + + pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE, + NULL, FLASH_K8P3215UQB_SECTOR_SIZE, + FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE, + DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE, + 4, + 0x00EC, 0x007E, 0x0003, 0x0001, + 0x0555, 0x2aa, 0); + + digic_load_rom(s, addr, FLASH_K8P3215UQB_SIZE, def_filename); } static DigicBoard digic4_board_canon_a1100 = { .ram_size = 64 * 1024 * 1024, + .add_rom1 = digic4_add_k8p3215uqb_rom, + .rom1_def_filename = "canon-a1100-rom1.bin", }; static void canon_a1100_init(QEMUMachineInitArgs *args) From 9082f12173d407290bcec9f4ae84242972b2480c Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 17 Dec 2013 19:42:37 +0000 Subject: [PATCH 56/62] MAINTAINERS: Document 'Canon DIGIC' machine Signed-off-by: Antony Pavlov Message-id: 1387188908-754-7-git-send-email-antonynpavlov@gmail.com Signed-off-by: Peter Maydell --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7f45d1d696..1fce9a5749 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -233,6 +233,12 @@ S: Supported F: hw/arm/highbank.c F: hw/net/xgmac.c +Canon DIGIC +M: Antony Pavlov +S: Maintained +F: include/hw/arm/digic.h +F: hw/*/digic* + Gumstix M: qemu-devel@nongnu.org S: Orphan From a1f05e79f2c207bded5efc23e8c6b1ca58161a8e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Dec 2013 19:42:37 +0000 Subject: [PATCH 57/62] vmstate: Add support for an array of ptimer_state * Add support for defining a vmstate field which is an array of pointers to structures, and use this to define a VMSTATE_PTIMER_ARRAY() which allows an array of ptimer_state* to be used by devices. Signed-off-by: Peter Maydell Message-id: 1387159292-10436-2-git-send-email-lig.fnst@cn.fujitsu.com --- include/hw/ptimer.h | 4 ++++ include/migration/vmstate.h | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h index 28fcaf17f8..a33edf4b0c 100644 --- a/include/hw/ptimer.h +++ b/include/hw/ptimer.h @@ -36,4 +36,8 @@ extern const VMStateDescription vmstate_ptimer; .offset = vmstate_offset_pointer(_state, _field, ptimer_state), \ } +#define VMSTATE_PTIMER_ARRAY(_f, _s, _n) \ + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(_f, _s, _n, 0, \ + vmstate_ptimer, ptimer_state) + #endif diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 9d09e60419..be193baba1 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -339,6 +339,16 @@ extern const VMStateInfo vmstate_info_bitmap; .offset = vmstate_offset_array(_state, _field, _type, _num), \ } +#define VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(_f, _s, _n, _v, _vmsd, _type) { \ + .name = (stringify(_f)), \ + .version_id = (_v), \ + .num = (_n), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type *), \ + .flags = VMS_ARRAY|VMS_STRUCT|VMS_ARRAY_OF_POINTER, \ + .offset = vmstate_offset_array(_s, _f, _type*, _n), \ +} + #define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ .num = (_num), \ From 3589de8c971df29562fcaf2d9b04f0886aff4866 Mon Sep 17 00:00:00 2001 From: liguang Date: Tue, 17 Dec 2013 19:42:37 +0000 Subject: [PATCH 58/62] hw/timer: add allwinner a10 timer Signed-off-by: liguang Reviewed-by: Peter Crosthwaite Message-id: 1387159292-10436-3-git-send-email-lig.fnst@cn.fujitsu.com Signed-off-by: Peter Maydell --- default-configs/arm-softmmu.mak | 2 + hw/timer/Makefile.objs | 2 + hw/timer/allwinner-a10-pit.c | 254 +++++++++++++++++++++++++++ include/hw/timer/allwinner-a10-pit.h | 58 ++++++ 4 files changed, 316 insertions(+) create mode 100644 hw/timer/allwinner-a10-pit.c create mode 100644 include/hw/timer/allwinner-a10-pit.h diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 2135be317b..14c68b490b 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -83,3 +83,5 @@ CONFIG_VERSATILE_I2C=y CONFIG_SDHCI=y CONFIG_INTEGRATOR_DEBUG=y + +CONFIG_ALLWINNER_A10_PIT=y diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index ea9f11f706..2c86c3d412 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -29,3 +29,5 @@ obj-$(CONFIG_TUSB6010) += tusb6010.o obj-$(CONFIG_DIGIC) += digic-timer.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o + +obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c new file mode 100644 index 0000000000..b27fce8cd2 --- /dev/null +++ b/hw/timer/allwinner-a10-pit.c @@ -0,0 +1,254 @@ +/* + * Allwinner A10 timer device emulation + * + * Copyright (C) 2013 Li Guang + * Written by Li Guang + * + * 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. + */ + +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "hw/timer/allwinner-a10-pit.h" + +static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size) +{ + AwA10PITState *s = AW_A10_PIT(opaque); + uint8_t index; + + switch (offset) { + case AW_A10_PIT_TIMER_IRQ_EN: + return s->irq_enable; + case AW_A10_PIT_TIMER_IRQ_ST: + return s->irq_status; + case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: + index = offset & 0xf0; + index >>= 4; + index -= 1; + switch (offset & 0x0f) { + case AW_A10_PIT_TIMER_CONTROL: + return s->control[index]; + case AW_A10_PIT_TIMER_INTERVAL: + return s->interval[index]; + case AW_A10_PIT_TIMER_COUNT: + s->count[index] = ptimer_get_count(s->timer[index]); + return s->count[index]; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + case AW_A10_PIT_WDOG_CONTROL: + break; + case AW_A10_PIT_WDOG_MODE: + break; + case AW_A10_PIT_COUNT_LO: + return s->count_lo; + case AW_A10_PIT_COUNT_HI: + return s->count_hi; + case AW_A10_PIT_COUNT_CTL: + return s->count_ctl; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + + return 0; +} + +static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AwA10PITState *s = AW_A10_PIT(opaque); + uint8_t index; + + switch (offset) { + case AW_A10_PIT_TIMER_IRQ_EN: + s->irq_enable = value; + break; + case AW_A10_PIT_TIMER_IRQ_ST: + s->irq_status &= ~value; + break; + case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: + index = offset & 0xf0; + index >>= 4; + index -= 1; + switch (offset & 0x0f) { + case AW_A10_PIT_TIMER_CONTROL: + s->control[index] = value; + if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) { + ptimer_set_count(s->timer[index], s->interval[index]); + } + if (s->control[index] & AW_A10_PIT_TIMER_EN) { + int oneshot = 0; + if (s->control[index] & AW_A10_PIT_TIMER_MODE) { + oneshot = 1; + } + ptimer_run(s->timer[index], oneshot); + } else { + ptimer_stop(s->timer[index]); + } + break; + case AW_A10_PIT_TIMER_INTERVAL: + s->interval[index] = value; + ptimer_set_limit(s->timer[index], s->interval[index], 1); + break; + case AW_A10_PIT_TIMER_COUNT: + s->count[index] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + } + break; + case AW_A10_PIT_WDOG_CONTROL: + s->watch_dog_control = value; + break; + case AW_A10_PIT_WDOG_MODE: + s->watch_dog_mode = value; + break; + case AW_A10_PIT_COUNT_LO: + s->count_lo = value; + break; + case AW_A10_PIT_COUNT_HI: + s->count_hi = value; + break; + case AW_A10_PIT_COUNT_CTL: + s->count_ctl = value; + if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) { + uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + s->count_lo = tmp_count; + s->count_hi = tmp_count >> 32; + s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN; + } + if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) { + s->count_lo = 0; + s->count_hi = 0; + s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN; + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } +} + +static const MemoryRegionOps a10_pit_ops = { + .read = a10_pit_read, + .write = a10_pit_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_a10_pit = { + .name = "a10.pit", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irq_enable, AwA10PITState), + VMSTATE_UINT32(irq_status, AwA10PITState), + VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR), + VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR), + VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR), + VMSTATE_UINT32(watch_dog_mode, AwA10PITState), + VMSTATE_UINT32(watch_dog_control, AwA10PITState), + VMSTATE_UINT32(count_lo, AwA10PITState), + VMSTATE_UINT32(count_hi, AwA10PITState), + VMSTATE_UINT32(count_ctl, AwA10PITState), + VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR), + VMSTATE_END_OF_LIST() + } +}; + +static void a10_pit_reset(DeviceState *dev) +{ + AwA10PITState *s = AW_A10_PIT(dev); + uint8_t i; + + s->irq_enable = 0; + s->irq_status = 0; + for (i = 0; i < 6; i++) { + s->control[i] = AW_A10_PIT_DEFAULT_CLOCK; + s->interval[i] = 0; + s->count[i] = 0; + ptimer_stop(s->timer[i]); + } + s->watch_dog_mode = 0; + s->watch_dog_control = 0; + s->count_lo = 0; + s->count_hi = 0; + s->count_ctl = 0; +} + +static void a10_pit_timer_cb(void *opaque) +{ + AwA10PITState *s = AW_A10_PIT(opaque); + uint8_t i; + + for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { + if (s->control[i] & AW_A10_PIT_TIMER_EN) { + s->irq_status |= 1 << i; + if (s->control[i] & AW_A10_PIT_TIMER_MODE) { + ptimer_stop(s->timer[i]); + s->control[i] &= ~AW_A10_PIT_TIMER_EN; + } + qemu_irq_pulse(s->irq[i]); + } + } +} + +static void a10_pit_init(Object *obj) +{ + AwA10PITState *s = AW_A10_PIT(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + QEMUBH * bh[AW_A10_PIT_TIMER_NR]; + uint8_t i; + + for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } + memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s, + TYPE_AW_A10_PIT, 0x400); + sysbus_init_mmio(sbd, &s->iomem); + + for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { + bh[i] = qemu_bh_new(a10_pit_timer_cb, s); + s->timer[i] = ptimer_init(bh[i]); + ptimer_set_freq(s->timer[i], 240000); + } +} + +static void a10_pit_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = a10_pit_reset; + dc->desc = "allwinner a10 timer"; + dc->vmsd = &vmstate_a10_pit; +} + +static const TypeInfo a10_pit_info = { + .name = TYPE_AW_A10_PIT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AwA10PITState), + .instance_init = a10_pit_init, + .class_init = a10_pit_class_init, +}; + +static void a10_register_types(void) +{ + type_register_static(&a10_pit_info); +} + +type_init(a10_register_types); diff --git a/include/hw/timer/allwinner-a10-pit.h b/include/hw/timer/allwinner-a10-pit.h new file mode 100644 index 0000000000..15efab8b5f --- /dev/null +++ b/include/hw/timer/allwinner-a10-pit.h @@ -0,0 +1,58 @@ +#ifndef AW_A10_PIT_H +#define AW_A10_PIT_H + +#include "hw/ptimer.h" + +#define TYPE_AW_A10_PIT "allwinner-A10-timer" +#define AW_A10_PIT(obj) OBJECT_CHECK(AwA10PITState, (obj), TYPE_AW_A10_PIT) + +#define AW_A10_PIT_TIMER_NR 6 +#define AW_A10_PIT_TIMER_IRQ 0x1 +#define AW_A10_PIT_WDOG_IRQ 0x100 + +#define AW_A10_PIT_TIMER_IRQ_EN 0 +#define AW_A10_PIT_TIMER_IRQ_ST 0x4 + +#define AW_A10_PIT_TIMER_CONTROL 0x0 +#define AW_A10_PIT_TIMER_EN 0x1 +#define AW_A10_PIT_TIMER_RELOAD 0x2 +#define AW_A10_PIT_TIMER_MODE 0x80 + +#define AW_A10_PIT_TIMER_INTERVAL 0x4 +#define AW_A10_PIT_TIMER_COUNT 0x8 +#define AW_A10_PIT_WDOG_CONTROL 0x90 +#define AW_A10_PIT_WDOG_MODE 0x94 + +#define AW_A10_PIT_COUNT_CTL 0xa0 +#define AW_A10_PIT_COUNT_RL_EN 0x2 +#define AW_A10_PIT_COUNT_CLR_EN 0x1 +#define AW_A10_PIT_COUNT_LO 0xa4 +#define AW_A10_PIT_COUNT_HI 0xa8 + +#define AW_A10_PIT_TIMER_BASE 0x10 +#define AW_A10_PIT_TIMER_BASE_END \ + (AW_A10_PIT_TIMER_BASE * 6 + AW_A10_PIT_TIMER_COUNT) + +#define AW_A10_PIT_DEFAULT_CLOCK 0x4 + +typedef struct AwA10PITState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + qemu_irq irq[AW_A10_PIT_TIMER_NR]; + ptimer_state * timer[AW_A10_PIT_TIMER_NR]; + MemoryRegion iomem; + + uint32_t irq_enable; + uint32_t irq_status; + uint32_t control[AW_A10_PIT_TIMER_NR]; + uint32_t interval[AW_A10_PIT_TIMER_NR]; + uint32_t count[AW_A10_PIT_TIMER_NR]; + uint32_t watch_dog_mode; + uint32_t watch_dog_control; + uint32_t count_lo; + uint32_t count_hi; + uint32_t count_ctl; +} AwA10PITState; + +#endif From c3931ee8b42def4089831b4d79e93c5b05667ff6 Mon Sep 17 00:00:00 2001 From: liguang Date: Tue, 17 Dec 2013 19:42:38 +0000 Subject: [PATCH 59/62] hw/intc: add allwinner A10 interrupt controller Signed-off-by: liguang Reviewed-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: 1387159292-10436-4-git-send-email-lig.fnst@cn.fujitsu.com Signed-off-by: Peter Maydell --- default-configs/arm-softmmu.mak | 1 + hw/intc/Makefile.objs | 1 + hw/intc/allwinner-a10-pic.c | 200 ++++++++++++++++++++++++++++ include/hw/intc/allwinner-a10-pic.h | 40 ++++++ 4 files changed, 242 insertions(+) create mode 100644 hw/intc/allwinner-a10-pic.c create mode 100644 include/hw/intc/allwinner-a10-pic.h diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 14c68b490b..216651e13e 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -85,3 +85,4 @@ CONFIG_SDHCI=y CONFIG_INTEGRATOR_DEBUG=y CONFIG_ALLWINNER_A10_PIT=y +CONFIG_ALLWINNER_A10_PIC=y diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 47ac44264c..60eb936e0d 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o +obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c new file mode 100644 index 0000000000..407d563514 --- /dev/null +++ b/hw/intc/allwinner-a10-pic.c @@ -0,0 +1,200 @@ +/* + * Allwinner A10 interrupt controller device emulation + * + * Copyright (C) 2013 Li Guang + * Written by Li Guang + * + * 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. + */ + +#include "hw/sysbus.h" +#include "hw/devices.h" +#include "sysemu/sysemu.h" +#include "hw/intc/allwinner-a10-pic.h" + +static void aw_a10_pic_update(AwA10PICState *s) +{ + uint8_t i; + int irq = 0, fiq = 0; + + for (i = 0; i < AW_A10_PIC_REG_NUM; i++) { + irq |= s->irq_pending[i] & ~s->mask[i]; + fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i]; + } + + qemu_set_irq(s->parent_irq, !!irq); + qemu_set_irq(s->parent_fiq, !!fiq); +} + +static void aw_a10_pic_set_irq(void *opaque, int irq, int level) +{ + AwA10PICState *s = opaque; + + if (level) { + set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]); + } + aw_a10_pic_update(s); +} + +static uint64_t aw_a10_pic_read(void *opaque, hwaddr offset, unsigned size) +{ + AwA10PICState *s = opaque; + uint8_t index = (offset & 0xc) / 4; + + switch (offset) { + case AW_A10_PIC_VECTOR: + return s->vector; + case AW_A10_PIC_BASE_ADDR: + return s->base_addr; + case AW_A10_PIC_PROTECT: + return s->protect; + case AW_A10_PIC_NMI: + return s->nmi; + case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8: + return s->irq_pending[index]; + case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8: + return s->fiq_pending[index]; + case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8: + return s->select[index]; + case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8: + return s->enable[index]; + case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8: + return s->mask[index]; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + + return 0; +} + +static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AwA10PICState *s = opaque; + uint8_t index = (offset & 0xc) / 4; + + switch (offset) { + case AW_A10_PIC_VECTOR: + s->vector = value & ~0x3; + break; + case AW_A10_PIC_BASE_ADDR: + s->base_addr = value & ~0x3; + case AW_A10_PIC_PROTECT: + s->protect = value; + break; + case AW_A10_PIC_NMI: + s->nmi = value; + break; + case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8: + s->irq_pending[index] &= ~value; + break; + case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8: + s->fiq_pending[index] &= ~value; + break; + case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8: + s->select[index] = value; + break; + case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8: + s->enable[index] = value; + break; + case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8: + s->mask[index] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + + aw_a10_pic_update(s); +} + +static const MemoryRegionOps aw_a10_pic_ops = { + .read = aw_a10_pic_read, + .write = aw_a10_pic_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_aw_a10_pic = { + .name = "a10.pic", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(vector, AwA10PICState), + VMSTATE_UINT32(base_addr, AwA10PICState), + VMSTATE_UINT32(protect, AwA10PICState), + VMSTATE_UINT32(nmi, AwA10PICState), + VMSTATE_UINT32_ARRAY(irq_pending, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_UINT32_ARRAY(fiq_pending, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_UINT32_ARRAY(enable, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_UINT32_ARRAY(select, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_UINT32_ARRAY(mask, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void aw_a10_pic_init(Object *obj) +{ + AwA10PICState *s = AW_A10_PIC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + + qdev_init_gpio_in(DEVICE(dev), aw_a10_pic_set_irq, AW_A10_PIC_INT_NR); + sysbus_init_irq(dev, &s->parent_irq); + sysbus_init_irq(dev, &s->parent_fiq); + memory_region_init_io(&s->iomem, OBJECT(s), &aw_a10_pic_ops, s, + TYPE_AW_A10_PIC, 0x400); + sysbus_init_mmio(dev, &s->iomem); +} + +static void aw_a10_pic_reset(DeviceState *d) +{ + AwA10PICState *s = AW_A10_PIC(d); + uint8_t i; + + s->base_addr = 0; + s->protect = 0; + s->nmi = 0; + s->vector = 0; + for (i = 0; i < AW_A10_PIC_REG_NUM; i++) { + s->irq_pending[i] = 0; + s->fiq_pending[i] = 0; + s->select[i] = 0; + s->enable[i] = 0; + s->mask[i] = 0; + } +} + +static void aw_a10_pic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = aw_a10_pic_reset; + dc->desc = "allwinner a10 pic"; + dc->vmsd = &vmstate_aw_a10_pic; + } + +static const TypeInfo aw_a10_pic_info = { + .name = TYPE_AW_A10_PIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AwA10PICState), + .instance_init = aw_a10_pic_init, + .class_init = aw_a10_pic_class_init, +}; + +static void aw_a10_register_types(void) +{ + type_register_static(&aw_a10_pic_info); +} + +type_init(aw_a10_register_types); diff --git a/include/hw/intc/allwinner-a10-pic.h b/include/hw/intc/allwinner-a10-pic.h new file mode 100644 index 0000000000..5721b2e6b6 --- /dev/null +++ b/include/hw/intc/allwinner-a10-pic.h @@ -0,0 +1,40 @@ +#ifndef AW_A10_PIC_H +#define AW_A10_PIC_H + +#define TYPE_AW_A10_PIC "allwinner-a10-pic" +#define AW_A10_PIC(obj) OBJECT_CHECK(AwA10PICState, (obj), TYPE_AW_A10_PIC) + +#define AW_A10_PIC_VECTOR 0 +#define AW_A10_PIC_BASE_ADDR 4 +#define AW_A10_PIC_PROTECT 8 +#define AW_A10_PIC_NMI 0xc +#define AW_A10_PIC_IRQ_PENDING 0x10 +#define AW_A10_PIC_FIQ_PENDING 0x20 +#define AW_A10_PIC_SELECT 0x30 +#define AW_A10_PIC_ENABLE 0x40 +#define AW_A10_PIC_MASK 0x50 + +#define AW_A10_PIC_INT_NR 95 +#define AW_A10_PIC_REG_NUM DIV_ROUND_UP(AW_A10_PIC_INT_NR, 32) + +typedef struct AwA10PICState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + MemoryRegion iomem; + qemu_irq parent_fiq; + qemu_irq parent_irq; + + uint32_t vector; + uint32_t base_addr; + uint32_t protect; + uint32_t nmi; + uint32_t irq_pending[AW_A10_PIC_REG_NUM]; + uint32_t fiq_pending[AW_A10_PIC_REG_NUM]; + uint32_t select[AW_A10_PIC_REG_NUM]; + uint32_t enable[AW_A10_PIC_REG_NUM]; + uint32_t mask[AW_A10_PIC_REG_NUM]; + /*priority setting here*/ +} AwA10PICState; + +#endif From 9158fa5451b5929f1d882ef08c30b4f4aadd6945 Mon Sep 17 00:00:00 2001 From: liguang Date: Tue, 17 Dec 2013 19:42:38 +0000 Subject: [PATCH 60/62] hw/arm: add allwinner a10 SoC support Signed-off-by: liguang Reviewed-by: Peter Crosthwaite Message-id: 1387159292-10436-5-git-send-email-lig.fnst@cn.fujitsu.com Signed-off-by: Peter Maydell --- default-configs/arm-softmmu.mak | 1 + hw/arm/Makefile.objs | 1 + hw/arm/allwinner-a10.c | 103 ++++++++++++++++++++++++++++++++ include/hw/arm/allwinner-a10.h | 35 +++++++++++ 4 files changed, 140 insertions(+) create mode 100644 hw/arm/allwinner-a10.c create mode 100644 include/hw/arm/allwinner-a10.h diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 216651e13e..ce1d620842 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -86,3 +86,4 @@ CONFIG_INTEGRATOR_DEBUG=y CONFIG_ALLWINNER_A10_PIT=y CONFIG_ALLWINNER_A10_PIC=y +CONFIG_ALLWINNER_A10=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index d7d37f241d..fab30a7247 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -7,3 +7,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o obj-y += omap1.o omap2.o strongarm.o +obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c new file mode 100644 index 0000000000..4658e19504 --- /dev/null +++ b/hw/arm/allwinner-a10.c @@ -0,0 +1,103 @@ +/* + * Allwinner A10 SoC emulation + * + * Copyright (C) 2013 Li Guang + * Written by Li Guang + * + * 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. + */ + +#include "hw/sysbus.h" +#include "hw/devices.h" +#include "hw/arm/allwinner-a10.h" + +static void aw_a10_init(Object *obj) +{ + AwA10State *s = AW_A10(obj); + + object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU); + object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); + + object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC); + qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default()); + + object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); + qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); +} + +static void aw_a10_realize(DeviceState *dev, Error **errp) +{ + AwA10State *s = AW_A10(dev); + SysBusDevice *sysbusdev; + uint8_t i; + qemu_irq fiq, irq; + Error *err = NULL; + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ); + fiq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ); + + object_property_set_bool(OBJECT(&s->intc), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbusdev = SYS_BUS_DEVICE(&s->intc); + sysbus_mmio_map(sysbusdev, 0, AW_A10_PIC_REG_BASE); + sysbus_connect_irq(sysbusdev, 0, irq); + sysbus_connect_irq(sysbusdev, 1, fiq); + for (i = 0; i < AW_A10_PIC_INT_NR; i++) { + s->irq[i] = qdev_get_gpio_in(DEVICE(&s->intc), i); + } + + object_property_set_bool(OBJECT(&s->timer), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbusdev = SYS_BUS_DEVICE(&s->timer); + sysbus_mmio_map(sysbusdev, 0, AW_A10_PIT_REG_BASE); + sysbus_connect_irq(sysbusdev, 0, s->irq[22]); + sysbus_connect_irq(sysbusdev, 1, s->irq[23]); + sysbus_connect_irq(sysbusdev, 2, s->irq[24]); + sysbus_connect_irq(sysbusdev, 3, s->irq[25]); + sysbus_connect_irq(sysbusdev, 4, s->irq[67]); + sysbus_connect_irq(sysbusdev, 5, s->irq[68]); + + serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], + 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); +} + +static void aw_a10_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aw_a10_realize; +} + +static const TypeInfo aw_a10_type_info = { + .name = TYPE_AW_A10, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AwA10State), + .instance_init = aw_a10_init, + .class_init = aw_a10_class_init, +}; + +static void aw_a10_register_types(void) +{ + type_register_static(&aw_a10_type_info); +} + +type_init(aw_a10_register_types) diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h new file mode 100644 index 0000000000..da36647f32 --- /dev/null +++ b/include/hw/arm/allwinner-a10.h @@ -0,0 +1,35 @@ +#ifndef ALLWINNER_H_ + +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "hw/char/serial.h" +#include "hw/arm/arm.h" +#include "hw/timer/allwinner-a10-pit.h" +#include "hw/intc/allwinner-a10-pic.h" + +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" + + +#define AW_A10_PIC_REG_BASE 0x01c20400 +#define AW_A10_PIT_REG_BASE 0x01c20c00 +#define AW_A10_UART0_REG_BASE 0x01c28000 + +#define AW_A10_SDRAM_BASE 0x40000000 + +#define TYPE_AW_A10 "allwinner-a10" +#define AW_A10(obj) OBJECT_CHECK(AwA10State, (obj), TYPE_AW_A10) + +typedef struct AwA10State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + ARMCPU cpu; + qemu_irq irq[AW_A10_PIC_INT_NR]; + AwA10PITState timer; + AwA10PICState intc; +} AwA10State; + +#define ALLWINNER_H_ +#endif From a01c005327007562f3313f3efe235f77309db809 Mon Sep 17 00:00:00 2001 From: liguang Date: Tue, 17 Dec 2013 19:42:38 +0000 Subject: [PATCH 61/62] hw/arm: add cubieboard support Signed-off-by: liguang Reviewed-by: Peter Crosthwaite Message-id: 1387159292-10436-6-git-send-email-lig.fnst@cn.fujitsu.com Signed-off-by: Peter Maydell --- hw/arm/Makefile.objs | 2 +- hw/arm/cubieboard.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ tests/qom-test.c | 1 + 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 hw/arm/cubieboard.c diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index fab30a7247..6088e53653 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -7,4 +7,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o obj-y += omap1.o omap2.o strongarm.o -obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o +obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c new file mode 100644 index 0000000000..3fcb6d22f5 --- /dev/null +++ b/hw/arm/cubieboard.c @@ -0,0 +1,69 @@ +/* + * cubieboard emulation + * + * Copyright (C) 2013 Li Guang + * Written by Li Guang + * + * 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. + */ + +#include "hw/sysbus.h" +#include "hw/devices.h" +#include "hw/boards.h" +#include "hw/arm/allwinner-a10.h" + +static struct arm_boot_info cubieboard_binfo = { + .loader_start = AW_A10_SDRAM_BASE, + .board_id = 0x1008, +}; + +typedef struct CubieBoardState { + AwA10State *a10; + MemoryRegion sdram; +} CubieBoardState; + +static void cubieboard_init(QEMUMachineInitArgs *args) +{ + CubieBoardState *s = g_new(CubieBoardState, 1); + Error *err = NULL; + + s->a10 = AW_A10(object_new(TYPE_AW_A10)); + object_property_set_bool(OBJECT(s->a10), true, "realized", &err); + if (err != NULL) { + error_report("Couldn't realize Allwinner A10: %s\n", + error_get_pretty(err)); + exit(1); + } + + memory_region_init_ram(&s->sdram, NULL, "cubieboard.ram", args->ram_size); + vmstate_register_ram_global(&s->sdram); + memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, + &s->sdram); + + cubieboard_binfo.ram_size = args->ram_size; + cubieboard_binfo.kernel_filename = args->kernel_filename; + cubieboard_binfo.kernel_cmdline = args->kernel_cmdline; + arm_load_kernel(&s->a10->cpu, &cubieboard_binfo); +} + +static QEMUMachine cubieboard_machine = { + .name = "cubieboard", + .desc = "cubietech cubieboard", + .init = cubieboard_init, +}; + + +static void cubieboard_machine_init(void) +{ + qemu_register_machine(&cubieboard_machine); +} + +machine_init(cubieboard_machine_init) diff --git a/tests/qom-test.c b/tests/qom-test.c index f67f7c2f09..5e5af7a50f 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -71,6 +71,7 @@ static const char *arm_machines[] = { "highbank", "midway", "canon-a1100", + "cubieboard", }; static const char *cris_machines[] = { From 84291fe7a34f8c2d595bcdb77ff506d1d60fcd7c Mon Sep 17 00:00:00 2001 From: liguang Date: Tue, 17 Dec 2013 19:42:38 +0000 Subject: [PATCH 62/62] MAINTAINERS: add myself to maintain allwinner-a10 Signed-off-by: liguang Message-id: 1387159292-10436-7-git-send-email-lig.fnst@cn.fujitsu.com Signed-off-by: Peter Maydell --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1fce9a5749..a5ab8f8cea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -219,6 +219,13 @@ F: *win32* ARM Machines ------------ +Allwinner-a10 +M: Li Guang +S: Maintained +F: hw/*/allwinner-a10* +F: include/hw/*/allwinner-a10* +F: hw/arm/cubieboard.c + Exynos M: Evgeny Voevodin M: Maksim Kozlov