Commit bbbd67f0 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/afaerber/tags/qom-cpu-for-2.0' into staging



QOM CPUState refactorings / X86CPU

* Deadlock fix for exit requests around CPU reset
* X86CPU x2apic for KVM
* X86CPU model subclasses
* SPARCCPU preparations for model subclasses
* -cpu arguments for arm, cris, lm32, moxie, openrisc, ppc, sh4, uc32
* m68k assertion cleanups
* CPUClass hooks for cpu.h inline functions
* Field movements from CPU_COMMON to CPUState and follow-up cleanups

# gpg: Signature made Thu 13 Mar 2014 19:06:56 GMT using RSA key ID 3E7E013F
# gpg: Good signature from "Andreas Färber <afaerber@suse.de>"
# gpg:                 aka "Andreas Färber <afaerber@suse.com>"

* remotes/afaerber/tags/qom-cpu-for-2.0: (58 commits)
  user-exec: Change exception_action() argument to CPUState
  cputlb: Change tlb_set_page() argument to CPUState
  cputlb: Change tlb_flush() argument to CPUState
  cputlb: Change tlb_flush_page() argument to CPUState
  target-microblaze: Replace DisasContext::env field with MicroBlazeCPU
  target-cris: Replace DisasContext::env field with CRISCPU
  exec: Change cpu_abort() argument to CPUState
  exec: Change memory_region_section_get_iotlb() argument to CPUState
  cputlb: Change tlb_unprotect_code_phys() argument to CPUState
  cpu-exec: Change cpu_resume_from_signal() argument to CPUState
  exec: Change cpu_breakpoint_{insert,remove{,_by_ref,_all}} argument
  exec: Change cpu_watchpoint_{insert,remove{,_by_ref,_all}} argument
  target-ppc: Use PowerPCCPU in PowerPCCPUClass::handle_mmu_fault hook
  translate-all: Change tb_flush_jmp_cache() argument to CPUState
  translate-all: Change tb_gen_code() argument to CPUState
  translate-all: Change cpu_io_recompile() argument to CPUState
  translate-all: Change tb_check_watchpoint() argument to CPUState
  translate-all: Change cpu_restore_state_from_tb() argument to CPUState
  translate-all: Change cpu_restore_state() argument to CPUState
  cpu-exec: Change cpu_loop_exit() argument to CPUState
  ...

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents d7f0a59f 2ef1f68d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1000,7 +1000,7 @@ int main(int argc, char **argv)
    memset(ts, 0, sizeof(TaskState));
    init_task_state(ts);
    ts->info = info;
    env->opaque = ts;
    cpu->opaque = ts;

#if defined(TARGET_I386)
    cpu_x86_set_cpl(env, 3);
+51 −55
Original line number Diff line number Diff line
@@ -23,29 +23,22 @@
#include "qemu/atomic.h"
#include "sysemu/qtest.h"

bool qemu_cpu_has_work(CPUState *cpu)
void cpu_loop_exit(CPUState *cpu)
{
    return cpu_has_work(cpu);
}

void cpu_loop_exit(CPUArchState *env)
{
    CPUState *cpu = ENV_GET_CPU(env);

    cpu->current_tb = NULL;
    siglongjmp(env->jmp_env, 1);
    siglongjmp(cpu->jmp_env, 1);
}

/* exit the current TB from a signal handler. The host registers are
   restored in a state compatible with the CPU emulator
 */
#if defined(CONFIG_SOFTMMU)
void cpu_resume_from_signal(CPUArchState *env, void *puc)
void cpu_resume_from_signal(CPUState *cpu, void *puc)
{
    /* XXX: restore cpu registers saved in host registers */

    env->exception_index = -1;
    siglongjmp(env->jmp_env, 1);
    cpu->exception_index = -1;
    siglongjmp(cpu->jmp_env, 1);
}
#endif

@@ -108,7 +101,7 @@ static void cpu_exec_nocache(CPUArchState *env, int max_cycles,
    if (max_cycles > CF_COUNT_MASK)
        max_cycles = CF_COUNT_MASK;

    tb = tb_gen_code(env, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
    tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
                     max_cycles);
    cpu->current_tb = tb;
    /* execute the generated code */
@@ -123,6 +116,7 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
                                      target_ulong cs_base,
                                      uint64_t flags)
{
    CPUState *cpu = ENV_GET_CPU(env);
    TranslationBlock *tb, **ptb1;
    unsigned int h;
    tb_page_addr_t phys_pc, phys_page1;
@@ -160,7 +154,7 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
    }
 not_found:
   /* if no translated code available, then translate it now */
    tb = tb_gen_code(env, pc, cs_base, flags, 0);
    tb = tb_gen_code(cpu, pc, cs_base, flags, 0);

 found:
    /* Move the last found TB to the head of the list */
