| 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 | {{{ |
| 15 | root@intrepid:~# apt-get update |
| 16 | root@intrepid:~# apt-get upgrade |
| 17 | root@intrepid:~# reboot |
| 18 | root@intrepid:~# uname -a |
| 19 | Linux intrepid 2.6.27-7-server #1 SMP Tue Nov 4 20:16:57 UTC 2008 x86_64 GNU/Linux |
| 20 | root@intrepid:~# apt-get install jfbterm v86d |
| 21 | root@intrepid:~# reboot |
| 22 | root@intrepid:~# dpkg -S chvt |
| 23 | kbd: /usr/share/man/man1/chvt.1.gz |
| 24 | kbd: /bin/chvt |
| 25 | root@intrepid:~# chvt 1 |
| 26 | root@intrepid:~# rmmod uvesafb |
| 27 | ERROR: Module uvesafb does not exist in /proc/modules |
| 28 | root@intrepid:~# modprobe uvesafb mode_option=1024x768 |
| 29 | root@intrepid:~# screen |
| 30 | root@intrepid:~# jfbterm -e ls |
| 31 | ... 略 ... |
| 32 | color 15 : ffff, ffff |
| 33 | cannot mmap(mmio) : Invalid argument |
| 34 | Segmentation 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 | {{{ |
| 57 | root@intrepid:~# modinfo uvesafb |
| 58 | filename: /lib/modules/2.6.27-7-server/kernel/drivers/video/uvesafb.ko |
| 59 | description: Framebuffer driver for VBE2.0+ compliant graphics boards |
| 60 | author: Michal Januszewski <spock@gentoo.org> |
| 61 | license: GPL |
| 62 | srcversion: 21EDEFDED06E0673208A0D5 |
| 63 | depends: |
| 64 | vermagic: 2.6.27-7-server SMP mod_unload modversions |
| 65 | parm: scroll:Scrolling mode, set to 'redraw', 'ypan', or 'ywrap' (scroll) |
| 66 | parm: vgapal:Set palette using VGA registers (invbool) |
| 67 | parm: pmipal:Set palette using PMI calls (bool) |
| 68 | parm: mtrr:Memory Type Range Registers setting. Use 0 to disable. (uint) |
| 69 | parm: blank:Enable hardware blanking (bool) |
| 70 | parm: nocrtc:Ignore CRTC timings when setting modes (bool) |
| 71 | parm: noedid:Ignore EDID-provided monitor limits when setting modes (bool) |
| 72 | parm: vram_remap:Set amount of video memory to be used [MiB] (uint) |
| 73 | parm: vram_total:Set total amount of video memoery [MiB] (uint) |
| 74 | parm: maxclk:Maximum pixelclock [MHz], overrides EDID data (ushort) |
| 75 | parm: maxhf:Maximum horizontal frequency [kHz], overrides EDID data (ushort) |
| 76 | parm: maxvf:Maximum vertical frequency [Hz], overrides EDID data (ushort) |
| 77 | parm: mode_option:Specify initial video mode as "<xres>x<yres>[-<bpp>][@<refresh>]" (charp) |
| 78 | parm: vbemode:VBE mode number to set, overrides the 'mode' option (ushort) |
| 79 | parm: v86d:Path to the v86d userspace helper. (string) |
| 80 | |
| 81 | root@intrepid:~# LANG=C apt-cache policy v86d |
| 82 | v86d: |
| 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 | |
| 90 | root@intrepid:~# LANG=C apt-cache policy jfbterm |
| 91 | jfbterm: |
| 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 | {{{ |
| 101 | root@intrepid:~# apt-get build-dep jfbterm |
| 102 | root@intrepid:~# apt-get install dpkg-dev |
| 103 | root@intrepid:~# apt-get source jfbterm |
| 104 | }}} |
| 105 | * uvesafb 作者的推測: |
| 106 | {{{ |
| 107 | As to why the console becomes unresponsive after exiting jfbterm -- |
| 108 | jfbterm sets KD_GRAPHICS mode on the console on which it is started, |
| 109 | and apparently fails to set it back to KD_TEXT before segfaulting. This |
| 110 | leaves the console in the broken state. |
| 111 | }}} |
| 112 | * 尋找合理懷疑對象: |
| 113 | {{{ |
| 114 | root@intrepid:~/jfbterm-0.4.7# grep "KD_GRAPHICS" * |
| 115 | vterm.c: ioctl(0, KDSETMODE, KD_GRAPHICS); |
| 116 | |
| 117 | root@intrepid:~/jfbterm-0.4.7# grep "KD_TEXT" * |
| 118 | main.c: if (mode == KD_TEXT) { |
| 119 | vterm.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 | {{{ |
| 128 | root@intrepid:~/jfbterm-0.4.7# grep "KD_GRAPHICS" * |
| 129 | vterm.c: ioctl(0, KDSETMODE, KD_GRAPHICS); |
| 130 | |
| 131 | root@intrepid:~/jfbterm-0.4.7# grep "KD_TEXT" * |
| 132 | main.c: if (mode == KD_TEXT) { |
| 133 | vterm.c: ioctl(0, KDSETMODE, KD_TEXT); |
| 134 | |
| 135 | root@intrepid:~/jfbterm-0.4.7# grep "cannot mmap" * |
| 136 | fbcommon.c: die("cannot mmap(smem)"); |
| 137 | fbcommon.c: die("cannot mmap(mmio)"); |
| 138 | fbcommon.c: print_message("cannot mmap(mmio) : %s\n", strerror(errno)); |
| 139 | }}} |
| 140 | * 啟用 DEBUG 旗標,編譯 jfbterm,使用 gdb 追蹤 |
| 141 | {{{ |
| 142 | root@intrepid:~/jfbterm-0.4.7# ./configure --enable-debug |
| 143 | root@intrepid:~/jfbterm-0.4.7# make |
| 144 | root@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 | |
| 152 | 482 void tfbm_open(TFrameBufferMemory* p) |
| 153 | |
| 154 | 566 p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE, |
| 155 | 567 MAP_SHARED, p->fh, p->slen); |
| 156 | 568 if ((long)p->mmio == -1) { |
| 157 | 569 #ifdef JFB_MMIO_CHECK |
| 158 | 570 die("cannot mmap(mmio)"); |
| 159 | 571 #else |
| 160 | 572 print_message("cannot mmap(mmio) : %s\n", strerror(errno)); |
| 161 | 573 #endif |
| 162 | }}} |
| 163 | {{{ |
| 164 | < main.c > |
| 165 | |
| 166 | 368 int main(int argc, char *argv[]) |
| 167 | 431 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 | {{{ |
| 172 | root@intrepid:~/jfbterm-0.4.7# apt-get install manpages-dev |
| 173 | }}} |
| 174 | {{{ |
| 175 | root@intrepid:~/jfbterm-0.4.7# man errno |
| 176 | |
| 177 | EINVAL Invalid argument (POSIX.1) |
| 178 | }}} |
| 179 | {{{ |
| 180 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); |
| 181 | |
| 182 | offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE). |
| 183 | |
| 184 | ERRORS |
| 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 | {{{ |
| 191 | root@intrepid:~/jfbterm-0.4.7# gdb |
| 192 | (gdb) file jfbterm |
| 193 | Reading symbols from /root/jfbterm-0.4.7/jfbterm...done. |
| 194 | (gdb) set args -e ls |
| 195 | (gdb) show args |
| 196 | Argument list to give program being debugged when it is started is "-e ls". |
| 197 | (gdb) break fbcommon.c:566 |
| 198 | Breakpoint 1 at 0x4034ba: file fbcommon.c, line 566. |
| 199 | (gdb) run |
| 200 | ... 略 ... |
| 201 | Breakpoint 1, tfbm_open (p=0x6146e0) at fbcommon.c:566 |
| 202 | 566 p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE, |
| 203 | (gdb) l |
| 204 | 561 } |
| 205 | 562 p->smem = (char *)p->smem + p->soff; |
| 206 | 563 |
| 207 | 564 p->moff = (u_long)(fb_fix.mmio_start) & (~PAGE_MASK); |
| 208 | 565 p->mlen = (fb_fix.mmio_len + p->moff + ~PAGE_MASK) & PAGE_MASK; |
| 209 | 566 p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE, |
| 210 | 567 MAP_SHARED, p->fh, p->slen); |
| 211 | 568 if ((long)p->mmio == -1) { |
| 212 | 569 #ifdef JFB_MMIO_CHECK |
| 213 | 570 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 | {{{ |
| 254 | root@intrepid:~# cat > get_sc_pagesize.c << EOF |
| 255 | #include <unistd.h> |
| 256 | #include <stdio.h> |
| 257 | |
| 258 | int main() |
| 259 | { |
| 260 | printf("_SC_PAGESIZE = %ld\n",sysconf(_SC_PAGESIZE)); |
| 261 | } |
| 262 | EOF |
| 263 | root@intrepid:~# gcc get_sc_pagesize.c -o get_sc_pagesize |
| 264 | root@intrepid:~# ./get_sc_pagesize |
| 265 | _SC_PAGESIZE = 4096 |
| 266 | }}} |
| 267 | * 根據 mmap 的 manpages,產生 EINVAL 錯誤的另一個原因是 length 等於零(從 Linux 2.6.12 以後),確認主因為 length 參數 = p->mlen = 0 |
| 268 | {{{ |
| 269 | MMAP(2): void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); |
| 270 | MMAP(2): EINVAL (since Linux 2.6.12) length was 0. |
| 271 | |
| 272 | 566 p->mmio = (u_char*)mmap(NULL, p->mlen, PROT_READ|PROT_WRITE, |
| 273 | 567 MAP_SHARED, p->fh, p->slen); |
| 274 | (gdb) p p->mlen |
| 275 | $3 = 0 |
| 276 | }}} |