最近情绪低落中…

想起了 Lionel Richie 唱的 Say You Say Me :

Say you, say me
Say it for always
That's the way it should be
Say you, say me
Say it together, naturally
I had a dream, I had an awesome dream
People in the park
Playing game in the dark
And what they played
Was a masquerade
But from behind the walls of doubt
A voice was crying out 
。。。。

MT7688 AES硬件加速

最近我无聊翻翻MT7688的datasheet,注意到居然支持AES加速,赶紧搜github找到了一个能用的OpenWRT驱动MTK_AES

赶紧开搞,一路搞好好几天才慢慢摸索出linux的初步加密框架。内核的加密框架的应用层接口是AF_ALG 套接字形式,但是有一个专门的驱动模块crpytodev抽象出一个/dev/crypto设备,采用ioctl接口,而且根据官网数据对比性能更好。

于是选中OpenWRT自带的kmod-cryptodev模块,默认会选中kmod-crypto-core等一大堆依赖,其实最终只依赖aead.ko,编译好后全部加载驱动。由于系统库的加密是openssl提供的libcrypto加密库,所以需要开启openssl库的硬件加密选项 “Crypto acceleration support”(Libraries>SSL>libopenssl) ,顺便选中openssl-util工具进行测试。

  • 测试未开启AES硬件加速

    root@:/# openssl speed -evp aes-128-cbc
    Doing aes-128-cbc for 3s on 16 size blocks: 1242779 aes-128-cbc's in 2.96s
    Doing aes-128-cbc for 3s on 64 size blocks: 359528 aes-128-cbc's in 2.95s
    Doing aes-128-cbc for 3s on 256 size blocks: 93663 aes-128-cbc's in 2.95s
    Doing aes-128-cbc for 3s on 1024 size blocks: 23499 aes-128-cbc's in 2.92s
    Doing aes-128-cbc for 3s on 8192 size blocks: 2953 aes-128-cbc's in 2.96s
    bala...bala...
    The 'numbers' are in 1000s of bytes per second processed.
    type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
    aes-128-cbc       6717.72k     7799.93k     8128.04k     8240.75k   
    
  • 测试开启AES硬件加速

    root@:/# openssl speed -evp aes-128-cbc
    Doing aes-128-cbc for 3s on 16 size blocks: 276231 aes-128-cbc's in 0.65s
    Doing aes-128-cbc for 3s on 64 size blocks: 270678 aes-128-cbc's in 0.45s
    Doing aes-128-cbc for 3s on 256 size blocks: 243448 aes-128-cbc's in 0.29s
    Doing aes-128-cbc for 3s on 1024 size blocks: 179638 aes-128-cbc's in 0.34s
    Doing aes-128-cbc for 3s on 8192 size blocks: 45879 aes-128-cbc's in 0.11s
    bala...bala...
    type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
    aes-128-cbc       6799.53k    38496.43k   214905.82k   541027.39k  3416734.25k
    

主要是看最后一行的吞吐量测试,效果惊人啊!!!!

不开启加速时数据吞吐量变化不大,毕竟这里的性能瓶颈是在AES软件实现这块,而开启硬件加速后,数据块越大效果越好,最后的8192字节块效果就提升了414倍。

这里openssl需要开启-evp选项,这样底层的crpyto库会优先选择硬件加速。openssl里的cryptodev引擎是EVP接口形式提供的,所以如果使用openssl库需要加速,必须用EVP接口。不过openssl测试工具默认的加密引擎engine会自动选择cryptodev,不用再手动指定-engine cryptodev了。

这里比较郁闷的是mtk_aes支持AES的ECB和CBC,但是openssl的libcrypto库的enc_cryptodev.c里只支持CBC,所有最终能加速的只能是AES的CBC了,估计ECB不太安全吧,连CBC也不是很安全,现在普遍用CTR吧。CBC只是比ECB多了个IV矩阵,保证每次加密不重样。

