搜索
bottom↓
回复: 25
打印 上一主题 下一主题

【分享】简单粗暴Cortex-M23/M33

[复制链接]

出0入296汤圆

跳转到指定楼层
1
发表于 2020-10-21 22:18:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Gorgon_Meducer 于 2020-10-21 22:25 编辑


【说在前面的话】

自从Arm在2016年的十月发布两款Armv8-M架构的新处理器Cortex-M23和Cortex-M33以来,已经过去了3年多,而市面上基于这两款处理器的微控制器产品也刚刚才崭露头角。
很多才刚刚通过开发板熟悉Cortex-M0/M0+/M3/M4处理器的童鞋可能心中又要飘过弹幕:
谁TM告诉我,这个M23和M33是什么鬼?
从个位数一下蹦到两位数了喂!
前面十几位兄弟怎么了?喂!
别说跟M3有啥关系,这以后下第n代是不是就该叫2333333了?

该来的总会来,那么如何简单粗暴的理解这两个全新的处理器呢?以下是傻孩子独家特别提供的的无责任囫囵吞枣公式:

Cortex-M23 =
Cortex-M0/M0 + 硬件除法器 + 性能提升 +
专门的栈溢出硬件检测+

指令集不可忽略的小动作 +
安全扩展(TrustZone for Armv8-M) +
MPU开发者模型的友好化改进

Cortex-M33 =
Cortex-M3/M4 + 性能提升 +
专门的栈溢出硬件检测+
指令集不可忽略的小动作 +
安全扩展(TrustZone for Armv8-M)+
MPU开发者模型的友好化改进

再简单点说就是无敌增强版的“M0/M0+,M3/M4”加“安全扩展”。有人说,Armv8-M的主要功能就是为Cortex-M家族引入TrustZone,这么看来也是不无道理的。
1.    增强版的Cortex-M0/M0+


根据官方的说法,Cortex-M23实现的是Armv8-M架构的Baseline子架构,我们不妨理解为手机里面的“入门级”产品。



Cortex-M23从定位上也非常直接,就是给Cortex-M0/M0+增加个安全扩展。因此,实际上所有为Cortex-M0/M0+编译生成的二进制代码基本上都可以“无修”的在Cortex-M23/M33上执行——除非你原本的代码使用了MPU。此外Cortex-M23居然配备了硬件除法器,这无疑在原本Cortex-M0和Cortex-M0+主打的8位/16位市场上把“基本配置”又提升了一个档次。


指令集上,Cortex-M23师承Armv6-M,除了支持“安全扩展”所必须的一系列指令之外,这款入门级产品还做了一个“不可忽略的小动作”——也就是说,除了Cortex-M33以外,Cortex-M23也可以通过很小的代价支持“暗代码(eXecute Only Memory, XOM)”。



什么是暗代码呢?和“暗物质”只能理论上知道它存在却很难探测到类似——“暗代码”是一类只能由处理器执行(取指令)却根本无法用任何形式读取机器码(OPCODE)内容的程序——也就是人们常说的XO(eXecute-Only)代码。“暗代码”并不是依靠内核来实现的,但却需要编译器和内核共同努力才能支持。这是因为XOM本质上是芯片厂家在地址空间上划分出的一段特殊区域——只能由处理器取指令、用于代码的运行(Instruction Fetch),而不能进行普通的数据访问(Data Access)。这就要求“暗代码”里不能直接保存任何常数——它们必须编码到指令里面——成为指令的一部分,以指令编码中的立即数形式存在。

Armv6-M的指令集大部分都是16位的Thumb指令,16位的指令可以用于编码的立即数的二进制位长度可想而知——少得可怜。Armv7-M由于引入了32位的Thumb2指令集,从而极大增强了指令携带立即数的能力。为了将这一能力引入Armv8-M的Baseline指令集,MOVT和MOVW这两个可以分别携带32位立即数“高、低16位”的指令就被特别加入到Cortex-M23所使用的指令集中。考虑到Armv8-M 强调信息安全,“暗指令”对固件的保护有多大的分量,可想而知。



结论:Cortex-M23——这个M0+不简单。

2.    增强版的Cortex-M3/M4