@@ -170,12 +164,13 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
        tcg_ctx.tb_ctx.tb_phys_hash[h] = tb;
    }
    /* we add the TB in the virtual pc hash table */
    env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
    cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
    return tb;
}

static inline TranslationBlock *tb_find_fast(CPUArchState *env)
{
    CPUState *cpu = ENV_GET_CPU(env);
    TranslationBlock *tb;
    target_ulong cs_base, pc;
    int flags;
@@ -184,7 +179,7 @@ static inline TranslationBlock *tb_find_fast(CPUArchState *env)
       always be the same before a given translated block
       is executed. */
    cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
    tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
    tb = cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
    if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base ||
                 tb->flags != flags)) {
        tb = tb_find_slow(env, pc, cs_base, flags);
@@ -201,10 +196,11 @@ void cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)

static void cpu_handle_debug_exception(CPUArchState *env)
{
    CPUState *cpu = ENV_GET_CPU(env);
    CPUWatchpoint *wp;

    if (!env->watchpoint_hit) {
        QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
    if (!cpu->watchpoint_hit) {
        QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
            wp->flags &= ~BP_WATCHPOINT_HIT;
        }
    }
@@ -283,16 +279,16 @@ int cpu_exec(CPUArchState *env)
#else
#error unsupported target CPU
#endif
    env->exception_index = -1;
    cpu->exception_index = -1;

    /* prepare setjmp context for exception handling */
    for(;;) {
        if (sigsetjmp(env->jmp_env, 0) == 0) {
        if (sigsetjmp(cpu->jmp_env, 0) == 0) {
            /* if an exception is pending, we execute it here */
            if (env->exception_index >= 0) {
                if (env->exception_index >= EXCP_INTERRUPT) {
            if (cpu->exception_index >= 0) {
                if (cpu->exception_index >= EXCP_INTERRUPT) {
                    /* exit request from the cpu execution loop */
                    ret = env->exception_index;
                    ret = cpu->exception_index;
                    if (ret == EXCP_DEBUG) {
                        cpu_handle_debug_exception(env);
                    }
@@ -305,11 +301,11 @@ int cpu_exec(CPUArchState *env)
#if defined(TARGET_I386)
                    cc->do_interrupt(cpu);
#endif
                    ret = env->exception_index;
                    ret = cpu->exception_index;
                    break;
#else
                    cc->do_interrupt(cpu);
                    env->exception_index = -1;
                    cpu->exception_index = -1;
#endif
                }
            }
@@ -324,8 +320,8 @@ int cpu_exec(CPUArchState *env)
                    }
                    if (interrupt_request & CPU_INTERRUPT_DEBUG) {
                        cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
                        env->exception_index = EXCP_DEBUG;
                        cpu_loop_exit(env);
                        cpu->exception_index = EXCP_DEBUG;
                        cpu_loop_exit(cpu);
                    }
#if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \
    defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \
@@ -333,8 +329,8 @@ int cpu_exec(CPUArchState *env)
                    if (interrupt_request & CPU_INTERRUPT_HALT) {
                        cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
                        cpu->halted = 1;
                        env->exception_index = EXCP_HLT;
                        cpu_loop_exit(env);
                        cpu->exception_index = EXCP_HLT;
                        cpu_loop_exit(cpu);
                    }
#endif
#if defined(TARGET_I386)
@@ -348,8 +344,8 @@ int cpu_exec(CPUArchState *env)
                            cpu_svm_check_intercept_param(env, SVM_EXIT_INIT,
                                                          0);
                            do_cpu_init(x86_cpu);
                            env->exception_index = EXCP_HALTED;
                            cpu_loop_exit(env);
                            cpu->exception_index = EXCP_HALTED;
                            cpu_loop_exit(cpu);
                    } else if (interrupt_request & CPU_INTERRUPT_SIPI) {
                            do_cpu_sipi(x86_cpu);
                    } else if (env->hflags2 & HF2_GIF_MASK) {
@@ -420,7 +416,7 @@ int cpu_exec(CPUArchState *env)
#elif defined(TARGET_LM32)
                    if ((interrupt_request & CPU_INTERRUPT_HARD)
                        && (env->ie & IE_IE)) {
                        env->exception_index = EXCP_IRQ;
                        cpu->exception_index = EXCP_IRQ;
                        cc->do_interrupt(cpu);
                        next_tb = 0;
                    }
@@ -429,7 +425,7 @@ int cpu_exec(CPUArchState *env)
                        && (env->sregs[SR_MSR] & MSR_IE)
                        && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))
                        && !(env->iflags & (D_FLAG | IMM_FLAG))) {
                        env->exception_index = EXCP_IRQ;
                        cpu->exception_index = EXCP_IRQ;
                        cc->do_interrupt(cpu);
                        next_tb = 0;
                    }
@@ -437,7 +433,7 @@ int cpu_exec(CPUArchState *env)
                    if ((interrupt_request & CPU_INTERRUPT_HARD) &&
                        cpu_mips_hw_interrupts_pending(env)) {
                        /* Raise it */
                        env->exception_index = EXCP_EXT_INTERRUPT;
                        cpu->exception_index = EXCP_EXT_INTERRUPT;
                        env->error_code = 0;
                        cc->do_interrupt(cpu);
                        next_tb = 0;
@@ -454,7 +450,7 @@ int cpu_exec(CPUArchState *env)
                            idx = EXCP_TICK;
                        }
                        if (idx >= 0) {
                            env->exception_index = idx;
                            cpu->exception_index = idx;
                            cc->do_interrupt(cpu);
                            next_tb = 0;
                        }
@@ -469,7 +465,7 @@ int cpu_exec(CPUArchState *env)
                            if (((type == TT_EXTINT) &&
                                  cpu_pil_allowed(env, pil)) ||
                                  type != TT_EXTINT) {
                                env->exception_index = env->interrupt_index;
                                cpu->exception_index = env->interrupt_index;
                                cc->do_interrupt(cpu);
                                next_tb = 0;
                            }
@@ -478,7 +474,7 @@ int cpu_exec(CPUArchState *env)
#elif defined(TARGET_ARM)
                    if (interrupt_request & CPU_INTERRUPT_FIQ
                        && !(env->daif & PSTATE_F)) {
                        env->exception_index = EXCP_FIQ;
                        cpu->exception_index = EXCP_FIQ;
                        cc->do_interrupt(cpu);
                        next_tb = 0;
                    }
@@ -494,14 +490,14 @@ int cpu_exec(CPUArchState *env)
                    if (interrupt_request & CPU_INTERRUPT_HARD
                        && ((IS_M(env) && env->regs[15] < 0xfffffff0)
                            || !(env->daif & PSTATE_I))) {
                        env->exception_index = EXCP_IRQ;
                        cpu->exception_index = EXCP_IRQ;
                        cc->do_interrupt(cpu);
                        next_tb = 0;
                    }
#elif defined(TARGET_UNICORE32)
                    if (interrupt_request & CPU_INTERRUPT_HARD
                        && !(env->uncached_asr & ASR_I)) {
                        env->exception_index = UC32_EXCP_INTR;
                        cpu->exception_index = UC32_EXCP_INTR;
                        cc->do_interrupt(cpu);
                        next_tb = 0;
                    }
@@ -536,7 +532,7 @@ int cpu_exec(CPUArchState *env)
                            }
                        }
                        if (idx >= 0) {
                            env->exception_index = idx;
                            cpu->exception_index = idx;
                            env->error_code = 0;
                            cc->do_interrupt(cpu);
                            next_tb = 0;
@@ -546,7 +542,7 @@ int cpu_exec(CPUArchState *env)
                    if (interrupt_request & CPU_INTERRUPT_HARD
                        && (env->pregs[PR_CCS] & I_FLAG)
                        && !env->locked_irq) {
                        env->exception_index = EXCP_IRQ;
                        cpu->exception_index = EXCP_IRQ;
                        cc->do_interrupt(cpu);
                        next_tb = 0;
                    }
@@ -558,7 +554,7 @@ int cpu_exec(CPUArchState *env)
                            m_flag_archval = M_FLAG_V32;
                        }
                        if ((env->pregs[PR_CCS] & m_flag_archval)) {
                            env->exception_index = EXCP_NMI;
                            cpu->exception_index = EXCP_NMI;
                            cc->do_interrupt(cpu);
                            next_tb = 0;
                        }