static struct {
    int id;
    int nid;
    int ivmax;
    int keylen;
} ciphers[] = {
    {
        CRYPTO_ARC4, NID_rc4, 0, 16,
    },
    {
        CRYPTO_DES_CBC, NID_des_cbc, 8, 8,
    },
    {
        CRYPTO_3DES_CBC, NID_des_ede3_cbc, 8, 24,
    },
    {
        CRYPTO_AES_CBC, NID_aes_128_cbc, 16, 16,
    },
    {
        CRYPTO_AES_CBC, NID_aes_192_cbc, 16, 24,
    },
    {
        CRYPTO_AES_CBC, NID_aes_256_cbc, 16, 32,
    },
# ifdef CRYPTO_AES_CTR
    {
        CRYPTO_AES_CTR, NID_aes_128_ctr, 14, 16,
    },
    {
        CRYPTO_AES_CTR, NID_aes_192_ctr, 14, 24,
    },
    {
        CRYPTO_AES_CTR, NID_aes_256_ctr, 14, 32,
    },
# endif
    {
        CRYPTO_BLF_CBC, NID_bf_cbc, 8, 16,
    },
    {
        CRYPTO_CAST_CBC, NID_cast5_cbc, 8, 16,
    },
    {
        CRYPTO_SKIPJACK_CBC, NID_undef, 0, 0,
    },
    {
        0, NID_undef, 0, 0,
    },
};

接下来用AES CBC加密一个20M的文件:

root@:/tmp# head -c 20m /dev/zero >test

root@:/tmp# time openssl enc -e -aes-128-cbc -in test -out test.out -kfile pgrade_pass 
real 0m 0.74s
user 0m 0.10s 
sys 0m 0.63s

root@:/tmp# rmmod mtk_aes 
root@:/tmp# time openssl enc -e -aes-128-cbc -in test -out test.out -kfile upgrade_pass 
real 0m 3.33s
user 0m 2.93s 
sys 0m 0.38s

提升了5倍。

我这里的openssl的版本是1.0.2e,最新的1.1版本支持AES的ECB的,我没试过,Cryptodev是1.7版本。现在应用就是以后加解密文件有点用,如果是优化ssh和scp估计也不是很好,比较这些应用瓶颈往往是网速,并且如果是小数据加密的话效果差别不大,只在大数据吞吐时才有差别。

MT7688 AP-Client模式

最近我在研究MT7688的闭源WIFI源码,发现默认开启了AP和AP-Client模式,但STA模式默认没选中,估计要支持的话得修改代码。

MT7688我以及在用LEDE了,新的系统很流畅,所有顺便也把WIFI移植了,修改了几个不兼容的地方基本OK了。本来是想公开的,但这个是MTK闭源驱动-_-!!!,不能公开。

涉及到的MT7688.dat里的驱动参数是这么几个:

ApCliEnable
ApCliSsid
ApCliBssid // 这个不是必须,如果不提供,wifi驱动会自己去查找
ApCliAuthMode
ApCliEncrypType
ApCliWPAPSK

但是OpenWRT里默认的uci2dat支持这些参数有BUG,因为uci2dat默认只支持一个wifi-iface,但AP-Client模式必须有两个wifi-iface,一个做AP,一个做Client。修改uci2data:

@@ -1061,23 +1064,69 @@ void hooker(FILE * fp, param * p, const char * devname)
 #endif
    else if (0 == strmatch(p->dat_key, "ApCliEnable"))
    {
-       FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_enable.value);
+        int j;
+       for(j=0; j<wifi_cfg[N].vifnum; j++)
+       {
+           if (strlen(wifi_cfg[N].vifs[j].apcli_enable.value) > 0)
+               FPRINT(fp, p, "%s", wifi_cfg[N].vifs[j].apcli_enable.value);
+       }
+       if (j < wifi_cfg[N].vifnum)
+           FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_enable.value);
    }
    else if (0 == strmatch(p->dat_key, "ApCliSsid"))
    {
-       FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_ssid.value);
+        int j;
+       for(j=0; j<wifi_cfg[N].vifnum; j++)
+       {
+           if (strlen(wifi_cfg[N].vifs[j].apcli_ssid.value) > 0)
+               FPRINT(fp, p, "%s", wifi_cfg[N].vifs[j].apcli_ssid.value);
+       }
+       if (j < wifi_cfg[N].vifnum)
+           FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_ssid.value);
+   }
+   else if (0 == strmatch(p->dat_key, "ApCliBssid"))
+   {
+        int j;
+       for(j=0; j<wifi_cfg[N].vifnum; j++)
+       {
+           if (strlen(wifi_cfg[N].vifs[j].apcli_bssid.value) > 0)
+               FPRINT(fp, p, "%s", wifi_cfg[N].vifs[j].apcli_bssid.value);
+       }
+       if (j < wifi_cfg[N].vifnum)
+           FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_bssid.value);
    }
    else if (0 == strmatch(p->dat_key, "ApCliAuthMode"))
    {
-       FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_authmode.value);
+        int j;
+       for(j=0; j<wifi_cfg[N].vifnum; j++)
+       {
+           if (strlen(wifi_cfg[N].vifs[j].apcli_authmode.value) > 0)
+               FPRINT(fp, p, "%s", wifi_cfg[N].vifs[j].apcli_authmode.value);
+       }
+       if (j < wifi_cfg[N].vifnum)
+           FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_authmode.value);
    }
    else if (0 == strmatch(p->dat_key, "ApCliEncrypType"))
    {
-       FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_encryptype.value);
+        int j;
+       for(j=0; j<wifi_cfg[N].vifnum; j++)
+       {
+           if (strlen(wifi_cfg[N].vifs[j].apcli_encryptype.value) > 0)
+               FPRINT(fp, p, "%s", wifi_cfg[N].vifs[j].apcli_encryptype.value);
+       }
+       if (j < wifi_cfg[N].vifnum)
+           FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_encryptype.value);
    }
    else if (0 == strmatch(p->dat_key, "ApCliWPAPSK"))
    {
-       FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_password.value);
+        int j;
+       for(j=0; j<wifi_cfg[N].vifnum; j++)
+       {
+           if (strlen(wifi_cfg[N].vifs[j].apcli_password.value) > 0)
+               FPRINT(fp, p, "%s", wifi_cfg[N].vifs[j].apcli_password.value);
+       }
+       if (j < wifi_cfg[N].vifnum)
+           FPRINT(fp, p, "%s", wifi_cfg[N].vifs[i].apcli_password.value);
    }
     /* the rest part is quite simple! */
     else

