2016年3月22日 星期二

使用 c++ 來開發 uefi/edk2 程式 (0)

進學致和,行方思遠。
20220622 補充:
在「write uefi program without edk2」之後, 我已經不用 edk2 來開發 uefi, 當然也不用在處理哪些可怕的 inf, dsc 檔案。

使用 c++ 也不需要像這篇這麼花功夫, 之後不會用本篇的方法來使用 c++, 這篇就留個紀錄。

我也知道為什麼 global object ctor 沒有被正常喚起, 因為沒做任何事情, 本來就不會被正常喚起, 手動呼叫 GLOBAL_XX function 算是正常用法。

以下原文:

edk2 並沒有支援用 c++ 來開發 uefi 程式, 我真是不敢相信還有這麼原始的開發環境, 竟然不支援 c++。我可是 c++ 愛好者, 不能用自己喜歡的語言開發程式, 感覺很不爽。

c++ 的好處我說過好幾次了 (疑! 我根本沒說過!!), c 我也是可以寫的, 為什麼堅持用 c++ 呢? 還真的說不上原因, 大概是網路上很多的討論都對 c++ 很不好, 什麼 XX 比不上 C, YY 比不上 JAVA, 這些言論更讓我對 c++ 抱不平, 決定要多多推廣 c++, 我又不善嘴炮言詞, 只好用程式碼來證明 c++ 的能耐。

UEFI原理与编程》第十章說明如何用 c++ (g++) 來開發 uefi 程式並提供了 GcppPkg 這個範例, 不過很可惜, 這部份書上寫的不夠詳細, 我花費了不少時間還是搞不定怎麼使用這個範例。

最後我做了和 GcppPkg 類似的事情, 再加上《UEFI原理与编程》第十章的說明, 搞出了自己的 c++ 作法。所以我雖然沒搞定書上的 GcppPkg 範例, 但是書中內容還是幫了我不少。

最困難的是編譯環境, 花了不少時間我才搞定 (沒有搞懂, 是搞定), 找了 edk2/AppPkg/Applications/Main/Main.c 來修改, 這是使用 c 語言 main 的開發方式。

我建立了 edk2/AppPkg/Applications/Main/m.cpp (從 edk2/AppPkg/Applications/Main/Main.c 複製而來), 也複製了一份 m.inf, m.inf 最重要的是 L27 ~ 42, 把 source code 的檔案填進去, build 就會去編譯這些檔案。

m.inf
 1 ## @file
 2 #   A simple, basic, application showing how the Hello application could be
 3 #   built using the "Standard C Libraries" from StdLib.
 4 #
 5 #  Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
 6 #  This program and the accompanying materials
 7 #  are licensed and made available under the terms and conditions of the BSD License
 8 #  which accompanies this distribution. The full text of the license may be found at
 9 #  http://opensource.org/licenses/bsd-license.
10 #
11 #  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 #  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 ##
14 
15 [Defines]
16   INF_VERSION                    = 0x00010006
17   BASE_NAME                      = m
18   FILE_GUID                      = 5ea97c46-7491-4dfd-b442-747010f3ce5f
19   MODULE_TYPE                    = UEFI_APPLICATION
20   VERSION_STRING                 = 0.1
21   ENTRY_POINT                    = ShellCEntryLib
22 
23 #
24 #  VALID_ARCHITECTURES           = IA32 X64
25 #
26 
27 [Sources]
28   m.cpp
29   bst.cpp    
30   #crtbegin.cpp  
31   eh.cpp      
32   mylist.cpp  
33   mymap.cpp     
34   myvec.cpp
35   cstring.cpp   
36   gdeque.cpp  
37   k_stdio.cpp  
38   mem.cpp  
39   myiostream.cpp  
40   mystring.cpp  
41   #my_setjmp.c
42   my_setjmp.S
43 
46 [Packages]
47   StdLib/StdLib.dec
48   MdePkg/MdePkg.dec
49   ShellPkg/ShellPkg.dec
50 
51 [LibraryClasses]
52   LibC
53   LibStdio
54 
55 [BuildOptions]
56    #GCC:*_*_X64_CC_FLAGS = -fno-exceptions -fno-rtti -ffreestanding -nostdlib -nodefaultlibs -std=c++11
57    MSFT:*_*_*_CC_FLAGS =
58 


