summaryrefslogtreecommitdiffstats
path: root/target/arm/internals.h
Commit message (Collapse)AuthorAgeFilesLines
* target/arm: Introduce core_to_aa64_mmu_idxRichard Henderson2020-03-051-0/+6
| | | | | | | | | | | If by context we know that we're in AArch64 mode, we need not test for M-profile when reconstructing the full ARMMMUIdx. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20200302175829.2183-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Move DBGDIDR into ARMISARegistersPeter Maydell2020-02-211-3/+3
| | | | | | | | | | We're going to want to read the DBGDIDR register from KVM in a subsequent commit, which means it needs to be in the ARMISARegisters sub-struct. Move it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200214175116.9164-12-peter.maydell@linaro.org
* target/arm: Stop assuming DBGDIDR always existsPeter Maydell2020-02-211-0/+42
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The AArch32 DBGDIDR defines properties like the number of breakpoints, watchpoints and context-matching comparators. On an AArch64 CPU, the register may not even exist if AArch32 is not supported at EL1. Currently we hard-code use of DBGDIDR to identify the number of breakpoints etc; this works for all our TCG CPUs, but will break if we ever add an AArch64-only CPU. We also have an assert() that the AArch32 and AArch64 registers match, which currently works only by luck for KVM because we don't populate either of these ID registers from the KVM vCPU and so they are both zero. Clean this up so we have functions for finding the number of breakpoints, watchpoints and context comparators which look in the appropriate ID register. This allows us to drop the "check that AArch64 and AArch32 agree on the number of breakpoints etc" asserts: * we no longer look at the AArch32 versions unless that's the right place to be looking * it's valid to have a CPU (eg AArch64-only) where they don't match * we shouldn't have been asserting the validity of ID registers in a codepath used with KVM anyway Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200214175116.9164-11-peter.maydell@linaro.org
* target/arm: Add _aa32_ to isar_feature functions testing 32-bit ID registersPeter Maydell2020-02-211-1/+1
| | | | | | | | | | | | | | | | | | Enforce a convention that an isar_feature function that tests a 32-bit ID register always has _aa32_ in its name, and one that tests a 64-bit ID register always has _aa64_ in its name. We already follow this except for three cases: thumb_div, arm_div and jazelle, which all need _aa32_ adding. (As noted in the comment, isar_feature_aa32_fp16_arith() is an exception in that it currently tests ID_AA64PFR0_EL1, but will switch to MVFR1 once we've properly implemented FP16 for AArch32.) Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20200214175116.9164-2-peter.maydell@linaro.org
* target/arm: Split out aa64_va_parameter_tbi, aa64_va_parameter_tbidRichard Henderson2020-02-211-3/+0Star
| | | | | | | | | | | | | | | | | | For the purpose of rebuild_hflags_a64, we do not need to compute all of the va parameters, only tbi. Moreover, we can compute them in a form that is more useful to storing in hflags. This eliminates the need for aa64_va_parameter_both, so fold that in to aa64_va_parameter. The remaining calls to aa64_va_parameter are in get_phys_addr_lpae and in pauth_helper.c. This reduces the total cpu consumption of aa64_va_parameter in a kernel boot plus a kvm guest kernel boot from 3% to 0.5%. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200216194343.21331-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Update MSR access to UAORichard Henderson2020-02-131-0/+3
| | | | | | | Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200208125816.14954-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Enforce PAN semantics in get_S1protRichard Henderson2020-02-131-0/+13
| | | | | | | | | | If we have a PAN-enforcing mmu_idx, set prot == 0 if user_rw != 0. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200208125816.14954-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Update MSR access for PANRichard Henderson2020-02-131-0/+6
| | | | | | | | | | | For aarch64, there's a dedicated msr (imm, reg) insn. For aarch32, this is done via msr to cpsr. Writes from el0 are ignored, which is already handled by the CPSR_USER mask. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200208125816.14954-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Introduce aarch64_pstate_valid_maskRichard Henderson2020-02-131-0/+12
| | | | | | | | | | Use this along the exception return path, where we previously accepted any values. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200208125816.14954-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Mask CPSR_J when Jazelle is not enabledRichard Henderson2020-02-131-1/+4
| | | | | | | | | | The J bit signals Jazelle mode, and so of course is RES0 when the feature is not enabled. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20200208125816.14954-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Split out aarch32_cpsr_valid_maskRichard Henderson2020-02-131-0/+21
| | | | | | | | | | | | | Split this helper out of msr_mask in translate.c. At the same time, transform the negative reductive logic to positive accumulative logic. It will be usable along the exception paths. While touching msr_mask, fix up formatting. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20200208125816.14954-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Add mmu_idx for EL1 and EL2 w/ PAN enabledRichard Henderson2020-02-131-0/+9
| | | | | | | | | | | | | | | | To implement PAN, we will want to swap, for short periods of time, to a different privileged mmu_idx. In addition, we cannot do this with flushing alone, because the AT* instructions have both PAN and PAN-less versions. Add the ARMMMUIdx*_PAN constants where necessary next to the corresponding ARMMMUIdx* constant. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200208125816.14954-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Add arm_mmu_idx_is_stage1_of_2Richard Henderson2020-02-131-0/+18
| | | | | | | | | | Use a common predicate for querying stage1-ness. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200208125816.14954-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Add regime_has_2_rangesRichard Henderson2020-02-071-0/+18
| | | | | | | | | | | Create a predicate to indicate whether the regime has both positive and negative addresses. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Reorganize ARMMMUIdxRichard Henderson2020-02-071-0/+35
| | | | | | | | | | | | Prepare for, but do not yet implement, the EL2&0 regime. This involves adding the new MMUIdx enumerators and adjusting some of the MMUIdx related predicates to match. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Rename ARMMMUIdx_S1E2 to ARMMMUIdx_E2Richard Henderson2020-02-071-1/+1
| | | | | | | | | | | | This is part of a reorganization to the set of mmu_idx. The non-secure EL2 regime only has a single stage translation; there is no point in pointing out that the idx is for stage1. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Rename ARMMMUIdx*_S1E3 to ARMMMUIdx*_SE3Richard Henderson2020-02-071-1/+1
| | | | | | | | | | | | This is part of a reorganization to the set of mmu_idx. The EL3 regime only has a single stage translation, and is always secure. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Rename ARMMMUIdx_S1SE[01] to ARMMMUIdx_SE10_[01]Richard Henderson2020-02-071-2/+2
| | | | | | | | | | | This is part of a reorganization to the set of mmu_idx. This emphasizes that they apply to the Secure EL1&0 regime. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Rename ARMMMUIdx_S1NSE* to ARMMMUIdx_Stage1_E*Richard Henderson2020-02-071-3/+3
| | | | | | | | | | | | This is part of a reorganization to the set of mmu_idx. The EL1&0 regime is the only one that uses 2-stage translation. Spelling out Stage avoids confusion with Secure. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Rename ARMMMUIdx_S2NS to ARMMMUIdx_Stage2Richard Henderson2020-02-071-1/+1
| | | | | | | | | | The EL1&0 regime is the only one that uses 2-stage translation. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Rename ARMMMUIdx*_S12NSE* to ARMMMUIdx*_E10_*Richard Henderson2020-02-071-2/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This is part of a reorganization to the set of mmu_idx. This emphasizes that they apply to the EL1&0 regime. The ultimate goal is -- Non-secure regimes: ARMMMUIdx_E10_0, ARMMMUIdx_E20_0, ARMMMUIdx_E10_1, ARMMMUIdx_E2, ARMMMUIdx_E20_2, -- Secure regimes: ARMMMUIdx_SE10_0, ARMMMUIdx_SE10_1, ARMMMUIdx_SE3, -- Helper mmu_idx for non-secure EL1&0 stage1 and stage2 ARMMMUIdx_Stage2, ARMMMUIdx_Stage1_E0, ARMMMUIdx_Stage1_E1, The 'S' prefix is reserved for "Secure". Unless otherwise specified, each mmu_idx represents all stages of translation. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Split out arm_mmu_idx_elRichard Henderson2019-10-241-0/+9
| | | | | | | | | | Avoid calling arm_current_el() twice. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20191023150057.25731-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Declare some M-profile functions publiclyPhilippe Mathieu-Daudé2019-07-011-0/+42
| | | | | | | | | | | In the next commit we will split the M-profile functions from this file. Some function will be called out of helper.c. Declare them in the "internals.h" header. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-id: 20190701132516.26392-22-philmd@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Declare arm_log_exception() function publiclyPhilippe Mathieu-Daudé2019-07-011-0/+2
| | | | | | | | | | | | | In few commits we will split the M-profile functions from this file, and this function will also be called in the new file. Declare it in the "internals.h" header. Since it is in the middle of a block of M profile functions, move it previous to this block to ease the later refactor. Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-id: 20190701132516.26392-21-philmd@redhat.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Restrict PSCI to TCGPhilippe Mathieu-Daudé2019-07-011-1/+5
| | | | | | | | | Under KVM, the kernel gets the HVC call and handle the PSCI requests. Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-id: 20190701132516.26392-20-philmd@redhat.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Move TLB related routines to tlb_helper.cPhilippe Mathieu-Daudé2019-07-011-3/+0Star
| | | | | | | | | | | | These routines are TCG specific. The arm_deliver_fault() function is only used within the new helper. Make it static. Suggested-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-id: 20190701132516.26392-13-philmd@redhat.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Declare get_phys_addr() function publiclyPhilippe Mathieu-Daudé2019-07-011-0/+16
| | | | | | | | | | | In the next commit we will split the TLB related routines of this file, and this function will also be called in the new file. Declare it in the "internals.h" header. Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-id: 20190701132516.26392-12-philmd@redhat.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Convert to CPUClass::tlb_fillRichard Henderson2019-05-101-4/+6
| | | | | | Cc: qemu-arm@nongnu.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
* target/arm: Split helper_msr_i_pstate into 3Richard Henderson2019-03-051-0/+15
| | | | | | | | | | | | The EL0+UMA check is unique to DAIF. While SPSel had avoided the check by nature of already checking EL >= 1, the other post v8.0 extensions to MSR (imm) allow EL0 and do not require UMA. Avoid the unconditional write to pc and use raise_exception_ra to unwind. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20190301200501.16533-5-richard.henderson@linaro.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Compute TB_FLAGS for TBI for user-onlyPeter Maydell2019-02-051-21/+0Star
| | | | | | | | | | | Enables, but does not turn on, TBI for CONFIG_USER_ONLY. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20190204132126.3255-4-richard.henderson@linaro.org [PMM: adjusted #ifdeffery to placate clang, which otherwise complains about static functions that are unused in the CONFIG_USER_ONLY build] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Default handling of BTYPE during translationRichard Henderson2019-02-051-0/+6
| | | | | | | | | | | | | | | | | The branch target exception for guarded pages has high priority, and only 8 instructions are valid for that case. Perform this check before doing any other decode. Clear BTYPE after all insns that neither set BTYPE nor exit via exception (DISAS_NORETURN). Not yet handled are insns that exit via DISAS_NORETURN for some other reason, like direct branches. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20190128223118.5255-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Decode TBID from TCRRichard Henderson2019-01-211-0/+1
| | | | | | | | | | Use TBID in aa64_va_parameters depending on the data parameter. This automatically updates all existing users of the function. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20190108223129.5570-23-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Add aa64_va_parameters_bothRichard Henderson2019-01-211-3/+12
| | | | | | | | | We will want to check TBI for I and D simultaneously. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20190108223129.5570-22-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Export aa64_va_parameters to internals.hRichard Henderson2019-01-211-0/+17
| | | | | | | | | | | We need to reuse this from helper-a64.c. Provide a stub definition for CONFIG_USER_ONLY. This matches the stub definitions that we removed for arm_regime_tbi{0,1} before. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20190108223129.5570-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Create ARMVAParameters and helpersRichard Henderson2019-01-211-0/+14
| | | | | | | | | | | | Split out functions to extract the virtual address parameters. Let the functions choose T0 or T1 address space half, if present. Extract (most of) the control bits that vary between EL or Tx. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20190108223129.5570-19-richard.henderson@linaro.org [PMM: fixed minor checkpatch comment nits] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Introduce arm_stage1_mmu_idxRichard Henderson2019-01-211-0/+15
| | | | | | | | | | While we could expose stage_1_mmu_idx, the combination is probably going to be more useful. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20190108223129.5570-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Introduce arm_mmu_idxRichard Henderson2019-01-211-0/+8
| | | | | | | | | | | | | | | | The pattern ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false)); is computing the full ARMMMUIdx, stripping off the ARM bits, and then putting them back. Avoid the extra two steps with the appropriate helper function. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20190108223129.5570-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Add PAuth helpersRichard Henderson2019-01-211-0/+6
| | | | | | | | | | The cryptographic internals are stubbed out for now, but the enable and trap bits are checked. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20190108223129.5570-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Introduce raise_exception_raRichard Henderson2019-01-211-0/+7
| | | | | | | | | | This path uses cpu_loop_exit_restore to unwind current processor state. Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20190108223129.5570-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Move id_aa64mmfr* to ARMISARegistersPeter Maydell2018-12-131-1/+2
| | | | | | | | | | At the same time, define the fields for these registers, and use those defines in arm_pamax(). Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181203203839.757-2-richard.henderson@linaro.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: fixed up typo (s/achf/ahcf/) belatedly spotted by RTH] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
* target/arm: Hyp mode R14 is shared with User and SystemPeter Maydell2018-11-131-0/+16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Hyp mode is an exception to the general rule that each AArch32 mode has its own r13, r14 and SPSR -- it has a banked r13 and SPSR but shares its r14 with User and System mode. We were incorrectly implementing it as banked, which meant that on entry to Hyp mode r14 was 0 rather than the USR/SYS r14. We provide a new function r14_bank_number() which is like the existing bank_number() but provides the index into env->banked_r14[]; bank_number() provides the index to use for env->banked_r13[] and env->banked_cpsr[]. All the points in the code that were using bank_number() to index into env->banked_r14[] are updated for consintency: * switch_mode() -- this is the only place where we fix an actual bug * aarch64_sync_32_to_64() and aarch64_sync_64_to_32(): no behavioural change as we already special-cased Hyp R14 * kvm32.c: no behavioural change since the guest can't ever be in Hyp mode, but conceptually the right thing to do * msr_banked()/mrs_banked(): we can never get to the case that accesses banked_r14[] with tgtmode == ARM_CPU_MODE_HYP, so no behavioural change Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20181109173553.22341-2-peter.maydell@linaro.org
* target/arm: Correctly implement handling of HCR_EL2.{VI, VF}Peter Maydell2018-11-131-0/+18
| | | | | | | | | | | | | | | | | | In commit 8a0fc3a29fc2315325400 we tried to implement HCR_EL2.{VI,VF}, but we got it wrong and had to revert it. In that commit we implemented them as simply tracking whether there is a pending virtual IRQ or virtual FIQ. This is not correct -- these bits cause a software-generated VIRQ/VFIQ, which is distinct from whether there is a hardware-generated VIRQ/VFIQ caused by the external interrupt controller. So we need to track separately the HCR_EL2 bit state and the external virq/vfiq line state, and OR the two together to get the actual pending VIRQ/VFIQ state. Fixes: 8a0fc3a29fc2315325400c738f807d0d4ae0ab7f Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-id: 20181109134731.11605-4-peter.maydell@linaro.org
* target/arm: Report correct syndrome for FP/SIMD traps to Hyp modePeter Maydell2018-10-241-1/+13
| | | | | | | | | | | | | For traps of FP/SIMD instructions to AArch32 Hyp mode, the syndrome provided in HSR has more information than is reported to AArch64. Specifically, there are extra fields TA and coproc which indicate whether the trapped instruction was FP or SIMD. Add this extra information to the syndromes we construct, and mask it out when taking the exception to AArch64. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181012144235.19646-11-peter.maydell@linaro.org
* target/arm: Get IL bit correct for v7 syndrome valuesPeter Maydell2018-10-241-5/+2Star
| | | | | | | | | | | | | | | | | | | | | For the v7 version of the Arm architecture, the IL bit in syndrome register values where the field is not valid was defined to be UNK/SBZP. In v8 this is RES1, which is what QEMU currently implements. Handle the desired v7 behaviour by squashing the IL bit for the affected cases: * EC == EC_UNCATEGORIZED * prefetch aborts * data aborts where ISV is 0 (The fourth case listed in the v8 Arm ARM DDI 0487C.a in section G7.2.70, "illegal state exception", can't happen on a v7 CPU.) This deals with a corner case noted in a comment. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181012144235.19646-10-peter.maydell@linaro.org
* target/arm: New utility function to extract EC from syndromePeter Maydell2018-10-241-0/+5
| | | | | | | | | Create and use a utility function to extract the EC field from a syndrome, rather than open-coding the shift. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181012144235.19646-9-peter.maydell@linaro.org
* target/arm: Make switch_mode() file-localPeter Maydell2018-10-241-1/+0Star
| | | | | | | | | | The switch_mode() function is defined in target/arm/helper.c and used only in that file and nowhere else, so we can make it file-local rather than global. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181012144235.19646-3-peter.maydell@linaro.org
* target/arm: Improve debug logging of AArch32 exception returnPeter Maydell2018-10-241-0/+18
| | | | | | | | | | | | | | | | | | | | | | For AArch32, exception return happens through certain kinds of CPSR write. We don't currently have any CPU_LOG_INT logging of these events (unlike AArch64, where we log in the ERET instruction). Add some suitable logging. This will log exception returns like this: Exception return from AArch32 hyp to usr PC 0x80100374 paralleling the existing logging in the exception_return helper for AArch64 exception returns: Exception return from AArch64 EL2 to AArch64 EL0 PC 0x8003045c Exception return from AArch64 EL2 to AArch32 EL0 PC 0x8003045c (Note that an AArch32 exception return can only be AArch32->AArch32, never to AArch64.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181012144235.19646-2-peter.maydell@linaro.org
* target/arm: Add v8M stack limit checks on NS function callsPeter Maydell2018-10-081-0/+9
| | | | | | | | | | | | | | Check the v8M stack limits when pushing the frame for a non-secure function call via BLXNS. In order to be able to generate the exception we need to promote raise_exception() from being local to op_helper.c so we can call it from helper.c. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181002163556.10279-8-peter.maydell@linaro.org
* target/arm: Add v8M stack checks on ADD/SUB/MOV of SPPeter Maydell2018-10-081-0/+14
| | | | | | | | | | | | | | | Add code to insert calls to a helper function to do the stack limit checking when we handle these forms of instruction that write to SP: * ADD (SP plus immediate) * ADD (SP plus register) * SUB (SP minus immediate) * SUB (SP minus register) * MOV (register) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181002163556.10279-5-peter.maydell@linaro.org
* target/arm: Move v7m_using_psp() to internals.hPeter Maydell2018-10-081-0/+16
| | | | | | | | | | We're going to want v7m_using_psp() in op_helper.c in the next patch, so move it from helper.c to internals.h. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20181002163556.10279-4-peter.maydell@linaro.org
hine support (ARM).' href='/bwlp/qemu.git/commit/hw/onenand.c?id=7e7c5e4c1ba5c9b7efcf1b0c1e34ea150c286e58'>7e7c5e4c1b ^
bc24a225af ^
8da3ff1809 ^
7e7c5e4c1b ^

fcf5787c02 ^
8da3ff1809 ^
7e7c5e4c1b ^

5923ba424b ^
7e7c5e4c1b ^
5923ba424b ^
7e7c5e4c1b ^
5923ba424b ^

7e7c5e4c1b ^






































9e6e9247a4 ^

7e7c5e4c1b ^


9e6e9247a4 ^

7e7c5e4c1b ^


a8170e5e97 ^
689a1921ae ^
7e7c5e4c1b ^
bc24a225af ^
8da3ff1809 ^
7e7c5e4c1b ^




























5923ba424b ^

7e7c5e4c1b ^



9e6e9247a4 ^


7e7c5e4c1b ^


















82866965e9 ^
7e7c5e4c1b ^




























9e6e9247a4 ^


7e7c5e4c1b ^


689a1921ae ^



7e7c5e4c1b ^

887c74ca19 ^
7e7c5e4c1b ^
887c74ca19 ^
af073cd9de ^
500954e35c ^
7e7c5e4c1b ^
a17c17a274 ^
af073cd9de ^
a8170e5e97 ^
b9d38e9510 ^
7e7c5e4c1b ^

7267c0947d ^
500954e35c ^

2d256e6f65 ^
689a1921ae ^
4be746345f ^
7267c0947d ^
500954e35c ^

4be746345f ^
887c74ca19 ^

a3efecb847 ^
a17c17a274 ^


887c74ca19 ^

a17c17a274 ^
4be746345f ^
63efb1d9c4 ^
7267c0947d ^
7e7c5e4c1b ^
1cfe48c1ce ^
f8ed85ac99 ^
c5705a7728 ^
689a1921ae ^
7e7c5e4c1b ^





689a1921ae ^
af073cd9de ^

3cad405bab ^
500954e35c ^




500954e35c ^
7e7c5e4c1b ^
999e12bbe8 ^




4be746345f ^
999e12bbe8 ^




39bffca203 ^
999e12bbe8 ^
887c74ca19 ^
39bffca203 ^
4f67d30b5e ^
999e12bbe8 ^

8c43a6f05d ^
af073cd9de ^
39bffca203 ^


500954e35c ^
7e7c5e4c1b ^
83f7d43a9e ^
500954e35c ^
39bffca203 ^
7e7c5e4c1b ^
c580d92b0e ^
500954e35c ^
c580d92b0e ^
af073cd9de ^


c580d92b0e ^
500954e35c ^
83f7d43a9e ^
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870















                                                                  
                                                                          
                                                                 

   
                       
                       
                  
                           
                   
                               
                                 
                        
                      
                              
                              
                     
                        






                                                                          





                                                                      




                     
              
                

                  

                          


                     

                            
                              

                        

                           












                        
                 





                     
               





























                                   




                                                                  

                                                           
                                                             
                                                                             







                                                           
                                                
 
                                                                             

 
                                         
 







                                        

             

 
                                                          
 














                                   

 



                                                   



















                                                               

                                                           



                             
                                                                  
                                                    















                                          
                        






                                                        

                                                                 

                                                                    


     

                                                  


                                    

 
                                                                       

                           

                                                  
                     

                                                                   
                                          
                 
     





                                                     
                                                                       

                          


                   



                                                      
                                                 
                        
                         
                                
                                                                     





                                           
                                                    






                                        
                             
                                                                         

             
                               
                       

         
 
                  

 
                                                                        



                           
                     

                                                                         
                     
         
                                                         
                                          
                 
            
                                                                              
     
 


             
                                                                        

                          

                   
                                                 
                                  

                                                                          
                         
                               
                   
                                                                             















                                                                   
                             

                                                             

             
                   

                  

 
                                                                  
 
                               
 
                             
                           

                                   
                         
                                                    

                                                                         

                          

                                                                           


                                                                 

                                                                            









                                                                

     

                     
             

     

                     
             

 
                                            


















                                                                   
                         






























































































                                                                           



                                                  






                                                             


























































                                                                              
                          








                                             

                                                                      




                           
                                                       
                                           
 
                                              
                                  

                     
                           
                                            

                                             
                         
                                       
                         
                                        

                                                           






































                                                                               

                                                                       


                      

                                                                             


             
                                                    
                                                        
 
                                              
                                  




























                                                                      

                                                         



                                                           


                                                                       


















                                                     
                           




























                                                                          


                                                                 


     



                                            

  
                                                           
 
                                            
                                    
                                                       
              
                            
 
                         
                  

                                    
                                     

                                                  
                                                                           
                                               
                  
                                                       

                                                    
                                       

                                                            
         


                                                                       

                                             
         
                            
     
                                                     
                                                  
                                                                       
                                                             
                                         
                                             





                                                                           
                         

                                         
                                     




                                                 
 
 




                                                                   
                                                  




                                                              
                                          
 
                                  
                                     
                                                   

 
                                      
                                   


                                          
  
 
                                        
 
                                        
 
 
                                                  
 


                                               
 
 
                                 
/*
 * OneNAND flash memories emulation.
 *
 * Copyright (C) 2008 Nokia Corporation
 * Written by Andrzej Zaborowski <andrew@openedhand.com>
 *
 * 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 or
 * (at your option) version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "sysemu/block-backend.h"
#include "exec/memory.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/module.h"

/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */
#define PAGE_SHIFT	11

/* Fixed */
#define BLOCK_SHIFT	(PAGE_SHIFT + 6)

#define TYPE_ONE_NAND "onenand"
#define ONE_NAND(obj) OBJECT_CHECK(OneNANDState, (obj), TYPE_ONE_NAND)

typedef struct OneNANDState {
    SysBusDevice parent_obj;

    struct {
        uint16_t man;
        uint16_t dev;
        uint16_t ver;
    } id;
    int shift;
    hwaddr base;
    qemu_irq intr;
    qemu_irq rdy;
    BlockBackend *blk;
    BlockBackend *blk_cur;
    uint8_t *image;
    uint8_t *otp;
    uint8_t *current;
    MemoryRegion ram;
    MemoryRegion mapped_ram;
    uint8_t current_direction;
    uint8_t *boot[2];
    uint8_t *data[2][2];
    MemoryRegion iomem;
    MemoryRegion container;
    int cycle;
    int otpmode;

    uint16_t addr[8];
    uint16_t unladdr[8];
    int bufaddr;
    int count;
    uint16_t command;
    uint16_t config[2];
    uint16_t status;
    uint16_t intstatus;
    uint16_t wpstatus;

    ECCState ecc;

    int density_mask;
    int secs;
    int secs_cur;
    int blocks;
    uint8_t *blockwp;
} OneNANDState;

enum {
    ONEN_BUF_BLOCK = 0,
    ONEN_BUF_BLOCK2 = 1,
    ONEN_BUF_DEST_BLOCK = 2,
    ONEN_BUF_DEST_PAGE = 3,
    ONEN_BUF_PAGE = 7,
};

enum {
    ONEN_ERR_CMD = 1 << 10,
    ONEN_ERR_ERASE = 1 << 11,
    ONEN_ERR_PROG = 1 << 12,
    ONEN_ERR_LOAD = 1 << 13,
};

enum {
    ONEN_INT_RESET = 1 << 4,
    ONEN_INT_ERASE = 1 << 5,
    ONEN_INT_PROG = 1 << 6,
    ONEN_INT_LOAD = 1 << 7,
    ONEN_INT = 1 << 15,
};

enum {
    ONEN_LOCK_LOCKTIGHTEN = 1 << 0,
    ONEN_LOCK_LOCKED = 1 << 1,
    ONEN_LOCK_UNLOCKED = 1 << 2,
};

static void onenand_mem_setup(OneNANDState *s)
{
    /* XXX: We should use IO_MEM_ROMD but we broke it earlier...
     * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to
     * write boot commands.  Also take note of the BWPS bit.  */
    memory_region_init(&s->container, OBJECT(s), "onenand",
                       0x10000 << s->shift);
    memory_region_add_subregion(&s->container, 0, &s->iomem);
    memory_region_init_alias(&s->mapped_ram, OBJECT(s), "onenand-mapped-ram",
                             &s->ram, 0x0200 << s->shift,
                             0xbe00 << s->shift);
    memory_region_add_subregion_overlap(&s->container,
                                        0x0200 << s->shift,
                                        &s->mapped_ram,
                                        1);
}

static void onenand_intr_update(OneNANDState *s)
{
    qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1);
}

