QEMU的重新编译(下)

前面介绍了完整的重新编译QEMU 的方法,下面介绍一个另外的方法,稍微简便一点。

简单的说,我使用前面提到的方法升级了所有的Package,然后把msys64目录进行了打包。有需要的朋友可以下载下面的内容:

[1] Env.7z  里面有两个目录,一个是 msys64 这是上面提到的所有工具,解压后将这个目录放在c:根目录下;另外一个 MSYS2 64Bit 里面有指向MinGW 的快捷方式,解压后放在桌面即可,后面的打开msys2 终端都是从 MSYS2 MinGW 64-Bit 运行的。

[2] Qemu-4.2.0.tar 源代码。不需要事先解压,直接放置到 c:\msys64\home目录下。

编译方法:

[1] 使用 “MSYS MinGW 64-bit”快捷方式打开 msys2 终端,然后进入 home 目录

[2] 解压 Qemu 源代码,命令是 tar xvf qemu-4.2.0.tar

[3] 在解压后的目录中创建build 目录

[4] 设置编译目标,我们只玩 X86 X64 所以目标设定为x86_64-softmmu即可。

../configure –prefix=/qemu –target-list=x86_64-softmmu  –enable-sdl –enable-tools

[5] 之后,运行 Make即可编译,相比 VirtualBox 这个快多了。

[6] 再运行  make install

[7] 可以在  c:\msys64 下面找到qemu目录,进入之后运行

[8] 可以看到虚拟机正常运行起来了

目前看起来这样的方法美中不足是编译出来的 exe 只能在 msys64 环境下运行,无法在Windows下面直接运行。

下载链接  https://pan.baidu.com/s/1Zgkl4uORtMDEkHrT2LP2Sw 密码:98yu

Leonardo改装为USB读卡器

前面介绍了如何用 Leonardo 实现SD卡的读写,这次介绍如何实现将其改装为 USB SD 卡读卡器。

经过搜索在 http://elasticsheep.com/2010/04/teensy2-usb-mass-storage-with-an-sd-card/  给出了一个代码。结合上面提到的硬件可以完成功能(CS Pin 直接接地,此外其他连接相同)。

使用 C:\WinAVR-20100110\进行编译:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>cd\
C:\>cd C:\WinAVR-20100110\SDCardReader
C:\WinAVR-20100110\SDCardReader>setenv.bat
C:\WinAVR-20100110\SDCardReader>PATH=C:\WinAVR-20100110\bin;C:\WinAVR-20100110\utils\bin;C:\Windows\System32\wbem
C:\WinAVR-20100110\SDCardReader>cd "LowLevelMassStorage+SD"
C:\WinAVR-20100110\SDCardReader\LowLevelMassStorage+SD>make
-------- begin --------
avr-gcc (WinAVR 20100110) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Size before:
AVR Memory Usage
----------------
Device: atmega32u4
Program:    9094 bytes (27.8% Full)
(.text + .data + .bootloader)
Data:        712 bytes (27.8% Full)
(.data + .bss + .noinit)
Checking for invalid events...
---- Compile Time Library Options ----
USB_DEVICE_ONLY
FIXED_CONTROL_ENDPOINT_SIZE=8
FIXED_NUM_CONFIGURATIONS=1
USE_FLASH_DESCRIPTORS
USE_STATIC_OPTIONS=(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)
INTERRUPT_CONTROL_ENDPOINT
--------------------------------------
--------- Target Information ---------
AVR Model: atmega32u4
Board: USER
Clock: 16000000Hz CPU, 16000000Hz Master
--------------------------------------
Size after:
AVR Memory Usage
----------------
Device: atmega32u4

Program:    9094 bytes (27.8% Full)
(.text + .data + .bootloader)
Data:        712 bytes (27.8% Full)
(.data + .bss + .noinit)
-------- end --------

编译之后生成 MassStorage.hex 文件,我使用Arduino的上传方式进行烧写,烧写之前按下 reset  button 然后马上运行,特别注意根据你自己的实际情况选择COM,我这里是 COM7 (意思是按下 Reset 之后出现的 Bootloader 的COM号):