@@ -572,7 +568,7 @@ int cpu_exec(CPUArchState *env)
                           hardware doesn't rely on this, so we
                           provide/save the vector when the interrupt is
                           first signalled.  */
                        env->exception_index = env->pending_vector;
                        cpu->exception_index = env->pending_vector;
                        do_interrupt_m68k_hardirq(env);
                        next_tb = 0;
                    }
@@ -584,7 +580,7 @@ int cpu_exec(CPUArchState *env)
                    }
#elif defined(TARGET_XTENSA)
                    if (interrupt_request & CPU_INTERRUPT_HARD) {
                        env->exception_index = EXC_IRQ;
                        cpu->exception_index = EXC_IRQ;
                        cc->do_interrupt(cpu);
                        next_tb = 0;
                    }
@@ -600,8 +596,8 @@ int cpu_exec(CPUArchState *env)
                }
                if (unlikely(cpu->exit_request)) {
                    cpu->exit_request = 0;
                    env->exception_index = EXCP_INTERRUPT;
                    cpu_loop_exit(env);
                    cpu->exception_index = EXCP_INTERRUPT;
                    cpu_loop_exit(cpu);
                }
                spin_lock(&tcg_ctx.tb_ctx.tb_lock);
                tb = tb_find_fast(env);
@@ -654,25 +650,25 @@ int cpu_exec(CPUArchState *env)
                        /* Instruction counter expired.  */
                        int insns_left;
                        tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
                        insns_left = env->icount_decr.u32;
                        if (env->icount_extra && insns_left >= 0) {
                        insns_left = cpu->icount_decr.u32;
                        if (cpu->icount_extra && insns_left >= 0) {
                            /* Refill decrementer and continue execution.  */
                            env->icount_extra += insns_left;
                            if (env->icount_extra > 0xffff) {
                            cpu->icount_extra += insns_left;
                            if (cpu->icount_extra > 0xffff) {
                                insns_left = 0xffff;
                            } else {
                                insns_left = env->icount_extra;
                                insns_left = cpu->icount_extra;
                            }
                            env->icount_extra -= insns_left;
                            env->icount_decr.u16.low = insns_left;
                            cpu->icount_extra -= insns_left;
                            cpu->icount_decr.u16.low = insns_left;
                        } else {
                            if (insns_left > 0) {
                                /* Execute remaining instructions.  */
                                cpu_exec_nocache(env, insns_left, tb);
                            }
                            env->exception_index = EXCP_INTERRUPT;
                            cpu->exception_index = EXCP_INTERRUPT;
                            next_tb = 0;
                            cpu_loop_exit(env);
                            cpu_loop_exit(cpu);
                        }
                        break;
                    }
+12 −13
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ static bool cpu_thread_is_idle(CPUState *cpu)
    if (cpu_is_stopped(cpu)) {
        return true;
    }
    if (!cpu->halted || qemu_cpu_has_work(cpu) ||
    if (!cpu->halted || cpu_has_work(cpu) ||
        kvm_halt_in_kernel()) {
        return false;
    }
@@ -139,11 +139,10 @@ static int64_t cpu_get_icount_locked(void)

    icount = qemu_icount;
    if (cpu) {
        CPUArchState *env = cpu->env_ptr;
        if (!can_do_io(env)) {
        if (!cpu_can_do_io(cpu)) {
            fprintf(stderr, "Bad clock read\n");
        }
        icount -= (env->icount_decr.u16.low + env->icount_extra);
        icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
    }
    return qemu_icount_bias + (icount << icount_time_shift);
}
@@ -1236,6 +1235,7 @@ int vm_stop_force_state(RunState state)

static int tcg_cpu_exec(CPUArchState *env)
{
    CPUState *cpu = ENV_GET_CPU(env);
    int ret;
#ifdef CONFIG_PROFILER
    int64_t ti;
@@ -1248,9 +1248,9 @@ static int tcg_cpu_exec(CPUArchState *env)
        int64_t count;
        int64_t deadline;
        int decr;
        qemu_icount -= (env->icount_decr.u16.low + env->icount_extra);
        env->icount_decr.u16.low = 0;
        env->icount_extra = 0;
        qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
        cpu->icount_decr.u16.low = 0;
        cpu->icount_extra = 0;
        deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);

        /* Maintain prior (possibly buggy) behaviour where if no deadline
@@ -1266,8 +1266,8 @@ static int tcg_cpu_exec(CPUArchState *env)
        qemu_icount += count;
        decr = (count > 0xffff) ? 0xffff : count;
        count -= decr;
        env->icount_decr.u16.low = decr;
        env->icount_extra = count;
        cpu->icount_decr.u16.low = decr;
        cpu->icount_extra = count;
    }
    ret = cpu_exec(env);
#ifdef CONFIG_PROFILER
@@ -1276,10 +1276,9 @@ static int tcg_cpu_exec(CPUArchState *env)
    if (use_icount) {
        /* Fold pending instructions back into the
           instruction counter, and clear the interrupt flag.  */
        qemu_icount -= (env->icount_decr.u16.low
                        + env->icount_extra);
        env->icount_decr.u32 = 0;
        env->icount_extra = 0;
        qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
        cpu->icount_decr.u32 = 0;
        cpu->icount_extra = 0;
    }
    return ret;
}
+12 −12
Original line number Diff line number Diff line
@@ -46,9 +46,9 @@ int tlb_flush_count;
 * entries from the TLB at any time, so flushing more entries than
 * required is only an efficiency issue, not a correctness issue.
 */
