顯示具有 Linux 標籤的文章。 顯示所有文章
顯示具有 Linux 標籤的文章。 顯示所有文章

2010年11月11日 星期四

OS case stusy

1.mm(virtual memory -> physical memory) GDT ( Global Descriptor Table )
  • 0 NULL
  • 1 kernel code
  • 2 kernel data
  • 3 user space code
  • 4 user space data
entry point
  • base -> entry address
  • limit -> entry size
ex: gdt_set_gate(0, 0, 0, 0, 0); // Null segment gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment IDT(interrupt description table) Faults, traps and exceptions ex:
  • 0 - Division by zero exception
  • 1 - Debug exception
  • 2 - Non maskable interrupt
  • 3 - Breakpoint exception
TSS(task state segment) @ system_call store each tasks info to kernel schedule , such as eax,ebx,esp... ex: // A struct describing a Task State Segment. struct tss_entry_struct { u32int prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list. u32int esp0; // The stack pointer to load when we change to kernel mode. u32int ss0; // The stack segment to load when we change to kernel mode. u32int esp1; // Unused... .... } paging @ virtual memory call block -> used hole -> unused level directory -> tables(1024) -> entries(1024) frame @ physical memory call 2.VFS and the initrd VFS (virtual file system) define the file loc, uid, and i_node link list ex : /usr/bin/ -> ./ls /dev/tty/ initrd (initial ramdisk) boot image loc floop, disc, usb, ... linux 0.0.1 http://kerneltrap.org/Linux/Dusting_Off_the_0.01_Kernel http://www.cppblog.com/jake1036/archive/2010/11/13/133530.aspx http://mapopa.blogspot.com/2010/07/error-bad-register-name-sil.html http://manpages.ubuntu.com/manpages/gutsy/man1/as86.1.html http://www.cppblog.com/jake1036/archive/2010/11/13/133530.aspx JAME'S micro kernel http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html http://www.osdever.net/tutorials/index http://en.wikipedia.org/wiki/Task_State_Segment http://www.faqs.org/docs/Linux-HOWTO/KernelAnalysis-HOWTO.html#ss5.4 x86 instruction set http://siyobik.info/index.php?module=x86 nasm http://www.nasm.us/doc/nasmdoc0.html

2010年11月9日 星期二

OrzMicroKernel case stusy && emulator

最近在研究Jserv大師 所寫的 "OrzMicroKernel", 果然是霧裡看花...有看沒有懂的地步,很多地方還是要靠 Google 來找答案...XD, 不過最重要的核心部份, 我想應該是 Schedule 摟. 可參考OS Kernel 相關閱讀與實作心得﹝一﹞,裡面會定義哪個 task 有使用權,是 Preemptive/Non-preemptive type, 以現今的 real time 系統下, 應該都有 support Preemptive 的架構. 底下我們就用 c 來實作個 Schedule emulator. 1.task 架構 利用 pthread 來模擬 task request os kernel 的型式. 分別會定義 task id, priority, time_out, work_time priority : 為 task 的使用優先權. 0>1>2.... time_out : 為 task 的 requirement time,表示這個 task 最低的容忍時間, 一但 time_out =0 時, 表示目前的 task 要立刻被執行, 可以 interrupt high priority 的 task. work_time : 為 task 所要 run 的時間. 2. queue 架構 normal queue : 為 task 存取的 queue emergency queue : 一但有 task 的 time_out=0, 立刻從 normal queue 中移入 emergency queue. emulator os 會先判斷 emergency queue是否為空, 如果為空的, 就 access normal queue. 做 一般 priority 判斷的結果. 如果不為空的, 則處理 emergency queue. Results:
   @iOS work on normal queue -> id @,0
@NORMAL queue
queue :: id  0,time_out :     9,priority :     0,work_time :     2
queue :: id  1,time_out :     2,priority :     1,work_time :     3
queue :: id  2,time_out :     4,priority :     2,work_time :     3

@EMERGENCY queue

   @iOS work on normal queue -> id @,0
@NORMAL queue
queue :: id  0,time_out :     8,priority :     0,work_time :     1
queue :: id  1,time_out :     1,priority :     1,work_time :     3
queue :: id  2,time_out :     3,priority :     2,work_time :     3

@EMERGENCY queue

   @iOS work on emergency queue -> id @,1
@NORMAL queue
queue :: id  2,time_out :     2,priority :     2,work_time :     3
queue :: id  0,time_out :     7,priority :     0,work_time :     1

@EMERGENCY queue
queue :: id  1,time_out :     0,priority :     1,work_time :     2

   @iOS work on emergency queue -> id @,1
@NORMAL queue
queue :: id  2,time_out :     1,priority :     2,work_time :     3
queue :: id  0,time_out :     6,priority :     0,work_time :     1
ps: 應該要好好認真的把 OS 看熟才是...XD project @ https://github.com/funningboy/SOC_c_model/tree/master/iOS/ Refs: OS Kernel 相關閱讀與實作心得﹝一﹞ http://en.wikipedia.org/wiki/X86_memory_segmentation x86 Instruction Set Reference x86 Disassembly/Loop Examples OrzMicrokernel tasks.asm 工作排程器的切換執行權函式註解,

2010年10月15日 星期五

CUDA @ NVIDIA hardware driver