D:\arduino-1.8.4\hardware\tools\avr\bin\avrdude -v -Cd:\arduino-1.8.4\hardware\tools\avr\etc\avrdude.conf -patmega32u4 -cavr109 -PCOM7 -b57600 -D -V -Uflash:w:./MassStorage.hex:i

烧写之后就可以正常工作了。

写入速度

读取速度能快一点

完整的代码和Lufa 下载(根据我的测试只有特定的 Lufa版本LUFA_091223才能通过编译,所以放在一起了,另外,为了减小体积我删除了 Lufa 里面的 Demo)。

TinkerNode NB-IoT 制作看股票的工具

人类社会的进步始终伴随着通讯技术的发展。当我们谈谈及通讯相关历史时,一定会提起中国古代用于示警外族入侵的烽火。但是这种方式信息容量非常小,只能传递“入侵发生”这个信号。类似的,17世纪的法国创建了基于视觉的全国性信号传递系统,具体是通过称作“信号塔” (semaphore towers)的建筑来实现。信号的表示方法接近旗语,每个高塔通过操控机械展示出不同的形状,然后使用接力的方法将消息传递出去。

                                    

              

随着时代发展和科技进步,人类的通讯不再局限于消息的传递,而是更加注重设备之间的互联。这就是“万物互联”的概念。TinkerNode NB-IoT 是 DFRobot 最新出品的物联网开发板。物联网(The Internet of things,IoT)顾名思义,就是物与物相连的互联网。这有两层意思:第一,物联网的核心和基础仍然是互联网,是在互联网基础上的延伸和扩展的网络;第二,其用户端延伸和扩展到了任何物品与物品之间,进行信息交换和通信。窄带物联网(Narrow Band Internet of Things,NB-IoT)是物联网领域一个新兴的技术,主要用于低移动性、小数据量、对时延不敏感的连接服务,其支持低功耗设备在网络中的数据传输,因此也是一种低功耗广域网(Low Power Wide Area Network,LPWAN)通信技术。相对于被逐渐淘汰的2G通信,NB-IoT具有三大优势:

大连接:海量链接的能力,在同一基站的情况下, NB-IoT 可以比现有无线技术提供 50~100 倍的接入数。一个扇区能够支持,10 万个连接,设备成本与功耗有效降低,网络架构得到优化。

广覆盖:在同样的频段下, NB-IoT比现有的网络增益提升了 20 dB,相当于提升了 100 倍的覆盖面积。

低功耗:NB-IoT借助 PSM(Power Saving Mode,节电模式)和 eDRX(Extended Discontinuous Reception,超长非连续接收)可实现更长待机。

TinkerNode NB-IoT开发板主控芯片是 ESP32,主频:240MHz,内存达到:520KB。

因此,使用它可以方便的完成很多工作。抽烟嚼槟榔吃中药和炒股票是重要的爱国标志,这次我就要用这个板子制作一个能够随时随地显示A股大盘走势的设备。

TinkerNode NB-IoT有着强大的通讯功能,这次用到的是它的WIFI通讯功能。从原理上说:TinkerNode NB-IoT访问一个网址,取得实时的大盘数据,然后将数据显示在液晶屏幕上。

硬件方面,除了TinkerNode NB-IoT还需要一个液晶屏。最终选择的是7寸液晶屏触摸串口屏。这个屏幕的特点首先是尺寸大,分辨率更是800×480。使用串口传输内容让显示非常方便。

多介绍两句这种串口屏。传统的屏幕需要在主机端生成好内容,然后不停的将内容数据发送到屏幕上,因此对于主机和接口有较高的要求。主机需要有足够的内存存放下要显示的内容,同时接口必须足够快并且不断发送数据刷新。而串口屏没有这样的限制,只要有串口就可以实现显示操作。实现的原理是:首先通过屏幕配套的图形化界面设计软件设计界面,比如,在屏幕中央会有一个20字节的字符串需要显示。然后将生成的配置文件烧写到屏幕上。在使用的时候单片机发出“在特定的位置显示字符串”即可。比如,在这次的作品上使用界面设计软件绘制如下:

具体就在800×400 的界面上放上2个显示文本变量外加实时曲线:

其中的文本变量都有对应的地址,比如上图中的“文本变量1”属性如下:

当屏幕串口收到 A5 5A 0E 82 04 86 30 33 31 36 20 34 33 32 32 FF FF 它可以知道一共有 0x0E 个数据,命令是 0x82,访问的地址是 0x0486,后面就是要显示的ASCII。于是就将就将ASCII 显示在文本变量1的位置上。接下来将生成配置文件Copy到U盘上,插入到屏幕的USB 口上电,屏幕自动完成读取。之后就可以使用串口操作了。当然目前串口屏没有统一标准,具体操作需要查看不同厂家手册。

TinkerNode NB-IoT板子上有一组串口,这次就使用它来完成和液晶屏的通讯(实际上只用TXD 即可,因为不需要屏幕反馈数据)

整体供电使用充电宝充电,一路直接提供给液晶屏(屏幕耗电在500MA,因此无法从板子提供),另外一路提供给TinkerNode NB-IoT,直接接入到板子5V位置(实验表明当没有充电电池的时候,太阳能电池板充电输入也无法正常工作)。

代码通过HTTP访问 http://hq.sinajs.cn/list=sh000001 这个网址获得实时信息。取得结果是类似“var hq_str_sh000001="上证指数,2727.0186,2702.1296,2745.6182,2751.8964,2702.4933,0,0,252019507,281583369664,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2020-03-20,15:01:59,00,";”这样的信息。细心的朋友也可以猜到sh000001是股票信息,可以通过更换这个代码获得特定股票的实时信息。
/**
 * BasicHTTPClient.ino
 *
 *  Created on: 24.05.2015
 *
 */

#include <Arduino.h>

#include <WiFi.h>
#include <WiFiMulti.h>

#include <HTTPClient.h>

#define USE_SERIAL Serial

WiFiMulti wifiMulti;

void setup() {

    USE_SERIAL.begin(115200);
    Serial2.begin(115200);
/*
    for(uint8_t t = 4; t > 0; t--) {
        USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
        USE_SERIAL.flush();
        delay(1000);
    }
*/
    wifiMulti.addAP("YOUID", "YouPassword");

}

//"var hq_str_sh000001=\"上证指数,3001.7616,2996.7618,2968.5174,3010.0286,2968.5174,0,0,352470970,378766618968,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2020-03-11,15:12:07,00,\";";
String payload;

//
// String Parser function
// Input: Str Source String
//        Arg1 - Output start position
//        Arg2 - Output end position
void GetNoData(int number, int *start, int *stop) {
  
  int Index=0;
  *start=0;
  *stop=0;
  // Find first '"'
  while ((Index<payload.length())&&(payload[Index]!='"'))
    {Index++;}
  // if current position is not '"', this means didn't find '"'
  if (payload[Index]!='"')  {return ;}
  // Look for the number string
  while (number>0) {
    *start=Index; *stop=Index;
    while ((Index<payload.length())&&(payload[Index]!=',')) {Index++;}
    *stop=Index;  
    Index++;
    number--;
  }     
}

