UEFI固件更新实战:基于EDK II的Capsule技术深度指南
一、问题篇:固件更新的挑战与本质
1.1 嵌入式设备的固件更新困境
在嵌入式系统开发中,固件更新是保障设备长期可靠运行的关键环节。然而,实际操作中开发者常面临三大核心难题:认证失败(固件镜像被篡改或来源不明)、依赖冲突(多设备间更新顺序导致的系统不稳定)、回滚风险(更新失败后设备变砖)。某工业控制设备厂商曾因未验证固件签名,导致恶意代码通过更新通道注入,造成生产线全面停工,直接损失超过500万元。
1.2 固件更新的技术本质
固件更新本质上是可信代码的安全分发与执行过程。不同于普通软件更新,固件更新直接操作硬件层,需要解决三个核心问题:
- 完整性:确保固件未被篡改
- 兼容性:处理不同硬件版本的适配
- 原子性:保证更新过程不被中断
UEFI规范中的Capsule更新机制正是为解决这些问题而设计的标准化方案。
1.3 传统更新方式的局限性
传统固件更新方式主要存在以下缺陷:
- 缺乏标准化:各厂商私有协议导致工具不通用
- 安全机制薄弱:多依赖物理接触或简单校验
- 管理复杂度高:难以实现大规模设备的统一管理
- 回滚机制缺失:更新失败后恢复困难
二、方案篇:Capsule更新技术架构解析
2.1 Capsule更新的核心组件
Capsule更新机制基于UEFI标准,主要包含以下关键组件:
Capsule镜像(加密签名的固件更新包,包含元数据与执行 payload):作为更新载体,必须包含标准头、认证信息和固件镜像三部分。
FMP协议(Firmware Management Protocol,固件管理协议):类似于固件更新的API接口,定义了获取固件信息、验证固件合法性、执行更新等标准操作。
ESRT表(EFI System Resource Table,系统资源表):记录系统中可更新固件设备的信息,相当于固件设备的"花名册"。
安全库:提供加密、签名验证等安全服务,如PKCS#7签名验证库。
2.2 分层架构设计
Capsule更新系统采用清晰的分层架构,确保各模块解耦与复用:
flowchart BT
subgraph 应用层
A[Capsule生成工具]
B[更新管理工具]
end
subgraph 协议层
C[FMP协议实现]
D[ESRT管理]
end
subgraph 驱动层
E[设备专用驱动]
F[安全验证驱动]
end
subgraph 硬件抽象层
G[固件存储接口]
H[硬件状态监控]
end
A --> C
B --> D
C --> E
C --> F
E --> G
F --> H
这种分层设计带来两大优势:一是不同硬件平台可复用上层逻辑,二是安全机制可独立升级而不影响其他模块。
2.3 安全更新流程设计
Capsule更新的安全流程包含四个关键阶段:
sequenceDiagram
participant 工具 as 更新工具
participant 协议 as FMP协议
participant 验证 as 安全验证
participant 设备 as 硬件设备
工具->>工具: 构建Capsule镜像
工具->>工具: 数字签名
工具->>协议: 提交更新请求
协议->>验证: 请求签名验证
验证->>协议: 返回验证结果
alt 验证通过
协议->>协议: 检查设备依赖
协议->>设备: 执行固件写入
设备->>协议: 返回写入状态
协议->>工具: 返回更新结果
else 验证失败
协议->>工具: 返回安全错误
end
三、实践篇:基于EDK II的Capsule开发详解
3.1 开发环境搭建
3.1.1 源码获取与环境配置
# 克隆EDK II源码仓库
git clone https://gitcode.com/gh_mirrors/ed/edk2.git
cd edk2
# 初始化子模块
git submodule update --init
# 设置构建环境
source edksetup.sh
# 安装必要依赖
sudo apt-get install build-essential uuid-dev iasl git nasm
3.1.2 工程结构设计
推荐的Capsule工具工程结构如下:
Edk2CapsuleTool/
├── App/
│ ├── CapsuleCreator.c # Capsule镜像创建工具
│ └── CapsuleCreator.inf # 模块信息文件
├── Library/
│ ├── CapsuleLib/ # Capsule处理库
│ └── SecurityLib/ # 安全处理库
└── Include/ # 头文件目录
3.1.3 常见环境问题解决
- 编译错误:确保使用EDK II支持的GCC版本(推荐GCC 5-9)
- 工具链缺失:运行
BaseTools/BinWrappers/PosixLike下的脚本自动配置 - 子模块问题:使用
git submodule sync同步子模块
3.2 Capsule镜像构建实现
3.2.1 镜像结构定义
// Capsule格式定义,遵循UEFI规范
typedef struct {
EFI_CAPSULE_HEADER Header; // 标准Capsule头
EFI_FIRMWARE_IMAGE_AUTHENTICATION AuthInfo; // 认证信息
FMP_PAYLOAD_HEADER FmpHeader; // FMP元数据
UINT8 Payload[]; // 固件镜像数据
} EDKII_CAPSULE;
3.2.2 镜像构建核心代码
/**
* 构建Capsule镜像
*
* @param[in] RawImage 原始固件镜像数据
* @param[in] ImageSize 原始镜像大小
* @param[in] Version 固件版本号
* @param[out] Capsule 输出的Capsule镜像
* @param[out] CapsuleSize 输出的Capsule大小
* @return EFI_STATUS 操作结果
*/
EFI_STATUS BuildCapsule(
IN UINT8 *RawImage,
IN UINTN ImageSize,
IN UINT32 Version,
OUT UINT8 **Capsule,
OUT UINTN *CapsuleSize
) {
// 计算总大小 = 头部大小 + 镜像大小
*CapsuleSize = sizeof(EDKII_CAPSULE) + ImageSize;
// 分配内存
*Capsule = AllocatePool(*CapsuleSize);
if (*Capsule == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// 初始化Capsule头部
(*Capsule)->Header.CapsuleGuid = gEfiCapsuleGuid; // 设置标准GUID
(*Capsule)->Header.HeaderSize = sizeof(EFI_CAPSULE_HEADER);
(*Capsule)->Header.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET |
CAPSULE_FLAGS_INITIATE_RESET;
// 设置FMP元数据
(*Capsule)->FmpHeader.Version = Version;
(*Capsule)->FmpHeader.HeaderSize = sizeof(FMP_PAYLOAD_HEADER);
(*Capsule)->FmpHeader.PayloadSize = ImageSize;
// 复制固件镜像数据
CopyMem((*Capsule)->Payload, RawImage, ImageSize);
return EFI_SUCCESS;
}
3.2.3 常见问题
- 内存分配失败:使用
AllocatePool()而非标准malloc(),确保符合UEFI内存管理规范 - 版本号设置:必须使用32位无符号整数,建议采用
0xYYYYMMDD格式 - 标志位组合:根据需求组合
CAPSULE_FLAGS,如需要重启则添加CAPSULE_FLAGS_INITIATE_RESET
3.3 数字签名实现
3.3.1 签名流程设计
Capsule镜像必须经过数字签名以确保完整性和真实性。以下是基于PKCS#7的签名实现:
/**
* 为Capsule镜像添加数字签名
*
* @param[in,out] Capsule 待签名的Capsule镜像
* @param[in] CapsuleSize Capsule镜像大小
* @param[in] PrivateKey 私钥数据
* @param[in] KeySize 私钥大小
* @return EFI_STATUS 操作结果
*/
EFI_STATUS SignCapsule(
IN OUT UINT8 *Capsule,
IN UINTN CapsuleSize,
IN UINT8 *PrivateKey,
IN UINTN KeySize
) {
EFI_STATUS Status;
EDKII_CAPSULE *CapsulePtr;
UINT8 *DataToSign;
UINTN DataSize;
CapsulePtr = (EDKII_CAPSULE*)Capsule;
// 计算待签名数据大小 = FMP头部大小 + 固件数据大小
DataSize = sizeof(FMP_PAYLOAD_HEADER) + CapsulePtr->FmpHeader.PayloadSize;
DataToSign = (UINT8*)&CapsulePtr->FmpHeader;
// 调用PKCS#7签名函数,使用SecurityPkg提供的库
Status = Pkcs7Sign(
DataToSign, // 待签名数据
DataSize, // 数据大小
PrivateKey, // 私钥
KeySize, // 私钥大小
&CapsulePtr->AuthInfo // 输出签名信息
);
return Status;
}
3.3.2 签名验证实现
/**
* 验证Capsule镜像签名
*
* @param[in] Capsule 待验证的Capsule镜像
* @param[in] CapsuleSize Capsule镜像大小
* @param[in] PublicKey 公钥数据
* @param[in] KeySize 公钥大小
* @return EFI_STATUS 操作结果
*/
EFI_STATUS VerifyCapsuleSignature(
IN UINT8 *Capsule,
IN UINTN CapsuleSize,
IN UINT8 *PublicKey,
IN UINTN KeySize
) {
EFI_STATUS Status;
EDKII_CAPSULE *CapsulePtr;
UINT8 *SignedData;
UINTN SignedDataSize;
CapsulePtr = (EDKII_CAPSULE*)Capsule;
// 计算已签名数据大小
SignedDataSize = CapsuleSize - sizeof(EFI_CAPSULE_HEADER);
SignedData = (UINT8*)&CapsulePtr->AuthInfo;
// 调用PKCS#7验证函数
Status = Pkcs7Verify(
SignedData, // 已签名数据
SignedDataSize, // 数据大小
PublicKey, // 公钥
KeySize // 公钥大小
);
return Status;
}
3.3.3 常见问题
- 签名验证失败:检查公钥是否匹配,确保签名前数据未被修改
- 性能问题:在资源受限设备上,可预计算并缓存公钥哈希值
- 密钥管理:生产环境中应使用硬件安全模块(HSM)存储私钥
3.4 FMP协议实现
3.4.1 协议接口定义
FMP协议是Capsule更新的核心接口,必须实现以下五个函数:
// FMP协议结构体定义
EFI_FIRMWARE_MANAGEMENT_PROTOCOL gFmpProtocol = {
GetImageInfo, // 获取固件信息
GetImage, // 读取固件镜像
SetImage, // 更新固件镜像
CheckImage, // 验证镜像合法性
GetPackageInfo // 获取包信息
};
3.4.2 SetImage函数实现
/**
* 执行固件更新
*
* @param[in] This FMP协议实例
* @param[in] ImageIndex 镜像索引
* @param[in] Image 固件镜像数据
* @param[in] ImageSize 镜像大小
* @param[in] Progress 进度回调函数
* @param[out] LastAttemptStatus 上次尝试状态
* @return EFI_STATUS 操作结果
*/
EFI_STATUS EFIAPI SetImage(
IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
IN UINT8 ImageIndex,
IN CONST VOID *Image,
IN UINTN ImageSize,
IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress,
OUT UINT32 *LastAttemptStatus
) {
EFI_STATUS Status;
FMP_PRIVATE_DATA *Private;
EDKII_CAPSULE *Capsule;
// 初始化状态变量
*LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
// 获取私有数据
Private = FMP_PRIVATE_FROM_THIS(This);
// 加锁防止并发访问
Status = AcquireLockOrFail(&Private->Mutex);
if (EFI_ERROR(Status)) {
*LastAttemptStatus = LAST_ATTEMPT_STATUS_LOCK_FAILED;
return EFI_ACCESS_DENIED;
}
// 验证系统状态(电源、温度等)
Status = CheckSystemEnvironment();
if (EFI_ERROR(Status)) {
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ENVIRONMENT_ERROR;
ReleaseLock(&Private->Mutex);
return Status;
}
// 类型转换
Capsule = (EDKII_CAPSULE*)Image;
// 验证签名
Status = VerifyCapsuleSignature(
(UINT8*)Image,
ImageSize,
Private->PublicKey,
Private->PublicKeySize
);
if (EFI_ERROR(Status)) {
*LastAttemptStatus = LAST_ATTEMPT_STATUS_SIGNATURE_FAILED;
ReleaseLock(&Private->Mutex);
return EFI_SECURITY_VIOLATION;
}
// 检查版本号(防止降级)
if (Capsule->FmpHeader.Version <= Private->CurrentVersion) {
*LastAttemptStatus = LAST_ATTEMPT_STATUS_VERSION_ERROR;
ReleaseLock(&Private->Mutex);
return EFI_INVALID_PARAMETER;
}
// 执行依赖检查
Status = CheckDependencies(Capsule->FmpHeader.Dependencies,
Capsule->FmpHeader.DependencyCount);
if (EFI_ERROR(Status)) {
*LastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_ERROR;
ReleaseLock(&Private->Mutex);
return EFI_DEPENDENCY_ERROR;
}
// 调用设备特定的写入函数
Status = DeviceWriteFirmware(
Private->DeviceHandle,
Capsule->Payload,
Capsule->FmpHeader.PayloadSize,
Progress
);
// 更新状态并释放锁
if (EFI_ERROR(Status)) {
*LastAttemptStatus = LAST_ATTEMPT_STATUS_WRITE_FAILED;
} else {
Private->CurrentVersion = Capsule->FmpHeader.Version;
SaveCurrentVersion(Private->CurrentVersion);
}
ReleaseLock(&Private->Mutex);
return Status;
}
3.4.3 常见问题
- 并发访问:必须实现互斥机制,防止多线程同时更新
- 系统状态检查:更新前务必检查电源、温度等环境因素
- 版本控制:严格验证版本号,防止降级攻击
- 错误处理:详细记录错误状态,便于问题诊断
四、拓展篇:高级特性与最佳实践
4.1 固件存储结构解析
EDK II中的固件存储采用层次化结构,理解这一结构有助于优化Capsule更新实现:
固件卷结构:展示了固件存储的层次化组织,包含固件卷头、文件系统和多个文件,每个文件又包含多个节区
固件存储主要由以下层次构成:
- 固件卷(FV):顶层容器,包含多个固件文件
- 固件文件系统(FFS):管理固件文件的组织
- 固件文件:包含一个或多个节区
- 节区:实际存储代码或数据的单元
这种结构允许灵活的模块化设计,更新时可仅替换需要变更的节区,减少更新包大小。
4.2 依赖管理机制
复杂系统中,固件组件间存在依赖关系,如BIOS依赖特定版本的ME固件。FMP协议通过以下机制处理依赖:
/**
* 检查固件依赖关系
*
* @param[in] Dependencies 依赖列表
* @param[in] DependencyCount 依赖数量
* @return EFI_STATUS 操作结果
*/
EFI_STATUS CheckDependencies(
IN EFI_FIRMWARE_IMAGE_DEP *Dependencies,
IN UINT32 DependencyCount
) {
EFI_STATUS Status;
UINT32 Index;
EFI_GUID ImageTypeId;
EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
EFI_FIRMWARE_IMAGE_INFO *ImageInfo;
UINTN ImageInfoSize;
for (Index = 0; Index < DependencyCount; Index++) {
// 获取依赖设备的FMP协议
CopyGuid(&ImageTypeId, &Dependencies[Index].ImageTypeId);
Status = gBS->LocateProtocol(
&ImageTypeId,
NULL,
(VOID**)&Fmp
);
if (EFI_ERROR(Status)) {
return EFI_NOT_FOUND;
}
// 获取当前版本
ImageInfoSize = 0;
Status = Fmp->GetImageInfo(Fmp, 0, &ImageInfoSize, NULL);
if (Status == EFI_BUFFER_TOO_SMALL) {
ImageInfo = AllocatePool(ImageInfoSize);
Status = Fmp->GetImageInfo(Fmp, 0, &ImageInfoSize, ImageInfo);
if (!EFI_ERROR(Status)) {
// 检查版本是否满足最低要求
if (ImageInfo->Version < Dependencies[Index].MinimumVersion) {
FreePool(ImageInfo);
return EFI_DEPENDENCY_ERROR;
}
FreePool(ImageInfo);
}
}
}
return EFI_SUCCESS;
}
4.3 增量更新实现
为减少传输带宽和存储占用,可实现增量更新功能:
/**
* 创建增量更新包
*
* @param[in] OldImage 旧版本镜像
* @param[in] OldSize 旧版本大小
* @param[in] NewImage 新版本镜像
* @param[in] NewSize 新版本大小
* @param[out] DeltaImage 增量更新包
* @param[out] DeltaSize 增量包大小
* @return EFI_STATUS 操作结果
*/
EFI_STATUS CreateDeltaUpdate(
IN UINT8 *OldImage,
IN UINTN OldSize,
IN UINT8 *NewImage,
IN UINTN NewSize,
OUT UINT8 **DeltaImage,
OUT UINTN *DeltaSize
) {
EFI_STATUS Status;
UINT8 *DeltaData;
UINTN DeltaDataSize;
// 使用LZMA算法生成差分数据
Status = LzmaGenerateDelta(
OldImage,
OldSize,
NewImage,
NewSize,
&DeltaData,
&DeltaDataSize
);
if (EFI_ERROR(Status)) {
return Status;
}
// 构建增量Capsule
*DeltaSize = sizeof(EDKII_CAPSULE) + DeltaDataSize;
*DeltaImage = AllocatePool(*DeltaSize);
if (*DeltaImage == NULL) {
FreePool(DeltaData);
return EFI_OUT_OF_RESOURCES;
}
// 设置增量标志
(*DeltaImage)->FmpHeader.Flags |= FMP_PAYLOAD_FLAG_DELTA;
// 复制差分数据
CopyMem((*DeltaImage)->Payload, DeltaData, DeltaDataSize);
FreePool(DeltaData);
return EFI_SUCCESS;
}
4.4 性能优化建议
4.4.1 镜像压缩
使用LZMA或Deflate算法压缩固件镜像,减少传输时间和存储需求:
// 压缩固件镜像示例
Status = LzmaCompress(
RawImage, // 原始数据
ImageSize, // 原始大小
CompressedData, // 压缩后数据
&CompressedSize,// 压缩后大小
LZMA_DEFAULT_LEVEL // 压缩级别
);
4.4.2 并行验证
在多核系统上,可并行处理签名验证和依赖检查:
// 使用多线程并行处理
gBS->CreateEvent(
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
VerifySignatureThread,
(VOID*)Image,
&SignatureEvent
);
gBS->CreateEvent(
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
CheckDependenciesThread,
(VOID*)Image,
&DependencyEvent
);
// 等待所有线程完成
gBS->WaitForEvent(2, &EventArray[0], &Index);
4.4.3 分区更新
将固件分为多个独立分区,仅更新变更的分区:
// 分区更新示例
for (Index = 0; Index < PartitionCount; Index++) {
if (IsPartitionChanged(OldImage, NewImage, Index)) {
Status = UpdatePartition(Index, NewImage->Partitions[Index]);
if (EFI_ERROR(Status)) {
RollbackPartitions(Index); // 回滚已更新分区
return Status;
}
}
}
4.5 兼容性处理
4.5.1 多版本支持
处理不同硬件版本的兼容性:
// 硬件版本兼容性检查
EFI_STATUS CheckHardwareCompatibility(
IN UINT32 HardwareRevision,
IN UINT32 FirmwareVersion
) {
// 版本矩阵:硬件版本 -> 最低固件版本
static const struct {
UINT32 HardwareRev;
UINT32 MinFirmwareRev;
} CompatibilityMatrix[] = {
{0x100, 0x20230101}, // 硬件版本1.00最低支持固件20230101
{0x101, 0x20230601}, // 硬件版本1.01最低支持固件20230601
{0x200, 0x20240101}, // 硬件版本2.00最低支持固件20240101
};
UINTN Index;
for (Index = 0; Index < ARRAY_SIZE(CompatibilityMatrix); Index++) {
if (CompatibilityMatrix[Index].HardwareRev == HardwareRevision) {
if (FirmwareVersion >= CompatibilityMatrix[Index].MinFirmwareRev) {
return EFI_SUCCESS;
} else {
return EFI_INCOMPATIBLE_VERSION;
}
}
}
return EFI_UNSUPPORTED; // 不支持的硬件版本
}
4.5.2 回滚机制
实现安全回滚机制,应对更新失败:
// 固件更新与回滚流程
EFI_STATUS SafeUpdateFirmware(
IN UINT8 *NewImage,
IN UINTN ImageSize
) {
EFI_STATUS Status;
UINT8 *BackupImage;
UINTN BackupSize;
// 备份当前固件
Status = ReadCurrentFirmware(&BackupImage, &BackupSize);
if (EFI_ERROR(Status)) {
return Status;
}
// 执行更新
Status = WriteFirmware(NewImage, ImageSize);
if (EFI_ERROR(Status)) {
// 更新失败,回滚
WriteFirmware(BackupImage, BackupSize);
FreePool(BackupImage);
return Status;
}
// 验证更新结果
Status = VerifyFirmware(NewImage, ImageSize);
if (EFI_ERROR(Status)) {
// 验证失败,回滚
WriteFirmware(BackupImage, BackupSize);
FreePool(BackupImage);
return Status;
}
// 更新成功,释放备份
FreePool(BackupImage);
return EFI_SUCCESS;
}
4.6 安全最佳实践
4.6.1 最小权限原则
限制更新工具的权限,仅授予必要的硬件访问权限:
// 设置安全访问策略
EFI_STATUS SetSecurityAccessPolicy(
IN EFI_HANDLE DeviceHandle
) {
EFI_STATUS Status;
EFI_SECURITY_POLICY_PROTOCOL *SecurityPolicy;
// 获取安全策略协议
Status = gBS->LocateProtocol(
&gEfiSecurityPolicyProtocolGuid,
NULL,
(VOID**)&SecurityPolicy
);
if (EFI_ERROR(Status)) {
return Status;
}
// 设置最小权限策略
return SecurityPolicy->SetPolicy(
SecurityPolicy,
DeviceHandle,
SECURITY_POLICY_UPDATE_ONLY, // 仅允许更新操作
NULL,
0
);
}
4.6.2 审计日志
记录所有更新操作,便于安全审计:
// 记录更新日志
VOID LogUpdateEvent(
IN EFI_STATUS Status,
IN UINT32 OldVersion,
IN UINT32 NewVersion,
IN EFI_GUID *ImageTypeId
) {
UPDATE_LOG_ENTRY LogEntry;
// 填充日志信息
LogEntry.Timestamp = GetCurrentTime();
LogEntry.Status = Status;
LogEntry.OldVersion = OldVersion;
LogEntry.NewVersion = NewVersion;
CopyGuid(&LogEntry.ImageTypeId, ImageTypeId);
// 写入日志存储
WriteLogEntry(&LogEntry);
}
五、总结与展望
Capsule更新技术作为UEFI规范的重要组成部分,为嵌入式设备提供了安全、可靠的固件更新解决方案。本文从问题分析到方案设计,再到具体实现和高级特性,全面介绍了基于EDK II的Capsule开发技术。
随着物联网和边缘计算的发展,固件更新技术将面临新的挑战和机遇:
- 远程管理:结合Redfish等协议实现跨网络的固件管理
- AI辅助:利用机器学习预测更新风险,优化更新策略
- 量子安全:抗量子密码算法在固件签名中的应用
- 轻量化:针对资源受限设备的微型化Capsule实现
开发者应持续关注UEFI规范更新和EDK II社区发展,不断优化固件更新方案,确保设备在全生命周期内的安全性和可靠性。
通过本文介绍的技术和最佳实践,开发者可以构建符合工业标准的固件更新系统,为嵌入式设备提供坚实的安全保障。建议结合实际硬件平台,逐步实现基础功能、安全机制和高级特性,最终构建完整的固件更新生态系统。
GLM-5智谱 AI 正式发布 GLM-5,旨在应对复杂系统工程和长时域智能体任务。Jinja00
GLM-5-w4a8GLM-5-w4a8基于混合专家架构,专为复杂系统工程与长周期智能体任务设计。支持单/多节点部署,适配Atlas 800T A3,采用w4a8量化技术,结合vLLM推理优化,高效平衡性能与精度,助力智能应用开发Jinja00
jiuwenclawJiuwenClaw 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。Python0216- QQwen3.5-397B-A17BQwen3.5 实现了重大飞跃,整合了多模态学习、架构效率、强化学习规模以及全球可访问性等方面的突破性进展,旨在为开发者和企业赋予前所未有的能力与效率。Jinja00
AtomGit城市坐标计划AtomGit 城市坐标计划开启!让开源有坐标,让城市有星火。致力于与城市合伙人共同构建并长期运营一个健康、活跃的本地开发者生态。01
AntSK基于.Net9 + AntBlazor + SemanticKernel 和KernelMemory 打造的AI知识库/智能体,支持本地离线AI大模型。可以不联网离线运行。支持aspire观测应用数据CSS01