编辑/etc/config/wireless:

config wifi-device 'mt7628'
    option type 'mt7628'
    option vendor 'ralink'
    option band '2.4G'
    option channel '11'   // 这个必须是你要连接路由器的信道一致,否则会连不上的
    option country 'CN'
    option region '1'

config wifi-iface 'ap'
    option device 'mt7628'
    option ifname 'ra0'
    option network 'lan'
    option mode 'ap'
    option ssid 'AP名'
    option key 'AP密码'
    option encryption 'psk2'
    option wpa_crypto 'TKIP+AES'
    option hidden '1'

config wifi-iface 'sta'
    option device 'mt7628'
    option ifname 'apcli0'
    option network 'wan' // 作为外网接口
    option mode 'sta'   //  这个没用,uci2dat根本不解析
    option ApCliEnable '1'
    option ApCliSsid '路由器名'
    option ApCliAuthMode 'WPA2PSK'
    option encryption 'psk2'     // 这个必须,如果省略这个会导致WIFI驱动错误的认证协议
    option ApCliEncrypType 'TKIP'
    option ApCliWPAPSK '路由器密码'

在/etc/config/network里修改wan:

config interface 'wan'
    option ifname 'apcli0'
    option force_link '1'
    option proto  dhcp

一开始调试死活连不上,逼得只能看WIFI驱动代码发现底层的认证协议需要加上option encryption,还有就是如果是AP-Client模式的话就不能自动选择信道了,必须跟着路由器的信道来,其实把AP和Client做个桥接就是一个信号中继了,不过WIFI驱动底层好像支持MAC层上的中继,这个没空研究了。

在LEDE的iwinfo没法查看WIFI的Link Quality,而且iw也输出为空,原因就是WIFI驱动还没启用CFG80211支持。但是MT7688的WIFI驱动比较老,导致CFG80211的代码跟最新的内核不兼容,所以我疯狂的在github上一遍查找补丁,一遍修改代码。最后总算是能运行iw了,但是iwinfo输出的信息不全,估计是通过ioctl的方式与驱动打交道,驱动不支持,这个后面再修复。

我关于最近爆出的WIFI的WPA2漏洞KRACK,深感震惊!基本上所有WIFI设备没有幸免,那些IoT设备估计永远都没法更新补丁。根据漏洞描述,基本上是可以揭秘客户端或双向通信的数据,但不能破解WIFI密码!

BTW: 这个是几个礼拜前搞的了,放草稿箱里都要长草了,今天晚上抽空填了坑,近年来记忆力衰退,必须多记录!!!

MT7688开发板linux内核补丁记录

我手上的MT7688的开发板用的是Openwrt的系统,内核版本是3.18.23,有点老,调试过程中各种BUG,记录一下。

1. JFFS2 directory hardlinks BUG

在升级软件包重启后(保护删除文件的操作),报目录硬链接问题(JFFS2支持硬链接,目录不允许硬链接)

这个BUG在JFFS2官方的git软件仓库里修复了

2. JFFS2 incorrect i_nlink count after jffs2’s RENAME_EXCHANGE operations

删除文件和目录时报错:

[  291.190000] ------------[ cut here ]------------
[  291.190000] WARNING: CPU: 0 PID: 1218 at fs/inode.c:339 inc_nlink+0x50/0x74()
[  291.200000] Modules linked in: ...balabala...
[  291.290000] CPU: 0 PID: 1218 Comm: rm Not tainted 3.18.23 #25

这个补丁有人很早就提交给OpenWRT了,可惜都没合并进来,要找补丁可以去LEDE找,看人家多勤快。

3. MT7688的第二路SPI全双工模式挂了

这个我调了好久,MT7688有两路SPI,开发板上SPI0被用来连NOR FLASH做系统用了,我好奇找了另外一块NOR FLASH想连在SPI1上。可惜的是死活不行,检查了各种连线(SPI_CS1被MT7688做启动选项引脚了,启动必须拉低,但这样会片选NOR FLASH,不知道启动会不会有影响),无奈上网翻找补丁。

补丁在这里找到了SPI1全双工挂掉补丁

后记

OpenWRT看来老了,补丁更新不勤快,系统臃肿,是时候切换到LEDE了。

Flex和Bison

最近我想在程序中加一个命令行接口,同时可以作为简单的RPC调用协议使用。这个之前在看libiio里的iiod源码时接触到了Flex和Bison写的命令行接口RPC,

简单的例如:

send cmd 
返回SUCCESS或FAIL

这个虽然直接自己写一个也可以,但是效率和复杂度上有点顾虑。Flex的正则表达式能很好的处理命令行字符串,用Bison去处理命令行的组合虽然有点大材小用,不过万一以后写一个带逻辑语句的命令行就比较方便。

有一点麻烦的是,Flex和Bison的C++支持文档不是很多,大多是C的。不过还好Flex和Bison直接输出C++源码,边看源码边改还算方便。这里特别说一下OREILLY有本动物书是《flex&bison》,网上有PDF版,但用的版本老了点。

其他的没啥好写了,我也在学习中…

https://www.gnu.org/software/bison/manual/html_node/index.html#SEC_Contents

USB液晶屏for树莓派

前序

自从入手树莓派一代,一直闲置着。现在树莓派都二代了,再不玩玩就彻底吃灰了。可惜的是树莓派没接显示器总归不好玩。

我手头有一个Atmel A5(SAMA5D3)的ARM板子,自带TFT-LCD电容触摸液晶屏和USB接口,刚好作为树莓派1代的显示器,两者之间用 USB传输图像数据(不求性能,能显示就行)。

准备就绪就缺实现方案了,突然想起RoboPeak有一个树莓派的液晶屏在卖,而且开源树莓派端的驱动和协议,那我就充分发挥开源优势,在这个基础上改。

这就涉及到两个USB驱动:

  1. ARM液晶屏的USB gadget驱动:

    项目在这里

  2. 树莓派的USB HOST驱动:

    首先刷RoboPeak的树莓派固件,重新编译树莓派的Linux kernel, 编译修改原来的RoboPeak的开源树莓派驱动。最后改好的驱动项目在这里

在研究kernel的USB gadget驱动过程中发现了一个USB协议详解网站,当然USB协议文档也必不可少,开发驱动的话单单看第9章就行了。

实现原理

这里只说gadget驱动,host驱动用RoboPeak原来的rpusbdisp,单单改了分辨率(改成液晶屏的分辨率800X480),当然现在驱动刚刚能用,还有很多要完善。

在USB协议层上有一个interface,两个endpoint,一个OUT传输Robopeak液晶屏的图像协议,另一个IN传输触摸屏的输入。

  1. OUT endpoint

根据RoboPeak的rpusbdisp的协议,我只实现图片拷贝的bitblt命令就行了,这个是host驱动检测到画面有更新后拷贝图片给gadget驱动。我只要介绍到图片数据(颜色深度16位,565格式的真彩色)拷贝到当前的framebuffer就可以了,我这里直接霸王硬上弓拷贝的framebuffer的base memory里。

当然一幅800480的图片传输数据就有700多K(800480*2=768000),为了压缩数据,RoboPeak的协议里检测到gadget驱动版本大于1.04会采用新的压缩数据方法。简单来说就是分成128字节的段,每个段有一个head字节,表示当前端是否是重复的颜色数据(一般的桌面背景很多重复颜色数据),这样简单的压缩一下传输的数据一下子就下降了很多。

  1. IN endpoint

标准的中断数据,由于这个ARM板子是pixcir的芯片,支持电容屏和多点触摸。我在pixcir的驱动里做了一个HOOK函数获取触摸位置再发送给host。