void loop() {
  //USE_SERIAL.print("[HTTP] begin...\n");


    // wait for WiFi connection
    if((wifiMulti.run() == WL_CONNECTED)) {

        HTTPClient http;

        USE_SERIAL.print("[HTTP] begin...\n");
        // configure traged server and url
        //http.begin("https://www.howsmyssl.com/a/check", ca); //HTTPS
        http.begin("http://hq.sinajs.cn/list=sh000001"); //HTTP

        USE_SERIAL.print("[HTTP] GET...\n");
        // start connection and send HTTP header
        int httpCode = http.GET();

        // httpCode will be negative on error
        if(httpCode > 0) {
            // HTTP header has been send and Server response header has been handled
            USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);

            // file found at server
            if(httpCode == HTTP_CODE_OK) {
                payload = http.getString();
                USE_SERIAL.println(payload);
                int a,b,i;
                String CurrentHi,CurrentLow;
                long v;
                
                //时间
                GetNoData(32,&a,&b);
                for (int i=a;i<b;i++) { USE_SERIAL.print(payload[i]); } USE_SERIAL.print("\n");
               
                //A5 5A 12 82 04 0b 30 33 31 36 20 32 32 3A 32 39 3A 31 32 FF FF 
                Serial2.write(0xA5);
                Serial2.write(0x5A);
                Serial2.write(3+b-a+2);
                Serial2.write(0x82);
                Serial2.write(0x04);
                Serial2.write(0x0B);  
                for (int i=a;i<b;i++) { Serial2.write(payload[i]); } 
                Serial2.write(0xFF);
                Serial2.write(0xFF);
                
                //大盘
                GetNoData(4,&a,&b);
                for ( i=a;i<b;i++) { USE_SERIAL.print(payload[i]); } USE_SERIAL.print("\n");
                //A5 5A 0E 82 04 86 30 33 31 36 20 34 33 32 32 FF FF
                Serial2.write(0xA5);
                Serial2.write(0x5A);
                Serial2.write(3+b-a+2);
                Serial2.write(0x82);
                Serial2.write(0x04);
                Serial2.write(0x86);  
                for (int i=a;i<b;i++) { Serial2.write(payload[i]); } 
                Serial2.write(0xFF);
                Serial2.write(0xFF);
                
                //大盘整数
                for ( i=a;payload[i]!='.';i++) {CurrentHi=CurrentHi+payload[i];}
                //Serial.print(CurrentHi);Serial.print("\n");
                USE_SERIAL.print(CurrentHi.toInt());USE_SERIAL.print("\n");    
                //大盘小数
                for (i=i+1;i!=b;i++) {CurrentLow=CurrentLow+payload[i];}
                //Serial.print(CurrentHi);Serial.print("\n");
                USE_SERIAL.print(CurrentLow.toInt());USE_SERIAL.print("\n");  
                v= CurrentHi.toInt()*10000+CurrentLow.toInt();
                v=(v-27500000)/1000+200;
                USE_SERIAL.print(v);  USE_SERIAL.print("\n");  
                //A5 5A 04 84 01 00 00 
                Serial2.write(0xA5);
                Serial2.write(0x5A);
                Serial2.write(4);
                Serial2.write(0x84);
                Serial2.write(0x01);
                //Serial2.write(((CurrentHi.toInt()-2600)&0xFF00)>>8);  
                //Serial2.write((CurrentHi.toInt()-2600)&0xFF);
                Serial2.write((v>>8)&0xFF);  
                Serial2.write(v&0xFF);

            }
        } else {
            USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        }

        http.end();
    }

    delay(5000);
}

找一个纸盒,将屏幕固定在外面,因为除了供电没有其他线路,所以内部使用面包板插接即可:

工作的视频(因为这个屏幕支持触摸,所以看起来外面还有一层膜):

PowerHouse Mountain 的安装

PowerHouse Mountain 简称 PHM,是 Intel 提供的用于Debug 功耗的软件。因为这个软件会向系统中安装一些常驻软件同时调整系统设置,可能会导致账号无法登陆的问题, 因此个人不建议在工作机上安装。建议安装到一台专用机器上,或者安装在虚拟机中用于查看的Log。

  1. 运行安装文件

   2.建议使用默认路径

3.特别注意,建议勾选在桌面上创建快捷方式,默认不创建快捷方式(如果测试PSR 屏幕,桌面图标要尽量少)

4.中间会出现 Node.js 安装提示,选择 Next 继续

5.提示是否安装其他工具,我一般不会勾选(默认不勾选),因为没有必要

5.等待,很快即可完成安装

6.运行之后最上方有 Home (主界面)/ Collector (测试收集Log)/ Open Trace (查看抓取的日志)

注意:如果你打开之后无法看到下面红色方框中的选项那么说明安装有问题。原因不明,解决方法是重新安装 PHM 甚至重新安装系统。

7.现在就可以进行手工测试了,但是我并不推荐手工测试(Manual),因为比较麻烦需要手工唤醒等等,其他人复制现象的时候也会非常麻烦。因此,还请安装 WDTF。在没有安装之前  ModernStandby 是无法勾选的。

7.1 WDTF 的安装:找到和你 Windows 版本对应的 WDK (不同版本 WDK 之间会有差别)