static int onenand_pre_save(void *opaque)
{
    OneNANDState *s = opaque;
    if (s->current == s->otp) {
        s->current_direction = 1;
    } else if (s->current == s->image) {
        s->current_direction = 2;
    } else {
        s->current_direction = 0;
    }

    return 0;
}

static int onenand_post_load(void *opaque, int version_id)
{
    OneNANDState *s = opaque;
    switch (s->current_direction) {
    case 0:
        break;
    case 1:
        s->current = s->otp;
        break;
    case 2:
        s->current = s->image;
        break;
    default:
        return -1;
    }
    onenand_intr_update(s);
    return 0;
}

static const VMStateDescription vmstate_onenand = {
    .name = "onenand",
    .version_id = 1,
    .minimum_version_id = 1,
    .pre_save = onenand_pre_save,
    .post_load = onenand_post_load,
    .fields = (VMStateField[]) {
        VMSTATE_UINT8(current_direction, OneNANDState),
        VMSTATE_INT32(cycle, OneNANDState),
        VMSTATE_INT32(otpmode, OneNANDState),
        VMSTATE_UINT16_ARRAY(addr, OneNANDState, 8),
        VMSTATE_UINT16_ARRAY(unladdr, OneNANDState, 8),
        VMSTATE_INT32(bufaddr, OneNANDState),
        VMSTATE_INT32(count, OneNANDState),
        VMSTATE_UINT16(command, OneNANDState),
        VMSTATE_UINT16_ARRAY(config, OneNANDState, 2),
        VMSTATE_UINT16(status, OneNANDState),
        VMSTATE_UINT16(intstatus, OneNANDState),
        VMSTATE_UINT16(wpstatus, OneNANDState),
        VMSTATE_INT32(secs_cur, OneNANDState),
        VMSTATE_PARTIAL_VBUFFER(blockwp, OneNANDState, blocks),
        VMSTATE_UINT8(ecc.cp, OneNANDState),
        VMSTATE_UINT16_ARRAY(ecc.lp, OneNANDState, 2),
        VMSTATE_UINT16(ecc.count, OneNANDState),
        VMSTATE_BUFFER_POINTER_UNSAFE(otp, OneNANDState, 0,
            ((64 + 2) << PAGE_SHIFT)),
        VMSTATE_END_OF_LIST()
    }
};

