2012年5月21日 星期一

UEFI/PI (3) Security(SEC) Phase



  • SEC Phase 簡介
      SEC是reset 或 power-on 後 PI ( Platform Initialization)的第一個phase定義在Platform Initialization Specification, Ver. 1.2.1, Volume 1, Chapter 13。SEC必須確保firmware的 "完整"。 SEC他是相依於platform及CPU的,也就是說必須針對不同的CPU或平台來做移植。另外,SEC大部分的實作都是在assembly下的,而且是不可以壓縮的。
  • SEC Phase 的責任
      SEC有四個主要的任務分別如下:
      1. Restart SEC必須要處理所有的platform restart event,包含開機、重開機或是其他異常的重啟。
      2. Memory Sotre SEC必須要建立一塊臨時的memory在系統初始化前使用。使用例如CAR( Cache as Ram)或SRAM的技術。
      3. Root of Trustestart SEC從power on 開始要尋找和驗證PEI的最初步驟。目的是要保證Processor最開始的code是可以信賴的。
      4. Passing 傳送Handoff訊息和把control給PEI Fondation。傳送的訊息包含了a. 平台狀態、b. BFV(Boot Firmware Volume)位置及大小、c. temporary RAM的位置大小等等。

        SEC透過EFI_PEI_STARTUP_DESCRIPTOR將上述Passing的資料pass給PEI。EFI_PEI_STARTUP_DESCRIPTOR的資料結構是長這樣子的:
        typedef struct {
            UINTN                   BootFirmwareVolume;
            UINTN                   SizeOfCacheAsRam;
            EFI_PEI_PPI_DESCRIPTOR  *DispatchTable;
        } EFI_PEI_STARTUP_DESCRIPTOR;
    • SEC Platform Information PPI
        在上面我們有提到SEC利用(強制使用)EFI_PEI_STARTUP_DESCRIPTOR這個資料結構將Handoff德資訊pass給PEI。另外,我們也可以pass"額外"(optional)的PPI EFI_SEC_PLATFORM_INFORMATION_PPI給PEI。這個PPI 抽象化了PEI Fondation所需要用來discover dispatching PEIM的起始位置的platform-specific資訊。這個PPI也可以存在EFI_PEI_STARTUP_DESCRIPTOR這個資料結構的最後一個參數裡(*DispatchTable)。
    • Health Flag Bit Format
      • Health Flag Bit Format
        • SEC_PLATFORM_INFORMATION_PPI.PlatformInformation()定義了PFI_HEALTH_FLAGS存放由microcode、hardware或是Itanium才有的PAL 產生的CPU status。Health Flag的bit format如下圖:

           

          下圖則是每一個bit的說明。

      • Self-Test State Parameter
          Self-test state定義的參數for IA-32 Intel® processors 和 Intel® Itanium® processor family 是一樣的。某些 bit可能沒辦法直接對應到IA-32的CPU,這時候讀到的值就會是NULL。下圖說明了每一個bit 的意義。

          如果Status顯示functionally restricted,我們可以從vm,ia,fp得到failure的訊息。下面列出導致functionally restricted的情況:
          1. Processor 或 PAL偵測到且獨立出錯誤的component,所以不會被使用。
          2. Processor必須至少要有一個可以用的memory unit、ALU、shifter及branch unit。
          3. Floating-point may be disable。
          4. For Itanium,RSE(Register Stack Engine)is  not required to work,但register renaming logic必須要能正常work。
          5. Processor-controlled caches和register files之間的path在測試的過程中必須要能work。
          6. 讀取firmware address space必須要能正常work。

    • Processor-Specific Detail
        下面以IA-32為例,解釋SEC Phase在IA-32架構下的運作狀況。
        1. Locating PEI Foundation。
        2. 使用 architecture define的Handoff state把control交給PEI。
        3. 初始化processor-controlled memory resource例如processor data cache。
    • 參考資料:VOLUME 1: Platform Initialization Specification Pre-EFI Initialization Core Interface Version 1.2.1

      UEFI/PI (2) UEFI and Platform Initialization(PI)

      • Background

      • 前一篇說明了UEFI的大概,也提到了Tiano這個OpenSource Project. 這一篇將簡介UEFI的PI。
        過去Intel發展了 The Intel Platform Innovation framwork for UEFI或是"The Framework" 是一個符合UEFI規範的一個firmware實作。這一個幾乎以C來實作的framework支援了大部分的Intel CPU family。

        而目前,這個framework已經被更全面支援的UEFI Platform Initialization (PI)所取代了。UEFI和PI有兩個主要不同的點。
        1.  UEFI定義了OS, add-in driver和system firmware之間的Interface。(下圖藍色部分)
        2.  PI則定義了偏向Implementation的部分 e.g. 從 power on 到移交control給OS之間的所有操作。 (下圖綠色部分)
         
        PI負責從開機到UEFI可以work之間的流程,舉例來說,UEFI是負責跟OS做溝通,然而,UEFI本身並不處理memory initialization、recovery或platform initialization等等跟platform有關的事情。這些都是由PI做掉的。
          另外,UEFI跟PI的SPEC都是UEFI Fourm所制定的。 
        • PI 簡介
        • 如下圖,PI分成六個部分,
          從開機的過程來看,順序是由左到右。下面一一解釋各個流程在做什麼。
          1. SEC
          2. SEC是Security的簡稱,這一個phase是開機後第一個執行的phase。任務是確保在reset 或 power on之後的integraty是完整的。
          3. PEI
          4. PEI是pre-EFI initialization的簡稱。任務是先做CPU、Chipset及Memory等等的初始化。在這一個階段,PEI初始化基本的環境 for下一個 phase。另外,PEI執行完會有Hand-off block (HOB)傳給DXE
          5. DXE
          6. Driver Execution Environment這個階段負責enumerate及init device、初始化UEFI services、  及 protocol等等
          7. BDS
          8. Boot Dev Select這邊負責how and where to boot OS.
          9. TSL
          10. Transient System Load這個階段將control transit 給OS
          11. RT
          12. Runtime的階段已經脫離boot sequence了,這個階段是由OS執行中
          13. AL
          14. After Life指的是系統reboot或shutdown

        UEFI/PI (1) UEFI 簡介


        • 什麼是 UEFI ? 
        •  Unified Extensible Firmware Interface 定義了OSplatform firmware之間溝通的介面。例如跟一般都會有firmware的GPU 卡。

          UEFI的出現是INTEL為了要取代傳統的 BIOS(Legacy BIOS) 然後發展EFI最後移交出去改名成UEFI
           
        • 為什麼UEFI?

        • 1. Legacy BISO的執行環境都是在16 bit下,之後的CPU儘管支援64 bit還是必須"額外"實作支援16 bit 環境以支援BIOS執行。於是像INTEL的vendor就想改變這點取代傳統的BIOS
          2. 16 bit的CPUaddressing可以到2^20 Bytes ,也就是說極限到1024 KBytes,這當然大大的限制了BIOS可以做的事情。
          3. 對開發者來說,Legacy BIOS多以ASM來開發,懂的人相對地少且對於開發速度也會有程度的受限。
        • UEFI Resource

          1. Intel 移交EFI後實際管理的單位是Unified EFI Forum 因此在上面我們可以看到最新的SPEC.
          2. Intel Release了代號為Tiano 的EFI的實作後,我們可以直接從SourceForge去抓代號為Tianocore的Opensource project.
          更詳盡的UEFI簡介可以參考WIKI

        2012年5月17日 星期四

        C# with Json.NET 多個Constructor

        有一次我想要在C#中把object serialize成json然後存到硬碟。我選擇的是Json.NET這個套件。但當我要deserialize的時候卻發現轉不過來。

        原因是當你的class有多個或沒有constructor的時候就會有問題。你必須至少有一個constructor而且假設有多個建構子的時候必須要在建構子的宣告上面加上[JsonConstructor]
        例如
        [JsonConstructor]
        public sample(xxx a, yyy b)
        {
            
        }
        

        Windows 8 Metro Style App Binding

        Metro的binding真的有夠難懂。
        事情是這樣的,假設你有一個資料結構 plan
        public class PlanSource
        {
            public ObservableCollection PlanList = new ObservableCollection();
        }
        
        public class Plan
        {
            public string Name
            {
                set;
                get;
            }
            public ObservableCollection itemList=new ObservableCollection();
        }
        
        public class Item
        {
            public string DisplayName
            {
                get;
                set;
            }
        }
        
        
        然後你有一個ListView,並且你想要顯示所有PlanList 裡面的Name你可以這樣設計你的ListView
        
            
            
            
                
                    
                        
                        
                    
                
            
            
        
        
        然後可以在那一頁的cs檔(c#)裡面例如OnNavigatedTo 填上這段
        PlanSource ps=new PlanSource();
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        
            this.DataContext = ps;
        }
        
        這樣可以產生什麼效果呢?

        1. 如同一開始說的,這樣可以把PlanList裡面的plan的name顯示在list上
        2. 當資料更新的時候,List裡面的資料也會自己更新。要注意的是,因為我們是希望PlanList會自動更新,因此他必須是ObservableCollection的資料型態。
        再進一步,假設同一頁中有另外一個ListView想要顯示PlanListView中被選擇項目的Item的DisplayName。舉例來說,假設PlanList裡面有兩個Plan的物件,他們的Name 分別是plan1 跟plan2,那這個時候PlanListView就會顯示plan1 和plan2 。假設plan1裡也有兩個item他們的DisplayName分別是 item1 跟item2因此我希望當選擇testplan1的時候另外一個ListView可以顯示item1跟item2 e.g. TestPlan Item plan1 ----item1 | --item2 plan2 利用binding可以快速地達到這個目的,我們只要這樣設計另外一個ListView如下
                                                  
        
            
            
                        
                    
                
            
            
        
        

        Windows 8 Metro Style App Decoration Design Pattern

        目的是這樣的: 有多個頁面想要使用相同判斷邏輯的APP BAR
        因此,原本想要使用class繼承button 的control 的方式但是會有error,所以後來想到的方法是在原本的frame裡面嵌一個frame假設叫frame1,然後只要把子頁面的東西顯示在frame1上就可以達到這目的了。

        2012年4月18日 星期三

        Android SurfaceFlinger Part 2-1 - Android View System

        這裡要解釋的正式左邊的這張圖,一個app開起來之後不管是建立一般的物件或是使用skia這個2D的繪圖的library最後都是在放在surface上面。接下來要討論的就是在一個app建立之後view的建立、surface的建立和如何與surfaceflinger做溝通。


        在Android的app來說,他的外表示靠Activity來建立的,因此我們就從Activity開始來看。當一個 app要被啟動是利用zygote去fork一個process來啟動。最先會跑到 main()@ActivityThread.java 然後直到handleLaunchActivity()@ActivityThread.java 來建立Activity。

        handleLaunchActivity()@ActivityThread.java
        //利用performLaunchActivity()回傳一個activity
        Activity a = performLaunchActivity(r, customIntent);
        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward);
            ...
        }
        ...
        

        我們可以發現藉由performLaunchActivity()來建立一個activity之後呼叫handleResumeActivity() 先了解一下performLaunchActivity好了

        performLaunchActivity()@ActivityThread.java
            ...
           
           //launch一個activity
           activity = mInstrumentation.newActivity(
                            cl, component.getClassName(), r.intent);
            ...
           activity.mCalled = false;
          
           //在這裡呼叫activity裡頭的onCreate()
           mInstrumentation.callActivityOnCreate(activity, r.state);
           if (!activity.mCalled) {
               throw new SuperNotCalledException(
               "Activity " + r.intent.getComponent().toShortString() +
               " did not call through to super.onCreate()");
           }
           r.activity = activity;
           r.stopped = true;
           ...
        return activity;
        }
        

        我們繼續看一下mInstrumentation.newActivity() 的部份。

        newActivity()@Instrumentation.java
        public Activity newActivity(Class clazz, Context context, 
                    IBinder token, Application application, Intent intent, ActivityInfo info, 
                    CharSequence title, Activity parent, String id,
                    Object lastNonConfigurationInstance) throws InstantiationException, 
                    IllegalAccessException {
                Activity activity = (Activity)clazz.newInstance();
                ActivityThread aThread = null;
                
                //設定一些activity的東西
                activity.attach(context, aThread, this, token, application, intent,
                        info, title, parent, id,
                        (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
                        new Configuration());
                return activity;
            }
        
        
        我們看一下activity.attach()  

        attach()@Activity.java
        ...
        
        //利用PolicyManager.makeNewWindow來產生一個window
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ...
        
        那PolicyManager.makeNewWindow()是什麼呢?

        makeNewWindow()@PolicyManager.java
            ...
            static {
                // Pull in the actual implementation of the policy at run-time
                try {
                    //在run time的時候讀取
                    //frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
                    //一個PhonePolicy
                    Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
                    sPolicy = (IPolicy)policyClass.newInstance();
                } catch (ClassNotFoundException ex) {
                    ...
                }
            }
        
            // The static methods to spawn new policy-specific objects
            public static Window makeNewWindow(Context context) {
                return sPolicy.makeNewWindow(context);
            }
        
        如果我們去看Policy.java就可以發現真正的makeNewWindow了

        makeNewWindow()@Policy.java
        public Window makeNewWindow(Context context) {
                return new PhoneWindow(context);
        }
        
        回到performLaunchActivity(),他會去呼叫Activity裡面的onCreate()而在這裡面我們會利用setContentView()來設定我們的UI,接著看一下handleResumeActivity()

        handleResumeActivity()@ActivityThread.java
             ...
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            
            //取得decorView
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            
            //取得WindowManager
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                
                //將取得的 decor加入取得的WindowManager
                wm.addView(decor, l);
            }
        
            
        } 
               ...
        
        
        這裡有兩個地方可以看的。第一個這個decorView是什麼呢?第二個,這個WindowManager又是哪來的呢? 第一部份,r.window.getDecorView()的前面r.window是什麼呢?剛剛我們追過就是phoneWindow所以getDecorView()我們就直接看
        PhoneWindow.getDecorView()

        getDecorView()@PhoneWindow.java
          
        @Override
            public final View getDecorView() {
                if (mDecor == null) {
                    installDecor();
                }
                return mDecor;
            }
        private void installDecor() {
                if (mDecor == null) {
                    mDecor = generateDecor();
                    mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                    mDecor.setIsRootNamespace(true);
                }
                if (mContentParent == null) {
                    //generateLayout input是mDecor return 一個viewgroup
                    mContentParent = generateLayout(mDecor);
              ...
        }
        
        protected ViewGroup generateLayout(DecorView decor) {
                ...
                //取得title 並加入decor中
                View in = mLayoutInflater.inflate(layoutResource, null);
                decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
                
                //findViewById實際上是return getDecorView().findViewById(id);
                //他會去尋找{@link android.app.Activity#onCreate}所定義的xml
                ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
                if (contentParent == null) {
                    throw new RuntimeException("Window couldn't find content container view");
                }
                ...
                return contentParent;
        }
        

        因此我們可以發現,最剛開始windows manager裡面的view是一個PhoneWindow而PhoneWindow裡面是一個decorView裡面又包含了programmer定義的view 這是一個Decorator的design pattern 接著分析完了handleResumeActivity中的view, viewmanager之後我們繼續看 wm.addView(decor,l)

        addView()@Window.java
        public final void addView(View view, ViewGroup.LayoutParams params) {
                    // Let this throw an exception on a bad params.
                    WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;
                    CharSequence curTitle = wp.getTitle();
                    
                    ...
                    super.addView(view, params);
                }
        
        而LocalWindowManager 他是extends WindowManagerImpl.CompatModeWrapper, 因此我們去看一下WindowManagerImpl 的CompatModeWrapper才知道addView是寫些什麼

        addView()@WindowManagerImpl.java
        public void addView(View view, ViewGroup.LayoutParams params) {
                addView(view, params, null, false);
        }
        
        private void addView(View view, ViewGroup.LayoutParams params,
                    CompatibilityInfoHolder cih, boolean nest) {
                
                ...
                //ViewRootImpl是我們接下來的重點
                ViewRootImpl root;
                View panelParentView = null;
                
                synchronized (this) {
                    ...
                    
                    root = new ViewRootImpl(view.getContext());
                    root.mAddNesting = 1;
                    ...
                    mViews[index] = view;
                    mRoots[index] = root;
                    mParams[index] = wparams;
                }
                // do this last because it fires off messages to start doing things
                //這裡的view是我們一開始addView(mDecor,l)中的mDecor
                root.setView(view, wparams, panelParentView);
            }
        
        我們進入ViewRootImpl了解一下

        ViewRootImpl.java
        //ViewRootImpl繼承了Handler
        public final class ViewRootImpl extends Handler implements ViewParent,
                View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
            //這裡建立一個surface 的 object
            private final Surface mSurface = new Surface();
            //還有一個W 的object, W這個class 是繼承IWindow.stub
            final W mWindow;
            ....
            public ViewRootImpl(Context context) {
                ....
                //初始化對
                getWindowSession(context.getMainLooper());
                ....
                mWindow = new W(this);
                ....
            }
            ....
        }
        public static IWindowSession getWindowSession(Looper mainLooper) {
                synchronized (mStaticInit) {
                    if (!mInitialized) {
                        try {
                            InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
                            //open session透過Binder與WindowManagerService做溝通做溝通
                            sWindowSession = Display.getWindowManager().openSession(
                                    imm.getClient(), imm.getInputContext());
                            mInitialized = true;
                        } catch (RemoteException e) {
                        }
                    }
                    return sWindowSession;
                }
        }
        
        ok看完了ViewRootImpl的一些global variable及constructure之後我們回到剛剛的root.setView() setView()@ViewRootImpl.java
        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                synchronized (this) {
                    if (mView == null) {
                        mView = view;
                       ...
        
                        // Schedule the first layout -before- adding to the window
                        // manager, to make sure we do the relayout before receiving
                        // any other events from the system.
                        requestLayout();//實際上就是scheduleTraversals()
                        ...
                        try {
                            mOrigWindowType = mWindowAttributes.type;
                            res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
                                    getHostVisibility(), mAttachInfo.mContentInsets,
                                    mInputChannel);
                        }
                        ...
            }
        public void requestLayout() {
                checkThread();
                mLayoutRequested = true;
                
                scheduleTraversals();
        }
        public void scheduleTraversals() {
                if (!mTraversalScheduled) {
                    mTraversalScheduled = true;
        
                    //noinspection ConstantConditions
                    if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
                        final long now = System.nanoTime();
                        Log.d(TAG, "Latency: Scheduled traversal, it has been "
                                + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
                                + "ms since the last traversal finished.");
                    }
        
                    sendEmptyMessage(DO_TRAVERSAL);
                }
        }
        public void handleMessage(Message msg) {
                switch (msg.what) {
                ...
                case DO_TRAVERSAL:
                    ...
        
                    performTraversals();
        
                    ...
        }
        private void performTraversals() {
           .... 
           //取得一個surface
           relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
           ....
           //從surface裡面取得一個canvas然後畫在上面再把他show到螢幕
           draw(fullRedrawNeeded);
           ....
        }
        private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
                    boolean insetsPending) throws RemoteException {
        
                ....
               //透過IWindowSession與WindowManagerService溝通取得一個surface 
               int relayoutResult = sWindowSession.relayout(
                        mWindow, mSeq, params,
                        (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                        (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                        viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0,
                        mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
                        mPendingConfiguration, mSurface);
                //Log.d(TAG, "<<<<<< BACK FROM relayout");
                
                ....
                return relayoutResult;
            }
        
        private void draw(boolean fullRedrawNeeded) {
                //mSurface是剛剛ViewRootImpl的global variable
                Surface surface = mSurface;
                ...
                
                if (!dirty.isEmpty() || mIsAnimating) {
                    Canvas canvas;
                    try {
                        int left = dirty.left;
                        int top = dirty.top;
                        int right = dirty.right;
                        int bottom = dirty.bottom;
        
                        ....
                        //lock一塊canvas
                        canvas = surface.lockCanvas(dirty);
        
                        ....
                            try {
                                ....
                                //把東西畫到canvas上
                                mView.draw(canvas);
                            } finally {
                                ....
                            }
        
                            ....
                        }
        
                    } finally {
                        //把canvas show到螢幕上
                        surface.unlockCanvasAndPost(canvas);
                    }
                }
        
                ....
                if (animating) {
                    mFullRedrawNeeded = true;
                    scheduleTraversals();
                }
            }
        
        總結一下,在這一篇我們說了
        1. 建立一個Activity之後會有一個PhoneWindow 這個PhoneWindow裡面包含了DecorView他是一個framelayout 我們的app所畫得ui會包含在Decorview裡頭 
        2. 建立一個Activity之後也會產生一個LocalWindowManager主要實做在WindowManagerImpl而他包含了ViewRootImpl
        3. ViewRootImpl包含了一個view指向DecorView及透過Binder從WindowServiceManager取得的surface
        4. 要畫出ui首先透過mSurface lock一塊canvas 然後透過mView.draw畫在上面 然後unlock 之後把他show在螢幕上

        此篇是依照鄧凡平的Android系統原理深入解析 去trace 而成