4/29/2018

Raspberry Pi 의 Device Tree 및 cmdline 수정

1. Raspberry Pi Booting 및 설정 분석

  • Raspberry Pi Boot관련정보 
  1. cmdline.txt : kernel cmdline 수정가능 
  2. config.txt : Raspberry 설정 파일 
  3. bootcode.bin : Raspberry bootloader로 SoC에의해 Loading 되고 start*.elf를 로딩
  4. start.elf, start_x.elf, start_db.elf, start_cd.elf : firmware로 사용되지만 elf파일형식 
  5. fixup.dat, fixup_x.dat, fixup_db.dat, fixup_cd.dat : start*.elf의 linker file 
  6. *.dtb : Device Tree Blob

  https://wikidocs.net/3198
  https://wikidocs.net/18317


1.1 /boot 파일 확인 

일단 아래를 보면, dtb 파일과 kernel.img가 많아서 현재 사용중인 이미지를 알아야겠다.
추측을 하면 아래의 것을 사용할 것으로 짐작

$ cd /boot
bcm2708-rpi-0-w.dtb     bcm2709-rpi-2-b.dtb       bootcode.bin   fixup_cd.dat  issue.txt         LICENSE.oracle  start.elf
bcm2708-rpi-b.dtb       bcm2710-rpi-3-b.dtb       cmdline.txt    fixup.dat     kernel7.img       overlays        start_x.elf
bcm2708-rpi-b-plus.dtb  bcm2710-rpi-3-b-plus.dtb  config.txt     fixup_db.dat  kernel.img        start_cd.elf
bcm2708-rpi-cm.dtb      bcm2710-rpi-cm3.dtb       COPYING.linux  fixup_x.dat   LICENCE.broadcom  start_db.elf


1.2 DTS 와 DTB 구조 복습 (Device Tree) 

Device Tree 의 기본이해 (관련예제도 링크)
  https://ahyuo79.blogspot.com/2015/08/kernel-boot-kernel-device-tree.html
  https://www.elinux.org/images/a/ad/Arm-soc-checklist.pdf

Device Tree Spec (v0.2 와 v0.1) 및 각 문법 이해 (필독)
  https://www.devicetree.org/specifications/
  https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.2
  https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.1

Device Tree 활용
  https://elinux.org/Device_Tree_Usage


  • DTS의 기본구조 
/ 로 시작하여 model과 아래 좌측을 부터 정의하기 시작하여 구성

  • 상위기본구성 분석
  1. / 의 model= MACHINE 정보이름과 compatibale 에 해당 arch를 선택 (MACHINE 정보)
  2.  #address-cells 1 , #size-cells  1 의 의미는 
    1. child의 address cell u32 갯수와 size cell u32 갯수 말하며 이는 reg에 적용 
  3. memory@0 의 의미는 memory address 0 의미 
    1. reg = <0  0x20000000) address 와 size 
  4. uart@fe00100 의 의미는 uart 의 address는 0xfe00100 
    1. reg = <0xfe001000 0x100 > 은 address 와 size 



  • Sample DTS (Deivce Tree Syntax) 구조 (상위 Spec 문서참조)
아래와 같이 DTS도 Version 이 존재하며, 각 Version 별로 문법이 변경될 것이므로 이부분을 반드시 확인
C와 같이 Preprocessor가 먼저 진행 빌드되므로 이점 주의 
$ vi arch/arm/boot/dts/mysample.dts  //kernel or uboot 
/dts-v1/;  // device tree spec 를 반드시 참조하며, 각 node의 manual 별도로 보자 

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>

//커널의  ./include/dt-bindings/  
//커널의 ./scripts/dtc/include-prefixes/dt-bindings    
// dtc는 ./scripts/dtc 에 존재 

#include "sample.dtsi"


// 아래와 같이 C처럼 Preprocess(상위 include)가 동작가능하며, 주석도 /**/ // 둘다 가능

#define TEST  //preprocess 도 동작가능 C처럼 Preprocess가 다른 것 보다 먼저 실행되는 것을 주의  

#if 1 // jhlee 

#endif

#ifndef TEST

#endif

#ifdef TEST

#endif

#if defined(TEST) //jhlee

#else //orgin

#endif