/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */
static void onenand_reset(OneNANDState *s, int cold)
{
    memset(&s->addr, 0, sizeof(s->addr));
    s->command = 0;
    s->count = 1;
    s->bufaddr = 0;
    s->config[0] = 0x40c0;
    s->config[1] = 0x0000;
    onenand_intr_update(s);
    qemu_irq_raise(s->rdy);
    s->status = 0x0000;
    s->intstatus = cold ? 0x8080 : 0x8010;
    s->unladdr[0] = 0;
    s->unladdr[1] = 0;
    s->wpstatus = 0x0002;
    s->cycle = 0;
    s->otpmode = 0;
    s->blk_cur = s->blk;
    s->current = s->image;
    s->secs_cur = s->secs;

    if (cold) {
        /* Lock the whole flash */
        memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks);

        if (s->blk_cur && blk_pread(s->blk_cur, 0, s->boot[0],
                                    8 << BDRV_SECTOR_BITS) < 0) {
            hw_error("%s: Loading the BootRAM failed.\n", __func__);
        }
    }
}

static void onenand_system_reset(DeviceState *dev)
{
    OneNANDState *s = ONE_NAND(dev);

    onenand_reset(s, 1);
}

static inline int onenand_load_main(OneNANDState *s, int sec, int secn,
                void *dest)
{
    assert(UINT32_MAX >> BDRV_SECTOR_BITS > sec);
    assert(UINT32_MAX >> BDRV_SECTOR_BITS > secn);
    if (s->blk_cur) {
        return blk_pread(s->blk_cur, sec << BDRV_SECTOR_BITS, dest,
                         secn << BDRV_SECTOR_BITS) < 0;
    } else if (sec + secn > s->secs_cur) {
        return 1;
    }

    memcpy(dest, s->current + (sec << 9), secn << 9);

    return 0;
}

