1 # 2 # linux_logo in i386 assembly language 3 # based on the code from ll_asm-0.36 4 # 5 # By Vince Weaver <vince _at_ deater.net> 6 # 7 # Modified to remove non-deterministic system calls 8 # And to avoid reading from /proc 9 # 10 11 .include "../logo.include" 12 13 # offsets into the results returned by the uname syscall 14 .equ U_SYSNAME,0 15 .equ U_NODENAME,65 16 .equ U_RELEASE,65*2 17 .equ U_VERSION,(65*3) 18 .equ U_MACHINE,(65*4) 19 .equ U_DOMAINNAME,65*5 20 21 # offset into the results returned by the sysinfo syscall 22 .equ S_TOTALRAM,16 23 24 # Sycscalls 25 .equ SYSCALL_EXIT, 1 26 .equ SYSCALL_WRITE, 4 27 28 # 29 .equ STDIN,0 30 .equ STDOUT,1 31 .equ STDERR,2 32 33 .globl _start 34 _start: 35 #========================= 36 # PRINT LOGO 37 #========================= 38 39 # LZSS decompression algorithm implementation 40 # by Stephan Walter 2002, based on LZSS.C by Haruhiko Okumura 1989 41 # optimized some more by Vince Weaver 42 43 # we used to fill the buffer with FREQUENT_CHAR 44 # but, that only gains us one byte of space in the lzss image. 45 # the lzss algorithm does automatic RLE... pretty clever 46 # so we compress with NUL as FREQUENT_CHAR and it is pre-done for us 47 48 mov $(N-F), %bp # R 49 50 mov $logo, %esi # %esi points to logo (for lodsb) 51 52 mov $out_buffer, %edi # point to out_buffer 53 push %edi # save this value for later 54 55 decompression_loop: 56 lodsb # load in a byte 57 58 mov $0xff, %bh # re-load top as a hackish 8-bit counter 59 mov %al, %bl # move in the flags 60 61 test_flags: 62 cmp $logo_end, %esi # have we reached the end? 63 je done_logo # if so, exit 64 65 shr $1, %ebx # shift bottom bit into carry flag 66 jc discrete_char # if set, we jump to discrete char 67 68 offset_length: 69 lodsw # get match_length and match_position 70 mov %eax,%edx # copy to edx 71 # no need to mask dx, as we do it 72 # by default in output_loop 73 74 shr $(P_BITS),%eax 75 add $(THRESHOLD+1),%al 76 mov %al,%cl # cl = (ax >> P_BITS) + THRESHOLD + 1 77 # (=match_length) 78 79 output_loop: 80 and $POSITION_MASK,%dh # mask it 81 mov text_buf(%edx), %al # load byte from text_buf[] 82 inc %edx # advance pointer in text_buf 83 store_byte: 84 stosb # store it 85 86 mov %al, text_buf(%ebp) # store also to text_buf[r] 87 inc %ebp # r++ 88 and $(N-1), %bp # mask r 89 90 loop output_loop # repeat until k>j 91 92 or %bh,%bh # if 0 we shifted through 8 and must 93 jnz test_flags # re-load flags 94 95 jmp decompression_loop 96 97 discrete_char: 98 lodsb # load a byte 99 inc %ecx # we set ecx to one so byte 100 # will be output once 101 # (how do we know ecx is zero?) 102 103 jmp store_byte # and cleverly store it 104 105 106 # end of LZSS code 107 108 done_logo: 109 110 pop %ebp # get out_buffer and keep in bp 111 mov %ebp,%ecx # move out_buffer to ecx 112 113 call write_stdout # print the logo 114 115 # 116 # Setup 117 # 118 setup: 119 mov $strcat,%edx # use edx as call pointer 120 121 122 #========================== 123 # PRINT VERSION 124 #========================== 125 126 # push $SYSCALL_UNAME # uname syscall 127 # pop %eax # in 3 bytes 128 # mov $uname_info,%ebx # uname struct 129 # int $0x80 # do syscall 130 131 mov %ebp,%edi # point %edi to out_buffer 132 133 mov $(uname_info+U_SYSNAME),%esi # os-name from uname "Linux" 134 call *%edx # call strcat 135 136 mov $ver_string,%esi # source is " Version " 137 call *%edx # call strcat 138 push %esi # save our .txt pointer 139 140 mov $(uname_info+U_RELEASE),%esi # version from uname "2.4.1" 141 call *%edx # call strcat 142 143 pop %esi # restore .txt pointer 144 # source is ", Compiled " 145 call *%edx # call strcat 146 push %esi # store for later 147 148 mov $(uname_info+U_VERSION),%esi # compiled date 149 call *%edx # call strcat 150 151 mov %ebp,%ecx # move out_buffer to ecx 152 153 mov $0xa,%ax # store linefeed on end 154 stosw # and zero 155 156 call *%edx # call strcat 157 158 call center_and_print # center and print 159 160 #=============================== 161 # Middle-Line 162 #=============================== 163 164 #========= 165 # Load /proc/cpuinfo into buffer 166 #========= 167 168 push %edx # save call pointer 169 170 # push $SYSCALL_OPEN # load 5 [ open() ] 171 # pop %eax # in 3 bytes 172 173 # mov $cpuinfo,%ebx # '/proc/cpuinfo' 174 # xor %ecx,%ecx # 0 = O_RDONLY <bits/fcntl.h> 175 # cdq # clear edx in clever way 176 # int $0x80 # syscall. fd in eax. 177 # we should check that eax>=0 178 179 # mov %eax,%ebx # save our fd 180 181 # push $SYSCALL_READ # load 3 = read() 182 # pop %eax # in 3 bytes 183 184 mov $disk_buffer,%ecx 185 186 # mov $16,%dh # 4096 is maximum size of proc file #) 187 # we load sneakily by knowing 188 # 16<<8 = 4096. be sure edx clear 189 190 191 # int $0x80 192 193 # push $SYSCALL_CLOSE # close (to be correct) 194 # pop %eax 195 # int $0x80 196 197 #============= 198 # Number of CPUs 199 #============= 200 number_of_cpus: 201 202 xor %ebx,%ebx # chip count 203 204 # $disk_buffer still in ecx 205 bogo_loop: 206 mov (%ecx), %eax # load 4 bytes into eax 207 inc %ecx # increment pointer 208 209 cmp $0,%al # check for end of file 210 je done_bogo 211 212 # Grrr, due to a bug in binutils 2.18.50.0.9 213 # (which unfortunately shipped with Fedora 10) 214 # http://sourceware.org/bugzilla/show_bug.cgi?id=6878 215 # We can't use the apostrophe character 216 217 # cmp $('o'<<24+'g'<<16+'o'<<8+'b'),%eax 218 cmp $(0x6f<<24+0x67<<16+0x6f<<8+0x62),%eax 219 # "bogo" in little-endian 220 221 jne bogo_loop # if not equal, keep going 222 223 inc %ebx # otherwise, we have a bogo 224 inc %ebx # times two for future magic 225 jmp bogo_loop 226 227 done_bogo: 228 lea one-6(%ebx,%ebx,2), %esi 229 # Load into esi 230 # [one]+(num_cpus*6) 231 # 232 # the above multiplies by three 233 # esi = (ebx+(ebx*2)) 234 # and we double-incremented ebx 235 # earlier 236 237 mov %ebp,%edi # move output buffer to edi 238 239 pop %edx # restore call pointer 240 call *%edx # copy it (call strcat) 241 242 # mov $' ',%al # print a space 243 mov $0x20,%al # print a space 244 stosb 245 246 push %ebx # store cpu count 247 push %edx # store strcat pointer 248 249 #========= 250 # MHz 251 #========= 252 print_mhz: 253 # mov $('z'<<24+'H'<<16+'M'<<8+' '),%ebx 254 mov $(0x7a<<24+0x48<<16+0x4d<<8+0x20),%ebx 255 # find ' MHz' and grab up to . 256 # we are little endian 257 # mov $'.',%ah 258 mov $0x2e,%ah 259 260 # below is same as "sub $(strcat-find_string),%edx 261 # gas won't let us force the one-byte constant 262 .byte 0x83,0xEA,strcat-find_string 263 264 call *%edx # call find string 265 266 mov %ebx,%eax # clever way to get MHz in, sadly 267 ror $8,%eax # not any smaller than a mov 268 stosl 269 270 #========= 271 # Chip Name 272 #========= 273 chip_name: 274 275 # because of ugly newer cpuinfos from intel I had to hack this 276 # now we grab the first two words in the name field and use that 277 # it works on all recent Intel and AMD chips. Older things 278 # might choke 279 280 # mov $('e'<<24+'m'<<16+'a'<<8+'n'),%ebx 281 mov $(0x65<<24+0x6d<<16+0x61<<8+0x6e),%ebx 282 # find 'name\t: ' and grab up to \n 283 # we are little endian 284 # mov $' ',%ah 285 mov $0x20,%ah 286 call *%edx # print first word 287 stosb # store a space 288 call skip_spaces # print next word 289 290 pop %edx 291 pop %ebx # restore chip count 292 pop %esi 293 294 call *%edx # ' Processor' 295 cmpb $2,%bl 296 jne print_s 297 inc %esi # if singular, skip the s 298 print_s: 299 call *%edx # 's, ' 300 301 push %esi # restore the values 302 push %edx 303 304 #======== 305 # RAM 306 #======== 307 308 # push $SYSCALL_SYSINFO # sysinfo() syscall 309 # pop %eax 310 # mov $sysinfo_buff,%ebx 311 # int $0x80 312 313 mov (sysinfo_buff+S_TOTALRAM),%eax # size in bytes of RAM 314 shr $20,%eax # divide by 1024*1024 to get M 315 adc $0, %eax # round 316 317 318 call num_to_ascii 319 320 pop %edx # restore strcat pointer 321 322 pop %esi # print 'M RAM, ' 323 call *%edx # call strcat 324 325 push %esi 326 327 328 #======== 329 # Bogomips 330 #======== 331 332 # mov $('s'<<24+'p'<<16+'i'<<8+'m'),%ebx 333 mov $(0x73<<24+0x70<<16+0x69<<8+0x6d),%ebx 334 # find 'mips\t: ' and grab up to \n 335 mov $0xa,%ah 336 call find_string 337 338 pop %esi # bogo total follows RAM 339 340 call *%edx # call strcat 341 342 push %esi 343 344 mov %ebp,%ecx # point ecx to out_buffer 345 346 347 call center_and_print # center and print 348 349 #================================= 350 # Print Host Name 351 #================================= 352 353 mov %ebp,%edi # point to output_buffer 354 355 mov $(uname_info+U_NODENAME),%esi # host name from uname() 356 call *%edx # call strcat 357 358 # ecx is unchanged 359 call center_and_print # center and print 360 361 pop %ecx # (.txt) pointer to default_colors 362 363 call write_stdout 364 365 366 #================================ 367 # Exit 368 #================================ 369 exit: 370 xor %ebx,%ebx 371 xor %eax,%eax 372 inc %eax # put exit syscall number (1) in eax 373 int $0x80 # and exit 374 375 376 #================================= 377 # FIND_STRING 378 #================================= 379 # ah is char to end at 380 # ebx is 4-char ascii string to look for 381 # edi points at output buffer 382 383 find_string: 384 385 mov $disk_buffer-1,%esi # look in cpuinfo buffer 386 find_loop: 387 inc %esi 388 cmpb $0, (%esi) # are we at EOF? 389 je done # if so, done 390 391 cmp (%esi), %ebx # do the strings match? 392 jne find_loop # if not, loop 393 394 # ! if we get this far, we matched 395 396 find_colon: 397 lodsb # repeat till we find colon 398 cmp $0,%al # this is actually smaller code 399 je done # than an or ecx/repnz scasb 400 401 # cmp $':',%al 402 cmp $0x3a,%al 403 jne find_colon 404 405 406 skip_spaces: 407 lodsb # skip spaces 408 cmp $0x20,%al # Loser new intel chips have lots?? 409 je skip_spaces 410 411 store_loop: 412 cmp $0,%al 413 je done 414 cmp %ah,%al # is it end string? 415 je almost_done # if so, finish 416 # cmp $'\n',%al # also end if linefeed 417 cmp $0xa,%al # also end if linefeed 418 je almost_done 419 stosb # if not store and continue 420 lodsb # load value 421 jmp store_loop 422 423 almost_done: 424 425 movb $0, (%edi) # replace last value with NUL 426 done: 427 ret 428 429 430 #================================ 431 # strcat 432 #================================ 433 434 strcat: 435 lodsb # load a byte from [ds:esi] 436 stosb # store a byte to [es:edi] 437 cmp $0,%al # is it zero? 438 jne strcat # if not loop 439 dec %edi # point to one less than null 440 ret # return 441 442 #============================== 443 # center_and_print 444 #============================== 445 # string to center in ecx 446 447 center_and_print: 448 push %edx 449 push %ecx # save the string pointer 450 inc %edi # move to a clear buffer 451 push %edi # save for later 452 453 # mov $('['<<8+27),%ax # we want to output ^[[ 454 mov $(0x5b<<8+27),%ax # we want to output ^[[ 455 stosw 456 457 cdq # clear dx 458 459 str_loop2: # find end of string 460 inc %edx 461 cmpb $0,(%ecx,%edx) # repeat till we find zero 462 jne str_loop2 463 464 push $81 # one added to cheat, we don't 465 # count the trailing '\n' 466 pop %eax 467 468 cmp %eax,%edx # see if we are >=80 469 jl not_too_big # if so, don't center 470 push $80 471 pop %edx 472 473 not_too_big: 474 sub %edx,%eax # subtract size from 80 475 476 shr %eax # then divide by 2 477 478 call num_to_ascii # print number of spaces 479 # mov $'C',%al # tack a 'C' on the end 480 mov $0x43,%al # tack a 'C' on the end 481 # ah is zero from num_to_ascii 482 stosw # store C and a NULL 483 pop %ecx # pop the pointer to ^[[xC 484 485 call write_stdout # write to the screen 486 487 done_center: 488 pop %ecx # restore string pointer 489 # and trickily print the real string 490 491 pop %edx 492 493 #================================ 494 # WRITE_STDOUT 495 #================================ 496 # ecx has string 497 # eax,ebx,ecx,edx trashed 498 write_stdout: 499 push %edx 500 push $SYSCALL_WRITE # put 4 in eax (write syscall) 501 pop %eax # in 3 bytes of code 502 503 cdq # clear edx 504 505 xor %ebx,%ebx # put 1 in ebx (stdout) 506 inc %ebx # in 3 bytes of code 507 508 # another way of doing this: lea 1(%edx), %ebx 509 510 str_loop1: 511 inc %edx 512 cmpb $0,(%ecx,%edx) # repeat till zero 513 jne str_loop1 514 515 int $0x80 # run the syscall 516 pop %edx 517 ret 518 519 ############################## 520 # num_to_ascii 521 ############################## 522 # ax = value to print 523 # edi points to where we want it 524 525 num_to_ascii: 526 push $10 527 pop %ebx 528 xor %ecx,%ecx # clear ecx 529 div_by_10: 530 cdq # clear edx 531 div %ebx # divide 532 push %edx # save for later 533 inc %ecx # add to length counter 534 or %eax,%eax # was Q zero? 535 jnz div_by_10 # if not divide again 536 537 write_out: 538 pop %eax # restore in reverse order 539 add $0x30, %al # convert to ASCII 540 stosb # save digit 541 loop write_out # loop till done 542 ret 543 544 #=========================================================================== 545 # section .data 546 #=========================================================================== 547 .data 548 549 ver_string: .ascii " Version \0" 550 compiled_string: .ascii ", Compiled \0" 551 processor: .ascii " Processor\0" 552 s_comma: .ascii "s, \0" 553 ram_comma: .ascii "M RAM, \0" 554 bogo_total: .ascii " Bogomips Total\n\0" 555 556 default_colors: .ascii "\033[0m\n\n\0" 557 558 cpuinfo: .ascii "/proc/cpuinfo\0" 559 560 561 one: .ascii "One\0\0\0" 562 two: .ascii "Two\0\0\0" 563 three: .ascii "Three\0" 564 four: .ascii "Four\0" 565 566 .include "../logo.lzss_new" 567 568 disk_buffer: 569 .ascii "processor : 0\n" 570 .ascii "vendor_id : AuthenticAMD\n" 571 .ascii "cpu family : 6\n" 572 .ascii "model : 6\n" 573 .ascii "model name : AMD Athlon(tm) XP 2000+\n" 574 .ascii "stepping : 2\n" 575 .ascii "cpu MHz : 1665.267\n" 576 .ascii "cache size : 256 KB\n" 577 .ascii "fdiv_bug : no\n" 578 .ascii "hlt_bug : no\n" 579 .ascii "f00f_bug : no\n" 580 .ascii "coma_bug : no\n" 581 .ascii "fpu : yes\n" 582 .ascii "fpu_exception : yes\n" 583 .ascii "cpuid level : 1\n" 584 .ascii "wp : yes\n" 585 .ascii "flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse syscall mmxext 3dnowext 3dnow up\n" 586 .ascii "bogomips : 3330.53\n" 587 .ascii "clflush size : 32\n" 588 .ascii "power management: ts\n\0" 589 590 uname_info: 591 .ascii "Linux\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 592 .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 593 594 .ascii "tobler\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 595 .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 596 597 .ascii "2.6.29\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 598 .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 599 600 .ascii "#1 SMP Mon May 4 09:51:54 EDT 2009\0\0" 601 .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 602 603 .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 604 .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 605 606 .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 607 .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 608 609 610 sysinfo_buff: 611 .long 0,0,0,0,512*1024*1024,0,0,0,0 612 .long 0,0,0,0,0,0,0,0,0 613 614 #============================================================================ 615 # section .bss 616 #============================================================================ 617 .bss 618 619 .lcomm text_buf, (N+F-1) 620 .lcomm out_buffer,16384 621 622 623 624 625 626