7.2 打开之后进入 Shell,运行如下命令

8.至此,PHM 的安装已经完成,可以通过勾选上方的ModernStandby进行测试,其他参数不需要修改,保持默认即可。第一次运行会出现要求重启的提示,根据要求重新启动系统再次运行 PHM 即可。在测试期间,建议拔掉全部USB设备避免干扰。测试是自动的,屏幕会自动黑掉,5分钟后自动唤醒。比如,下图就是正常取得Log 后的提示:

9.之后,可以通过Open Trace 选择打开Log 文件,比如,我们需要看的就是上面路径下 Contents.phms 这文件。

10.打开后,在 Report 中,勾选 “Select Report”中的 PCH_SLP_S0B即可可以看到结果:

Windows 10 无法 Win+L锁屏

最近我的一台电脑出现 Win+ L 无法锁屏的问题。一番搜索下来有很多文章介绍在注册表中有一个控制的位置。但是我根据文章无法找到对应的位置,后来使用搜索功能在 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon 找到。

DisableLockWorkstation = 1 表示禁止锁屏功能,0 表示可以锁屏。

所以,出现同样问题时,不妨先查看上面的那个注册表位置。

一个奇怪的 Python CSV 处理问题

我需要Python处理一个类似下面的 CSV文件:

于是编写代码先尝试读取之:

import csv
with open('Datax.CSV', encoding='utf-8') as csvfile:
	reader = csv.DictReader(csvfile)
	for row in reader:
		print(row['Process Name'])

奇怪的事情发生了:我可以读取除了第一个”Time of Day”之外的所有 row 的值。当我写出来 print(row[‘Time of Day’]) 的时候会出现下面的错误:

C:\Users\Administrator\AppData\Local\Programs\Python\Python38>go2.py
Traceback (most recent call last):
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python38\go2.py", l
ine 5, in <module>
    print(row['Time of Day'])
KeyError: 'Time of Day'

测试数据文件在这里,代码在这里。有兴趣的朋友在看解释之前可以尝试自己解决一下。

接下来我各种猜测,默认是因为空格等等,都不是Root Cause。后来用代码将取得的 Key 打印一下:

惊奇的发现第一个是 ‘\ufeff”Time of Day”‘。用十六进制软件打开数据文件查看:

在文件头上有 EF BB BF ,这是CSV 文件的 Unicode 头。就是它引起了我们奇怪的问题。

知道了原因之后,可以通过尝试构造出一个相同的Key 来解决:

import csv
with open('Datax.CSV', encoding='utf-8') as csvfile:
	reader = csv.DictReader(csvfile)
	for row in reader:
		print(row["\ufeff\"Time of Day\""])

运行正常:

结论:这是因为数据文件的 Unicode 头导致的……. 话说 Python 处理数据还真是方便,虽然调试和编写耗时不短,但是代码长度大大出乎我的意料。

Modern Standby简介

本文是根据作者对于 Modern Standby 的理解写成的,在某些概念上会和定义有冲突,希望读者特别注意如果有冲突以官方文档为准。

CPU 作为PC的大脑通常是最大的耗电大户。与之类似,人脑重量只占体重的2%,却会消耗整个身体所需能量的17%和氧气的20%。所以, CPU省电是整个PC系统降低功耗的关键。随着笔记本电脑的普及,如何降低功耗延长待机时间成为一个突出需求。在进一步介绍之前需要提及一下之前的休眠。在ACPI 规范中定义了一些 PC 省电的状态。比如下图所示的S1 S3 和S4,当处于 S0 状态时,CPU处于工作状态:

如果能让CPU 在S0 的时候尽量降低功耗,那么可以节省出很多电力。此外,人们希望省电不要影响操作体验。为此,Intel 和微软携手一起推出了 Modern Standby。据说这个概念来源自手机:对于用户来说,按下按键锁屏,屏幕黑掉之后系统会做一些动作来省电。具体的操作并不需要用户操心,相比PC用户,Legacy Sleep需要用户操作选择S1 S3 或者S4;手机用户按下按钮马上屏幕可以亮起来继续之前的使用。于是,Intel 和微软也希望能在PC上实现类似功能。比如,用户使用Windows平板电脑按下电源关闭屏幕就开始省电,再次按下就马上恢复使用,这样就能做到在不影响客户体验的情况下节省电力。

