Changes between Initial Version and Version 1 of jazz/jfbterm


Ignore:
Timestamp:
Nov 21, 2008, 1:25:54 PM (16 years ago)
Author:
jazz
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • jazz/jfbterm

    v1 v1  
     1[[PageOutline]]
     2
     3== BUGFIX: jfbterm (1) ==
     4
     5 * [wiki:jazz/08-11-15 08-11-15@GMT-6 原紀錄 BUGFIX: jfbterm (1)]
     6
     7 * [DRBL] jfbterm Bug
     8   * https://bugs.launchpad.net/ubuntu/+source/jfbterm/+bug/253163
     9   * http://launchpadlibrarian.net/16414131/jfbterm-segfault.txt
     10   * http://launchpadlibrarian.net/16414135/jfbterm-segfault-strace.txt
     11   * [測試]
     12     * 安裝 [http://ftp.twaren.net/Linux/Ubuntu/ubuntu-cd/8.10/ubuntu-8.10-server-amd64.iso Ubuntu 8.10 Server AMD64]
     13     * 用 Ubuntu 8.10 的測試步驟:
     14{{{
     15root@intrepid:~# apt-get update
     16root@intrepid:~# apt-get upgrade
     17root@intrepid:~# reboot
     18root@intrepid:~# uname -a
     19Linux intrepid 2.6.27-7-server #1 SMP Tue Nov 4 20:16:57 UTC 2008 x86_64 GNU/Linux
     20root@intrepid:~# apt-get install jfbterm v86d
     21root@intrepid:~# reboot
     22root@intrepid:~# dpkg -S chvt
     23kbd: /usr/share/man/man1/chvt.1.gz
     24kbd: /bin/chvt
     25root@intrepid:~# chvt 1
     26root@intrepid:~# rmmod uvesafb
     27ERROR: Module uvesafb does not exist in /proc/modules
     28root@intrepid:~# modprobe uvesafb mode_option=1024x768
     29root@intrepid:~# screen
     30root@intrepid:~# jfbterm -e ls
     31... 略 ...
     32color 15 : ffff, ffff
     33cannot mmap(mmio) : Invalid argument
     34Segmentation fault
     35}}}
     36 * [備註] 如果沒有裝 v86d 的話,當執行 modprobe uvesafb 時會在 dmesg 看到以下資訊
     37{{{
     38[  703.839376] uvesafb: failed to execute /sbin/v86d
     39[  703.840224] uvesafb: make sure that the v86d helper is installed and executable
     40[  703.841494] uvesafb: Getting VBE info block failed (eax=0x4f00, err=-2)
     41[  703.842307] uvesafb: vbe_init() failed with -22
     42[  703.843019] uvesafb: probe of uvesafb.0 failed with error -22
     43}}}
     44 * [備註] 如果有裝 v86d 的話,當執行 modprobe uvesafb 時會在 dmesg 看到以下資訊
     45{{{
     46[  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
     47[  268.884099] uvesafb: no monitor limits have been set, default refresh rate will be used
     48[  268.885202] uvesafb: VBE state buffer size cannot be determined (eax=0x0, err=0)
     49[  268.885265] uvesafb: scrolling: redraw
     50[  268.896617] mtrr: your processor doesn't support write-combining
     51[  268.909303] Console: switching to colour frame buffer device 128x48
     52[  269.938085] uvesafb: framebuffer at 0xf0000000, mapped to 0xffffc20000180000, using 16384k, total 16384k
     53[  269.938101] fb0: VESA VGA frame buffer device
     54}}}
     55 * 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。
     56{{{
     57root@intrepid:~# modinfo uvesafb
     58filename:       /lib/modules/2.6.27-7-server/kernel/drivers/video/uvesafb.ko
     59description:    Framebuffer driver for VBE2.0+ compliant graphics boards
     60author:         Michal Januszewski <spock@gentoo.org>
     61license:        GPL
     62srcversion:     21EDEFDED06E0673208A0D5
     63depends:
     64vermagic:       2.6.27-7-server SMP mod_unload modversions
     65parm:           scroll:Scrolling mode, set to 'redraw', 'ypan', or 'ywrap' (scroll)
     66parm:           vgapal:Set palette using VGA registers (invbool)
     67parm:           pmipal:Set palette using PMI calls (bool)
     68parm:           mtrr:Memory Type Range Registers setting. Use 0 to disable. (uint)
     69parm:           blank:Enable hardware blanking (bool)
     70parm:           nocrtc:Ignore CRTC timings when setting modes (bool)
     71parm:           noedid:Ignore EDID-provided monitor limits when setting modes (bool)
     72parm:           vram_remap:Set amount of video memory to be used [MiB] (uint)
     73parm:           vram_total:Set total amount of video memoery [MiB] (uint)
     74parm:           maxclk:Maximum pixelclock [MHz], overrides EDID data (ushort)
     75parm:           maxhf:Maximum horizontal frequency [kHz], overrides EDID data (ushort)
     76parm:           maxvf:Maximum vertical frequency [Hz], overrides EDID data (ushort)
     77parm:           mode_option:Specify initial video mode as "<xres>x<yres>[-<bpp>][@<refresh>]" (charp)
     78parm:           vbemode:VBE mode number to set, overrides the 'mode' option (ushort)
     79parm:           v86d:Path to the v86d userspace helper. (string)
     80
     81root@intrepid:~# LANG=C apt-cache policy v86d
     82v86d:
     83  Installed: 0.1.5-1ubuntu2
     84  Candidate: 0.1.5-1ubuntu2
     85  Version table:
     86 *** 0.1.5-1ubuntu2 0
     87        500 http://tw.archive.ubuntu.com intrepid/universe Packages
     88        100 /var/lib/dpkg/status
     89
     90root@intrepid:~# LANG=C apt-cache policy jfbterm
     91jfbterm:
     92  Installed: 0.4.7-7.2
     93  Candidate: 0.4.7-7.2
     94  Version table:
     95 *** 0.4.7-7.2 0
     96        500 http://tw.archive.ubuntu.com intrepid/universe Packages
     97        100 /var/lib/dpkg/status
     98}}}
     99 * 追蹤 jfbterm 的 deb package 原始碼
     100{{{
     101root@intrepid:~# apt-get build-dep jfbterm
     102root@intrepid:~# apt-get install dpkg-dev
     103root@intrepid:~# apt-get source jfbterm
     104}}}
     105 * uvesafb 作者的推測:
     106{{{
     107As to why the console becomes unresponsive after exiting jfbterm --
     108jfbterm sets KD_GRAPHICS mode on the console on which it is started,
     109and apparently fails to set it back to KD_TEXT before segfaulting.  This
     110leaves the console in the broken state.
     111}}}
     112 * 尋找合理懷疑對象:
     113{{{
     114root@intrepid:~/jfbterm-0.4.7# grep "KD_GRAPHICS" *
     115vterm.c:        ioctl(0, KDSETMODE, KD_GRAPHICS);
     116
     117root@intrepid:~/jfbterm-0.4.7# grep "KD_TEXT" *
     118main.c:        if (mode == KD_TEXT) {
     119vterm.c:        ioctl(0, KDSETMODE, KD_TEXT);
     120}}}
     121
     122== BUGFIX: jfbterm (2) ==
     123
     124 * [wiki:jazz/08-11-16 08-11-16@GMT-6 原紀錄 BUGFIX: jfbterm (2)]
     125
     126 * 尋找合理懷疑對象:
     127{{{
     128root@intrepid:~/jfbterm-0.4.7# grep "KD_GRAPHICS" *
     129vterm.c:        ioctl(0, KDSETMODE, KD_GRAPHICS);
     130
     131root@intrepid:~/jfbterm-0.4.7# grep "KD_TEXT" *
     132main.c:        if (mode == KD_TEXT) {
     133vterm.c:        ioctl(0, KDSETMODE, KD_TEXT);
     134
     135root@intrepid:~/jfbterm-0.4.7# grep "cannot mmap" *
     136fbcommon.c:             die("cannot mmap(smem)");
     137fbcommon.c:             die("cannot mmap(mmio)");
     138fbcommon.c:             print_message("cannot mmap(mmio) : %s\n", strerror(errno));
     139}}}
     140 * 啟用 DEBUG 旗標,編譯 jfbterm,使用 gdb 追蹤
     141{{{
     142root@intrepid:~/jfbterm-0.4.7# ./configure --enable-debug
     143root@intrepid:~/jfbterm-0.4.7# make
     144root@intrepid:~/jfbterm-0.4.7# gdb ./jfbterm
     145(gdb) run
     146}}}
     147 * 縱使用 gdb 還是無法正常跳回文字模式,因此直接追原始碼。
     148 * 根據錯誤訊息,應該是錯在 fbcommon.c 的第 572 行,往前追造成錯誤的原因是 566 行的 mmap(),歸屬在 tfbm_open() 函數
     149{{{
     150< fbcommon.c >
     151
     152482 void tfbm_open(TFrameBufferMemory* p)
     153
     154566         p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE,
     155567                                 MAP_SHARED, p->fh, p->slen);
     156568         if ((long)p->mmio == -1) {
     157569 #ifdef JFB_MMIO_CHECK
     158570                 die("cannot mmap(mmio)");
     159571 #else
     160572                 print_message("cannot mmap(mmio) : %s\n", strerror(errno));
     161573 #endif
     162}}}
     163{{{
     164< main.c >
     165
     166368 int main(int argc, char *argv[])
     167431         tfbm_open(&gFramebuffer);
     168}}}
     169 * 安裝 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'''__).
     170 * 這裡注意到 mmap 的 offset 參數必須是 sysconf 中定義 _SC_PAGE_SIZE 的倍數。('''offset''' must be a __'''multiple of the page size'''__ as returned by sysconf(_SC_PAGE_SIZE).)
     171{{{
     172root@intrepid:~/jfbterm-0.4.7# apt-get install manpages-dev
     173}}}
     174{{{
     175root@intrepid:~/jfbterm-0.4.7# man errno
     176
     177       EINVAL          Invalid argument (POSIX.1)
     178}}}
     179{{{
     180void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
     181
     182offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).
     183
     184ERRORS
     185       EINVAL We don't like addr, length, or offset (e.g., they are too large, or not aligned on a page boundary).
     186       EINVAL (since Linux 2.6.12) length was 0.
     187       EINVAL flags contained neither MAP_PRIVATE or MAP_SHARED, or contained both of these values.
     188}}}
     189 * 使用 gdb 設定中斷點在 fbcomm.c 第 566 行並觀察變數狀態
     190{{{
     191root@intrepid:~/jfbterm-0.4.7# gdb
     192(gdb) file jfbterm
     193Reading symbols from /root/jfbterm-0.4.7/jfbterm...done.
     194(gdb) set args -e ls
     195(gdb) show args
     196Argument list to give program being debugged when it is started is "-e ls".
     197(gdb) break fbcommon.c:566
     198Breakpoint 1 at 0x4034ba: file fbcommon.c, line 566.
     199(gdb) run
     200... 略 ...
     201Breakpoint 1, tfbm_open (p=0x6146e0) at fbcommon.c:566
     202566             p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE,
     203(gdb) l
     204561             }
     205562             p->smem = (char *)p->smem + p->soff;
     206563
     207564             p->moff = (u_long)(fb_fix.mmio_start) & (~PAGE_MASK);
     208565             p->mlen = (fb_fix.mmio_len + p->moff + ~PAGE_MASK) & PAGE_MASK;
     209566             p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE,
     210567                                     MAP_SHARED, p->fh, p->slen);
     211568             if ((long)p->mmio == -1) {
     212569     #ifdef JFB_MMIO_CHECK
     213570                     die("cannot mmap(mmio)");
     214(gdb) p fb_fix.mmio_len
     215$1 = 0
     216(gdb) p p->moff
     217$2 = 0
     218(gdb) p p->mlen
     219$3 = 0
     220(gdb) p p->fh
     221$4 = 6
     222(gdb) p p->slen
     223$5 = 1572864
     224(gdb) p fb_fix.smem_len
     225$6 = 1572864
     226}}}
     227
     228== BUGFIX: jfbterm (3) ==
     229
     230 * [wiki:jazz/08-11-17 08-11-17@GMT-6 原紀錄 BUGFIX: jfbterm (3)]
     231
     232 * 編輯 GRUB 的 menu.lst 就可以不用每次都跑 modprobe
     233{{{
     234#!diff
     235--- menu.lst.org        2008-11-19 06:56:47.000000000 +0800
     236+++ menu.lst    2008-11-19 06:56:21.000000000 +0800
     237@@ -125,7 +125,7 @@
     238
     239 title          Ubuntu 8.10, kernel 2.6.27-7-server
     240 uuid           574912ac-8bd6-4cc7-90c6-8c5362033fec
     241-kernel         /boot/vmlinuz-2.6.27-7-server root=UUID=574912ac-8bd6-4cc7-90c6-8c5362033fec ro quiet splash
     242+kernel         /boot/vmlinuz-2.6.27-7-server root=UUID=574912ac-8bd6-4cc7-90c6-8c5362033fec ro quiet splash vga=0x305
     243 initrd         /boot/initrd.img-2.6.27-7-server
     244 quiet
     245}}}
     246 * mmap 的 offset 參數必須是 sysconf 中定義 _SC_PAGE_SIZE 的倍數。('''offset''' must be a __'''multiple of the page size'''__ as returned by sysconf(_SC_PAGE_SIZE).)
     247 * 使用 gdb 設定中斷點在 fbcomm.c 第 566 行並觀察變數狀態,可以知道送到 mmap 函數的 offset 參數內容為 1572864 。
     248{{{
     249(gdb) p p->slen
     250$5 = 1572864
     251}}}
     252 * 為了確認 offset 參數是否為 _SC_PAGESIZE 的整數倍,先撰寫測試程式,用 sysconf 取得目前系統的 _SC_PAGESIZE 屬性。
     253{{{
     254root@intrepid:~# cat > get_sc_pagesize.c << EOF
     255#include <unistd.h>
     256#include <stdio.h>
     257
     258int main()
     259{
     260  printf("_SC_PAGESIZE = %ld\n",sysconf(_SC_PAGESIZE));
     261}
     262EOF
     263root@intrepid:~# gcc get_sc_pagesize.c -o get_sc_pagesize
     264root@intrepid:~# ./get_sc_pagesize
     265_SC_PAGESIZE = 4096
     266}}}
     267 * 根據 mmap 的 manpages,產生 EINVAL 錯誤的另一個原因是 length 等於零(從 Linux 2.6.12 以後),確認主因為 length 參數 = p->mlen = 0
     268{{{
     269MMAP(2): void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
     270MMAP(2): EINVAL (since Linux 2.6.12) length was 0.
     271
     272566             p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE,
     273567                                     MAP_SHARED, p->fh, p->slen);
     274(gdb) p p->mlen
     275$3 = 0
     276}}}