当前位置: 首页 > news >正文

至强服务器BIOS/UEFI驱动开发笔记

至强服务器BIOS/UEFI驱动开发笔记

  • 驱动开发基础
    • Hello UEFI Driver 项目
      • 选择项目位置
      • 初始化驱动代码文件结构
      • 驱动程序入口和基本功能
      • 导入AMI工程
      • AMI平台Hello UEFI Driver 编译问题
      • 测试结果
    • 打印设备列表
      • 继续开发`HelloWorldSupported`函数
      • 依赖配置
      • 使用脚本编译
      • 编译测试此DXE驱动模块
      • 改进`HelloWorldSupported`函数
      • 问题
    • 继续实验
  • AMI实战
    • SDL和CIF
      • 以界面方式增加工程
      • 以代码方式增加工程
      • RoboVeb
      • 踩过的坑
    • AMI VEB构建技巧
      • AMI VEB命令行构建
      • AMI构建单个工程
    • AMI App开发
      • 关键函数和协议
      • 主要步骤
      • HellWorld.c
      • 测试结果
    • PCI Driver开发
      • 基于EDKII工程的HelloWorldDxe
      • 测试结果
      • 继续开发`Supported`函数
      • 测试结果
    • AMI PCI 驱动开发
      • 新增加的DXE驱动放到和BIOS固件的哪里?
      • U盘和键盘全消失等问题
      • 矛盾的根源
  • UDK2015
    • 编译环境
      • Windows编译环境
      • OvmfPkg
      • VS2008安装问题
      • UDK2017
      • OVMF 2015
    • 经典的DXE驱动案例
      • VGA驱动
      • 仿照VGA驱动修改MyPciDxe
      • 以上代码迁移到服务器
    • 驱动的其它属性
      • 工具类函数
      • 简化的Supported函数与初步的Start函数
      • 驱动的名字

  1. 实验使用的CPU架构Broardwell。
  2. EFI App的构建过程实际上先构建可以在OS上运行的动态库/可执行文件,然后利用PE32+工具改为UEFI运行。
  3. UEFI基于GObject(https://docs.gtk.org/gobject/)用C模拟OOP或C++。
  4. JRE 1.7安装目录整个拷贝到VisualeBios.exe所在目录,并改名为jre。则Visual Bios的启动不需要安装JRE。卸载JRE 1.7验证。
  5. UEFI编译系统强制要求函数的局部变量统一声明在函数体的头部,否则报错。
  6. GRUB运行在BDS阶段,因为GRUB运行期间未调用ExitBootServices方法,实际调用此方法的是OS Loader。
  7. UEFI环境特点
    1. 支持X86、X64、ARM等平台
    2. 单核CPU,没有线程,没有进程
    3. 没有抢断/优先级
    4. 没有中断,唯一的路径是定时器
    5. 模块内部通讯通过Protocols(协议)和Events(事件)
    6. C语言编程(原文:Programming is done through C language,实际上有汇编)
  8. 包的声明用dsc,模块的声明用inf,模块的依赖用dec

驱动开发基础

Hello UEFI Driver 项目

用UEFI Shell装载驱动进行测试。受载板平台限制,测试工程放在AMI项目里。

选择项目位置

与UEFI App开发不同,驱动代码所处项目架构应当与硬件构成映射关系。如果驱动代码与驱动硬件不在相同架构,则开发者需要手动处理固件布局才成封装成为正确的固件。我亲自踩坑证明这个说法:
在这里插入图片描述
关键错误消息:Build\GetPpiName.c(1) : fatal error C1083: Cannot open include file: '/RELEASE_MYTOOLS/PpiTableIA32.c': No such file or directory。不知道驱动项目存放须按规定的开发者会认为这个问题是玄学问题,怎么生成的代码找不到生成的代码呢?建议参照下图和项目结构选择合适的UEFI驱动存储放置:

在这里插入图片描述
根据驱动目标选择合适的项目位置。本示例的目标为USB键盘驱动,因此选择MdeModulePkg/Bus。当然,如果你已对BIOS固件布局已非常清楚,你可以随意。

初始化驱动代码文件结构

新建目录HelloWorldDxe,新建HelloWorldDxe.inf,代码如下:

[Defines]INF_VERSION = 0x00010005BASE_NAME=HelloWorldDxeFILE_GUID = de296c9d-8bac-08bc-ac6d-db2998aff781MODULE_TYPE = UEFI_DRIVERVERSION_STRING = 0.1ENTRY_POINT = DriverMain[Sources]HelloWorldDxe.c[Packages]MdePkg/MdePkg.decMdeModulePkg/MdeModulePkg.dec[LibraryClasses]BaseLibBaseMemoryLibDebugLibMemoryAllocationLibPrintLibUefiDriverEntryPointUefiLib

驱动程序入口和基本功能

新建HelloWorldDxe.c,代码如下:

#include <Uefi.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/ComponentName2.h>
#include <Protocol/ComponentName.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>#define HELLOWORLD_VERSION 0x10EFI_STATUS EFIAPI HelloWorldStart(IN EFI_DRIVER_BINDING_PROTOCOL* This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath
)
{EFI_STATUS status = EFI_SUCCESS;Print(L"[HelloWorldStart] HelloWorld driver started.\n");return status;
}EFI_STATUS EFIAPI HelloWorldSupported(IN EFI_DRIVER_BINDING_PROTOCOL* This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath
)
{EFI_STATUS status = EFI_SUCCESS;Print(L"[HelloWorldSupported] HelloWorld driver supported.\n");return status;
}EFI_STATUS EFIAPI HelloWorldStop(IN EFI_DRIVER_BINDING_PROTOCOL* This,IN EFI_HANDLE Controller,IN UINTN NumberOfChildren,IN EFI_HANDLE* ChildHandleBuffer
)
{EFI_STATUS status = EFI_SUCCESS;Print(L"[HelloWorldStop] HelloWorld driver stopped.\n");return status;
}EFI_DRIVER_BINDING_PROTOCOL g_helloworld_driver_binding = {HelloWorldSupported,HelloWorldStart,HelloWorldStop,HELLOWORLD_VERSION,NULL,NULL
};EFI_STATUS EFIAPI DriverMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE* SystemTable
)
{EFI_STATUS status = EFI_SUCCESS;status = EfiLibInstallDriverBindingComponentName2(ImageHandle,SystemTable,&g_helloworld_driver_binding,ImageHandle,NULL,NULL);ASSERT_EFI_ERROR(status);return status;
}