除了Modern Standby(缩写 MS,还有些人会缩写成 MSB),该技术还可以被称作:Connected Standby (缩写 CS),S0ix或者 Sleep S0。这里特别提一下用 Connected Standby称呼 Modern Standby 功能并不恰当。Modern Standby包括了2种,一种是 Connected Standby,最主要的意思是睡眠的时候仍然联网,比如:收邮件。进入CS 之后后台仍然能够收邮件,唤醒之后马上就能看到新邮件。还有一种是 Disconnected Standby,就是睡眠的时候断网。很明显,因为有联网的操作,经常让系统睡的不踏实,人们也不知道睡眠的时候 Windows 究竟在后台干嘛。甚至很多时候 Windows后台升级操作会严重影响休眠,表面看着睡下去,但是实际上 CPU 并没有睡下去依然在工作。

此外,微软和 Intel 对于 Modern Standby 理解上有差别。前者以关闭屏幕为标志,屏幕一灭就是开始睡;后者是以 SLPS0 信号拉低(Assert)为标志的。所以在测试的时候也经常发现 Software 报告的 Sleep 会高于 Hardware Sleep 的情况。

SLPS0 是存在 CPU上的实体线路,与之类似的是主板上的 Sleep S3 信号,还有Sleep S4 信号。CPU 通过SLPS0告诉外围设备:我现在睡 Modern Standby了。比如,EC看到了这个信号被拉低就知道系统进入 MS,可以做一些相应动作。

在前期的研发过程中,示波器测量SLPS0 信号是最准确的判定是否进入 MS 的方法。使用示波器,可以看到正常情况下 SLPS0 信号是每隔一段时间起来一次的。这就是CPU 起来看看是否有需要他处理的事件,如果有的话处理一下,处理完成继续睡,如果没有继续睡。还有一种特殊情况,比如 Skype 能够在MS的状态下随时接收呼叫,这个功能是网卡配合,将一些特殊的 Pattern 注册到网卡中。当系统进入 MS 后,网卡保持工作,这个工作不需要CPU参与;如果发生呼叫,远端服务器会将这个 Pattern 发送到网卡上,网卡发现匹配后唤醒CPU来进行处理。

从BIOS 的角度来说 Connected Standby 调试很困难,因为基本上所有的软件调试都要依赖CPU 来处理,CPU 睡了也没人处理。另外,CPU 进入 Connected Standby 之后 CPU并没有准确的停止位置。比如:Sleep S1 我们能在 ACPI 中拦截到;Sleep S4 我们知道CPU 是关掉了。但是对于Connected Standby 没人知道当下 CPU 的 EIP 指向何处。产用的调试方法是运行 Intel 专用调试软件 Powerhouse Mountain抓取Log,但是对于一些硬件问题这种方法无能为力;更复杂的问题需要使用 Intel 专用设备来调试。

QEMU 的重新编译(上)