相对Cortex-M3/M4来说,Cortex-M33在性能上有了提升并不是什么意料之外的事情,不提也罢。值得说明的是,从城里来的Cortex-M7在性能上仍然可以"甩其他Cortex-M土包子几条街"——6级流水线和3级流水线的差别可是"三缸夏利和六缸宝马之间的差距"所不能比拟的!(认真脸)。






3.     ARMv8-M是个知错就改的好少年


       我不知道有多少人真正用过Armv7-M,也就是Cortex-M3/M4的MPU——简单说就是个以Region为单位来修改Memory属性的系统级外设。原本设计的时候想法很简单,一个Region,给个大小(Size)给个基地址(Base Address),再给个属性(Memory Attribute),一使能,就工作了,很简单,很Happy。然而,出于优(pi)化(gu)内(jue)核(ding)面(nao)积(dai)的原因,Region地址范围的设定被人为加入了一个限定:

基地址(Base Address)必须对齐(Aligned with)到它的尺寸(Size),而且尺寸必须是2的整数次方(还必须大于4次方)。

举个例子:一个Region大小为512K,那么基地址必须是512K的整数倍……如果你还不能理解这个问题蛋疼的点在哪里,设想一个任意大小的Region该怎么设定,比如,一个234K大小的Memory该咋办?——还能咋办,用多个Region组合出来呗。正是这个蛋疼的限制,导致几乎没有什么RTOS可以很好的使用MPU,也罕有身边的项目把MPU这么骨感的现实应用的如理想般美好。
那么Armv8-M做了什么呢?他更正了这一蛋疼的设定,即:Region的设置由“基地址+尺寸”进化为“起始地址+终止地址”,除了这两个地址都必须是32字节的整倍数的要求外,再也没有变态的关于“基地址必须是Region大小的整倍数”这样的限定。是不是突然觉得眼前一亮,是不是突然发现了一个宝藏?MPU顿时好玩起来。

结论:ARMv8-M的MPU是个好同志,士别三日当刮目相看
4.      安全扩展(Trust Zone for ARMv8-M)又如何简单的理解呢?


  • TrustZone for Armv8-M TrustZone 是什么关系

首先 “TrustZone for ARMv8-M” 是一个 专有名词,它和 Cortex-A 系列上引入的 “TrustZone”  具有以下的共同特点:



    • 都是销售用语

    • 都高举 TrustZone 大旗

    • 仅在纯理论层面共享一些抽象的模型,用于理解和设计嵌入式信息安全

    • 安全效果基本相同



它们至少在以下几个方面存在差异:



    • 架构定义完全不同

    • 技术实现完全不同

    • 执行效率完全不同

    • 各类成本完全不同

    • 使用方法完全不同

    • ……



    (其实,我个人觉得TrustZone for ARMv8-M 比 TrustZone 要先进。这当然不仅仅因为“我是 Cortex-M 阵营的”,更因为我觉得“用脚趾头想都知道,TrustZone for ARMv8-M 是后来者,当然有充分的理由比 TrustZone 先进啦。”)


  • 功能安全Safety 信息安全Security

打个比方,你买了一个智能灯泡,那么对于这个产品来说:



    • 用于保护灯泡不会因为电压过高、过低或者电流过大而损坏的保护电路,实现的就是功能安全,用英文单词 Safety 表示;

    • 用于保护你家灯泡不被隔壁老王控制,或者保护你家灯泡上的摄像头(如果有的话)以及麦克风(如果有的话)不被隔壁老王窃听的设计,实现的就是信息安全,用英文单词 Security 表示。



    再进一步总结来说,你可以简单粗暴的认为:


Safety 保证的是系统在各种不同(通常是极端)的环境下,都拥有正常的工作逻辑;或者说所提供的功能和服务都是正常的;如果环境太极端,就进入某种保护状态,以避免为用户提供错误或者危险的服务。——Safety 对抗的是来自环境的挑战


Security 保证的是在人为破坏的情况下,系统能有效地检测到攻击行为、确保有效信息不会被泄露、系统不会被未经授权的用户所控制—— Security 对抗的是隔壁老王以及各类隐藏在网络上的云老王

NOTE:关于Safety和Security的关系,更多详细信息,请参考文章《大白话说嵌入式安全(1)》

