From nobody Sun Nov 16 00:58:29 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1744859479; cv=none; d=zohomail.com; s=zohoarc; b=F2+yYd3SQe3Cpa6yUNQ3kSt9LlDOdt8TYDQRPKkPSUYYFpDMD3Go5SYAYbQiqXY3llFCG3TAvD+u83L8Bqi2D7/L1F4O7hrmTGy6cTOlbZSxmYolKnxa+xHGeJlhqOdWQt8gicbep+rgyHteA4UkHOCjdBdFBjeYEGg0E0QDy74= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1744859479; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=djUkYb5cEKtm2vQ5HA5jLxSOp7umrEQ+H89N60eQqhU=; b=ZJIwZkAQfsKVjCqxP0mF+fscLBiqGtFucsXt/BRUeJkqIbRzJ3/aObGOfRAodFaxMnbwfUUZiUEznWFfcwkXGXdmtU9c0ZNiHUJsE6J/UKIyxkodBNgs91BlbWpyF2a4fZEA9zxfJEukfSDc35Mwr3ehPx4Mu64akB0oHyeN/XI= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744859479346307.49663927512154; Wed, 16 Apr 2025 20:11:19 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5Fdt-000652-2r; Wed, 16 Apr 2025 23:10:21 -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 1u5Fdn-0005zH-FB; Wed, 16 Apr 2025 23:10:16 -0400 Received: from mail.aspeedtech.com ([211.20.114.72] helo=TWMBX01.aspeed.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5Fdl-0000B0-69; Wed, 16 Apr 2025 23:10:15 -0400 Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1258.12; Thu, 17 Apr 2025 11:09:57 +0800 Received: from mail.aspeedtech.com (192.168.10.10) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1258.12 via Frontend Transport; Thu, 17 Apr 2025 11:09:57 +0800 To: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= , Peter Maydell , Steven Lee , Troy Lee , Jamin Lin , Andrew Jeffery , Joel Stanley , "open list:ASPEED BMCs" , "open list:All patches CC here" CC: , Kane-Chen-AS Subject: [PATCH v2 1/3] hw/misc/aspeed_otp: Add Aspeed OTP memory device model Date: Thu, 17 Apr 2025 11:09:53 +0800 Message-ID: <20250417030957.2586802-2-kane_chen@aspeedtech.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250417030957.2586802-1-kane_chen@aspeedtech.com> References: <20250417030957.2586802-1-kane_chen@aspeedtech.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=211.20.114.72; envelope-from=kane_chen@aspeedtech.com; helo=TWMBX01.aspeed.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_FAIL=0.001, SPF_PASS=-0.001 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: , Reply-to: Kane Chen From: Kane Chen via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744859483591019000 From: Kane-Chen-AS This introduces a new model for the ASPEED OTP (One-Time Programmable) memory. The device is implemented as a `SysBusDevice` and provides an abstracted interface for OTP read, write (program), and default value initialization. OTP content is backed by a block device and supports QEMU=E2=80=99s drive infrastructure via the "drive" property. Features: - Enforces irreversible bit programming logic (0->1 or 1->0) - Provides interface for SoC/secure controller integration - Validates bounds and bit-level constraints - Uses QEMU error handling conventions and logging Signed-off-by: Kane-Chen-AS --- hw/misc/aspeed_otpmem.c | 217 ++++++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + include/hw/misc/aspeed_otpmem.h | 40 ++++++ 3 files changed, 258 insertions(+) create mode 100644 hw/misc/aspeed_otpmem.c create mode 100644 include/hw/misc/aspeed_otpmem.h diff --git a/hw/misc/aspeed_otpmem.c b/hw/misc/aspeed_otpmem.c new file mode 100644 index 0000000000..cf4b3aa4d2 --- /dev/null +++ b/hw/misc/aspeed_otpmem.c @@ -0,0 +1,217 @@ +/* + * ASPEED OTP (One-Time Programmable) memory + * + * Copyright (C) 2025 Aspeed + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/block/block.h" +#include "hw/block/flash.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "system/block-backend.h" +#include "qemu/log.h" +#include "qemu/option.h" +#include "hw/sysbus.h" +#include "qemu/error-report.h" +#include "hw/misc/aspeed_otpmem.h" + +static const Property aspeed_otpmem_properties[] =3D { + DEFINE_PROP_DRIVE("drive", AspeedOTPMemState, blk), +}; + +static void aspeed_otpmem_read(void *opaque, uint32_t addr, + uint32_t *out, Error **errp) +{ + AspeedOTPMemState *otp =3D ASPEED_OTPMEM(opaque); + + if (!otp->blk) { + error_setg(errp, "OTP memory is not initialized"); + return; + } + + if (out =3D=3D NULL) { + error_setg(errp, "out is NULL"); + return; + } + + if (addr > (otp->max_size - 4)) { + error_setg(errp, "OTP memory 0x%x is exceeded", addr); + return; + } + + if (blk_pread(otp->blk, (int64_t)addr, sizeof(uint32_t), out, 0) < 0) { + error_setg(errp, "Failed to read data 0x%x", addr); + return; + } + return; +} + +static bool valid_program_data(uint32_t otp_addr, + uint32_t value, uint32_t prog_bit) +{ + uint32_t programmed_bits, has_programmable_bits; + bool is_odd =3D otp_addr & 1; + + /* + * prog_bit uses 0s to indicate target bits to program: + * - if OTP word is even-indexed, programmed bits flip 0->1 + * - if odd, bits flip 1->0 + * Bit programming is one-way only and irreversible. + */ + if (is_odd) { + programmed_bits =3D ~value & prog_bit; + } else { + programmed_bits =3D value & (~prog_bit); + } + + /* If there is some bit can be programed, to accept the request */ + has_programmable_bits =3D value ^ (~prog_bit); + + if (programmed_bits) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Found programmed bits in addr %x\n", + __func__, otp_addr); + for (int i =3D 0; i < 32; ++i) { + if (programmed_bits & (1U << i)) { + qemu_log_mask(LOG_GUEST_ERROR, + " Programmed bit %d\n", + i); + } + } + } + + return has_programmable_bits !=3D 0; +} + +static bool program_otpmem_data(void *opaque, uint32_t otp_addr, + uint32_t prog_bit, uint32_t *value) +{ + AspeedOTPMemState *s =3D ASPEED_OTPMEM(opaque); + bool is_odd =3D otp_addr & 1; + uint32_t otp_offset =3D otp_addr << 2; + + if (blk_pread(s->blk, (int64_t)otp_offset, + sizeof(uint32_t), value, 0) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to read data 0x%x\n", + __func__, otp_offset); + return false; + } + + if (!valid_program_data(otp_addr, *value, prog_bit)) { + return false; + } + + if (is_odd) { + *value &=3D ~prog_bit; + } else { + *value |=3D ~prog_bit; + } + + return true; +} + +static void aspeed_otpmem_prog(void *s, uint32_t otp_addr, + uint32_t data, Error **errp) +{ + AspeedOTPMemState *otp =3D ASPEED_OTPMEM(s); + uint32_t otp_offset, value; + + if (!otp->blk) { + error_setg(errp, "OTP memory is not initialized"); + return; + } + + if (otp_addr > (otp->max_size >> 2)) { + error_setg(errp, "OTP memory 0x%x is exceeded", otp_addr); + return; + } + + otp_offset =3D otp_addr << 2; + if (!program_otpmem_data(s, otp_addr, data, &value)) { + error_setg(errp, "Failed to program data"); + return; + } + + if (blk_pwrite(otp->blk, (int64_t)otp_offset, + sizeof(value), &value, 0) < 0) { + error_setg(errp, "Failed to write data"); + } + + return; +} + +static void aspeed_otpmem_set_default(void *s, uint32_t otp_offset, + uint32_t data, Error **errp) +{ + AspeedOTPMemState *otp =3D ASPEED_OTPMEM(s); + + if ((otp_offset + 4) > otp->max_size) { + error_setg(errp, "OTP memory 0x%x is exceeded", otp_offset); + return; + } + + if (blk_pwrite(otp->blk, (int64_t)otp_offset, + sizeof(data), &data, 0) < 0) { + error_setg(errp, "Failed to write data"); + } + return; +} + +static AspeedOTPMemOps aspeed_otpmem_ops =3D { + .read =3D aspeed_otpmem_read, + .prog =3D aspeed_otpmem_prog, + .set_default_value =3D aspeed_otpmem_set_default +}; + +static void aspeed_otpmem_realize(DeviceState *dev, Error **errp) +{ + AspeedOTPMemState *s =3D ASPEED_OTPMEM(dev); + + if (!s->blk) { + error_setg(&error_fatal, "OTP memory is not initialized"); + return; + } + + s->max_size =3D blk_getlength(s->blk); + if (s->max_size < 0 || (s->max_size % 4)) { + error_setg(&error_fatal, + "Unexpected OTP memory size: %" PRId64 "", + s->max_size); + return; + } + + s->ops =3D &aspeed_otpmem_ops; + + return; +} + +static void aspeed_otpmem_system_reset(DeviceState *dev) +{ + return; +} + +static void aspeed_otpmem_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, aspeed_otpmem_system_reset); + dc->realize =3D aspeed_otpmem_realize; + device_class_set_props(dc, aspeed_otpmem_properties); + +} + +static const TypeInfo aspeed_otpmem_types[] =3D { + { + .name =3D TYPE_ASPEED_OTPMEM, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(AspeedOTPMemState), + .class_init =3D aspeed_otpmem_class_init, + }, +}; + +DEFINE_TYPES(aspeed_otpmem_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 6d47de482c..ed1eaaa2ad 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -136,6 +136,7 @@ system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_sbc.c', 'aspeed_sdmc.c', 'aspeed_xdma.c', + 'aspeed_otpmem.c', 'aspeed_peci.c', 'aspeed_sli.c')) =20 diff --git a/include/hw/misc/aspeed_otpmem.h b/include/hw/misc/aspeed_otpme= m.h new file mode 100644 index 0000000000..11e2de70b6 --- /dev/null +++ b/include/hw/misc/aspeed_otpmem.h @@ -0,0 +1,40 @@ +/* + * ASPEED OTP (One-Time Programmable) memory + * + * Copyright (C) 2025 Aspeed + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef ASPEED_OTPMMEM_H +#define ASPEED_OTPMMEM_H + +#include "hw/sysbus.h" +#include "qapi/error.h" + +#define TYPE_ASPEED_OTPMEM "aspeed.otpmem" +#define ASPEED_OTPMEM_DRIVE "otpmem" + +#define ASPEED_OTPMEM(obj) OBJECT_CHECK(AspeedOTPMemState, (obj), \ + TYPE_ASPEED_OTPMEM) + +typedef struct AspeedOTPMemOps { + void (*read)(void *s, uint32_t addr, uint32_t *out, Error **errp); + void (*prog)(void *s, uint32_t addr, uint32_t data, Error **errp); + void (*set_default_value)(void *s, uint32_t otp_offset, + uint32_t data, Error **errp); +} AspeedOTPMemOps; + +typedef struct AspeedOTPMemState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + BlockBackend *blk; + int64_t max_size; + + AspeedOTPMemOps *ops; +} AspeedOTPMemState; + +#endif /* ASPEED_OTPMMEM_H */ + --=20 2.43.0 From nobody Sun Nov 16 00:58:29 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1744859481; cv=none; d=zohomail.com; s=zohoarc; b=THe7JO+vEEbdb57APpHHqOiwxYyURKBJpb450xfpoNupYiTI7JI3ixZAxJlq4KuSogzbzXSbhCfnTXPybCzOiaei2gCALBH0/joyshHNL4FlbjckUcDOPCxBPNGC2SMTstwAvRfS7fXlsmakwEEB3lbcA89BWNS8eIr3CoCpg/o= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1744859481; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=Al63NJ6blyRTh7yFQItAG+IMnrLRj+bLWHFKu96cHKA=; b=V/HUMGZFFy+ZXL92qL6eG+VGqAUbda+pmsZKAu5Uk7pbM85HBSohEPGy1C6hmkS9u3KxANfRozmPUTV8TJTzU35pRFPTSXtkCbCsNX2lFVCkt1B8sHNK4o0+4tceMu76OfGs62V9rmsXsJ8fiKw7xteZGLPbUlAepVnlw5fxCiM= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744859481044914.916297963931; Wed, 16 Apr 2025 20:11:21 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5Fdv-000683-SX; Wed, 16 Apr 2025 23:10:23 -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 1u5Fdt-00066N-Cn; Wed, 16 Apr 2025 23:10:21 -0400 Received: from mail.aspeedtech.com ([211.20.114.72] helo=TWMBX01.aspeed.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5Fdr-0000B0-EM; Wed, 16 Apr 2025 23:10:20 -0400 Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1258.12; Thu, 17 Apr 2025 11:09:58 +0800 Received: from mail.aspeedtech.com (192.168.10.10) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1258.12 via Frontend Transport; Thu, 17 Apr 2025 11:09:58 +0800 To: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= , Peter Maydell , Steven Lee , Troy Lee , Jamin Lin , Andrew Jeffery , Joel Stanley , "open list:ASPEED BMCs" , "open list:All patches CC here" CC: , Kane-Chen-AS Subject: [PATCH v2 2/3] hw/misc/aspeed_sbc: Connect Aspeed OTP memory device to SBC controller Date: Thu, 17 Apr 2025 11:09:54 +0800 Message-ID: <20250417030957.2586802-3-kane_chen@aspeedtech.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250417030957.2586802-1-kane_chen@aspeedtech.com> References: <20250417030957.2586802-1-kane_chen@aspeedtech.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=211.20.114.72; envelope-from=kane_chen@aspeedtech.com; helo=TWMBX01.aspeed.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_FAIL=0.001, SPF_PASS=-0.001 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: , Reply-to: Kane Chen From: Kane Chen via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744859483588019000 Content-Type: text/plain; charset="utf-8" From: Kane-Chen-AS This patch integrates the `aspeed.otpmem` device with the ASPEED Secure Boot Controller (SBC). The SBC now accepts an OTP backend via a QOM link property ("otpmem"), enabling internal access to OTP content for controller-specific logic. This connection provides the foundation for future enhancements involving fuse storage, device configuration, or secure manufacturing data provisioning. Signed-off-by: Kane-Chen-AS --- hw/misc/aspeed_sbc.c | 155 +++++++++++++++++++++++++++++++++++ include/hw/misc/aspeed_sbc.h | 15 ++++ 2 files changed, 170 insertions(+) diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index e4a6bd1581..ed128c5dee 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -17,7 +17,11 @@ #include "migration/vmstate.h" =20 #define R_PROT (0x000 / 4) +#define R_CMD (0x004 / 4) +#define R_ADDR (0x010 / 4) #define R_STATUS (0x014 / 4) +#define R_CAMP1 (0x020 / 4) +#define R_CAMP2 (0x024 / 4) #define R_QSR (0x040 / 4) =20 /* R_STATUS */ @@ -57,6 +61,152 @@ static uint64_t aspeed_sbc_read(void *opaque, hwaddr ad= dr, unsigned int size) return s->regs[addr]; } =20 +static void aspeed_sbc_otpmem_read(void *opaque) +{ + AspeedSBCState *s =3D ASPEED_SBC(opaque); + uint32_t otp_addr, data, otp_offset; + bool is_data =3D false; + Error *local_err =3D NULL; + + if (s->otpmem =3D=3D NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: OTP not initialized\n", + __func__); + return; + } + + otp_addr =3D s->regs[R_ADDR]; + if (otp_addr < OTP_DATA_DWORD_COUNT) { + is_data =3D true; + } else if (otp_addr >=3D OTP_TOTAL_DWORD_COUNT) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid OTP addr 0x%x\n", + __func__, otp_addr); + return; + } + otp_offset =3D otp_addr << 2; + + s->otpmem->ops->read(s->otpmem, otp_offset, &data, &local_err); + if (local_err) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to read data 0x%x, %s\n", + __func__, otp_offset, + error_get_pretty(local_err)); + error_free(local_err); + return; + } + s->regs[R_CAMP1] =3D data; + + if (is_data) { + s->otpmem->ops->read(s->otpmem, otp_offset + 4, &data, &local_err); + if (local_err) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to read data 0x%x, %s\n", + __func__, otp_offset, + error_get_pretty(local_err)); + error_free(local_err); + return; + } + s->regs[R_CAMP2] =3D data; + } +} + +static void mr_handler(uint32_t otp_addr, uint32_t data) +{ + switch (otp_addr) { + case MODE_REGISTER: + case MODE_REGISTER_A: + case MODE_REGISTER_B: + /* HW behavior, do nothing here */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unsupported address 0x%x\n", + __func__, otp_addr); + return; + } +} + +static void aspeed_sbc_otpmem_write(void *opaque) +{ + AspeedSBCState *s =3D ASPEED_SBC(opaque); + uint32_t otp_addr, data; + + otp_addr =3D s->regs[R_ADDR]; + data =3D s->regs[R_CAMP1]; + + if (otp_addr =3D=3D 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ignore write program bit request\n", + __func__); + } else if (otp_addr >=3D MODE_REGISTER) { + mr_handler(otp_addr, data); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled OTP write address 0x%x\n", + __func__, otp_addr); + } +} + +static void aspeed_sbc_otpmem_prog(void *opaque) +{ + AspeedSBCState *s =3D ASPEED_SBC(opaque); + uint32_t otp_addr, value; + Error *local_err =3D NULL; + + if (s->otpmem =3D=3D NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: OTP not initialized\n", + __func__); + return; + } + otp_addr =3D s->regs[R_ADDR]; + value =3D s->regs[R_CAMP1]; + if (otp_addr >=3D OTP_TOTAL_DWORD_COUNT) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid OTP addr 0x%x\n", + __func__, otp_addr); + return; + } + + s->otpmem->ops->prog(s->otpmem, otp_addr, value, &local_err); + if (local_err) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to program data 0x%x to 0x%x, %s\n", + __func__, value, otp_addr, + error_get_pretty(local_err)); + error_free(local_err); + return; + } +} + +static void aspeed_sbc_handle_command(void *opaque, uint32_t cmd) +{ + AspeedSBCState *s =3D ASPEED_SBC(opaque); + + s->regs[R_STATUS] &=3D ~(OTP_MEM_IDLE | OTP_IDLE); + + switch (cmd) { + case READ_CMD: + aspeed_sbc_otpmem_read(s); + break; + case WRITE_CMD: + aspeed_sbc_otpmem_write(s); + break; + case PROG_CMD: + aspeed_sbc_otpmem_prog(s); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unknown command 0x%x\n", + __func__, cmd); + break; + } + + s->regs[R_STATUS] |=3D (OTP_MEM_IDLE | OTP_IDLE); +} + + static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { @@ -78,6 +228,9 @@ static void aspeed_sbc_write(void *opaque, hwaddr addr, = uint64_t data, "%s: write to read only register 0x%" HWADDR_PRIx "\= n", __func__, addr << 2); return; + case R_CMD: + aspeed_sbc_handle_command(opaque, data); + return; default: break; } @@ -139,6 +292,8 @@ static const VMStateDescription vmstate_aspeed_sbc =3D { static const Property aspeed_sbc_properties[] =3D { DEFINE_PROP_BOOL("emmc-abr", AspeedSBCState, emmc_abr, 0), DEFINE_PROP_UINT32("signing-settings", AspeedSBCState, signing_setting= s, 0), + DEFINE_PROP_LINK("otpmem", AspeedSBCState, otpmem, + TYPE_ASPEED_OTPMEM, AspeedOTPMemState *), }; =20 static void aspeed_sbc_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h index 405e6782b9..8ae59d977e 100644 --- a/include/hw/misc/aspeed_sbc.h +++ b/include/hw/misc/aspeed_sbc.h @@ -10,6 +10,7 @@ #define ASPEED_SBC_H =20 #include "hw/sysbus.h" +#include "hw/misc/aspeed_otpmem.h" =20 #define TYPE_ASPEED_SBC "aspeed.sbc" #define TYPE_ASPEED_AST2600_SBC TYPE_ASPEED_SBC "-ast2600" @@ -27,6 +28,18 @@ OBJECT_DECLARE_TYPE(AspeedSBCState, AspeedSBCClass, ASPE= ED_SBC) #define QSR_SHA384 (0x2 << 10) #define QSR_SHA512 (0x3 << 10) =20 +#define READ_CMD (0x23b1e361) +#define WRITE_CMD (0x23b1e362) +#define PROG_CMD (0x23b1e364) + +#define OTP_DATA_DWORD_COUNT (0x800) +#define OTP_TOTAL_DWORD_COUNT (0x1000) +#define OTP_FILE_SIZE (OTP_TOTAL_DWORD_COUNT * sizeof(uint32= _t)) + +#define MODE_REGISTER (0x1000) +#define MODE_REGISTER_A (0x3000) +#define MODE_REGISTER_B (0x5000) + struct AspeedSBCState { SysBusDevice parent; =20 @@ -36,6 +49,8 @@ struct AspeedSBCState { MemoryRegion iomem; =20 uint32_t regs[ASPEED_SBC_NR_REGS]; + + AspeedOTPMemState *otpmem; }; =20 struct AspeedSBCClass { --=20 2.43.0 From nobody Sun Nov 16 00:58:29 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1744859479; cv=none; d=zohomail.com; s=zohoarc; b=jWK5lm2GG0e2Zaf63toNNOM3yhwG5Ynt9UHBDDanIvdDW75tOPjiyD+GotKlM15e0QIwNIteglcGWwMU0j95/rffeuMwGUN+A+ZV+nZyyCc8f+q5bqXs1er9/S1mpe46ynhnyllbu6E/69+UA5YaBqW28Uof95zbsvZWZRdoXLk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1744859479; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=bUjj155XiSLYxlUcyB10Xn0j4Ei6lEwpy/78j8RI4OU=; b=aGrndKFPrYtfiz/4hsSCVfkH8de89h7FWLtxRNt5lkzDkUtIeTkSHZQhV2ukHatWeX295NpdrIWrIzZS0pqHBiv2eVLLGZ/P+2KA5jLD5ZxA1qQwc7+IpALEHllThdnJPPKdC4VxzkjiR1HpBAsQ3xJGOZ33TUkxL1WNydUo0wQ= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1744859479614239.83557737412013; Wed, 16 Apr 2025 20:11:19 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1u5Fdz-0006Cw-31; Wed, 16 Apr 2025 23:10:27 -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 1u5Fdx-0006C6-E2; Wed, 16 Apr 2025 23:10:25 -0400 Received: from mail.aspeedtech.com ([211.20.114.72] helo=TWMBX01.aspeed.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1u5Fdv-0000B0-Hc; Wed, 16 Apr 2025 23:10:25 -0400 Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1258.12; Thu, 17 Apr 2025 11:09:58 +0800 Received: from mail.aspeedtech.com (192.168.10.10) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1258.12 via Frontend Transport; Thu, 17 Apr 2025 11:09:58 +0800 To: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= , Peter Maydell , Steven Lee , Troy Lee , Jamin Lin , Andrew Jeffery , Joel Stanley , "open list:ASPEED BMCs" , "open list:All patches CC here" CC: , Kane-Chen-AS Subject: [PATCH v2 3/3] hw/arm: Integrate Aspeed OTP memory into AST10x0 and AST2600 SoCs Date: Thu, 17 Apr 2025 11:09:55 +0800 Message-ID: <20250417030957.2586802-4-kane_chen@aspeedtech.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250417030957.2586802-1-kane_chen@aspeedtech.com> References: <20250417030957.2586802-1-kane_chen@aspeedtech.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=211.20.114.72; envelope-from=kane_chen@aspeedtech.com; helo=TWMBX01.aspeed.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_FAIL=0.001, SPF_PASS=-0.001 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: , Reply-to: Kane Chen From: Kane Chen via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1744859482891019100 Content-Type: text/plain; charset="utf-8" From: Kane-Chen-AS This patch wires up the OTP memory device (`aspeed.otpmem`) into the AST1030 and AST2600 SoC models. The device is initialized, attached to a backing block drive (`-drive id=3Dotpmem`) and linked to the SBC controller via a QOM link. The default OTP memory image can be generated using the following command. ```bash for i in $(seq 1 2048); do printf '\x00\x00\x00\x00\xff\xff\xff\xff' done > otpmem.img ``` To load the OTP memory image into the guest, use: ```bash ./qemu-system-arm \ -drive id=3Dotpmem,file=3Dotpmem.img,if=3Dnone,format=3Draw \ ... ``` Note: Do not use the -snapshot option, or OTP data writes will not persist to the image file. Signed-off-by: Kane-Chen-AS --- hw/arm/aspeed_ast10x0.c | 19 +++++++++++++++++++ hw/arm/aspeed_ast2600.c | 19 +++++++++++++++++++ include/hw/arm/aspeed_soc.h | 2 ++ 3 files changed, 40 insertions(+) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index ec329f4991..eaa70feb9f 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -15,6 +15,7 @@ #include "system/system.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" +#include "system/block-backend-global-state.h" #include "hw/arm/aspeed_soc.h" =20 #define ASPEED_SOC_IOMEM_SIZE 0x00200000 @@ -156,6 +157,8 @@ static void aspeed_soc_ast1030_init(Object *obj) =20 object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); =20 + object_initialize_child(obj, "otpmem", &s->otpmem, TYPE_ASPEED_OTPMEM); + for (i =3D 0; i < sc->wdts_num; i++) { snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); @@ -194,6 +197,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev= _soc, Error **errp) Error *err =3D NULL; int i; g_autofree char *sram_name =3D NULL; + BlockBackend *blk; =20 if (!clock_has_source(s->sysclk)) { error_setg(errp, "sysclk clock must be wired up by the board code"= ); @@ -359,6 +363,21 @@ static void aspeed_soc_ast1030_realize(DeviceState *de= v_soc, Error **errp) ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_bas= e); } =20 + /* OTP memory */ + blk =3D blk_by_name(ASPEED_OTPMEM_DRIVE); + if (blk) { + blk_set_perm(blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, + 0, &error_fatal); + qdev_prop_set_drive(DEVICE(&s->otpmem), "drive", blk); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->otpmem), errp)) { + return; + } + /* Assign OTP memory to SBC */ + object_property_set_link(OBJECT(&s->sbc), "otpmem", + OBJECT(&s->otpmem), &error_abort); + } + /* Secure Boot Controller */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { return; diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 1f994ba26c..9fe3eeeb0e 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/misc/unimp.h" +#include "system/block-backend-global-state.h" #include "hw/arm/aspeed_soc.h" #include "qemu/module.h" #include "qemu/error-report.h" @@ -263,6 +264,8 @@ static void aspeed_soc_ast2600_init(Object *obj) =20 object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); =20 + object_initialize_child(obj, "otpmem", &s->otpmem, TYPE_ASPEED_OTPMEM); + object_initialize_child(obj, "iomem", &s->iomem, TYPE_UNIMPLEMENTED_DE= VICE); object_initialize_child(obj, "video", &s->video, TYPE_UNIMPLEMENTED_DE= VICE); object_initialize_child(obj, "dpmcu", &s->dpmcu, TYPE_UNIMPLEMENTED_DE= VICE); @@ -293,6 +296,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev= , Error **errp) AspeedSoCClass *sc =3D ASPEED_SOC_GET_CLASS(s); qemu_irq irq; g_autofree char *sram_name =3D NULL; + BlockBackend *blk; =20 /* Default boot region (SPI memory or ROMs) */ memory_region_init(&s->spi_boot_container, OBJECT(s), @@ -628,6 +632,21 @@ static void aspeed_soc_ast2600_realize(DeviceState *de= v, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->i3c.devices[i]), 0, irq); } =20 + /* OTP memory */ + blk =3D blk_by_name(ASPEED_OTPMEM_DRIVE); + if (blk) { + blk_set_perm(blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, + 0, &error_fatal); + qdev_prop_set_drive(DEVICE(&s->otpmem), "drive", blk); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->otpmem), errp)) { + return; + } + /* Assign OTP memory to SBC */ + object_property_set_link(OBJECT(&s->sbc), "otpmem", + OBJECT(&s->otpmem), &error_abort); + } + /* Secure Boot Controller */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { return; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index f069d17d16..2d15c6047a 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -36,6 +36,7 @@ #include "hw/usb/hcd-ehci.h" #include "qom/object.h" #include "hw/misc/aspeed_lpc.h" +#include "hw/misc/aspeed_otpmem.h" #include "hw/misc/unimp.h" #include "hw/misc/aspeed_peci.h" #include "hw/fsi/aspeed_apb2opb.h" @@ -73,6 +74,7 @@ struct AspeedSoCState { AspeedSMCState spi[ASPEED_SPIS_NUM]; EHCISysBusState ehci[ASPEED_EHCIS_NUM]; AspeedSBCState sbc; + AspeedOTPMemState otpmem; AspeedSLIState sli; AspeedSLIState sliio; MemoryRegion secsram; --=20 2.43.0