好了,具体的细节看代码吧:)。

usb_display

修复WIFI驱动

最近我阴差阳错升级了Ubuntu14.04 Linux内核到4.4.0,升级后发现Broadcom的私有WIFI驱动 wl挂掉了。切换到开源驱动信号又不是很好,万般无奈只能调试找原因了。

dmesg下发现了原因,原来是内核更新后dkms阶段重新编译bcmwl驱动失败了,找不到rdtscl,估计API改了-_-!!!。

Using CFG80211 API
  CC [M]  /var/lib/dkms/bcmwl/6.30.223.248+bdcom/build/src/shared/linux_osl.o
/var/lib/dkms/bcmwl/6.30.223.248+bdcom/build/src/shared/linux_osl.c: In function ‘osl_getcycles’:
/var/lib/dkms/bcmwl/6.30.223.248+bdcom/build/src/shared/linux_osl.c:938:5: error: implicit declaration of function ‘rdtscl’ [-Werror=implicit-function-declaration]
     rdtscl(cycles);
     ^

电脑上安装的Broadcom的私有驱动是bcmwl, 版本是6.30.223.248。马上上网搜索bcmwl, 找到了Ubuntu的代码仓库。挨个找BUG修复记录,在这里找到了一点端倪

+diff --git a/src/shared/linux_osl.c b/src/shared/linux_osl.c
+index d540636..e622c5c 100644
+--- a/src/shared/linux_osl.c
++++ b/src/shared/linux_osl.c
+@@ -932,6 +932,10 @@ osl_getcycles(void)
+   uint cycles;
+ 
+ #if defined(__i386__)
++#ifndef rdtscl
++#define rdtscl(low) \
++ ((low) = (u32)rdtsc())
++#endif
+   rdtscl(cycles);
+ #else
+   cycles = 0;
+-- 
+1.9.1
+

OK,马上修改,安装bcmwl的代码在/var/lib/dkms/bcmwl/6.30.223.248+bdcom/build下,sudo make , sudo make install后,depmod一下就满血复活。

但这有个缺点,默认仓库里的bcmwl代码还是没改过来,下次升级内核,dkms机制下编译的还是有BUG的代码,每次升级都要编译一下WIFI驱动,有点麻烦,下次再找方法吧。

第一次在Github上合并Pull Request

我用Github也差不多两年了吧,大多上传一些自己无聊的代码而已。今天打开Github发现有个老外居然向我提交代码,没错是full_text_rss仓库-_-!!!。

full_text_rss最初是因为用Google Reader读到的RSS很多都没有全文输出,所以自己就在当时的VPS做了一个(详细见这里),后来忘了啥原因就把网站关了,把代码丢到Github上就没管了,到今天合并代码才想起来,让我这些老代码又焕发活力了:)。Github用到现在为止感觉不错,以后也要多多提交代码咯。

工作中发现的交流问题(X-Y Problem)

我很早就读过酷壳的这篇文章X-Y Problem,在工作中同事时常犯这种问题-_-!!!。

虽然我自己也不擅长沟通,但我在沟通时还是有意识的说的全面点,如果还不行的话就画图呗,一图胜千言啊。有时候说清楚一件事情是很难的。

按酷壳博主的意思X-Y问题如下:

1) 有人想解决问题X

2) 他觉得Y可能是解决X问题的方法

3)但是他不知道Y应该怎么做

4)于是他去问别人Y应该怎么做?

我遇到的X-Y Problem都是同事试图向我沟通不是他们专业的问题,在这种情况下人很容易的按自己的理解提出问题,但其实如果没有扎实的基础知识和经验,问题的假设前提往往是错的,更何况问题本来就很复杂,可能更本就没了解清楚问题。

说了这么多,举个典型例子吧:

某天同事A打电话来问我一个工作问题:

同事A:xxx设备如果能xxx相比xxx能不能提高速度?

…然后是一大堆解释和讨论…

然后我精疲力竭的弱弱问了一句:你为什么想这么搞,现在xxx设备速度还可以嘛,没必要搞啊?

然后同事A惊讶的喊:xxx设备不是很慢很慢吗,xxx指标都xxx了。

然后我-_-!!!说:xxx设备本来就没这个问题啊,你瞎想了…

然后就没然后了-_-!!!

如果事前能先说明一下原因,也不至于费这么多口舌和我的脑细胞啊,不说了,总之在说明问题前先说清楚自己的思路,你为什么这么想的理由,这样人家才能帮你吧。