QEMU 是功能强大的虚拟机,支持了大量的CPU 同时可以直接使用 EDK2 作为启动BIOS。之前我介绍过如何编译VirtualBox 的代码以便修改它使用的ASL Code,这次介绍一些如何重新编译 QEMU,具体方法如下(方法来自https://my.oschina.net/ejoyc/blog/1587798):

我是在 VirtualBox 的虚拟机中测试的,操作系统是Win10 16299。

[1] 安装msys2
    打开msys2官网http://www.msys2.org/下载x64版的msys2, 安装到目录c:\msys64
    Python环境也是需要的, 安装Python2.7.12到目录D:\Python27

[2] 更新源
    进入目录c:\msys64\etc\pacman.d
    在mirrorlist.msys的前面插入
        Server = http://mirrors.ustc.edu.cn/msys2/msys/$arch
    在mirrorlist.mingw32的前面插入
        Server = http://mirrors.ustc.edu.cn/msys2/mingw/i686
    在mirrorlist.mingw64的前面插入
        Server = http://mirrors.ustc.edu.cn/msys2/mingw/x86_64

[3] 更新msys2
    pacman -Syu

  这里会有一次异常,具体现象是出现一个错误提示然后就不动了。直接叉掉窗口之后重新启动虚拟机再次执行上面这个命令,等待完成即可。
    pacman -Su

[4] 准备编译环境

       将代码放在 c:\msys64\home 目录下
    通过“MSYS2 MinGW 64-bit”打开msys2终端: 

       Cd..   

       这步之后会进入 home 目录下   
    PATH=/c/Python27:/c/Python27/DLLs:$PATH
    pacman -S base-devel git
    pacman -S mingw-w64-x86_64-binutils mingw-w64-x86_64-crt-git
    pacman -S mingw-w64-x86_64-headers-git mingw-w64-x86_64-gcc-libs
    pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-gdb mingw-w64-x86_64-make
    pacman -S mingw-w64-x86_64-tools-git mingw-w64-x86_64-pkg-config
    pacman -S mingw-w64-x86_64-winpthreads-git mingw-w64-x86_64-libwinpthread-git
    pacman -S mingw-w64-x86_64-winstorecompat-git mingw-w64-x86_64-libmangle-git
    pacman -S mingw-w64-x86_64-pixman mingw-w64-x86_64-SDL2 mingw-w64-x86_64-glib2
    pacman -S mingw-w64-x86_64-capstone

上述命令中除了第一个其他都能很快完成,当然也和你的网络有很大关系。当你不确定是否卡死的时候,可以用任务管理器查看 CPU占用率确定。

[5] 编译QEMU

下载最新的 Qemu 我用的是qemu-4.2.0.tar

  tar xvf qemu-4.2.0.tar  (原文提供的是tar.xy 文件,使用这个命令tar xvJf qemu-2.10.1.tar.xz)
   cd ./qemu-2.10.1.tar
   mkdir build
    cd build
     ../configure –prefix=/qemu –target-list=x86_64-softmmu –enable-sdl –enable-tools
    make
    make install

我们只需要 X64的虚拟机,所以target-list 指定 x86_64-softmmu 即可。

[6] 使用qemu
   此时qemu就编译完成并安装到c:\msys2\qemu目录中。这里生成的 QEMU 没有办法直接运行,还需要打开 msys2 MinGW 64-bit 然后到生成的 qemu 目录中,用 ./qemu-system-x86_64w.exe 即可以运行。

整体来说上面的方法比较麻烦,但是操作下来还是比较顺利的。

上面所需的工具我都放在网盘中了,文件如下:

qemu-w64-setup-20200201.exe   这是QEMU网站提供的官方 Windows 64位安装程序

python-2.7.12.amd64.msi       Python 2.7  

msys2-x86_64-20190524.exe     MSYS2 安装文件 

qemu-4.2.0.tar                QEMU 4.0.0 源代码 

分享链接  https://pan.baidu.com/s/1_7pAiRKDZEstrccGWZBYeQ 密码: vpzr

WinDBG 调试 Notepad

WinDBG 有两种模式,一种是用来调试Kernel 的,比如,前面介绍过的双机通过USB3 互联,然后从一端调试另外一端;另外一种是用来调试User Mode的Application,比如,在本机打开WinDBG调试本机当前的 NotePad。

我原来的理解是,如果双机互联之后都能够调试Kernel Mode,那么调试远端机器上的Application应该手到擒来。但是多次试验都没有成功,后来咨询了一下天杀,他表示这条路不通如果需要调试Application 必须老老实实的用UserMode。

这次WinDBG试验的目标是调整Notepad 打开文件对话框,使得原来无法聚焦在编辑界面的对话框可以聚焦。意思是打开 Open对话框时无法再点击后面的主程序界面进行编辑。 本文根据 https://www.codeproject.com/Articles/1276860/Hacking-with-Windbg-Part-1-Notepad 翻译。

这次介绍的是用 WinDBG 来调试本机的 Notepad (记事本):

  1. 打开Notepad.exe,打开WinDBG, File -> Start debugging  -> Attach to process 选择 notepad.exe

Attach之后会自动停下来:

2.使用 x notepad!* 可以查看这个进程的 symbols,如下

3.查看一下带有 open 字样的函数,可以看到 notepad!ShowOpenSaveDialog ,这个可能性很大

5.在这个函数上设置断点,命令  bp notepad!ShowOpenSaveDialog,之后使用 bl 命令查看确定断点已经下好

6.使用 g 命令, 然后使用 Notepad file->open 调出对话框。这时会触发中断

7.此时再使用 u 反编译,u notepad!ShowOpenSaveDialog

同样,可以使用 uf 来一次性反编译整个notepad!ShowOpenSaveDialog

再使用 k 命令【参考1】查看堆栈信息,当前已经在 notepad!ShowOpenSaveDialog 中

7.接下来使用 ub 命令,我们前面使用过 u命令来反编译指定位置和之后的命令。这里ub这个命令是用来反编译指定位置当前指令之前的汇编代码,b这里就是代表向后查看(backward)的意思。【参考2】

              可以看到在调用 notepad!ShowOpenSaveDialog 之前有给参数赋值的命令。使用下面的命令直接修改指令,然后再次使用ub 检查,可以看到已经修改完成

0:008> a 00007ff6`ce9c2263
00007ff6`ce9c2263 xor ecx,ecx
DBGHELP: SharedUserData - virtual symbol module
00007ff6`ce9c2265 nop
00007ff6`ce9c2266 

8.使用 g 命令让 notepad 运行起来,这时当打开文件对话框仍然可以在编辑区域输入内容。

       总结:上面展示了几个 WinDBG 的基本操作,不过第7步的操作在我看起来有些莫名其妙,notepad!ShowOpenSaveDialog是Notepad 自定义的函数,不知道作者是如何得出 ecx 中是对话框参数的结论的,中间似乎缺少必要的推理过程。

参考:

1. https://blog.csdn.net/chenyujing1234/article/details/7743460#t11

15. k     命令用来显示当前线程的堆栈,如下

0:018> k

跟d命令一样,k后面也可以跟很多后缀,比如kb kp,kn,kv,kl等,这些后缀控制了显示的格式和信息。

栈指令k[b|p|P|v]

这四条指令显示的内容类似,但是每个指令都有特色;

KB显示三个参数;

Kp显示所有的参数,但需要Full Symbols或Private PDBSymbols支持。KP与Kp相似,只是KP将参数换行显示了;

Kv用于显示FPO和调用约定;

KD,用于显示Stack的Dump,在跟踪栈时比较有用。

这些指令区分大小。

2. https://www.cnblogs.com/developersupport/p/windbgcommand-u.html

DFRobot TinkerNode NB-IoT 开发板安装指南(支持包的安装)

为了让你的开发板能够在 Arduino IDE中跑起来,需要在“开发板管理器”中安装这个板子的开发包。具体操作首先请参考下文:


https://mc.dfrobot.com.cn/thread-303693-1-1.html

如果你碰到下面这样的错误无法继续

”下载 http://downloads.arduino.cc/packages/package_index.json 时出错“,那么请参考本文的方法:

1.同样的打开首选项,将附加开发板管理器网址修改为

http://download.dfrobot.top/TinkerNode-NB-IoT/package_TinkerNode-NB-IoT_index.json

2.点击下面的直接编辑链接打开对应的目录,在我这边是

“C:\Users\Ziv2013\AppData\Local\Arduino15”

3.打开 preferences.txt 文件,找到下面两行:

target_package=arduino

target_platform=avr

修改为

target_package=TinkerNode_NB-IoT

target_platform=esp32

同时,将 package_TinkerNode-NB-IoT_index.json 文件丢在这个目录下

4.重启 Arduino ,打开“开发板管理器”,同样的你会遇到上面相同的错误,只是这次,出现错误之后输入 ti 能够在列表中看到需要的板子

选择安装,耐心等待即可:

耗时比较长,还经常出现不动的情况,可以用任务管理器Kill Javaw.exe 然后再次启动Arduino ,重复上面的操作可以继续下载(有断点续传)。当然,如果还是太慢无法下载完整的话,推荐直接联系 DFRobot 客服让他们直接提供离线文件。