除了透過 OpenGL 來做2D/3D 硬體加速控制外. NVIDIA 也有一套類似 OpenGL 的 software (hardware)control 叫 "CUDA",只能說這是家很有個性的公司,因為連 Linux 的 driver 也要透過 wrapper 來溝通,這邊姑且不論 wrapper 的架構.我們就以 GPU 的觀點來看,在 GPU 的 design 上,主要是處理影像的運算(float point),如 vertex, ploy, text...的type.所以內部可分成很多的 ALU macros 跟 private/share cache ...可以 parallel or pipeline 的架構處理大量的 DSP 運算.而 CUDA 可以用 software 的語法描述來直接控制 hardware macros. Refs CUDA tutorial pycuda @ python CUDA Training ps: 沒有 NVIDIA 的顯卡,所以只好無聊寫個 ALU macro emulator...XD 大致上是有 3 個 independent 的 ALU macros, 每個 ALU 的 status 會存入 queue 中, 而 schedule management 會去 ask work queue, 判斷每個 ALU macro 的狀態, 如果 ALU macro = IDLE, schedule management 就會把 command assign 給這個 ALU macro. sample part @ code
void *check_work_queue(void *t){
int i;

  for(;;){
   //check 4 each ALU in work_queue
   pthread_mutex_lock(&count_mutex);
   gid = -1;
   for(i=0; i<3; i++){
       if( is_work_queue_exist(&qulist,i) == QUEUE_ER_ARB ){
            gid = i;
            break;
        }
    }

 //   dump_work_queue(qulist);
   pthread_mutex_unlock(&count_mutex);

   sleep(ALU_DEF_DELAY);

     if( DONE == ALU_OK)
         pthread_exit(NULL);
  }
}
Results
time @ Fri Oct 15 16:51:49 2010

assign ALU 0
command :     0,in_a :     3,in_b :     4

time @ Fri Oct 15 16:51:51 2010
queue :: id  0,cmd :     0,in_a :     3,in_b :     4, (pre)out_c :     7

assign ALU 1
command :     1,in_a :     3,in_b :     4

time @ Fri Oct 15 16:51:53 2010
queue :: id  1,cmd :     1,in_a :     3,in_b :     4, (pre)out_c :    -1

assign ALU 1
command :     2,in_a :     3,in_b :     4

time @ Fri Oct 15 16:51:55 2010
queue :: id  0,cmd :     0,in_a :     3,in_b :     4, (pre)out_c :     7
queue :: id  1,cmd :     1,in_a :     3,in_b :     4, (pre)out_c :    -1

assign ALU 2
project download here

2010年9月27日 星期一

OpenEmbedded case study

BitBake 為OpenEmbedded 的核心,主要解決 cross-compile && configure 的問題.因為在 embedded 系統下,光是 Environment build 就要花一斷時間,又要考慮到porting 的問題更是雪上加霜, 所以藉由 BitBake 來做到 svn, git, svk + compile + configure, 透過版本的 syn 跟 local build 達到快速客至化的動作. 可參考底下的連結,有比較詳細的說明. pic ref OpenEmbedded and BitBake Refs: Welcome to OpenEmbedded 轉換OpenEmbedded的repository為Subversion系統 SVK與嵌入式系統開發 OpenEmbedded First Try SVK 使用雜記與隨想 OpenEmbedded and BitBake

2010年9月26日 星期日

Curt @Jserv case study

難得有機會可以拜讀大師(Jserv)的作品"Curt",這是網路上有名的自由軟體作者Jserv所做的小型OS系統,系統雖小但五藏俱全,提供了sp, stat, help 的 threads,跟Schedule 的機制,系統流程主要可分成兩大部份,Step 1.boot-loader, 把image load 到RAM的low level assembly code, Step 2. start OS && schedule handle. 詳細流程可參考"國立台灣師範大學資訊工程系"的"嵌入式系統"教材. 底下小弟只是把所知到的data整理成flow表,如需詳細說明部份在Curt的Download檔裡也有說明. Step1. *initial set
*flush TLB/ICahe/DCahe
*Interrupt disable *clock set *UART Hardware 2 Console Display set *all enable && jump to RAM main() @0xc0000000 這邊也可參考類似的flow @Uboot . Step2. *OS start *initial ready_lists 4 each threads, delayed_list,termination_wait_list,termination_wait_list *set thread 2 thread table && priority/function set * run thread && schedule detection (SCHED_TIME_EXPIRE||SCHED_THREAD_REQUEST)
        //儲存CPU status && Disable Interrupt
        cpu_sr = save_cpu_sr();

        //取得最高priority 的 thread  Id
        top_prio = get_top_prio(); 

        /* If the timer expires... */
        if (sched_type == SCHED_TIME_EXPIRE) {
                /* Threads that are currently running will continue to run it
                 * at the highest priority */

                if (current_thread->prio < top_prio) {
                        current_thread->time_quantum = TIME_QUANTUM;
                        restore_cpu_sr(cpu_sr);
                        return;
                }
                /* otherwise, threads in a ready state, then run the highest
                 * priority one. */

                //取得 ready_list的 first Node 2 entry list && change context switch mode
                pnode = delete_front_list(&ready_list[top_prio]);
                if (is_empty_list(&ready_list[top_prio]))
                        prio_exist_flag[top_prio] = false;
                next_thread = entry_list(pnode, thread_struct, node);
                next_thread->state = RUNNING;
                next_thread->time_quantum = TIME_QUANTUM;

                /* Ready to change the status of the currently executing thread */
                current_thread->state = READY;
                insert_back_list(&ready_list[current_thread->prio],
                                 ¤t_thread->node);
                prio_exist_flag[current_thread->prio] = true;
                total_csw_cnt++;
                /* actual context switching */
                context_switch_in_interrupt();
        }
