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