再來是把這個 m.inf 加到 AppPkg.dsc。

AppPkg.dsc.diff
 1  [LibraryClasses]
 3    #
 4    # Entry Point Libraries
 5    #
 6 @@ -105,6 +106,7 @@
 7  ########################################################################
 8  
 9  [Components]
10  AppPkg/Applications/Main/m.inf         


以下是測試的 c++ 檔案。cout 和 vector 不是 edk2 提供的, 是移植我那個玩具標準 c++ 程式庫得來的, namespace 是 DS 而不是 std, 書上的範例是移植 stl 過來, 我既然已經有自己的版本, 玩具歸玩具, 沒道理不用的。

還順便測試一下目前最潮的 lambda 語法。

還有 setjmp/longjmp x64 的版本。

m.cpp
 1 #include  "myvec.h"
 2 #include  "myiostream.h"
 3 #include "my_setjmp.h"
 4 using namespace DS;
 5 
 6 class Obj
 7 {
 8   public:
 9     Obj()
10     {
11       i=10;
12       ::printf("ctor\n");
13     }
14     ~Obj()
15     {
16       ::printf("dtor\n");
17     }
18   private:
19     int i;
20 };
21 
22 jmp_buf jbuf;
23 
24 int lambda_test(int girls = 3, int boys = 4)
25 {
26   auto totalChild = [](int x, int y) ->int{return x+y;};
27   return totalChild(girls, boys);
28 }
29 
30 int main (IN int Argc, IN char **Argv)
31 {
32   Obj obj;
33 
34   int total = lambda_test();
35   cout << "total: " << total << endl;
36 
37   vector<int> vec_i;
38 
39   vec_i.push_back(1);
40   vec_i.push_back(2);
41   vec_i.push_back(3);
42   for (int i=0 ; i < vec_i.size() ; ++i)
43     cout << vec_i[i] << endl;
44 
45   char *area = (char*)mymalloc(20);
46   char *a1 = new char [20];
47 
48   int j_ret = 9;
49   //printf("aaa\n");
50   //getchar();
51   j_ret = my_setjmp(jbuf);
52 
53   if (j_ret == 0)
54   {
55     printf("00\n");
56   }
57   else 
58   {
59     printf("11\n");
60     return 0;
61   }
62 
63   my_longjmp(jbuf, 5);
64   return 0;
65 }


build -p AppPkg/AppPkg.dsc 就可以編譯出 m.efi 了, 不過先別急著打這個指令, 還要寫個 script gcc (書上提供了一個, 照抄後再加上 g++ option, ref list 1), 判斷是 .c 檔時出動 gcc, .cpp 檔時出動 g++。當然也可能要修改 uefi/edk2/BaseTools/Scripts/GccBase.lds, 不過目前就不要搞太複雜, 這樣就可以了。

list 1. gcc script
 1 #!/bin/sh
 3 iscpp=0
 4 for i in "$@" ; do
 5   if [ "-" != "${i:0:1}" ] ; then
 6     if [ "cpp" = "${i##*.}" ]; then
 7       iscpp=1
 8     fi
 9   fi
10 done
11 
12 if [ $iscpp = 0 ]; then
13   echo gcc
14   /usr/bin/gcc -DUEFI $@
15 else
16   echo g++
18   /usr/bin/g++ -DUEFI -fpermissive -I. -DSTM32 -fno-exceptions -fno-rtti -ffreestanding -nostdlib -nodefaultlibs -std=c++11 $@
19 fi


好了, 現在可以痛快的敲下 build -p AppPkg/AppPkg.dsc

fig 2 為測試畫面, ctor/dtor 正常發動了, lambda 也正常, DS::vector, DS::cout 也正常, 感謝 bjarne stroustrup; 感謝 c++。

fig 2模擬器測試畫面


為了保險起見, 我在真實機器上同樣也做了測試, 真的是可以跑的。

真實機器的 uefi 執行畫面


global object ctor/dtor 沒有搞定, 比我想的還複雜些, 這不是什麼問題, 不要用就好了。 XD

我是用手動呼叫 GLOBAL_XX function 來搞定這件事。