void tlb_flush(CPUArchState *env, int flush_global)
void tlb_flush(CPUState *cpu, int flush_global)
{
    CPUState *cpu = ENV_GET_CPU(env);
    CPUArchState *env = cpu->env_ptr;

#if defined(DEBUG_TLB)
    printf("tlb_flush:\n");
@@ -58,7 +58,7 @@ void tlb_flush(CPUArchState *env, int flush_global)
    cpu->current_tb = NULL;

    memset(env->tlb_table, -1, sizeof(env->tlb_table));
    memset(env->tb_jmp_cache, 0, sizeof(env->tb_jmp_cache));
    memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache));

    env->tlb_flush_addr = -1;
    env->tlb_flush_mask = 0;
@@ -77,9 +77,9 @@ static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
    }
}

void tlb_flush_page(CPUArchState *env, target_ulong addr)
void tlb_flush_page(CPUState *cpu, target_ulong addr)
{
    CPUState *cpu = ENV_GET_CPU(env);
    CPUArchState *env = cpu->env_ptr;
    int i;
    int mmu_idx;

@@ -93,7 +93,7 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr)
               TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
               env->tlb_flush_addr, env->tlb_flush_mask);
#endif
        tlb_flush(env, 1);
        tlb_flush(cpu, 1);
        return;
    }
    /* must reset current TB so that interrupts cannot modify the
@@ -106,7 +106,7 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr)
        tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
    }

    tb_flush_jmp_cache(env, addr);
    tb_flush_jmp_cache(cpu, addr);
}

/* update the TLBs so that writes to code in the virtual page 'addr'
@@ -119,7 +119,7 @@ void tlb_protect_code(ram_addr_t ram_addr)

/* update the TLB so that writes in physical page 'phys_addr' are no longer
   tested for self modifying code */