static inline int onenand_prog_main(OneNANDState *s, int sec, int secn,
                void *src)
{
    int result = 0;

    if (secn > 0) {
        uint32_t size = secn << BDRV_SECTOR_BITS;
        uint32_t offset = sec << BDRV_SECTOR_BITS;
        assert(UINT32_MAX >> BDRV_SECTOR_BITS > sec);
        assert(UINT32_MAX >> BDRV_SECTOR_BITS > secn);
        const uint8_t *sp = (const uint8_t *)src;
        uint8_t *dp = 0;
        if (s->blk_cur) {
            dp = g_malloc(size);
            if (!dp || blk_pread(s->blk_cur, offset, dp, size) < 0) {
                result = 1;
            }
        } else {
            if (sec + secn > s->secs_cur) {
                result = 1;
            } else {
                dp = (uint8_t *)s->current + offset;
            }
        }
        if (!result) {
            uint32_t i;
            for (i = 0; i < size; i++) {
                dp[i] &= sp[i];
            }
            if (s->blk_cur) {
                result = blk_pwrite(s->blk_cur, offset, dp, size, 0) < 0;
            }
        }
        if (dp && s->blk_cur) {
            g_free(dp);
        }
    }

    return result;
}

static inline int onenand_load_spare(OneNANDState *s, int sec, int secn,
                void *dest)
{
    uint8_t buf[512];

    if (s->blk_cur) {
        uint32_t offset = (s->secs_cur + (sec >> 5)) << BDRV_SECTOR_BITS;
        if (blk_pread(s->blk_cur, offset, buf, BDRV_SECTOR_SIZE) < 0) {
            return 1;
        }
        memcpy(dest, buf + ((sec & 31) << 4), secn << 4);
    } else if (sec + secn > s->secs_cur) {
        return 1;
    } else {
        memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4);
    }

    return 0;
}