uefi-shceme 就是這麼做的。

uefi function 手冊: http://www.bluestop.org/edk2/docs/trunk/getchar_8c.html

2016年3月16日 星期三

[books] UEFI原理与编程

20150306 購於台南若水堂 463 nt/89 rmb
《UEFI原理与编程》這本書真是不簡單, 我查閱 amazon.com, amazon.jp, 這兩個網站中相關 uefi 主題的書籍也不多 (20150311), 沒想到中國就出了一本, 重點是中文的。

會對 uefi 有興趣單純只是想寫個 bootloader, 因為傳統 biso 0x7c00 那個方法已經快要過時了, 寫了一個 simple os kernel 的我還想讓這個 simple os kernel 能在新的 x86 上執行, 自然得學習 uefi/edk2 了。

後來我發現了一種不需要使用 edk2 的用法, 只要 gcc + makefile 即可, grub2 也是用類似的方法, 請參考: uefi os loader (0) - write uefi program without edk2

可以把 uefi 想成是一個小型的 os, 我們寫的 uefi 程式就和在 dos, windows, linux, 下的執行檔一樣, uefi 也提供很多豐富的 api 讓我們使用, 就和 bios 的中斷編號一樣, 只是改成 c function。

edk2 是 uefi 的開發工具, 和傳統 biso bootloader 不同, 已經不能很單純的用 gcc 來開發了, 我覺得這不算好事, 但也不是壞事, 不過第一版的 edk 只能用 ms vc 來開發那就不是好事了, 而 efi 執行檔是 pe 也讓我覺得格格不入, 不太爽。

而沒有支援 c++ 讓我更是不爽中的不爽, 這個開發工具用了很多 oo 觀念, 卻硬是要用 c 來模擬, 而不乾脆的支援 c++。好在作者在第十章提供了使用 c++ 編譯器的方法, 總算讓我有機會使用 c++。

第一章介紹了 uefi 啟動過程, 有哪些階段。第二章則是介紹 edk2 開發工具, 有 windows, linux 兩個本版, 還有模擬器, 方便用來除錯。

第三章很重要, 介紹怎麼開發 uefi 程式, edk2 需要編寫 3 個檔案, 類似 makefile, 不過 makefile 只要一個, edk2 要寫 3 個 (dsc, dec, inf), 很累人。還有各種 uefi 程式種類, 這章得要仔細閱讀, 才能理解整個 uefi/edk2 開發流程。

第四章介紹怎麼使用 protocol, 這個可以想成需要什麼服務, 就打開那個 protocol, 例如檔案讀寫, 就要打開相對應的 protocol, 幾乎是使用任何服務的基礎。

可惜書中沒有介紹如何寫 uefi os loader, 我知道為什麼, 因為這不是一個簡單的工作, 比起 legacy bios 的 os loader, 困難多了, 有這樣經驗的人, 應該是少數, 而且有些困難的問題要處理, 要一一克服並不容易。

書中範例:
https://github.com/zhenghuadai/uefi-programming
https://code.google.com/p/uefi-programming-guider/wiki/Download

ref:
UEFI实战(1)

2016年3月12日 星期六

如何從中國亞馬遜購物 X 淘寶上海登航集貨

要如何從中國亞馬遜購物呢? 有兩個問題要解決。
  1. 付款
  2. 寄送貨物
當然也可以直接用信用卡付款直接送到台灣, 不過看完運費後你可能就會取消訂單了。

fig 1. 激省淘金術!淘寶買物教戰守則, 寫的很不錯的淘寶教學書籍
拜在淘寶使用集貨的經驗, 在 ptt 看到《淘寶上海登航集貨》的推薦文, 想說之前都是用淘寶官方的圓通, 由於是淘寶的官方集貨, 在使用上簡化不少, 不過遇到《圓通的集貨站 - 誇張的書籍重量》這問題後, 我就想試試其他集貨商, 收費也比較便宜。放大膽子試試這家好了, 後來才發現集貨的店家有一大堆, 可以好好選擇。

至於使用支付寶付款, 一樣拜淘寶的使用經驗, 我終於知道怎麼用支付寶了。