实际上 Security 必然是通过硬件和软件实现的,只有它的功能、逻辑得到充分的保护,才能有效地对抗攻击者。因此,“老王”们通常利用攻击系统的Safety,也就是功能安全,来试图破构建其上的 信息安全—— Security 是建立在 Safety 之上的,谈论 Security 的时候必然离不开Safety——这也是大家常常混淆着两个概念的原因。同时,我们不能因为它们有单方向的依存关系(Security 依赖于 Safety,反过来却不一定),就认为我们可以不分场合的将它们混用。


  • 为什么人们突然这么重视 Security

过去,大多数微控制器的项目,1)本地团队自己就可以完成了,2)往往不用跟第三方合作,3)也不需要大规模的连接到网络上,4)模块化的目的单纯为了快速开发,因而过去的系统在信息安全上的问题并不是非常突出,基本上就停留在克隆抄袭这个层面上。

然而,除了克隆抄袭这个永恒的原因以外,1)IoT 的到来使得更多的嵌入式设备无法孤立的存在,因此通信安全变得突出;2)生态系统和平台的概念深入人心,单一的本地团队越来越无法独立的完成整个项目,因而与第三方合作成为常态,这就必然导致第三方黑盒子模块的引入,运行时的系统信息安全变得突出;3)商业模式的建立鼓励多方合作,模块化只会帮助IP更高效的被使用而并不天然保护知识产权,更进一步说,单纯的模块化技术并不能保证厂商从最终产品的收益中获得持续稳定的收益。


基于以上原因,简单说就是:因为要与老王合作卖煎饼,我提供设备,老王提供服务,我即担心隔壁老王“你懂的”原因窥探你的财产,同时又希望老王不至于偷了我设备的图纸、把我一脚踢开自己赚钱,所以Security 在 IoT 时代是必须的


  • 一句话抓住安全技术的精髓
  • Security 技术实现的核心是 隔离(Isolation )。

  • Isolation 在时间上的实现就是把处理器时间按照不同的安全级别进行分配——建立所谓的安全的运行(Secure )和非安全(Non-secure)的运行、或者是不同安全级别的运行模式。

  • Isolation 在空间上的实现就是各类对存储器以及外设访问的权限控制(Access Attribution Management)。



值得特别说明的是,对访问权限的控制是一个通用的工具(Tool),你既可以用它来实现各类资源的分配,比如操作系统中的资源管理;也可以用它来实现信息安全。这并不是说,资源管理是信息安全的一部分,也不能说信息安全就是通过资源管理来实现的——这种说法最要命的地方就是“似是而非”,因而迷惑了很多人。如果有人跟你讨(争)论这个,我的建议是:你跟他打赌吵架都没用,自己心里明白就可以了——“对对对,诸葛孔明是两个人”。


  • TrustZone for ARMv8-M 之 程序员不得不知道的技术

既然我们就是要简单粗暴,那么就不用扯那么多犊子。ARMv8-M Security Extension 的本质仍然是实现 Isolation。那么要达到怎样的效果呢?




    • CPU在时间上被划分成了两个运行状态:Secure state Non-Secure state

    • 空间上,4GB 的地址空间被划分为两个阵营:Secure MemoryNon-Secure Memory

    • 保存在Secure Memory 上的代码就是 Secure Code,它必须在 Secure State下运行;保存在Non-Secure Memory上的代码就是Non-Secure Code,它必须在Non-Secure State下运行——简单说就是“你是你、我是我”。

    • Secure Code可以访问所有的数据。


Non-Secure Memory:  你瞅啥?

Secure Code: 瞅你咋地?

Non-Secure Memory: 不……咋地……你……你瞅我我也不知道……


    • Non-Secure Code只能访问Non-Secure Memory上的数据;

Secure Memory:  你瞅啥?

Non-secure Code: 瞅你咋地?

Secure Memory: 我老大你认识不?

Secure Fault: 是谁在我地盘上横啊?

Non-secure Code: 哥,这……这误会阿……哥

Secure Fault: 误会?Secure Memory是你来得地er么?你!这!就!是!搞!事!来啊,拖走!