/ {   // / { 는 DTS Main을 의미하며, 만약 sample.dtsi 이 것이 포함이 되어 있다면, 그곳에 위치에 들어간다고 생각하면됨

//보통 Kernel에서 model 에 정보를 넣으면, 부팅시 Machine Name 확인가능 
//조금 고급스럽게 하고자 하면 색 정보도 같이 넣어 만들자  
//sample.dtsi 이미 있지만, 중복으로 넣어 최종이것으로 빌드하면 최종값으로 적용가능  
        //model = "fsl,mpc8572da";     
        //MACHINE 정보 Name이며 마음대로 변경,DT_MACHINE_START (Device Tree용) or MACHINE_START         
        model = "EVM_JHLEE";       
        compatible = "fsl,mpc8572da";       //board 호환성이며, "manufacturer,model" kernel 검색 
};
// }; 는 상위 model 때문에 삽입 

#if 1 //아래 다시 중복설정할 경우 이것이 최종 값으로 결정되며, model만 재선언 (제조사 생략)하여, 내맘대로 변경    
/ {
      // Kernel Boot시 색깔까지 넣어 표시   
      model = "\x1b[1m \x1b[93m  Jeonghun Board Ver 0.0 \x1b[0m"; // Machin Name을 변경      
};
#endif 

// 주석 
/* 주석 */


/* 
보통 gpio node가 구성되고, PINMUX에서 GPIO로 설정하면, User에서 /sys/class/gpio/export 를 이용하여 직접사용가능 
GPIO4_18  보통 (4-1)*32+18 = 114  
GPIO4_19  보통 (4-1)*32+18 = 115
*/
/*
 /sys/class/gpio/export 
 /sys/class/gpio/gpio114/direction
 https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
 항상 compatible 에서 device driver와 연결하므로, Kernel에서 Pinmux를 하였다고, 자동으로 /sys/class/gpio/export에 적용되지 않는다 
 */ 

  • 필요없는 부분 중복된 부분삭제 관련예제
  1. /delete-node/      : node or lable 삭제
  2. /delete-property/ : node 의 property 삭제
  3. /omit-if-no-ref/    : dtc에게 사용하지 않는다면, 삭제 
  https://elinux.org/Device_Tree_Source_Undocumented

상위에서 설명했듯이 중복많이 허용하며, 최종선언된 dts기준으로 파싱을 시작하기때문에 필요없다면, 
나만의 dts를 만들경우 최종 dts include하고 필요없는 것을 정리하도록하자  

// 이미 이전에 선언된 uart1 label 의 property의 status를 okay ->disabled 변경 
&uart1 {
   status = "disabled";
};

// 이미 이전에 선언된 uart1 lalel 의 특정 property만 삭제 
&uart1 {
    /delete-property/ fsl,uart-has-rtscts;
};


/* 

node의 구성 은 보통 label: node로  or node로만 구성될 수가 있으며, label에는 &를 사용 

label: node  

adv_bridge0: adv7535@3d {
...
};

adv_bridge1: adv7535@3d {
...
};
*/
 

// node를 삭제방법은 label에는 &사용하며, node 를 직접입력할 경우 &불필요

/delete-node/ &adv_bridge0;
/delete-node/ &adv_bridge1;

  • OS에서는 호환성목적으로 aliases 기능을 사용
각 Chip label 이름이 다 다르게 정의될 수 있으므로, OS가 알수 있도록 이를 변경 
// aliases는 node의 새로 naming 하여 쉽게 node구성하며, 주로 OS 즉 linux 호환성을위해서 사용해준다  
// 구성은 이전과 같이 label에 &사용
aliases {
     rtc0 = &snvsrtc;
     rtc1 = &rtc;
};


chosen {
      bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};

https://elinux.org/Device_Tree_Usage#aliases_Node

  • #xxx-cells 의 의미 
child의 cells 의 갯수를 정의해주는 것으로 기본적인 address-cells 과 size-cells 이 존재한다.
더불어 다양한 #로 시작하는 cells들이 존재 
/dts-v1/;

