2013年9月26日 星期四

x86 machine code 初探 (6) - displacement-only

mod reg r/m 當 r/m 是 101 時, 也是例外。這表示 displacement only。

ex1:
address_mode.S
 1 # practice x86 machine code
 2 #.code16
 3 .code32
 4 .text
 5 .global begin
 6 begin:
 7   mov val, %esi
 8
 9 val:
10 .int

這個竟然這麼麻煩, 要出動 readelf

readelf -a   address_mode.elf 
Symbol table '.symtab' contains 14 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000100     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 SECTION LOCAL  DEFAULT    2 
     3: 00000000     0 SECTION LOCAL  DEFAULT    3 
     4: 00000000     0 SECTION LOCAL  DEFAULT    4 
     5: 00000000     0 SECTION LOCAL  DEFAULT    5 
     6: 00000000     0 FILE    LOCAL  DEFAULT  ABS address_mode.o
     7: 00000106     0 NOTYPE  LOCAL  DEFAULT    1 val
     8: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
     9: 00000106     0 NOTYPE  GLOBAL DEFAULT    1 _text_end
    10: 00000100     0 NOTYPE  GLOBAL DEFAULT    1 begin
    11: 00000106     0 NOTYPE  GLOBAL DEFAULT    1 _data_end
    12: 00000100     0 NOTYPE  GLOBAL DEFAULT    1 _text
    13: 00000106     0 NOTYPE  GLOBAL DEFAULT    1 _data

L7 : val 的位址 0x106
objdump -d address_mode.elf
8b 35 06 01 00 00     mov    0x106,%esi

mod reg r/m 35 => 00 110 101
mod, r/m 查表 DS:Disp32
reg 110 => %esi
disp32 => 06 01 00 00 => 00 00 01 06
b8 自己查。

ex2:

address_mode.S
 1 # practice x86 machine code
 2 #.code16
 3 .code32
 4 .text
 5 .global begin
 6 begin:
 7   movb $0x12, val
 8
 9 val:
10 .byte

gcc -m32  -g -Wall -Wextra -Werror -nostdlib -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-rtti -fno-stack-protector -c address_mode.S
ld -m elf_i386 -static -Tas.ld -nostdlib --nmagic -o address_mode.elf address_mode.o

objdump
c6 05 07 01 00 00 12  movb   $0x12,0x107


readelf -a   address_mode.elf 

Symbol table '.symtab' contains 14 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000100     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 SECTION LOCAL  DEFAULT    2 
     3: 00000000     0 SECTION LOCAL  DEFAULT    3 
     4: 00000000     0 SECTION LOCAL  DEFAULT    4 
     5: 00000000     0 SECTION LOCAL  DEFAULT    5 
     6: 00000000     0 FILE    LOCAL  DEFAULT  ABS address_mode.o
     7: 00000107     0 NOTYPE  LOCAL  DEFAULT    1 val
     8: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
     9: 00000107     0 NOTYPE  GLOBAL DEFAULT    1 _text_end
    10: 00000100     0 NOTYPE  GLOBAL DEFAULT    1 begin
    11: 00000107     0 NOTYPE  GLOBAL DEFAULT    1 _data_end
    12: 00000100     0 NOTYPE  GLOBAL DEFAULT    1 _text
    13: 00000107     0 NOTYPE  GLOBAL DEFAULT    1 _data
L7 : val 的位址 0x107

mod reg r/m 05 => 00 000 101
r/m 101

100:    c6 05 07 01 00 00 12     movb   $0x12,0x107
c6 查表 One-Byte Opcode Map Eb, Ib

ref:Intel_64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 3A, 3B and 3C
page Vol. 2C B-16

mov 指令格式
immediate to memory 1100 011w : mod 000 r/m : immediate data

應該是 Ib

I
Immediate data. The value of the operand is encoded in subsequent bytes of the instruction.

b
Byte (regardless of operand size attribute)

07 01 00 00 : displacement
12 : immediate

2013年9月21日 星期六

x86 machine code 初探 (5) - 兩個 modrm base/index with scale factor address mode

modrm 的 r/m:100 時, 這是一個特殊的記號, 代表下一個 byte 又是一個 modrm, 不過目前被稱為 SIB 欄位。Programming THE 80386 p71 稱這是 two-byte address mode encoding。