Non-secure Code: 哥……哥,消消气,你看啊,这地盘也不是你划分的,咱还不得找管事er的人说理不是?



    • Secure Memory 和 Non-Secure Memory是由 Secure Attribution UnitImplementation Defined Attribution Unit 共同决定的。
      你可以把他们理解为一对夫妻:男人 IDAU 主外(由芯片厂商定义),女主内(SAU,是 Secure Code 在运行时刻通过寄存器来配置的)。对Cortex-M23/33的每一个总线访问,SAU和IDAU都会根据目标地址对比自己所掌握的信息进行投票,仲裁的优先顺序依次是:Non-Secure, Non-Secure-CallableSecure。谁更接近Secure谁说了算。

SAU: 呦~ Fault哥,又抓到人啦?

Secure Fault: 可不,Secure Memoy说这小子是Non-Secure的。

Secure Memory:  姐,你可要帮我做主,这小子居然瞅了我一眼!

Non-secure Code: 我哪知道……

SAU/IDAU : 小子,说其他没用,地址多少?

Non-secure Code:  我……来自非安全域……

SAU: 呦,Non-Secure的人啊,你知道你偷看的地址属于Secure的么?你就瞅人家?你也不照照镜子,Secure Memory是你能瞅的么?

Non-secure Code:我哪知道它是Secure Memory啊?

SAU/IDAU : 我说是就是!

Non-secure Code:哦……哦……(低头不敢看)

SAU/IDAU:Fault 哥,去查查老祖宗立下的规矩,该汇报就汇报,该RESET就RESET,按程序办事。

Secure Fault: 得令,走你!



    • Secure Code会通过一些专用的 API 来为Non-Secure Code提供服务,这些专用的API被称为 Secure Entry。Secure Entry 必须放在 Non-Secure-Callable 属性的Memory内。Non-Secure-Callable 本身其实是Secure Memory,但是它特殊的地方就在于可以存放Secure Entry。——你可以把NSC理解为银行的营业大厅,而那些防弹玻璃上开的一个个柜台小洞就是Secure Entry


    • TrustZone for ARMv8-M 所追求的是,Non-Secure Code 和 Secure Code都 以为自己独占整个系统。对Cortex-M23来说,Non-Secure Code以为自己运行在一个Cortex-M0/M0+上;而对Cortex-M33来说,Non-Secure Code来说,它已为自己独占的是Cortex-M3/M4。
      我们知道,Non-Secure Code的独占是“错觉”,因为它并不知道Secure Code的存在,任何出格的访问(站在它角度来说,任何对未知空间的访问)都会被截获,当作Secure Fault。Secure Code的独占是货真价实的,因为它不仅知道Non-Secure的存在,也可以随时访问他们。
      为了构建这种错觉,代价是巨大的。对于一些核心的资源,比如NVIC,SysTick,MPU,Cortex-M23/33都货真价实的为Non-Secure Code提供了额外的一份;对于另外一些昂贵的核心资源,比如流水线,通用寄存器页、Debug逻辑、浮点运算单元,Secure Code就只能屈尊和Non-Secure Code分时复用(共享)了。


    • Non-Secure Code 与 Secure Code 通过Secure Entry进行信息交换。当Non-Secure Code调用Secure Entry时,如果Secure Entry是有效的,并且存放于NSC Memory里,那么CPU就会从Non-Secure state切换到Secure State,并运行NSC里面的代码(NSC是Secure Memory,所以里面的代码是Secure Code),一般来说,Secure Entry会立即跳转到其它纯粹的Secure Memory中执行。
      Secure Code可以借助特殊的函数指针以回调(Callback)的方式调用Non-Secure Memory中的代码(并暂时性的切换回Non-Secure state)。更多的细节,还请自己阅读公开的各类文档。
    • Secure Code 和 Non-Secure Code 都可以拥有自己的异常处理程序,这里面涉及到的Secure和Non-Secure的切换都是硬件自动处理好的,程序员不用操心。值得说明的是,复位以后,整个系统都是Secure状态,所有的异常都属于Secure Code,这个时候,只有Secure Code可以大发慈悲的分配一些中断给Non-Secure Code使用。而且,我们有一个专门的寄存器位可以让所有Non-Secure Exception的优先级比任何Secure Exception都“低人一等”。