static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn,
                void *src)
{
    int result = 0;
    if (secn > 0) {
        const uint8_t *sp = (const uint8_t *)src;
        uint8_t *dp = 0, *dpp = 0;
        uint32_t offset = (s->secs_cur + (sec >> 5)) << BDRV_SECTOR_BITS;
        assert(UINT32_MAX >> BDRV_SECTOR_BITS > s->secs_cur + (sec >> 5));
        if (s->blk_cur) {
            dp = g_malloc(512);
            if (!dp
                || blk_pread(s->blk_cur, offset, dp, BDRV_SECTOR_SIZE) < 0) {
                result = 1;
            } else {
                dpp = dp + ((sec & 31) << 4);
            }
        } else {
            if (sec + secn > s->secs_cur) {
                result = 1;
            } else {
                dpp = s->current + (s->secs_cur << 9) + (sec << 4);
            }
        }
        if (!result) {
            uint32_t i;
            for (i = 0; i < (secn << 4); i++) {
                dpp[i] &= sp[i];
            }
            if (s->blk_cur) {
                result = blk_pwrite(s->blk_cur, offset, dp,
                                    BDRV_SECTOR_SIZE, 0) < 0;
            }
        }
        g_free(dp);
    }
    return result;
}

static inline int onenand_erase(OneNANDState *s, int sec, int num)
{
    uint8_t *blankbuf, *tmpbuf;

    blankbuf = g_malloc(512);
    tmpbuf = g_malloc(512);
    memset(blankbuf, 0xff, 512);
    for (; num > 0; num--, sec++) {
        if (s->blk_cur) {
            int erasesec = s->secs_cur + (sec >> 5);
            if (blk_pwrite(s->blk_cur, sec << BDRV_SECTOR_BITS, blankbuf,
                           BDRV_SECTOR_SIZE, 0) < 0) {
                goto fail;
            }
            if (blk_pread(s->blk_cur, erasesec << BDRV_SECTOR_BITS, tmpbuf,
                          BDRV_SECTOR_SIZE) < 0) {
                goto fail;
            }
            memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4);
            if (blk_pwrite(s->blk_cur, erasesec << BDRV_SECTOR_BITS, tmpbuf,
                           BDRV_SECTOR_SIZE, 0) < 0) {
                goto fail;
            }
        } else {
            if (sec + 1 > s->secs_cur) {
                goto fail;
            }
            memcpy(s->current + (sec << 9), blankbuf, 512);
            memcpy(s->current + (s->secs_cur << 9) + (sec << 4),
                   blankbuf, 1 << 4);
        }
    }

    g_free(tmpbuf);
    g_free(blankbuf);
    return 0;