使用支付寶的困難點在於怎麼取得支付寶餘額, 中國亞馬遜和淘寶似乎不同, 只能使用支付寶餘額付款, 無法使用支付寶信用卡、支付寶 ATM 轉帳付款。而怎麼取得支付寶餘額這得自己想法辦了, 網路上有人在幫忙換支付寶餘額儲值, 這是一個辦法。

而關於支付寶餘額實名的傳聞很多: 據聞 20160520 就不能用, 再後來又傳出 20160701 才不能用, 不過現在 (20160804) 還是可以用支付寶餘額付款, 中國法規朝夕令改, 我已經不敢放太多錢在支付寶餘額。 不能用就不能用, 花錢還得看你臉色嗎?

不用管支付寶了, 中國亞馬遜可以直接用信用卡付款, 再配合集貨送回購買的書籍, 說實在我不是很喜歡用支付寶, 就像我不喜歡用 paypal 一樣, 可以直接用信用卡付錢, 沒理由給這些支付方式再抽一筆的。

淘寶不像台灣的拍賣網站, 很直覺就知道怎麼使用了, 所以有很多的教學文, 甚至還有書教你怎麼買東西, 你一定很疑惑, 買東西還要人教哦! 這是真的, 我也是看了《激省淘金術!淘寶買物教戰守則》之後才更會用的。常常可以看到討論區有很多基本的問題, 可以參考 fig 1 的書, 會很清楚怎麼從淘寶購買東西, 這本是我看過最符合目前淘寶的教學書籍, 之前看的幾本功效都不太, 不是和目前淘寶的版本不同, 就是寫的不夠詳細。

一開始我是用支付寶的信用卡付款功能, 不過額外的費用太高了, 國外手續費 1.5%, 刷卡支付 3%, 不好的匯率, 扒 3 層皮阿, 現在我用儲值的支付寶直接付費, 不僅省下額外費用, 就算要退款也單純多了。

《软件调试》這本我本來在淘寶買, 不過賣家沒貨我就從中國亞馬遜下手了, 淘寶賣家的退款是退回到信用卡 (又要手續費, 又有匯率問題, 退款也要扒兩層皮), 我那時還沒有儲值支付寶。

回到中國亞馬遜, 我使用中國亞馬遜很久了, 可是從來沒用我的帳號買過東西, 就是前面這些問題, 後來找到代買才有辦法從中國亞馬遜買到東西。

中國亞馬遜的付款問題, 我一樣選用支付寶付款, 最後結帳會連到支付寶頁面, 按下去付錢就搞定, 已經改用信用卡付款。

運送則要靠《淘寶上海登航集貨》, 一般集貨的使用方法都是去申請個帳號, 再登入去看東西來了沒, 有多重, 然後集貨商會給你個地址, 收件地址就填這個, 收信人也可能是另外一個名字,這並不是容易的操作, 就算是在中國沒有語言的問題, 也還是會有問題, 有問題就要問人, 要問人就要問到這集貨商的員工, 而問題要是沒人回答, 問了也是白問。

淘寶上海登航集貨》使用阿里旺旺來服務客人, 所以不需要有申請帳號的動作, 直接到他的頁面看地址/收件人怎麼寫就搞定。

有問題怎麼辦?  不管你是熟手還是生手, 當然一定會有問題, 這時候就使用阿里旺旺來聯絡客服人員, 很多人反應有些集貨商都沒回應, 我想是其人手不足, 這間則不會, 反應回覆還算即時, 這就是最大的優點了, 客服人員要忙到凌晨兩點, 真是辛苦。而當他們收到物品的時候, 會透過阿里旺旺來傳訊息, 不用自己登錄去看目前如何, 還蠻方便。

本來我只打算在淘寶上買東西用這家集貨, 不過既然可以集貨不在淘寶買的應該也可以吧, 詢問之後果然是可以的, 就把中國亞馬遜的心愿单挑幾本書結帳。
剛成立的訂單

多核多线程技术 平装 – 2011年1月1日
售价: ¥40.80 (8.2折)
  • 定价: ¥49.50
满99元免运费且可货到付款 详情

软件调试:Software Debugging 平装 – 2008年6月1日
售价: ¥102.20 (8折)
  • 定价: ¥128.00