address_mode.S
1 # practice x86 machine code
2 #.code16
3 .code32
4 .text
5 .global begin
6 begin:
7   mov 24(%esp,%esi,8), %eax

objdump:
8b 44 f4 18 mov 0x18(%esp,%esi,8),%eax

att syntax: displacement(base, index, scale)

mov
8B  /r   MOV r16,r/m16     2/4           Move r/m word to word register
8B  /r   MOV r32,r/m32     2/4           Move r/m dword to dword register

44 -> mod reg r/m 01 000 100 reg:000 -> %eax
mod 01 表示 8bit displacement(0x18)
r/m 100, 所以下一個也是 modrm (SIB)

f4 -> mod reg r/m 11 110 100 這代表著
scale index base
11    110   100
8     %esi  %esp

若是 mode 10 表示 32 bit displacement。

ex:
8b 84 f4 32 54 76 98     mov    -0x6789abce(%esp,%esi,8),%eax
44 -> 84
od reg r/m 10 000 100

解說完畢。

2013年9月17日 星期二

作業系統之前的程式 for stm32f4discovery (6) - systick

和一般 stm32 書上的介紹不同, 我不想只單純的呼叫廠商寫好的 library, 這只要有手就會了, 你只想學到這樣的程度而已嗎?應該和我一樣不會滿足這樣的學習方式吧!我想搞懂到底是如何設定中斷, systick register, 參考別人的程式碼自然是最快的, 這是 scmrtos 的 systick 程式碼。

Ports/CortexM3/GCC/OS_Target_asm.S
.weak  __init_system_timer
__init_system_timer:

    LDR     R1, =NVIC_SYSPRI15      // Set the SysTick exception priority (lowest)
    LDR     R2, =NVIC_ST_PRI
    STRB    R2, [R1]

    LDR     R1, =NVIC_ST_RELOAD     // Setup SysTick
    LDR     R2, =(SYSTICKFREQ/SYSTICKINTRATE-1)
    STR     R2, [R1]
    LDR     R1, =NVIC_ST_CTRL       // Enable and run SysTick
    LDR     R2, =(NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_INTEN | NVIC_ST_CTRL_ENABLE)
    STR     R2, [R1]
    BX      LR

Ports/CortexM4F/GCC/OS_Target_cpp.cpp
#pragma weak __init_system_timer
extern "C" void __init_system_timer()
{
        SysTickPriority = 0xFF;
        SysTick->LOAD = SYSTICKFREQ/SYSTICKINTRATE-1;
        SysTick->CTRL = NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_INTEN | NVIC_ST_CTRL_ENABLE;
}


Ports/CortexM4F/GCC/OS_Target_cpp.cpp
// SysTick stuff
struct systick_t
{
    uint32_t       CTRL;
        uint32_t       LOAD;
        uint32_t       VAL;
        uint32_t const CALIB;
};

enum
{
    NVIC_ST_CTRL_CLK_SRC = 0x00000004,       // Clock Source.
    NVIC_ST_CTRL_INTEN   = 0x00000002,       // Interrupt enable.
    NVIC_ST_CTRL_ENABLE  = 0x00000001        // Counter mode.
};

systick_t volatile * const SysTick = (systick_t volatile * const)0xE000E010UL;

基本上只要參考這 3 個 register (0xe000e010, 0xe000e014, 0xe000e018), 設定完成後, systick isr 就可以工作了。以下是我自己的測試程式。

ref: L36 ~ L43, 這個程式應該不需要多做說明, 翻開這本書 arm cortex-m3: 嵌入式系統設計入門 p8-12, 對照一下就知道了。或是 Cortex™-M3 Technical Reference Manual p 8-8 (file name: DDI0337E_cortex_m3_r1p1_trm.pdf)

 x/1xw 0xe000e018 可以看到這個值會倒數, 變成 0 時就會呼叫 systick_isr。使用 gdb break point 會暫停倒數。

systick.S
 1 # test systick
 2
 3 .equ STACK_TOP, 0x20000800
 4 .text
 5 .global _start
 6 .code 16
 7 .syntax unified
 8 _start:
 9   .word STACK_TOP, start