导入AMI工程

  1. 在ModuleExplorer视图中依次展开:ComponentsCoreMdeModulePkgLibraryInstances,右键单击,选择弹出菜单Add INF Module。关于Select EDK Project Root选项,选择结果无论是否正确,导入结果都存在错误。下文第2步和第3步就是纠错。
    在这里插入图片描述
  2. 找到导入的工程,移动生成的sdl文件到inf文件所在目录,并更名。
    在这里插入图片描述
  3. 按下图所示修改MdeModulePkg\Library\LibraryInstances.cif
    在这里插入图片描述
    在这里插入图片描述
  4. 关掉VeB软件,重新打开。编译报错:<work root>\Build\GrangevillePkg\RELEASE_MYTOOLS\X64\MdeModulePkg\Bus\HelloWorldDxe\HelloWorldDxe\DEBUG\AutoGen.h(16) : fatal error C1083: Cannot open include file: 'Uefi.h': No such file or directory。原因是HelloWorldDxe.inf有错。具体错误是Package依赖错误地写成dsc,正确的做法是写成dec

AMI平台Hello UEFI Driver 编译问题

  1. AMI的工程不能运行EDKII提供的命令
    在这里插入图片描述

  2. VeB不允许编译单个驱动

    这是假象。实际原因是VeB没有把导入INF生成的sdl文件放到inf所在目录。解决办法是移动sdl文件到inf文件所在目录并修正上级cif文件中的错误。

在这里插入图片描述

