From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 95E94C7EE29 for ; Sat, 3 Jun 2023 23:37:28 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q5anl-0005dZ-3i; Sat, 03 Jun 2023 19:36:53 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1q5anZ-0005Qq-Pv; Sat, 03 Jun 2023 19:36:42 -0400 Received: from mail-pl1-x62b.google.com ([2607:f8b0:4864:20::62b]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1q5anX-0003RF-Si; Sat, 03 Jun 2023 19:36:41 -0400 Received: by mail-pl1-x62b.google.com with SMTP id d9443c01a7336-1b04706c85fso32992805ad.0; Sat, 03 Jun 2023 16:36:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1685835398; x=1688427398; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=61UYK1ub4Ws4w2ZD2up92+UUW4NyC0w778LN/exkdHU=; b=hqKNLzx1inSlsmsLSpjtHqPwmO4wfhpXZlliMXdxAK9GWHVKJbDVu9HuKnJRgdUeL6 N/ENHfQ1EAYFR9RKnZpnSpJ+28Jsk+cvEh+C9mtv7KLw0e174m0PLuuj5fIUby7uUKTY MMHufUW/1i3o1wY2sAJoa4R1frI7euA4C946oDWvj3206gFenRzlYpfacfzMYezwchO2 ZlsmeiOSrUAfRtSIJdIfAoGVRtMxEpOhuULccP9JYrzHKAX8koSbRzjBABeJZXoMr6xf mRPWIUsT6W1OczCgf+82AJD3DGwSqK87T3SJqtYTczSCdfGi3IVfHpivqcobsdlzxvwj PJfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685835398; x=1688427398; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=61UYK1ub4Ws4w2ZD2up92+UUW4NyC0w778LN/exkdHU=; b=FPOqsfoCGKVTzFQYZ6jPgivMamxC2Eig4K3ERjEStYMyj+aekAP+iTizTYmAiz64Xs 2LBSXCMh9dNV++qNThLTzF1NTXRXqRgRs0w04Xs9RMQzXdDCvmRutgDMQUA3+OYz8pvB LykaS/LaYC+i9TjxLDd1F02zpVzYBKvhGYtYR8mTpWnXPP6vBhlt+cjUQMLu5ZYpZOuw WHwE6OrPQ4WRm+reV9pXCnoq+CXdXTvBo/zqET1I8W7ly/uRAXnq49MgA/afV/MLMMbe 7BI67pDnJgDKhN3Ac9YTab6054b47ZDu8mFZKO9GlDtU3DufvKk0wkP/+fYCOK3UqdRd 50Zw== X-Gm-Message-State: AC+VfDweXfrNWxm2A2xb6ZLKHuucDUZcsKovcR9tvTIhjuwudQ1L9/mw AaHk4sHWZnF8dwWTciPx3jc1/o0udQc= X-Google-Smtp-Source: ACHHUZ4WPaahkaRPSUtxKLACD3Sp5NKHCDGySbJVpS82ylHVr3mcHAWK9sWiQk22/qGW5mHb+Lu9Kg== X-Received: by 2002:a17:902:c944:b0:1b0:5d15:3132 with SMTP id i4-20020a170902c94400b001b05d153132mr3880366pla.58.1685835398483; Sat, 03 Jun 2023 16:36:38 -0700 (PDT) Received: from wheely.local0.net ([203.221.142.9]) by smtp.gmail.com with ESMTPSA id io23-20020a17090312d700b001aface7bdd8sm3743533plb.31.2023.06.03.16.36.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 03 Jun 2023 16:36:38 -0700 (PDT) From: Nicholas Piggin To: qemu-ppc@nongnu.org Cc: Nicholas Piggin , qemu-devel@nongnu.org, Daniel Henrique Barboza , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Subject: [PATCH 4/4] target/ppc: Implement core timebase state machine and TFMR Date: Sun, 4 Jun 2023 09:36:12 +1000 Message-Id: <20230603233612.125879-5-npiggin@gmail.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230603233612.125879-1-npiggin@gmail.com> References: <20230603233612.125879-1-npiggin@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Received-SPF: pass client-ip=2607:f8b0:4864:20::62b; envelope-from=npiggin@gmail.com; helo=mail-pl1-x62b.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This implements the core timebase state machine, which is the core side of the time-of-day system in POWER processors. This facility is operated by control fields in the TFMR register, which also contains status fields. The core timebase interacts with the chiptod hardware, primarily to receive TOD updates, to synchronise timebase with other cores. This model does not actually update TB values with TOD or updates received from the chiptod, as timebases are always synchronised. It does step through the states required to perform the update. There are several asynchronous state transitions. These are modelled using using mfTFMR to drive state changes, because it is expected that firmware poll the register to wait for those states. This is good enough to test basic firmware behaviour without adding real timers. The values chosen are arbitrary. Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 34 ++++++++ target/ppc/timebase_helper.c | 147 ++++++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 2 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index d73cce8474..b1520ea4db 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1177,6 +1177,13 @@ struct CPUArchState { /* PowerNV chiptod / timebase facility state. */ int tb_ready_for_tod; /* core TB ready to receive TOD from chiptod */ int tod_sent_to_tb; /* chiptod sent TOD to the core TB */ + + /* + * Timers for async events are simulated by mfTFAC because TFAC is to be + * polled for event. + */ + int tb_state_timer; + int tb_sync_pulse_timer; #endif #endif @@ -2527,6 +2534,33 @@ enum { HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23), }; +/* TFMR */ +enum { + TFMR_CONTROL_MASK = PPC_BITMASK(0, 24), + TFMR_MASK_HMI = PPC_BIT(10), + TFMR_TB_ECLIPZ = PPC_BIT(14), + TFMR_LOAD_TOD_MOD = PPC_BIT(16), + TFMR_MOVE_CHIP_TOD_TO_TB = PPC_BIT(18), + TFMR_CLEAR_TB_ERRORS = PPC_BIT(24), + TFMR_STATUS_MASK = PPC_BITMASK(25, 63), + TFMR_TBST_ENCODED = PPC_BITMASK(28, 31), /* TBST = TB State */ + TFMR_TBST_LAST = PPC_BITMASK(32, 35), /* Previous TBST */ + TFMR_TB_ENABLED = PPC_BIT(40), + TFMR_TB_VALID = PPC_BIT(41), + TFMR_TB_SYNC_OCCURED = PPC_BIT(42), +}; + +/* TFMR TBST */ +enum { + TBST_RESET = 0x0, + TBST_SEND_TOD_MOD = 0x1, + TBST_NOT_SET = 0x2, + TBST_SYNC_WAIT = 0x6, + TBST_GET_TOD = 0x7, + TBST_TB_RUNNING = 0x8, + TBST_TB_ERROR = 0x9, +}; + /*****************************************************************************/ #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index 34b1d5ad05..11a06fafe6 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -272,14 +272,157 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) #if defined(TARGET_PPC64) /* POWER processor Timebase Facility */ +static unsigned int tfmr_get_tb_state(uint64_t tfmr) +{ + return (tfmr & TFMR_TBST_ENCODED) >> (63 - 31); +} + +static uint64_t tfmr_new_tb_state(uint64_t tfmr, unsigned int tbst) +{ + tfmr &= ~TFMR_TBST_LAST; + tfmr |= (tfmr & TFMR_TBST_ENCODED) >> 4; /* move state to last state */ + tfmr &= ~TFMR_TBST_ENCODED; + tfmr |= (uint64_t)tbst << (63 - 31); /* move new state to state */ + + if (tbst == TBST_TB_RUNNING) { + tfmr |= TFMR_TB_VALID; + } else { + tfmr &= ~TFMR_TB_VALID; + } + + return tfmr; +} + +static void tb_state_machine_step(CPUPPCState *env) +{ + uint64_t tfmr = env->spr[SPR_TFMR]; + unsigned int tbst = tfmr_get_tb_state(tfmr); + + if (!(tfmr & TFMR_TB_ECLIPZ) || tbst == TBST_TB_ERROR) { + return; + } + + if (env->tb_sync_pulse_timer) { + env->tb_sync_pulse_timer--; + } else { + tfmr |= TFMR_TB_SYNC_OCCURED; + env->spr[SPR_TFMR] = tfmr; + } + + if (env->tb_state_timer) { + env->tb_state_timer--; + return; + } + + if (tfmr & TFMR_LOAD_TOD_MOD) { + tfmr &= ~TFMR_LOAD_TOD_MOD; + if (tbst == TBST_GET_TOD) { + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); + } else { + tfmr = tfmr_new_tb_state(tfmr, TBST_SEND_TOD_MOD); + /* State seems to transition immediately */ + tfmr = tfmr_new_tb_state(tfmr, TBST_NOT_SET); + } + } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) { + if (tbst == TBST_SYNC_WAIT) { + tfmr = tfmr_new_tb_state(tfmr, TBST_GET_TOD); + env->tb_state_timer = 3; + } else if (tbst == TBST_GET_TOD) { + if (env->tod_sent_to_tb) { + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_RUNNING); + tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; + env->tb_ready_for_tod = 0; + env->tod_sent_to_tb = 0; + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB " + "state machine in invalid state 0x%x\n", tbst); + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); + env->tb_ready_for_tod = 0; + } + } + + env->spr[SPR_TFMR] = tfmr; +} + target_ulong helper_load_tfmr(CPUPPCState *env) { - return env->spr[SPR_TFMR]; + tb_state_machine_step(env); + + return env->spr[SPR_TFMR] | TFMR_TB_ECLIPZ; } void helper_store_tfmr(CPUPPCState *env, target_ulong val) { - env->spr[SPR_TFMR] = val; + uint64_t tfmr = env->spr[SPR_TFMR]; + unsigned int tbst = tfmr_get_tb_state(tfmr); + + if (!(val & TFMR_TB_ECLIPZ)) { + qemu_log_mask(LOG_UNIMP, "TFMR non-ECLIPZ mode not implemented\n"); + tfmr &= ~TFMR_TBST_ENCODED; + tfmr &= ~TFMR_TBST_LAST; + goto out; + } + + /* Update control bits */ + tfmr = (tfmr & ~TFMR_CONTROL_MASK) | (val & TFMR_CONTROL_MASK); + + /* mtspr always clears this */ + tfmr &= ~TFMR_TB_SYNC_OCCURED; + env->tb_sync_pulse_timer = 1; + + /* + * We don't implement any of the error status bits that can be + * cleared by writing 1 to them. TB error injection / simulation + * would have to implement some. + * + * Also don't implement mfTB failing when the TB state machine is + * not running. + */ + + if (((tfmr | val) & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) == + (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) { + qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: LOAD_TOD_MOD and " + "MOVE_CHIP_TOD_TO_TB both set\n"); + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); + env->tb_ready_for_tod = 0; + goto out; + } + + if (tfmr & TFMR_CLEAR_TB_ERRORS) { + tfmr = tfmr_new_tb_state(tfmr, TBST_RESET); + tfmr &= ~TFMR_CLEAR_TB_ERRORS; + tfmr &= ~TFMR_LOAD_TOD_MOD; + tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; + env->tb_ready_for_tod = 0; + env->tod_sent_to_tb = 0; + goto out; + } + + if (tbst == TBST_TB_ERROR) { + qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: mtspr TFMR in TB_ERROR" + " state\n"); + return; + } + + if (tfmr & TFMR_LOAD_TOD_MOD) { + env->tb_state_timer = 3; + } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) { + if (tbst == TBST_NOT_SET) { + tfmr = tfmr_new_tb_state(tfmr, TBST_SYNC_WAIT); + env->tb_ready_for_tod = 1; + env->tb_state_timer = 3; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB " + "not in TB not set state 0x%x\n", + tbst); + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); + env->tb_ready_for_tod = 0; + } + } + +out: + env->spr[SPR_TFMR] = tfmr; } #endif -- 2.40.1