免运费且可货到付款 详情
 
幸運的是還有 100 折抵 30 的紅包, 99 又免運費, 已經比我在淘寶看到的還便宜了。最後這兩本書花了 113 rmb。

運送中

送達

等到所有貨物到達上海登航轉運中心後, 會有客服人員用阿里旺旺通知, 並告知重量, 自己可以決定要不要送回台灣, 我這次累積了 12 件貨品, 總重 7.6 公斤, 一公斤 12 rmb, 總費用 12*8=96 rmb。如果確定要送回台灣了, 還要告知集貨商台灣的地址, 還有包裹的內容物品項, 像我就告知是書籍。集貨商會給一個黑貓宅急便的編號, 可以查訊運送進度, 不過似乎得等到台灣才有辦法查詢, 無法得知是否正常清關這段, 有問題也許可以直接問集貨商。國際貨運會遇到很多問題, 討論區上有很多的討論都可以看看, 也許下次就是自己會遇到的問題。

這兩本書總重 2kg, 所以我實際上付了, 113 + 12*2 = 137 rmb, 等於台幣 685, 不算貴。

儲倉時間是 60 天, 有的集貨商超過天數要加錢, 圓通超過 14 天就要加收錢, 有的則是丟掉貨物, 自己需要注意。這家集貨商還有體積的限制, 長、寬、高乘起來要在 160cm 以下, 要不然會改以體積收費, 會貴上不少, 可以請集貨商分開包裝寄回台灣。

中國物品有分普貨、特貨, kindle 是特貨, 這家不送。所以要送特貨得在找找別的辦法。

我目前在台灣購買簡體中文書籍的管道有: 台北天瓏書局、博客來、若水堂、三民網路書局、ruten 拍賣, 他們的定價都不便宜, 有時候沒有庫存書籍, 也是要等調貨, 尤其是三民, 得等上非常久的時間, 而且三民的網站反應時間很慢, 購物車還會掉資料, 用起來還蠻痛苦的, 要不是沒有選擇, 我是不會去用三民的網站。

之前則是請代買來購買中國亞馬遜的書籍, 費率和代買費用以及集貨費也沒什麼彈性, 一樣不便宜。



終於不需要請代買了, 可以自己控制集貨的重量, 自己掌控的感覺真好。

中國亞馬遜的書價錢變動很快, 《C 程序设计的抽象思维我看過》64 ~ 84 rmb, 才短短一個禮拜價錢差異頗大, 當然也可以看看其他店家的售價再來決定要買哪家的, 有不同的策略。

在不知道怎麼用中國亞馬遜買書之前, 我在淘寶買書, 不過很怕買到影印版的, 每次都要仔細詢問, 還麻煩, 而且有些店家不願意送轉運中心。

文後補充:
這篇使用卡式台胞證來實名認證:
挑戰全台最低齡卡式台胞證(無銀行卡)支付寶實名認證教學

這篇是淘寶被盜帳號:
支付寶帳號被盜用盜刷到整個送人

一鍵下單是很方便的功能, 不過應該會有人會擔心其安全性, 請參考:
亚马逊 (Amazon) 的一键下单安全性如何?
了解之後就會知道亞馬遜設計的苦心, 在方便性與安全性中取得了兩者的優勢, 很不簡單, 放心用吧!

更改「一鍵下單」的付款方式:
https://www.amazon.cn/gp/help/customer/display.html?nodeId=201252570

這篇教學文也可參考:
我如何在台灣購買大陸Amazon電子書並在手機上閱讀

亚马逊 (Amazon) 的一键下单安全性如何?

2016年3月6日 星期日

[books] 若為自由故 - rms 自傳

請代買購於 amazon.cn, 20150911 轉帳, 
20150916 拿到, 39.4 * 5.35 + 79 = 265.715
向 GNU/Linux 致敬, 有了 linux 這個 os kernel, 加上 GNU 的自由軟體套件, 才讓我得以使用 unix like 的 GNU/Linux 作業系統。

在學生時代, 不知道為什麼, 學校教育沒有開如何使用 unix 的課程, 所有的學生得要自己去學習 unix, 而我總是對那些連入主機的終端機很頭疼, 根本就不知道怎麼學習起這個 unix 系統。