测试结果

  1. UEFI Shell运行运行load指令可见大量的HelloWorld输出,说明UEFI驱动管理支持一个设备绑定多个驱动,不会因为某个设备已存在绑定的驱动而停止匹配新驱动。UEFI如何选择调用哪个驱动呢?驱动的版本的如何在驱动选择中发挥作用的?
  2. 服务器启动慢问题存在新证据,证据指向问题发生在SEC或者PEI阶段。下图右边神秘的数字,在HelloWorldDxe集成进BIOS固件之前不确定它显示时CPU执行阶段。现在可以证明处于DXE阶段。那么,从通电到HelloWorldDxe产生输出的大约10秒钟时间,很可能都处于SEC和PEI阶段。现在没有确定串口设备未初始化造成的HelloWorldDxe无输出的时间。

在这里插入图片描述

  1. EFI Shell启动时会再执行一次设备驱动管理过程。
    在这里插入图片描述

  2. UEFI驱动管理在得到Supported函数的返回结果为EFI_SUCCESS后立即调用Start函数。上图Supported输出与Start输出成对出现无间断说明这一点。

  3. 任何驱动应在EFI Shell中先load试运行。否则驱动出错造成很大的麻烦。DXE出错的结果是载板变砖头,救砖的办法可能只有把FLASH从电路板上焊下来,烧好程序后再焊上去。

  4. 固件烧录与固件运行时不同。以下截图的实验:

    1. A版本包含HelloWorldDxe驱动,B版本与2023XXXX版本相同,唯一的区别是重新编译。编译环境、工具完全相同。
    2. 固件升级到A版本,重启后驱动绑定过程出现大量的HelloWorld打印
    3. 固件升级到B版本,重启时控制台出现大量的HelloWorld打印
      在这里插入图片描述

打印设备列表

继续开发HelloWorldSupported函数

目标:以字符串形式输出所有Device关键字,尝试寻找设备特征,在特征中搜寻键盘设备。代码如下:

EFI_STATUS EFIAPI HelloWorldSupported(IN EFI_DRIVER_BINDING_PROTOCOL* This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath
)
{EFI_STATUS status = EFI_SUCCESS;EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* device2txt = NULL;CHAR16* device_path = NULL;status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (void**)&device2txt);if (EFI_ERROR(status)){Print(L"[HelloWorld Driver] LocateProtocol result: %d\n", status);return status;}device_path = device2txt->ConvertDeviceNodeToText(RemainingDevicePath, TRUE, TRUE);Print(L"[HelloWorld Driver] device: %s\n", device_path);return EFI_UNSUPPORTED;
}

依赖配置

这里采用VeB配置。

  1. 新增外部依赖DevicePathLib
    在这里插入图片描述
  2. 新增Protocols依赖gEfiDevicePathProtocolGuidgEfiDevicePathToTextProtocolGuid
    在这里插入图片描述

使用脚本编译

脚本编译的目的是为CI做准备。

@echo off
chcp 65001
title AMI UEFI Build Tool
echo Any question can be sent to zhtqs8@163.com
set CCX86DIR=<work root>\software\Aptio_5.x\x86\x86
set CCX64DIR=<work root>\software\Aptio_5.x\x86\amd64
set TOOLS_DIR=<work root>\software\Aptio_5.x\BuildTools
set PATH=%PATH%;<work root>\software\Aptio_5.x\x86\x86
set PATH=%PATH%;<work root>\software\Aptio_5.x\amd64
set PATH=%PATH%;<work root>\software\Aptio_5.x\x86
set PATH=%PATH%;<work root>\software\Aptio_5.x\BuildTools
set PATH=%PATH%;<work root>\software\Aptio_5.x\BuildTools\Bin\Win32
set PATH=%PATH%;<work root>\software\Aptio_5.x\VisualeBios\jre\bin\
cd <work root>
cmd /k

运行指令make rebuild,结果如下:
在这里插入图片描述