code ref: Jserv's 解析 CuRT 與嵌入式系統設計 放大參考here ps: 如有錯誤的地方歡迎指正,謝謝

2010年9月24日 星期五

x.org case study

在dinotrace @ waveform viewer提到 GUI Interface 的實現方式,原本以為只有 widget(GUI layout) + event(process trigger) ...,但其實 x.org(X11) 在terminal 的表現上比較像 Server/Client 的概念. 可參考底下的說明...XD,真的是隔行如隔山,讀起來被感艱辛阿...各位看官就當看故事書吧. Xorg 嶄新的硬體加速與效能提昇機制 Xorg 嶄新的硬體加速與效能提昇機制(续) flow chart. 請參考名詞解說 Refs: The Xlib Manual Xlib API wm-spec http://www.opengl.org/

2010年9月20日 星期一

Mouse emulator @ Linux

底下是 In-Air Mouse and Joystick 的 Demo,利用 Remote control 來模擬真實 mouse 的情形.想說不如來找找有沒有source code 可以研究.沒想到居然還有個叫 Keymouse 的 project,喝喝...看來google 真是太神了. 1.先用 modprobe 掛載 devices到 /dev/input上,
% sudo modprobe uinput
2. sample code ref here 確定uinput掛載好後,用ioctl()來控制IO2的讀寫.最後用linux/input.h 中的 input_event來模擬我們 input的vector.
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <sys/time.h>
#include <unistd.h>

// Test the uinput module

struct uinput_user_dev   uinp;
struct input_event       event;

int main(void) {

            int ufile, retcode, i;

            ufile = open("/dev/input/uinput", O_WRONLY | O_NDELAY );
            printf("open /dev/input/uinput returned %d.\n", ufile);
            if (ufile == 0) {
                        printf("Could not open uinput.\n");
                        return -1;
            }

            memset(&uinp, 0, sizeof(uinp));
            strncpy(uinp.name, "simulated mouse", 20);
            uinp.id.version = 4;
            uinp.id.bustype = BUS_USB;

            ioctl(ufile, UI_SET_EVBIT, EV_KEY);
            ioctl(ufile, UI_SET_EVBIT, EV_REL);
            ioctl(ufile, UI_SET_RELBIT, REL_X);
            ioctl(ufile, UI_SET_RELBIT, REL_Y);

            for (i=0; i<256; i++) {
                        ioctl(ufile, UI_SET_KEYBIT, i);
            }

            ioctl(ufile, UI_SET_KEYBIT, BTN_MOUSE);

            // create input device in input subsystem
            retcode = write(ufile, &uinp, sizeof(uinp));
            printf("First write returned %d.\n", retcode);

            retcode = (ioctl(ufile, UI_DEV_CREATE));
            printf("ioctl UI_DEV_CREATE returned %d.\n", retcode);
            if (retcode) {
                        printf("Error create uinput device %d.\n", retcode);
                        return -1;
            }

            // NOW DO STUFF !!!!

            for (i=0; i<100; i++) {

                        struct timeval tv1;

                        // move pointer upleft by 5 pixels
                        memset(&event, 0, sizeof(event));
                        gettimeofday(&event.time, NULL);
                        event.type = EV_REL;
                        event.code = REL_X;
                        event.value = -5;
                        write(ufile, &event, sizeof(event));
            
                        memset(&event, 0, sizeof(event));
                        gettimeofday(&event.time, NULL);
                        event.type = EV_REL;
                        event.code = REL_Y;
                        event.value = -5;
                        write(ufile, &event, sizeof(event));
            
                        memset(&event, 0, sizeof(event));
                        gettimeofday(&event.time, NULL);
                        event.type = EV_SYN;
                        event.code = SYN_REPORT;
                        event.value = 0;
                        write(ufile, &event, sizeof(event));

                        // wait just a moment
                        do { gettimeofday(&tv1, NULL); } while ((tv1.tv_usec & 
0x3FFF) != 0);
                        do { gettimeofday(&tv1, NULL); } while ((tv1.tv_usec & 
0x3FFF) == 0);
            }

            // destroy the device
            ioctl(ufile, UI_DEV_DESTROY);

            close(ufile);

}
ps: Keymouse 也是用此原理來做的, 你可以在 device.cpp 發現相似的地方. Refs: Sending simulated mouse events using uinput Mouseemu / uinput Linux Cross Reference Linux 驅動程式的 I/O, #4: fops->ioctl 實作

2010年9月18日 星期六

3D graphic @ Mesa case study

最近剛開始研究圖形介面,發現 Mesa 有提供Hw 加速的功能,可透過 Mesa 的 API 直接控制硬體來減少Sw 的複雜度. 底下找了一些相關的data,有興趣的人可以看看 Refs: x.org case study Linux 的3D加速--DRI Mesa Mesa 3D OpenGL [PDF] OpenGL Programming Guide

2010年9月17日 星期五

X window protocol