10   .type start, function @ let lsb to 1
11   .word int_isr+1
12   .word int_isr+1
13   .word int_isr+1
14   .word int_isr+1
15   .word int_isr+1
16   .word int_isr+1
17   .word int_isr+1
18   .word int_isr+1
19   .word int_isr+1
20   .word int_isr+1
21   .word int_isr+1
22   .word int_isr+1
23   .word int_isr+1
24   .word systick_isr+1
25   .word int_isr+1
26   .word int_isr+1
27   .word int_isr+1
28
29 start:
30
31 @ ref:  arm cortex-m3: 嵌入式系統設計入門 p8-12
32 @ 0xe000e010, 0xe000e014, 0xe000e018
33 @ systick setting
34 @ 0xe000ed24 check systick isr active
35 @ 0xe000e018 check systick count
36   ldr r0, =0xe000e010 @ control reg
37   mov r1, #0
38   str r1, [r0]
39   ldr r1, =0x3fff
40   str r1, [r0, #4]
41   str r1, [r0, #8]
42   mov r1, #0x7
43   str r1, [r0] @ enable systick
44
45 @ watch 0xe000ed24, 0xe000ed04 ref: arm cortex-m3: 嵌入式系統設計入門 p8-9
46   mov r1, #0x33
47 deadloop:
48   mov r1, #0x22
49   b deadloop
50
51 systick_isr:
52   PUSH    {lr}
53   mov r0, #0x05
54   mov r1, #0x27
55   @bx lr
56   POP     {pc}
57
58 int_isr:
59   nop
60   movs r1, #0xf
61 @  push {r1}
62   bx lr

快了, 終於接近我要做的東西了。

arm cortex-m3: 嵌入式系統設計入門第 14 章 systick 計時器有更進階的說明, bit[31] 可以檢查是否有外部時脈來源可以當然 systick 時脈。8-13 表 8-12 校正暫存器 (0xe000e01c)

2013年9月14日 星期六

作業系統之前的程式 for stm32f4discovery (5) - pendsv interrupt

目的是要喚起 pendsv 這個 isr, 這個程式碼其實不難, 原理也不複雜, 可是卻花了好大功夫才搞定。

L41 ~ 43, L51 ~ 53, 在設定 priority, 預期在 svc_isr 之後, 要跳進 pendsv_handle。在 svc_isr 設定 pendsv 的 pending bit, 等著離開 svc_isr 之後被喚起。

嘗試很久都無法喚起 pendsv_handle, 都無法成功, 我甚至還另外寫了一個 c 的版本, c 的版本順利 work, 終於讓我找到問題。

問題是 ... 沒有問題, 這個程式從頭開始就是對的, 只是 pendsv_handle 並不是在離開 svc_isr 後立刻執行, 而是在過了一段時間之後才執行。真是奇怪阿!

由於這緣故, 讓我多花了好幾天解決這問題。

程式碼註解中有提到可 dump 哪些暫存器來觀察, 你有和我買一樣的書會很方便, 沒有的話就看:
Cortex™-M3 Technical Reference Manual (filename:DDI0337E_cortex_m3_r1p1_trm.pdf)

pendsv.S
  1 # test pendSV handler and systick
  2 
  3 .equ STACK_TOP, 0x20000800
  4 .text
  5 .global _start
  6 .code 16
  7 .syntax unified
  8 _start:
  9   .word STACK_TOP, start
 10   .type start, function @ let lsb to 1
 11   .word int_isr+1
 12   .word int_isr+1
 13   .word int_isr+1
 14   .word int_isr+1
 15   .word int_isr+1
 16   .word int_isr+1
 17   .word int_isr+1
 18   .word int_isr+1
 19   .word int_isr+1
 20   .word svc_isr+1 @ svc isr
 21   .word int_isr+1
 22   .word int_isr+1
 23   .word pendsv_handle+1
 24   .word systick_isr+1
 25   .word int_isr+1
 26   .word int_isr+1
 27   .word int_isr+1
 28 
 29 start:
 30 
 31   @ ref:  arm cortex-m3: 嵌入式系統設計入門 p8-10
 32 @  ldr r0, =0xe000ed0c
 33 @  ldr r1, =0x05fa0500
 34 @  strb r1, [r0]
 35 @
 36 @ ref: @ Cortex™-M3 Technical Reference Manual (file name: DDI0337E_cortex_m3_r1p1_trm.pdf)
 37 @    System Handler Priority Registers (p8-27)
 38   @ pendsv priority
 39 
 40 
 41   ldr r0, =0xe000ed22
 42   mov r1, #0xff
 43   strb r1, [r0]
 44 
 45   @ systick priority
 46   ldr r0, =0xe000ed23
 47   mov r1, #0xff
 48   strb r1, [r0]
 49 
 50   @ svc priority
 51   ldr r0, =0xe000ed1f
 52   mov r1, #0xff
 53   strb r1, [r0]
 54 
 55 
 56 @ systick setting
 57 #if 0
 58 @ 0xe000ed24 check systick isr active
 59 @ 0xe000e018 check systick count
 60   ldr r0, =0xe000e010 @ control reg
 61   mov r1, #0
 62   str r1, [r0]
 63   ldr r1, =0x3fff
 64   str r1, [r0, #4]
 65   str r1, [r0, #8]
 66   mov r1, #0x7
 67   str r1, [r0] @ enable systick
 68 #else
 69   svc 0
 70 #endif
 71 
 72 @  cpsie i
 73 
 74 @ watch 0xe000ed24, 0xe000ed04 ref: arm cortex-m3: 嵌入式系統設計入門 p8-9
 75   mov r1, #0x33
 76 deadloop:
 77   mov r1, #0x22
 78   b deadloop
 79 
 80 pendsv_handle:
 81   movs r2, #0x12
 82   nop
 83   bx lr
 84 
 85 svc_isr:
 86   push {r4-r11, lr}
 87 #if 1
 88   @ enable pendsv ref: STM32F207 高性能网络型 MCU 嵌入式系统设计 p 412
 89   ldr r0, =0xe000ed04 
 90   ldr r1, [r0]
 91   orr r1, #0x10000000
 92   str r1, [r0]
 93 #endif
 94   nop
 95   movs r3, #0x56
 96   pop {r4-r11, pc}
 97   #bx lr
 98 
 99 systick_isr:
100   PUSH    {R14}
101   @ enable pendsv ref: STM32F207 高性能网络型 MCU 嵌入式系统设计 p 412
102   ldr r0, =0xe000ed04 
103   ldr r1, [r0]
104   orr r1, #0x10000000
105   str r1, [r0]
106   movs r0, #0x98
107 @  bx lr
108   POP     {R14}
109 
110 int_isr:
111   nop
112   movs r1, #0xf
113 @  push {r1}
114   bx lr
115 
116 .data
117 .space  0x200, 0
118 psp_stack_top:
119 
120 .space  0x400, 0
121 msp_stack_top:
122 

以下為  gdb trace 的結果, 在 svc_isr, pendsv_handle 我加入一些說明。

gdb.result
 1 descent@descent-u:stm32f4_prog$ arm-none-eabi-gdb -x g.sh 
 2 GNU gdb (Sourcery CodeBench Lite 2012.03-56) 7.2.50.20100908-cvs
 3 Copyright (C) 2010 Free Software Foundation, Inc.
 4 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
 5 This is free software: you are free to change and redistribute it.
 6 There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
 7 and "show warranty" for details.
 8 This GDB was configured as "--host=i686-pc-linux-gnu --target=arm-none-eabi".
 9 For bug reporting instructions, please see:
10 <https://support.codesourcery.com/GNUToolchain/>.
11 Breakpoint 1 at 0x7c: file pendsv.S, line 89.
12 Breakpoint 2 at 0x74: file pendsv.S, line 82.
13 start () at pendsv.S:41
14 41        ldr r0, =0xe000ed22
15 (gdb) c
16 Continuing.
17 Note: automatically using hardware breakpoints for read-only addresses.
18 
19 Breakpoint 1, svc_isr () at pendsv.S:89
20 89        ldr r0, =0xe000ed04 
21 (gdb) x/1xw 0xe000ed04
22 0xe000ed04:     0x0000080b
23 (gdb) x/1xw 0xe000ed24
24 0xe000ed24:     0x00000080
進入 svc_isr 之後, dump 0xe000ed04, 0xe000ed24
從 0xe000ed04/0xe000ed24 register 知道 svc 正在執行
25 (gdb) n
26 90        ldr r1, [r0]
27 (gdb) n
28 91        orr r1, #0x10000000
29 (gdb) n
30 92        str r1, [r0]
31 (gdb) n
32 94        nop
33 (gdb) n
34 95        movs r3, #0x56
35 (gdb) x/1xw 0xe000ed04
36 0xe000ed04:     0x1000e80b
37 (gdb) x/1xw 0xe000ed24
38 0xe000ed24:     0x00000080
set pendsv pend bit, L28 
39 (gdb) n
40 96        pop {r4-r11, pc}
41 (gdb) n
42 start () at pendsv.S:75
43 75        mov r1, #0x33
44 (gdb) n
45 deadloop () at pendsv.S:77
46 77        mov r1, #0x22
47 (gdb) n
48 78        b deadloop
49 (gdb) n
50 77        mov r1, #0x22
51 (gdb) n
52 78        b deadloop
53 (gdb) n
54 77        mov r1, #0x22
55 (gdb) n
56 78        b deadloop
57 (gdb) n
58 77        mov r1, #0x22
59 (gdb) c
60 Continuing.
61 
62 Breakpoint 2, pendsv_handle () at pendsv.S:82
63 82        nop
離開 svc_isr 進入 deadloop 後才跳到 pendsv_handle 
64 (gdb) n
65 83        bx lr
66 (gdb) x/1xw 0xe000ed04
67 0xe000ed04:     0x0000080e
68 (gdb) x/1xw 0xe000ed24
69 0xe000ed24:     0x00000400
得知 pendsv 正在執行 
70 (gdb) n
71 deadloop () at pendsv.S:77
回到 deadloop 
72 77        mov r1, #0x22
73 (gdb) n
74 78        b deadloop
75 (gdb) n
76 77        mov r1, #0x22
77 (gdb) n
78 78        b deadloop
79 (gdb) c
80 Continuing.
81 ^C
82 Program received signal SIGTRAP, Trace/breakpoint trap.
83 deadloop () at pendsv.S:77
84 77        mov r1, #0x22

你一定會有點疑惑, 為什麼要介紹這個對吧!嗯 ... 讓我賣個關子。

ref:

2013年9月7日 星期六

作業系統之前的程式 for stm32f4discovery (4) - two stack

cm3 有 3 種工作模式, 開機位於 2。
  1. handle mode + privileged
  2. thread mode + privileged
  3. thread mode + unprivileged

雖然我英文不行, 但我實在被中文字困惑, 還是把這個術語的英文找出來, 閱讀整段英文對我來說並不容易, 但只記點單字倒還不是問題。

2.2 Privileged access and user access
Code can execute as privileged or unprivileged. Unprivileged execution limits or excludes access to some resources. Privileged execution has access to all resources.

Handler mode is always privileged. Thread mode can be privileged or unprivileged.

Thread mode is privileged out of reset, but you can change it to user or unprivileged by clearing the CONTROL[0] bit using the MSR instruction. User access prevents:

  • use of some instructions such as CPS to set FAULTMASK and PRIMASK
  • access to most registers in System Control Space (SCS).

When Thread mode has been changed from privileged to user, it cannot change itself back to privileged. Only a Handler can change the privilege of Thread mode. Handler mode is always privileged.

ref: Cortex-M3 TM Revision: r1p1 Technical Reference Manual 2.2

以上和本主題都無關, 我把主題放在這之後, 厲害吧!

這隻程式主要是使用 cm3 的 2 個 stack, 解釋使用 msp, psp 時, 這兩個 stack 如何變化。

two_stack.S
 1 # use msp, psp 2 stack
 2 
 3 .equ STACK_TOP, 0x20000800
 4 .text
 5 .global _start
 6 .code 16
 7 .syntax unified
 8 _start:
 9   .word STACK_TOP, start
10   .type start, function @ let lsb to 1
11   .word int_isr+1
12   .word int_isr+1
13   .word int_isr+1
14   .word int_isr+1
15   .word int_isr+1
16   .word int_isr+1
17   .word int_isr+1
18   .word int_isr+1
19   .word int_isr+1
20   .word int_isr+1 @ svc isr
21   .word int_isr+1
22   .word int_isr+1
23   .word int_isr+1
24   .word int_isr+1
25   .word int_isr+1
26   .word int_isr+1
27   .word int_isr+1
28 
29 start:
30   mrs r0, control @ read control to r0
31 
32   movs r0, #10
33 
34   ldr sp, =#msp_stack_top @ msp
35 
36   push {r0}
37 
38   ldr r0, =#psp_stack_top
39   msr psp, r0
40 
41   mov r0, #0x3
42   msr control, r0
43 
44   mrs r0, control
45 
46   push {r0}
47 
48   svc 0
49 
50 deadloop:
51   nop
52   b deadloop
53 
54 int_isr:
55   nop
56   movs r1, #0xf
57   push {r1}
58   bx lr
59 
60 .data
61 .space  0x200, 0
62 psp_stack_top:
63 
64 .space  0x400, 0
65 msp_stack_top:

34   ldr sp, =#msp_stack_top @ msp
開機時就會設定 msp, 不過這裡再設定一次

38   ldr r0, =#psp_stack_top
39   msr psp, r0
設定 psp

41   mov r0, #0x3
42   msr control, r0
從這裡之後, 藉由設定 control register, 會使用 psp, 並進入 thread/unprivileged mode。

before svc 0 (current is user access (thread/unprivileged) & use psp)

control        0x3    3 '\003'
msp 0x200005fc 0x200005fc
psp 0x200001fc 0x200001fc

after svc 0

control        0x1    1 '\001'
msp 0x200005fc 0x200005fc
psp 0x200001d8 0x200001d8

觀察 stack 變化, 使用 svc 跳到中斷常式後, 會 push 暫存器到 psp, 而在中斷常式會使用 msp 操作 stack。

在 L58 離開中斷常式後, psp 也回到原來值。

control        0x3    3 '\003'
msp            0x200005f8    0x200005f8
psp            0x200001fc    0x200001fc

再來的 stack 操作也是以 psp 為主。

2013年9月4日 星期三

[books] 一步步写嵌入式操作系统:ARM编程的方法与实践

20111212 購於台南成大若水堂, 39X6 = 234 NT
會買這本純粹是被裡頭的記憶體管理演算法吸引, 類似 linux slab 的方式來實作記憶體管理。

當時我 (2011) 在學習 os 的撰寫, 會關注相關書籍, 雖然我主要學習平台是 x86, 但也不排斥看 arm 的相關書籍。

本書簡單來說就是一步一步打造 arm os, 完成 os 所需的功能。

這本是個大驚奇, 薄薄一本, 它比我想像的還要有用, 對於學習寫 os, 裡頭提到的檔案系統、記憶體管理、中斷處理、分頁管理都有實作程式碼。但是她絕版了, 好在要找到還是很容易, 我在後續的技術文章上會多次提到這本書, 我會提及裡頭已經寫的內容, 但我不會把這些內容再一次的寫出來, 因為書中的解釋已經很棒了, 我不可能在寫的比書中內容更好了。所以擁有這麼一本書是很重要的, 如果你沒有, 建議想辦法搞到它, 提示: 在 google 打這本書的書名即可。

书名: 一步步写嵌入式操作系统: ARM 编程的方法与实践
出版社: 电子工业出版社; 第1版 (2011年1月1日)
平装: 265页
语种:简体中文
开本: 16
ISBN: 9787121122408, 7121122405
条形码: 9787121122408
商品尺寸: 23.6 x 16.8 x 1.8 cm
商品重量: 481 g

目录

第1章 搭建工作环境
1.1 选择合适的开发环境
1.2 开发工具的使用
1.3 虚拟硬件的安装和使用
1.4 总结
第2章 基础知识
2.1 使用c语言写第一段程序
2.2 用脚本链接目标文件
2.3 用汇编语言编写程序
2.4 汇编和c的混合编程
2.5 makefile
2.6 总结
第3章 操作系统的启动
3.1 启动流程
3.2 mmu
3.3 gcc内联汇编
3.4 总结
第4章 打印函数
4.1 打印函数实例
4.2 实现自己的打印函数
4.3 总结

第5章 中断处理
5.1 arm的中断
5.2 简单的中断处理实例
5.3 复杂的中断处理实例
5.4 更优秀的中断嵌套方法
5.5 总结
第6章 动态内存管理
6.1 伙伴算法
6.2 slab
6.3 kmalloc函数
6.4 总结
第7章 框架
7.1 驱动程序框架
7.2 文件系统框架
7.3 总结
第8章 运行用户程序
第9章 进程
结束语
参考资料

3.1.x (p36 - p52) 介紹 freertos 的初始化, 有 c runtime 的 bss 初始化, 還有 linker script 的說明, 讓我得以突破最困難的關卡, 就算是寫 x86 os, 此書還是能幫上不少忙, 因為這部份和使用什麼 cpu 較為無關, 也是因為過了這最困難的關卡, 我才能繼續往 os 的路上邁進。

這是什麼呢? 為什麼那麼重要? 這是從組合語言往 c 語言邁進的道路, 若第一行程式是組合語言, 從什麼時候才可以用 c 語言呢? 那就是要打造 c 語言執行環境 (c runtime), 而 bss 初始化是重點之一 (是的, 有之一), 而每個平台有不同的設定要做, 把相關事情都搞定, 才可以使用 c 語言。

你一定可以想像我為了要「知道」這件事情, google 了多少資料, c runtime, c startup code, 趴啦趴啦的關鍵字我試過很多, 但找到的東西不是看不懂就是不是我要的資料, 沒想到在這本簡體中文書籍就找到。之前嘗試了一堆反組譯 c code, 雖然不無小補, 但總是打不到核心, 這本書一舉為我解惑。

3.2 在說明 mmu, 如何設定 page table, 實體位址和虛擬位址如何轉換, 要花點時間、耐心慢慢欣賞。我根據這節的知識, 完成了《作業系統之前的程式 for rpi2 (1) - mmu (0) : 位址轉換》這篇文章。

3.3 在說明如何使用 inline assembly in gcc, 有用過的人一定會被那種奇怪的語法搞亂, 我總是記不住到底該怎麼用, 寫 os 難免會用到組合語言, 忘記了就回來翻翻。

chapter 5 在講述中斷, 這麼部份是我的弱點, 這章對我來說很難, 我看了好幾次, 勉強有了一點概念, arm 的中斷和 x86 有不小的差異, 得先切換到某個模式, 設定其 sp 暫存器。

init.s
 1 /*
 2 init.s:
 3 Copyright (C) 2009  david leels <davidontech@gmail.com>
 4 
 5 This program is free software: you can redistribute it and/or modify
 6 it under the terms of the GNU General Public License as published by
 7 the Free Software Foundation, either version 3 of the License, or
 8 (at your option) any later version.
 9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see http://www.gnu.org/licenses/.
17 */
18 .equ DISABLE_IRQ,  0x80
19 .equ DISABLE_FIQ,  0x40
20 .equ SYS_MOD,   0x1f
21 .equ IRQ_MOD,   0x12
22 .equ FIQ_MOD,   0x11
23 .equ SVC_MOD,   0x13
24 .equ ABT_MOD,   0x17
25 .equ UND_MOD,   0x1b
26 
27 .equ MEM_SIZE,   0x800000
28 .equ TEXT_BASE,   0x30000000
29 
30 .equ _SVC_STACK,  (TEXT_BASE+MEM_SIZE-4)
31 .equ _IRQ_STACK,  (_SVC_STACK-0x400)
32 .equ _FIQ_STACK,  (_IRQ_STACK-0x400)
33 .equ _ABT_STACK,  (_FIQ_STACK-0x400)
34 .equ _UND_STACK,  (_ABT_STACK-0x400)
35 .equ _SYS_STACK,  (_UND_STACK-0x400)
36 
37 .text
38 .code 32
39 .global __vector_reset
40 
41 .extern plat_boot
42 .extern __bss_start__
43 .extern __bss_end__
44 
45 __vector_reset:
46      msr cpsr_c,#(DISABLE_IRQ|DISABLE_FIQ|SVC_MOD)
47  ldr sp,=_SVC_STACK
48  msr cpsr_c,#(DISABLE_IRQ|DISABLE_FIQ|IRQ_MOD)
49  ldr sp,=_IRQ_STACK
50  msr cpsr_c,#(DISABLE_IRQ|DISABLE_FIQ|FIQ_MOD)
51  ldr sp,=_FIQ_STACK
52  msr cpsr_c,#(DISABLE_IRQ|DISABLE_FIQ|ABT_MOD)
53  ldr sp,=_ABT_STACK
54  msr cpsr_c,#(DISABLE_IRQ|DISABLE_FIQ|UND_MOD)
55  ldr sp,=_UND_STACK
56  msr cpsr_c,#(DISABLE_IRQ|DISABLE_FIQ|SYS_MOD)
57  ldr sp,=_SYS_STACK
58 
59 _clear_bss:
60  ldr r1,_bss_start_
61  ldr r3,_bss_end_
62  mov r2,#0x0
63 1:
64  cmp r1,r3
65  beq _main
66  str r2,[r1],#0x4
67  b 1b
68 
69 _main:
70  b plat_boot
71 
72 _bss_start_:.word   __bss_start__
73 _bss_end_:.word   __bss_end__

再來介紹中斷要怎麼處理, 以及進入某個中斷, pc 暫存器的值是指向哪裡? 可不一定都是 r14, 有時候是 r14-4, 有時候是 r14-8。

進階的部份則是如何有效率處理中斷, nested 中斷要怎麼實作, 還真的蠻複雜的。

6.1.2.2 介紹了 linux 的 linked list 資料結構, 這個可和課本上的不同, 你一定想知道它特別在哪裡吧, 我《補充》了一個範例程式。

7.2 會完成一個 romfs 檔案系統, 這個比 fat 還簡單, 很容易理解, 每個人都能實作出來。可惜的是這是唯讀的檔案系統, 若能有 ext2 這種檔案系統會比較好用, 不用自己寫, 可以用 https://github.com/gkostka/lwext4 來實作 ext2。

8.2 介紹了 elf 可執行檔, 可不是只有單純的說明 elf 那些欄位, 只知道名詞解釋是學不到什麼的, 所以書中還付了一個 elf loader, 讓讀者們了解怎麼載入一個 elf 執行檔, 你都會寫 elf loader 了, 還能不懂什麼是 elf 格式嗎? 書中介紹的是 elf 32, elf 64 有點不同, 應該難不倒你。

linux/kernel/module.c static int load_module(struct load_info *info, const char __user *uargs, int flags) 這個 function 做的是一樣的事情, 也可以參考這個來載入 elf 格式的檔案。

市面上很多類似的書籍, 不過因為是 arm 平台, 需要有個開發版來練習才行, 沒有開發板, 根本無法學習書中知識, 而有些書籍是用 armcc 來當作開發工具; 本書使用的是 skyeye 模擬器來練習 arm os, 以及使用 gnu cross compiler, 剛好符合我的學習需求。有了實作, 才能吸收書上知識, 而模擬器雖然不能百分之百和真實 arm 機器一樣, 但已經可以練習 os 程式了。

模擬器是練習 os 的利器, 尤其是 debug 的功能, 儘管有著和真實世界的差異, 但還是能幫上不少忙。

裡頭珍貴的部份是程式碼, 而且是小型的程式, 不像真實 os 那麼複雜, 總計有:
  • romfs 檔案系統
  • process switch
  • fork/exec
  • 記憶體管理程式和 paging
  • 讀取 elf

重點是薄薄一本, 265 頁, 讓人有容易看完的錯覺。XD

不過若到在真實機器執行書上 os 要克服的問題有:
uart, 在模擬器是假設 uart 已經初始完成, 真實機器上可得先克服這關才行, 這可不是容易的事情。

這樣的書竟然只要 39 人民幣, 實在是太便宜了。

官方網址:

http://www.leeos.org/
http://leeosorg.appspot.com/


可以下載 toolchain 和範例程式碼。

我在 github 備份了一份 (好險)。
https://github.com/descent/arm_os
https://github.com/Joyounger/leeos 這是另外一位讀者的練習

一步步写嵌入式操作系统:ARM编程的方法与实践 - 第1個 helloworld.c 範例
這是書中第1個 helloworld.c 範例, 需要修正一些問題

compile skyeye 1.3.5

http://www.flatws.cn/article/program/shell/2011-05-06/24333.html
Ubuntu 12.04下安装skyeye1.3.3

真奇怪, 還需要自己下載 libiconv (ftp://ftp.gnu.org/pub/gnu/libiconv)

apt-get install python-dev

make install will install here:
/opt/skyeye/
use gdb in skyeye

http://dabod.blogspot.tw/2006/07/gdb-skyeye.html
run time error:
http://blog.chinaunix.net/uid-26009923-id-3258932.html



一步步写嵌入式操作系统:
豆瓣评分 7.9
我的評分: 5 顆星