编译测试此DXE驱动模块

  1. 编译,确认编译输出

    - Done -
    Build end time: 17:07:12, Sep.15 2023
    Build total time: 00:00:07
    
  2. 运行load HelloWorldDxe.efi,确认结果

    RemainingDevicePath的值始终为:F3 EE 00 F0。期望的结果为不同的设备不同的值。UEFI Driver Writer’s Guide大部分示例显示,Supported适用的流程是开发者用期望的Protocol尝试打开。结果成功就是支持,结果失败就是不支持。很多示例㫫示Start还会把这个逻辑再运行一次。

    RemainingDevicePath不一定指设备,它还兼顾方便开发者在当前设备下挂载子设备。另外:

相关文章:

至强服务器BIOS/UEFI驱动开发笔记

至强服务器BIOS/UEFI驱动开发笔记 驱动开发基础Hello UEFI Driver 项目选择项目位置初始化驱动代码文件结构驱动程序入口和基本功能导入AMI工程AMI平台Hello UEFI Driver 编译问题测试结果打印设备列表继续开发`HelloWorldSupported`函数依赖配置使用脚本编译编译测试此DXE驱动…...

Linux:Termius连接本地虚拟机与虚拟机快照

Termius连接本地虚拟机与虚拟机快照 1. Termius连接本地虚拟机2. 虚拟机快照与还原2.1 设置快照以及恢复 附录 1. Termius连接本地虚拟机 ifconfig -a 查看配置 连接成功 2. 虚拟机快照与还原 在学习阶段我们无法避免的可能损坏Linux操作系统。 如果损坏的话&#xff0c;重新…...

高校教务系统登录页面JS分析——四川大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密码加…...

Kafka SASL认证授权(四)认证源码解析

Kafka SASL认证授权(四)认证源码解析。 官网地址:https://kafka.apache.org/ 一、认证流程 在了解kafka网络模型的基础上,了解它的认证流程: ApiVersionsRequest->SaslHandshakeRequest->a series of SASL client and server tokens corresponding to the mechani…...

软件测试学习(一)基础概念、实质、说明书测试、分类、动态黑盒测试

目录 软件测试概念、背景 软件测试员究竟做些什么 大多数软件测试员应该具备的素质 软件测试的实质 完全测试程序是不可能的 测试无法显示潜伏的软件缺陷 并非所有软件缺陷都要修复 软件测试员在产品小组中不受欢迎 术语&#xff1a;精准和准确 产品说明书的测试技术…...

在fastapi中实现异步

在FastAPI应用中使用异步特性可以提高并发性能&#xff0c;但如果您要调用的模型是同步的&#xff0c;可能会导致阻塞。为了实现异步处理&#xff0c;您可以将阻塞的操作委托给线程池或进程池&#xff0c;以便异步执行。 以下是一种基本方法来实现异步处理图片识别任务&#x…...

js数组去重

在JavaScript中&#xff0c;有很多方法可以用来去除数组中的重复项。以下是一些常见的方法&#xff1a; 方法一&#xff1a;使用Set Set是ES6中的新数据类型&#xff0c;它只存储唯一值。因此&#xff0c;我们可以利用这一特性来去重。 let array [1, 2, 3, 2, 1, 4, 3, 5,…...

【前端】根据后端返回的url进行下载并设置文件下载名称

在我们项目当中存储文件是存储到厂商的服务器上的&#xff0c;然后厂商返回一个可以直接下载url地址&#xff0c;但是前端使用这个url下载的时候永远都是保存一个名字&#xff0c;这时候我们就需要设置文件保存的名称&#xff0c; 那么如何实现呢&#xff1f;使用了fet…...

《视觉SLAM十四讲》公式推导(一)

文章目录 CH3 三维空间刚体运动CH3-1 旋转矩阵的推导CH3-2 旋转矩阵是正交矩阵的证明CH3-3 变换矩阵的逆的推导CH3-4 罗德里格斯公式推导 CH3 三维空间刚体运动 CH3-1 旋转矩阵的推导 &#xff08;1&#xff09;二维空间中的旋转矩阵 易得 { x ′ ∣ O P ′ ∣ c o s ( θ …...

简单好用的解压缩软件:keka 中文 for mac