因為工作的關係,要從PC連到workstation,但又必須透過x window protocol把 desktop Display 丟回來.雖然有 X.org 的 x.server x.client, 但功能還是不完整,於是就找到了 Xephyr 這個tool.可透過底下的script來完成我們要的jobs.當然你也可以用 Xephyr 提供的 multi desktops Display,來做多個視窗管理.
Xephyr -kb -query xxx.xxx.xxx.xxx -fullscreen -ac :10
Refs: X Window System core protocol Xephyr Multiseat Configuration/Xephyr

2010年9月13日 星期一

execve @ Linux Kernel

execve 有點像 include(jump)到 external 的exe(執行檔),等exe執行完後在回到 Internal Process. 到雖然一般都用 *.tcl, *.sh 來連接 exe 比較方便.比較不用 care 到 I/O 的 Interface.既然 Linux 有提供這樣的方式,姑且就來看看摟... step 1. 先產生個 sample.exe % gcc -o sample sample.c ps: ./sample 需要代入參數 ex: ./sample Hello
#include <stdio.h>
#include <stdlib.h>

int main(int argc,char* argv[]){

 if(argc!=2){
  printf("usage %s <string> \n",argv[0]);
  exit(EXIT_FAILURE);
 }
  printf("%s\n",argv[1]);
  exit(EXIT_SUCCESS); 
}
step 2. 用 execve 來執行 sample.exe 注意 ./sample 是需要代入參數 , 如 "./sample Hello" 所以帶入 execve() 時的 newargv 參數會長 "./sample Hello", 所以在Array define 中必須要用NULL 隔開.
#include <stdio.h>
#include <stdlib.h>


int main(int argc,char *argv[]){
   char *newargv[] = { NULL, "Hello", NULL };
   char *newenviron[] = { NULL };

 if (argc != 2) {
                fprintf(stderr, "Usage: %s <file-to-exec>\n", argv[0]);
                exit(EXIT_FAILURE);
               }

               newargv[0] = argv[1];

               execve(argv[1], newargv, newenviron);
               perror("execve");   /* execve() only returns on error */
               exit(EXIT_FAILURE);

}
Refs: Linux Programmer's Manual EXECVE(2) Execve problem with invoking execve problem solaris

ptrace @ Linux Kernel

在 Parent Process assign to Child Process 的方法有很多種,如 fork, vfork ,clone...但這都還是在比較高層的 assign 方式, 其實 trace code 後,不難發現都是透過 ptrace 這個 func 來做底層的 register assign, 以 x86 為例. 你會看到 eax, ecx, esp, ebp, 的 assign 方式. 有點像是 assembly encode/decode 的方法...,當然我們也可以用 ptrace 的方式來 trace system calls.
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/reg.h>
#include <sys/syscall.h>   /* For SYS_write etc */

#include <stdio.h>
#include <stdlib.h>

int main()
{   pid_t child;
    long orig_eax, eax;
    long params[3];
    int status;
    int insyscall = 0;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/bin/ls", "ls", NULL);
    }
    else {
       while(1) {
          wait(&status);
          if(WIFEXITED(status))
              break;
          orig_eax = ptrace(PTRACE_PEEKUSER,
                     child, 4 * ORIG_EAX, NULL);
          if(orig_eax == SYS_write) {
             if(insyscall == 0) {
                /* Syscall entry */
                insyscall = 1;
                params[0] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * EBX,
                                   NULL);
                params[1] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * ECX,
                                   NULL);
                params[2] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * EDX,
                                   NULL);
                printf("Write called with "
                       "%ld, %ld, %ld\n",
                       params[0], params[1],
                       params[2]);
                }
          else { /* Syscall exit */
                eax = ptrace(PTRACE_PEEKUSER,
                             child, 4 * EAX, NULL);
                    printf("Write returned "
                           "with %ld\n", eax);
                    insyscall = 0;
                }
            }
            ptrace(PTRACE_SYSCALL,
                   child, NULL, NULL);
        }
    }
    return 0;
}

code ref : Playing with ptrace ps : 當然你也可以用 strace 來trace 執行時所呼叫的system call. Refs: ptrace Playing with ptrace

pipe @ Linux Kernel

pipe 為一種基於 Parent && Child 之間的 communication Channel, 有點類似 FIFO 的架構, 但跟FIFO 還是有點不同.pipe會先建立起個緩衝區,分別做讀完(pipefd[0]),寫完(pipefd[1])緩衝區的動作. 底下透過 Parent 建立起 Child Process(pipe Read), 等 Child End 之後在還給 Parent 做(pipe write).
#include >sys/wait.h>
#include >stdio.h>
#include >stdlib.h>
#include >unistd.h>
#include >string.h>

int
main(int argc, char *argv[])
{
    int pipefd[2];
    pid_t cpid;
    char buf;

    if (argc != 2) {
     fprintf(stderr, "Usage: %s >string>\n", argv[0]);
     exit(EXIT_FAILURE);
    }

    if (pipe(pipefd) == -1) { // create pipe structure...
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    cpid = fork();  // create child proc
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

   if (cpid == 0) {      /* Child reads from pipe */
       printf("Child...\n");
  close(pipefd[1]); /* Close unused write end */
    // disable write func
        while (read(pipefd[0], &buf, 1) > 0) // read from argv[] one chart by one chart
            write(STDOUT_FILENO, &buf, 1); // write 2 STDOUT

        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);   // disable read func
        _exit(EXIT_SUCCESS);   // child proc end

    } else {                /* Parent writes argv[1] to pipe */
 printf("Parent...\n");
        close(pipefd[0]);          /* Close unused read end */
        write(pipefd[1], argv[1], strlen(argv[1]));
        close(pipefd[1]);          /* Reader will see EOF */
        wait(NULL);                /* Wait for child */
        exit(EXIT_SUCCESS);
    }
}
code ref: Linux Programmer's Manual Refs : Linux Programmer's Manual PIPE(2) pipe(7) - Linux man page Executing programs with C(Linux)