/ {
    #address-cells = <1>; // address-cells의 의미 child의 reg의 address u32 갯수  
    #size-cells = <1>;    // size-cells 의 의미 child의 reg의 size u32 갯수  

    ...

    serial@101f0000 {
        compatible = "arm,pl011";
        reg = <0x101f0000 0x1000 >; // 상위 address-cells 수(1) 와 size-cells 수(1)로 결정
    };

    serial@101f2000 {
        compatible = "arm,pl011";
        reg = <0x101f2000 0x1000 >;
    };

    gpio@101f3000 {
        compatible = "arm,pl061";
        reg = <0x101f3000 0x1000
               0x101f4000 0x0010>; // 상위 address-cells 수(1) 와 size-cells 수(1)로 결정 연속 2개 
    };

    interrupt-controller@10140000 {
        compatible = "arm,pl190";
        reg = <0x10140000 0x1000 >;
    };

    spi@10115000 {
        compatible = "arm,pl022";
        reg = <0x10115000 0x1000 >;
    };

    ...

https://elinux.org/Device_Tree_Usage#Memory_Mapped_Devices
};

  • range 의 의미 
OS는 virtual address를 사용하며, 이를 위해서 memory map-io로 연결하여 사용한다. 
이때 각 영역의 부분의 정의해주는 것이 range의 기능이다. 
/dts-v1/;

/ {
    compatible = "acme,coyotes-revenge";
    #address-cells = <1>;  //parent의 reg의 address u32 수  
    #size-cells = <1>     //parent의 reg의 size u32 수  
    ...
    external-bus {
        #address-cells = <2>;  //child의 reg의 address u32 수  
        #size-cells = <1>;     //child의 reg의 size u32 수  
 
/*
range(Address Translation) 는 address translation을 위한 list 구성 
   1. the child's #address-cells value(2),  
   2. the parent's #address-cells value(1), 
   3. the child's #size-cells(1) 
마지막 size로 memory map을 하여 영역 넣어구성 아래와 같이 mapping 

- Offset 0 from chip select 0 is mapped to address range 0x10100000 - 0x1010ffff
- Offset 0 from chip select 1 is mapped to address range 0x10160000 - 0x1016ffff
- Offset 0 from chip select 2 is mapped to address range 0x30000000 - 0x30ffffff

*/
        ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
                  1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
                  2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash

        ethernet@0,0 {
            compatible = "smc,smc91c111";
            reg = <0 0 0x1000>;  // 상위 child address 2개 와 size 1  
        };

        i2c@1,0 {
            compatible = "acme,a1234-i2c-bus";
            #address-cells = <1>; // node child address u32 수  
            #size-cells = <0>;    // node child reg의 e u32 수  
            reg = <1 0 0x1000>;   // 상위 child address  2개 와 size 1  
            rtc@58 {
                compatible = "maxim,ds1338";
                reg = <58>;             //상위 node child (1,0) 
            };
        };

        flash@2,0 {
            compatible = "samsung,k8f1315ebm", "cfi-flash";
            reg = <2 0 0x4000000>;
        };
    };
};
https://elinux.org/Device_Tree_Usage#Ranges_.28Address_Translation.29
https://elinux.org/Device_Tree_Usage#PCI_Host_Bridge
https://elinux.org/Device_Tree_Usage#How_Interrupts_Work



Kernel GPIO 기본사용법
  https://www.kernel.org/doc/Documentation/gpio/gpio-legacy.txt

  • Device Tree 관련문서
Device Tree는 현재 각 Platform Device Driver마다 각각의 설정값들이 존재하는데, 관련부분을 아래에서 검색해서 찾자
  https://www.kernel.org/doc/Documentation/devicetree/bindings/

  • DTB (Flattened Device Tree 라고 불림) 의 구조 





1.3 Device Tree 비교 및 확인 

Raspberry Pi에 dtc 설치 되었는지 확인

  • 사용중인 Device Tree를 확인
/proc/device-tree 정보를 dtc를 이용하여 dts 파일생성방법, 역으로 생성했기때문에 원래사용하던 dts와는 완전동일할 수는 없다.

$ dtc -I fs -O dts -o current.dts /proc/device-tree/ 

  • DTB To DTS로 변경 
dtb 파일을 dts 파일로 역으로 변경했으므로, 상위와 같이 원래 dts와 완전동일할 수가 없음

$ sudo dtc -I dtb -O dts -o bcm2710-rpi-3-b-plus.dts /boot/bcm2710-rpi-3-b-plus.dtb
$ sudo dtc -I dtb -O dts -o bcm2710-rpi-3-b.dts /boot/bcm2710-rpi-3-b.dtb
$ sudo dtc -I dtb -O dts -o bcm2708-rpi-b-plus.dts /boot/bcm2708-rpi-b-plus.dtb

  • DTS 비교 