Keka是一款功能全面、易于使用的文件压缩和解压缩软件&#xff0c;为Mac用户提供了便捷的文件管理工具。它支持多种压缩格式&#xff0c;具有快速解压和强大的压缩功能&#xff0c;让您能够轻松地处理各种文件压缩需求。 隐私非常重要 安全共享只需设置密码并创建高度加密的文…...

【UE 插件】UE4 虚幻引擎 插件开发(带源码插件打包、无源码插件打包) 有这一篇文章就够了!!!

目录 0 引言1 快速入门1.1 新建插件的前提1.2 创建插件步骤1.3 打包插件 2 无源代码的插件制作3 插件详细介绍3.1 插件的使用方法3.1 UE 预置插件模版3.1.1 空白3.1.2 纯内容3.1.3 编辑器独立窗口3.1.4 编辑器工具栏按钮3.1.5 编辑器模式3.1.6 第三方库3.1.7 蓝图库 3.2 插件中…...

C# CodeFormer 图像修复

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms;namespace 图像修复 {p…...

Android Studio的笔记--HttpURLConnection使用GET下载zip文件

HttpURLConnection使用GET下载zip文件 http get下载zip文件MainActivity.javaAndroidMainfest.xmlactivity_main.xmllog http get下载zip文件 MainActivity.java 用HttpURLConnection GET方法进行需注意&#xff1a; 1、Android 9及以上版本需要设置这个&#xff0c;否则会有…...

phantom3D模体

phantom是人头模型&#xff0c;分为2D和3D两种&#xff0c;matlab中可直接调用phantom(size)生成2D数据&#xff0c;如图1&#xff0c;而三维需要对应函数文件&#xff0c;下载&#xff1a;3D 图1 2D phantom 3D模体为一个椭球体&#xff0c;只能生成xyz三个方向相同维度的模…...

贪心算法解决批量开票限额的问题

具体问题&#xff1a;批量订单开票 限制&#xff1a;1.开最少的张数 2.每张限额10w # 贪心算法 def split_invoice_by_item(items):items_sorted sorted(items, keylambda x: x.price, reverseTrue)invoices []for item in items_sorted:# 尝试将商品加入已有的发票中added …...

Unity后台登录/获取数据——BestHTTP的使用Get/Post

一、使用BestHTTP实现登录功能&#xff08;Post&#xff09; 登录具体的步骤如下&#xff1a; 1&#xff1a;传入你的用户名和密码&#xff0c;这是一条包括链接和用户名密码的链接 2&#xff1a;使用BestHTTP的Post功能将链接传到服务器后台 3&#xff1a;后台拿到了你传送…...

【Windows日志】记录系统事件的日志

文章目录 一、概要二、Windows日志介绍 2.1 应用程序日志2.2 系统日志2.3 安全日志 三、查看与分析日志四、常见事件ID 4.1 登录事件 4.1.1 4624登陆成功4.1.2 4625登陆失败 4.2 特权使用4.3 账户管理事件4.4 账户登录事件5.2 事件ID汇总 一、概要 Windows主要有以下三类日…...

物联网开发学习笔记——目录索引

什么是物联网&#xff1f; 物联网的英文名称是Internet of Things。IoT则是Internet of Things的缩写。 通俗地说&#xff0c;就是把设备与互联网连接起来&#xff0c;进行信息交互。 目录 一、开发环境配置 工欲善其事必先利其器&#xff0c;首先是开发环境配置。 开发环…...

Prometheus:优秀和强大的监控报警工具

文章目录 概述Prometheus的底层技术和原理数据模型数据采集数据存储查询语言数据可视化 Prometheus的部署Prometheus的使用配置数据采集目标查询监控数据设置警报规则 查看数据可视化总结 概述 Prometheus是一款开源的监控和警报工具&#xff0c;用于收集和存储系统和应用程序…...

Appium

# 获取元素和屏幕截图 echo on adb shell uiautomator dump /sdcard/app.uix adb pull /sdcard/app.uix F:\APP\app.uixadb shell screencap -p /sdcard/app.png adb pull /sdcard/app.png F:\APP\app.png卸载appium npm uninstall appium -g 重新安装appium npm install -g a…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...