2010年9月11日 星期六

fork && clone @ Linux Kernel

除了用 fork 的方式來實現多線程的方式外,也可以用 clone 的方式.兩者最大的差別是在 Parent copy or not, fork 會copy Parent 的 space, 而 clone 卻是和 Parent 共用 space, 前者比較多用於對外的多平行執行序如external server/client,後者比較多用於 internal的 Process 如 mm management. fork_smp.c 可發現 Parent 的 data 不會被 Child 改變.
#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[]) {
        int count = 1;
        int child;

        if(!(child = vfork())) {
                printf("This is son, his count is: %d. and his pid is: %d\n", ++count, getpid());
        } else {
                printf("This is father, his count is: %d, his pid is: %d\n", count, getpid());
        }
return 0;
}
clone.c 發現 Parent 的 Data 會被 Child 而改變.
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <signal.h>
#define FIBER_STACK 8192

int a;
void * stack;
int do_something(){
        printf("This is son, the pid is:%d, the a is: %d\n", getpid(), ++a);
        free(stack);
        exit(1);
}
int main() {
        void * stack;
        a = 1;
        stack = malloc(FIBER_STACK);
        if(!stack) {
                printf("The stack failed\n");
                exit(0);
        }

        printf("creating son thread!!!\n");

        clone(&do_something, (char *)stack + FIBER_STACK, CLONE_VM|CLONE_VFORK, 0);
         printf("This is father, my pid is: %d, the a is: %d\n", getpid(), a);
         exit(1);
}
Refs: fork,vfork和clone底层实现 fork, vfork, clone,pthread_create,kernel_thread clone fork及vfork的区别

2010年9月8日 星期三

disassemble your code @ objdump ....

除了用 gcc -S 來產生 *.s 的 assembly code之外. 也可以用 objdump + strace 的 command 來做 assemble && system call trace. sample code @ c
TT *get_TTNode(TT *p,int Id){
    TT *tPtr = p;

    while(tPtr!=NULL){
      if( tPtr->Id == Id ){
           return tPtr; break;
      }
      tPtr = tPtr->Nxt;
   }

return NULL;
}
objdump -d (disassemble)
080484d3 <get_TTNode>:
 80484d3:       55                      push   %ebp
 80484d4:       89 e5                   mov    %esp,%ebp                        // store current stack pointer
 80484d6:       83 ec 14                sub    $0x14,%esp
 80484d9:       8b 45 08                mov    0x8(%ebp),%eax                   // get "TT *p"
 80484dc:       89 45 fc                mov    %eax,-0x4(%ebp)                  // "TT *tPtr = p"
 80484df:       eb 1b                   jmp    80484fc <get_TTNode+0x29>  // jump to while loop 
 80484e1:       8b 45 fc                mov    -0x4(%ebp),%eax                  // "tPtr->Id"
 80484e4:       8b 00                   mov    (%eax),%eax                      // mov to eax register
 80484e6:       3b 45 0c                cmp    0xc(%ebp),%eax                   // cmp (tPtr->Id == Id )? true : false;
 80484e9:       75 08                   jne    80484f3 <get_TTNode+0x20>
 80484eb:       8b 45 fc                mov    -0x4(%ebp),%eax                  
 80484ee:       89 45 ec                mov    %eax,-0x14(%ebp)                 // return tPtr
 80484f1:       eb 16                   jmp    8048509 <get_TTNode+0x36>  // break
 80484f3:       8b 45 fc                mov    -0x4(%ebp),%eax
 80484f6:       8b 40 0c                mov    0xc(%eax),%eax                   // tPtr = tPtr->Nxt
 80484f9:       89 45 fc                mov    %eax,-0x4(%ebp)
 80484fc:       83 7d fc 00             cmpl   $0x0,-0x4(%ebp)                  // while loop
 8048500:       75 df                   jne    80484e1 <get_TTNode+0xe>           // cmp (tPtr!=NULL)? true : false;
 8048502:       c7 45 ec 00 00 00 00    movl   $0x0,-0x14(%ebp)
 8048509:       8b 45 ec                mov    -0x14(%ebp),%eax                 //return NULL
 804850c:       c9                      leave  
 804850d:       c3                      ret 
Refs : objdump strace

2010年9月4日 星期六

SMP @ Linux Kernel case study

在多核心架構下,除了可以用 sched.h 底下的 CPU_SET 來指定 schedule list上的 schedule processor 要給那個CPU使用外.還要考慮CPU lock/schedule 的機制.一但多個CPU Access 相同的 Memory Address時,就需要 lock priority 來確保 Memory Address 不會被 overwrite. 在Linux kernel 上提供了 spinlock_t 和rwlock(read write lock)的機制, 但在效能上 rwlock 要等lock被解鎖後才能動作,所以在 kernel 2.6 下加入了 RCU 的機制, 透過 COPY 的方式建立新的pointer, 不需要等lock解鎖就可執行,等執行完後在update 之前的pointer, 所以再效能上可降低 wait unlock 的時間. sample code 4 CPU_SET
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/sysinfo.h>
#include<unistd.h>