fail:
    g_free(tmpbuf);
    g_free(blankbuf);
    return 1;
}

static void onenand_command(OneNANDState *s)
{
    int b;
    int sec;
    void *buf;
#define SETADDR(block, page)			\
    sec = (s->addr[page] & 3) +			\
            ((((s->addr[page] >> 2) & 0x3f) +	\
              (((s->addr[block] & 0xfff) |	\
                (s->addr[block] >> 15 ?		\
                 s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9));
#define SETBUF_M()				\
    buf = (s->bufaddr & 8) ?			\
            s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0];	\
    buf += (s->bufaddr & 3) << 9;
#define SETBUF_S()				\
    buf = (s->bufaddr & 8) ?			\
            s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1];	\
    buf += (s->bufaddr & 3) << 4;

    switch (s->command) {
    case 0x00:	/* Load single/multiple sector data unit into buffer */
        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)

        SETBUF_M()
        if (onenand_load_main(s, sec, s->count, buf))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;

#if 0
        SETBUF_S()
        if (onenand_load_spare(s, sec, s->count, buf))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
#endif

        /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
         * or    if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
         * then we need two split the read/write into two chunks.
         */
        s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
        break;
    case 0x13:	/* Load single/multiple spare sector into buffer */
        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)

        SETBUF_S()
        if (onenand_load_spare(s, sec, s->count, buf))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;

        /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
         * or    if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
         * then we need two split the read/write into two chunks.
         */
        s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
        break;
    case 0x80:	/* Program single/multiple sector data unit from buffer */
        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)

        SETBUF_M()
        if (onenand_prog_main(s, sec, s->count, buf))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;