总结来说,TrustZone for ARMv8-M 创造了两个世界,Secure domainNon-Secure domain。SAU/IDAU共同将4G地址空间拆分为多个Secure,Non-Secure和Non-Secure-Callable的Region。无论是Secure domain还是Secure domain都可以把自己看作是一个普通的Cortex-M0/M0+或者Cortex-M3/M4处理器来开发——大家都有自己独立的NVIC,Systick甚至是独立的MPU。

Secure 可以打 Non-Secure,Non-Secure不能还手,因为所有的资源理论上都首先属于Secure domain



  • 结语

Cortex-M23/33 所引入的安全扩展为整个ARM嵌入式系统的信息安全提供了基础(Fundation)或者说根基(The Root of the Security)。然而,单单依靠“基础”不足以构建起坚固的防御体系——芯片厂商、OEM厂商,软件IP厂商、工具链、系统软件及应用设计,任何一环都需要引入必要的信息安全技术和措施。我们可以说:

没有 TrustZone for Armv8-M,建立在 Cortex-M 系统之上的安全将是空中楼阁;而单单依靠 TrustZone for Armv8-M 来保护信息安全,更是掩耳盗铃。用户不仅要知道自己要保护什么,如何保护,更要知道:构建一个基础坚实的安全设计所要做的 比贴一张“TrustZone Inside”的标签纸 到自己的产品上 要多得多的多。




如果你喜欢我的思维,欢迎订阅 裸机思维

出15入186汤圆

2
发表于 2020-10-21 22:38:16 来自手机 | 只看该作者
感谢大师指点

出0入0汤圆

3
发表于 2020-10-22 07:26:24 来自手机 | 只看该作者
感谢大师的专业分享!

出0入42汤圆

4
发表于 2020-10-22 08:09:28 来自手机 | 只看该作者
感谢大师分享

出0入0汤圆

5
发表于 2020-10-22 08:23:04 来自手机 | 只看该作者
大师好厉害!

出50入0汤圆

6
发表于 2020-10-22 08:46:27 | 只看该作者
感谢大师指点

出5入8汤圆

7
发表于 2020-10-22 08:53:27 | 只看该作者
谢谢  大神科普

出425入0汤圆

8
发表于 2020-10-22 09:11:06 | 只看该作者
深刻的东西,用了浅白的语言给解释了。

出0入0汤圆

9
发表于 2020-10-22 09:28:55 | 只看该作者
不明觉厉,还是不懂。

出0入8汤圆

10
发表于 2020-10-22 09:34:01 | 只看该作者
关注一下,谢谢分享!

出0入0汤圆

11
发表于 2020-10-22 10:14:27 | 只看该作者
敏感数据如何导入到security区域呢

出0入0汤圆

12
发表于 2020-10-22 14:14:10 | 只看该作者
言简意赅,有声有色、声色并茂、深入浅出

出0入54汤圆

13
发表于 2020-10-22 14:33:33 | 只看该作者
能说说trustzone和MPU设置的区别吗?
MPU+CPU特权模式是现在Autosar实现memory partition的基础,感觉和你上面说的trustzone很多功能是重复的,当然你上面说的专门区域存放securty entry是MPU没有的。

出0入0汤圆

14
发表于 2020-10-22 15:16:19 | 只看该作者
和 MPU 比, TrustZone 用起来是要方便很多的,只需要考虑将哪些东西放到安全区,还有哪些外设只能在安全区使用就可以了

MPU 首先是 8 个区域其实不怎么够用, OS 就要占掉一大半,剩下几个区域给任务用,配共享区域都费劲
而且只靠 MPU 的话,虽然 ARMv8-M 的 MPU 区域配置比之前好用了,但是因为区域数量的限制,要去配一些外设寄存器所在地址空间的访问权限的话,还是几乎不能用

出0入25汤圆

15
发表于 2020-10-25 23:22:37 | 只看该作者
lusson 发表于 2020-10-22 14:33
能说说trustzone和MPU设置的区别吗?
MPU+CPU特权模式是现在Autosar实现memory partition的基础,感觉和你 ...


TrustZone下SysTick、NVIC、异常屏蔽寄存器甚至是中断向量表都有两份,Secure和Non-secure状态下各有一份,两种状态就像两个独立的芯片一样,可以分别为每种状态创建独立的工程编写代码。而MPU隔离根据特权等级,非特权级下没法使用中断,进中断自动进入特权级,因此没法为两个特权级分别独立开发程序。