#define __USE_GNU
#include<sched.h>
#include<ctype.h>
#include<string.h>

int main(int argc, char* argv[])
{
        int num = sysconf(_SC_NPROCESSORS_CONF);
        int created_thread = 0;
        int myid;
        int i;
        int j = 0;

        cpu_set_t mask;
        cpu_set_t get;

        if (argc != 2)
        {
                printf("usage : ./cpu num\n");
                exit(1);
        }

        myid = atoi(argv[1]);

        printf("system has %i processor(s). \n", num);

        CPU_ZERO(&mask);
        CPU_SET(myid, &mask);

        if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
        {
                printf("warning: could not set CPU affinity, continuing...\n");
        }
        while (1)
        {

                CPU_ZERO(&get);
                if (sched_getaffinity(0, sizeof(get), &get) == -1)
                {
                        printf("warning: cound not get cpu affinity, continuing...\n");
                }
                for (i = 0; i < num; i++)
                {
                        if (CPU_ISSET(i, &get))
                        {
                                printf("this process %d is running processor : %d\n",getpid(), i);
                        }
                }
        }
        return 0;
}
code reference [精彩] 发一个多CPU中进程与CPU绑定的例子 Refs: Linux RCU机制详解 Read-copy-update

2010年8月26日 星期四

uBoot Case Study @ omap3

在Booot的流程中, boot-loader 會先做些簡單的初始化, 如CPU/RAM set...and load Kernel imag to RAM,之後把使用權還給 Kernel build.會根據現有的Architecture 載入相對應的Driver 跟 configure 載入 root files,最後 run script 建立起 Application. 底下針對 u-boot-2010.06 OMP cotexA8 @beagle 來做說明. Step1. 在 uBoot 的 Flow 上中要先定義好 CPU && Board 的 Type 1.ARC -> CPU config ARM, X86, MIPS... 2.ARC -> Board config TI, ASUS... 這邊假設是用 CPU @ u-boot-2010.06/arch/arm/cpu/arm_cortexa8 Board @ u-boot-2010.06/board/ti/beagle Step2. uBoot loaded && LOW level set @ Arc/CPU 在 u-boot.lds 中會定義 bootload 的起始位置跟Entry point@ 0x00000000. and entry @ arm_cortexa8/start.o Ref: u-boot.lds
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
        . = 0x00000000;

        . = ALIGN(4);
        .text   :
        {
                arch/arm/cpu/arm_cortexa8/start.o       (.text)
                *(.text)
        }

而在 start.s 中,主要透過Assembly code 來設定 @ARM CPU 基本的function Mode,跟load boot-loader 到 RAM 的動作. start.s
.globl _start
_start: b       reset
        ldr     pc, _undefined_instruction
        ldr     pc, _software_interrupt
        ldr     pc, _prefetch_abort
        ldr     pc, _data_abort
        ldr     pc, _not_used
        ldr     pc, _irq
        ldr     pc, _fiq

_undefined_instruction: .word undefined_instruction
_software_interrupt:    .word software_interrupt
_prefetch_abort:        .word prefetch_abort
_data_abort:            .word data_abort
_not_used:              .word not_used
_irq:                   .word irq
_fiq:                   .word fiq
_pad:                   .word 0x12345678 /* now 16*4=64 */
lowlevel_init.S 設定 hold chip 的 Freq. 包含 PLL set, Divide set, Mux set, Memory set, and PLL lock wait(等待 PLL 能穩定).
.globl lowlevel_init
lowlevel_init:
        ldr     sp, SRAM_STACK
        str     ip, [sp]        /* stash old link register */
        mov     ip, lr          /* save link reg across call */
        bl      s_init          /* go setup pll, mux, memory */
        ldr     ip, [sp]        /* restore save ip */
        mov     lr, ip          /* restore link reg */

        /* back to arch calling code */
        mov     pc, lr
....
pll_ctl_add:
        .word CM_CLKEN_PLL
pll_div_add1:
        .word CM_CLKSEL1_PLL
cache.S 中設定 Icache(instruction cache) && Dcache(data cache). 如 cache enable, cache disable, cache flush...
l2_cache_enable:
        stmfd   r13!, {r0, r1, r2, lr}
        @ ES2 onwards we can disable/enable L2 ourselves
        bl      get_cpu_rev
        cmp     r0, #CPU_3XX_ES20
        blt     l2_cache_disable_EARLIER_THAN_ES2
        mrc     15, 0, r3, cr1, cr0, 1
        orr     r3, r3, #2
        mcr     15, 0, r3, cr1, cr0, 1
        b       l2_cache_enable_END
step3. other sets GPIO, sys_info, syslib... 其實就是分別依序填入control value 到相對應的"control Register",能讓系統能做到簡單的work,之後在load kernel image, and build.... Refs : ARM cortexa8 beagle Refs: Das U-Boot -- the Universal Boot Loader Inside the Linux boot process Booting 基于S3C2410的Linux全线移植文档 作者:dozec U-Boot Quick Reference

2010年6月13日 星期日

power management 4 Linux