#if 0
        SETBUF_S()
        if (onenand_prog_spare(s, sec, s->count, buf))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
#endif

        /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
         * or    if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
         * then we need two split the read/write into two chunks.
         */
        s->intstatus |= ONEN_INT | ONEN_INT_PROG;
        break;
    case 0x1a:	/* Program single/multiple spare area sector from buffer */
        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)

        SETBUF_S()
        if (onenand_prog_spare(s, sec, s->count, buf))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;

        /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
         * or    if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
         * then we need two split the read/write into two chunks.
         */
        s->intstatus |= ONEN_INT | ONEN_INT_PROG;
        break;
    case 0x1b:	/* Copy-back program */
        SETBUF_S()

        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
        if (onenand_load_main(s, sec, s->count, buf))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;

        SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE)
        if (onenand_prog_main(s, sec, s->count, buf))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;

        /* TODO: spare areas */

        s->intstatus |= ONEN_INT | ONEN_INT_PROG;
        break;

    case 0x23:	/* Unlock NAND array block(s) */
        s->intstatus |= ONEN_INT;

        /* XXX the previous (?) area should be locked automatically */
        for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
            if (b >= s->blocks) {
                s->status |= ONEN_ERR_CMD;
                break;
            }
            if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
                break;

            s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED;
        }
        break;
    case 0x27:	/* Unlock All NAND array blocks */
        s->intstatus |= ONEN_INT;

        for (b = 0; b < s->blocks; b ++) {
            if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
                break;

            s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED;
        }
        break;

    case 0x2a:	/* Lock NAND array block(s) */
        s->intstatus |= ONEN_INT;

        for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
            if (b >= s->blocks) {
                s->status |= ONEN_ERR_CMD;
                break;
            }
            if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
                break;

            s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED;
        }
        break;
    case 0x2c:	/* Lock-tight NAND array block(s) */
        s->intstatus |= ONEN_INT;

        for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
            if (b >= s->blocks) {
                s->status |= ONEN_ERR_CMD;
                break;
            }
            if (s->blockwp[b] == ONEN_LOCK_UNLOCKED)
                continue;

            s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN;
        }
        break;

    case 0x71:	/* Erase-Verify-Read */
        s->intstatus |= ONEN_INT;
        break;
    case 0x95:	/* Multi-block erase */
        qemu_irq_pulse(s->intr);
        /* Fall through.  */
    case 0x94:	/* Block erase */
        sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) |
                        (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0))
                << (BLOCK_SHIFT - 9);
        if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9)))
            s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE;

        s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
        break;
    case 0xb0:	/* Erase suspend */
        break;
    case 0x30:	/* Erase resume */
        s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
        break;

    case 0xf0:	/* Reset NAND Flash core */
        onenand_reset(s, 0);
        break;
    case 0xf3:	/* Reset OneNAND */
        onenand_reset(s, 0);
        break;

    case 0x65:	/* OTP Access */
        s->intstatus |= ONEN_INT;
        s->blk_cur = NULL;
        s->current = s->otp;
        s->secs_cur = 1 << (BLOCK_SHIFT - 9);
        s->addr[ONEN_BUF_BLOCK] = 0;
        s->otpmode = 1;
        break;

    default:
        s->status |= ONEN_ERR_CMD;
        s->intstatus |= ONEN_INT;
        qemu_log_mask(LOG_GUEST_ERROR, "unknown OneNAND command %x\n",
                      s->command);
    }

    onenand_intr_update(s);
}

static uint64_t onenand_read(void *opaque, hwaddr addr,
                             unsigned size)
{
    OneNANDState *s = (OneNANDState *) opaque;
    int offset = addr >> s->shift;

    switch (offset) {
    case 0x0000 ... 0xbffe:
        return lduw_le_p(s->boot[0] + addr);

    case 0xf000:	/* Manufacturer ID */
        return s->id.man;
    case 0xf001:	/* Device ID */
        return s->id.dev;
    case 0xf002:	/* Version ID */
        return s->id.ver;
    /* TODO: get the following values from a real chip!  */
    case 0xf003:	/* Data Buffer size */
        return 1 << PAGE_SHIFT;
    case 0xf004:	/* Boot Buffer size */
        return 0x200;
    case 0xf005:	/* Amount of buffers */
        return 1 | (2 << 8);
    case 0xf006:	/* Technology */
        return 0;

    case 0xf100 ... 0xf107:	/* Start addresses */
        return s->addr[offset - 0xf100];

    case 0xf200:	/* Start buffer */
        return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10)));

    case 0xf220:	/* Command */
        return s->command;
    case 0xf221:	/* System Configuration 1 */
        return s->config[0] & 0xffe0;
    case 0xf222:	/* System Configuration 2 */
        return s->config[1];

    case 0xf240:	/* Controller Status */
        return s->status;
    case 0xf241:	/* Interrupt */
        return s->intstatus;
    case 0xf24c:	/* Unlock Start Block Address */
        return s->unladdr[0];
    case 0xf24d:	/* Unlock End Block Address */
        return s->unladdr[1];
    case 0xf24e:	/* Write Protection Status */
        return s->wpstatus;

    case 0xff00:	/* ECC Status */
        return 0x00;
    case 0xff01:	/* ECC Result of main area data */
    case 0xff02:	/* ECC Result of spare area data */
    case 0xff03:	/* ECC Result of main area data */
    case 0xff04:	/* ECC Result of spare area data */
        qemu_log_mask(LOG_UNIMP,
                      "onenand: ECC result registers unimplemented\n");
        return 0x0000;
    }

    qemu_log_mask(LOG_GUEST_ERROR, "read of unknown OneNAND register 0x%x\n",
                  offset);
    return 0;
}