Richard Matthew Stallman 對於自由軟體的頑固態度也在本書出現, 這本書在其堅持之下, 也是 open source 的, 我都不知道出版社要怎麼賺錢了, 感謝 oreilly。譯者要不到 rms 的簽名只因為他那本書沒有 open, 記得把 GPL 印出來找 rms 簽名, 他肯定會簽的, 當然這本也可以。

一樣有中文版的電子版本 (當然阿, 都要 open 了): https://www.freemindworld.com/faif

趕快去 fork, latex 檔案耶, 太棒了。

書上從印表機談起, 到怎麼因為這因素開啟了他的自由軟體之路。

也談起他的家庭, 不過和很多厲害的人一樣, 似乎都有在與人相處上的問題, 只能說老天是公平的, 什麼都給了你別人還要不要混阿!

在 MIT 打開教授門的描述很有趣, 想辦法打開教授的門也算是 hack 行為, 就只為了使用教授的電腦。

子曰: 「窈窕淑女,君子好逑」, rms 也一樣, 看到漂亮的女士, 也是會盯著看呢!

GPL 的發明比他寫了 emacs, gcc, gdb 還重要, 畢竟這些都會有人有能力寫出來, 但 GPL 沒有他這樣想法的人是不會出現的, 還為了 GPL 和律師們有過討論, GPL 可是有通過律師的驗證哦!

也許有人會說有這樣的本領不靠這本事賺錢真是太可惜了, 不過 1990 rms 獲得了麥克阿瑟院士, 也得到 24 萬美金的獎金。

opensource 這個辭彙想必讓 rms 很不是滋味, 我不知道和其 free software 有什麼本質上的區別, 但 free 在英文實在太容易被誤會了, 在 2000 年左右, 中文很多翻譯也是翻成免費的意思, 完全誤解了這個的「自由」的精神。

而後來 linux 大出風頭也讓 rms 不好受, 明明 gnu 軟體支撐了整個 linux 作業系統, 但只有 linux 這個 kernel 受到矚目, 所以他很強調 GNU/Linux, 這樣實在太長了, 我也不想這樣稱呼, 傳道的事就讓 rms 去忙吧!

這本書不算很精彩, 只有像我這個有愛的人才會特別去找來看吧! 以下書籍推薦給對 opensource 有興趣的朋友。

2016年3月2日 星期三

c 語言 - 比較 char c==0xff

不經一事不長一智

還真不知道怎麼寫這個標題, 從程式碼看結果。

d.cpp
 1 #include <stdio.h>
 2 
 3 int main(int argc, char *argv[])
 4 {
 5   char c = 0xff;
 6 
 7   printf("c: %hhx\n", c);
 8   if (c == 0xff)
 9   {
10     printf("yy\n");
11   }
12   else
13   {
14     printf("zz\n");
15   }
16   return 0;
17 }

d.cpp 應該要印出 yy 還是 zz 呢? 答案可能出乎你的意料之外, 回答 yy 或是 zz 的朋友恭喜你們都答對了。

編譯指令
g++ -m32 -o d.x86 d.cpp
arm-linux-gnueabihf-g++ -o d.rpi2 d.cpp


x86 測試結果
descent@debian64:tmp$ g++ -m32 -o d.x86 d.cpp
descent@debian64:tmp$ ./d.x86
c: ff
zz


rpi2 測試結果
raspberrypi:~# g++ -o d.arm d.cpp
raspberrypi:~# ./d.arm
c: ff
yy

怎麼辦? 把 char c = 0xff; 改成 unsigned char c = 0xff; 就可以得到相同結果 yy; 而把 char c = 0xff; 改成 signed char c = 0xff 則會得到相同結果 zz。

以下的 gcc option 也可以做到類似的效果。

man gcc
-fsigned-char
Let the type "char" be signed, like "signed char".

Note that this is equivalent to -fno-unsigned-char, which is the negative form of -funsigned-char. Likewise, the option -fno-signed-char is equivalent to -funsigned-char.

看來還是把 char 的 signed/unsigned 指定好比較保險。

arm 版本的 gcc char 是 unsigned char; x86 版本的 gcc char 是 signed char。