Propose : 在 low power design 上, "Power Management" 佔了很重要的地位. 利用 Power ON/OFF 來開關 Devices, 讓沒有在 work 的 devices 能進入 "Sleep" or "Shutdown" Mode... ,而這些判斷機制主要是透過上層的 software 演算法來決策. 因為過度的 ON/OFF 會消耗 "switch power", 讓電容不斷的 charge and discharge 導致不必要的 Power 消耗, 有時甚至比待機時候還來的耗電. 所以在 Power Management software design 上, 透過 control signal 打給底層的 hardware, 讓 hardware 有 state 上的變化, 再藉由每個不同的 state 來 define devices 的 on/off. Target : 用 c++ 來模擬 Power Management 的 hardware mode. 底下我們用 Linux 的 Power Management 來當例子. Ref: state define and detail info Linux power management other Refs : Android Power Management Power Planning power_man.h


#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <list>

using namespace std;

enum PWSTATE {
 PW_RUN  =0,
 PW_STANDBY =1,
 PW_SLEEP =2,
 PW_SHUTDOWN =3,
 PW_NOP  =4,
};

enum PWSWITCH {
 ON =0,
 OFF =1,
 SLEEP =2,
 LOW =3,
};

enum PWERROR {
 CUR2NXT_OK =0,
 CUR2NXT_ERR  =1,
 CUR2PUT_OK =2,
 CUR2PUT_ERR =3,
};

template<class T>
class Power_Man_IN {

public:
        T       ReBoot;
        T       Activity;
        T       InActivity;
        T       ShutDown;
        T       CLK;
 T Button;
};

template<class T>
class Power_Man_OT {

public:
       T   CPU;
       T   LCD;
       T   Keys;
       T   Audio;
}; 

class Power_Man {

public:  
  Power_Man_IN<int8_t> PW_MN_IN_Pt;
  Power_Man_OT<int8_t> PW_MN_OT_Pt;

  list<Power_Man_IN<int8_t> > TST_VEC_LIST;  
  
  int8_t Cur_ST;
  int8_t Nxt_ST;
 
  void SET2PW_MN_IN(int8_t r,int8_t a,int8_t i,int8_t s,int8_t c,int8_t b);
  void SET2PW_MN_OT(int8_t c,int8_t l,int8_t k,int8_t a);
  int8_t PRO2PW_MN();
  int8_t Put2OutREG();

  void DisPlaySET2PW_MN_IN();
  void DisPlaySET2PW_MN_OT();

//================================
// Test Bench 
//================================
  void SET_TEST_VECTOR(int8_t r,int8_t a,int8_t i,int8_t s,int8_t c,int8_t b);
  void SET_TEST_LIST();
};
power_man.cpp

 
//#include <systemc.h>
#include "power_man.h"

void Power_Man::SET_TEST_VECTOR(int8_t r,int8_t a,int8_t i,int8_t s,int8_t c,int8_t b){
  Power_Man_IN<int8_t> *VECPt = new Power_Man_IN<int8_t>;
    VECPt->ReBoot = r;
    VECPt->Activity = a;
    VECPt->InActivity = i;
    VECPt->ShutDown = s;
    VECPt->CLK  = c;
    VECPt->Button = b;
  
    TST_VEC_LIST.push_back(*VECPt);

  delete VECPt;
 }

//====================================
// Write your Test Bench here
//====================================
void Power_Man::SET_TEST_LIST(){

Cur_ST = PW_NOP;
Nxt_ST = PW_NOP;

SET_TEST_VECTOR(OFF, OFF, OFF, OFF, OFF, OFF);
// Set to  Run ST
SET_TEST_VECTOR(ON,  OFF, OFF, OFF, OFF, OFF);
SET_TEST_VECTOR(OFF, OFF, OFF, OFF, OFF, OFF);
SET_TEST_VECTOR(OFF, OFF, OFF, OFF, OFF, OFF);

// Set to STANDBY
SET_TEST_VECTOR(OFF, OFF, ON,  OFF, OFF, OFF);
SET_TEST_VECTOR(OFF, OFF, OFF, OFF, OFF, OFF);
SET_TEST_VECTOR(OFF, OFF, OFF, OFF, OFF, OFF);

}


void Power_Man::SET2PW_MN_IN(int8_t r,int8_t a,int8_t i,int8_t s,int8_t c,int8_t b){
 PW_MN_IN_Pt.ReBoot  = r;
 PW_MN_IN_Pt.Activity = a;
 PW_MN_IN_Pt.InActivity = i;
 PW_MN_IN_Pt.ShutDown = s;
 PW_MN_IN_Pt.CLK  = c;
 PW_MN_IN_Pt.Button = b;
}

void Power_Man::SET2PW_MN_OT(int8_t c,int8_t l,int8_t k,int8_t a){
 PW_MN_OT_Pt.CPU  = c;
 PW_MN_OT_Pt.LCD  = l;
 PW_MN_OT_Pt.Keys = k;
 PW_MN_OT_Pt.Audio = a;
}