static void onenand_write(void *opaque, hwaddr addr,
                          uint64_t value, unsigned size)
{
    OneNANDState *s = (OneNANDState *) opaque;
    int offset = addr >> s->shift;
    int sec;

    switch (offset) {
    case 0x0000 ... 0x01ff:
    case 0x8000 ... 0x800f:
        if (s->cycle) {
            s->cycle = 0;

            if (value == 0x0000) {
                SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
                onenand_load_main(s, sec,
                                1 << (PAGE_SHIFT - 9), s->data[0][0]);
                s->addr[ONEN_BUF_PAGE] += 4;
                s->addr[ONEN_BUF_PAGE] &= 0xff;
            }
            break;
        }

        switch (value) {
        case 0x00f0:	/* Reset OneNAND */
            onenand_reset(s, 0);
            break;

        case 0x00e0:	/* Load Data into Buffer */
            s->cycle = 1;
            break;

        case 0x0090:	/* Read Identification Data */
            memset(s->boot[0], 0, 3 << s->shift);
            s->boot[0][0 << s->shift] = s->id.man & 0xff;
            s->boot[0][1 << s->shift] = s->id.dev & 0xff;
            s->boot[0][2 << s->shift] = s->wpstatus & 0xff;
            break;

        default:
            qemu_log_mask(LOG_GUEST_ERROR,
                          "unknown OneNAND boot command %" PRIx64 "\n",
                          value);
        }
        break;

    case 0xf100 ... 0xf107:	/* Start addresses */
        s->addr[offset - 0xf100] = value;
        break;

    case 0xf200:	/* Start buffer */
        s->bufaddr = (value >> 8) & 0xf;
        if (PAGE_SHIFT == 11)
            s->count = (value & 3) ?: 4;
        else if (PAGE_SHIFT == 10)
            s->count = (value & 1) ?: 2;
        break;

    case 0xf220:	/* Command */
        if (s->intstatus & (1 << 15))
            break;
        s->command = value;
        onenand_command(s);
        break;
    case 0xf221:	/* System Configuration 1 */
        s->config[0] = value;
        onenand_intr_update(s);
        qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1);
        break;
    case 0xf222:	/* System Configuration 2 */
        s->config[1] = value;
        break;

    case 0xf241:	/* Interrupt */
        s->intstatus &= value;
        if ((1 << 15) & ~s->intstatus)
            s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE |
                            ONEN_ERR_PROG | ONEN_ERR_LOAD);
        onenand_intr_update(s);
        break;
    case 0xf24c:	/* Unlock Start Block Address */
        s->unladdr[0] = value & (s->blocks - 1);
        /* For some reason we have to set the end address to by default
         * be same as start because the software forgets to write anything
         * in there.  */
        s->unladdr[1] = value & (s->blocks - 1);
        break;
    case 0xf24d:	/* Unlock End Block Address */
        s->unladdr[1] = value & (s->blocks - 1);
        break;

    default:
        qemu_log_mask(LOG_GUEST_ERROR,
                      "write to unknown OneNAND register 0x%x\n",
                      offset);
    }
}

static const MemoryRegionOps onenand_ops = {
    .read = onenand_read,
    .write = onenand_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
};

static void onenand_realize(DeviceState *dev, Error **errp)
{
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    OneNANDState *s = ONE_NAND(dev);
    uint32_t size = 1 << (24 + ((s->id.dev >> 4) & 7));
    void *ram;
    Error *local_err = NULL;

    s->base = (hwaddr)-1;
    s->rdy = NULL;
    s->blocks = size >> BLOCK_SHIFT;
    s->secs = size >> 9;
    s->blockwp = g_malloc(s->blocks);
    s->density_mask = (s->id.dev & 0x08)
        ? (1 << (6 + ((s->id.dev >> 4) & 7))) : 0;
    memory_region_init_io(&s->iomem, OBJECT(s), &onenand_ops, s, "onenand",
                          0x10000 << s->shift);
    if (!s->blk) {
        s->image = memset(g_malloc(size + (size >> 5)),
                          0xff, size + (size >> 5));
    } else {
        if (blk_is_read_only(s->blk)) {
            error_setg(errp, "Can't use a read-only drive");
            return;
        }
        blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
                     BLK_PERM_ALL, &local_err);
        if (local_err) {
            error_propagate(errp, local_err);
            return;
        }
        s->blk_cur = s->blk;
    }
    s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT),
                    0xff, (64 + 2) << PAGE_SHIFT);
    memory_region_init_ram_nomigrate(&s->ram, OBJECT(s), "onenand.ram",
                           0xc000 << s->shift, &error_fatal);
    vmstate_register_ram_global(&s->ram);
    ram = memory_region_get_ram_ptr(&s->ram);
    s->boot[0] = ram + (0x0000 << s->shift);
    s->boot[1] = ram + (0x8000 << s->shift);
    s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift);
    s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift);
    s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift);
    s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift);
    onenand_mem_setup(s);
    sysbus_init_irq(sbd, &s->intr);
    sysbus_init_mmio(sbd, &s->container);
    vmstate_register(VMSTATE_IF(dev),
                     ((s->shift & 0x7f) << 24)
                     | ((s->id.man & 0xff) << 16)
                     | ((s->id.dev & 0xff) << 8)
                     | (s->id.ver & 0xff),
                     &vmstate_onenand, s);
}

static Property onenand_properties[] = {
    DEFINE_PROP_UINT16("manufacturer_id", OneNANDState, id.man, 0),
    DEFINE_PROP_UINT16("device_id", OneNANDState, id.dev, 0),
    DEFINE_PROP_UINT16("version_id", OneNANDState, id.ver, 0),
    DEFINE_PROP_INT32("shift", OneNANDState, shift, 0),
    DEFINE_PROP_DRIVE("drive", OneNANDState, blk),
    DEFINE_PROP_END_OF_LIST(),
};

static void onenand_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->realize = onenand_realize;
    dc->reset = onenand_system_reset;
    device_class_set_props(dc, onenand_properties);
}

static const TypeInfo onenand_info = {
    .name          = TYPE_ONE_NAND,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(OneNANDState),
    .class_init    = onenand_class_init,
};

static void onenand_register_types(void)
{
    type_register_static(&onenand_info);
}

void *onenand_raw_otp(DeviceState *onenand_device)
{
    OneNANDState *s = ONE_NAND(onenand_device);

    return s->otp;
}

type_init(onenand_register_types)