사용 중인  DTS 상위 DTB 다르기 때문에 일단 상위 /boot의 dtb 사용을 의심.

$ diff -urN current.dts bcm2710-rpi-3-b-plus.dts 
--- current.dts 2019-02-18 23:34:57.966477519 +0900
+++ bcm2710-rpi-3-b-plus.dts 2019-02-18 23:36:18.841755013 +0900
@@ -1,1159 +1,941 @@
 /dts-v1/;
 
+/memreserve/ 0x0000000000000000 0x0000000000001000;
 / {
- compatible = "raspberrypi,3-model-b", "brcm,bcm2837";
- serial-number = "000000003befb7f8";
- model = "Raspberry Pi 3 Model B Rev 1.2";
- memreserve = 0x3b400000 0x4c00000="";
+ compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837";
+ model = "Raspberry Pi 3 Model B+";

$ diff -urN current.dts bcm2710-rpi-3-b.dts 
--- current.dts 2019-02-18 23:34:57.966477519 +0900
+++ bcm2710-rpi-3-b.dts 2019-02-18 23:42:58.263596240 +0900
@@ -1,1159 +1,940 @@
 /dts-v1/;
 
+/memreserve/ 0x0000000000000000 0x0000000000001000;
 / {
  compatible = "raspberrypi,3-model-b", "brcm,bcm2837";
- serial-number = "000000003befb7f8";
- model = "Raspberry Pi 3 Model B Rev 1.2";
- memreserve = <0x3b400000 0x4c00000="">;
+ model = "Raspberry Pi 3 Model B";
  interrupt-parent = <0x1>;
  #address-cells = <0x1>;
  #size-cells = <0x1>;

$ diff -urN current.dts bcm2708-rpi-b-plus.dts | head -n 30
--- current.dts 2019-02-18 23:34:57.966477519 +0900
+++ bcm2708-rpi-b-plus.dts 2019-02-18 23:51:14.696159160 +0900
@@ -1,1235 +1,1066 @@
 /dts-v1/;
 
+/memreserve/ 0x0000000000000000 0x0000000000001000;
 / {
- compatible = "raspberrypi,3-model-b", "brcm,bcm2837";
- serial-number = "000000003befb7f8";
- model = "Raspberry Pi 3 Model B Rev 1.2";
- memreserve = <0x3b400000 0x4c00000="">;
+ compatible = "brcm,bcm2835";
+ model = "Raspberry Pi Model B+";
  interrupt-parent = <0x1>;
  #address-cells = <0x1>;
  #size-cells = <0x1>;

  • bootcode 분석
현재 사용중인 Device Tree를 정확하게 잘 몰라 다른 bootloader 를 분석했지만, 역시나. grep을 이용하여 dtb를 찾으려해도 찾지를 못했다.

$ hd /boot/bootcode.bin 
...
0000c6e0  75 70 0a 00 46 61 69 6c  65 64 20 74 6f 20 69 6e  |up..Failed to in|
0000c6f0  69 74 69 61 6c 69 73 65  20 6c 69 6e 6b 0a 00 00  |itialise link...|
0000c700  49 6e 69 74 69 61 6c 69  73 65 20 68 75 62 0a 00  |Initialise hub..|
0000c710  46 6f 75 6e 64 20 25 64  20 70 6f 72 74 73 2c 20  |Found %d ports, |
0000c720  6d 75 6c 74 69 5f 74 74  20 3d 20 25 64 0a 00 53  |multi_tt = %d..S|
0000c730  65 74 74 69 6e 67 20 69  6e 74 65 72 66 61 63 65  |etting interface|
0000c740  20 25 64 0a 00 46 61 69  6c 65 64 20 74 6f 20 73  | %d..Failed to s|
0000c750  65 74 20 69 6e 74 65 72  66 61 63 65 20 25 64 0a  |et interface %d.|
0000c760  00 45 6e 61 62 6c 69 6e  67 20 50 4f 52 54 20 50  |.Enabling PORT P|
0000c770  4f 57 45 52 20 6f 6e 20  70 6f 72 74 20 25 64 0a  |OWER on port %d.|
0000c780  00 46 61 69 6c 65 64 20  74 6f 20 73 65 74 20 66  |.Failed to set f|
0000c790  65 61 74 75 72 65 20 48  55 42 5f 46 45 41 54 55  |eature HUB_FEATU|
0000c7a0  52 45 5f 50 4f 52 54 5f  50 4f 57 45 52 20 25 64  |RE_PORT_POWER %d|
0000c7b0  0a 00 57 61 69 74 69 6e  67 20 66 6f 72 20 64 65  |..Waiting for de|
0000c7c0  76 69 63 65 73 20 74 6f  20 72 65 73 70 6f 6e 64  |vices to respond|
0000c7d0  20 74 6f 20 72 65 73 65  74 0a 00 46 6f 75 6e 64  | to reset..Found|
0000c7e0  20 64 65 76 69 63 65 20  6f 6e 20 70 6f 72 74 20  | device on port |
0000c7f0  25 64 0a 00 46 6f 75 6e  64 20 68 69 67 68 73 70  |%d..Found highsp|
0000c800  65 65 64 20 64 65 76 69  63 65 0a 00 48 75 62 20  |eed device..Hub |
0000c810  64 65 76 69 63 65 20 66  6f 75 6e 64 20 61 74 20  |device found at |
0000c820  61 64 64 72 20 25 64 2c  20 65 6e 75 6d 65 72 61  |addr %d, enumera|
0000c830  74 69 6e 67 20 48 55 42  0a 00 44 65 76 69 63 65  |ting HUB..Device|
0000c840  20 66 6f 75 6e 64 3a 20  74 79 70 65 20 3d 20 25  | found: type = %|
0000c850  73 2c 20 61 64 64 72 20  3d 20 25 64 0a 00 4d 61  |s, addr = %d..Ma|
0000c860  73 73 20 73 74 6f 72 61  67 65 00 45 74 68 65 72  |ss storage.Ether|
0000c870  6e 65 74 20 61 64 61 70  74 65 72 00 49 67 6e 6f  |net adapter.Igno|
0000c880  72 69 6e 67 20 6c 6f 77  20 73 70 65 65 64 20 64  |ring low speed d|
0000c890  65 76 69 63 65 0a 00 44  65 76 69 63 65 20 66 61  |evice..Device fa|
0000c8a0  69 6c 65 64 20 74 6f 20  72 65 73 70 6f 6e 64 20  |iled to respond |
0000c8b0  74 6f 20 72 65 73 65 74  0a 00 00 00 79 00 00 00  |to reset....y...|
....

$ strings /boot/bootcode.bin | grep dtb

  • 제공하는 Kernel Image 분석 
/boot/kernel.img 와 kernel7.img는 아래와 같이 다르게 나오며, 압축때문인 것으로 생각이 된다.

$ cat /proc/version 
Linux version 4.14.34-v7+ (dc4@dc4-XPS13-9333) (gcc version 4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611)) #1110 SMP Mon Apr 16 15:18:51 BST 2018

$ hd /boot/kernel.img | grep 'Linux'

$ strings /boot/kernel.img | grep 'Linux'
Uncompressing Linux...

$ strings /boot/kernel7.img | grep 'Linux'
Uncompressing Linux...


uImage라고 한다면, 64 byte의 헤더를 버려야 하지만, 상위 이미지를 보면 uImage 구조와 다른 것 같다.
zImage라고 생각이 들어 아래와 같이 시도해본다.


uImage 구조체
  http://www.isysop.com/unpacking-and-repacking-u-boot-uimage-files/
  http://forum.falinux.com/zbxe/index.php?document_srl=564748&mid=lecture_tip

zImage 구조
  https://wiki.kldp.org/KoreanDoc/html/EmbeddedKernel-KLDP/kernel-image-file-structure.html

zImage (LZMA)
  https://stackoverflow.com/questions/37672417/getting-kernel-version-from-the-compressed-kernel-image

GPIO
  https://www.kosagi.com/w/index.php?title=Definitive_GPIO_guide
  • 사용중인 Kernel 분석 
사용중인 Kernel은 잘 나온다. (압축이 되지 않았으니 잘나오는 것 같다)

$ sudo strings /dev/mem | grep 'Linux'   
....
Booting Linux on physical CPU 0x0
Linux version 4.14.34-v7+ (dc4@dc4-XPS13-9333) (gcc version 4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611)) #1110 SMP Mon Apr 16 15:18:51 BST 2018