为两个状态分别开发程序还是很重要的,比如非安全程序可以自己开发,而安全程序由你要连接的物联网服务器供应商开发,因为他们需要保证只有被信任的设备才能连接他们的服务器,,

关于为Secure和Non-secure分别创建工程可以看下下面这个文档的3.2节

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入54汤圆

16
发表于 2020-10-26 09:25:14 | 只看该作者
XIVN1987 发表于 2020-10-25 23:22
TrustZone下SysTick、NVIC、异常屏蔽寄存器甚至是中断向量表都有两份,Secure和Non-secure状态下各有一 ...

还可以这样操作?中断向量不一样的话,启动地址不一样所以可以2个工程?
看来得研究一下这个。

不过我们现在的应用中,MPU是可以动态配置的。比如AUTOSAR的3个OS_APP,其中一个(OS_APP0)是特权级,另外2个(OS_APP1,OS_APP2)是非特权级,但我又想另外2个之间也不能相互访问(至少写RAM的权限要限制),防止他们有干扰。
MPU的情况下是OS属于特权级,在OS_APP切换时可以设置对应的权限,比如切到OS_APP1或OS_APP2时重新配置MPU的权限,类似这种单纯一个trustZone应该是做不到的吧,是否还需要再结合MPU

出0入54汤圆

17
发表于 2020-10-26 09:30:10 | 只看该作者
可能trustZone更关注的是security的安全。MPU是从memory出发关注程序运行的安全,比如程序跑飞,堆栈溢出等。

出0入25汤圆

18
发表于 2020-10-26 11:20:02 | 只看该作者
lusson 发表于 2020-10-26 09:25
还可以这样操作?中断向量不一样的话,启动地址不一样所以可以2个工程?
看来得研究一下这个。

上电默认进入Secure状态,使用VTOR_S指定的向量表(一般在0地址),切入Non-secure程序前可以设置VTOR_NS,给非安全状态另外设置一个向量表,,这样两种状态下的中断响应分别查询不同的向量表。。(有一组NVIC_ITNS0-15寄存器,可以指定每一个中断是进入Secure状态还是Non-secure状态)

有TrustZone的情况下有两个MPU,一个MPU_S,一个MPU_NS,,分别用于安全状态和非安全状态,,

另外,还可以设置只在Non-secure状态下可调试,这样就彻底阻断了普通开发者读取安全程序和数据的可能,,

出0入54汤圆

19
发表于 2020-10-26 13:14:51 | 只看该作者
XIVN1987 发表于 2020-10-26 11:20
上电默认进入Secure状态,使用VTOR_S指定的向量表(一般在0地址),切入Non-secure程序前可以设置VTOR_NS ...

看了你上面的pdf,大概明白了。谢了。

出0入0汤圆

20
发表于 2020-10-26 16:39:46 | 只看该作者
大师讲课就是直白

出0入296汤圆

21
 楼主| 发表于 2020-11-3 11:14:22 | 只看该作者
XIVN1987 发表于 2020-10-25 23:22
TrustZone下SysTick、NVIC、异常屏蔽寄存器甚至是中断向量表都有两份,Secure和Non-secure状态下各有一 ...

多谢帮忙看场子。

出0入0汤圆

22
发表于 2020-11-3 14:49:50 | 只看该作者
感谢大师指点

出0入0汤圆

23
发表于 2020-12-13 12:39:14 | 只看该作者
搬凳子 听课 思维很重要

出0入0汤圆

24
发表于 2021-11-2 18:43:11 | 只看该作者
听课了听课了,谢谢大师

出0入4汤圆

25
发表于 2023-7-10 17:07:41 | 只看该作者
Gorgon_Meducer 发表于 2020-11-3 11:14
多谢帮忙看场子。
(引用自21楼)

大师,有他的权威指南吗?您还会出中文版本吗?

出0入296汤圆

26
 楼主| 发表于 2023-11-15 11:50:36 | 只看该作者
SCREA 发表于 2023-7-10 17:07
大师,有他的权威指南吗?您还会出中文版本吗?
(引用自25楼)

权威指南的中文版正在翻译,原版1000多页,是个大工程。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-18 10:32

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表