wiki:jazz/jfbterm

Version 1 (modified by jazz, 16 years ago) (diff)

--

BUGFIX: jfbterm (1)

  • [DRBL] jfbterm Bug
  • [備註] 如果沒有裝 v86d 的話,當執行 modprobe uvesafb 時會在 dmesg 看到以下資訊
    [  703.839376] uvesafb: failed to execute /sbin/v86d
    [  703.840224] uvesafb: make sure that the v86d helper is installed and executable
    [  703.841494] uvesafb: Getting VBE info block failed (eax=0x4f00, err=-2)
    [  703.842307] uvesafb: vbe_init() failed with -22
    [  703.843019] uvesafb: probe of uvesafb.0 failed with error -22
    
  • [備註] 如果有裝 v86d 的話,當執行 modprobe uvesafb 時會在 dmesg 看到以下資訊
    [  268.848377] uvesafb: VMware, IncVMware virtual machine2.0, VMware virtual machine2.0, 2.0, OEM: V M ware, Inc. VBE support 2.0VMware, IncVMware virtual machine2.0, VBE v2.0
    [  268.884099] uvesafb: no monitor limits have been set, default refresh rate will be used
    [  268.885202] uvesafb: VBE state buffer size cannot be determined (eax=0x0, err=0)
    [  268.885265] uvesafb: scrolling: redraw
    [  268.896617] mtrr: your processor doesn't support write-combining
    [  268.909303] Console: switching to colour frame buffer device 128x48
    [  269.938085] uvesafb: framebuffer at 0xf0000000, mapped to 0xffffc20000180000, using 16384k, total 16384k
    [  269.938101] fb0: VESA VGA frame buffer device
    
  • Ubuntu 8.10 Kernel 2.6.27-7-server 的 uvesafb 參數包括 scroll、vgapal、pmipal、mtrr、blank、nocrtc、noedid、vram_remap、vram_total、maxclk、maxhf、maxvf、mode_option、vbemode、v86d。
    root@intrepid:~# modinfo uvesafb
    filename:       /lib/modules/2.6.27-7-server/kernel/drivers/video/uvesafb.ko
    description:    Framebuffer driver for VBE2.0+ compliant graphics boards
    author:         Michal Januszewski <spock@gentoo.org>
    license:        GPL
    srcversion:     21EDEFDED06E0673208A0D5
    depends:
    vermagic:       2.6.27-7-server SMP mod_unload modversions
    parm:           scroll:Scrolling mode, set to 'redraw', 'ypan', or 'ywrap' (scroll)
    parm:           vgapal:Set palette using VGA registers (invbool)
    parm:           pmipal:Set palette using PMI calls (bool)
    parm:           mtrr:Memory Type Range Registers setting. Use 0 to disable. (uint)
    parm:           blank:Enable hardware blanking (bool)
    parm:           nocrtc:Ignore CRTC timings when setting modes (bool)
    parm:           noedid:Ignore EDID-provided monitor limits when setting modes (bool)
    parm:           vram_remap:Set amount of video memory to be used [MiB] (uint)
    parm:           vram_total:Set total amount of video memoery [MiB] (uint)
    parm:           maxclk:Maximum pixelclock [MHz], overrides EDID data (ushort)
    parm:           maxhf:Maximum horizontal frequency [kHz], overrides EDID data (ushort)
    parm:           maxvf:Maximum vertical frequency [Hz], overrides EDID data (ushort)
    parm:           mode_option:Specify initial video mode as "<xres>x<yres>[-<bpp>][@<refresh>]" (charp)
    parm:           vbemode:VBE mode number to set, overrides the 'mode' option (ushort)
    parm:           v86d:Path to the v86d userspace helper. (string)
    
    root@intrepid:~# LANG=C apt-cache policy v86d
    v86d:
      Installed: 0.1.5-1ubuntu2
      Candidate: 0.1.5-1ubuntu2
      Version table:
     *** 0.1.5-1ubuntu2 0
            500 http://tw.archive.ubuntu.com intrepid/universe Packages
            100 /var/lib/dpkg/status
    
    root@intrepid:~# LANG=C apt-cache policy jfbterm
    jfbterm:
      Installed: 0.4.7-7.2
      Candidate: 0.4.7-7.2
      Version table:
     *** 0.4.7-7.2 0
            500 http://tw.archive.ubuntu.com intrepid/universe Packages
            100 /var/lib/dpkg/status
    
  • 追蹤 jfbterm 的 deb package 原始碼
    root@intrepid:~# apt-get build-dep jfbterm
    root@intrepid:~# apt-get install dpkg-dev
    root@intrepid:~# apt-get source jfbterm
    
  • uvesafb 作者的推測:
    As to why the console becomes unresponsive after exiting jfbterm --
    jfbterm sets KD_GRAPHICS mode on the console on which it is started,
    and apparently fails to set it back to KD_TEXT before segfaulting.  This
    leaves the console in the broken state.
    
  • 尋找合理懷疑對象:
    root@intrepid:~/jfbterm-0.4.7# grep "KD_GRAPHICS" *
    vterm.c:        ioctl(0, KDSETMODE, KD_GRAPHICS);
    
    root@intrepid:~/jfbterm-0.4.7# grep "KD_TEXT" *
    main.c:        if (mode == KD_TEXT) {
    vterm.c:        ioctl(0, KDSETMODE, KD_TEXT);
    

BUGFIX: jfbterm (2)

  • 尋找合理懷疑對象:
    root@intrepid:~/jfbterm-0.4.7# grep "KD_GRAPHICS" *
    vterm.c:        ioctl(0, KDSETMODE, KD_GRAPHICS);
    
    root@intrepid:~/jfbterm-0.4.7# grep "KD_TEXT" *
    main.c:        if (mode == KD_TEXT) {
    vterm.c:        ioctl(0, KDSETMODE, KD_TEXT);
    
    root@intrepid:~/jfbterm-0.4.7# grep "cannot mmap" *
    fbcommon.c:             die("cannot mmap(smem)");
    fbcommon.c:             die("cannot mmap(mmio)");
    fbcommon.c:             print_message("cannot mmap(mmio) : %s\n", strerror(errno));
    
  • 啟用 DEBUG 旗標,編譯 jfbterm,使用 gdb 追蹤
    root@intrepid:~/jfbterm-0.4.7# ./configure --enable-debug
    root@intrepid:~/jfbterm-0.4.7# make
    root@intrepid:~/jfbterm-0.4.7# gdb ./jfbterm
    (gdb) run
    
  • 縱使用 gdb 還是無法正常跳回文字模式,因此直接追原始碼。
  • 根據錯誤訊息,應該是錯在 fbcommon.c 的第 572 行,往前追造成錯誤的原因是 566 行的 mmap(),歸屬在 tfbm_open() 函數
    < fbcommon.c >
    
    482 void tfbm_open(TFrameBufferMemory* p)
    
    566         p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE,
    567                                 MAP_SHARED, p->fh, p->slen);
    568         if ((long)p->mmio == -1) {
    569 #ifdef JFB_MMIO_CHECK
    570                 die("cannot mmap(mmio)");
    571 #else
    572                 print_message("cannot mmap(mmio) : %s\n", strerror(errno));
    573 #endif
    
    < main.c >
    
    368 int main(int argc, char *argv[])
    431         tfbm_open(&gFramebuffer);
    
  • 安裝 manpages-dev 套件,查 mmap 、 strerror 跟 errno 的 manpage。從 cannot mmap(mmio) : Invalid argument 這個錯誤訊息可以判斷 errno 等於 EINVAL。而 mmap 發生 EINVAL 的情形有三種,最可能的原因是第一個:We don't like addr, length, or offset (e.g., they are too large, or not aligned on a page boundary).
  • 這裡注意到 mmap 的 offset 參數必須是 sysconf 中定義 _SC_PAGE_SIZE 的倍數。(offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).)
    root@intrepid:~/jfbterm-0.4.7# apt-get install manpages-dev
    
    root@intrepid:~/jfbterm-0.4.7# man errno
    
           EINVAL          Invalid argument (POSIX.1)
    
    void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    
    offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).
    
    ERRORS
           EINVAL We don't like addr, length, or offset (e.g., they are too large, or not aligned on a page boundary).
           EINVAL (since Linux 2.6.12) length was 0.
           EINVAL flags contained neither MAP_PRIVATE or MAP_SHARED, or contained both of these values.
    
  • 使用 gdb 設定中斷點在 fbcomm.c 第 566 行並觀察變數狀態
    root@intrepid:~/jfbterm-0.4.7# gdb
    (gdb) file jfbterm
    Reading symbols from /root/jfbterm-0.4.7/jfbterm...done.
    (gdb) set args -e ls
    (gdb) show args
    Argument list to give program being debugged when it is started is "-e ls".
    (gdb) break fbcommon.c:566
    Breakpoint 1 at 0x4034ba: file fbcommon.c, line 566.
    (gdb) run
    ... 略 ...
    Breakpoint 1, tfbm_open (p=0x6146e0) at fbcommon.c:566
    566             p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE,
    (gdb) l
    561             }
    562             p->smem = (char *)p->smem + p->soff;
    563
    564             p->moff = (u_long)(fb_fix.mmio_start) & (~PAGE_MASK);
    565             p->mlen = (fb_fix.mmio_len + p->moff + ~PAGE_MASK) & PAGE_MASK;
    566             p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE,
    567                                     MAP_SHARED, p->fh, p->slen);
    568             if ((long)p->mmio == -1) {
    569     #ifdef JFB_MMIO_CHECK
    570                     die("cannot mmap(mmio)");
    (gdb) p fb_fix.mmio_len
    $1 = 0
    (gdb) p p->moff
    $2 = 0
    (gdb) p p->mlen
    $3 = 0
    (gdb) p p->fh
    $4 = 6
    (gdb) p p->slen
    $5 = 1572864
    (gdb) p fb_fix.smem_len
    $6 = 1572864
    

BUGFIX: jfbterm (3)

  • 編輯 GRUB 的 menu.lst 就可以不用每次都跑 modprobe
    • menu.lst

      old new  
      125125
      126126title          Ubuntu 8.10, kernel 2.6.27-7-server
      127127uuid           574912ac-8bd6-4cc7-90c6-8c5362033fec
      128 kernel         /boot/vmlinuz-2.6.27-7-server root=UUID=574912ac-8bd6-4cc7-90c6-8c5362033fec ro quiet splash
       128kernel         /boot/vmlinuz-2.6.27-7-server root=UUID=574912ac-8bd6-4cc7-90c6-8c5362033fec ro quiet splash vga=0x305
      129129initrd         /boot/initrd.img-2.6.27-7-server
      130130quiet
  • mmap 的 offset 參數必須是 sysconf 中定義 _SC_PAGE_SIZE 的倍數。(offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).)
  • 使用 gdb 設定中斷點在 fbcomm.c 第 566 行並觀察變數狀態,可以知道送到 mmap 函數的 offset 參數內容為 1572864 。
    (gdb) p p->slen
    $5 = 1572864
    
  • 為了確認 offset 參數是否為 _SC_PAGESIZE 的整數倍,先撰寫測試程式,用 sysconf 取得目前系統的 _SC_PAGESIZE 屬性。
    root@intrepid:~# cat > get_sc_pagesize.c << EOF
    #include <unistd.h>
    #include <stdio.h>
    
    int main()
    {
      printf("_SC_PAGESIZE = %ld\n",sysconf(_SC_PAGESIZE));
    }
    EOF
    root@intrepid:~# gcc get_sc_pagesize.c -o get_sc_pagesize
    root@intrepid:~# ./get_sc_pagesize
    _SC_PAGESIZE = 4096
    
  • 根據 mmap 的 manpages,產生 EINVAL 錯誤的另一個原因是 length 等於零(從 Linux 2.6.12 以後),確認主因為 length 參數 = p->mlen = 0
    MMAP(2): void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    MMAP(2): EINVAL (since Linux 2.6.12) length was 0.
    
    566             p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE,
    567                                     MAP_SHARED, p->fh, p->slen);
    (gdb) p p->mlen
    $3 = 0