| | 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 | }}} |