void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr,
void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr,
                             target_ulong vaddr)
{
    cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
@@ -221,10 +221,11 @@ static void tlb_add_large_page(CPUArchState *env, target_ulong vaddr,
/* Add a new TLB entry. At most one entry for a given virtual address
   is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the
   supplied size is only used by tlb_flush_page.  */
void tlb_set_page(CPUArchState *env, target_ulong vaddr,
void tlb_set_page(CPUState *cpu, target_ulong vaddr,
                  hwaddr paddr, int prot,
                  int mmu_idx, target_ulong size)
{
    CPUArchState *env = cpu->env_ptr;
    MemoryRegionSection *section;
    unsigned int index;
    target_ulong address;
@@ -232,7 +233,6 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr,
    uintptr_t addend;
    CPUTLBEntry *te;
    hwaddr iotlb, xlat, sz;
    CPUState *cpu = ENV_GET_CPU(env);

    assert(size >= TARGET_PAGE_SIZE);
    if (size != TARGET_PAGE_SIZE) {
@@ -261,7 +261,7 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr,
    }

    code_address = address;
    iotlb = memory_region_section_get_iotlb(env, section, vaddr, paddr, xlat,
    iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat,
                                            prot, &address);

    index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
@@ -322,7 +322,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
        if (cc->do_unassigned_access) {
            cc->do_unassigned_access(cpu, addr, false, true, 0, 4);
        } else {
            cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x"
            cpu_abort(cpu, "Trying to execute code outside RAM or ROM at 0x"
                      TARGET_FMT_lx "\n", addr);
        }
    }
+60 −58

File changed.

Preview size limit exceeded, changes collapsed.

Loading