int8_t Power_Man::PRO2PW_MN(){
 
 switch(Cur_ST){
 case PW_RUN : Nxt_ST = (PW_MN_IN_Pt.InActivity == ON )? PW_STANDBY  :
       (PW_MN_IN_Pt.ShutDown   == ON )? PW_SHUTDOWN : PW_RUN;       break;
 case PW_STANDBY : Nxt_ST = (PW_MN_IN_Pt.InActivity == ON )? PW_SLEEP    :
                                   (PW_MN_IN_Pt.Activity   == ON )? PW_RUN      : PW_STANDBY;   break; 
 case PW_SLEEP : Nxt_ST = (PW_MN_IN_Pt.Activity   == ON )? PW_RUN      : PW_SLEEP;     break;
 case PW_SHUTDOWN: Nxt_ST = (PW_MN_IN_Pt.Button     == ON )? PW_RUN      : PW_SHUTDOWN;  break;
 case PW_NOP : Nxt_ST = (PW_MN_IN_Pt.ReBoot     == ON )? PW_RUN      : PW_NOP; break;
 default:                return   CUR2NXT_ERR;  break;
        }
     
      Cur_ST = Nxt_ST;

 return CUR2NXT_OK;
}  

int8_t Power_Man::Put2OutREG(){
 switch(Cur_ST){
 case PW_RUN : SET2PW_MN_OT(ON,   ON,  ON,  ON ); break;
 case PW_STANDBY : SET2PW_MN_OT(LOW,  OFF, ON,  ON ); break;
 case PW_SLEEP : SET2PW_MN_OT(SLEEP,OFF, OFF, OFF); break;
 case PW_SHUTDOWN: SET2PW_MN_OT(SLEEP,OFF, OFF, OFF); break;
 case PW_NOP : SET2PW_MN_OT(OFF,  OFF, OFF, OFF); break;
 default:                       return  CUR2PUT_ERR; break;
 }

return CUR2PUT_OK;
}

void Power_Man::DisPlaySET2PW_MN_IN(){
int8_t r = PW_MN_IN_Pt.ReBoot;  
int8_t a = PW_MN_IN_Pt.Activity; 
int8_t i = PW_MN_IN_Pt.InActivity; 
int8_t s = PW_MN_IN_Pt.ShutDown; 
int8_t c = PW_MN_IN_Pt.CLK;
int8_t  b = PW_MN_IN_Pt.Button;

cout<<"CTL\t"<<"CLK:\t"<<int(c)<<"\tREBT:\t"<<int(r)<<"\tACT:\t"<<int(a)<<"\tINACT:\t"<<int(i)<<"\tSHTDN:\t"<<int(s)<<"\tBUTTON:\t"<<int(b)<<endl;  
}

void Power_Man::DisPlaySET2PW_MN_OT(){
int8_t c = PW_MN_OT_Pt.CPU;
int8_t l = PW_MN_OT_Pt.LCD;
int8_t k = PW_MN_OT_Pt.Keys;
int8_t a = PW_MN_OT_Pt.Audio;

cout<<"PUT\t"<<"CPU:\t"<<int(c)<<"\tLCD:\t"<<int(l)<<"\tKEYS:\t"<<int(k)<<"\tAUDIO:\t"<<int(a)<<endl;

}



int main() {

_List_iterator<Power_Man_IN<int8_t> > it;

Power_Man *PW_MN_Pt = new Power_Man;

PW_MN_Pt->SET_TEST_LIST();

for(it=PW_MN_Pt->TST_VEC_LIST.begin(); it!=PW_MN_Pt->TST_VEC_LIST.end(); it++){

if( PW_MN_Pt->Put2OutREG()!= CUR2PUT_OK ){
  cout<<"CUR2PUT Error"<<endl; return -1; }

  PW_MN_Pt->DisPlaySET2PW_MN_OT();
  
  int8_t r = it->ReBoot;
  int8_t a = it->Activity;
  int8_t i = it->InActivity;
  int8_t s = it->ShutDown;
  int8_t c = it->CLK;
  int8_t b = it->Button;

  PW_MN_Pt->SET2PW_MN_IN( r, a, i, s, c, b);
  PW_MN_Pt->DisPlaySET2PW_MN_IN();

if( PW_MN_Pt->PRO2PW_MN()!= CUR2NXT_OK ){
 cout<<"CUR2NXT Error"<<endl; return -1; }


}

delete PW_MN_Pt;

return 0;
}
Results: PUT CPU: 1 LCD: 1 KEYS: 1 AUDIO: 1 CTL CLK: 1 REBT: 1 ACT: 1 INACT: 1 SHTDN: 1 BUTTON: 1 PUT CPU: 1 LCD: 1 KEYS: 1 AUDIO: 1 CTL CLK: 1 REBT: 0 ACT: 1 INACT: 1 SHTDN: 1 BUTTON: 1 PUT CPU: 0 LCD: 0 KEYS: 0 AUDIO: 0 CTL CLK: 1 REBT: 1 ACT: 1 INACT: 1 SHTDN: 1 BUTTON: 1 PUT CPU: 0 LCD: 0 KEYS: 0 AUDIO: 0 CTL CLK: 1 REBT: 1 ACT: 1 INACT: 1 SHTDN: 1 BUTTON: 1 PUT CPU: 0 LCD: 0 KEYS: 0 AUDIO: 0 CTL CLK: 1 REBT: 1 ACT: 1 INACT: 0 SHTDN: 1 BUTTON: 1 PUT CPU: 3 LCD: 1 KEYS: 0 AUDIO: 0 PS:目前只有考慮 CPU, LCD, KEYS, AUDIO 的 control. 當然你也可以自己 define 所要 control 的 devices download : http://sites.google.com/site/funningboy/c/power_man.cpp?attredirects=0&d=1 http://sites.google.com/site/funningboy/c/power_man.h?attredirects=0&d=1